react-os-shell 4.9.0 → 4.11.0

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.
Files changed (46) hide show
  1. package/README.md +16 -1
  2. package/dist/{Browser-7AQ7Y4Z7.js → Browser-V3F6MZ6J.js} +5 -5
  3. package/dist/{Browser-7AQ7Y4Z7.js.map → Browser-V3F6MZ6J.js.map} +1 -1
  4. package/dist/{Calculator-B7KJQ2NF.js → Calculator-U7JLNROS.js} +4 -4
  5. package/dist/{Calculator-B7KJQ2NF.js.map → Calculator-U7JLNROS.js.map} +1 -1
  6. package/dist/ConfirmDialog-Y4BYT77R.js +4 -0
  7. package/dist/{ConfirmDialog-U5H3MP3M.js.map → ConfirmDialog-Y4BYT77R.js.map} +1 -1
  8. package/dist/{CurrencyConverter-AILEZUIU.js → CurrencyConverter-2UFQ75WE.js} +4 -4
  9. package/dist/{CurrencyConverter-AILEZUIU.js.map → CurrencyConverter-2UFQ75WE.js.map} +1 -1
  10. package/dist/{Documents-IX6JISOV.js → Documents-3V7XPDOS.js} +5 -5
  11. package/dist/{Documents-IX6JISOV.js.map → Documents-3V7XPDOS.js.map} +1 -1
  12. package/dist/{Files-O7WK4TQG.js → Files-TH37G6YY.js} +5 -5
  13. package/dist/{Files-O7WK4TQG.js.map → Files-TH37G6YY.js.map} +1 -1
  14. package/dist/{Notepad-LO7FRU34.js → Notepad-OXSKELWV.js} +5 -5
  15. package/dist/{Notepad-LO7FRU34.js.map → Notepad-OXSKELWV.js.map} +1 -1
  16. package/dist/{PomodoroTimer-DSVXJ7D4.js → PomodoroTimer-Q53KOASQ.js} +5 -5
  17. package/dist/{PomodoroTimer-DSVXJ7D4.js.map → PomodoroTimer-Q53KOASQ.js.map} +1 -1
  18. package/dist/{Preview-QCAN5EU3.js → Preview-J6YDYCX4.js} +5 -5
  19. package/dist/{Preview-QCAN5EU3.js.map → Preview-J6YDYCX4.js.map} +1 -1
  20. package/dist/{Spreadsheet-SUP2H2B4.js → Spreadsheet-BSBZ46CE.js} +5 -5
  21. package/dist/{Spreadsheet-SUP2H2B4.js.map → Spreadsheet-BSBZ46CE.js.map} +1 -1
  22. package/dist/{Stock-UT2JMEVS.js → Stock-SRUFZGQC.js} +4 -4
  23. package/dist/{Stock-UT2JMEVS.js.map → Stock-SRUFZGQC.js.map} +1 -1
  24. package/dist/{Weather-37TNIYWI.js → Weather-47E3IRKF.js} +4 -4
  25. package/dist/{Weather-37TNIYWI.js.map → Weather-47E3IRKF.js.map} +1 -1
  26. package/dist/{WorldClock-EESGFXPN.js → WorldClock-7L2YHNC3.js} +4 -4
  27. package/dist/{WorldClock-EESGFXPN.js.map → WorldClock-7L2YHNC3.js.map} +1 -1
  28. package/dist/apps/index.js +15 -15
  29. package/dist/{chunk-G6AUDFI4.js → chunk-E2EGGV2Z.js} +121 -41
  30. package/dist/chunk-E2EGGV2Z.js.map +1 -0
  31. package/dist/{chunk-TIKW3XWE.js → chunk-J4XK47N3.js} +4 -4
  32. package/dist/{chunk-TIKW3XWE.js.map → chunk-J4XK47N3.js.map} +1 -1
  33. package/dist/{chunk-UH3H3KAN.js → chunk-JROEOUJD.js} +3 -3
  34. package/dist/{chunk-UH3H3KAN.js.map → chunk-JROEOUJD.js.map} +1 -1
  35. package/dist/chunk-VG2WSTEJ.js +7 -0
  36. package/dist/{chunk-NQTQPOT4.js.map → chunk-VG2WSTEJ.js.map} +1 -1
  37. package/dist/{chunk-BOTXJSDK.js → chunk-XVHRGMS3.js} +3 -3
  38. package/dist/{chunk-BOTXJSDK.js.map → chunk-XVHRGMS3.js.map} +1 -1
  39. package/dist/index.d.ts +25 -6
  40. package/dist/index.js +24 -6
  41. package/dist/index.js.map +1 -1
  42. package/dist/styles.css +4 -3
  43. package/package.json +2 -1
  44. package/dist/ConfirmDialog-U5H3MP3M.js +0 -4
  45. package/dist/chunk-G6AUDFI4.js.map +0 -1
  46. package/dist/chunk-NQTQPOT4.js +0 -7
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/apps/WorldClock.tsx"],"names":[],"mappings":"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","file":"WorldClock-EESGFXPN.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 { AVAILABLE_CITIES, fetchCityWeather, getCondition, toFahrenheit, type CityWeather } from './_weatherData';\n\nconst SETTINGS_KEY = 'world_clock_appearance';\nconst DEFAULT_CITIES = ['London', 'Shanghai', 'New York'];\n\ninterface WeatherPrefs { useFahrenheit: boolean }\n\n/**\n * Resolve the local time for the supplied IANA timezone into a digital\n * `12:34` + `AM` / `PM` pair. Used by the World Clock rows.\n */\nfunction digitalTime(tz: string, now: Date): { time: string; ampm: string } {\n try {\n const parts = new Intl.DateTimeFormat('en-US', {\n timeZone: tz, hour: 'numeric', minute: '2-digit', hour12: true,\n }).formatToParts(now);\n const h = parts.find(p => p.type === 'hour')?.value || '12';\n const m = parts.find(p => p.type === 'minute')?.value || '00';\n const ampm = (parts.find(p => p.type === 'dayPeriod')?.value || '').toUpperCase();\n return { time: `${h}:${m}`, ampm };\n } catch { return { time: '', ampm: '' }; }\n}\n\n/**\n * World Clock widget — multi-city list. Each row pairs a tiny analogue\n * clock (minimal-style: ring, four cardinal dots, two hands) with the\n * city's current weather. The weather emoji doubles as a day/night cue\n * (☀ vs 🌙 for clear, the same icons for everything else). Reads the\n * shared Open-Meteo cache so it doesn't re-hit the API when the Weather\n * widget already has the data.\n */\nexport default function WorldClock() {\n const { prefs, save } = useShellPrefs();\n const [appearance, setAppearance] = useState(() => loadAppearance(SETTINGS_KEY));\n const [settingsOpen, setSettingsOpen] = useState(false);\n const [configCities, setConfigCities] = useState<string[]>([]);\n const [configAppearance, setConfigAppearance] = useState<WidgetAppearance>(appearance);\n\n const wxPrefs: WeatherPrefs = {\n useFahrenheit: !!(prefs.weather_prefs as WeatherPrefs | undefined)?.useFahrenheit,\n };\n const [configWxPrefs, setConfigWxPrefs] = useState(wxPrefs);\n\n // Tick once every 30 s so the analogue minute-hands drift smoothly. We\n // don't show seconds so faster polling is wasted re-renders.\n const [now, setNow] = useState(() => new Date());\n useEffect(() => {\n const t = setInterval(() => setNow(new Date()), 30 * 1000);\n return () => clearInterval(t);\n }, []);\n\n // Resolve the saved city list and migrate the legacy IANA-timezone\n // format (`Europe/London`) over to the new city-name format\n // (`London`).\n const tzToCity: Record<string, string> = Object.fromEntries(\n Object.entries(AVAILABLE_CITIES).map(([city, info]) => [info.tz, city])\n );\n const rawClocks = prefs.world_clocks as string[] | undefined;\n const cities: string[] = (() => {\n if (!Array.isArray(rawClocks) || rawClocks.length === 0) return DEFAULT_CITIES;\n const out: string[] = [];\n for (const v of rawClocks) {\n const asCity = AVAILABLE_CITIES[v] ? v : tzToCity[v];\n if (asCity && !out.includes(asCity)) out.push(asCity);\n }\n return out.length ? out : DEFAULT_CITIES;\n })();\n\n const [data, setData] = useState<Record<string, CityWeather>>({});\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n let cancelled = false;\n setLoading(true);\n (async () => {\n const next: Record<string, CityWeather> = {};\n await Promise.all(cities.map(async c => {\n const w = await fetchCityWeather(c);\n if (w) next[c] = w;\n }));\n if (!cancelled) { setData(next); setLoading(false); }\n })();\n return () => { cancelled = true; };\n }, [cities.join(',')]);\n\n useWidgetSettings(useCallback(() => {\n setConfigCities([...cities]);\n setConfigAppearance({ ...appearance });\n setConfigWxPrefs({ ...wxPrefs });\n setSettingsOpen(true);\n }, [cities.join(','), appearance, wxPrefs.useFahrenheit]));\n\n const saveSettings = () => {\n if (configCities.length === 0) return;\n save({\n world_clocks: configCities,\n weather_prefs: { ...(prefs.weather_prefs as object | undefined ?? {}), useFahrenheit: configWxPrefs.useFahrenheit },\n });\n setAppearance(configAppearance);\n localStorage.setItem(SETTINGS_KEY, JSON.stringify(configAppearance));\n setSettingsOpen(false);\n };\n\n const formatTemp = (c: number) => wxPrefs.useFahrenheit ? `${toFahrenheit(c)}°` : `${c}°`;\n\n return (\n <>\n {/* Theme-aware panel — same colour as the taskbar so all dashboard\n * widgets read as a coordinated set across light and dark themes.\n * Rows use `divide-y` rather than per-row border classes so the\n * last row never paints a stray separator line at the panel's\n * bottom edge. */}\n <div\n style={{\n backgroundColor: `rgb(var(--taskbar-bg-rgb, 243 244 246) / ${appearance.activeOpacity / 100})`,\n backdropFilter: appearance.activeBlur > 0 ? `blur(${appearance.activeBlur}px)` : undefined,\n }}>\n <div className=\"divide-y divide-gray-200\">\n {loading && cities.every(c => !data[c]) && (\n <div className=\"px-3 py-6 text-center text-xs text-gray-500\">Loading…</div>\n )}\n {cities.map((cityName) => {\n const w = data[cityName];\n const tz = AVAILABLE_CITIES[cityName]?.tz ?? 'UTC';\n const { time, ampm } = digitalTime(tz, now);\n if (!w) {\n return (\n <div key={cityName} className=\"px-4 py-3\">\n <div className=\"flex items-baseline justify-between gap-2\">\n <span className=\"text-[15px] font-semibold tracking-tight text-gray-900 truncate\">{cityName}</span>\n <span className=\"tabular-nums text-[13px] font-medium text-gray-500\">{time}<span className=\"text-[9px] ml-0.5 opacity-70\">{ampm}</span></span>\n </div>\n <div className=\"text-[11px] text-gray-400 mt-1\">Loading weather…</div>\n </div>\n );\n }\n const [condition, emoji] = getCondition(w.code, w.isDay);\n return (\n <div key={cityName} className=\"px-4 py-3\">\n {/* Top — city name on the left, big temperature on the right. */}\n <div className=\"flex items-baseline justify-between gap-2\">\n <span className=\"text-[15px] font-semibold tracking-tight text-gray-900 truncate\">{cityName}</span>\n <span className=\"text-[26px] font-extralight leading-none tabular-nums tracking-tight text-gray-900 shrink-0\">{formatTemp(w.temp)}</span>\n </div>\n\n {/* Bottom — emoji · digital time · condition on the left;\n H/L on the right. */}\n <div className=\"flex items-center justify-between gap-2 mt-1.5 text-[11px]\">\n <div className=\"flex items-center gap-1.5 min-w-0 text-gray-500\">\n <span className=\"text-base leading-none shrink-0\">{emoji}</span>\n <span className=\"tabular-nums font-semibold text-gray-700 shrink-0\">\n {time}<span className=\"text-[9px] font-medium ml-0.5 opacity-70\">{ampm}</span>\n </span>\n <span className=\"text-gray-300 shrink-0\">·</span>\n <span className=\"truncate\">{condition}</span>\n </div>\n <span className=\"tabular-nums text-gray-500 shrink-0\">H:{formatTemp(w.high)} L:{formatTemp(w.low)}</span>\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\">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={() => setConfigWxPrefs(p => ({ ...p, useFahrenheit: o.key }))}\n className={`px-3 py-1 text-xs font-medium rounded-lg border transition-colors ${configWxPrefs.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 <p className=\"text-[10px] text-gray-400 mt-1\">Shared with the Weather widget.</p>\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-56 overflow-y-auto\">\n {Object.keys(AVAILABLE_CITIES).map(name => (\n <label key={name} 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(name)}\n onChange={() => setConfigCities(prev => prev.includes(name) ? prev.filter(c => c !== name) : [...prev, name])}\n className=\"rounded border-gray-300 text-blue-600 focus:ring-blue-500 h-3.5 w-3.5\" />\n {name}\n </label>\n ))}\n </div>\n </div>\n </WidgetSettingsModal>\n </>\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/apps/WorldClock.tsx"],"names":[],"mappings":"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","file":"WorldClock-7L2YHNC3.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 { AVAILABLE_CITIES, fetchCityWeather, getCondition, toFahrenheit, type CityWeather } from './_weatherData';\n\nconst SETTINGS_KEY = 'world_clock_appearance';\nconst DEFAULT_CITIES = ['London', 'Shanghai', 'New York'];\n\ninterface WeatherPrefs { useFahrenheit: boolean }\n\n/**\n * Resolve the local time for the supplied IANA timezone into a digital\n * `12:34` + `AM` / `PM` pair. Used by the World Clock rows.\n */\nfunction digitalTime(tz: string, now: Date): { time: string; ampm: string } {\n try {\n const parts = new Intl.DateTimeFormat('en-US', {\n timeZone: tz, hour: 'numeric', minute: '2-digit', hour12: true,\n }).formatToParts(now);\n const h = parts.find(p => p.type === 'hour')?.value || '12';\n const m = parts.find(p => p.type === 'minute')?.value || '00';\n const ampm = (parts.find(p => p.type === 'dayPeriod')?.value || '').toUpperCase();\n return { time: `${h}:${m}`, ampm };\n } catch { return { time: '', ampm: '' }; }\n}\n\n/**\n * World Clock widget — multi-city list. Each row pairs a tiny analogue\n * clock (minimal-style: ring, four cardinal dots, two hands) with the\n * city's current weather. The weather emoji doubles as a day/night cue\n * (☀ vs 🌙 for clear, the same icons for everything else). Reads the\n * shared Open-Meteo cache so it doesn't re-hit the API when the Weather\n * widget already has the data.\n */\nexport default function WorldClock() {\n const { prefs, save } = useShellPrefs();\n const [appearance, setAppearance] = useState(() => loadAppearance(SETTINGS_KEY));\n const [settingsOpen, setSettingsOpen] = useState(false);\n const [configCities, setConfigCities] = useState<string[]>([]);\n const [configAppearance, setConfigAppearance] = useState<WidgetAppearance>(appearance);\n\n const wxPrefs: WeatherPrefs = {\n useFahrenheit: !!(prefs.weather_prefs as WeatherPrefs | undefined)?.useFahrenheit,\n };\n const [configWxPrefs, setConfigWxPrefs] = useState(wxPrefs);\n\n // Tick once every 30 s so the analogue minute-hands drift smoothly. We\n // don't show seconds so faster polling is wasted re-renders.\n const [now, setNow] = useState(() => new Date());\n useEffect(() => {\n const t = setInterval(() => setNow(new Date()), 30 * 1000);\n return () => clearInterval(t);\n }, []);\n\n // Resolve the saved city list and migrate the legacy IANA-timezone\n // format (`Europe/London`) over to the new city-name format\n // (`London`).\n const tzToCity: Record<string, string> = Object.fromEntries(\n Object.entries(AVAILABLE_CITIES).map(([city, info]) => [info.tz, city])\n );\n const rawClocks = prefs.world_clocks as string[] | undefined;\n const cities: string[] = (() => {\n if (!Array.isArray(rawClocks) || rawClocks.length === 0) return DEFAULT_CITIES;\n const out: string[] = [];\n for (const v of rawClocks) {\n const asCity = AVAILABLE_CITIES[v] ? v : tzToCity[v];\n if (asCity && !out.includes(asCity)) out.push(asCity);\n }\n return out.length ? out : DEFAULT_CITIES;\n })();\n\n const [data, setData] = useState<Record<string, CityWeather>>({});\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n let cancelled = false;\n setLoading(true);\n (async () => {\n const next: Record<string, CityWeather> = {};\n await Promise.all(cities.map(async c => {\n const w = await fetchCityWeather(c);\n if (w) next[c] = w;\n }));\n if (!cancelled) { setData(next); setLoading(false); }\n })();\n return () => { cancelled = true; };\n }, [cities.join(',')]);\n\n useWidgetSettings(useCallback(() => {\n setConfigCities([...cities]);\n setConfigAppearance({ ...appearance });\n setConfigWxPrefs({ ...wxPrefs });\n setSettingsOpen(true);\n }, [cities.join(','), appearance, wxPrefs.useFahrenheit]));\n\n const saveSettings = () => {\n if (configCities.length === 0) return;\n save({\n world_clocks: configCities,\n weather_prefs: { ...(prefs.weather_prefs as object | undefined ?? {}), useFahrenheit: configWxPrefs.useFahrenheit },\n });\n setAppearance(configAppearance);\n localStorage.setItem(SETTINGS_KEY, JSON.stringify(configAppearance));\n setSettingsOpen(false);\n };\n\n const formatTemp = (c: number) => wxPrefs.useFahrenheit ? `${toFahrenheit(c)}°` : `${c}°`;\n\n return (\n <>\n {/* Theme-aware panel — same colour as the taskbar so all dashboard\n * widgets read as a coordinated set across light and dark themes.\n * Rows use `divide-y` rather than per-row border classes so the\n * last row never paints a stray separator line at the panel's\n * bottom edge. */}\n <div\n style={{\n backgroundColor: `rgb(var(--taskbar-bg-rgb, 243 244 246) / ${appearance.activeOpacity / 100})`,\n backdropFilter: appearance.activeBlur > 0 ? `blur(${appearance.activeBlur}px)` : undefined,\n }}>\n <div className=\"divide-y divide-gray-200\">\n {loading && cities.every(c => !data[c]) && (\n <div className=\"px-3 py-6 text-center text-xs text-gray-500\">Loading…</div>\n )}\n {cities.map((cityName) => {\n const w = data[cityName];\n const tz = AVAILABLE_CITIES[cityName]?.tz ?? 'UTC';\n const { time, ampm } = digitalTime(tz, now);\n if (!w) {\n return (\n <div key={cityName} className=\"px-4 py-3\">\n <div className=\"flex items-baseline justify-between gap-2\">\n <span className=\"text-[15px] font-semibold tracking-tight text-gray-900 truncate\">{cityName}</span>\n <span className=\"tabular-nums text-[13px] font-medium text-gray-500\">{time}<span className=\"text-[9px] ml-0.5 opacity-70\">{ampm}</span></span>\n </div>\n <div className=\"text-[11px] text-gray-400 mt-1\">Loading weather…</div>\n </div>\n );\n }\n const [condition, emoji] = getCondition(w.code, w.isDay);\n return (\n <div key={cityName} className=\"px-4 py-3\">\n {/* Top — city name on the left, big temperature on the right. */}\n <div className=\"flex items-baseline justify-between gap-2\">\n <span className=\"text-[15px] font-semibold tracking-tight text-gray-900 truncate\">{cityName}</span>\n <span className=\"text-[26px] font-extralight leading-none tabular-nums tracking-tight text-gray-900 shrink-0\">{formatTemp(w.temp)}</span>\n </div>\n\n {/* Bottom — emoji · digital time · condition on the left;\n H/L on the right. */}\n <div className=\"flex items-center justify-between gap-2 mt-1.5 text-[11px]\">\n <div className=\"flex items-center gap-1.5 min-w-0 text-gray-500\">\n <span className=\"text-base leading-none shrink-0\">{emoji}</span>\n <span className=\"tabular-nums font-semibold text-gray-700 shrink-0\">\n {time}<span className=\"text-[9px] font-medium ml-0.5 opacity-70\">{ampm}</span>\n </span>\n <span className=\"text-gray-300 shrink-0\">·</span>\n <span className=\"truncate\">{condition}</span>\n </div>\n <span className=\"tabular-nums text-gray-500 shrink-0\">H:{formatTemp(w.high)} L:{formatTemp(w.low)}</span>\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\">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={() => setConfigWxPrefs(p => ({ ...p, useFahrenheit: o.key }))}\n className={`px-3 py-1 text-xs font-medium rounded-lg border transition-colors ${configWxPrefs.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 <p className=\"text-[10px] text-gray-400 mt-1\">Shared with the Weather widget.</p>\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-56 overflow-y-auto\">\n {Object.keys(AVAILABLE_CITIES).map(name => (\n <label key={name} 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(name)}\n onChange={() => setConfigCities(prev => prev.includes(name) ? prev.filter(c => c !== name) : [...prev, name])}\n className=\"rounded border-gray-300 text-blue-600 focus:ring-blue-500 h-3.5 w-3.5\" />\n {name}\n </label>\n ))}\n </div>\n </div>\n </WidgetSettingsModal>\n </>\n );\n}\n"]}
@@ -2,25 +2,25 @@ export { openFilesInTrashMode, setFilesDemoTree } from '../chunk-HC6N2OXO.js';
2
2
  export { setBrowserStartUrl } from '../chunk-BFBWLCO6.js';
3
3
  import '../chunk-H5NDQZ4A.js';
4
4
  export { setPdfPreview } from '../chunk-BV6SQ46N.js';
5
- export { BUILTIN_APP_INFO } from '../chunk-TIKW3XWE.js';
5
+ export { BUILTIN_APP_INFO } from '../chunk-J4XK47N3.js';
6
6
  export { setSpreadsheetPreview } from '../chunk-4SQU5YV6.js';
7
- import '../chunk-NQTQPOT4.js';
8
- import '../chunk-G6AUDFI4.js';
7
+ import '../chunk-VG2WSTEJ.js';
8
+ import '../chunk-E2EGGV2Z.js';
9
9
  import '../chunk-RVT2A2MO.js';
10
10
  import { lazy } from 'react';
11
11
 
12
- var Calculator = lazy(() => import('../Calculator-B7KJQ2NF.js'));
13
- var Spreadsheet = lazy(() => import('../Spreadsheet-SUP2H2B4.js'));
14
- var Weather = lazy(() => import('../Weather-37TNIYWI.js'));
15
- var CurrencyConverter = lazy(() => import('../CurrencyConverter-AILEZUIU.js'));
16
- var PomodoroTimer = lazy(() => import('../PomodoroTimer-DSVXJ7D4.js'));
17
- var Notepad = lazy(() => import('../Notepad-LO7FRU34.js'));
18
- var WorldClock = lazy(() => import('../WorldClock-EESGFXPN.js'));
19
- var Stock = lazy(() => import('../Stock-UT2JMEVS.js'));
20
- var Preview = lazy(() => import('../Preview-QCAN5EU3.js'));
21
- var Documents = lazy(() => import('../Documents-IX6JISOV.js'));
22
- var Files = lazy(() => import('../Files-O7WK4TQG.js'));
23
- var Browser = lazy(() => import('../Browser-7AQ7Y4Z7.js'));
12
+ var Calculator = lazy(() => import('../Calculator-U7JLNROS.js'));
13
+ var Spreadsheet = lazy(() => import('../Spreadsheet-BSBZ46CE.js'));
14
+ var Weather = lazy(() => import('../Weather-47E3IRKF.js'));
15
+ var CurrencyConverter = lazy(() => import('../CurrencyConverter-2UFQ75WE.js'));
16
+ var PomodoroTimer = lazy(() => import('../PomodoroTimer-Q53KOASQ.js'));
17
+ var Notepad = lazy(() => import('../Notepad-OXSKELWV.js'));
18
+ var WorldClock = lazy(() => import('../WorldClock-7L2YHNC3.js'));
19
+ var Stock = lazy(() => import('../Stock-SRUFZGQC.js'));
20
+ var Preview = lazy(() => import('../Preview-J6YDYCX4.js'));
21
+ var Documents = lazy(() => import('../Documents-3V7XPDOS.js'));
22
+ var Files = lazy(() => import('../Files-TH37G6YY.js'));
23
+ var Browser = lazy(() => import('../Browser-V3F6MZ6J.js'));
24
24
  var utilityApps = {
25
25
  "/calculator": { component: Calculator, label: "Calculator", size: "sm", allowPinOnTop: true, utility: true, widget: true, dimensions: [280, 420] },
26
26
  "/spreadsheet": { component: Spreadsheet, label: "Spreadsheets", size: "2xl", appStyle: true, multiInstance: true },
@@ -1,5 +1,5 @@
1
1
  import { useShellAuth, glassStyle, GLASS_DIVIDER, navIcons } from './chunk-RVT2A2MO.js';
2
- import { createContext, Component, useReducer, useRef, useCallback, useState, useEffect, useMemo, useContext, useId, useSyncExternalStore, useLayoutEffect, isValidElement, cloneElement, Suspense } from 'react';
2
+ import { createContext, Component, useReducer, useRef, useCallback, useState, useEffect, useMemo, useContext, useId, useLayoutEffect, useSyncExternalStore, isValidElement, cloneElement, Suspense } from 'react';
3
3
  import { Dialog, DialogBackdrop, DialogPanel, DialogTitle } from '@headlessui/react';
4
4
  import { XMarkIcon, ExclamationTriangleIcon } from '@heroicons/react/24/outline';
5
5
  import { createPortal } from 'react-dom';
@@ -355,6 +355,33 @@ var MinimizedContext = createContext({
355
355
  restoreIfMinimized: () => false,
356
356
  openItems: []
357
357
  });
358
+ var WindowDirtyContext = createContext(null);
359
+ function useWindowDirty(dirty) {
360
+ const register = useContext(WindowDirtyContext);
361
+ const idRef = useRef(null);
362
+ if (idRef.current === null) idRef.current = /* @__PURE__ */ Symbol("window-dirty");
363
+ useLayoutEffect(() => {
364
+ if (!register) return;
365
+ return register(idRef.current, dirty);
366
+ }, [register, dirty]);
367
+ }
368
+ function useWindowDirtyRegistry() {
369
+ const registrations = useRef(/* @__PURE__ */ new Map());
370
+ const [dirty, setDirty] = useState(false);
371
+ const registerDirty = useCallback((id, value) => {
372
+ const updateAggregate = () => {
373
+ const next = Array.from(registrations.current.values()).some(Boolean);
374
+ setDirty((current) => current === next ? current : next);
375
+ };
376
+ registrations.current.set(id, value);
377
+ updateAggregate();
378
+ return () => {
379
+ registrations.current.delete(id);
380
+ updateAggregate();
381
+ };
382
+ }, []);
383
+ return { dirty, registerDirty };
384
+ }
358
385
  function useWindowManager() {
359
386
  return useContext(MinimizedContext);
360
387
  }
@@ -365,11 +392,12 @@ function shortcutSpecFor(item) {
365
392
  return { entityType, entityId, label: item.label };
366
393
  }
367
394
  function PageWindow({ item, onClose, accentRgb }) {
395
+ const { dirty, registerDirty } = useWindowDirtyRegistry();
368
396
  const raw = WINDOW_REGISTRY[item.route];
369
397
  if (!raw || !isPageEntry(raw)) return null;
370
398
  const entry = raw;
371
399
  const Component2 = entry.component;
372
- return /* @__PURE__ */ jsx(Modal, { open: true, onClose, icon: navIcons[item.route], title: entry.label, size: entry.size || "2xl", allowPinOnTop: entry.allowPinOnTop, initialPosition: entry.initialPosition, widget: entry.widget, compact: entry.compact, appStyle: entry.appStyle, flushBody: entry.flushBody, autoHeight: entry.autoHeight, autoMinHeight: entry.autoMinHeight, dimensions: entry.dimensions, windowKey: item.id, openedFromKey: item.openedFrom, accentRgb, children: /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ jsx(LoadingSpinner, {}) }), children: /* @__PURE__ */ jsx(Component2, {}) }) });
400
+ return /* @__PURE__ */ jsx(Modal, { open: true, onClose, icon: navIcons[item.route], title: entry.label, size: entry.size || "2xl", dirty, allowPinOnTop: entry.allowPinOnTop, initialPosition: entry.initialPosition, widget: entry.widget, compact: entry.compact, appStyle: entry.appStyle, flushBody: entry.flushBody, autoHeight: entry.autoHeight, autoMinHeight: entry.autoMinHeight, dimensions: entry.dimensions, windowKey: item.id, openedFromKey: item.openedFrom, accentRgb, children: /* @__PURE__ */ jsx(WindowDirtyContext.Provider, { value: registerDirty, children: /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ jsx(LoadingSpinner, {}) }), children: /* @__PURE__ */ jsx(Component2, {}) }) }) });
373
401
  }
