import { CSSProperties } from 'react'; import { ChatBackground } from '../../state/settings'; import { blueprint } from './backgrounds/blueprint'; import { stars } from './backgrounds/stars'; import { topographic } from './backgrounds/topographic'; import { herringbone } from './backgrounds/herringbone'; import { crosshatch } from './backgrounds/crosshatch'; import { chevron } from './backgrounds/chevron'; import { polka } from './backgrounds/polka'; import { triangles } from './backgrounds/triangles'; import { plaid } from './backgrounds/plaid'; import { tactical } from './backgrounds/tactical'; import { circuit } from './backgrounds/circuit'; import { hexgrid } from './backgrounds/hexgrid'; import { waves } from './backgrounds/waves'; import { neon } from './backgrounds/neon'; import { animRain } from './backgrounds/animRain'; import { animStars } from './backgrounds/animStars'; import { animPulse } from './backgrounds/animPulse'; import { animAurora } from './backgrounds/animAurora'; import { animFireflies } from './backgrounds/animFireflies'; export const BG_OPTIONS: { value: ChatBackground; label: string }[] = [ { value: 'none', label: 'None' }, { value: 'blueprint', label: 'Blueprint' }, { value: 'carbon', label: 'Carbon' }, { value: 'stars', label: 'Stars' }, { value: 'topographic', label: 'Topographic' }, { value: 'herringbone', label: 'Herringbone' }, { value: 'crosshatch', label: 'Crosshatch' }, { value: 'chevron', label: 'Chevron' }, { value: 'polka', label: 'Polka' }, { value: 'triangles', label: 'Triangles' }, { value: 'plaid', label: 'Plaid' }, { value: 'tactical', label: 'Tactical' }, { value: 'circuit', label: 'Circuit' }, { value: 'hexgrid', label: 'Hex Grid' }, { value: 'waves', label: 'Waves' }, { value: 'neon', label: 'Neon Grid' }, { value: 'aurora', label: 'Aurora' }, { value: 'anim-rain', label: 'Digital Rain' }, { value: 'anim-stars', label: 'Star Drift' }, { value: 'anim-pulse', label: 'Grid Pulse' }, { value: 'anim-aurora', label: 'Aurora Flow' }, { value: 'anim-fireflies', label: 'Fireflies' }, ]; // `none`, `carbon` and `aurora` stay inline: carbon + aurora are the kept user // favorites, none is the empty layer. Every other background is a premium // per-pattern module under ./backgrounds/ (each exposes a `dark` + `light` // variant). Keeping the whole record here lets getChatBg stay the single entry // point and preserves the Record exhaustiveness check. const DARK: Record = { none: {}, carbon: { backgroundColor: '#0e0e0e', backgroundImage: [ 'repeating-linear-gradient(45deg, rgba(255,255,255,0.035) 0, rgba(255,255,255,0.035) 2px, transparent 0, transparent 50%)', 'repeating-linear-gradient(135deg, rgba(255,255,255,0.035) 0, rgba(255,255,255,0.035) 2px, transparent 0, transparent 50%)', ].join(','), backgroundSize: '8px 8px', }, aurora: { backgroundColor: '#030810', backgroundImage: [ 'radial-gradient(ellipse at 20% 30%, rgba(0,255,136,0.08) 0%, transparent 55%)', 'radial-gradient(ellipse at 80% 70%, rgba(0,100,255,0.08) 0%, transparent 55%)', 'radial-gradient(ellipse at 50% 10%, rgba(120,0,255,0.06) 0%, transparent 50%)', 'radial-gradient(ellipse at 60% 90%, rgba(0,212,255,0.06) 0%, transparent 50%)', ].join(','), }, blueprint: blueprint.dark, stars: stars.dark, topographic: topographic.dark, herringbone: herringbone.dark, crosshatch: crosshatch.dark, chevron: chevron.dark, polka: polka.dark, triangles: triangles.dark, plaid: plaid.dark, tactical: tactical.dark, circuit: circuit.dark, hexgrid: hexgrid.dark, waves: waves.dark, neon: neon.dark, 'anim-rain': animRain.dark, 'anim-stars': animStars.dark, 'anim-pulse': animPulse.dark, 'anim-aurora': animAurora.dark, 'anim-fireflies': animFireflies.dark, }; const LIGHT: Record = { none: {}, carbon: { backgroundColor: '#efefef', backgroundImage: [ 'repeating-linear-gradient(45deg, rgba(0,0,0,0.04) 0, rgba(0,0,0,0.04) 2px, transparent 0, transparent 50%)', 'repeating-linear-gradient(135deg, rgba(0,0,0,0.04) 0, rgba(0,0,0,0.04) 2px, transparent 0, transparent 50%)', ].join(','), backgroundSize: '8px 8px', }, aurora: { backgroundColor: '#f4faf8', backgroundImage: [ 'radial-gradient(ellipse at 20% 30%, rgba(0,160,80,0.09) 0%, transparent 55%)', 'radial-gradient(ellipse at 80% 70%, rgba(0,80,200,0.09) 0%, transparent 55%)', 'radial-gradient(ellipse at 50% 10%, rgba(100,0,200,0.07) 0%, transparent 50%)', 'radial-gradient(ellipse at 60% 90%, rgba(0,160,200,0.07) 0%, transparent 50%)', ].join(','), }, blueprint: blueprint.light, stars: stars.light, topographic: topographic.light, herringbone: herringbone.light, crosshatch: crosshatch.light, chevron: chevron.light, polka: polka.light, triangles: triangles.light, plaid: plaid.light, tactical: tactical.light, circuit: circuit.light, hexgrid: hexgrid.light, waves: waves.light, neon: neon.light, 'anim-rain': animRain.light, 'anim-stars': animStars.light, 'anim-pulse': animPulse.light, 'anim-aurora': animAurora.light, 'anim-fireflies': animFireflies.light, }; export const getChatBg = ( bg: ChatBackground, isDark: boolean, pauseAnimations?: boolean, ): CSSProperties => { const style = isDark ? DARK[bg] : LIGHT[bg]; const reducedMotion = typeof window !== 'undefined' && window.matchMedia('(prefers-reduced-motion: reduce)').matches; if ((pauseAnimations || reducedMotion) && style.animation) { const { animation: _anim, ...rest } = style; return rest; } // For animated backgrounds, promote the element to its own compositor layer so // background-position keyframes don't trigger repaints on descendant elements. if (style.animation) { return { ...style, willChange: 'background-position', contain: 'paint' }; } return style; };