import { ChatBgVariants } from './types'; import { rainFall } from './animRain.css'; // anim-rain — "Digital Rain" — a premium take on the Matrix code-rain motif. // // Concept: sparse vertical columns of falling glyph-streaks. Each streak is a // soft vertical gradient that fades from a brighter LEADING glyph (the drop's // head) up into a dim trailing tail, punctuated by a scatter of faint monospace // glyph marks so it reads as CODE rather than plain stripes. It floats over a // near-black base carrying a subtle green phosphor cast and a gentle vignette. // Columns are deliberately sparse (only a handful across the 260px-wide tile) // so the reading area breathes and text always wins the contrast fight. // // SEAMLESS TILING + PAN — the streak SVG tile is 260×200. Its content is // authored to wrap top↔bottom: each streak's gradient and glyphs are placed so // the tile is vertically continuous, and the animation (see animRain.css.ts) // pans this first layer down by EXACTLY one tile height (200px) per cycle, so // the "fall" loops with no seam. The base / vignette layers are 100% 100% and // stay fixed (the keyframe holds them at '0 0'). // // ANIMATION-STRIP SAFETY — getChatBg removes `animation` for reduced-motion / // pause-animations users, so the non-animation properties below already read as // a finished, gorgeous STATIC rain: a frozen frame of streaks over the base. // // CSP / Tauri-safe: inline SVG via encodeURIComponent (NOT base64). oklch used // throughout; alphas kept low so both themes stay WCAG-AA-friendly for text. // One vertical streak-column, colour-parameterised. Placed at x within a // 260-wide tile. `head` is the bright leading-glyph colour, `tail` the dim // trailing colour, `glyph` the colour of the riding monospace glyph ticks. const streak = ( x: number, headY: number, // y of the leading glyph (drop head) len: number, // trailing tail length upward head: string, tail: string, glyph: string, ): string => { const topY = headY - len; const id = `g${x}_${headY}`; // unique even when two columns share an x // Vertical fade: transparent at the tail top → tail colour → bright head. const grad = ` `; // The streak body is a soft, slightly-blurred vertical bar. const bar = ``; // A few monospace glyph ticks riding the column (short horizontal dashes). const ticks = [0.22, 0.45, 0.68, 0.86] .map((f, i) => { const gy = Math.round(topY + len * f); const gw = i % 2 === 0 ? 5 : 3; const op = i === 3 ? '0.9' : '0.5'; return ``; }) .join(''); // The leading glyph: a brighter small square cap at the head. const cap = ``; return grad + bar + ticks + cap; }; // Full 260×200 tile. Columns are wrapped vertically: a column whose head sits // low in the tile has its tail running off the top, and a companion column // re-enters that space, so panning by one tile height reads as continuous fall. const tile = (head: string, tail: string, glyph: string): string => { const cols = [ streak(24, 150, 140, head, tail, glyph), streak(78, 60, 120, head, tail, glyph), streak(122, 196, 160, head, tail, glyph), // head near bottom → tail wraps up streak(122, 40, 160, head, tail, glyph), // partner near top completes the wrap streak(178, 110, 100, head, tail, glyph), streak(232, 176, 130, head, tail, glyph), ].join(''); const svg = `${cols}`; return `url("data:image/svg+xml,${encodeURIComponent(svg)}")`; }; export const animRain: ChatBgVariants = { // Dark: phosphor-green streaks on deep near-black with a faint green cast. dark: { backgroundColor: 'oklch(0.16 0.02 150)', backgroundImage: [ // 1) the falling streak columns (this is the panned layer) tile( 'oklch(0.75 0.14 150 / 0.5)', // head — bright phosphor glyph 'oklch(0.68 0.12 150 / 0.28)', // tail — dim phosphor 'oklch(0.82 0.1 150 / 0.5)', // glyph ticks — brightest ), // 2) soft top-down phosphor haze so the rain has atmosphere 'linear-gradient(180deg, oklch(0.24 0.04 150 / 0.55) 0%, transparent 40%)', // 3) subtle green cast pooling toward the bottom 'radial-gradient(120% 90% at 50% 100%, oklch(0.28 0.05 150 / 0.45) 0%, transparent 60%)', // 4) vignette — quiet the corners so the reading column stays clean 'radial-gradient(140% 140% at 50% 45%, transparent 60%, oklch(0.1 0.02 150 / 0.6) 100%)', ].join(','), backgroundSize: ['260px 200px', '100% 100%', '100% 100%', '100% 100%'].join(','), backgroundPosition: ['0 0', '0 0', '0 0', '0 0'].join(','), animation: `${rainFall} 12s linear infinite`, }, // Light: soft teal-grey streaks on a pale cool base — elegant, never neon. light: { backgroundColor: 'oklch(0.97 0.008 165)', backgroundImage: [ tile( 'oklch(0.55 0.07 165 / 0.4)', // head — soft teal-grey drop 'oklch(0.62 0.05 165 / 0.22)', // tail — faint teal-grey 'oklch(0.5 0.06 165 / 0.42)', // glyph ticks ), // gentle cool wash from the top 'linear-gradient(180deg, oklch(0.94 0.015 175 / 0.6) 0%, transparent 42%)', // faint teal pooling at the bottom edge 'radial-gradient(120% 90% at 50% 100%, oklch(0.9 0.02 170 / 0.5) 0%, transparent 60%)', // soft vignette in cool grey 'radial-gradient(140% 140% at 50% 45%, transparent 62%, oklch(0.88 0.02 165 / 0.5) 100%)', ].join(','), backgroundSize: ['260px 200px', '100% 100%', '100% 100%', '100% 100%'].join(','), backgroundPosition: ['0 0', '0 0', '0 0', '0 0'].join(','), animation: `${rainFall} 12s linear infinite`, }, };