374
402
  function DocFavStar({ entityType, entityId, label }) {
375
403
  const queryClient = useQueryClient();
@@ -397,6 +425,7 @@ function RestoredRegistryModal({ item, onClose, onMinimize, accentRgb }) {
397
425
  if (!raw || !isEntityEntry(raw)) return null;
398
426
  const entry = raw;
399
427
  const [editing, setEditing] = useState(false);
428
+ const { dirty, registerDirty } = useWindowDirtyRegistry();
400
429
  const qkPrefix = entry.queryKey || entry.endpoint.replace(/^\/|\/$/g, "").split("/").pop() || item.entityType;
401
430
  const isDuplicate = item.entitySnapshot?._duplicate;
402
431
  const isDraft = typeof item.entityId === "string" && item.entityId.startsWith("new-");
@@ -426,12 +455,12 @@ function RestoredRegistryModal({ item, onClose, onMinimize, accentRgb }) {
426
455
  return () => window.removeEventListener("modal-reorder", handler);
427
456
  }, [refetch, entry.selfFetching, isDuplicate, isDraft]);
428
457
  const handleClose = useCallback(() => {
429
- if (editing) {
458
+ if (editing && !dirty) {
430
459
  setEditing(false);
431
460
  } else {
432
461
  onClose();
433
462
  }
434
- }, [editing, onClose]);
463
+ }, [editing, dirty, onClose]);
435
464
  const titleBase = entry.selfFetching ? item.label : entity ? entry.title(entity, editing, setEditing) : item.label;
