@circadian/sol 0.2.8 → 0.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +185 -3
- package/dist/devtools/index.js +1 -1
- package/dist/index.d.ts +129 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1050 -4
- package/dist/index.js.map +1 -1
- package/dist/{solar-theme-provider-6-EJ4jGB.js → solar-theme-provider-BcP2n4b7.js} +299 -12
- package/dist/solar-theme-provider-BcP2n4b7.js.map +1 -0
- package/package.json +1 -1
- package/dist/solar-theme-provider-6-EJ4jGB.js.map +0 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["WMO_MAP: Record<number, WeatherCategory>","fetchWeather","WMO_MAP","useWeatherData","finalPalette: WidgetPalette","opts: Intl.DateTimeFormatOptions","liveWeatherCategory: WeatherCategory | null","liveTemperatureC: number | null","WMO_MAP: Record<number, WeatherCategory>","palette: WidgetPalette","liveWeatherCategory: WeatherCategory | null","liveTemperatureC: number | null","props: CompactSkinProps","opts: Intl.DateTimeFormatOptions"],"sources":["../src/widgets/solar-widget.shell.tsx","../src/widgets/compact-widget.shell.tsx"],"sourcesContent":["'use client';\n\n/**\n * widgets/solar-widget.shell.tsx (skin-aware shell)\n *\n * Weather fetch is now centralised here alongside the compact shell:\n * - Fetches temperature + weather_code from open-meteo once per mount\n * - Resolves liveWeatherCategory: weatherCategoryOverride ?? live category\n * - Passes liveWeatherCategory + liveTemperatureC down to skin components\n * - Skins should prefer these props over running their own fetch\n *\n * simulatedDate prop wins over ctx.simulatedDate (set by SolarDevTools),\n * which wins over undefined (live time). This means the devtools timeline\n * scrubber moves the orb in every widget without requiring any prop wiring.\n */\n\nimport { useEffect, useLayoutEffect, useMemo, useState } from 'react';\nimport type { SolarPhase } from '../hooks/useSolarPosition';\nimport { lerpHex } from '../lib/solar-lerp';\nimport { useSolarTheme } from '../provider/solar-theme-provider';\nimport type { DesignMode, WidgetPalette } from '../skins/types/widget-skin.types';\n\n// ─── Re-exported types ────────────────────────────────────────────────────────\n\nexport type ExpandDirection =\n | 'top-left'\n | 'top-center'\n | 'top-right'\n | 'center-left'\n | 'center'\n | 'center-right'\n | 'bottom-left'\n | 'bottom-center'\n | 'bottom-right';\n\nexport type WidgetSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';\n\nexport type WeatherCategory =\n | 'clear'\n | 'partly-cloudy'\n | 'overcast'\n | 'fog'\n | 'drizzle'\n | 'rain'\n | 'heavy-rain'\n | 'snow'\n | 'heavy-snow'\n | 'thunder';\n\nexport type CustomPalettes = Partial<Record<SolarPhase, { bg: [string, string, string] }>>;\n\nimport { SKINS } from '../skins/index';\nexport const PALETTES = SKINS.foundry.widgetPalettes;\n\n// ─── WMO weather code map ─────────────────────────────────────────────────────\n\nconst WMO_MAP: Record<number, WeatherCategory> = {\n 0: 'clear',\n 1: 'clear',\n 2: 'partly-cloudy',\n 3: 'overcast',\n 45: 'fog',\n 48: 'fog',\n 51: 'drizzle',\n 53: 'drizzle',\n 55: 'drizzle',\n 61: 'rain',\n 63: 'rain',\n 65: 'heavy-rain',\n 71: 'snow',\n 73: 'snow',\n 75: 'heavy-snow',\n 80: 'rain',\n 82: 'heavy-rain',\n 85: 'snow',\n 86: 'heavy-snow',\n 95: 'thunder',\n 96: 'thunder',\n 99: 'thunder',\n};\n\ninterface LiveWeather {\n temperatureC: number;\n category: WeatherCategory;\n}\n\nasync function fetchWeather(lat: number, lon: number): Promise<LiveWeather> {\n const url = new URL('https://api.open-meteo.com/v1/forecast');\n url.searchParams.set('latitude', String(lat));\n url.searchParams.set('longitude', String(lon));\n url.searchParams.set('current', 'temperature_2m,weather_code');\n url.searchParams.set('forecast_days', '1');\n const data = (await fetch(url.toString()).then((r) => r.json())) as {\n current: { temperature_2m: number; weather_code: number };\n };\n return {\n temperatureC: Math.round(data.current.temperature_2m),\n category: WMO_MAP[data.current.weather_code] ?? 'clear',\n };\n}\n\nfunction useWeatherData(lat: number | null, lon: number | null) {\n const [weather, setWeather] = useState<LiveWeather | null>(null);\n useEffect(() => {\n if (!lat || !lon) return;\n let dead = false;\n fetchWeather(lat, lon)\n .then((w) => {\n if (!dead) setWeather(w);\n })\n .catch(() => {});\n const id = setInterval(\n () =>\n fetchWeather(lat, lon)\n .then((w) => {\n if (!dead) setWeather(w);\n })\n .catch(() => {}),\n 30 * 60 * 1000,\n );\n return () => {\n dead = true;\n clearInterval(id);\n };\n }, [lat, lon]);\n return weather;\n}\n\n// ─── Widget props ─────────────────────────────────────────────────────────────\n\nexport interface SolarWidgetProps {\n expandDirection?: ExpandDirection;\n size?: WidgetSize;\n showFlag?: boolean;\n showWeather?: boolean;\n hoverEffect?: boolean;\n customPalettes?: CustomPalettes;\n phaseOverride?: SolarPhase;\n weatherCategoryOverride?: WeatherCategory | null;\n /** Explicit simulated date. Falls back to ctx.simulatedDate (from SolarDevTools)\n * then to real current time. */\n simulatedDate?: Date;\n /** Lock the widget state: `true` = always expanded, `false` = always collapsed.\n * Omit or pass `undefined` for default localStorage-driven behaviour. */\n forceExpanded?: boolean;\n}\n\n// ─── Palette blending helper ──────────────────────────────────────────────────\n\nfunction blendWidgetPalette(from: WidgetPalette, to: WidgetPalette, t: number): WidgetPalette {\n return {\n bg: [\n lerpHex(from.bg[0], to.bg[0], t),\n lerpHex(from.bg[1], to.bg[1], t),\n lerpHex(from.bg[2], to.bg[2], t),\n ],\n textColor: lerpHex(from.textColor, to.textColor, t),\n accentColor: lerpHex(from.accentColor, to.accentColor, t),\n orb: lerpHex(from.orb, to.orb, t),\n outerGlow: from.outerGlow,\n mode: t < 0.5 ? from.mode : to.mode,\n };\n}\n\n// ─── Shell component ──────────────────────────────────────────────────────────\n\nexport function SolarWidget({\n expandDirection = 'bottom-right',\n size = 'lg',\n showFlag = false,\n showWeather = false,\n hoverEffect = false,\n customPalettes,\n phaseOverride,\n weatherCategoryOverride,\n simulatedDate: simulatedDateProp,\n forceExpanded,\n}: SolarWidgetProps) {\n const [mounted, setMounted] = useState(false);\n useLayoutEffect(() => setMounted(true), []);\n\n const {\n phase,\n blend,\n activeSkin,\n timezone,\n latitude,\n longitude,\n simulatedDate: ctxSimulatedDate,\n setCustomPalettes,\n } = useSolarTheme();\n\n // Register customPalettes into context so DevTools can read them\n useEffect(() => {\n setCustomPalettes(customPalettes);\n return () => setCustomPalettes(undefined);\n }, [customPalettes, setCustomPalettes]);\n\n // prop wins → context (devtools) → undefined (live)\n const simulatedDate = simulatedDateProp ?? ctxSimulatedDate;\n\n const [expanded, setExpanded] = useState(false);\n\n const activePhase = phaseOverride ?? phase;\n const activeBlend = phaseOverride\n ? { phase: phaseOverride, nextPhase: phaseOverride, t: 0 }\n : blend;\n\n const fromPalette = activeSkin.widgetPalettes[activeBlend.phase];\n const toPalette = activeSkin.widgetPalettes[activeBlend.nextPhase];\n\n const blendedPalette = useMemo(\n () => blendWidgetPalette(fromPalette, toPalette, activeBlend.t),\n [fromPalette, toPalette, activeBlend.t],\n );\n\n const finalPalette: WidgetPalette = useMemo(() => {\n if (!customPalettes?.[activePhase]) return blendedPalette;\n return { ...blendedPalette, bg: customPalettes[activePhase]?.bg };\n }, [blendedPalette, customPalettes, activePhase]);\n\n const time = useMemo(() => {\n const d = simulatedDate ?? new Date();\n const opts: Intl.DateTimeFormatOptions = {\n hour: '2-digit',\n minute: '2-digit',\n hourCycle: 'h23',\n };\n if (timezone) opts.timeZone = timezone;\n const parts = new Intl.DateTimeFormat('en-GB', opts).formatToParts(d);\n const hh = parts.find((p) => p.type === 'hour')?.value ?? '00';\n const mm = parts.find((p) => p.type === 'minute')?.value ?? '00';\n return `${hh}:${mm}`;\n }, [simulatedDate, timezone]);\n\n // ── Centralised weather fetch ────────────────────────────────────────────\n const liveWeather = useWeatherData(latitude ?? null, longitude ?? null);\n\n const liveWeatherCategory: WeatherCategory | null = showWeather\n ? (weatherCategoryOverride ?? liveWeather?.category ?? null)\n : null;\n\n const liveTemperatureC: number | null = liveWeather?.temperatureC ?? null;\n\n const SkinComponent = activeSkin.Component;\n\n return (\n <div style={{ visibility: mounted ? 'visible' : 'hidden' }}>\n <SkinComponent\n phase={activePhase}\n blend={activeBlend}\n expanded={expanded}\n onToggle={() => setExpanded((v) => !v)}\n expandDirection={expandDirection}\n size={size}\n time={time}\n location=\"\"\n showFlag={showFlag}\n showWeather={showWeather}\n hoverEffect={hoverEffect}\n weather={weatherCategoryOverride}\n liveWeatherCategory={liveWeatherCategory}\n liveTemperatureC={liveTemperatureC}\n palette={finalPalette}\n latitude={latitude}\n longitude={longitude}\n timezone={timezone}\n simulatedDate={simulatedDate}\n forceExpanded={forceExpanded}\n />\n </div>\n );\n}\n","'use client';\n\n/**\n * widgets/compact-widget.shell.tsx\n *\n * Shell for the compact solar widget — a slim pill/bar format.\n *\n * simulatedDate prop wins over ctx.simulatedDate (set by SolarDevTools),\n * which wins over undefined (live time). This means the devtools timeline\n * scrubber moves the orb in compact widgets without requiring any prop wiring.\n */\n\nimport { useEffect, useLayoutEffect, useMemo, useState } from 'react';\nimport type { SolarBlend, SolarPhase } from '../hooks/useSolarPosition';\nimport { lerpHex } from '../lib/solar-lerp';\nimport { useSolarTheme } from '../provider/solar-theme-provider';\nimport type { DesignMode, SkinDefinition, WidgetPalette } from '../skins/types/widget-skin.types';\nimport type { CustomPalettes } from './solar-widget.shell';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type WeatherCategory =\n | 'clear'\n | 'partly-cloudy'\n | 'overcast'\n | 'fog'\n | 'drizzle'\n | 'rain'\n | 'heavy-rain'\n | 'snow'\n | 'heavy-snow'\n | 'thunder';\n\nexport type CompactSize = 'sm' | 'md' | 'lg';\n\n// ─── WMO weather code map ─────────────────────────────────────────────────────\n\nconst WMO_MAP: Record<number, WeatherCategory> = {\n 0: 'clear',\n 1: 'clear',\n 2: 'partly-cloudy',\n 3: 'overcast',\n 45: 'fog',\n 48: 'fog',\n 51: 'drizzle',\n 53: 'drizzle',\n 55: 'drizzle',\n 61: 'rain',\n 63: 'rain',\n 65: 'heavy-rain',\n 71: 'snow',\n 73: 'snow',\n 75: 'heavy-snow',\n 80: 'rain',\n 82: 'heavy-rain',\n 85: 'snow',\n 86: 'heavy-snow',\n 95: 'thunder',\n 96: 'thunder',\n 99: 'thunder',\n};\n\n// ─── Centralised weather fetch ────────────────────────────────────────────────\n\ninterface LiveWeather {\n temperatureC: number;\n category: WeatherCategory;\n}\n\nasync function fetchWeather(lat: number, lon: number): Promise<LiveWeather> {\n const url = new URL('https://api.open-meteo.com/v1/forecast');\n url.searchParams.set('latitude', String(lat));\n url.searchParams.set('longitude', String(lon));\n url.searchParams.set('current', 'temperature_2m,weather_code');\n url.searchParams.set('forecast_days', '1');\n const data = (await fetch(url.toString()).then((r) => r.json())) as {\n current: { temperature_2m: number; weather_code: number };\n };\n return {\n temperatureC: Math.round(data.current.temperature_2m),\n category: WMO_MAP[data.current.weather_code] ?? 'clear',\n };\n}\n\nfunction useWeatherData(lat: number | null, lon: number | null) {\n const [weather, setWeather] = useState<LiveWeather | null>(null);\n useEffect(() => {\n if (!lat || !lon) return;\n let dead = false;\n fetchWeather(lat, lon)\n .then((w) => {\n if (!dead) setWeather(w);\n })\n .catch(() => {});\n const id = setInterval(\n () =>\n fetchWeather(lat, lon)\n .then((w) => {\n if (!dead) setWeather(w);\n })\n .catch(() => {}),\n 30 * 60 * 1000,\n );\n return () => {\n dead = true;\n clearInterval(id);\n };\n }, [lat, lon]);\n return weather;\n}\n\n// ─── Props passed to every compact skin component ─────────────────────────────\n\nexport interface CompactSkinProps {\n phase: SolarPhase;\n blend: SolarBlend;\n time: string;\n location: string;\n flag?: string;\n temperature?: string;\n weather?: WeatherCategory | null;\n liveWeatherCategory: WeatherCategory | null;\n liveTemperatureC: number | null;\n latitude?: number | null;\n longitude?: number | null;\n timezone?: string | null;\n simulatedDate?: Date;\n showFlag: boolean;\n showWeather: boolean;\n showTemperature: boolean;\n size: CompactSize;\n palette: WidgetPalette;\n}\n\n// ─── Public props for the shell ───────────────────────────────────────────────\n\nexport interface CompactWidgetProps {\n design?: DesignMode;\n overridePhase?: SolarPhase | null;\n time?: string;\n location?: string;\n flag?: string;\n temperature?: string;\n weather?: WeatherCategory | null;\n weatherCategoryOverride?: WeatherCategory | null;\n showFlag?: boolean;\n showWeather?: boolean;\n showTemperature?: boolean;\n size?: CompactSize;\n latitude?: number | null;\n longitude?: number | null;\n timezone?: string | null;\n /** Override background colors per phase */\n customPalettes?: CustomPalettes;\n /** Explicit simulated date. Falls back to ctx.simulatedDate (from SolarDevTools)\n * then to real current time. */\n simulatedDate?: Date;\n className?: string;\n}\n\n// ─── Palette blending ─────────────────────────────────────────────────────────\n\nfunction blendPalette(skin: SkinDefinition, blend: SolarBlend): WidgetPalette {\n const from = skin.widgetPalettes[blend.phase];\n const to = skin.widgetPalettes[blend.nextPhase];\n const t = blend.t;\n if (t === 0) return from;\n const lerp = (a: string, b: string) => lerpHex(a, b, t);\n return {\n bg: [lerp(from.bg[0], to.bg[0]), lerp(from.bg[1], to.bg[1]), lerp(from.bg[2], to.bg[2])] as [\n string,\n string,\n string,\n ],\n textColor: lerp(from.textColor, to.textColor),\n accentColor: lerp(from.accentColor, to.accentColor),\n orb: lerp(from.orb, to.orb),\n outerGlow: lerp(from.outerGlow, to.outerGlow),\n mode: from.mode,\n };\n}\n\n// ─── Shell ────────────────────────────────────────────────────────────────────\n\nexport function CompactWidget({\n design: designOverride,\n overridePhase,\n time,\n location = '',\n flag,\n temperature,\n weather = null,\n weatherCategoryOverride,\n showFlag = false,\n showWeather = false,\n showTemperature = true,\n size = 'md',\n latitude,\n longitude,\n timezone,\n customPalettes,\n simulatedDate: simulatedDateProp,\n className = '',\n}: CompactWidgetProps) {\n const [mounted, setMounted] = useState(false);\n useLayoutEffect(() => setMounted(true), []);\n\n const ctx = useSolarTheme();\n\n // Register customPalettes into context so DevTools can read them\n useEffect(() => {\n ctx.setCustomPalettes(customPalettes);\n return () => ctx.setCustomPalettes(undefined);\n }, [customPalettes, ctx.setCustomPalettes]);\n\n // prop wins → context (devtools) → undefined (live)\n const simulatedDate = simulatedDateProp ?? ctx.simulatedDate;\n\n const skin = useMemo(() => {\n if (!designOverride || designOverride === ctx.design) return ctx.activeSkin;\n return ctx.activeSkin;\n }, [designOverride, ctx.design, ctx.activeSkin]);\n\n const phase = overridePhase ?? ctx.phase;\n const blend = overridePhase\n ? { phase: overridePhase, nextPhase: overridePhase, t: 0 }\n : ctx.blend;\n\n const blendedPalette = useMemo(() => blendPalette(skin, blend), [skin, blend]);\n\n const palette: WidgetPalette = useMemo(() => {\n if (!customPalettes?.[phase]) return blendedPalette;\n return { ...blendedPalette, bg: customPalettes[phase]?.bg };\n }, [blendedPalette, customPalettes, phase]);\n\n const resolvedLat = latitude ?? ctx.latitude;\n const resolvedLon = longitude ?? ctx.longitude;\n const resolvedTz = timezone ?? ctx.timezone;\n\n // ── Centralised weather fetch ────────────────────────────────────────────\n const liveWeather = useWeatherData(resolvedLat ?? null, resolvedLon ?? null);\n\n const liveWeatherCategory: WeatherCategory | null = showWeather\n ? (weatherCategoryOverride ?? weather ?? liveWeather?.category ?? null)\n : null;\n\n const liveTemperatureC: number | null = liveWeather?.temperatureC ?? null;\n\n // ── Resolve time string ──────────────────────────────────────────────────\n // If caller passes an explicit time string, use it. Otherwise derive from\n // simulatedDate (or real time) so the clock display matches the orb.\n const resolvedTime = useMemo(() => {\n if (time) return time;\n const d = simulatedDate ?? new Date();\n const opts: Intl.DateTimeFormatOptions = {\n hour: '2-digit',\n minute: '2-digit',\n hourCycle: 'h23',\n };\n if (resolvedTz) opts.timeZone = resolvedTz;\n const parts = new Intl.DateTimeFormat('en-GB', opts).formatToParts(d);\n const hh = parts.find((p) => p.type === 'hour')?.value ?? '00';\n const mm = parts.find((p) => p.type === 'minute')?.value ?? '00';\n return `${hh}:${mm}`;\n }, [time, simulatedDate, resolvedTz]);\n\n const props: CompactSkinProps = {\n phase,\n blend,\n time: resolvedTime,\n location,\n flag,\n temperature,\n weather,\n liveWeatherCategory,\n liveTemperatureC,\n latitude: resolvedLat,\n longitude: resolvedLon,\n timezone: resolvedTz,\n simulatedDate,\n showFlag,\n showWeather,\n showTemperature,\n size,\n palette,\n };\n\n const CompactComponent = (\n skin as SkinDefinition & {\n CompactComponent?: React.ComponentType<CompactSkinProps>;\n }\n ).CompactComponent;\n\n if (!CompactComponent) {\n return (\n <div className={className} style={{ opacity: 0.4, fontSize: 11, color: '#888' }}>\n Compact not implemented for {skin.id}\n </div>\n );\n }\n\n return (\n <div\n className={className}\n style={{ visibility: mounted ? 'visible' : 'hidden', isolation: 'isolate' }}\n >\n <CompactComponent {...props} />\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["WMO_MAP: Record<number, WeatherCategory>","fetchWeather","WMO_MAP","useWeatherData","finalPalette: WidgetPalette","opts: Intl.DateTimeFormatOptions","liveWeatherCategory: WeatherCategory | null","liveTemperatureC: number | null","WMO_MAP: Record<number, WeatherCategory>","palette: WidgetPalette","liveWeatherCategory: WeatherCategory | null","liveTemperatureC: number | null","props: CompactSkinProps","opts: Intl.DateTimeFormatOptions","UNIVERSAL_PHASE_MOTION: MotionProfile","VOID_MOTION: MotionProfile","PARCHMENT_MOTION: MotionProfile","SIGNAL_MOTION: MotionProfile","MERIDIAN_MOTION: MotionProfile","SUNDIAL_MOTION: MotionProfile","PAPER_MOTION: MotionProfile","FOUNDRY_MOTION: MotionProfile","MINERAL_MOTION: MotionProfile","TIDE_MOTION: MotionProfile","AURORA_MOTION: MotionProfile","SKIN_MOTION_PROFILES: Record<DesignMode, MotionProfile>"],"sources":["../src/widgets/solar-widget.shell.tsx","../src/widgets/compact-widget.shell.tsx","../src/shared/shader-motion-profiles.tsx","../src/shared/solar-shader-bg.tsx"],"sourcesContent":["'use client';\n\n/**\n * widgets/solar-widget.shell.tsx (skin-aware shell)\n *\n * Weather fetch is now centralised here alongside the compact shell:\n * - Fetches temperature + weather_code from open-meteo once per mount\n * - Resolves liveWeatherCategory: weatherCategoryOverride ?? live category\n * - Passes liveWeatherCategory + liveTemperatureC down to skin components\n * - Skins should prefer these props over running their own fetch\n *\n * simulatedDate prop wins over ctx.simulatedDate (set by SolarDevTools),\n * which wins over undefined (live time). This means the devtools timeline\n * scrubber moves the orb in every widget without requiring any prop wiring.\n */\n\nimport { useEffect, useLayoutEffect, useMemo, useState } from 'react';\nimport type { SolarPhase } from '../hooks/useSolarPosition';\nimport { lerpHex } from '../lib/solar-lerp';\nimport { useSolarTheme } from '../provider/solar-theme-provider';\nimport type { DesignMode, WidgetPalette } from '../skins/types/widget-skin.types';\n\n// ─── Re-exported types ────────────────────────────────────────────────────────\n\nexport type ExpandDirection =\n | 'top-left'\n | 'top-center'\n | 'top-right'\n | 'center-left'\n | 'center'\n | 'center-right'\n | 'bottom-left'\n | 'bottom-center'\n | 'bottom-right';\n\nexport type WidgetSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';\n\nexport type WeatherCategory =\n | 'clear'\n | 'partly-cloudy'\n | 'overcast'\n | 'fog'\n | 'drizzle'\n | 'rain'\n | 'heavy-rain'\n | 'snow'\n | 'heavy-snow'\n | 'thunder';\n\nexport type CustomPalettes = Partial<Record<SolarPhase, { bg: [string, string, string] }>>;\n\nimport { SKINS } from '../skins/index';\nexport const PALETTES = SKINS.foundry.widgetPalettes;\n\n// ─── WMO weather code map ─────────────────────────────────────────────────────\n\nconst WMO_MAP: Record<number, WeatherCategory> = {\n 0: 'clear',\n 1: 'clear',\n 2: 'partly-cloudy',\n 3: 'overcast',\n 45: 'fog',\n 48: 'fog',\n 51: 'drizzle',\n 53: 'drizzle',\n 55: 'drizzle',\n 61: 'rain',\n 63: 'rain',\n 65: 'heavy-rain',\n 71: 'snow',\n 73: 'snow',\n 75: 'heavy-snow',\n 80: 'rain',\n 82: 'heavy-rain',\n 85: 'snow',\n 86: 'heavy-snow',\n 95: 'thunder',\n 96: 'thunder',\n 99: 'thunder',\n};\n\ninterface LiveWeather {\n temperatureC: number;\n category: WeatherCategory;\n}\n\nasync function fetchWeather(lat: number, lon: number): Promise<LiveWeather> {\n const url = new URL('https://api.open-meteo.com/v1/forecast');\n url.searchParams.set('latitude', String(lat));\n url.searchParams.set('longitude', String(lon));\n url.searchParams.set('current', 'temperature_2m,weather_code');\n url.searchParams.set('forecast_days', '1');\n const data = (await fetch(url.toString()).then((r) => r.json())) as {\n current: { temperature_2m: number; weather_code: number };\n };\n return {\n temperatureC: Math.round(data.current.temperature_2m),\n category: WMO_MAP[data.current.weather_code] ?? 'clear',\n };\n}\n\nfunction useWeatherData(lat: number | null, lon: number | null) {\n const [weather, setWeather] = useState<LiveWeather | null>(null);\n useEffect(() => {\n if (!lat || !lon) return;\n let dead = false;\n fetchWeather(lat, lon)\n .then((w) => {\n if (!dead) setWeather(w);\n })\n .catch(() => {});\n const id = setInterval(\n () =>\n fetchWeather(lat, lon)\n .then((w) => {\n if (!dead) setWeather(w);\n })\n .catch(() => {}),\n 30 * 60 * 1000,\n );\n return () => {\n dead = true;\n clearInterval(id);\n };\n }, [lat, lon]);\n return weather;\n}\n\n// ─── Widget props ─────────────────────────────────────────────────────────────\n\nexport interface SolarWidgetProps {\n expandDirection?: ExpandDirection;\n size?: WidgetSize;\n showFlag?: boolean;\n showWeather?: boolean;\n hoverEffect?: boolean;\n customPalettes?: CustomPalettes;\n phaseOverride?: SolarPhase;\n weatherCategoryOverride?: WeatherCategory | null;\n /** Explicit simulated date. Falls back to ctx.simulatedDate (from SolarDevTools)\n * then to real current time. */\n simulatedDate?: Date;\n /** Lock the widget state: `true` = always expanded, `false` = always collapsed.\n * Omit or pass `undefined` for default localStorage-driven behaviour. */\n forceExpanded?: boolean;\n}\n\n// ─── Palette blending helper ──────────────────────────────────────────────────\n\nfunction blendWidgetPalette(from: WidgetPalette, to: WidgetPalette, t: number): WidgetPalette {\n return {\n bg: [\n lerpHex(from.bg[0], to.bg[0], t),\n lerpHex(from.bg[1], to.bg[1], t),\n lerpHex(from.bg[2], to.bg[2], t),\n ],\n textColor: lerpHex(from.textColor, to.textColor, t),\n accentColor: lerpHex(from.accentColor, to.accentColor, t),\n orb: lerpHex(from.orb, to.orb, t),\n outerGlow: from.outerGlow,\n mode: t < 0.5 ? from.mode : to.mode,\n };\n}\n\n// ─── Shell component ──────────────────────────────────────────────────────────\n\nexport function SolarWidget({\n expandDirection = 'bottom-right',\n size = 'lg',\n showFlag = false,\n showWeather = false,\n hoverEffect = false,\n customPalettes,\n phaseOverride,\n weatherCategoryOverride,\n simulatedDate: simulatedDateProp,\n forceExpanded,\n}: SolarWidgetProps) {\n const [mounted, setMounted] = useState(false);\n useLayoutEffect(() => setMounted(true), []);\n\n const {\n phase,\n blend,\n activeSkin,\n timezone,\n latitude,\n longitude,\n simulatedDate: ctxSimulatedDate,\n setCustomPalettes,\n } = useSolarTheme();\n\n // Register customPalettes into context so DevTools can read them\n useEffect(() => {\n setCustomPalettes(customPalettes);\n return () => setCustomPalettes(undefined);\n }, [customPalettes, setCustomPalettes]);\n\n // prop wins → context (devtools) → undefined (live)\n const simulatedDate = simulatedDateProp ?? ctxSimulatedDate;\n\n const [expanded, setExpanded] = useState(false);\n\n const activePhase = phaseOverride ?? phase;\n const activeBlend = phaseOverride\n ? { phase: phaseOverride, nextPhase: phaseOverride, t: 0 }\n : blend;\n\n const fromPalette = activeSkin.widgetPalettes[activeBlend.phase];\n const toPalette = activeSkin.widgetPalettes[activeBlend.nextPhase];\n\n const blendedPalette = useMemo(\n () => blendWidgetPalette(fromPalette, toPalette, activeBlend.t),\n [fromPalette, toPalette, activeBlend.t],\n );\n\n const finalPalette: WidgetPalette = useMemo(() => {\n if (!customPalettes?.[activePhase]) return blendedPalette;\n return { ...blendedPalette, bg: customPalettes[activePhase]?.bg };\n }, [blendedPalette, customPalettes, activePhase]);\n\n const time = useMemo(() => {\n const d = simulatedDate ?? new Date();\n const opts: Intl.DateTimeFormatOptions = {\n hour: '2-digit',\n minute: '2-digit',\n hourCycle: 'h23',\n };\n if (timezone) opts.timeZone = timezone;\n const parts = new Intl.DateTimeFormat('en-GB', opts).formatToParts(d);\n const hh = parts.find((p) => p.type === 'hour')?.value ?? '00';\n const mm = parts.find((p) => p.type === 'minute')?.value ?? '00';\n return `${hh}:${mm}`;\n }, [simulatedDate, timezone]);\n\n // ── Centralised weather fetch ────────────────────────────────────────────\n const liveWeather = useWeatherData(latitude ?? null, longitude ?? null);\n\n const liveWeatherCategory: WeatherCategory | null = showWeather\n ? (weatherCategoryOverride ?? liveWeather?.category ?? null)\n : null;\n\n const liveTemperatureC: number | null = liveWeather?.temperatureC ?? null;\n\n const SkinComponent = activeSkin.Component;\n\n return (\n <div style={{ visibility: mounted ? 'visible' : 'hidden' }}>\n <SkinComponent\n phase={activePhase}\n blend={activeBlend}\n expanded={expanded}\n onToggle={() => setExpanded((v) => !v)}\n expandDirection={expandDirection}\n size={size}\n time={time}\n location=\"\"\n showFlag={showFlag}\n showWeather={showWeather}\n hoverEffect={hoverEffect}\n weather={weatherCategoryOverride}\n liveWeatherCategory={liveWeatherCategory}\n liveTemperatureC={liveTemperatureC}\n palette={finalPalette}\n latitude={latitude}\n longitude={longitude}\n timezone={timezone}\n simulatedDate={simulatedDate}\n forceExpanded={forceExpanded}\n />\n </div>\n );\n}\n","'use client';\n\n/**\n * widgets/compact-widget.shell.tsx\n *\n * Shell for the compact solar widget — a slim pill/bar format.\n *\n * simulatedDate prop wins over ctx.simulatedDate (set by SolarDevTools),\n * which wins over undefined (live time). This means the devtools timeline\n * scrubber moves the orb in compact widgets without requiring any prop wiring.\n */\n\nimport { useEffect, useLayoutEffect, useMemo, useState } from 'react';\nimport type { SolarBlend, SolarPhase } from '../hooks/useSolarPosition';\nimport { lerpHex } from '../lib/solar-lerp';\nimport { useSolarTheme } from '../provider/solar-theme-provider';\nimport type { DesignMode, SkinDefinition, WidgetPalette } from '../skins/types/widget-skin.types';\nimport type { CustomPalettes } from './solar-widget.shell';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type WeatherCategory =\n | 'clear'\n | 'partly-cloudy'\n | 'overcast'\n | 'fog'\n | 'drizzle'\n | 'rain'\n | 'heavy-rain'\n | 'snow'\n | 'heavy-snow'\n | 'thunder';\n\nexport type CompactSize = 'sm' | 'md' | 'lg';\n\n// ─── WMO weather code map ─────────────────────────────────────────────────────\n\nconst WMO_MAP: Record<number, WeatherCategory> = {\n 0: 'clear',\n 1: 'clear',\n 2: 'partly-cloudy',\n 3: 'overcast',\n 45: 'fog',\n 48: 'fog',\n 51: 'drizzle',\n 53: 'drizzle',\n 55: 'drizzle',\n 61: 'rain',\n 63: 'rain',\n 65: 'heavy-rain',\n 71: 'snow',\n 73: 'snow',\n 75: 'heavy-snow',\n 80: 'rain',\n 82: 'heavy-rain',\n 85: 'snow',\n 86: 'heavy-snow',\n 95: 'thunder',\n 96: 'thunder',\n 99: 'thunder',\n};\n\n// ─── Centralised weather fetch ────────────────────────────────────────────────\n\ninterface LiveWeather {\n temperatureC: number;\n category: WeatherCategory;\n}\n\nasync function fetchWeather(lat: number, lon: number): Promise<LiveWeather> {\n const url = new URL('https://api.open-meteo.com/v1/forecast');\n url.searchParams.set('latitude', String(lat));\n url.searchParams.set('longitude', String(lon));\n url.searchParams.set('current', 'temperature_2m,weather_code');\n url.searchParams.set('forecast_days', '1');\n const data = (await fetch(url.toString()).then((r) => r.json())) as {\n current: { temperature_2m: number; weather_code: number };\n };\n return {\n temperatureC: Math.round(data.current.temperature_2m),\n category: WMO_MAP[data.current.weather_code] ?? 'clear',\n };\n}\n\nfunction useWeatherData(lat: number | null, lon: number | null) {\n const [weather, setWeather] = useState<LiveWeather | null>(null);\n useEffect(() => {\n if (!lat || !lon) return;\n let dead = false;\n fetchWeather(lat, lon)\n .then((w) => {\n if (!dead) setWeather(w);\n })\n .catch(() => {});\n const id = setInterval(\n () =>\n fetchWeather(lat, lon)\n .then((w) => {\n if (!dead) setWeather(w);\n })\n .catch(() => {}),\n 30 * 60 * 1000,\n );\n return () => {\n dead = true;\n clearInterval(id);\n };\n }, [lat, lon]);\n return weather;\n}\n\n// ─── Props passed to every compact skin component ─────────────────────────────\n\nexport interface CompactSkinProps {\n phase: SolarPhase;\n blend: SolarBlend;\n time: string;\n location: string;\n flag?: string;\n temperature?: string;\n weather?: WeatherCategory | null;\n liveWeatherCategory: WeatherCategory | null;\n liveTemperatureC: number | null;\n latitude?: number | null;\n longitude?: number | null;\n timezone?: string | null;\n simulatedDate?: Date;\n showFlag: boolean;\n showWeather: boolean;\n showTemperature: boolean;\n size: CompactSize;\n palette: WidgetPalette;\n}\n\n// ─── Public props for the shell ───────────────────────────────────────────────\n\nexport interface CompactWidgetProps {\n design?: DesignMode;\n overridePhase?: SolarPhase | null;\n time?: string;\n location?: string;\n flag?: string;\n temperature?: string;\n weather?: WeatherCategory | null;\n weatherCategoryOverride?: WeatherCategory | null;\n showFlag?: boolean;\n showWeather?: boolean;\n showTemperature?: boolean;\n size?: CompactSize;\n latitude?: number | null;\n longitude?: number | null;\n timezone?: string | null;\n /** Override background colors per phase */\n customPalettes?: CustomPalettes;\n /** Explicit simulated date. Falls back to ctx.simulatedDate (from SolarDevTools)\n * then to real current time. */\n simulatedDate?: Date;\n className?: string;\n}\n\n// ─── Palette blending ─────────────────────────────────────────────────────────\n\nfunction blendPalette(skin: SkinDefinition, blend: SolarBlend): WidgetPalette {\n const from = skin.widgetPalettes[blend.phase];\n const to = skin.widgetPalettes[blend.nextPhase];\n const t = blend.t;\n if (t === 0) return from;\n const lerp = (a: string, b: string) => lerpHex(a, b, t);\n return {\n bg: [lerp(from.bg[0], to.bg[0]), lerp(from.bg[1], to.bg[1]), lerp(from.bg[2], to.bg[2])] as [\n string,\n string,\n string,\n ],\n textColor: lerp(from.textColor, to.textColor),\n accentColor: lerp(from.accentColor, to.accentColor),\n orb: lerp(from.orb, to.orb),\n outerGlow: lerp(from.outerGlow, to.outerGlow),\n mode: from.mode,\n };\n}\n\n// ─── Shell ────────────────────────────────────────────────────────────────────\n\nexport function CompactWidget({\n design: designOverride,\n overridePhase,\n time,\n location = '',\n flag,\n temperature,\n weather = null,\n weatherCategoryOverride,\n showFlag = false,\n showWeather = false,\n showTemperature = true,\n size = 'md',\n latitude,\n longitude,\n timezone,\n customPalettes,\n simulatedDate: simulatedDateProp,\n className = '',\n}: CompactWidgetProps) {\n const [mounted, setMounted] = useState(false);\n useLayoutEffect(() => setMounted(true), []);\n\n const ctx = useSolarTheme();\n\n // Register customPalettes into context so DevTools can read them\n useEffect(() => {\n ctx.setCustomPalettes(customPalettes);\n return () => ctx.setCustomPalettes(undefined);\n }, [customPalettes, ctx.setCustomPalettes]);\n\n // prop wins → context (devtools) → undefined (live)\n const simulatedDate = simulatedDateProp ?? ctx.simulatedDate;\n\n const skin = useMemo(() => {\n if (!designOverride || designOverride === ctx.design) return ctx.activeSkin;\n return ctx.activeSkin;\n }, [designOverride, ctx.design, ctx.activeSkin]);\n\n const phase = overridePhase ?? ctx.phase;\n const blend = overridePhase\n ? { phase: overridePhase, nextPhase: overridePhase, t: 0 }\n : ctx.blend;\n\n const blendedPalette = useMemo(() => blendPalette(skin, blend), [skin, blend]);\n\n const palette: WidgetPalette = useMemo(() => {\n if (!customPalettes?.[phase]) return blendedPalette;\n return { ...blendedPalette, bg: customPalettes[phase]?.bg };\n }, [blendedPalette, customPalettes, phase]);\n\n const resolvedLat = latitude ?? ctx.latitude;\n const resolvedLon = longitude ?? ctx.longitude;\n const resolvedTz = timezone ?? ctx.timezone;\n\n // ── Centralised weather fetch ────────────────────────────────────────────\n const liveWeather = useWeatherData(resolvedLat ?? null, resolvedLon ?? null);\n\n const liveWeatherCategory: WeatherCategory | null = showWeather\n ? (weatherCategoryOverride ?? weather ?? liveWeather?.category ?? null)\n : null;\n\n const liveTemperatureC: number | null = liveWeather?.temperatureC ?? null;\n\n // ── Resolve time string ──────────────────────────────────────────────────\n // If caller passes an explicit time string, use it. Otherwise derive from\n // simulatedDate (or real time) so the clock display matches the orb.\n const resolvedTime = useMemo(() => {\n if (time) return time;\n const d = simulatedDate ?? new Date();\n const opts: Intl.DateTimeFormatOptions = {\n hour: '2-digit',\n minute: '2-digit',\n hourCycle: 'h23',\n };\n if (resolvedTz) opts.timeZone = resolvedTz;\n const parts = new Intl.DateTimeFormat('en-GB', opts).formatToParts(d);\n const hh = parts.find((p) => p.type === 'hour')?.value ?? '00';\n const mm = parts.find((p) => p.type === 'minute')?.value ?? '00';\n return `${hh}:${mm}`;\n }, [time, simulatedDate, resolvedTz]);\n\n const props: CompactSkinProps = {\n phase,\n blend,\n time: resolvedTime,\n location,\n flag,\n temperature,\n weather,\n liveWeatherCategory,\n liveTemperatureC,\n latitude: resolvedLat,\n longitude: resolvedLon,\n timezone: resolvedTz,\n simulatedDate,\n showFlag,\n showWeather,\n showTemperature,\n size,\n palette,\n };\n\n const CompactComponent = (\n skin as SkinDefinition & {\n CompactComponent?: React.ComponentType<CompactSkinProps>;\n }\n ).CompactComponent;\n\n if (!CompactComponent) {\n return (\n <div className={className} style={{ opacity: 0.4, fontSize: 11, color: '#888' }}>\n Compact not implemented for {skin.id}\n </div>\n );\n }\n\n return (\n <div\n className={className}\n style={{ visibility: mounted ? 'visible' : 'hidden', isolation: 'isolate' }}\n >\n <CompactComponent {...props} />\n </div>\n );\n}\n","/**\n * shader-motion-profiles.ts\n *\n * Per-skin motion personalities for SolarShaderBg.\n *\n * This is the creative core of the background shader system. Colors come from\n * each skin's shaderPalettes — motion comes from here. Together they define\n * a complete atmospheric identity per skin per phase.\n *\n * Motion axes:\n * distortion — organic mesh noise. High = liquid, flowing. Low = crystalline, still.\n * swirl — vortex pull. High = spiral drama. Low = gentle drift.\n * speed — animation rate. 0 = frozen. 1 = reference. >1 = fast.\n * grainOverlay — film grain. High in dark/still skins, low in bright/active ones.\n *\n * ─── Design intent per skin ───────────────────────────────────────────────────\n *\n * VOID The anti-skin. Barely alive. Night = almost nothing moves.\n * Noon nudges up slightly — but \"slightly\" for Void is still near-zero.\n * Grain is highest at midnight because darkness has texture.\n *\n * PARCHMENT A document. Documents don't move. speed=0 everywhere.\n * The only \"motion\" is the CSS transition on color as the phase shifts.\n *\n * SIGNAL Brutalist terminal. No organic drift — that would feel wrong.\n * Instead: very low distortion, almost-zero swirl, high grain.\n * The background is raw, not smooth.\n *\n * MERIDIAN Airy and clean. Moves gently but constantly — like a well-ventilated\n * room. Half the universal values throughout.\n *\n * SUNDIAL Ancient, measured. The dial shadow moves once per hour.\n * Low speed, low swirl. Grain is stone-textured.\n *\n * PAPER Uncoated stock breathes. Organic, unhurried. Medium everything —\n * never dramatic, never static.\n *\n * FOUNDRY The reference skin. Uses the universal phase-energy curve:\n * low at night, high at noon, dramatic at sunrise/sunset.\n * Closest to the default PHASE_MOTION values.\n *\n * MINERAL Gemstones don't flow — they refract. Low distortion (facets, not\n * liquid), moderate swirl (internal light scattering), medium speed.\n * Grain is low — stones are smooth.\n *\n * TIDE The sea rolls. High distortion at all phases (waves never stop),\n * moderate swirl, speed follows tidal rhythm. Low grain — water is clear.\n *\n * AURORA The most variable. At midnight/night: maximum everything —\n * the lights are dancing. During day: gentle and warm, the aurora\n * is invisible. At dusk/sunset: the bands return.\n */\n\nimport type { SolarPhase } from '../hooks/useSolarPosition';\nimport type { DesignMode, ShaderMotion } from '../skins/types/widget-skin.types';\n\n// ─── Type ─────────────────────────────────────────────────────────────────────\n\nexport type MotionProfile = Record<SolarPhase, ShaderMotion>;\n\n// ─── Universal fallback — used when a skin has no shaderMotion ────────────────\n// The energy arc of the sun: slow at night, rising through dawn,\n// peaking near noon/sunset, unwinding through dusk.\n\nexport const UNIVERSAL_PHASE_MOTION: MotionProfile = {\n midnight: { distortion: 0.2, swirl: 0.04, speed: 0.1, grainOverlay: 0.18 },\n night: { distortion: 0.28, swirl: 0.06, speed: 0.15, grainOverlay: 0.15 },\n dawn: { distortion: 0.52, swirl: 0.14, speed: 0.26, grainOverlay: 0.1 },\n sunrise: { distortion: 0.68, swirl: 0.22, speed: 0.38, grainOverlay: 0.07 },\n morning: { distortion: 0.54, swirl: 0.13, speed: 0.5, grainOverlay: 0.05 },\n 'solar-noon': { distortion: 0.36, swirl: 0.07, speed: 0.64, grainOverlay: 0.04 },\n afternoon: { distortion: 0.44, swirl: 0.1, speed: 0.5, grainOverlay: 0.05 },\n sunset: { distortion: 0.72, swirl: 0.3, speed: 0.34, grainOverlay: 0.08 },\n dusk: { distortion: 0.44, swirl: 0.18, speed: 0.2, grainOverlay: 0.14 },\n};\n\n// ─── Per-skin motion profiles ─────────────────────────────────────────────────\n\n// VOID — barely alive. The orb is the only presence; the background should\n// not compete. Maximum grain at midnight because pure black has texture.\nconst VOID_MOTION: MotionProfile = {\n midnight: { distortion: 0.08, swirl: 0.01, speed: 0.04, grainOverlay: 0.28 },\n night: { distortion: 0.1, swirl: 0.02, speed: 0.05, grainOverlay: 0.22 },\n dawn: { distortion: 0.14, swirl: 0.03, speed: 0.07, grainOverlay: 0.16 },\n sunrise: { distortion: 0.16, swirl: 0.04, speed: 0.09, grainOverlay: 0.12 },\n morning: { distortion: 0.14, swirl: 0.03, speed: 0.1, grainOverlay: 0.1 },\n 'solar-noon': { distortion: 0.1, swirl: 0.02, speed: 0.12, grainOverlay: 0.08 },\n afternoon: { distortion: 0.12, swirl: 0.03, speed: 0.1, grainOverlay: 0.1 },\n sunset: { distortion: 0.18, swirl: 0.05, speed: 0.08, grainOverlay: 0.14 },\n dusk: { distortion: 0.12, swirl: 0.03, speed: 0.06, grainOverlay: 0.2 },\n};\n\n// PARCHMENT — a document doesn't move. speed=0 everywhere.\n// The phase shift is expressed only through the CSS color transition.\nconst PARCHMENT_MOTION: MotionProfile = {\n midnight: { distortion: 0.0, swirl: 0.0, speed: 0.0, grainOverlay: 0.06 },\n night: { distortion: 0.0, swirl: 0.0, speed: 0.0, grainOverlay: 0.05 },\n dawn: { distortion: 0.0, swirl: 0.0, speed: 0.0, grainOverlay: 0.04 },\n sunrise: { distortion: 0.0, swirl: 0.0, speed: 0.0, grainOverlay: 0.03 },\n morning: { distortion: 0.0, swirl: 0.0, speed: 0.0, grainOverlay: 0.02 },\n 'solar-noon': { distortion: 0.0, swirl: 0.0, speed: 0.0, grainOverlay: 0.02 },\n afternoon: { distortion: 0.0, swirl: 0.0, speed: 0.0, grainOverlay: 0.02 },\n sunset: { distortion: 0.0, swirl: 0.0, speed: 0.0, grainOverlay: 0.03 },\n dusk: { distortion: 0.0, swirl: 0.0, speed: 0.0, grainOverlay: 0.05 },\n};\n\n// SIGNAL — brutalist terminal. No organic drift. Raw grain, mechanical.\n// Distortion is very low (pixels don't flow in a terminal).\n// Grain is high — CRT texture, not smoothness.\nconst SIGNAL_MOTION: MotionProfile = {\n midnight: { distortion: 0.06, swirl: 0.0, speed: 0.06, grainOverlay: 0.32 },\n night: { distortion: 0.06, swirl: 0.0, speed: 0.06, grainOverlay: 0.3 },\n dawn: { distortion: 0.08, swirl: 0.01, speed: 0.08, grainOverlay: 0.26 },\n sunrise: { distortion: 0.08, swirl: 0.01, speed: 0.08, grainOverlay: 0.24 },\n morning: { distortion: 0.1, swirl: 0.01, speed: 0.1, grainOverlay: 0.22 },\n 'solar-noon': { distortion: 0.1, swirl: 0.01, speed: 0.1, grainOverlay: 0.2 },\n afternoon: { distortion: 0.1, swirl: 0.01, speed: 0.1, grainOverlay: 0.22 },\n sunset: { distortion: 0.08, swirl: 0.01, speed: 0.08, grainOverlay: 0.26 },\n dusk: { distortion: 0.06, swirl: 0.0, speed: 0.06, grainOverlay: 0.3 },\n};\n\n// MERIDIAN — clean and airy. Constant gentle drift. Never dramatic.\n// Half the universal values across all phases.\nconst MERIDIAN_MOTION: MotionProfile = {\n midnight: { distortion: 0.12, swirl: 0.02, speed: 0.08, grainOverlay: 0.08 },\n night: { distortion: 0.14, swirl: 0.03, speed: 0.1, grainOverlay: 0.07 },\n dawn: { distortion: 0.24, swirl: 0.06, speed: 0.14, grainOverlay: 0.05 },\n sunrise: { distortion: 0.3, swirl: 0.09, speed: 0.2, grainOverlay: 0.04 },\n morning: { distortion: 0.26, swirl: 0.06, speed: 0.26, grainOverlay: 0.03 },\n 'solar-noon': { distortion: 0.18, swirl: 0.04, speed: 0.32, grainOverlay: 0.02 },\n afternoon: { distortion: 0.22, swirl: 0.05, speed: 0.26, grainOverlay: 0.03 },\n sunset: { distortion: 0.32, swirl: 0.12, speed: 0.18, grainOverlay: 0.04 },\n dusk: { distortion: 0.22, swirl: 0.08, speed: 0.12, grainOverlay: 0.06 },\n};\n\n// SUNDIAL — ancient, measured. The gnomon shadow moves once per hour.\n// Low speed, low swirl. Stone grain.\nconst SUNDIAL_MOTION: MotionProfile = {\n midnight: { distortion: 0.18, swirl: 0.03, speed: 0.07, grainOverlay: 0.2 },\n night: { distortion: 0.2, swirl: 0.04, speed: 0.08, grainOverlay: 0.18 },\n dawn: { distortion: 0.28, swirl: 0.07, speed: 0.14, grainOverlay: 0.12 },\n sunrise: { distortion: 0.34, swirl: 0.1, speed: 0.18, grainOverlay: 0.09 },\n morning: { distortion: 0.28, swirl: 0.06, speed: 0.22, grainOverlay: 0.08 },\n 'solar-noon': { distortion: 0.2, swirl: 0.04, speed: 0.28, grainOverlay: 0.06 },\n afternoon: { distortion: 0.24, swirl: 0.05, speed: 0.22, grainOverlay: 0.07 },\n sunset: { distortion: 0.36, swirl: 0.14, speed: 0.16, grainOverlay: 0.1 },\n dusk: { distortion: 0.24, swirl: 0.08, speed: 0.1, grainOverlay: 0.14 },\n};\n\n// PAPER — uncoated stock breathes. Organic, unhurried. Medium everything.\n// Never dramatic, never static. Grain increases slightly at night.\nconst PAPER_MOTION: MotionProfile = {\n midnight: { distortion: 0.22, swirl: 0.04, speed: 0.1, grainOverlay: 0.22 },\n night: { distortion: 0.26, swirl: 0.06, speed: 0.14, grainOverlay: 0.18 },\n dawn: { distortion: 0.38, swirl: 0.1, speed: 0.22, grainOverlay: 0.12 },\n sunrise: { distortion: 0.46, swirl: 0.14, speed: 0.3, grainOverlay: 0.09 },\n morning: { distortion: 0.38, swirl: 0.09, speed: 0.38, grainOverlay: 0.07 },\n 'solar-noon': { distortion: 0.28, swirl: 0.05, speed: 0.46, grainOverlay: 0.05 },\n afternoon: { distortion: 0.34, swirl: 0.07, speed: 0.38, grainOverlay: 0.06 },\n sunset: { distortion: 0.5, swirl: 0.2, speed: 0.26, grainOverlay: 0.1 },\n dusk: { distortion: 0.34, swirl: 0.12, speed: 0.16, grainOverlay: 0.14 },\n};\n\n// FOUNDRY — the reference skin. Full universal energy arc.\n// Rich, heavy, machined. Slightly more swirl than the bare universal.\nconst FOUNDRY_MOTION: MotionProfile = {\n midnight: { distortion: 0.22, swirl: 0.04, speed: 0.1, grainOverlay: 0.16 },\n night: { distortion: 0.3, swirl: 0.07, speed: 0.16, grainOverlay: 0.13 },\n dawn: { distortion: 0.55, swirl: 0.16, speed: 0.28, grainOverlay: 0.09 },\n sunrise: { distortion: 0.7, swirl: 0.26, speed: 0.4, grainOverlay: 0.06 },\n morning: { distortion: 0.56, swirl: 0.15, speed: 0.52, grainOverlay: 0.04 },\n 'solar-noon': { distortion: 0.38, swirl: 0.08, speed: 0.66, grainOverlay: 0.03 },\n afternoon: { distortion: 0.46, swirl: 0.12, speed: 0.52, grainOverlay: 0.04 },\n sunset: { distortion: 0.74, swirl: 0.34, speed: 0.36, grainOverlay: 0.07 },\n dusk: { distortion: 0.46, swirl: 0.2, speed: 0.22, grainOverlay: 0.12 },\n};\n\n// MINERAL — gemstones refract, they don't flow. Low distortion, moderate swirl\n// (internal scattering), medium speed. Grain is very low — facets are sharp.\nconst MINERAL_MOTION: MotionProfile = {\n midnight: { distortion: 0.14, swirl: 0.1, speed: 0.12, grainOverlay: 0.06 },\n night: { distortion: 0.16, swirl: 0.12, speed: 0.14, grainOverlay: 0.05 },\n dawn: { distortion: 0.22, swirl: 0.18, speed: 0.22, grainOverlay: 0.04 },\n sunrise: { distortion: 0.28, swirl: 0.24, speed: 0.32, grainOverlay: 0.03 },\n morning: { distortion: 0.22, swirl: 0.18, speed: 0.4, grainOverlay: 0.02 },\n 'solar-noon': { distortion: 0.16, swirl: 0.14, speed: 0.5, grainOverlay: 0.02 },\n afternoon: { distortion: 0.2, swirl: 0.16, speed: 0.4, grainOverlay: 0.02 },\n sunset: { distortion: 0.3, swirl: 0.28, speed: 0.28, grainOverlay: 0.03 },\n dusk: { distortion: 0.2, swirl: 0.2, speed: 0.18, grainOverlay: 0.04 },\n};\n\n// TIDE — the sea rolls. High distortion at all phases (waves never stop).\n// Speed follows a tidal rhythm: slower at dead of night, building through dawn.\n// Grain is very low — water is clear, not grainy.\nconst TIDE_MOTION: MotionProfile = {\n midnight: { distortion: 0.5, swirl: 0.08, speed: 0.18, grainOverlay: 0.03 },\n night: { distortion: 0.54, swirl: 0.1, speed: 0.22, grainOverlay: 0.03 },\n dawn: { distortion: 0.64, swirl: 0.16, speed: 0.34, grainOverlay: 0.02 },\n sunrise: { distortion: 0.72, swirl: 0.2, speed: 0.44, grainOverlay: 0.02 },\n morning: { distortion: 0.68, swirl: 0.16, speed: 0.54, grainOverlay: 0.01 },\n 'solar-noon': { distortion: 0.6, swirl: 0.12, speed: 0.62, grainOverlay: 0.01 },\n afternoon: { distortion: 0.64, swirl: 0.14, speed: 0.54, grainOverlay: 0.01 },\n sunset: { distortion: 0.74, swirl: 0.24, speed: 0.4, grainOverlay: 0.02 },\n dusk: { distortion: 0.6, swirl: 0.18, speed: 0.28, grainOverlay: 0.03 },\n};\n\n// AURORA — the most variable. At midnight/night the lights dance at full\n// intensity. During the day the aurora is invisible — calm, warm sky.\n// At dusk/sunset the bands return. Grain is very low — aurora is luminous.\nconst AURORA_MOTION: MotionProfile = {\n midnight: { distortion: 0.8, swirl: 0.44, speed: 0.7, grainOverlay: 0.02 },\n night: { distortion: 0.86, swirl: 0.5, speed: 0.8, grainOverlay: 0.02 },\n dawn: { distortion: 0.62, swirl: 0.32, speed: 0.52, grainOverlay: 0.03 },\n sunrise: { distortion: 0.38, swirl: 0.16, speed: 0.3, grainOverlay: 0.04 },\n morning: { distortion: 0.18, swirl: 0.05, speed: 0.18, grainOverlay: 0.04 },\n 'solar-noon': { distortion: 0.14, swirl: 0.03, speed: 0.14, grainOverlay: 0.03 },\n afternoon: { distortion: 0.18, swirl: 0.05, speed: 0.18, grainOverlay: 0.04 },\n sunset: { distortion: 0.46, swirl: 0.24, speed: 0.42, grainOverlay: 0.03 },\n dusk: { distortion: 0.68, swirl: 0.38, speed: 0.6, grainOverlay: 0.02 },\n};\n\n// ─── Skin → profile map ───────────────────────────────────────────────────────\n\nexport const SKIN_MOTION_PROFILES: Record<DesignMode, MotionProfile> = {\n void: VOID_MOTION,\n parchment: PARCHMENT_MOTION,\n signal: SIGNAL_MOTION,\n meridian: MERIDIAN_MOTION,\n sundial: SUNDIAL_MOTION,\n paper: PAPER_MOTION,\n foundry: FOUNDRY_MOTION,\n mineral: MINERAL_MOTION,\n tide: TIDE_MOTION,\n aurora: AURORA_MOTION,\n};\n","'use client';\n// ════════════════════════════════════════════════════════════════════════════\n// FILE: components/solar-shader-bg.client.tsx\n// ════════════════════════════════════════════════════════════════════════════\n\nimport {\n createContext,\n useContext,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport type { SolarBlend, SolarPhase } from '../hooks/useSolarPosition';\nimport {\n UNIVERSAL_SEASON_MODIFIERS,\n applySeasonalModifier,\n resolveSeasonalModifier,\n} from '../lib/seasonal-blend';\nimport { lerpColor } from '../lib/solar-lerp';\nimport type { SeasonalBlend } from '../lib/useSeason';\nimport { useSolarTheme } from '../provider/solar-theme-provider';\nimport type {\n ShaderImage,\n ShaderMotion,\n ShaderPalette,\n SkinDefinition,\n} from '../skins/types/widget-skin.types';\nimport { SKIN_MOTION_PROFILES, UNIVERSAL_PHASE_MOTION } from './shader-motion-profiles';\n\n// ─── Variant context ──────────────────────────────────────────────────────────\n\nexport type ShaderVariant = 'showcase' | 'dashboard' | 'editorial';\n\nconst ShaderVariantCtx = createContext<ShaderVariant>('showcase');\n\nexport function ShaderVariantProvider({\n variant,\n children,\n}: {\n variant: ShaderVariant;\n children: React.ReactNode;\n}) {\n return <ShaderVariantCtx.Provider value={variant}>{children}</ShaderVariantCtx.Provider>;\n}\n\nexport function useShaderVariant(): ShaderVariant {\n return useContext(ShaderVariantCtx);\n}\n\n// ─── Dashboard motion modifier ────────────────────────────────────────────────\n\nconst DASHBOARD_MOTION_SCALE = {\n speed: 0.45,\n distortion: 0.5,\n swirl: 0.4,\n grainOverlay: 1.3,\n} as const;\n\nconst DASHBOARD_OPACITY_SCALE = 0.65;\n\nfunction applyDashboardMotion(m: ShaderMotion): ShaderMotion {\n return {\n speed: m.speed * DASHBOARD_MOTION_SCALE.speed,\n distortion: m.distortion * DASHBOARD_MOTION_SCALE.distortion,\n swirl: m.swirl * DASHBOARD_MOTION_SCALE.swirl,\n grainOverlay: Math.min(1, m.grainOverlay * DASHBOARD_MOTION_SCALE.grainOverlay),\n };\n}\n\n// ─── Editorial motion modifier ────────────────────────────────────────────────\n\nconst EDITORIAL_MOTION_SCALE = {\n speed: 0.3,\n distortion: 0.35,\n swirl: 0.25,\n grainOverlay: 1.4,\n} as const;\n\nconst EDITORIAL_OPACITY_SCALE = 0.35;\n\nfunction applyEditorialMotion(m: ShaderMotion): ShaderMotion {\n return {\n speed: m.speed * EDITORIAL_MOTION_SCALE.speed,\n distortion: m.distortion * EDITORIAL_MOTION_SCALE.distortion,\n swirl: m.swirl * EDITORIAL_MOTION_SCALE.swirl,\n grainOverlay: Math.min(1, m.grainOverlay * EDITORIAL_MOTION_SCALE.grainOverlay),\n };\n}\n\nfunction mixToward(hex: string, target: string, mix: number): string {\n return lerpColor(hex, target, mix);\n}\n\nfunction applyEditorialPalette(p: ShaderPalette): ShaderPalette {\n const darkBase = '#08080c';\n const darkVignette = '#030306';\n return {\n ...p,\n colors: p.colors.map((c) => mixToward(c, darkBase, 0.92)) as [string, string, string, string],\n colorBack: mixToward(p.colorBack, darkBase, 0.95),\n vignette: mixToward(p.vignette, darkVignette, 0.9),\n cssFallback: `linear-gradient(135deg, ${darkBase} 0%, #0c0c14 100%)`,\n };\n}\n\n// ─── Interpolation helpers ────────────────────────────────────────────────────\n\nfunction lerpNum(a: number, b: number, t: number): number {\n return a + (b - a) * t;\n}\n\nfunction lerpMotion(a: ShaderMotion, b: ShaderMotion, t: number): ShaderMotion {\n return {\n distortion: lerpNum(a.distortion, b.distortion, t),\n swirl: lerpNum(a.swirl, b.swirl, t),\n speed: lerpNum(a.speed, b.speed, t),\n grainOverlay: lerpNum(a.grainOverlay, b.grainOverlay, t),\n };\n}\n\nfunction lerpPalette(a: ShaderPalette, b: ShaderPalette, t: number): ShaderPalette {\n return {\n colors: a.colors.map((ca, i) => lerpColor(ca, b.colors[i] ?? ca, t)) as [\n string,\n string,\n string,\n string,\n ],\n colorBack: lerpColor(a.colorBack, b.colorBack, t),\n opacity: lerpNum(a.opacity, b.opacity, t),\n vignette: lerpColor(a.vignette, b.vignette, t),\n cssFallback: a.cssFallback,\n image: a.image,\n };\n}\n\n// ─── Motion profile resolution ────────────────────────────────────────────────\n\nfunction resolveMotionProfile(skin: SkinDefinition): Record<SolarPhase, ShaderMotion> {\n if (skin.shaderMotion) return skin.shaderMotion;\n return SKIN_MOTION_PROFILES[skin.id] ?? UNIVERSAL_PHASE_MOTION;\n}\n\n// ─── Image config resolution ──────────────────────────────────────────────────\n\nfunction resolveImage(skin: SkinDefinition, palette: ShaderPalette): ShaderImage | undefined {\n return palette.image ?? skin.defaultImage;\n}\n\n// ─── Core config computation ──────────────────────────────────────────────────\n\nfunction computeConfig(\n skin: SkinDefinition,\n blend: SolarBlend,\n seasonal?: { blend: SeasonalBlend; disabled: boolean },\n) {\n const motionProfile = resolveMotionProfile(skin);\n const { phase, nextPhase, t } = blend;\n let palette = lerpPalette(skin.shaderPalettes[phase], skin.shaderPalettes[nextPhase], t);\n\n if (seasonal && !seasonal.disabled) {\n const mod = resolveSeasonalModifier(seasonal.blend, {\n ...UNIVERSAL_SEASON_MODIFIERS,\n ...skin.seasonalModifiers,\n });\n palette = applySeasonalModifier(palette, mod);\n }\n\n return {\n palette,\n motion: lerpMotion(motionProfile[phase], motionProfile[nextPhase], t),\n };\n}\n\n// ─── Hex → RGB helper ─────────────────────────────────────────────────────────\n\nfunction hexToRgb(hex: string): [number, number, number] {\n const result = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(hex);\n return result\n ? [\n Number.parseInt(result[1], 16) / 255,\n Number.parseInt(result[2], 16) / 255,\n Number.parseInt(result[3], 16) / 255,\n ]\n : [0, 0, 0];\n}\n\n// ─── WebGL SolarFlare shader ──────────────────────────────────────────────────\n// Inspired by a radiant solar flare effect. Uses the skin's 4 shader palette\n// colors as a blended flare color against the colorBack background.\n// Motion profile drives speed, intensity, spread and pulse.\n\nconst VS_SOURCE = `#version 300 es\n in vec2 a_position;\n void main() {\n gl_Position = vec4(a_position, 0.0, 1.0);\n }\n`;\n\nconst FS_SOURCE = `#version 300 es\n precision highp float;\n\n uniform vec2 r; // resolution\n uniform float t; // time\n uniform vec3 u_bg; // background color (colorBack)\n uniform vec3 u_c0; // palette color 0\n uniform vec3 u_c1; // palette color 1\n uniform vec3 u_c2; // palette color 2\n uniform vec3 u_c3; // palette color 3\n uniform float u_intensity;\n uniform float u_spread;\n uniform float u_pulseRate;\n uniform float u_speed;\n uniform float u_grain;\n\n out vec4 o;\n\n // Hash for grain\n float hash(vec2 p) {\n return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);\n }\n\n void main() {\n vec4 FC = gl_FragCoord;\n vec2 p = (FC.xy * 2. - r) / r.y;\n\n // Average all 4 palette colors\n vec3 avgColor = (u_c0 + u_c1 + u_c2 + u_c3) * 0.25;\n\n // Normalize to a fixed luminance so the flare shape is identical\n // across all skins regardless of how bright/dark the palette is.\n // Dark palettes (Void midnight ≈ 0.01) and bright palettes\n // (Paper morning ≈ 0.95) both produce the same-sized sun.\n float lum = dot(avgColor, vec3(0.299, 0.587, 0.114));\n vec3 flareColor = lum > 0.001\n ? avgColor * (0.30 / lum) // rescale to target luminance 0.30\n : vec3(0.30); // neutral grey fallback\n\n // Solar flare radiance — matches the reference SolarFlare shader.\n // length(p) creates a circular distance field from center.\n // The exp(mod(dot(...))) creates alive, pulsing texture inside the sun.\n float l = u_intensity - length(p);\n\n o = tanh(\n vec4(flareColor, 0.0)\n / max(l, -l * u_spread)\n / exp(\n mod(dot(FC, sin(FC.yxyx)) + t * u_speed, 2.0)\n + sin(t * u_speed + sin(t * u_speed / u_pulseRate + p.y))\n )\n );\n\n // Film grain\n float grain = (hash(FC.xy + fract(t)) - 0.5) * u_grain;\n\n // Composite: background + flare glow + grain\n o = vec4(u_bg + o.rgb + grain, 1.0);\n }\n`;\n\ninterface SolarFlareCanvasProps {\n backgroundColor: string;\n colors: [string, string, string, string];\n speed: number;\n intensity: number;\n spread: number;\n pulseRate: number;\n grain: number;\n}\n\nfunction SolarFlareCanvas({\n backgroundColor,\n colors,\n speed,\n intensity,\n spread,\n pulseRate,\n grain,\n}: SolarFlareCanvasProps) {\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const animationRef = useRef<number | null>(null);\n\n const configRef = useRef({\n backgroundColor,\n colors,\n speed,\n intensity,\n spread,\n pulseRate,\n grain,\n });\n\n useEffect(() => {\n configRef.current = {\n backgroundColor,\n colors,\n speed,\n intensity,\n spread,\n pulseRate,\n grain,\n };\n }, [backgroundColor, colors, speed, intensity, spread, pulseRate, grain]);\n\n // useLayoutEffect ensures WebGL context is created and the first frame is\n // drawn *before* the browser paints, preventing a blank-canvas flash on\n // client-side navigation remounts.\n useLayoutEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n const gl = canvas.getContext('webgl2');\n if (!gl) return;\n\n const createShader = (type: number, source: string) => {\n const shader = gl.createShader(type);\n if (!shader) return null;\n gl.shaderSource(shader, source);\n gl.compileShader(shader);\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n gl.deleteShader(shader);\n return null;\n }\n return shader;\n };\n\n const vs = createShader(gl.VERTEX_SHADER, VS_SOURCE);\n const fs = createShader(gl.FRAGMENT_SHADER, FS_SOURCE);\n if (!vs || !fs) return;\n\n const program = gl.createProgram();\n if (!program) return;\n\n gl.attachShader(program, vs);\n gl.attachShader(program, fs);\n gl.linkProgram(program);\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) return;\n\n const positionBuffer = gl.createBuffer();\n gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);\n gl.bufferData(\n gl.ARRAY_BUFFER,\n new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]),\n gl.STATIC_DRAW,\n );\n\n const posLoc = gl.getAttribLocation(program, 'a_position');\n gl.enableVertexAttribArray(posLoc);\n gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);\n\n const loc = {\n r: gl.getUniformLocation(program, 'r'),\n t: gl.getUniformLocation(program, 't'),\n bg: gl.getUniformLocation(program, 'u_bg'),\n c0: gl.getUniformLocation(program, 'u_c0'),\n c1: gl.getUniformLocation(program, 'u_c1'),\n c2: gl.getUniformLocation(program, 'u_c2'),\n c3: gl.getUniformLocation(program, 'u_c3'),\n intensity: gl.getUniformLocation(program, 'u_intensity'),\n spread: gl.getUniformLocation(program, 'u_spread'),\n pulseRate: gl.getUniformLocation(program, 'u_pulseRate'),\n speed: gl.getUniformLocation(program, 'u_speed'),\n grain: gl.getUniformLocation(program, 'u_grain'),\n };\n\n const resizeCanvas = () => {\n const dpr = window.devicePixelRatio || 1;\n canvas.width = Math.round(canvas.offsetWidth * dpr);\n canvas.height = Math.round(canvas.offsetHeight * dpr);\n gl.viewport(0, 0, canvas.width, canvas.height);\n };\n\n window.addEventListener('resize', resizeCanvas);\n resizeCanvas();\n\n const startTime = performance.now();\n\n const render = () => {\n const cfg = configRef.current;\n gl.useProgram(program);\n\n gl.uniform2f(loc.r, canvas.width, canvas.height);\n gl.uniform1f(loc.t, (performance.now() - startTime) / 1000);\n\n const bg = hexToRgb(cfg.backgroundColor);\n gl.uniform3f(loc.bg, bg[0], bg[1], bg[2]);\n\n const c0 = hexToRgb(cfg.colors[0]);\n const c1 = hexToRgb(cfg.colors[1]);\n const c2 = hexToRgb(cfg.colors[2]);\n const c3 = hexToRgb(cfg.colors[3]);\n gl.uniform3f(loc.c0, c0[0], c0[1], c0[2]);\n gl.uniform3f(loc.c1, c1[0], c1[1], c1[2]);\n gl.uniform3f(loc.c2, c2[0], c2[1], c2[2]);\n gl.uniform3f(loc.c3, c3[0], c3[1], c3[2]);\n\n gl.uniform1f(loc.intensity, cfg.intensity);\n gl.uniform1f(loc.spread, cfg.spread);\n gl.uniform1f(loc.pulseRate, cfg.pulseRate);\n gl.uniform1f(loc.speed, cfg.speed);\n gl.uniform1f(loc.grain, cfg.grain);\n\n gl.drawArrays(gl.TRIANGLES, 0, 6);\n animationRef.current = requestAnimationFrame(render);\n };\n\n render();\n\n return () => {\n window.removeEventListener('resize', resizeCanvas);\n if (animationRef.current) cancelAnimationFrame(animationRef.current);\n gl.deleteProgram(program);\n gl.deleteShader(vs);\n gl.deleteShader(fs);\n gl.deleteBuffer(positionBuffer);\n };\n }, []);\n\n return (\n <canvas\n ref={canvasRef}\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n width: '100%',\n height: '100%',\n }}\n />\n );\n}\n\n// ─── Client-side mount tracking ───────────────────────────────────────────────\n// After the very first hydration, any subsequent mount is a client-side navigation\n// remount — no SSR/hydration mismatch is possible, so the canvas can render\n// immediately without the `mounted` gate (which would cause a one-frame blink).\nlet _hasHydrated = false;\n\n// ─── Props ────────────────────────────────────────────────────────────────────\n\nexport interface SolarShaderBgProps {\n skinOverride?: SkinDefinition;\n blendOverride?: SolarBlend;\n opacityOverride?: number;\n variant?: ShaderVariant;\n className?: string;\n style?: React.CSSProperties;\n}\n\n// ─── SolarShaderBg ────────────────────────────────────────────────────────────\n\nexport function SolarShaderBg({\n skinOverride,\n blendOverride,\n opacityOverride,\n variant: variantProp,\n className,\n style,\n}: SolarShaderBgProps = {}) {\n const theme = useSolarTheme();\n const { activeSkin, blend: contextBlend, seasonalBlend } = theme;\n const contextVariant = useShaderVariant();\n\n const skin = skinOverride ?? activeSkin;\n const blend = blendOverride ?? contextBlend;\n const variant = variantProp ?? contextVariant;\n const seasonal = { blend: seasonalBlend, disabled: false };\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: intentional fine-grained deps — blend object reference changes every render; subscribing to specific fields avoids thrashing computeConfig\n const { palette, motion: rawMotion } = useMemo(\n () => computeConfig(skin, blend, seasonal),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [blend.phase, blend.nextPhase, blend.t, skin, seasonalBlend.season, seasonalBlend.t],\n );\n\n const variantPalette = variant === 'editorial' ? applyEditorialPalette(palette) : palette;\n\n const shaderMotion =\n variant === 'dashboard'\n ? applyDashboardMotion(rawMotion)\n : variant === 'editorial'\n ? applyEditorialMotion(rawMotion)\n : rawMotion;\n\n const resolvedOpacity =\n opacityOverride ??\n (variant === 'dashboard'\n ? variantPalette.opacity * DASHBOARD_OPACITY_SCALE\n : variant === 'editorial'\n ? variantPalette.opacity * EDITORIAL_OPACITY_SCALE\n : variantPalette.opacity);\n\n const imageConfig = resolveImage(skin, variantPalette);\n\n // Gate dynamic layers behind a client-only flag to avoid hydration mismatches.\n // On the very first mount (SSR hydration), start as false and flip via effect.\n // On subsequent mounts (client-side navigation), start as true immediately\n // so the WebGL canvas renders without a one-frame blink.\n const [mounted, setMounted] = useState(_hasHydrated);\n useLayoutEffect(() => {\n _hasHydrated = true;\n setMounted(true);\n }, []);\n\n useEffect(() => {\n document.documentElement.style.setProperty('--solar-shader-bg', variantPalette.colorBack);\n return () => {\n document.documentElement.style.removeProperty('--solar-shader-bg');\n };\n }, [variantPalette.colorBack]);\n\n // Map shader motion → SolarFlare parameters\n // intensity ≥ 3.5 ensures the radial edge is always off-screen (max corner\n // distance in normalised coords is ~1.15), so the glow fills the entire\n // viewport as an atmospheric wash — no visible \"sun\" circle.\n const flareSpeed = 0.6 + shaderMotion.speed * 0.8;\n const flareIntensity = 3.5 + shaderMotion.distortion * 0.5;\n const flareSpread = 8.0 + shaderMotion.swirl * 8.0;\n const flarePulseRate = 0.4 + (1.0 - shaderMotion.swirl) * 0.5;\n const flareGrain = shaderMotion.grainOverlay * 0.12;\n\n return (\n <div\n className={className}\n suppressHydrationWarning\n style={{\n position: 'absolute',\n inset: 0,\n overflow: 'hidden',\n background: variantPalette.cssFallback || variantPalette.colorBack,\n ...style,\n }}\n >\n {mounted && (\n <>\n {/* ── Layer 1: WebGL SolarFlare (renders instantly — no flash) ── */}\n <div className=\"absolute inset-0\" style={{ opacity: resolvedOpacity }}>\n <SolarFlareCanvas\n backgroundColor={variantPalette.colorBack}\n colors={variantPalette.colors}\n speed={flareSpeed}\n intensity={flareIntensity}\n spread={flareSpread}\n pulseRate={flarePulseRate}\n grain={flareGrain}\n />\n </div>\n\n {/* ── Layer 2: Atmospheric image ──────────────────────────────── */}\n {imageConfig && (\n <div\n className=\"absolute inset-0\"\n aria-hidden\n style={{\n opacity: imageConfig.opacity ?? 0.18,\n pointerEvents: 'none',\n background: 'var(--solar-accent)',\n WebkitMaskImage: `url(${imageConfig.src})`,\n maskImage: `url(${imageConfig.src})`,\n WebkitMaskSize: 'cover',\n maskSize: 'cover',\n WebkitMaskPosition: imageConfig.objectPosition ?? 'center center',\n maskPosition: imageConfig.objectPosition ?? 'center center',\n WebkitMaskRepeat: 'no-repeat',\n maskRepeat: 'no-repeat',\n }}\n />\n )}\n\n {/* ── Layer 3: Vignette ───────────────────────────────────────── */}\n <div\n className=\"absolute inset-0\"\n aria-hidden\n style={{\n pointerEvents: 'none',\n background: `radial-gradient(ellipse 85% 70% at 50% 50%, transparent 0%, ${variantPalette.vignette} 100%)`,\n }}\n />\n </>\n )}\n </div>\n );\n}\n\n// ─── SolarShaderBgFull ────────────────────────────────────────────────────────\n\nexport function SolarShaderBgFull(props: SolarShaderBgProps = {}) {\n return <SolarShaderBg {...props} style={{ zIndex: 0, ...props.style }} />;\n}\n\n// ─── useSolarShaderConfig ─────────────────────────────────────────────────────\n\nexport function useSolarShaderConfig(\n opts: {\n skinOverride?: SkinDefinition;\n blendOverride?: SolarBlend;\n } = {},\n) {\n const { activeSkin, blend: contextBlend, seasonalBlend } = useSolarTheme();\n const skin = opts.skinOverride ?? activeSkin;\n const blend = opts.blendOverride ?? contextBlend;\n const seasonal = { blend: seasonalBlend, disabled: false };\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: intentional fine-grained deps — blend object reference changes every render; subscribing to specific fields avoids thrashing computeConfig\n return useMemo(\n () => computeConfig(skin, blend, seasonal),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [blend.phase, blend.nextPhase, blend.t, skin, seasonalBlend.season, seasonalBlend.t],\n );\n}\n"],"mappings":"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"}
|
|
@@ -497,7 +497,7 @@ var require_tz = /* @__PURE__ */ __commonJS({ "node_modules/tz-lookup/tz.js": ((
|
|
|
497
497
|
const RAD = Math.PI / 180;
|
|
498
498
|
const DEG = 180 / Math.PI;
|
|
499
499
|
/** Day of year (1–366) */
|
|
500
|
-
function dayOfYear(date) {
|
|
500
|
+
function dayOfYear$1(date) {
|
|
501
501
|
const start = new Date(date.getFullYear(), 0, 0);
|
|
502
502
|
const diff = date.getTime() - start.getTime();
|
|
503
503
|
return Math.floor(diff / 864e5);
|
|
@@ -548,7 +548,7 @@ function riseSetMinutes(latRad, declRad, altitudeDeg, longitudeDeg, utcOffsetMin
|
|
|
548
548
|
* Compute full solar position for a given date + location
|
|
549
549
|
*/
|
|
550
550
|
function computeSolarPosition(date, latitudeDeg, longitudeDeg, utcOffsetMinutes) {
|
|
551
|
-
const { eot, decl } = equationOfTimeAndDeclination(dayOfYear(date));
|
|
551
|
+
const { eot, decl } = equationOfTimeAndDeclination(dayOfYear$1(date));
|
|
552
552
|
const latRad = latitudeDeg * RAD;
|
|
553
553
|
const declRad = decl * RAD;
|
|
554
554
|
const localMinutes = date.getHours() * 60 + date.getMinutes() + date.getSeconds() / 60;
|
|
@@ -6818,6 +6818,172 @@ function injectWidgetCSS() {
|
|
|
6818
6818
|
injected = true;
|
|
6819
6819
|
}
|
|
6820
6820
|
|
|
6821
|
+
//#endregion
|
|
6822
|
+
//#region src/lib/seasonal-blend.ts
|
|
6823
|
+
const IDENTITY_MODIFIER = {
|
|
6824
|
+
saturationScale: 1,
|
|
6825
|
+
lightnessShift: 0,
|
|
6826
|
+
hueRotateDeg: 0,
|
|
6827
|
+
tintStrength: 0
|
|
6828
|
+
};
|
|
6829
|
+
const UNIVERSAL_SEASON_MODIFIERS = {
|
|
6830
|
+
spring: {
|
|
6831
|
+
saturationScale: 1.1,
|
|
6832
|
+
lightnessShift: .02,
|
|
6833
|
+
hueRotateDeg: 8,
|
|
6834
|
+
tintColor: "#a8d8a0",
|
|
6835
|
+
tintStrength: .06
|
|
6836
|
+
},
|
|
6837
|
+
summer: {
|
|
6838
|
+
saturationScale: 1.18,
|
|
6839
|
+
lightnessShift: .03,
|
|
6840
|
+
hueRotateDeg: 10,
|
|
6841
|
+
tintColor: "#ffe066",
|
|
6842
|
+
tintStrength: .07
|
|
6843
|
+
},
|
|
6844
|
+
autumn: {
|
|
6845
|
+
saturationScale: .9,
|
|
6846
|
+
lightnessShift: -.03,
|
|
6847
|
+
hueRotateDeg: -18,
|
|
6848
|
+
tintColor: "#c8692a",
|
|
6849
|
+
tintStrength: .1
|
|
6850
|
+
},
|
|
6851
|
+
winter: {
|
|
6852
|
+
saturationScale: .82,
|
|
6853
|
+
lightnessShift: -.04,
|
|
6854
|
+
hueRotateDeg: -25,
|
|
6855
|
+
tintColor: "#8ab4d4",
|
|
6856
|
+
tintStrength: .08
|
|
6857
|
+
}
|
|
6858
|
+
};
|
|
6859
|
+
function hexToRgb$2(hex$1) {
|
|
6860
|
+
const clean = hex$1.replace("#", "");
|
|
6861
|
+
return [
|
|
6862
|
+
Number.parseInt(clean.slice(0, 2), 16) / 255,
|
|
6863
|
+
Number.parseInt(clean.slice(2, 4), 16) / 255,
|
|
6864
|
+
Number.parseInt(clean.slice(4, 6), 16) / 255
|
|
6865
|
+
];
|
|
6866
|
+
}
|
|
6867
|
+
function rgbToHsl(r, g, b) {
|
|
6868
|
+
const max = Math.max(r, g, b);
|
|
6869
|
+
const min = Math.min(r, g, b);
|
|
6870
|
+
const l = (max + min) / 2;
|
|
6871
|
+
let h = 0;
|
|
6872
|
+
let s = 0;
|
|
6873
|
+
if (max !== min) {
|
|
6874
|
+
const d = max - min;
|
|
6875
|
+
s = l > .5 ? d / (2 - max - min) : d / (max + min);
|
|
6876
|
+
switch (max) {
|
|
6877
|
+
case r:
|
|
6878
|
+
h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
|
|
6879
|
+
break;
|
|
6880
|
+
case g:
|
|
6881
|
+
h = ((b - r) / d + 2) / 6;
|
|
6882
|
+
break;
|
|
6883
|
+
case b:
|
|
6884
|
+
h = ((r - g) / d + 4) / 6;
|
|
6885
|
+
break;
|
|
6886
|
+
}
|
|
6887
|
+
}
|
|
6888
|
+
return [
|
|
6889
|
+
h,
|
|
6890
|
+
s,
|
|
6891
|
+
l
|
|
6892
|
+
];
|
|
6893
|
+
}
|
|
6894
|
+
function hslToRgb(h, s, l) {
|
|
6895
|
+
if (s === 0) return [
|
|
6896
|
+
l,
|
|
6897
|
+
l,
|
|
6898
|
+
l
|
|
6899
|
+
];
|
|
6900
|
+
const hue2rgb = (p$1, q$1, _t) => {
|
|
6901
|
+
let t = _t;
|
|
6902
|
+
if (t < 0) t += 1;
|
|
6903
|
+
if (t > 1) t -= 1;
|
|
6904
|
+
if (t < 1 / 6) return p$1 + (q$1 - p$1) * 6 * t;
|
|
6905
|
+
if (t < 1 / 2) return q$1;
|
|
6906
|
+
if (t < 2 / 3) return p$1 + (q$1 - p$1) * (2 / 3 - t) * 6;
|
|
6907
|
+
return p$1;
|
|
6908
|
+
};
|
|
6909
|
+
const q = l < .5 ? l * (1 + s) : l + s - l * s;
|
|
6910
|
+
const p = 2 * l - q;
|
|
6911
|
+
return [
|
|
6912
|
+
hue2rgb(p, q, h + 1 / 3),
|
|
6913
|
+
hue2rgb(p, q, h),
|
|
6914
|
+
hue2rgb(p, q, h - 1 / 3)
|
|
6915
|
+
];
|
|
6916
|
+
}
|
|
6917
|
+
function rgbToHex(r, g, b) {
|
|
6918
|
+
const toHex = (x) => Math.round(Math.max(0, Math.min(1, x)) * 255).toString(16).padStart(2, "0");
|
|
6919
|
+
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
6920
|
+
}
|
|
6921
|
+
function lerpNum$6(a, b, t) {
|
|
6922
|
+
return a + (b - a) * t;
|
|
6923
|
+
}
|
|
6924
|
+
/**
|
|
6925
|
+
* Apply a SeasonalModifier to a single hex color.
|
|
6926
|
+
* Works in HSL space for hue rotation + saturation + lightness,
|
|
6927
|
+
* then blends toward tintColor if provided.
|
|
6928
|
+
*/
|
|
6929
|
+
function shiftColor(hex$1, mod) {
|
|
6930
|
+
if (!hex$1 || hex$1.length < 7) return hex$1;
|
|
6931
|
+
const [r, g, b] = hexToRgb$2(hex$1);
|
|
6932
|
+
let [h, s, l] = rgbToHsl(r, g, b);
|
|
6933
|
+
h = ((h + mod.hueRotateDeg / 360) % 1 + 1) % 1;
|
|
6934
|
+
s = Math.max(0, Math.min(1, s * mod.saturationScale));
|
|
6935
|
+
l = Math.max(0, Math.min(1, l + mod.lightnessShift));
|
|
6936
|
+
const [sr$5, sg, sb] = hslToRgb(h, s, l);
|
|
6937
|
+
let result = rgbToHex(sr$5, sg, sb);
|
|
6938
|
+
if (mod.tintColor && mod.tintStrength > 0) {
|
|
6939
|
+
const [tr, tg, tb] = hexToRgb$2(mod.tintColor);
|
|
6940
|
+
const [fr, fg, fb] = hexToRgb$2(result);
|
|
6941
|
+
result = rgbToHex(lerpNum$6(fr, tr, mod.tintStrength), lerpNum$6(fg, tg, mod.tintStrength), lerpNum$6(fb, tb, mod.tintStrength));
|
|
6942
|
+
}
|
|
6943
|
+
return result;
|
|
6944
|
+
}
|
|
6945
|
+
/**
|
|
6946
|
+
* Linearly interpolate between two SeasonalModifiers.
|
|
6947
|
+
* Used for smooth crossfades at solstice/equinox boundaries.
|
|
6948
|
+
*/
|
|
6949
|
+
function lerpModifier(a, b, t) {
|
|
6950
|
+
return {
|
|
6951
|
+
saturationScale: lerpNum$6(a.saturationScale, b.saturationScale, t),
|
|
6952
|
+
lightnessShift: lerpNum$6(a.lightnessShift, b.lightnessShift, t),
|
|
6953
|
+
hueRotateDeg: lerpNum$6(a.hueRotateDeg, b.hueRotateDeg, t),
|
|
6954
|
+
tintStrength: lerpNum$6(a.tintStrength, b.tintStrength, t),
|
|
6955
|
+
tintColor: t < .5 ? a.tintColor : b.tintColor
|
|
6956
|
+
};
|
|
6957
|
+
}
|
|
6958
|
+
/**
|
|
6959
|
+
* Apply a SeasonalModifier to every color in a ShaderPalette.
|
|
6960
|
+
* Returns a new palette — the original is not mutated.
|
|
6961
|
+
*
|
|
6962
|
+
* The modifier shifts hue, saturation, and lightness then applies a tint wash.
|
|
6963
|
+
* The result is blended back toward the original at `strength` 0→1:
|
|
6964
|
+
* 0 = identity, 1 = fully modified.
|
|
6965
|
+
*/
|
|
6966
|
+
function applySeasonalModifier(palette, mod, strength = 1) {
|
|
6967
|
+
if (strength <= 0) return palette;
|
|
6968
|
+
const effective = strength < 1 ? lerpModifier(IDENTITY_MODIFIER, mod, strength) : mod;
|
|
6969
|
+
return {
|
|
6970
|
+
...palette,
|
|
6971
|
+
colors: palette.colors.map((c) => shiftColor(c, effective)),
|
|
6972
|
+
colorBack: shiftColor(palette.colorBack, effective),
|
|
6973
|
+
vignette: shiftColor(palette.vignette, effective)
|
|
6974
|
+
};
|
|
6975
|
+
}
|
|
6976
|
+
/**
|
|
6977
|
+
* Given a SeasonalBlend and a modifier map, return the interpolated modifier
|
|
6978
|
+
* ready to pass to applySeasonalModifier.
|
|
6979
|
+
*
|
|
6980
|
+
* @param blend The SeasonalBlend from useSeason()
|
|
6981
|
+
* @param modifiers Per-season modifier map (skin's or universal default)
|
|
6982
|
+
*/
|
|
6983
|
+
function resolveSeasonalModifier(blend, modifiers = UNIVERSAL_SEASON_MODIFIERS) {
|
|
6984
|
+
return lerpModifier(modifiers[blend.season] ?? IDENTITY_MODIFIER, modifiers[blend.nextSeason] ?? IDENTITY_MODIFIER, blend.t);
|
|
6985
|
+
}
|
|
6986
|
+
|
|
6821
6987
|
//#endregion
|
|
6822
6988
|
//#region src/lib/solar-lerp.ts
|
|
6823
6989
|
/**
|
|
@@ -7316,6 +7482,96 @@ function getBrowserTimezone() {
|
|
|
7316
7482
|
}
|
|
7317
7483
|
}
|
|
7318
7484
|
|
|
7485
|
+
//#endregion
|
|
7486
|
+
//#region src/lib/useSeason.ts
|
|
7487
|
+
/** Day-of-year for each northern hemisphere season start (approximate). */
|
|
7488
|
+
const SEASON_STARTS_NORTH = {
|
|
7489
|
+
spring: 79,
|
|
7490
|
+
summer: 172,
|
|
7491
|
+
autumn: 265,
|
|
7492
|
+
winter: 355
|
|
7493
|
+
};
|
|
7494
|
+
/** Days either side of a transition within which to apply crossfade. */
|
|
7495
|
+
const CROSSFADE_DAYS = 14;
|
|
7496
|
+
const SEASON_ORDER = [
|
|
7497
|
+
"spring",
|
|
7498
|
+
"summer",
|
|
7499
|
+
"autumn",
|
|
7500
|
+
"winter"
|
|
7501
|
+
];
|
|
7502
|
+
function dayOfYear(date) {
|
|
7503
|
+
const start = new Date(date.getFullYear(), 0, 0);
|
|
7504
|
+
const diff = date.getTime() - start.getTime();
|
|
7505
|
+
return Math.floor(diff / 864e5);
|
|
7506
|
+
}
|
|
7507
|
+
function daysInYear(year) {
|
|
7508
|
+
return new Date(year, 1, 29).getMonth() === 1 ? 366 : 365;
|
|
7509
|
+
}
|
|
7510
|
+
function computeSeasonalBlend(doy, isNorthern, totalDays) {
|
|
7511
|
+
const adjustedDoy = isNorthern ? doy : (doy + 182) % totalDays || totalDays;
|
|
7512
|
+
let currentSeason = "winter";
|
|
7513
|
+
let currentIdx = 0;
|
|
7514
|
+
for (let i$1 = SEASON_ORDER.length - 1; i$1 >= 0; i$1--) if (adjustedDoy >= SEASON_STARTS_NORTH[SEASON_ORDER[i$1]]) {
|
|
7515
|
+
currentSeason = SEASON_ORDER[i$1];
|
|
7516
|
+
currentIdx = i$1;
|
|
7517
|
+
break;
|
|
7518
|
+
}
|
|
7519
|
+
const nextSeason = SEASON_ORDER[(currentIdx + 1) % SEASON_ORDER.length];
|
|
7520
|
+
let daysToNext = SEASON_STARTS_NORTH[nextSeason] - adjustedDoy;
|
|
7521
|
+
if (daysToNext < 0) daysToNext += totalDays;
|
|
7522
|
+
let t = 0;
|
|
7523
|
+
if (daysToNext <= CROSSFADE_DAYS) {
|
|
7524
|
+
const raw = (CROSSFADE_DAYS - daysToNext) / CROSSFADE_DAYS;
|
|
7525
|
+
const clamped = Math.max(0, Math.min(1, raw));
|
|
7526
|
+
t = clamped * clamped * (3 - 2 * clamped);
|
|
7527
|
+
}
|
|
7528
|
+
return {
|
|
7529
|
+
season: currentSeason,
|
|
7530
|
+
nextSeason,
|
|
7531
|
+
t
|
|
7532
|
+
};
|
|
7533
|
+
}
|
|
7534
|
+
/**
|
|
7535
|
+
* Pure function — compute the SeasonalBlend for a given date and latitude.
|
|
7536
|
+
*
|
|
7537
|
+
* @param date The date to evaluate (default: now)
|
|
7538
|
+
* @param latitudeN Decimal degrees, positive = north (default: 0)
|
|
7539
|
+
* @param override Force a fixed season, bypassing computation
|
|
7540
|
+
*/
|
|
7541
|
+
function getSeasonalBlend(date = /* @__PURE__ */ new Date(), latitudeN = 0, override) {
|
|
7542
|
+
if (override) return {
|
|
7543
|
+
season: override,
|
|
7544
|
+
nextSeason: SEASON_ORDER[(SEASON_ORDER.indexOf(override) + 1) % SEASON_ORDER.length],
|
|
7545
|
+
t: 0
|
|
7546
|
+
};
|
|
7547
|
+
const isNorthern = latitudeN >= 0;
|
|
7548
|
+
return computeSeasonalBlend(dayOfYear(date), isNorthern, daysInYear(date.getFullYear()));
|
|
7549
|
+
}
|
|
7550
|
+
/**
|
|
7551
|
+
* Hook — returns the current SeasonalBlend, re-evaluated once per hour.
|
|
7552
|
+
*
|
|
7553
|
+
* @param latitudeN Decimal degrees, positive = north.
|
|
7554
|
+
* Pass null while coordinates are still loading.
|
|
7555
|
+
* @param override Force a specific season (for dev previews / user settings).
|
|
7556
|
+
* @param simulatedDate Use a specific date instead of now (for SolarDevTools).
|
|
7557
|
+
*/
|
|
7558
|
+
function useSeason(latitudeN, override, simulatedDate) {
|
|
7559
|
+
const [blend, setBlend] = useState(() => getSeasonalBlend(simulatedDate ?? /* @__PURE__ */ new Date(), latitudeN ?? 0, override));
|
|
7560
|
+
useEffect(() => {
|
|
7561
|
+
const recompute = () => {
|
|
7562
|
+
setBlend(getSeasonalBlend(simulatedDate ?? /* @__PURE__ */ new Date(), latitudeN ?? 0, override));
|
|
7563
|
+
};
|
|
7564
|
+
recompute();
|
|
7565
|
+
const id$2 = setInterval(recompute, 3600 * 1e3);
|
|
7566
|
+
return () => clearInterval(id$2);
|
|
7567
|
+
}, [
|
|
7568
|
+
latitudeN,
|
|
7569
|
+
override,
|
|
7570
|
+
simulatedDate
|
|
7571
|
+
]);
|
|
7572
|
+
return blend;
|
|
7573
|
+
}
|
|
7574
|
+
|
|
7319
7575
|
//#endregion
|
|
7320
7576
|
//#region node_modules/motion/dist/es/framer-motion/dist/es/context/LayoutGroupContext.mjs
|
|
7321
7577
|
const LayoutGroupContext = createContext({});
|
|
@@ -54528,7 +54784,7 @@ function getAccentFg(accent) {
|
|
|
54528
54784
|
* - scopeId = undefined → writes to :root (global / singleton mode)
|
|
54529
54785
|
* - scopeId = "sol-scope-3" → writes to #sol-scope-3 (isolated mode)
|
|
54530
54786
|
* The scoped vars override :root vars for all descendants of that wrapper div. */
|
|
54531
|
-
function writeCssVars(skin, phase, t = 0, nextPhase, scopeId) {
|
|
54787
|
+
function writeCssVars(skin, phase, t = 0, nextPhase, scopeId, seasonal) {
|
|
54532
54788
|
if (typeof document === "undefined") return;
|
|
54533
54789
|
const from = skin.phaseVars[phase];
|
|
54534
54790
|
const to = skin.phaseVars[nextPhase ?? phase];
|
|
@@ -54541,9 +54797,22 @@ function writeCssVars(skin, phase, t = 0, nextPhase, scopeId) {
|
|
|
54541
54797
|
const surface = lerp(from.surface, to.surface);
|
|
54542
54798
|
const shaderPalFrom = skin.shaderPalettes[phase];
|
|
54543
54799
|
const shaderPalTo = skin.shaderPalettes[nextPhase ?? phase];
|
|
54544
|
-
|
|
54545
|
-
|
|
54800
|
+
let shaderVignette = t > 0 ? lerpHex(shaderPalFrom.vignette, shaderPalTo.vignette, t) : shaderPalFrom.vignette;
|
|
54801
|
+
let shaderColorBack = t > 0 ? lerpHex(shaderPalFrom.colorBack, shaderPalTo.colorBack, t) : shaderPalFrom.colorBack;
|
|
54546
54802
|
const shaderFallback = shaderPalFrom.cssFallback;
|
|
54803
|
+
if (seasonal && !seasonal.disabled) {
|
|
54804
|
+
const mod = resolveSeasonalModifier(seasonal.blend, {
|
|
54805
|
+
...UNIVERSAL_SEASON_MODIFIERS,
|
|
54806
|
+
...skin.seasonalModifiers
|
|
54807
|
+
});
|
|
54808
|
+
const tempPalette = applySeasonalModifier({
|
|
54809
|
+
...shaderPalFrom,
|
|
54810
|
+
vignette: shaderVignette,
|
|
54811
|
+
colorBack: shaderColorBack
|
|
54812
|
+
}, mod);
|
|
54813
|
+
shaderVignette = tempPalette.vignette;
|
|
54814
|
+
shaderColorBack = tempPalette.colorBack;
|
|
54815
|
+
}
|
|
54547
54816
|
if (!scopeId) {
|
|
54548
54817
|
const root = document.documentElement;
|
|
54549
54818
|
if (root.getAttribute("data-solar-skin") !== skin.id) root.setAttribute("data-solar-skin", skin.id);
|
|
@@ -54585,12 +54854,19 @@ const SolarThemeCtx = createContext({
|
|
|
54585
54854
|
simulatedDate: void 0,
|
|
54586
54855
|
setSimulatedDate: noop,
|
|
54587
54856
|
customPalettes: void 0,
|
|
54588
|
-
setCustomPalettes: noop
|
|
54857
|
+
setCustomPalettes: noop,
|
|
54858
|
+
season: "spring",
|
|
54859
|
+
seasonalBlend: {
|
|
54860
|
+
season: "spring",
|
|
54861
|
+
nextSeason: "spring",
|
|
54862
|
+
t: 0
|
|
54863
|
+
},
|
|
54864
|
+
setSeasonOverride: noop
|
|
54589
54865
|
});
|
|
54590
54866
|
function useSolarTheme() {
|
|
54591
54867
|
return useContext(SolarThemeCtx);
|
|
54592
54868
|
}
|
|
54593
|
-
function SolarThemeProvider({ children, initialDesign = "foundry", isolated = false }) {
|
|
54869
|
+
function SolarThemeProvider({ children, initialDesign = "foundry", isolated = false, seasonOverride: seasonOverrideProp, disableSeasonalBlend = false }) {
|
|
54594
54870
|
const geo = useCountryCodeFromGeolocation({ immediate: true });
|
|
54595
54871
|
const scopeId = useRef(isolated ? nextScopeId() : void 0).current;
|
|
54596
54872
|
const wrapperRef = useRef(null);
|
|
@@ -54639,6 +54915,9 @@ function SolarThemeProvider({ children, initialDesign = "foundry", isolated = fa
|
|
|
54639
54915
|
}
|
|
54640
54916
|
}, [isolated]);
|
|
54641
54917
|
const activeSkin = SKINS[design];
|
|
54918
|
+
const [seasonOverrideState, setSeasonOverrideState] = useState(seasonOverrideProp);
|
|
54919
|
+
const seasonalBlend = useSeason(latitude, seasonOverrideProp ?? seasonOverrideState, simulatedDate);
|
|
54920
|
+
const setSeasonOverride = useCallback((s) => setSeasonOverrideState(s ?? void 0), []);
|
|
54642
54921
|
const setOverridePhase = useCallback((phase) => {
|
|
54643
54922
|
setOverridePhaseState(phase);
|
|
54644
54923
|
if (!isolated) setSessionPhaseOverride(phase);
|
|
@@ -54690,7 +54969,10 @@ function SolarThemeProvider({ children, initialDesign = "foundry", isolated = fa
|
|
|
54690
54969
|
isolated
|
|
54691
54970
|
]);
|
|
54692
54971
|
useLayoutEffect(() => {
|
|
54693
|
-
writeCssVars(activeSkin, activeBlend.phase, activeBlend.t, activeBlend.nextPhase, scopeId
|
|
54972
|
+
writeCssVars(activeSkin, activeBlend.phase, activeBlend.t, activeBlend.nextPhase, scopeId, {
|
|
54973
|
+
blend: seasonalBlend,
|
|
54974
|
+
disabled: disableSeasonalBlend
|
|
54975
|
+
});
|
|
54694
54976
|
if (!isolated && !document.documentElement.hasAttribute("data-solar-ready")) requestAnimationFrame(() => {
|
|
54695
54977
|
document.documentElement.setAttribute("data-solar-ready", "");
|
|
54696
54978
|
});
|
|
@@ -54698,7 +54980,9 @@ function SolarThemeProvider({ children, initialDesign = "foundry", isolated = fa
|
|
|
54698
54980
|
activeSkin,
|
|
54699
54981
|
activeBlend,
|
|
54700
54982
|
scopeId,
|
|
54701
|
-
isolated
|
|
54983
|
+
isolated,
|
|
54984
|
+
seasonalBlend,
|
|
54985
|
+
disableSeasonalBlend
|
|
54702
54986
|
]);
|
|
54703
54987
|
useEffect(() => {
|
|
54704
54988
|
if (!scopeId) return;
|
|
@@ -54727,7 +55011,10 @@ function SolarThemeProvider({ children, initialDesign = "foundry", isolated = fa
|
|
|
54727
55011
|
simulatedDate,
|
|
54728
55012
|
setSimulatedDate,
|
|
54729
55013
|
customPalettes,
|
|
54730
|
-
setCustomPalettes
|
|
55014
|
+
setCustomPalettes,
|
|
55015
|
+
season: seasonalBlend.season,
|
|
55016
|
+
seasonalBlend,
|
|
55017
|
+
setSeasonOverride
|
|
54731
55018
|
};
|
|
54732
55019
|
if (isolated) return /* @__PURE__ */ jsx(SolarThemeCtx.Provider, {
|
|
54733
55020
|
value: theme,
|
|
@@ -54747,5 +55034,5 @@ function SolarThemeProvider({ children, initialDesign = "foundry", isolated = fa
|
|
|
54747
55034
|
}
|
|
54748
55035
|
|
|
54749
55036
|
//#endregion
|
|
54750
|
-
export {
|
|
54751
|
-
//# sourceMappingURL=solar-theme-provider-
|
|
55037
|
+
export { resolveSeasonalModifier as _, useSeason as a, getSessionTimeMinutes as c, lerpColor as d, lerpHex as f, lerpModifier as g, applySeasonalModifier as h, getSeasonalBlend as i, setSessionLive as l, UNIVERSAL_SEASON_MODIFIERS as m, useSolarTheme as n, clearSessionTimeMinutes as o, IDENTITY_MODIFIER as p, SKINS as r, getSessionIsLive as s, SolarThemeProvider as t, setSessionTimeMinutes as u };
|
|
55038
|
+
//# sourceMappingURL=solar-theme-provider-BcP2n4b7.js.map
|