react-os-shell 0.2.43 → 0.2.45
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/{Browser-5ZCLRIRU.js → Browser-LSJORULM.js} +3 -3
- package/dist/{Browser-5ZCLRIRU.js.map → Browser-LSJORULM.js.map} +1 -1
- package/dist/{Calculator-OHL2AZ52.js → Calculator-ZKEH53OV.js} +4 -4
- package/dist/{Calculator-OHL2AZ52.js.map → Calculator-ZKEH53OV.js.map} +1 -1
- package/dist/{Calendar-6AHL3UJY.js → Calendar-PKRZ5MBU.js} +154 -55
- package/dist/Calendar-PKRZ5MBU.js.map +1 -0
- package/dist/{CurrencyConverter-XZVZ7XOF.js → CurrencyConverter-5N44NZ6Z.js} +56 -50
- package/dist/CurrencyConverter-5N44NZ6Z.js.map +1 -0
- package/dist/{Documents-IPVZ47JX.js → Documents-CQVIIFZV.js} +3 -3
- package/dist/{Documents-IPVZ47JX.js.map → Documents-CQVIIFZV.js.map} +1 -1
- package/dist/{Email-Z7FDKAFD.js → Email-CR6XS2AD.js} +5 -5
- package/dist/{Email-Z7FDKAFD.js.map → Email-CR6XS2AD.js.map} +1 -1
- package/dist/Files-ITIKVHIE.js +8 -0
- package/dist/{Files-KEHRZ2UY.js.map → Files-ITIKVHIE.js.map} +1 -1
- package/dist/{GeminiChat-ITU46EH4.js → GeminiChat-XTEBZIVK.js} +3 -3
- package/dist/{GeminiChat-ITU46EH4.js.map → GeminiChat-XTEBZIVK.js.map} +1 -1
- package/dist/{Minesweeper-NGN4O6C4.js → Minesweeper-QGUPDVRS.js} +3 -3
- package/dist/{Minesweeper-NGN4O6C4.js.map → Minesweeper-QGUPDVRS.js.map} +1 -1
- package/dist/{Notepad-W3YYZ3GS.js → Notepad-74CQPZCV.js} +3 -3
- package/dist/{Notepad-W3YYZ3GS.js.map → Notepad-74CQPZCV.js.map} +1 -1
- package/dist/PomodoroTimer-FHSOLF3O.js +627 -0
- package/dist/PomodoroTimer-FHSOLF3O.js.map +1 -0
- package/dist/Preview-4MBQI66Q.js +7 -0
- package/dist/{Preview-4354N46U.js.map → Preview-4MBQI66Q.js.map} +1 -1
- package/dist/{Spreadsheet-FCFII6DW.js → Spreadsheet-MKXPPSKV.js} +3 -3
- package/dist/{Spreadsheet-FCFII6DW.js.map → Spreadsheet-MKXPPSKV.js.map} +1 -1
- package/dist/TodoList-7JZ2SLDI.js +494 -0
- package/dist/TodoList-7JZ2SLDI.js.map +1 -0
- package/dist/Weather-YXSCSPQT.js +424 -0
- package/dist/Weather-YXSCSPQT.js.map +1 -0
- package/dist/WorldClock-XHM7WAUV.js +196 -0
- package/dist/WorldClock-XHM7WAUV.js.map +1 -0
- package/dist/apps/index.d.ts +10 -11
- package/dist/apps/index.js +23 -21
- package/dist/apps/index.js.map +1 -1
- package/dist/chunk-25L4DIKH.js +90 -0
- package/dist/chunk-25L4DIKH.js.map +1 -0
- package/dist/{chunk-62MVMTBT.js → chunk-5VXRBUEH.js} +20 -3
- package/dist/chunk-5VXRBUEH.js.map +1 -0
- package/dist/chunk-7CCHEEYC.js +94 -0
- package/dist/chunk-7CCHEEYC.js.map +1 -0
- package/dist/{chunk-T2NQXP2J.js → chunk-7M3BBAHQ.js} +10 -4
- package/dist/chunk-7M3BBAHQ.js.map +1 -0
- package/dist/{chunk-IY7JJVHR.js → chunk-DUUANLLE.js} +3 -3
- package/dist/{chunk-IY7JJVHR.js.map → chunk-DUUANLLE.js.map} +1 -1
- package/dist/chunk-MK3HLUO4.js +380 -0
- package/dist/chunk-MK3HLUO4.js.map +1 -0
- package/dist/{chunk-2SRU4BYH.js → chunk-MTLVXT2C.js} +4 -4
- package/dist/{chunk-2SRU4BYH.js.map → chunk-MTLVXT2C.js.map} +1 -1
- package/dist/{chunk-46LICZUM.js → chunk-MVWEL34Y.js} +3 -2
- package/dist/chunk-MVWEL34Y.js.map +1 -0
- package/dist/{chunk-TVOBLSSV.js → chunk-UK2AA3J6.js} +3 -3
- package/dist/{chunk-TVOBLSSV.js.map → chunk-UK2AA3J6.js.map} +1 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.js +4089 -11
- package/dist/index.js.map +1 -1
- package/dist/styles.css +6 -4
- package/package.json +1 -1
- package/dist/Calendar-6AHL3UJY.js.map +0 -1
- package/dist/CurrencyConverter-XZVZ7XOF.js.map +0 -1
- package/dist/Files-KEHRZ2UY.js +0 -8
- package/dist/PomodoroTimer-T2J5NDJR.js +0 -196
- package/dist/PomodoroTimer-T2J5NDJR.js.map +0 -1
- package/dist/Preview-4354N46U.js +0 -7
- package/dist/Weather-XTADR7Z3.js +0 -266
- package/dist/Weather-XTADR7Z3.js.map +0 -1
- package/dist/WorldClock-OFK2EA2H.js +0 -126
- package/dist/WorldClock-OFK2EA2H.js.map +0 -1
- package/dist/chunk-46LICZUM.js.map +0 -1
- package/dist/chunk-62MVMTBT.js.map +0 -1
- package/dist/chunk-7KZWBIDL.js +0 -4144
- package/dist/chunk-7KZWBIDL.js.map +0 -1
- package/dist/chunk-T2NQXP2J.js.map +0 -1
package/dist/Weather-XTADR7Z3.js
DELETED
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
import { useShellPrefs } from './chunk-36VM54SC.js';
|
|
2
|
-
import { loadAppearance, WidgetSettingsModal } from './chunk-TVOBLSSV.js';
|
|
3
|
-
import { useWidgetSettings } from './chunk-T2NQXP2J.js';
|
|
4
|
-
import './chunk-PLGHQ7QW.js';
|
|
5
|
-
import { useState, useEffect, useCallback } from 'react';
|
|
6
|
-
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
7
|
-
|
|
8
|
-
var WMO = {
|
|
9
|
-
0: ["Clear Sky", "\u2600\uFE0F", "\u{1F319}", "from-sky-400 to-blue-500", "from-indigo-800 to-slate-900"],
|
|
10
|
-
1: ["Mainly Clear", "\u{1F324}\uFE0F", "\u{1F319}", "from-sky-400 to-blue-500", "from-indigo-800 to-slate-900"],
|
|
11
|
-
2: ["Partly Cloudy", "\u26C5", "\u2601\uFE0F", "from-sky-400 to-blue-400", "from-indigo-700 to-slate-800"],
|
|
12
|
-
3: ["Overcast", "\u2601\uFE0F", "\u2601\uFE0F", "from-gray-400 to-gray-500", "from-gray-700 to-slate-800"],
|
|
13
|
-
45: ["Foggy", "\u{1F32B}\uFE0F", "\u{1F32B}\uFE0F", "from-gray-400 to-gray-500", "from-gray-700 to-slate-800"],
|
|
14
|
-
48: ["Foggy", "\u{1F32B}\uFE0F", "\u{1F32B}\uFE0F", "from-gray-400 to-gray-500", "from-gray-700 to-slate-800"],
|
|
15
|
-
51: ["Light Drizzle", "\u{1F326}\uFE0F", "\u{1F327}\uFE0F", "from-gray-400 to-blue-500", "from-gray-700 to-indigo-800"],
|
|
16
|
-
53: ["Drizzle", "\u{1F327}\uFE0F", "\u{1F327}\uFE0F", "from-gray-500 to-blue-600", "from-gray-700 to-indigo-800"],
|
|
17
|
-
55: ["Heavy Drizzle", "\u{1F327}\uFE0F", "\u{1F327}\uFE0F", "from-gray-500 to-blue-600", "from-gray-700 to-indigo-800"],
|
|
18
|
-
61: ["Light Rain", "\u{1F326}\uFE0F", "\u{1F327}\uFE0F", "from-gray-400 to-blue-500", "from-gray-700 to-indigo-800"],
|
|
19
|
-
63: ["Rain", "\u{1F327}\uFE0F", "\u{1F327}\uFE0F", "from-gray-500 to-blue-600", "from-gray-700 to-indigo-800"],
|
|
20
|
-
65: ["Heavy Rain", "\u{1F327}\uFE0F", "\u{1F327}\uFE0F", "from-gray-600 to-blue-700", "from-gray-700 to-indigo-900"],
|
|
21
|
-
71: ["Light Snow", "\u{1F328}\uFE0F", "\u{1F328}\uFE0F", "from-blue-200 to-blue-400", "from-blue-800 to-slate-900"],
|
|
22
|
-
73: ["Snow", "\u2744\uFE0F", "\u2744\uFE0F", "from-blue-300 to-blue-500", "from-blue-800 to-slate-900"],
|
|
23
|
-
75: ["Heavy Snow", "\u2744\uFE0F", "\u2744\uFE0F", "from-blue-400 to-blue-600", "from-blue-800 to-slate-900"],
|
|
24
|
-
80: ["Rain Showers", "\u{1F327}\uFE0F", "\u{1F327}\uFE0F", "from-gray-500 to-blue-600", "from-gray-700 to-indigo-800"],
|
|
25
|
-
82: ["Heavy Showers", "\u{1F327}\uFE0F", "\u{1F327}\uFE0F", "from-gray-600 to-blue-700", "from-gray-700 to-indigo-900"],
|
|
26
|
-
95: ["Thunderstorm", "\u26C8\uFE0F", "\u26C8\uFE0F", "from-gray-700 to-indigo-800", "from-gray-800 to-indigo-950"],
|
|
27
|
-
96: ["Thunderstorm", "\u26C8\uFE0F", "\u26C8\uFE0F", "from-gray-700 to-indigo-800", "from-gray-800 to-indigo-950"],
|
|
28
|
-
99: ["Thunderstorm", "\u26C8\uFE0F", "\u26C8\uFE0F", "from-gray-700 to-indigo-900", "from-gray-800 to-indigo-950"]
|
|
29
|
-
};
|
|
30
|
-
var getCondition = (code, isDay = true) => {
|
|
31
|
-
const entry = WMO[code] || ["Unknown", "\u2753", "\u2753", "from-gray-400 to-gray-500", "from-gray-700 to-slate-800"];
|
|
32
|
-
return [entry[0], isDay ? entry[1] : entry[2], isDay ? entry[3] : entry[4]];
|
|
33
|
-
};
|
|
34
|
-
var AVAILABLE_CITIES = {
|
|
35
|
-
"Sydney": { lat: -33.8688, lon: 151.2093 },
|
|
36
|
-
"London": { lat: 51.5074, lon: -0.1278 },
|
|
37
|
-
"Los Angeles": { lat: 34.0522, lon: -118.2437 },
|
|
38
|
-
"Shanghai": { lat: 31.2304, lon: 121.4737 },
|
|
39
|
-
"New York": { lat: 40.7128, lon: -74.006 },
|
|
40
|
-
"Tokyo": { lat: 35.6762, lon: 139.6503 },
|
|
41
|
-
"Dubai": { lat: 25.2048, lon: 55.2708 },
|
|
42
|
-
"Singapore": { lat: 1.3521, lon: 103.8198 },
|
|
43
|
-
"Hong Kong": { lat: 22.3193, lon: 114.1694 },
|
|
44
|
-
"Paris": { lat: 48.8566, lon: 2.3522 },
|
|
45
|
-
"Berlin": { lat: 52.52, lon: 13.405 },
|
|
46
|
-
"Mumbai": { lat: 19.076, lon: 72.8777 },
|
|
47
|
-
"Bangkok": { lat: 13.7563, lon: 100.5018 },
|
|
48
|
-
"Melbourne": { lat: -37.8136, lon: 144.9631 },
|
|
49
|
-
"Toronto": { lat: 43.6532, lon: -79.3832 },
|
|
50
|
-
"Miami": { lat: 25.7617, lon: -80.1918 },
|
|
51
|
-
"Chicago": { lat: 41.8781, lon: -87.6298 },
|
|
52
|
-
"Auckland": { lat: -36.8485, lon: 174.7633 }
|
|
53
|
-
};
|
|
54
|
-
var DEFAULT_CITIES = ["Sydney", "London", "Los Angeles", "Shanghai"];
|
|
55
|
-
var STORAGE_KEY = "weather_cities";
|
|
56
|
-
var SETTINGS_KEY = "weather_appearance";
|
|
57
|
-
var CACHE_KEY = "weather_multi_cache";
|
|
58
|
-
var CACHE_TTL = 30 * 60 * 1e3;
|
|
59
|
-
var DEFAULT_PREFS = { useFahrenheit: false, showLocalTime: false, use24Hour: false };
|
|
60
|
-
function loadCities() {
|
|
61
|
-
try {
|
|
62
|
-
const s = JSON.parse(localStorage.getItem(STORAGE_KEY) || "");
|
|
63
|
-
if (Array.isArray(s) && s.length) return s;
|
|
64
|
-
} catch {
|
|
65
|
-
}
|
|
66
|
-
return DEFAULT_CITIES;
|
|
67
|
-
}
|
|
68
|
-
var toF = (c) => Math.round(c * 9 / 5 + 32);
|
|
69
|
-
function getTimeInTz(timezone, use24Hour = false) {
|
|
70
|
-
try {
|
|
71
|
-
const now = /* @__PURE__ */ new Date();
|
|
72
|
-
const h24Parts = new Intl.DateTimeFormat("en-US", { timeZone: timezone, hour: "numeric", minute: "2-digit", hour12: false }).formatToParts(now);
|
|
73
|
-
const h24 = parseInt(h24Parts.find((p) => p.type === "hour")?.value || "0");
|
|
74
|
-
const m = parseInt(h24Parts.find((p) => p.type === "minute")?.value || "0");
|
|
75
|
-
if (use24Hour) {
|
|
76
|
-
return { hours: h24, minutes: m, text: `${String(h24).padStart(2, "0")}:${String(m).padStart(2, "0")}` };
|
|
77
|
-
}
|
|
78
|
-
const parts = new Intl.DateTimeFormat("en-US", { timeZone: timezone, hour: "numeric", minute: "2-digit", hour12: true }).formatToParts(now);
|
|
79
|
-
const h12 = parseInt(parts.find((p) => p.type === "hour")?.value || "12");
|
|
80
|
-
const period = parts.find((p) => p.type === "dayPeriod")?.value || "";
|
|
81
|
-
return { hours: h24, minutes: m, text: `${h12}:${String(m).padStart(2, "0")} ${period}` };
|
|
82
|
-
} catch {
|
|
83
|
-
return { hours: 0, minutes: 0, text: "" };
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
function Weather() {
|
|
87
|
-
const [cities, setCities] = useState(loadCities);
|
|
88
|
-
const [appearance, setAppearance] = useState(() => loadAppearance(SETTINGS_KEY));
|
|
89
|
-
const [data, setData] = useState([]);
|
|
90
|
-
const [loading, setLoading] = useState(true);
|
|
91
|
-
const [, setTick] = useState(0);
|
|
92
|
-
useEffect(() => {
|
|
93
|
-
const t2 = setInterval(() => setTick((n) => n + 1), 6e4);
|
|
94
|
-
return () => clearInterval(t2);
|
|
95
|
-
}, []);
|
|
96
|
-
const [settingsOpen, setSettingsOpen] = useState(false);
|
|
97
|
-
const [configCities, setConfigCities] = useState([]);
|
|
98
|
-
const [configAppearance, setConfigAppearance] = useState(appearance);
|
|
99
|
-
const { prefs: shellPrefs, save: saveShellPrefs } = useShellPrefs();
|
|
100
|
-
const prefs = { ...DEFAULT_PREFS, ...shellPrefs.weather_prefs ?? {} };
|
|
101
|
-
const [configPrefs, setConfigPrefs] = useState(prefs);
|
|
102
|
-
useWidgetSettings(useCallback(() => {
|
|
103
|
-
setConfigCities([...cities]);
|
|
104
|
-
setConfigAppearance({ ...appearance });
|
|
105
|
-
setConfigPrefs({ ...prefs });
|
|
106
|
-
setSettingsOpen(true);
|
|
107
|
-
}, [cities, appearance, prefs]));
|
|
108
|
-
const fetchAll = useCallback(async (cityList, force = false) => {
|
|
109
|
-
if (!force) {
|
|
110
|
-
try {
|
|
111
|
-
const cached = JSON.parse(localStorage.getItem(CACHE_KEY) || "{}");
|
|
112
|
-
const key = cityList.join(",");
|
|
113
|
-
if (cached[key] && Date.now() - cached[key].ts < CACHE_TTL) {
|
|
114
|
-
setData(cached[key].data);
|
|
115
|
-
setLoading(false);
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
} catch {
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
setLoading(true);
|
|
122
|
-
const results = [];
|
|
123
|
-
for (const city of cityList) {
|
|
124
|
-
const coords = AVAILABLE_CITIES[city];
|
|
125
|
-
if (!coords) continue;
|
|
126
|
-
try {
|
|
127
|
-
const res = await fetch(`https://api.open-meteo.com/v1/forecast?latitude=${coords.lat}&longitude=${coords.lon}¤t=temperature_2m,weather_code,is_day&daily=temperature_2m_max,temperature_2m_min&forecast_days=1&timezone=auto`);
|
|
128
|
-
const w = await res.json();
|
|
129
|
-
results.push({ city, temp: Math.round(w.current.temperature_2m), code: w.current.weather_code, high: Math.round(w.daily.temperature_2m_max[0]), low: Math.round(w.daily.temperature_2m_min[0]), isDay: w.current.is_day === 1, timezone: w.timezone || "UTC" });
|
|
130
|
-
} catch {
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
setData(results);
|
|
134
|
-
setLoading(false);
|
|
135
|
-
try {
|
|
136
|
-
const c = JSON.parse(localStorage.getItem(CACHE_KEY) || "{}");
|
|
137
|
-
c[cityList.join(",")] = { data: results, ts: Date.now() };
|
|
138
|
-
localStorage.setItem(CACHE_KEY, JSON.stringify(c));
|
|
139
|
-
} catch {
|
|
140
|
-
}
|
|
141
|
-
}, []);
|
|
142
|
-
useEffect(() => {
|
|
143
|
-
fetchAll(cities);
|
|
144
|
-
}, [cities, fetchAll]);
|
|
145
|
-
const saveSettings = () => {
|
|
146
|
-
if (configCities.length === 0) return;
|
|
147
|
-
setCities(configCities);
|
|
148
|
-
setAppearance(configAppearance);
|
|
149
|
-
saveShellPrefs({ weather_prefs: configPrefs });
|
|
150
|
-
localStorage.setItem(STORAGE_KEY, JSON.stringify(configCities));
|
|
151
|
-
localStorage.setItem(SETTINGS_KEY, JSON.stringify(configAppearance));
|
|
152
|
-
setSettingsOpen(false);
|
|
153
|
-
};
|
|
154
|
-
const t = (c) => prefs.useFahrenheit ? `${toF(c)}\xB0F` : `${c}\xB0`;
|
|
155
|
-
if (loading && data.length === 0) {
|
|
156
|
-
return /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-full bg-gradient-to-b from-sky-400 to-blue-500 rounded-lg text-white/70 text-sm", children: "Loading..." });
|
|
157
|
-
}
|
|
158
|
-
const dynamicHeight = data.length * 96 + 16;
|
|
159
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
160
|
-
/* @__PURE__ */ jsx(
|
|
161
|
-
"div",
|
|
162
|
-
{
|
|
163
|
-
className: "flex flex-col rounded-lg text-white overflow-hidden",
|
|
164
|
-
style: {
|
|
165
|
-
minHeight: dynamicHeight,
|
|
166
|
-
backgroundColor: `rgba(15, 23, 42, ${appearance.activeOpacity / 100})`,
|
|
167
|
-
// slate-900 with alpha
|
|
168
|
-
backdropFilter: appearance.activeBlur > 0 ? `blur(${appearance.activeBlur}px)` : void 0
|
|
169
|
-
},
|
|
170
|
-
children: /* @__PURE__ */ jsx("div", { className: "flex-1 flex flex-col gap-2 p-2", children: data.map((d) => {
|
|
171
|
-
const [condition] = getCondition(d.code, d.isDay);
|
|
172
|
-
const rowBg = d.isDay ? "bg-gradient-to-br from-sky-400 via-sky-300 to-sky-500" : "bg-gradient-to-br from-slate-800 via-blue-950 to-slate-900";
|
|
173
|
-
return /* @__PURE__ */ jsxs("div", { className: `rounded-2xl px-4 py-3 flex flex-col justify-between gap-3 ${rowBg}`, children: [
|
|
174
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", children: [
|
|
175
|
-
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
176
|
-
/* @__PURE__ */ jsx("div", { className: "text-lg font-semibold leading-tight truncate", children: d.city }),
|
|
177
|
-
prefs.showLocalTime && /* @__PURE__ */ jsx("div", { className: "text-xs opacity-90 mt-0.5 tabular-nums", children: getTimeInTz(d.timezone, prefs.use24Hour).text })
|
|
178
|
-
] }),
|
|
179
|
-
/* @__PURE__ */ jsx("div", { className: "text-4xl font-extralight leading-none tracking-tight tabular-nums shrink-0", children: t(d.temp) })
|
|
180
|
-
] }),
|
|
181
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-end justify-between text-[11px]", children: [
|
|
182
|
-
/* @__PURE__ */ jsx("span", { className: "opacity-95", children: condition }),
|
|
183
|
-
/* @__PURE__ */ jsxs("span", { className: "opacity-90 tabular-nums", children: [
|
|
184
|
-
"H:",
|
|
185
|
-
t(d.high),
|
|
186
|
-
" L:",
|
|
187
|
-
t(d.low)
|
|
188
|
-
] })
|
|
189
|
-
] })
|
|
190
|
-
] }, d.city);
|
|
191
|
-
}) })
|
|
192
|
-
}
|
|
193
|
-
),
|
|
194
|
-
/* @__PURE__ */ jsxs(
|
|
195
|
-
WidgetSettingsModal,
|
|
196
|
-
{
|
|
197
|
-
open: settingsOpen,
|
|
198
|
-
onClose: () => setSettingsOpen(false),
|
|
199
|
-
title: "Weather Settings",
|
|
200
|
-
appearance: configAppearance,
|
|
201
|
-
onAppearanceChange: setConfigAppearance,
|
|
202
|
-
onSave: saveSettings,
|
|
203
|
-
children: [
|
|
204
|
-
/* @__PURE__ */ jsx("div", { className: "space-y-3", children: /* @__PURE__ */ jsxs("div", { children: [
|
|
205
|
-
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-gray-700 mb-2", children: "Display" }),
|
|
206
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 mb-2", children: [
|
|
207
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm text-gray-600 w-24", children: "Temperature" }),
|
|
208
|
-
/* @__PURE__ */ jsx("div", { className: "flex gap-1", children: [{ key: false, label: "\xB0C" }, { key: true, label: "\xB0F" }].map((o) => /* @__PURE__ */ jsx(
|
|
209
|
-
"button",
|
|
210
|
-
{
|
|
211
|
-
onClick: () => setConfigPrefs((p) => ({ ...p, useFahrenheit: o.key })),
|
|
212
|
-
className: `px-3 py-1 text-xs font-medium rounded-lg border transition-colors ${configPrefs.useFahrenheit === o.key ? "bg-blue-600 text-white border-blue-600" : "bg-white text-gray-700 border-gray-300 hover:bg-gray-50"}`,
|
|
213
|
-
children: o.label
|
|
214
|
-
},
|
|
215
|
-
String(o.key)
|
|
216
|
-
)) })
|
|
217
|
-
] }),
|
|
218
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 mb-2", children: [
|
|
219
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm text-gray-600 w-24", children: "Time Format" }),
|
|
220
|
-
/* @__PURE__ */ jsx("div", { className: "flex gap-1", children: [{ key: false, label: "AM/PM" }, { key: true, label: "24H" }].map((o) => /* @__PURE__ */ jsx(
|
|
221
|
-
"button",
|
|
222
|
-
{
|
|
223
|
-
onClick: () => setConfigPrefs((p) => ({ ...p, use24Hour: o.key })),
|
|
224
|
-
className: `px-3 py-1 text-xs font-medium rounded-lg border transition-colors ${configPrefs.use24Hour === o.key ? "bg-blue-600 text-white border-blue-600" : "bg-white text-gray-700 border-gray-300 hover:bg-gray-50"}`,
|
|
225
|
-
children: o.label
|
|
226
|
-
},
|
|
227
|
-
String(o.key)
|
|
228
|
-
)) })
|
|
229
|
-
] }),
|
|
230
|
-
/* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 cursor-pointer", children: [
|
|
231
|
-
/* @__PURE__ */ jsx(
|
|
232
|
-
"input",
|
|
233
|
-
{
|
|
234
|
-
type: "checkbox",
|
|
235
|
-
checked: configPrefs.showLocalTime,
|
|
236
|
-
onChange: (e) => setConfigPrefs((p) => ({ ...p, showLocalTime: e.target.checked })),
|
|
237
|
-
className: "rounded border-gray-300 text-blue-600 h-3.5 w-3.5"
|
|
238
|
-
}
|
|
239
|
-
),
|
|
240
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm text-gray-600", children: "Show local time" })
|
|
241
|
-
] })
|
|
242
|
-
] }) }),
|
|
243
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
244
|
-
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-gray-700 mb-2", children: "Cities" }),
|
|
245
|
-
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-1 max-h-48 overflow-y-auto", children: Object.keys(AVAILABLE_CITIES).map((city) => /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 text-sm py-1 cursor-pointer hover:bg-gray-50 rounded px-2", children: [
|
|
246
|
-
/* @__PURE__ */ jsx(
|
|
247
|
-
"input",
|
|
248
|
-
{
|
|
249
|
-
type: "checkbox",
|
|
250
|
-
checked: configCities.includes(city),
|
|
251
|
-
onChange: () => setConfigCities((prev) => prev.includes(city) ? prev.filter((c) => c !== city) : [...prev, city]),
|
|
252
|
-
className: "rounded border-gray-300 text-blue-600 focus:ring-blue-500 h-3.5 w-3.5"
|
|
253
|
-
}
|
|
254
|
-
),
|
|
255
|
-
city
|
|
256
|
-
] }, city)) })
|
|
257
|
-
] })
|
|
258
|
-
]
|
|
259
|
-
}
|
|
260
|
-
)
|
|
261
|
-
] });
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
export { Weather as default };
|
|
265
|
-
//# sourceMappingURL=Weather-XTADR7Z3.js.map
|
|
266
|
-
//# sourceMappingURL=Weather-XTADR7Z3.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/apps/Weather.tsx"],"names":["t"],"mappings":"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","file":"Weather-XTADR7Z3.js","sourcesContent":["import { useState, useEffect, useCallback } from 'react';\nimport { useWidgetSettings } from '../shell/Modal';\nimport WidgetSettingsModal, { loadAppearance, type WidgetAppearance } from '../shell/WidgetSettingsModal';\nimport { useShellPrefs } from '../shell/ShellPrefs';\n\n// [condition, day emoji, night emoji, day gradient, night gradient]\nconst WMO: Record<number, [string, string, string, string, string]> = {\n 0: ['Clear Sky', '☀️', '🌙', 'from-sky-400 to-blue-500', 'from-indigo-800 to-slate-900'],\n 1: ['Mainly Clear', '🌤️', '🌙', 'from-sky-400 to-blue-500', 'from-indigo-800 to-slate-900'],\n 2: ['Partly Cloudy', '⛅', '☁️', 'from-sky-400 to-blue-400', 'from-indigo-700 to-slate-800'],\n 3: ['Overcast', '☁️', '☁️', 'from-gray-400 to-gray-500', 'from-gray-700 to-slate-800'],\n 45: ['Foggy', '🌫️', '🌫️', 'from-gray-400 to-gray-500', 'from-gray-700 to-slate-800'],\n 48: ['Foggy', '🌫️', '🌫️', 'from-gray-400 to-gray-500', 'from-gray-700 to-slate-800'],\n 51: ['Light Drizzle', '🌦️', '🌧️', 'from-gray-400 to-blue-500', 'from-gray-700 to-indigo-800'],\n 53: ['Drizzle', '🌧️', '🌧️', 'from-gray-500 to-blue-600', 'from-gray-700 to-indigo-800'],\n 55: ['Heavy Drizzle', '🌧️', '🌧️', 'from-gray-500 to-blue-600', 'from-gray-700 to-indigo-800'],\n 61: ['Light Rain', '🌦️', '🌧️', 'from-gray-400 to-blue-500', 'from-gray-700 to-indigo-800'],\n 63: ['Rain', '🌧️', '🌧️', 'from-gray-500 to-blue-600', 'from-gray-700 to-indigo-800'],\n 65: ['Heavy Rain', '🌧️', '🌧️', 'from-gray-600 to-blue-700', 'from-gray-700 to-indigo-900'],\n 71: ['Light Snow', '🌨️', '🌨️', 'from-blue-200 to-blue-400', 'from-blue-800 to-slate-900'],\n 73: ['Snow', '❄️', '❄️', 'from-blue-300 to-blue-500', 'from-blue-800 to-slate-900'],\n 75: ['Heavy Snow', '❄️', '❄️', 'from-blue-400 to-blue-600', 'from-blue-800 to-slate-900'],\n 80: ['Rain Showers', '🌧️', '🌧️', 'from-gray-500 to-blue-600', 'from-gray-700 to-indigo-800'],\n 82: ['Heavy Showers', '🌧️', '🌧️', 'from-gray-600 to-blue-700', 'from-gray-700 to-indigo-900'],\n 95: ['Thunderstorm', '⛈️', '⛈️', 'from-gray-700 to-indigo-800', 'from-gray-800 to-indigo-950'],\n 96: ['Thunderstorm', '⛈️', '⛈️', 'from-gray-700 to-indigo-800', 'from-gray-800 to-indigo-950'],\n 99: ['Thunderstorm', '⛈️', '⛈️', 'from-gray-700 to-indigo-900', 'from-gray-800 to-indigo-950'],\n};\n\nconst getCondition = (code: number, isDay = true) => {\n const entry = WMO[code] || ['Unknown', '❓', '❓', 'from-gray-400 to-gray-500', 'from-gray-700 to-slate-800'];\n return [entry[0], isDay ? entry[1] : entry[2], isDay ? entry[3] : entry[4]] as [string, string, string];\n};\n\nconst AVAILABLE_CITIES: Record<string, { lat: number; lon: number }> = {\n 'Sydney': { lat: -33.8688, lon: 151.2093 },\n 'London': { lat: 51.5074, lon: -0.1278 },\n 'Los Angeles': { lat: 34.0522, lon: -118.2437 },\n 'Shanghai': { lat: 31.2304, lon: 121.4737 },\n 'New York': { lat: 40.7128, lon: -74.0060 },\n 'Tokyo': { lat: 35.6762, lon: 139.6503 },\n 'Dubai': { lat: 25.2048, lon: 55.2708 },\n 'Singapore': { lat: 1.3521, lon: 103.8198 },\n 'Hong Kong': { lat: 22.3193, lon: 114.1694 },\n 'Paris': { lat: 48.8566, lon: 2.3522 },\n 'Berlin': { lat: 52.5200, lon: 13.4050 },\n 'Mumbai': { lat: 19.0760, lon: 72.8777 },\n 'Bangkok': { lat: 13.7563, lon: 100.5018 },\n 'Melbourne': { lat: -37.8136, lon: 144.9631 },\n 'Toronto': { lat: 43.6532, lon: -79.3832 },\n 'Miami': { lat: 25.7617, lon: -80.1918 },\n 'Chicago': { lat: 41.8781, lon: -87.6298 },\n 'Auckland': { lat: -36.8485, lon: 174.7633 },\n};\n\nconst DEFAULT_CITIES = ['Sydney', 'London', 'Los Angeles', 'Shanghai'];\nconst STORAGE_KEY = 'weather_cities';\nconst SETTINGS_KEY = 'weather_appearance';\nconst CACHE_KEY = 'weather_multi_cache';\nconst CACHE_TTL = 30 * 60 * 1000;\n\ninterface CityWeather { city: string; temp: number; code: number; high: number; low: number; isDay: boolean; timezone: string }\ninterface WeatherPrefs { useFahrenheit: boolean; showLocalTime: boolean; use24Hour: boolean }\nconst DEFAULT_PREFS: WeatherPrefs = { useFahrenheit: false, showLocalTime: false, use24Hour: false };\n\nfunction loadCities(): string[] {\n try { const s = JSON.parse(localStorage.getItem(STORAGE_KEY) || ''); if (Array.isArray(s) && s.length) return s; } catch {}\n return DEFAULT_CITIES;\n}\n\nconst toF = (c: number) => Math.round(c * 9 / 5 + 32);\n\nfunction getTimeInTz(timezone: string, use24Hour = false): { hours: number; minutes: number; text: string } {\n try {\n const now = new Date();\n const h24Parts = new Intl.DateTimeFormat('en-US', { timeZone: timezone, hour: 'numeric', minute: '2-digit', hour12: false }).formatToParts(now);\n const h24 = parseInt(h24Parts.find(p => p.type === 'hour')?.value || '0');\n const m = parseInt(h24Parts.find(p => p.type === 'minute')?.value || '0');\n if (use24Hour) {\n return { hours: h24, minutes: m, text: `${String(h24).padStart(2, '0')}:${String(m).padStart(2, '0')}` };\n }\n const parts = new Intl.DateTimeFormat('en-US', { timeZone: timezone, hour: 'numeric', minute: '2-digit', hour12: true }).formatToParts(now);\n const h12 = parseInt(parts.find(p => p.type === 'hour')?.value || '12');\n const period = parts.find(p => p.type === 'dayPeriod')?.value || '';\n return { hours: h24, minutes: m, text: `${h12}:${String(m).padStart(2, '0')} ${period}` };\n } catch { return { hours: 0, minutes: 0, text: '' }; }\n}\n\n/** Tiny analog clock SVG */\nfunction MiniClock({ hours, minutes, size = 20 }: { hours: number; minutes: number; size?: number }) {\n const r = size / 2;\n const hAngle = ((hours % 12) + minutes / 60) * 30 - 90;\n const mAngle = minutes * 6 - 90;\n const hRad = (hAngle * Math.PI) / 180;\n const mRad = (mAngle * Math.PI) / 180;\n const hLen = r * 0.5;\n const mLen = r * 0.7;\n return (\n <svg width={size} height={size} className=\"shrink-0\">\n <circle cx={r} cy={r} r={r - 1} fill=\"rgba(255,255,255,0.15)\" stroke=\"rgba(255,255,255,0.4)\" strokeWidth={1} />\n {/* Hour hand */}\n <line x1={r} y1={r} x2={r + Math.cos(hRad) * hLen} y2={r + Math.sin(hRad) * hLen}\n stroke=\"white\" strokeWidth={1.5} strokeLinecap=\"round\" />\n {/* Minute hand */}\n <line x1={r} y1={r} x2={r + Math.cos(mRad) * mLen} y2={r + Math.sin(mRad) * mLen}\n stroke=\"white\" strokeWidth={1} strokeLinecap=\"round\" />\n {/* Center dot */}\n <circle cx={r} cy={r} r={1} fill=\"white\" />\n </svg>\n );\n}\n\nexport default function Weather() {\n const [cities, setCities] = useState(loadCities);\n const [appearance, setAppearance] = useState(() => loadAppearance(SETTINGS_KEY));\n const [data, setData] = useState<CityWeather[]>([]);\n const [loading, setLoading] = useState(true);\n // Tick every minute so clocks update\n const [, setTick] = useState(0);\n useEffect(() => { const t = setInterval(() => setTick(n => n + 1), 60000); return () => clearInterval(t); }, []);\n const [settingsOpen, setSettingsOpen] = useState(false);\n const [configCities, setConfigCities] = useState<string[]>([]);\n const [configAppearance, setConfigAppearance] = useState<WidgetAppearance>(appearance);\n // Prefs live in the consumer-supplied prefs adapter so they persist\n // reliably across re-mounts without the local useState/localStorage dance.\n const { prefs: shellPrefs, save: saveShellPrefs } = useShellPrefs();\n const prefs: WeatherPrefs = { ...DEFAULT_PREFS, ...(shellPrefs.weather_prefs as WeatherPrefs | undefined ?? {}) };\n const [configPrefs, setConfigPrefs] = useState<WeatherPrefs>(prefs);\n\n useWidgetSettings(useCallback(() => {\n setConfigCities([...cities]);\n setConfigAppearance({ ...appearance });\n setConfigPrefs({ ...prefs });\n setSettingsOpen(true);\n }, [cities, appearance, prefs]));\n\n const fetchAll = useCallback(async (cityList: string[], force = false) => {\n if (!force) {\n try {\n const cached = JSON.parse(localStorage.getItem(CACHE_KEY) || '{}');\n const key = cityList.join(',');\n if (cached[key] && Date.now() - cached[key].ts < CACHE_TTL) {\n setData(cached[key].data); setLoading(false); return;\n }\n } catch {}\n }\n setLoading(true);\n const results: CityWeather[] = [];\n for (const city of cityList) {\n const coords = AVAILABLE_CITIES[city];\n if (!coords) continue;\n try {\n const res = await fetch(`https://api.open-meteo.com/v1/forecast?latitude=${coords.lat}&longitude=${coords.lon}¤t=temperature_2m,weather_code,is_day&daily=temperature_2m_max,temperature_2m_min&forecast_days=1&timezone=auto`);\n const w = await res.json();\n results.push({ city, temp: Math.round(w.current.temperature_2m), code: w.current.weather_code, high: Math.round(w.daily.temperature_2m_max[0]), low: Math.round(w.daily.temperature_2m_min[0]), isDay: w.current.is_day === 1, timezone: w.timezone || 'UTC' });\n } catch {}\n }\n setData(results); setLoading(false);\n try { const c = JSON.parse(localStorage.getItem(CACHE_KEY) || '{}'); c[cityList.join(',')] = { data: results, ts: Date.now() }; localStorage.setItem(CACHE_KEY, JSON.stringify(c)); } catch {}\n }, []);\n\n useEffect(() => { fetchAll(cities); }, [cities, fetchAll]);\n\n const saveSettings = () => {\n if (configCities.length === 0) return;\n setCities(configCities);\n setAppearance(configAppearance);\n saveShellPrefs({ weather_prefs: configPrefs });\n localStorage.setItem(STORAGE_KEY, JSON.stringify(configCities));\n localStorage.setItem(SETTINGS_KEY, JSON.stringify(configAppearance));\n setSettingsOpen(false);\n };\n\n const t = (c: number) => prefs.useFahrenheit ? `${toF(c)}°F` : `${c}°`;\n\n if (loading && data.length === 0) {\n return <div className=\"flex items-center justify-center h-full bg-gradient-to-b from-sky-400 to-blue-500 rounded-lg text-white/70 text-sm\">Loading...</div>;\n }\n\n // Cards are ~88 px tall (px-4 py-3 + 2 stacked text rows) + 8 px gap. Add\n // 16 px for the panel's own p-2 padding so the widget never collapses below\n // its rendered height.\n const dynamicHeight = data.length * 96 + 16;\n\n return (\n <>\n {/* Outer panel sets the user-tunable translucency via background alpha\n * (NOT `opacity`, which would also fade the row colors into gray) and\n * carries the rounded clip. Rows fill the panel edge-to-edge so each\n * city's day/night gradient reads at full saturation. */}\n <div className=\"flex flex-col rounded-lg text-white overflow-hidden\"\n style={{\n minHeight: dynamicHeight,\n backgroundColor: `rgba(15, 23, 42, ${appearance.activeOpacity / 100})`, // slate-900 with alpha\n backdropFilter: appearance.activeBlur > 0 ? `blur(${appearance.activeBlur}px)` : undefined,\n }}>\n {/* iOS-style city cards — each its own rounded tile sitting on the\n * panel's slate backdrop so the day/night gradient pops. Layout:\n * city + time on top-left, large temperature on top-right,\n * condition + H/L on the bottom row. */}\n <div className=\"flex-1 flex flex-col gap-2 p-2\">\n {data.map(d => {\n const [condition] = getCondition(d.code, d.isDay);\n const rowBg = d.isDay\n ? 'bg-gradient-to-br from-sky-400 via-sky-300 to-sky-500'\n : 'bg-gradient-to-br from-slate-800 via-blue-950 to-slate-900';\n return (\n <div key={d.city} className={`rounded-2xl px-4 py-3 flex flex-col justify-between gap-3 ${rowBg}`}>\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0 flex-1\">\n <div className=\"text-lg font-semibold leading-tight truncate\">{d.city}</div>\n {prefs.showLocalTime && (\n <div className=\"text-xs opacity-90 mt-0.5 tabular-nums\">\n {getTimeInTz(d.timezone, prefs.use24Hour).text}\n </div>\n )}\n </div>\n <div className=\"text-4xl font-extralight leading-none tracking-tight tabular-nums shrink-0\">\n {t(d.temp)}\n </div>\n </div>\n <div className=\"flex items-end justify-between text-[11px]\">\n <span className=\"opacity-95\">{condition}</span>\n <span className=\"opacity-90 tabular-nums\">H:{t(d.high)} L:{t(d.low)}</span>\n </div>\n </div>\n );\n })}\n </div>\n </div>\n\n <WidgetSettingsModal open={settingsOpen} onClose={() => setSettingsOpen(false)} title=\"Weather Settings\"\n appearance={configAppearance} onAppearanceChange={setConfigAppearance} onSave={saveSettings}>\n <div className=\"space-y-3\">\n <div>\n <h3 className=\"text-sm font-semibold text-gray-700 mb-2\">Display</h3>\n <div className=\"flex items-center gap-3 mb-2\">\n <span className=\"text-sm text-gray-600 w-24\">Temperature</span>\n <div className=\"flex gap-1\">\n {([{ key: false, label: '°C' }, { key: true, label: '°F' }] as const).map(o => (\n <button key={String(o.key)} onClick={() => setConfigPrefs(p => ({ ...p, useFahrenheit: o.key }))}\n className={`px-3 py-1 text-xs font-medium rounded-lg border transition-colors ${configPrefs.useFahrenheit === o.key ? 'bg-blue-600 text-white border-blue-600' : 'bg-white text-gray-700 border-gray-300 hover:bg-gray-50'}`}>\n {o.label}\n </button>\n ))}\n </div>\n </div>\n <div className=\"flex items-center gap-3 mb-2\">\n <span className=\"text-sm text-gray-600 w-24\">Time Format</span>\n <div className=\"flex gap-1\">\n {([{ key: false, label: 'AM/PM' }, { key: true, label: '24H' }] as const).map(o => (\n <button key={String(o.key)} onClick={() => setConfigPrefs(p => ({ ...p, use24Hour: o.key }))}\n className={`px-3 py-1 text-xs font-medium rounded-lg border transition-colors ${configPrefs.use24Hour === o.key ? 'bg-blue-600 text-white border-blue-600' : 'bg-white text-gray-700 border-gray-300 hover:bg-gray-50'}`}>\n {o.label}\n </button>\n ))}\n </div>\n </div>\n <label className=\"flex items-center gap-2 cursor-pointer\">\n <input type=\"checkbox\" checked={configPrefs.showLocalTime} onChange={e => setConfigPrefs(p => ({ ...p, showLocalTime: e.target.checked }))}\n className=\"rounded border-gray-300 text-blue-600 h-3.5 w-3.5\" />\n <span className=\"text-sm text-gray-600\">Show local time</span>\n </label>\n </div>\n </div>\n <div>\n <h3 className=\"text-sm font-semibold text-gray-700 mb-2\">Cities</h3>\n <div className=\"grid grid-cols-2 gap-1 max-h-48 overflow-y-auto\">\n {Object.keys(AVAILABLE_CITIES).map(city => (\n <label key={city} className=\"flex items-center gap-2 text-sm py-1 cursor-pointer hover:bg-gray-50 rounded px-2\">\n <input type=\"checkbox\" checked={configCities.includes(city)}\n onChange={() => setConfigCities(prev => prev.includes(city) ? prev.filter(c => c !== city) : [...prev, city])}\n className=\"rounded border-gray-300 text-blue-600 focus:ring-blue-500 h-3.5 w-3.5\" />\n {city}\n </label>\n ))}\n </div>\n </div>\n </WidgetSettingsModal>\n </>\n );\n}\n"]}
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import { ALL_TIMEZONES } from './chunk-7KZWBIDL.js';
|
|
2
|
-
import './chunk-PDFQNHW7.js';
|
|
3
|
-
import './chunk-NSU7OHPC.js';
|
|
4
|
-
import './chunk-46LICZUM.js';
|
|
5
|
-
import { useShellPrefs } from './chunk-36VM54SC.js';
|
|
6
|
-
import './chunk-D7PYW2QS.js';
|
|
7
|
-
import './chunk-WIJ45SYD.js';
|
|
8
|
-
import { loadAppearance, WidgetSettingsModal } from './chunk-TVOBLSSV.js';
|
|
9
|
-
import { useWidgetSettings } from './chunk-T2NQXP2J.js';
|
|
10
|
-
import './chunk-PLGHQ7QW.js';
|
|
11
|
-
import { useState, useEffect, useCallback } from 'react';
|
|
12
|
-
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
13
|
-
|
|
14
|
-
var DEFAULT_CLOCKS = ["Europe/London", "Asia/Shanghai", "America/Los_Angeles", "America/New_York"];
|
|
15
|
-
var SETTINGS_KEY = "world_clock_appearance";
|
|
16
|
-
function WorldClock() {
|
|
17
|
-
const [now, setNow] = useState(/* @__PURE__ */ new Date());
|
|
18
|
-
const [appearance, setAppearance] = useState(() => loadAppearance(SETTINGS_KEY));
|
|
19
|
-
const [settingsOpen, setSettingsOpen] = useState(false);
|
|
20
|
-
const [configClocks, setConfigClocks] = useState([]);
|
|
21
|
-
const [configAppearance, setConfigAppearance] = useState(appearance);
|
|
22
|
-
const { prefs, save } = useShellPrefs();
|
|
23
|
-
const worldClocks = prefs.world_clocks ?? DEFAULT_CLOCKS;
|
|
24
|
-
useEffect(() => {
|
|
25
|
-
const t = setInterval(() => setNow(/* @__PURE__ */ new Date()), 1e4);
|
|
26
|
-
return () => clearInterval(t);
|
|
27
|
-
}, []);
|
|
28
|
-
useWidgetSettings(useCallback(() => {
|
|
29
|
-
setConfigClocks([...worldClocks]);
|
|
30
|
-
setConfigAppearance({ ...appearance });
|
|
31
|
-
setSettingsOpen(true);
|
|
32
|
-
}, [worldClocks, appearance]));
|
|
33
|
-
const hourIn = (tz) => {
|
|
34
|
-
try {
|
|
35
|
-
const parts = new Intl.DateTimeFormat("en-US", { timeZone: tz, hour: "numeric", hour12: false }).formatToParts(now);
|
|
36
|
-
const h = parseInt(parts.find((p) => p.type === "hour")?.value || "0", 10);
|
|
37
|
-
return h === 24 ? 0 : h;
|
|
38
|
-
} catch {
|
|
39
|
-
return 12;
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
const isDay = (tz) => {
|
|
43
|
-
const h = hourIn(tz);
|
|
44
|
-
return h >= 6 && h < 18;
|
|
45
|
-
};
|
|
46
|
-
const fmtTime = (tz) => now.toLocaleTimeString(void 0, { hour: "2-digit", minute: "2-digit", timeZone: tz });
|
|
47
|
-
const fmtOffset = (tz) => {
|
|
48
|
-
const parts = new Intl.DateTimeFormat("en", { timeZone: tz, timeZoneName: "shortOffset" }).formatToParts(now);
|
|
49
|
-
return parts.find((p) => p.type === "timeZoneName")?.value || "";
|
|
50
|
-
};
|
|
51
|
-
const fmtDate = (tz) => now.toLocaleDateString(void 0, { weekday: "short", month: "short", day: "numeric", timeZone: tz });
|
|
52
|
-
const localTz = typeof localStorage !== "undefined" && localStorage.getItem("user_timezone") || Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
53
|
-
const saveSettings = () => {
|
|
54
|
-
if (configClocks.length === 0) return;
|
|
55
|
-
save({ world_clocks: configClocks });
|
|
56
|
-
setAppearance(configAppearance);
|
|
57
|
-
localStorage.setItem(SETTINGS_KEY, JSON.stringify(configAppearance));
|
|
58
|
-
setSettingsOpen(false);
|
|
59
|
-
};
|
|
60
|
-
const labelFor = (tz) => ALL_TIMEZONES.find((t) => t.tz === tz)?.label || tz.split("/").pop()?.replace(/_/g, " ") || tz;
|
|
61
|
-
const cards = [{ tz: localTz, isLocal: true }, ...worldClocks.map((tz) => ({ tz, isLocal: false }))];
|
|
62
|
-
const dynamicHeight = 96 + worldClocks.length * 84 + 16;
|
|
63
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
64
|
-
/* @__PURE__ */ jsx(
|
|
65
|
-
"div",
|
|
66
|
-
{
|
|
67
|
-
className: "flex flex-col rounded-lg text-white overflow-hidden",
|
|
68
|
-
style: {
|
|
69
|
-
minHeight: dynamicHeight,
|
|
70
|
-
backgroundColor: `rgba(15, 23, 42, ${appearance.activeOpacity / 100})`,
|
|
71
|
-
backdropFilter: appearance.activeBlur > 0 ? `blur(${appearance.activeBlur}px)` : void 0
|
|
72
|
-
},
|
|
73
|
-
children: /* @__PURE__ */ jsx("div", { className: "flex-1 flex flex-col gap-2 p-2", children: cards.map(({ tz, isLocal }) => {
|
|
74
|
-
const day = isDay(tz);
|
|
75
|
-
const rowBg = day ? "bg-gradient-to-br from-sky-400 via-sky-300 to-sky-500" : "bg-gradient-to-br from-slate-800 via-blue-950 to-slate-900";
|
|
76
|
-
const sub = isLocal ? `${fmtDate(tz)} \xB7 ${tz.replace(/_/g, " ")}` : `${fmtDate(tz)} \xB7 ${fmtOffset(tz)}`;
|
|
77
|
-
return /* @__PURE__ */ jsxs(
|
|
78
|
-
"div",
|
|
79
|
-
{
|
|
80
|
-
className: `rounded-2xl px-4 py-3 flex items-center justify-between gap-3 ${rowBg}`,
|
|
81
|
-
children: [
|
|
82
|
-
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
83
|
-
isLocal && /* @__PURE__ */ jsx("div", { className: "text-[10px] uppercase tracking-wide opacity-80 mb-0.5", children: "Local Time" }),
|
|
84
|
-
/* @__PURE__ */ jsx("div", { className: "text-base font-semibold leading-tight truncate", children: labelFor(tz) }),
|
|
85
|
-
/* @__PURE__ */ jsx("div", { className: "text-[10px] opacity-90 truncate mt-0.5", children: sub })
|
|
86
|
-
] }),
|
|
87
|
-
/* @__PURE__ */ jsx("div", { className: `${isLocal ? "text-3xl" : "text-2xl"} font-extralight leading-none tracking-tight tabular-nums shrink-0`, children: fmtTime(tz) })
|
|
88
|
-
]
|
|
89
|
-
},
|
|
90
|
-
(isLocal ? "local-" : "") + tz
|
|
91
|
-
);
|
|
92
|
-
}) })
|
|
93
|
-
}
|
|
94
|
-
),
|
|
95
|
-
/* @__PURE__ */ jsx(
|
|
96
|
-
WidgetSettingsModal,
|
|
97
|
-
{
|
|
98
|
-
open: settingsOpen,
|
|
99
|
-
onClose: () => setSettingsOpen(false),
|
|
100
|
-
title: "World Clock Settings",
|
|
101
|
-
appearance: configAppearance,
|
|
102
|
-
onAppearanceChange: setConfigAppearance,
|
|
103
|
-
onSave: saveSettings,
|
|
104
|
-
children: /* @__PURE__ */ jsxs("div", { children: [
|
|
105
|
-
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-gray-700 mb-2", children: "Cities" }),
|
|
106
|
-
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-1 max-h-56 overflow-y-auto", children: ALL_TIMEZONES.filter((t) => t.tz !== localTz).map(({ tz, label }) => /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 text-sm py-1 cursor-pointer hover:bg-gray-50 rounded px-2", children: [
|
|
107
|
-
/* @__PURE__ */ jsx(
|
|
108
|
-
"input",
|
|
109
|
-
{
|
|
110
|
-
type: "checkbox",
|
|
111
|
-
checked: configClocks.includes(tz),
|
|
112
|
-
onChange: () => setConfigClocks((prev) => prev.includes(tz) ? prev.filter((t) => t !== tz) : [...prev, tz]),
|
|
113
|
-
className: "rounded border-gray-300 text-blue-600 focus:ring-blue-500 h-3.5 w-3.5"
|
|
114
|
-
}
|
|
115
|
-
),
|
|
116
|
-
label
|
|
117
|
-
] }, tz)) })
|
|
118
|
-
] })
|
|
119
|
-
}
|
|
120
|
-
)
|
|
121
|
-
] });
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
export { WorldClock as default };
|
|
125
|
-
//# sourceMappingURL=WorldClock-OFK2EA2H.js.map
|
|
126
|
-
//# sourceMappingURL=WorldClock-OFK2EA2H.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/apps/WorldClock.tsx"],"names":[],"mappings":";;;;;;;;;;;;;AAMA,IAAM,cAAA,GAAiB,CAAC,eAAA,EAAiB,eAAA,EAAiB,uBAAuB,kBAAkB,CAAA;AACnG,IAAM,YAAA,GAAe,wBAAA;AAcN,SAAR,UAAA,GAA8B;AACnC,EAAA,MAAM,CAAC,GAAA,EAAK,MAAM,IAAI,QAAA,iBAAS,IAAI,MAAM,CAAA;AACzC,EAAA,MAAM,CAAC,YAAY,aAAa,CAAA,GAAI,SAAS,MAAM,cAAA,CAAe,YAAY,CAAC,CAAA;AAC/E,EAAA,MAAM,CAAC,YAAA,EAAc,eAAe,CAAA,GAAI,SAAS,KAAK,CAAA;AACtD,EAAA,MAAM,CAAC,YAAA,EAAc,eAAe,CAAA,GAAI,QAAA,CAAmB,EAAE,CAAA;AAC7D,EAAA,MAAM,CAAC,gBAAA,EAAkB,mBAAmB,CAAA,GAAI,SAA2B,UAAU,CAAA;AAErF,EAAA,MAAM,EAAE,KAAA,EAAO,IAAA,EAAK,GAAI,aAAA,EAAc;AACtC,EAAA,MAAM,WAAA,GAAyB,MAAM,YAAA,IAAyC,cAAA;AAE9E,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,CAAA,GAAI,YAAY,MAAM,MAAA,qBAAW,IAAA,EAAM,GAAG,GAAK,CAAA;AACrD,IAAA,OAAO,MAAM,cAAc,CAAC,CAAA;AAAA,EAC9B,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,iBAAA,CAAkB,YAAY,MAAM;AAClC,IAAA,eAAA,CAAgB,CAAC,GAAG,WAAW,CAAC,CAAA;AAChC,IAAA,mBAAA,CAAoB,EAAE,GAAG,UAAA,EAAY,CAAA;AACrC,IAAA,eAAA,CAAgB,IAAI,CAAA;AAAA,EACtB,CAAA,EAAG,CAAC,WAAA,EAAa,UAAU,CAAC,CAAC,CAAA;AAG7B,EAAA,MAAM,MAAA,GAAS,CAAC,EAAA,KAAuB;AACrC,IAAA,IAAI;AACF,MAAA,MAAM,KAAA,GAAQ,IAAI,IAAA,CAAK,cAAA,CAAe,SAAS,EAAE,QAAA,EAAU,EAAA,EAAI,IAAA,EAAM,WAAW,MAAA,EAAQ,KAAA,EAAO,CAAA,CAAE,cAAc,GAAG,CAAA;AAClH,MAAA,MAAM,CAAA,GAAI,QAAA,CAAS,KAAA,CAAM,IAAA,CAAK,CAAA,CAAA,KAAK,CAAA,CAAE,IAAA,KAAS,MAAM,CAAA,EAAG,KAAA,IAAS,GAAA,EAAK,EAAE,CAAA;AAIvE,MAAA,OAAO,CAAA,KAAM,KAAK,CAAA,GAAI,CAAA;AAAA,IACxB,CAAA,CAAA,MAAQ;AAAE,MAAA,OAAO,EAAA;AAAA,IAAI;AAAA,EACvB,CAAA;AAGA,EAAA,MAAM,KAAA,GAAQ,CAAC,EAAA,KAAe;AAAE,IAAA,MAAM,CAAA,GAAI,OAAO,EAAE,CAAA;AAAG,IAAA,OAAO,CAAA,IAAK,KAAK,CAAA,GAAI,EAAA;AAAA,EAAI,CAAA;AAE/E,EAAA,MAAM,OAAA,GAAU,CAAC,EAAA,KAAe,GAAA,CAAI,kBAAA,CAAmB,MAAA,EAAW,EAAE,IAAA,EAAM,SAAA,EAAW,MAAA,EAAQ,SAAA,EAAW,QAAA,EAAU,IAAI,CAAA;AACtH,EAAA,MAAM,SAAA,GAAY,CAAC,EAAA,KAAe;AAChC,IAAA,MAAM,KAAA,GAAQ,IAAI,IAAA,CAAK,cAAA,CAAe,IAAA,EAAM,EAAE,QAAA,EAAU,EAAA,EAAI,YAAA,EAAc,aAAA,EAAe,CAAA,CAAE,cAAc,GAAG,CAAA;AAC5G,IAAA,OAAO,MAAM,IAAA,CAAK,CAAA,CAAA,KAAK,EAAE,IAAA,KAAS,cAAc,GAAG,KAAA,IAAS,EAAA;AAAA,EAC9D,CAAA;AACA,EAAA,MAAM,OAAA,GAAU,CAAC,EAAA,KAAe,GAAA,CAAI,mBAAmB,MAAA,EAAW,EAAE,OAAA,EAAS,OAAA,EAAS,OAAO,OAAA,EAAS,GAAA,EAAK,SAAA,EAAW,QAAA,EAAU,IAAI,CAAA;AAEpI,EAAA,MAAM,OAAA,GAAW,OAAO,YAAA,KAAiB,WAAA,IAAe,YAAA,CAAa,OAAA,CAAQ,eAAe,CAAA,IACvF,IAAA,CAAK,cAAA,EAAe,CAAE,eAAA,EAAgB,CAAE,QAAA;AAE7C,EAAA,MAAM,eAAe,MAAM;AACzB,IAAA,IAAI,YAAA,CAAa,WAAW,CAAA,EAAG;AAC/B,IAAA,IAAA,CAAK,EAAE,YAAA,EAAc,YAAA,EAAc,CAAA;AACnC,IAAA,aAAA,CAAc,gBAAgB,CAAA;AAC9B,IAAA,YAAA,CAAa,OAAA,CAAQ,YAAA,EAAc,IAAA,CAAK,SAAA,CAAU,gBAAgB,CAAC,CAAA;AACnE,IAAA,eAAA,CAAgB,KAAK,CAAA;AAAA,EACvB,CAAA;AAEA,EAAA,MAAM,QAAA,GAAW,CAAC,EAAA,KAChB,aAAA,CAAc,KAAK,CAAA,CAAA,KAAK,CAAA,CAAE,OAAO,EAAE,CAAA,EAAG,SACnC,EAAA,CAAG,KAAA,CAAM,GAAG,CAAA,CAAE,GAAA,IAAO,OAAA,CAAQ,IAAA,EAAM,GAAG,CAAA,IACtC,EAAA;AAML,EAAA,MAAM,QAAQ,CAAC,EAAE,EAAA,EAAI,OAAA,EAAS,SAAS,IAAA,EAAK,EAAG,GAAG,WAAA,CAAY,IAAI,CAAA,EAAA,MAAO,EAAE,IAAI,OAAA,EAAS,KAAA,GAAQ,CAAC,CAAA;AAIjG,EAAA,MAAM,aAAA,GAAgB,EAAA,GAAsB,WAAA,CAAY,MAAA,GAAS,EAAA,GAAsB,EAAA;AAEvF,EAAA,uBACE,IAAA,CAAA,QAAA,EAAA,EACE,QAAA,EAAA;AAAA,oBAAA,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QAAI,SAAA,EAAU,qDAAA;AAAA,QACb,KAAA,EAAO;AAAA,UACL,SAAA,EAAW,aAAA;AAAA,UACX,eAAA,EAAiB,CAAA,iBAAA,EAAoB,UAAA,CAAW,aAAA,GAAgB,GAAG,CAAA,CAAA,CAAA;AAAA,UACnE,gBAAgB,UAAA,CAAW,UAAA,GAAa,IAAI,CAAA,KAAA,EAAQ,UAAA,CAAW,UAAU,CAAA,GAAA,CAAA,GAAQ;AAAA,SACnF;AAAA,QACA,QAAA,kBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,gCAAA,EACZ,QAAA,EAAA,KAAA,CAAM,IAAI,CAAC,EAAE,EAAA,EAAI,OAAA,EAAQ,KAAM;AAC9B,UAAA,MAAM,GAAA,GAAM,MAAM,EAAE,CAAA;AACpB,UAAA,MAAM,KAAA,GAAQ,MACV,uDAAA,GACA,4DAAA;AACJ,UAAA,MAAM,GAAA,GAAM,UACR,CAAA,EAAG,OAAA,CAAQ,EAAE,CAAC,CAAA,MAAA,EAAM,GAAG,OAAA,CAAQ,IAAA,EAAM,GAAG,CAAC,CAAA,CAAA,GACzC,GAAG,OAAA,CAAQ,EAAE,CAAC,CAAA,MAAA,EAAM,SAAA,CAAU,EAAE,CAAC,CAAA,CAAA;AACrC,UAAA,uBACE,IAAA;AAAA,YAAC,KAAA;AAAA,YAAA;AAAA,cACC,SAAA,EAAW,iEAAiE,KAAK,CAAA,CAAA;AAAA,cACjF,QAAA,EAAA;AAAA,gCAAA,IAAA,CAAC,KAAA,EAAA,EAAI,WAAU,gBAAA,EACZ,QAAA,EAAA;AAAA,kBAAA,OAAA,oBACC,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,uDAAA,EAAwD,QAAA,EAAA,YAAA,EAAU,CAAA;AAAA,sCAElF,KAAA,EAAA,EAAI,SAAA,EAAU,gDAAA,EAAkD,QAAA,EAAA,QAAA,CAAS,EAAE,CAAA,EAAE,CAAA;AAAA,kCAC9E,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,wCAAA,EAA0C,QAAA,EAAA,GAAA,EAAI;AAAA,iBAAA,EAC/D,CAAA;AAAA,gCACA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,CAAA,EAAG,OAAA,GAAU,aAAa,UAAU,CAAA,kEAAA,CAAA,EACjD,QAAA,EAAA,OAAA,CAAQ,EAAE,CAAA,EACb;AAAA;AAAA,aAAA;AAAA,YAAA,CAXS,OAAA,GAAU,WAAW,EAAA,IAAM;AAAA,WAYtC;AAAA,QAEJ,CAAC,CAAA,EACH;AAAA;AAAA,KACF;AAAA,oBAEA,GAAA;AAAA,MAAC,mBAAA;AAAA,MAAA;AAAA,QAAoB,IAAA,EAAM,YAAA;AAAA,QAAc,OAAA,EAAS,MAAM,eAAA,CAAgB,KAAK,CAAA;AAAA,QAAG,KAAA,EAAM,sBAAA;AAAA,QACpF,UAAA,EAAY,gBAAA;AAAA,QAAkB,kBAAA,EAAoB,mBAAA;AAAA,QAAqB,MAAA,EAAQ,YAAA;AAAA,QAC/E,+BAAC,KAAA,EAAA,EACC,QAAA,EAAA;AAAA,0BAAA,GAAA,CAAC,IAAA,EAAA,EAAG,SAAA,EAAU,0CAAA,EAA2C,QAAA,EAAA,QAAA,EAAM,CAAA;AAAA,0BAC/D,GAAA,CAAC,SAAI,SAAA,EAAU,iDAAA,EACZ,wBAAc,MAAA,CAAO,CAAA,CAAA,KAAK,EAAE,EAAA,KAAO,OAAO,EAAE,GAAA,CAAI,CAAC,EAAE,EAAA,EAAI,KAAA,uBACtD,IAAA,CAAC,OAAA,EAAA,EAAe,WAAU,mFAAA,EACxB,QAAA,EAAA;AAAA,4BAAA,GAAA;AAAA,cAAC,OAAA;AAAA,cAAA;AAAA,gBAAM,IAAA,EAAK,UAAA;AAAA,gBAAW,OAAA,EAAS,YAAA,CAAa,QAAA,CAAS,EAAE,CAAA;AAAA,gBACtD,UAAU,MAAM,eAAA,CAAgB,UAAQ,IAAA,CAAK,QAAA,CAAS,EAAE,CAAA,GAAI,IAAA,CAAK,MAAA,CAAO,CAAA,CAAA,KAAK,MAAM,EAAE,CAAA,GAAI,CAAC,GAAG,IAAA,EAAM,EAAE,CAAC,CAAA;AAAA,gBACtG,SAAA,EAAU;AAAA;AAAA,aAAwE;AAAA,YACnF;AAAA,WAAA,EAAA,EAJS,EAKZ,CACD,CAAA,EACH;AAAA,SAAA,EACF;AAAA;AAAA;AACF,GAAA,EACF,CAAA;AAEJ","file":"WorldClock-OFK2EA2H.js","sourcesContent":["import { useState, useEffect, useCallback } from 'react';\nimport { useWidgetSettings } from '../shell/Modal';\nimport WidgetSettingsModal, { loadAppearance, type WidgetAppearance } from '../shell/WidgetSettingsModal';\nimport { useShellPrefs } from '../shell/ShellPrefs';\nimport { ALL_TIMEZONES } from '../shell/Layout';\n\nconst DEFAULT_CLOCKS = ['Europe/London', 'Asia/Shanghai', 'America/Los_Angeles', 'America/New_York'];\nconst SETTINGS_KEY = 'world_clock_appearance';\n\n/**\n * World Clock widget — iOS-style city cards with a per-row day/night\n * gradient that flips based on the local hour at each city. Day cards use\n * a bright sky blue, night cards a deep navy — same palette as the\n * Weather widget so the two read as a set when stacked.\n *\n * Settings (city list + appearance sliders) live in the right-click menu\n * via `useWidgetSettings` — there is no inline \"+ Add World Clock\"\n * button. Translucency is applied as a background-color alpha on the\n * outer panel (slate-900 base) so the row gradients stay vivid even at\n * lower opacity.\n */\nexport default function WorldClock() {\n const [now, setNow] = useState(new Date());\n const [appearance, setAppearance] = useState(() => loadAppearance(SETTINGS_KEY));\n const [settingsOpen, setSettingsOpen] = useState(false);\n const [configClocks, setConfigClocks] = useState<string[]>([]);\n const [configAppearance, setConfigAppearance] = useState<WidgetAppearance>(appearance);\n\n const { prefs, save } = useShellPrefs();\n const worldClocks: string[] = (prefs.world_clocks as string[] | undefined) ?? DEFAULT_CLOCKS;\n\n useEffect(() => {\n const t = setInterval(() => setNow(new Date()), 10000);\n return () => clearInterval(t);\n }, []);\n\n useWidgetSettings(useCallback(() => {\n setConfigClocks([...worldClocks]);\n setConfigAppearance({ ...appearance });\n setSettingsOpen(true);\n }, [worldClocks, appearance]));\n\n /** Local hour (0–23) at the given timezone — used to flip day/night. */\n const hourIn = (tz: string): number => {\n try {\n const parts = new Intl.DateTimeFormat('en-US', { timeZone: tz, hour: 'numeric', hour12: false }).formatToParts(now);\n const h = parseInt(parts.find(p => p.type === 'hour')?.value || '0', 10);\n // `hourCycle: h23` would be cleanest but isn't universally supported;\n // `hour12: false` returns 24 for midnight on some Node versions, so\n // normalize that to 0.\n return h === 24 ? 0 : h;\n } catch { return 12; }\n };\n /** Crude sunrise/sunset proxy — good enough for \"should this card look\n * bright or dark.\" Real solar calc would need the city's latitude. */\n const isDay = (tz: string) => { const h = hourIn(tz); return h >= 6 && h < 18; };\n\n const fmtTime = (tz: string) => now.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit', timeZone: tz });\n const fmtOffset = (tz: string) => {\n const parts = new Intl.DateTimeFormat('en', { timeZone: tz, timeZoneName: 'shortOffset' }).formatToParts(now);\n return parts.find(p => p.type === 'timeZoneName')?.value || '';\n };\n const fmtDate = (tz: string) => now.toLocaleDateString(undefined, { weekday: 'short', month: 'short', day: 'numeric', timeZone: tz });\n\n const localTz = (typeof localStorage !== 'undefined' && localStorage.getItem('user_timezone'))\n || Intl.DateTimeFormat().resolvedOptions().timeZone;\n\n const saveSettings = () => {\n if (configClocks.length === 0) return;\n save({ world_clocks: configClocks });\n setAppearance(configAppearance);\n localStorage.setItem(SETTINGS_KEY, JSON.stringify(configAppearance));\n setSettingsOpen(false);\n };\n\n const labelFor = (tz: string) =>\n ALL_TIMEZONES.find(t => t.tz === tz)?.label\n || tz.split('/').pop()?.replace(/_/g, ' ')\n || tz;\n\n // Local time gets the \"featured\" card at the top, then the user's chosen\n // cities. Each card is its own day/night-coloured tile sitting on the\n // panel's slate backdrop with `gap-2` between cards — same idiom as\n // Weather.\n const cards = [{ tz: localTz, isLocal: true }, ...worldClocks.map(tz => ({ tz, isLocal: false }))];\n\n // Card heights: featured local-time card is taller (3 lines + larger\n // time), each city card is ~76 px. Plus the panel's p-2 padding.\n const dynamicHeight = 96 /* local card */ + worldClocks.length * 84 /* city cards */ + 16;\n\n return (\n <>\n <div className=\"flex flex-col rounded-lg text-white overflow-hidden\"\n style={{\n minHeight: dynamicHeight,\n backgroundColor: `rgba(15, 23, 42, ${appearance.activeOpacity / 100})`,\n backdropFilter: appearance.activeBlur > 0 ? `blur(${appearance.activeBlur}px)` : undefined,\n }}>\n <div className=\"flex-1 flex flex-col gap-2 p-2\">\n {cards.map(({ tz, isLocal }) => {\n const day = isDay(tz);\n const rowBg = day\n ? 'bg-gradient-to-br from-sky-400 via-sky-300 to-sky-500'\n : 'bg-gradient-to-br from-slate-800 via-blue-950 to-slate-900';\n const sub = isLocal\n ? `${fmtDate(tz)} · ${tz.replace(/_/g, ' ')}`\n : `${fmtDate(tz)} · ${fmtOffset(tz)}`;\n return (\n <div key={(isLocal ? 'local-' : '') + tz}\n className={`rounded-2xl px-4 py-3 flex items-center justify-between gap-3 ${rowBg}`}>\n <div className=\"min-w-0 flex-1\">\n {isLocal && (\n <div className=\"text-[10px] uppercase tracking-wide opacity-80 mb-0.5\">Local Time</div>\n )}\n <div className=\"text-base font-semibold leading-tight truncate\">{labelFor(tz)}</div>\n <div className=\"text-[10px] opacity-90 truncate mt-0.5\">{sub}</div>\n </div>\n <div className={`${isLocal ? 'text-3xl' : 'text-2xl'} font-extralight leading-none tracking-tight tabular-nums shrink-0`}>\n {fmtTime(tz)}\n </div>\n </div>\n );\n })}\n </div>\n </div>\n\n <WidgetSettingsModal open={settingsOpen} onClose={() => setSettingsOpen(false)} title=\"World Clock Settings\"\n appearance={configAppearance} onAppearanceChange={setConfigAppearance} onSave={saveSettings}>\n <div>\n <h3 className=\"text-sm font-semibold text-gray-700 mb-2\">Cities</h3>\n <div className=\"grid grid-cols-2 gap-1 max-h-56 overflow-y-auto\">\n {ALL_TIMEZONES.filter(t => t.tz !== localTz).map(({ tz, label }) => (\n <label key={tz} className=\"flex items-center gap-2 text-sm py-1 cursor-pointer hover:bg-gray-50 rounded px-2\">\n <input type=\"checkbox\" checked={configClocks.includes(tz)}\n onChange={() => setConfigClocks(prev => prev.includes(tz) ? prev.filter(t => t !== tz) : [...prev, tz])}\n className=\"rounded border-gray-300 text-blue-600 focus:ring-blue-500 h-3.5 w-3.5\" />\n {label}\n </label>\n ))}\n </div>\n </div>\n </WidgetSettingsModal>\n </>\n );\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hooks/useGoogleAuth.ts"],"names":[],"mappings":"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","file":"chunk-46LICZUM.js","sourcesContent":["/**\n * Google OAuth2 hook for Gmail, Calendar, and Gemini access.\n *\n * Uses Google Identity Services (GIS) to get an access token with combined scopes.\n * Requires a Google Cloud OAuth2 Client ID configured in System Settings.\n *\n * Scopes requested:\n * - Gmail: read, compose, send, modify\n * - Calendar: read, write events\n * - Gemini: generative language (via Vertex AI or Google AI)\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\n\nconst SCOPES = [\n 'https://www.googleapis.com/auth/gmail.readonly',\n 'https://www.googleapis.com/auth/gmail.compose',\n 'https://www.googleapis.com/auth/gmail.send',\n 'https://www.googleapis.com/auth/gmail.modify',\n 'https://www.googleapis.com/auth/calendar.readonly',\n 'https://www.googleapis.com/auth/calendar.events',\n 'https://www.googleapis.com/auth/generative-language.retriever',\n].join(' ');\n\nconst TOKEN_KEY = 'google_access_token';\nconst TOKEN_EXPIRY_KEY = 'google_token_expiry';\nconst USER_KEY = 'google_user_info';\nconst CLIENT_ID_KEY = 'google_oauth_client_id';\n\ninterface GoogleUser {\n email: string;\n name: string;\n picture: string;\n}\n\ninterface GoogleAuthState {\n isConnected: boolean;\n user: GoogleUser | null;\n accessToken: string | null;\n loading: boolean;\n error: string | null;\n connect: () => void;\n disconnect: () => void;\n getClientId: () => string;\n setClientId: (id: string) => void;\n hasClientId: boolean;\n}\n\n// Load GIS script\nlet gisLoaded = false;\nlet gisLoadPromise: Promise<void> | null = null;\n\nfunction loadGisScript(): Promise<void> {\n if (gisLoaded) return Promise.resolve();\n if (gisLoadPromise) return gisLoadPromise;\n\n gisLoadPromise = new Promise((resolve, reject) => {\n const script = document.createElement('script');\n script.src = 'https://accounts.google.com/gsi/client';\n script.async = true;\n script.defer = true;\n script.onload = () => { gisLoaded = true; resolve(); };\n script.onerror = () => reject(new Error('Failed to load Google Identity Services'));\n document.head.appendChild(script);\n });\n return gisLoadPromise;\n}\n\nfunction isTokenValid(): boolean {\n const expiry = localStorage.getItem(TOKEN_EXPIRY_KEY);\n if (!expiry) return false;\n return Date.now() < parseInt(expiry, 10) - 60000; // 1 min buffer\n}\n\nexport function getGoogleAccessToken(): string | null {\n if (!isTokenValid()) return null;\n return localStorage.getItem(TOKEN_KEY);\n}\n\nexport function getGoogleClientId(): string {\n return localStorage.getItem(CLIENT_ID_KEY) || '';\n}\n\nexport default function useGoogleAuth(): GoogleAuthState {\n const [accessToken, setAccessToken] = useState<string | null>(() => isTokenValid() ? localStorage.getItem(TOKEN_KEY) : null);\n const [user, setUser] = useState<GoogleUser | null>(() => {\n try { const u = localStorage.getItem(USER_KEY); return u ? JSON.parse(u) : null; } catch { return null; }\n });\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const clientRef = useRef<any>(null);\n // Tracks an in-flight silent renewal so handleTokenResponse can suppress\n // its loading/error UI when the request didn't come from a user click.\n const silentInFlightRef = useRef(false);\n // setTimeout handle for the next scheduled silent renewal.\n const refreshTimerRef = useRef<number | null>(null);\n\n const clientId = getGoogleClientId();\n const hasClientId = !!clientId;\n\n const setClientId = useCallback((id: string) => {\n localStorage.setItem(CLIENT_ID_KEY, id);\n window.dispatchEvent(new Event('google-client-id-changed'));\n }, []);\n\n // Fetch user info from Google\n const fetchUserInfo = useCallback(async (token: string) => {\n try {\n const res = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', {\n headers: { Authorization: `Bearer ${token}` },\n });\n if (res.ok) {\n const data = await res.json();\n const userInfo: GoogleUser = { email: data.email, name: data.name, picture: data.picture };\n localStorage.setItem(USER_KEY, JSON.stringify(userInfo));\n setUser(userInfo);\n }\n } catch { /* ignore */ }\n }, []);\n\n const cancelRefreshTimer = useCallback(() => {\n if (refreshTimerRef.current !== null) {\n window.clearTimeout(refreshTimerRef.current);\n refreshTimerRef.current = null;\n }\n }, []);\n\n // Schedule a silent refresh to fire ~60s before the current token expires.\n // Google reissues a fresh token without showing UI when the user's Google\n // session is still active and they previously granted consent.\n const scheduleSilentRefresh = useCallback(() => {\n cancelRefreshTimer();\n const expiry = parseInt(localStorage.getItem(TOKEN_EXPIRY_KEY) || '0', 10);\n if (!expiry) return;\n const delay = Math.max(0, expiry - Date.now() - 60_000);\n refreshTimerRef.current = window.setTimeout(() => {\n refreshTimerRef.current = null;\n if (!clientRef.current) return;\n silentInFlightRef.current = true;\n try {\n clientRef.current.requestAccessToken({ prompt: '' });\n } catch {\n silentInFlightRef.current = false;\n }\n }, delay);\n }, [cancelRefreshTimer]);\n\n const handleTokenResponse = useCallback((response: any) => {\n const wasSilent = silentInFlightRef.current;\n silentInFlightRef.current = false;\n if (response.error) {\n if (wasSilent) {\n // Silent renewal failed (user signed out of Google, revoked access,\n // session expired, etc.). Drop the token quietly — the consumer\n // sees `isConnected = false` and the Connect button reappears.\n localStorage.removeItem(TOKEN_KEY);\n localStorage.removeItem(TOKEN_EXPIRY_KEY);\n setAccessToken(null);\n } else {\n setError(response.error);\n }\n setLoading(false);\n return;\n }\n const token = response.access_token;\n const expiresIn = response.expires_in || 3600;\n localStorage.setItem(TOKEN_KEY, token);\n localStorage.setItem(TOKEN_EXPIRY_KEY, String(Date.now() + expiresIn * 1000));\n setAccessToken(token);\n setError(null);\n setLoading(false);\n if (!wasSilent) fetchUserInfo(token);\n // Chain the next silent refresh.\n scheduleSilentRefresh();\n }, [fetchUserInfo, scheduleSilentRefresh]);\n\n // Initialize GIS client. Once ready, schedule a silent refresh if we\n // already hold a valid token (e.g. user just reopened the tab with time\n // left on the clock) — and if the token has actually expired, request a\n // fresh one silently so they don't have to click Connect again.\n useEffect(() => {\n if (!clientId) return;\n loadGisScript().then(() => {\n const google = (window as any).google;\n if (!google?.accounts?.oauth2) return;\n clientRef.current = google.accounts.oauth2.initTokenClient({\n client_id: clientId,\n scope: SCOPES,\n callback: handleTokenResponse,\n });\n if (isTokenValid()) {\n scheduleSilentRefresh();\n } else if (localStorage.getItem(TOKEN_KEY)) {\n // We had a token last session but it's now expired. Try silent\n // renewal — succeeds if the Google session is still alive.\n silentInFlightRef.current = true;\n try {\n clientRef.current.requestAccessToken({ prompt: '' });\n } catch {\n silentInFlightRef.current = false;\n }\n }\n }).catch(err => setError(err.message));\n return () => cancelRefreshTimer();\n }, [clientId, handleTokenResponse, scheduleSilentRefresh, cancelRefreshTimer]);\n\n const connect = useCallback(() => {\n if (!clientRef.current) {\n setError('Google client not initialized. Check your Client ID.');\n return;\n }\n setLoading(true);\n setError(null);\n clientRef.current.requestAccessToken({ prompt: 'consent' });\n }, []);\n\n const disconnect = useCallback(() => {\n cancelRefreshTimer();\n const token = localStorage.getItem(TOKEN_KEY);\n if (token) {\n const google = (window as any).google;\n if (google?.accounts?.oauth2) {\n google.accounts.oauth2.revoke(token);\n }\n }\n localStorage.removeItem(TOKEN_KEY);\n localStorage.removeItem(TOKEN_EXPIRY_KEY);\n localStorage.removeItem(USER_KEY);\n setAccessToken(null);\n setUser(null);\n }, [cancelRefreshTimer]);\n\n // Belt-and-suspenders expiry check. Most expiries are caught by the\n // scheduled refresh above; this fires every 30s if the timer ever\n // misses (e.g. setTimeout drift after long sleep).\n useEffect(() => {\n const interval = setInterval(() => {\n if (accessToken && !isTokenValid()) {\n setAccessToken(null);\n // Try one silent refresh before giving up.\n if (clientRef.current && !silentInFlightRef.current) {\n silentInFlightRef.current = true;\n try { clientRef.current.requestAccessToken({ prompt: '' }); }\n catch { silentInFlightRef.current = false; }\n }\n }\n }, 30000);\n return () => clearInterval(interval);\n }, [accessToken]);\n\n return {\n isConnected: !!accessToken && isTokenValid(),\n user,\n accessToken,\n loading,\n error,\n connect,\n disconnect,\n getClientId: () => clientId,\n setClientId,\n hasClientId,\n };\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/apps/google-demo-fixtures.ts"],"names":[],"mappings":"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","file":"chunk-62MVMTBT.js","sourcesContent":["/**\n * Demo-mode fixtures for the Google apps (Email, Calendar). Used when\n * `window.__REACT_OS_SHELL_DEMO_MODE__ === true` so the demo deployment\n * can show populated UIs without requiring a Google OAuth Client ID.\n *\n * Imported only by the demo-mode renderers — the real Gmail/Calendar\n * code paths never touch this file.\n */\n\nexport interface DemoEmail {\n id: string;\n from: string;\n subject: string;\n snippet: string;\n body: string;\n receivedAt: string; // relative to now\n unread: boolean;\n}\n\n// Shape matches Calendar.tsx's CalendarEvent so demo fixtures can be\n// dropped straight into its events list without translation.\nexport interface DemoCalendarEvent {\n id: string;\n title: string;\n date: string; // YYYY-MM-DD\n start_time?: string; // HH:MM\n end_time?: string; // HH:MM\n color: string; // matches the COLORS keys in Calendar.tsx\n description?: string;\n}\n\nexport function getDemoEmails(): DemoEmail[] {\n const now = Date.now();\n const m = (mins: number) => new Date(now - mins * 60_000).toISOString();\n return [\n {\n id: 'demo-1',\n from: 'Calendar <calendar-noreply@example.com>',\n subject: 'Reminder: Design review at 3:00 PM',\n snippet: 'You have a design review starting in 30 minutes with the platform team…',\n body: 'You have a design review starting in 30 minutes with the platform team. Agenda is in the doc — please skim before joining.',\n receivedAt: m(15),\n unread: true,\n },\n {\n id: 'demo-2',\n from: 'Sam Patel <sam@example.com>',\n subject: 'Re: STEP file from procurement',\n snippet: \"Got it — I'll drop the file in the shared folder by EOD. Let me know if the cap thickness needs to change.\",\n body: \"Got it — I'll drop the file in the shared folder by EOD. Let me know if the cap thickness needs to change.\\n\\nThanks,\\nSam\",\n receivedAt: m(85),\n unread: true,\n },\n {\n id: 'demo-3',\n from: 'GitHub <noreply@github.com>',\n subject: '[react-os-shell] PR #42 ready for review',\n snippet: 'A new pull request has been opened in your repository react-os-shell.',\n body: 'A new pull request has been opened in your repository react-os-shell.\\n\\n#42: feat(preview): capped section view\\n\\nView on GitHub: https://github.com/...',\n receivedAt: m(180),\n unread: false,\n },\n {\n id: 'demo-4',\n from: 'Stripe <noreply@stripe.com>',\n subject: 'Your Stripe payout has been processed',\n snippet: 'Your payout of $1,247.32 has been deposited to your bank account.',\n body: 'Your payout of $1,247.32 has been deposited to your bank account ending in 4242.\\n\\nView details in your Dashboard.',\n receivedAt: m(310),\n unread: false,\n },\n {\n id: 'demo-5',\n from: 'Maya Lin <maya.lin@example.com>',\n subject: 'Lunch tomorrow?',\n snippet: 'I have a couple options open — that ramen place on 4th, or the new Korean place near you?',\n body: 'I have a couple options open — that ramen place on 4th, or the new Korean place near you? 12:30?',\n receivedAt: m(720),\n unread: false,\n },\n {\n id: 'demo-6',\n from: 'AWS Billing <no-reply@aws.amazon.com>',\n subject: 'Your monthly AWS invoice',\n snippet: 'Your invoice for the period ending Apr 30 is now available.',\n body: 'Your invoice for the period ending April 30 is now available in the AWS console. Total: $84.21.',\n receivedAt: m(1440),\n unread: false,\n },\n ];\n}\n\n// 6 events scattered across the current week. The dates are recomputed\n// relative to \"now\" each time the function runs, so the demo always\n// feels current.\nexport function getDemoCalendarEvents(): DemoCalendarEvent[] {\n const now = new Date();\n const dayOfWeek = now.getDay();\n const weekStart = new Date(now);\n weekStart.setDate(now.getDate() - dayOfWeek);\n const dateAt = (dayOffset: number) => {\n const d = new Date(weekStart);\n d.setDate(weekStart.getDate() + dayOffset);\n return d.toISOString().split('T')[0];\n };\n return [\n { id: 'demo-event-1', title: 'Standup', date: dateAt(1), start_time: '09:30', end_time: '10:00', color: 'blue' },\n { id: 'demo-event-2', title: 'Design review', date: dateAt(2), start_time: '15:00', end_time: '16:00', color: 'purple', description: 'Platform team — Conference Room B' },\n { id: 'demo-event-3', title: 'Lunch with Maya', date: dateAt(3), start_time: '12:30', end_time: '13:30', color: 'orange', description: 'Ramen on 4th' },\n { id: 'demo-event-4', title: '1:1 with manager',date: dateAt(3), start_time: '16:00', end_time: '16:30', color: 'green' },\n { id: 'demo-event-5', title: 'Sprint planning', date: dateAt(4), start_time: '10:00', end_time: '11:30', color: 'pink' },\n { id: 'demo-event-6', title: 'Focus time', date: dateAt(5), start_time: '14:00', end_time: '17:00', color: 'gray' },\n ];\n}\n\nexport function isDemoMode(): boolean {\n return typeof window !== 'undefined' && (window as any).__REACT_OS_SHELL_DEMO_MODE__ === true;\n}\n"]}
|