436
465
  const entityIconKey = entry.icon;
437
466
  const entityIcon = entityIconKey ? navIcons[entityIconKey] : null;
@@ -457,12 +486,13 @@ function RestoredRegistryModal({ item, onClose, onMinimize, accentRgb }) {
457
486
  windowKey: item.id,
458
487
  openedFromKey: item.openedFrom,
459
488
  size: entry.size || "2xl",
489
+ dirty,
460
490
  dimensions: entry.dimensions,
461
491
  autoHeight: entry.autoHeight,
462
492
  autoMinHeight: entry.autoMinHeight,
463
493
  appStyle: entry.appStyle,
464
494
  accentRgb,
465
- children: /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(LoadingSpinner, {}), children: entry.selfFetching ? entry.render(null, handleClose, item.entityId, editing, setEditing) : isLoading && !entity ? /* @__PURE__ */ jsx(LoadingSpinner, {}) : entity ? entry.render(entity, handleClose, item.entityId, editing, setEditing) : /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500 py-8 text-center", children: "Not found." }) })
495
+ children: /* @__PURE__ */ jsx(WindowDirtyContext.Provider, { value: registerDirty, children: /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(LoadingSpinner, {}), children: entry.selfFetching ? entry.render(null, handleClose, item.entityId, editing, setEditing) : isLoading && !entity ? /* @__PURE__ */ jsx(LoadingSpinner, {}) : entity ? entry.render(entity, handleClose, item.entityId, editing, setEditing) : /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500 py-8 text-center", children: "Not found." }) }) })
466
496
  }
467
497
  );
468
498
  }
