@qodin-co/sol 0.1.8 → 0.1.9
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/dist/devtools/index.js +1 -1
- package/dist/index.js +34 -24
- package/dist/index.js.map +1 -1
- package/dist/{solar-theme-provider-DsXhcLfP.js → solar-theme-provider-CXILKRrU.js} +156 -152
- package/dist/solar-theme-provider-CXILKRrU.js.map +1 -0
- package/package.json +1 -1
- package/dist/solar-theme-provider-DsXhcLfP.js.map +0 -1
package/dist/devtools/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as getSessionIsLive, c as setSessionTimeMinutes, i as clearSessionTimeMinutes, n as useSolarTheme, o as getSessionTimeMinutes, s as setSessionLive } from "../solar-theme-provider-
|
|
1
|
+
import { a as getSessionIsLive, c as setSessionTimeMinutes, i as clearSessionTimeMinutes, n as useSolarTheme, o as getSessionTimeMinutes, s as setSessionLive } from "../solar-theme-provider-CXILKRrU.js";
|
|
2
2
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
3
3
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
4
|
import { createPortal } from "react-dom";
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { l as lerpHex, n as useSolarTheme, r as SKINS, t as SolarThemeProvider } from "./solar-theme-provider-
|
|
2
|
-
import { useEffect, useMemo, useState } from "react";
|
|
1
|
+
import { l as lerpHex, n as useSolarTheme, r as SKINS, t as SolarThemeProvider } from "./solar-theme-provider-CXILKRrU.js";
|
|
2
|
+
import { useEffect, useLayoutEffect, useMemo, useState } from "react";
|
|
3
3
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
4
|
|
|
5
5
|
//#region src/widgets/solar-widget.shell.tsx
|
|
@@ -73,6 +73,8 @@ function blendWidgetPalette(from, to, t) {
|
|
|
73
73
|
};
|
|
74
74
|
}
|
|
75
75
|
function SolarWidget({ expandDirection = "bottom-right", size = "lg", showFlag = false, showWeather = false, hoverEffect = false, customPalettes, phaseOverride, weatherCategoryOverride, simulatedDate: simulatedDateProp, forceExpanded }) {
|
|
76
|
+
const [mounted, setMounted] = useState(false);
|
|
77
|
+
useLayoutEffect(() => setMounted(true), []);
|
|
76
78
|
const { phase, blend, activeSkin, timezone, latitude, longitude, simulatedDate: ctxSimulatedDate } = useSolarTheme();
|
|
77
79
|
const simulatedDate = simulatedDateProp ?? ctxSimulatedDate;
|
|
78
80
|
const [expanded, setExpanded] = useState(false);
|
|
@@ -114,27 +116,30 @@ function SolarWidget({ expandDirection = "bottom-right", size = "lg", showFlag =
|
|
|
114
116
|
const liveWeatherCategory = showWeather ? weatherCategoryOverride ?? liveWeather?.category ?? null : null;
|
|
115
117
|
const liveTemperatureC = liveWeather?.temperatureC ?? null;
|
|
116
118
|
const SkinComponent = activeSkin.Component;
|
|
117
|
-
return /* @__PURE__ */ jsx(
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
119
|
+
return /* @__PURE__ */ jsx("div", {
|
|
120
|
+
style: { visibility: mounted ? "visible" : "hidden" },
|
|
121
|
+
children: /* @__PURE__ */ jsx(SkinComponent, {
|
|
122
|
+
phase: activePhase,
|
|
123
|
+
blend: activeBlend,
|
|
124
|
+
expanded,
|
|
125
|
+
onToggle: () => setExpanded((v) => !v),
|
|
126
|
+
expandDirection,
|
|
127
|
+
size,
|
|
128
|
+
time,
|
|
129
|
+
location: "",
|
|
130
|
+
showFlag,
|
|
131
|
+
showWeather,
|
|
132
|
+
hoverEffect,
|
|
133
|
+
weather: weatherCategoryOverride,
|
|
134
|
+
liveWeatherCategory,
|
|
135
|
+
liveTemperatureC,
|
|
136
|
+
palette: finalPalette,
|
|
137
|
+
latitude,
|
|
138
|
+
longitude,
|
|
139
|
+
timezone,
|
|
140
|
+
simulatedDate,
|
|
141
|
+
forceExpanded
|
|
142
|
+
})
|
|
138
143
|
});
|
|
139
144
|
}
|
|
140
145
|
|
|
@@ -214,6 +219,8 @@ function blendPalette(skin, blend) {
|
|
|
214
219
|
};
|
|
215
220
|
}
|
|
216
221
|
function CompactWidget({ design: designOverride, overridePhase, time, location = "", flag, temperature, weather = null, showFlag = false, showWeather = false, showTemperature = true, size = "md", latitude, longitude, timezone, simulatedDate: simulatedDateProp, className = "" }) {
|
|
222
|
+
const [mounted, setMounted] = useState(false);
|
|
223
|
+
useLayoutEffect(() => setMounted(true), []);
|
|
217
224
|
const ctx = useSolarTheme();
|
|
218
225
|
const simulatedDate = simulatedDateProp ?? ctx.simulatedDate;
|
|
219
226
|
const skin = useMemo(() => {
|
|
@@ -283,7 +290,10 @@ function CompactWidget({ design: designOverride, overridePhase, time, location =
|
|
|
283
290
|
});
|
|
284
291
|
return /* @__PURE__ */ jsx("div", {
|
|
285
292
|
className,
|
|
286
|
-
style: {
|
|
293
|
+
style: {
|
|
294
|
+
visibility: mounted ? "visible" : "hidden",
|
|
295
|
+
isolation: "isolate"
|
|
296
|
+
},
|
|
287
297
|
children: /* @__PURE__ */ jsx(CompactComponent, { ...props })
|
|
288
298
|
});
|
|
289
299
|
}
|
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>","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, 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 {\n phase,\n blend,\n activeSkin,\n timezone,\n latitude,\n longitude,\n simulatedDate: ctxSimulatedDate,\n } = useSolarTheme();\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 = { hour: '2-digit', minute: '2-digit', hour12: false };\n if (timezone) opts.timeZone = timezone;\n return new Intl.DateTimeFormat(undefined, opts).format(d);\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 <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 );\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, 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';\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 showFlag?: boolean;\n showWeather?: boolean;\n showTemperature?: boolean;\n size?: CompactSize;\n latitude?: number | null;\n longitude?: number | null;\n timezone?: string | null;\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 showFlag = false,\n showWeather = false,\n showTemperature = true,\n size = 'md',\n latitude,\n longitude,\n timezone,\n simulatedDate: simulatedDateProp,\n className = '',\n}: CompactWidgetProps) {\n const ctx = useSolarTheme();\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 palette = useMemo(() => blendPalette(skin, blend), [skin, blend]);\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 ? (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 = { hour: '2-digit', minute: '2-digit', hour12: false };\n if (resolvedTz) opts.timeZone = resolvedTz;\n return new Intl.DateTimeFormat(undefined, opts).format(d);\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 className={className} style={{ isolation: 'isolate' }}>\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>","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 } = useSolarTheme();\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 = { hour: '2-digit', minute: '2-digit', hour12: false };\n if (timezone) opts.timeZone = timezone;\n return new Intl.DateTimeFormat(undefined, opts).format(d);\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';\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 showFlag?: boolean;\n showWeather?: boolean;\n showTemperature?: boolean;\n size?: CompactSize;\n latitude?: number | null;\n longitude?: number | null;\n timezone?: string | null;\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 showFlag = false,\n showWeather = false,\n showTemperature = true,\n size = 'md',\n latitude,\n longitude,\n timezone,\n simulatedDate: simulatedDateProp,\n className = '',\n}: CompactWidgetProps) {\n const [mounted, setMounted] = useState(false);\n useLayoutEffect(() => setMounted(true), []);\n\n const ctx = useSolarTheme();\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 palette = useMemo(() => blendPalette(skin, blend), [skin, blend]);\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 ? (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 = { hour: '2-digit', minute: '2-digit', hour12: false };\n if (resolvedTz) opts.timeZone = resolvedTz;\n return new Intl.DateTimeFormat(undefined, opts).format(d);\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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"}
|