@@ -691,7 +721,7 @@ function TaskbarTabPreview({ items, anchorEl, onActivate, onClose, onMouseEnter,
691
721
  document.body
692
722
  );
693
723
  }
694
- function TaskbarWindows({ openWindows, onRemove, onCloseAll, onSplitView, onActivate, onActivateById }) {
724
+ function TaskbarWindows({ openWindows, onRemove, onSplitView, onActivate, onActivateById }) {
695
725
  const [target, setTarget] = useState(null);
696
726
  useEffect(() => {
697
727
  const el = document.getElementById("taskbar-windows");
@@ -940,6 +970,10 @@ function WindowManagerProvider({ children, windowAccentForRoute }) {
940
970
  const location = useLocation();
941
971
  const isAuthPage = AUTH_PAGES.some((p) => location.pathname.startsWith(p));
942
972
  const [openWindows, setOpenWindows] = useState(() => restoreWindowState());
973
+ const openWindowsRef = useRef(openWindows);
974
+ useLayoutEffect(() => {
975
+ openWindowsRef.current = openWindows;
976
+ }, [openWindows]);
943
977
  const hasUserActed = useRef(false);
944
978
  useEffect(() => {
945
979
  if (openWindows.length > 0) hasUserActed.current = true;
@@ -949,9 +983,16 @@ function WindowManagerProvider({ children, windowAccentForRoute }) {
949
983
  if (w.route) routeByWindowKey.set(w.id, w.route);
950
984
  }
951
985
  }, [openWindows]);
952
- const closeEntity = useCallback((id) => {
986
+ const forceRemoveWindow = useCallback((id) => {
953
987
  setOpenWindows((prev) => prev.filter((m) => m.id !== id));
954
988
  }, []);
989
+ const requestClose = useCallback((id) => {
990
+ if (!findPanelByWindowKey(id)) {
991
+ forceRemoveWindow(id);
992
+ return;
993
+ }
994
+ requestModalClose(id);
995
+ }, [forceRemoveWindow]);
955
996
  const activateAfterMount = (windowKey) => {
956
997
  setTimeout(() => {
957
998
  const panel = document.querySelector(`[data-modal-panel][data-window-key="${windowKey}"]`);
@@ -985,6 +1026,13 @@ function WindowManagerProvider({ children, windowAccentForRoute }) {
985
1026
  const openPage = useCallback((path) => {
986
1027
  if (!WINDOW_REGISTRY[path] || !isPageEntry(WINDOW_REGISTRY[path])) return;
987
1028
  const entry = WINDOW_REGISTRY[path];
1029
+ if (entry.widget) {
1030
+ const existing = openWindowsRef.current.find((m) => m.type === "page" && m.route === path);
1031
+ if (existing) {
1032
+ requestClose(existing.id);
1033
+ return;
1034
+ }
1035
+ }
988
1036
  const openedFrom = currentlyActiveWindowKey();
989
1037
  setOpenWindows((prev) => {
990
1038
  if (entry.multiInstance) {
@@ -1000,9 +1048,7 @@ function WindowManagerProvider({ children, windowAccentForRoute }) {
1000
1048
  }
1001
1049
  const existing = prev.find((m) => m.type === "page" && m.route === path);
1002
1050
  if (existing) {
1003
- if (entry.widget) {
1004
- return prev.filter((m) => m !== existing);
1005
- }
1051
+ if (entry.widget) return prev;
1006
1052
  activateAfterMount(existing.id);
1007
1053
  return prev;
1008
1054
  }
@@ -1016,12 +1062,13 @@ function WindowManagerProvider({ children, windowAccentForRoute }) {
1016
1062
  openedFrom
1017
1063
  }];
1018
1064
  });
1019
- }, []);
1065
+ }, [requestClose]);
1020
1066
  const minimize = useCallback(() => {
1021
1067
  }, []);
1022
1068
  const restore = useCallback(() => {
1023
1069
  }, []);
1024
- const remove = closeEntity;
1070
+ const closeEntity = requestClose;
1071
+ const remove = requestClose;
1025
1072
  const restoreIfMinimized = () => false;
1026
1073
  return /* @__PURE__ */ jsxs(MinimizedContext.Provider, { value: { openWindows, openEntity, openPage, closeEntity, minimize, items: [], openItems: openWindows, restore, remove, restoreIfMinimized }, children: [
1027
1074
  children,
@@ -1029,8 +1076,7 @@ function WindowManagerProvider({ children, windowAccentForRoute }) {
1029
1076
  TaskbarWindows,
1030
1077
  {
1031
1078
  openWindows,
1032
- onRemove: closeEntity,
1033
- onCloseAll: () => setOpenWindows([]),
1079
+ onRemove: requestClose,
1034
1080
  onSplitView: triggerSplitView,
1035
1081
  onActivate: (label) => {
1036
1082
  const panels = document.querySelectorAll("[data-modal-panel]");
@@ -1060,12 +1106,12 @@ function WindowManagerProvider({ children, windowAccentForRoute }) {
1060
1106
  return /* @__PURE__ */ jsx(
1061
1107
  WindowErrorBoundary,
1062
1108
  {
1063
- fallback: (error, reset) => /* @__PURE__ */ jsx(Modal, { open: true, onClose: () => closeEntity(item.id), title: item.label, size: "md", autoHeight: true, windowKey: item.id, children: /* @__PURE__ */ jsx(WindowCrashedFallback, { error, onReload: reset }) }),
1064
- children: /* @__PURE__ */ jsx(WindowShortcutProvider, { spec: shortcutSpecFor(item), children: /* @__PURE__ */ jsx(UndoProvider, { windowId: item.id, children: item.type === "page" ? /* @__PURE__ */ jsx(PageWindow, { item, onClose: () => closeEntity(item.id), accentRgb }) : /* @__PURE__ */ jsx(
1109
+ fallback: (error, reset) => /* @__PURE__ */ jsx(Modal, { open: true, onClose: () => forceRemoveWindow(item.id), title: item.label, size: "md", autoHeight: true, windowKey: item.id, children: /* @__PURE__ */ jsx(WindowCrashedFallback, { error, onReload: reset }) }),
1110
+ children: /* @__PURE__ */ jsx(WindowShortcutProvider, { spec: shortcutSpecFor(item), children: /* @__PURE__ */ jsx(UndoProvider, { windowId: item.id, children: item.type === "page" ? /* @__PURE__ */ jsx(PageWindow, { item, onClose: () => forceRemoveWindow(item.id), accentRgb }) : /* @__PURE__ */ jsx(
1065
1111
  RestoredRegistryModal,
1066
1112
  {
1067
1113
  item,
1068
- onClose: () => closeEntity(item.id),
1114
+ onClose: () => forceRemoveWindow(item.id),
1069
1115
  onMinimize: () => {
1070
1116
  },
1071
1117
  accentRgb
@@ -1451,7 +1497,7 @@ function CancelButton({ onClick, children, className }) {
1451
1497
  const ctx = useContext(ModalActionsContext);
1452
1498
  const handleClick = async () => {
1453
1499
  if (ctx?.isDirty) {
1454
- const { confirm: confirm2 } = await import('./ConfirmDialog-U5H3MP3M.js');
1500
+ const { confirm: confirm2 } = await import('./ConfirmDialog-Y4BYT77R.js');
1455
1501
  const ok = await confirm2({
1456
1502
  title: "Discard changes?",
1457
1503
  message: "You have unsaved changes. Are you sure you want to cancel? All changes will be lost.",
@@ -1858,6 +1904,12 @@ function registerModalEscapeInterceptor(fn) {
1858
1904
  escapeInterceptors.delete(fn);
1859
1905
  };
1860
1906
  }
1907
+ var MODAL_CLOSE_REQUEST_EVENT = "modal-close-request";
1908
+ function requestModalClose(windowKey) {
1909
+ window.dispatchEvent(new CustomEvent(MODAL_CLOSE_REQUEST_EVENT, {
1910
+ detail: { windowKey }
1911
+ }));
1912
+ }
1861
1913
  function runEscapeInterceptors(e) {
1862
1914
  for (const fn of escapeInterceptors) {
1863
1915
  try {
@@ -2103,6 +2155,7 @@ function Modal({ open, onClose, title, icon, copyText, size = "lg", dirty = fals
2103
2155
  const [windowMenu, setWindowMenu] = useState(null);
2104
2156
  const shortcutSpec = useContext(WindowShortcutContext);
2105
2157
  const { prefs: shellPrefs, save: saveShellPrefs } = useShellPrefs();
2158
+ const accentStripeOn = !!shellPrefs.window_accent_stripe;
2106
2159
  const favDocs = shellPrefs.favorite_documents || [];
2107
2160
  const isOnDesktop = !!shortcutSpec && favDocs.some((d) => d.entityType === shortcutSpec.entityType && d.entityId === shortcutSpec.entityId);
2108
2161
  const toggleDesktopShortcut = useCallback(() => {
@@ -2529,6 +2582,14 @@ function Modal({ open, onClose, title, icon, copyText, size = "lg", dirty = fals
2529
2582
  }
2530
2583
  onClose();
2531
2584
  }, [isDirty, onClose]);
2585
+ useLayoutEffect(() => {
2586
+ if (!open || !windowKey) return;
2587
+ const handler = (event) => {
2588
+ if (event.detail?.windowKey === windowKey) guardedClose();
2589
+ };
2590
+ window.addEventListener(MODAL_CLOSE_REQUEST_EVENT, handler);
2591
+ return () => window.removeEventListener(MODAL_CLOSE_REQUEST_EVENT, handler);
2592
+ }, [open, windowKey, guardedClose]);
2532
2593
  const preSnapBoxRef = useRef(null);
2533
2594
  const startDrag = useCallback((e) => {
2534
2595
  if (e.button !== 0) return;
@@ -2935,14 +2996,6 @@ function Modal({ open, onClose, title, icon, copyText, size = "lg", dirty = fals
2935
2996
  "aria-hidden": "true"
2936
2997
  }
2937
2998
  ),
2938
- accentRgb && !widget && !isMobile && /* @__PURE__ */ jsx(
2939
- "div",
2940
- {
2941
- "aria-hidden": "true",
2942
- className: "absolute top-0 left-0 right-0 h-[3px] z-[6] pointer-events-none",
2943
- style: { backgroundColor: `rgb(var(--window-accent-rgb) / ${isActive ? 0.9 : 0.55})` }
2944
- }
2945
- ),
2946
2999
  widget ? (
2947
3000
  /* Widget: no title bar — drag via body, close via right-click context menu */
2948
3001
  null
@@ -3056,6 +3109,14 @@ function Modal({ open, onClose, title, icon, copyText, size = "lg", dirty = fals
3056
3109
  ]
3057
3110
  }
3058
3111
  ),
3112
+ accentRgb && accentStripeOn && !widget && !isMobile && /* @__PURE__ */ jsx(
3113
+ "div",
3114
+ {
3115
+ "aria-hidden": "true",
3116
+ className: "h-[3px] shrink-0 pointer-events-none",
3117
+ style: { backgroundColor: `rgb(var(--window-accent-rgb) / ${isActive ? 0.9 : 0.55})` }
3118
+ }
3119
+ ),
3059
3120
  /* @__PURE__ */ jsx(ModalIdContext.Provider, { value: modalId, children: /* @__PURE__ */ jsx(ModalActionsContext.Provider, { value: { rightRef: actionsRef, leftRef: actionsLeftRef, notify: () => setHasActions(true), active: isActive, isDirty }, children: /* @__PURE__ */ jsx(
3060
3121
  "div",
3061
3122
  {
@@ -3264,15 +3325,26 @@ var prompt = (opts) => globalPromptFn(opts);
3264
3325
  function ConfirmProvider({ children }) {
3265
3326
  const [open, setOpen] = useState(false);
3266
3327
  const [options, setOptions] = useState({ message: "" });
3328
+ const [confirmId, setConfirmId] = useState(0);
3267
3329
  const resolveRef = useRef();
3330
+ const activeConfirmIdRef = useRef(null);
3331
+ const nextConfirmIdRef = useRef(0);
3332
+ const confirmQueueRef = useRef([]);
3333
+ const showConfirm = useCallback((request) => {
3334
+ activeConfirmIdRef.current = request.id;
3335
+ resolveRef.current = request.resolve;
3336
+ setConfirmId(request.id);
3337
+ setOptions(request.options);
3338
+ setOpen(true);
3339
+ }, []);
3268
3340
  const confirmFn = useCallback((opts) => {
3269
3341
  const normalized = typeof opts === "string" ? { message: opts } : opts;
3270
- setOptions(normalized);
3271
- setOpen(true);
3272
3342
  return new Promise((resolve) => {
3273
- resolveRef.current = resolve;
3343
+ const request = { id: ++nextConfirmIdRef.current, options: normalized, resolve };
3344
+ if (activeConfirmIdRef.current === null) showConfirm(request);
3345
+ else confirmQueueRef.current.push(request);
3274
3346
  });
3275
- }, []);
3347
+ }, [showConfirm]);
3276
3348
  const [dOpen, setDOpen] = useState(false);
3277
3349
  const [dOptions, setDOptions] = useState({ message: "", confirmWord: "Delete" });
3278
3350
  const [dInput, setDInput] = useState("");
@@ -3303,9 +3375,17 @@ function ConfirmProvider({ children }) {
3303
3375
  globalDestructiveConfirmFn = destructiveConfirmFn;
3304
3376
  globalPromptFn = promptFn;
3305
3377
  }, [confirmFn, destructiveConfirmFn, promptFn]);
3306
- const handleClose = (result) => {
3307
- setOpen(false);
3308
- resolveRef.current?.(result);
3378
+ const handleClose = (id, result) => {
3379
+ if (activeConfirmIdRef.current !== id) return;
3380
+ const resolve = resolveRef.current;
3381
+ const next = confirmQueueRef.current.shift();
3382
+ if (next) showConfirm(next);
3383
+ else {
3384
+ activeConfirmIdRef.current = null;
3385
+ resolveRef.current = void 0;
3386
+ setOpen(false);
3387
+ }
3388
+ resolve?.(result);
3309
3389
  };
3310
3390
  const handleDClose = (result) => {
3311
3391
  setDOpen(false);
@@ -3339,18 +3419,18 @@ function ConfirmProvider({ children }) {
3339
3419
  return true;
3340
3420
  }
3341
3421
  if (open) {
3342
- handleClose(false);
3422
+ handleClose(confirmId, false);
3343
3423
  return true;
3344
3424
  }
3345
3425
  return false;
3346
3426
  });
3347
- }, [open, dOpen, pOpen]);
3427
+ }, [open, dOpen, pOpen, confirmId]);
3348
3428
  const variant = options.variant || (options.confirmLabel?.toLowerCase().includes("delete") || options.message.toLowerCase().includes("delete") ? "danger" : "info");
3349
3429
  const confirmBtnClass = variant === "danger" ? "bg-red-600 hover:bg-red-700 text-white" : variant === "warning" ? "bg-yellow-600 hover:bg-yellow-700 text-white" : "bg-blue-600 hover:bg-blue-700 text-white";
3350
3430
  const iconClass = variant === "danger" ? "text-red-600 bg-red-100" : variant === "warning" ? "text-yellow-600 bg-yellow-100" : "text-blue-600 bg-blue-100";
3351
3431
  return /* @__PURE__ */ jsxs(ConfirmContext.Provider, { value: confirmFn, children: [
3352
3432
  children,
3353
- /* @__PURE__ */ jsxs(Dialog, { open, onClose: () => handleClose(false), className: "relative z-[9999]", children: [
3433
+ /* @__PURE__ */ jsxs(Dialog, { open, onClose: () => handleClose(confirmId, false), className: "relative z-[9999]", children: [
3354
3434
  /* @__PURE__ */ jsx(DialogBackdrop, { className: "fixed inset-0 bg-black/30 transition-opacity" }),
3355
3435
  /* @__PURE__ */ jsx("div", { className: "fixed inset-0 flex items-center justify-center p-4", children: /* @__PURE__ */ jsxs(DialogPanel, { className: "w-full max-w-md rounded-lg bg-white p-6 shadow-xl", children: [
3356
3436
  /* @__PURE__ */ jsxs("div", { className: "flex gap-4", children: [
@@ -3365,7 +3445,7 @@ function ConfirmProvider({ children }) {
3365
3445
  "button",
3366
3446
  {
3367
3447
  type: "button",
3368
- onClick: () => handleClose(false),
3448
+ onClick: () => handleClose(confirmId, false),
3369
3449
  className: "bg-white text-gray-700 border border-gray-300 px-4 py-2 text-sm font-medium rounded-lg hover:bg-gray-50",
3370
3450
  children: options.cancelLabel || "Cancel"
3371
3451
  }
@@ -3374,7 +3454,7 @@ function ConfirmProvider({ children }) {
3374
3454
  "button",
3375
3455
  {
3376
3456
  type: "button",
3377
- onClick: () => handleClose(true),
3457
+ onClick: () => handleClose(confirmId, true),
3378
3458
  className: `px-4 py-2 text-sm font-medium rounded-lg ${confirmBtnClass}`,
3379
3459
  children: options.confirmLabel || "OK"
3380
3460
  }
@@ -3491,6 +3571,6 @@ function ConfirmProvider({ children }) {
3491
3571
  ] });
3492
3572
  }
3493
3573
 
3494
- export { CancelButton, ConfirmProvider, CopyButton, DocFavStar, LoadingSpinner, Modal, ModalActions, PopupMenu, PopupMenuDivider, PopupMenuItem, PopupMenuLabel, SIDEBAR_STRIP_W, ShellPrefsProvider, UndoProvider, WINDOW_REGISTRY, WindowCrashedFallback, WindowErrorBoundary, WindowManagerProvider, WindowTitle, beginWindowGesture, cancelWindowFront, client_default, commitExposeHighlight, confirm, confirmDestructive, drainPerfEvents, exitExposeMode, forgetMaximizedWindowBoxes, getActiveModalId, getActiveWindowRoute, getExposeHighlight, getWindowPosition, isEntityEntry, isPageEntry, isShellApiClientConfigured, markMenuOpen, prompt, registerModalEscapeInterceptor, requestWindowFront, setExposeHighlight, setPerfCollecting, setShellApiClient, setShellWindowRegistry, setWindowDefaultPosition, setWindowPosition, subscribeExposeHighlight, toggleExposeMode, useConfirm, useIsActiveWindow, useIsMobile, useLocalStoragePrefs, useModalActive, useShellPrefs, useUndo, useUndoable, useUndoableState, useWidgetSettings, useWindowManager, useWindowMenuItem, useWindowTitle };
3495
- //# sourceMappingURL=chunk-G6AUDFI4.js.map
3496
- //# sourceMappingURL=chunk-G6AUDFI4.js.map
3574
+ export { CancelButton, ConfirmProvider, CopyButton, DocFavStar, LoadingSpinner, Modal, ModalActions, PopupMenu, PopupMenuDivider, PopupMenuItem, PopupMenuLabel, SIDEBAR_STRIP_W, ShellPrefsProvider, UndoProvider, WINDOW_REGISTRY, WindowCrashedFallback, WindowErrorBoundary, WindowManagerProvider, WindowTitle, beginWindowGesture, cancelWindowFront, client_default, commitExposeHighlight, confirm, confirmDestructive, drainPerfEvents, exitExposeMode, forgetMaximizedWindowBoxes, getActiveModalId, getActiveWindowRoute, getExposeHighlight, getWindowPosition, isEntityEntry, isPageEntry, isShellApiClientConfigured, markMenuOpen, prompt, registerModalEscapeInterceptor, requestWindowFront, setExposeHighlight, setPerfCollecting, setShellApiClient, setShellWindowRegistry, setWindowDefaultPosition, setWindowPosition, subscribeExposeHighlight, toggleExposeMode, useConfirm, useIsActiveWindow, useIsMobile, useLocalStoragePrefs, useModalActive, useShellPrefs, useUndo, useUndoable, useUndoableState, useWidgetSettings, useWindowDirty, useWindowManager, useWindowMenuItem, useWindowTitle };
3575
+ //# sourceMappingURL=chunk-E2EGGV2Z.js.map
3576
+ //# sourceMappingURL=chunk-E2EGGV2Z.js.map