react-os-shell 0.2.2 → 0.2.18

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 (43) hide show
  1. package/dist/{Browser-IAQ5N3LL.js → Browser-EBBYHPBS.js} +3 -3
  2. package/dist/{Browser-IAQ5N3LL.js.map → Browser-EBBYHPBS.js.map} +1 -1
  3. package/dist/{Calculator-7MIONNJK.js → Calculator-CAKKD24W.js} +4 -4
  4. package/dist/{Calculator-7MIONNJK.js.map → Calculator-CAKKD24W.js.map} +1 -1
  5. package/dist/{Calendar-2R2DWXTG.js → Calendar-DHAUIE7N.js} +3 -3
  6. package/dist/{Calendar-2R2DWXTG.js.map → Calendar-DHAUIE7N.js.map} +1 -1
  7. package/dist/{CurrencyConverter-BMTB7FLA.js → CurrencyConverter-5LWH5WLH.js} +4 -4
  8. package/dist/{CurrencyConverter-BMTB7FLA.js.map → CurrencyConverter-5LWH5WLH.js.map} +1 -1
  9. package/dist/{Documents-CBNJAM3Q.js → Documents-LECUPQZ4.js} +3 -3
  10. package/dist/{Documents-CBNJAM3Q.js.map → Documents-LECUPQZ4.js.map} +1 -1
  11. package/dist/{Email-BS6MESSZ.js → Email-AJYKU4M7.js} +3 -3
  12. package/dist/{Email-BS6MESSZ.js.map → Email-AJYKU4M7.js.map} +1 -1
  13. package/dist/Files-RPYWW7CV.js +7 -0
  14. package/dist/{Files-U3BSTCC3.js.map → Files-RPYWW7CV.js.map} +1 -1
  15. package/dist/{Minesweeper-XOSH6BW2.js → Minesweeper-JKDGSZMW.js} +3 -3
  16. package/dist/{Minesweeper-XOSH6BW2.js.map → Minesweeper-JKDGSZMW.js.map} +1 -1
  17. package/dist/{Notepad-DMSBGGMS.js → Notepad-VL2E3ZAM.js} +3 -3
  18. package/dist/{Notepad-DMSBGGMS.js.map → Notepad-VL2E3ZAM.js.map} +1 -1
  19. package/dist/{PomodoroTimer-3XLLIDV3.js → PomodoroTimer-5WFFVIXI.js} +4 -4
  20. package/dist/{PomodoroTimer-3XLLIDV3.js.map → PomodoroTimer-5WFFVIXI.js.map} +1 -1
  21. package/dist/Preview-WE6TMZ2L.js +6 -0
  22. package/dist/{Preview-Y2LMO2DL.js.map → Preview-WE6TMZ2L.js.map} +1 -1
  23. package/dist/{Spreadsheet-IRPGCABR.js → Spreadsheet-COXQUNF6.js} +3 -3
  24. package/dist/{Spreadsheet-IRPGCABR.js.map → Spreadsheet-COXQUNF6.js.map} +1 -1
  25. package/dist/{Weather-5IW43PAQ.js → Weather-4ZPRC6CB.js} +4 -4
  26. package/dist/{Weather-5IW43PAQ.js.map → Weather-4ZPRC6CB.js.map} +1 -1
  27. package/dist/apps/index.js +16 -16
  28. package/dist/{chunk-DIJ46HNS.js → chunk-CXR7YLO7.js} +3 -3
  29. package/dist/{chunk-DIJ46HNS.js.map → chunk-CXR7YLO7.js.map} +1 -1
  30. package/dist/{chunk-JEJHECSO.js → chunk-HTNCG36G.js} +4 -4
  31. package/dist/{chunk-JEJHECSO.js.map → chunk-HTNCG36G.js.map} +1 -1
  32. package/dist/{chunk-7P6DO3NC.js → chunk-IDUZR4KY.js} +27 -12
  33. package/dist/chunk-IDUZR4KY.js.map +1 -0
  34. package/dist/{chunk-IQV6QQBQ.js → chunk-QF5IZXIA.js} +3 -3
  35. package/dist/{chunk-IQV6QQBQ.js.map → chunk-QF5IZXIA.js.map} +1 -1
  36. package/dist/index.d.ts +9 -2
  37. package/dist/index.js +98 -112
  38. package/dist/index.js.map +1 -1
  39. package/dist/styles.css +11 -0
  40. package/package.json +1 -1
  41. package/dist/Files-U3BSTCC3.js +0 -7
  42. package/dist/Preview-Y2LMO2DL.js +0 -6
  43. package/dist/chunk-7P6DO3NC.js.map +0 -1
package/dist/index.d.ts CHANGED
@@ -633,6 +633,11 @@ declare function useShellEntityFetcher(): EntityFetcher;
633
633
  * Outside calls (anything not hitting `/auth/me/`) need the consumer to mount
634
634
  * <ShellEntityFetcherProvider> and wire `entityFetcher` on <WindowManager>.
635
635
  *
636
+ * When `setShellApiClient` has not been called, HTTP methods resolve with an
637
+ * empty payload instead of throwing — so consumers without a backend (the
638
+ * Pages demo, for instance) can still mount the shell. A one-time console
639
+ * warning surfaces the missing wiring without crashing the app.
640
+ *
636
641
  * Long-term this file is removed when each call site migrates to
637
642
  * `useShellPrefs()` directly.
638
643
  */
@@ -659,8 +664,10 @@ declare function setShellAuthBridge(bridge: {
659
664
  declare function glassStyle(opacity?: number): CSSProperties;
660
665
  /** Glass divider border color */
661
666
  declare const GLASS_DIVIDER = "border-white/20";
662
- /** Glass input/search bar background */
663
- declare const GLASS_INPUT_BG = "bg-white/15";
667
+ /** Glass input/search bar background — declared in styles.css so it can adapt
668
+ * to dark mode (a flat `bg-white/15` reads as a too-bright tile on the dark
669
+ * glass gradient). */
670
+ declare const GLASS_INPUT_BG = "glass-input-bg";
664
671
 
665
672
  declare function reportBug(submit: BugReportConfig['submit']): Promise<void>;
666
673
 
package/dist/index.js CHANGED
@@ -5,11 +5,11 @@ import { useGoogleAuth } from './chunk-46LICZUM.js';
5
5
  import { useShellPrefs } from './chunk-36VM54SC.js';
6
6
  export { ShellPrefsProvider, useLocalStoragePrefs, useShellPrefs } from './chunk-36VM54SC.js';
7
7
  import { playNotification, playStartup, soundsEnabled, getSoundConfig, SOUND_PACK_KEYS, SOUND_PACKS, SOUND_TYPES, SOUND_TYPE_LABELS, playLogout, setSoundForType, previewSound, setAllSounds } from './chunk-D7PYW2QS.js';
8
- import { setPdfPreview } from './chunk-IQV6QQBQ.js';
8
+ import { setPdfPreview } from './chunk-QF5IZXIA.js';
9
9
  import { toast_default } from './chunk-WIJ45SYD.js';
10
10
  export { toast_default as toast } from './chunk-WIJ45SYD.js';
11
- import { useWindowManager, glassStyle, PopupMenu, PopupMenuLabel, PopupMenuDivider, PopupMenuItem, Modal, startMenuCategories, useIsMobile, navSections, isSection, GLASS_INPUT_BG, navIcons, sectionIcons, ModalActions, useModalActive, subscribeMobileMode, getMobileMode, WINDOW_REGISTRY, isPageEntry, setMobileMode, LoadingSpinner, ThumbCard, activateModal } from './chunk-7P6DO3NC.js';
12
- export { CancelButton, CopyButton, DocFavStar, GLASS_DIVIDER, GLASS_INPUT_BG, Modal, ModalActions, PopupMenu, PopupMenuDivider, PopupMenuItem, PopupMenuLabel, WindowManagerProvider, WindowTitle, glassStyle, isEntityEntry, isPageEntry, setShellApiClient, setShellNavIcons, setShellWindowRegistry, useModalActive, useWidgetSettings, useWindowManager, useWindowMenuItem, useWindowTitle } from './chunk-7P6DO3NC.js';
11
+ import { useWindowManager, glassStyle, PopupMenu, PopupMenuLabel, PopupMenuDivider, PopupMenuItem, Modal, startMenuCategories, useIsMobile, navSections, isSection, GLASS_INPUT_BG, navIcons, sectionIcons, ModalActions, useModalActive, subscribeMobileMode, getMobileMode, WINDOW_REGISTRY, isPageEntry, setMobileMode, LoadingSpinner, ThumbCard, activateModal } from './chunk-IDUZR4KY.js';
12
+ export { CancelButton, CopyButton, DocFavStar, GLASS_DIVIDER, GLASS_INPUT_BG, Modal, ModalActions, PopupMenu, PopupMenuDivider, PopupMenuItem, PopupMenuLabel, WindowManagerProvider, WindowTitle, glassStyle, isEntityEntry, isPageEntry, setShellApiClient, setShellNavIcons, setShellWindowRegistry, useModalActive, useWidgetSettings, useWindowManager, useWindowMenuItem, useWindowTitle } from './chunk-IDUZR4KY.js';
13
13
  export { ConfirmProvider, confirm, confirmDestructive, prompt } from './chunk-PLGHQ7QW.js';
14
14
  import { createContext, useState, useRef, useEffect, useCallback, useLayoutEffect, useContext, isValidElement, cloneElement, useSyncExternalStore, useMemo, Suspense } from 'react';
15
15
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
@@ -706,7 +706,7 @@ function StatusBadge({ status }) {
706
706
  }
707
707
 
708
708
  // src/version.ts
709
- var VERSION = "0.2.1" ;
709
+ var VERSION = "0.2.17" ;
710
710
  var APP_VERSION = VERSION;
711
711
 
712
712
  // src/changelog.ts
@@ -2843,15 +2843,12 @@ function sizeIcon(node, fallback, sizeClass = "h-10 w-10") {
2843
2843
  }
2844
2844
  return node;
2845
2845
  }
2846
- function AppTile({ route, icon, badge }) {
2847
- return /* @__PURE__ */ jsxs("span", { className: `relative aspect-square w-full max-w-[80px] mx-auto rounded-2xl bg-gradient-to-br ${hashGradient(route)} flex items-center justify-center text-white shadow-sm border border-white/30`, children: [
2848
- sizeIcon(icon, FALLBACK_APP_ICON, "h-11 w-11"),
2849
- badge && /* @__PURE__ */ jsx("span", { className: "absolute -top-1 -right-1 h-2.5 w-2.5 rounded-full bg-blue-400 border-2 border-white" })
2850
- ] });
2846
+ function AppTile({ route, icon }) {
2847
+ return /* @__PURE__ */ jsx("span", { className: `relative aspect-square w-full rounded-2xl bg-gradient-to-br ${hashGradient(route)} flex items-center justify-center text-white shadow-sm border border-white/30`, children: sizeIcon(icon, FALLBACK_APP_ICON, "h-11 w-11") });
2851
2848
  }
2852
- function FolderTile({ section, navIcons: navIcons2, badge }) {
2849
+ function FolderTile({ section, navIcons: navIcons2 }) {
2853
2850
  const previewItems = section.items.slice(0, 4);
2854
- return /* @__PURE__ */ jsxs("span", { className: "relative aspect-square w-full max-w-[80px] mx-auto rounded-2xl bg-white/30 backdrop-blur-sm border border-white/40 p-1.5 grid grid-cols-2 gap-1 shadow-sm", children: [
2851
+ return /* @__PURE__ */ jsxs("span", { className: "relative aspect-square w-full rounded-2xl bg-white/30 backdrop-blur-sm border border-white/40 p-1.5 grid grid-cols-2 gap-1 shadow-sm", children: [
2855
2852
  previewItems.map((item) => /* @__PURE__ */ jsx(
2856
2853
  "span",
2857
2854
  {
@@ -2860,8 +2857,7 @@ function FolderTile({ section, navIcons: navIcons2, badge }) {
2860
2857
  },
2861
2858
  item.to
2862
2859
  )),
2863
- Array.from({ length: Math.max(0, 4 - previewItems.length) }).map((_, i) => /* @__PURE__ */ jsx("span", { className: "rounded-md bg-white/20" }, `empty-${i}`)),
2864
- badge !== void 0 && badge > 0 && /* @__PURE__ */ jsx("span", { className: "absolute -top-1 -right-1 min-w-[18px] h-[18px] px-1 rounded-full bg-blue-500 text-white text-[10px] font-bold leading-[18px] text-center border-2 border-white", children: badge })
2860
+ Array.from({ length: Math.max(0, 4 - previewItems.length) }).map((_, i) => /* @__PURE__ */ jsx("span", { className: "rounded-md bg-white/20" }, `empty-${i}`))
2865
2861
  ] });
2866
2862
  }
2867
2863
  function MobileHome({
@@ -3009,7 +3005,7 @@ function MobileHome({
3009
3005
  saveOrder(MOBILE_WIDGET_ORDER_KEY, next);
3010
3006
  }
3011
3007
  }, [widgetWindows, widgetOrder]);
3012
- const openCountByRoute = useMemo(() => {
3008
+ useMemo(() => {
3013
3009
  const map = /* @__PURE__ */ new Map();
3014
3010
  for (const w of openWindows) {
3015
3011
  if (!w.route) continue;
@@ -3017,39 +3013,33 @@ function MobileHome({
3017
3013
  }
3018
3014
  return map;
3019
3015
  }, [openWindows]);
3020
- const openInFolder = (folder) => {
3021
- const routes = new Set(folder.items.map((i) => i.to));
3022
- return openWindows.filter((w) => w.route && routes.has(w.route));
3023
- };
3024
3016
  const draggedIcon = dragId ? homeIcons.find((i) => i.id === dragId) : null;
3025
3017
  return /* @__PURE__ */ jsxs(Fragment, { children: [
3026
- /* @__PURE__ */ jsxs(
3018
+ /* @__PURE__ */ jsx(
3027
3019
  "div",
3028
3020
  {
3029
- className: "h-full overflow-y-auto px-3 pt-4 pb-4 select-none",
3021
+ className: "h-full overflow-y-auto px-4 pt-4 pb-4 select-none",
3030
3022
  style: {
3031
3023
  // Disable iOS long-press text-selection / "Copy" callout on icon labels.
3032
3024
  WebkitUserSelect: "none",
3033
3025
  WebkitTouchCallout: "none"
3034
3026
  },
3035
- children: [
3036
- widgetWindows.length > 0 && /* @__PURE__ */ jsx("section", { className: "mb-4", children: /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-6", children: widgetWindows.map((w) => {
3027
+ children: /* @__PURE__ */ jsxs("div", { children: [
3028
+ widgetWindows.length > 0 && /* @__PURE__ */ jsx("section", { className: "mb-4", children: /* @__PURE__ */ jsx("div", { className: "grid grid-cols-4 gap-4", children: widgetWindows.map((w) => {
3037
3029
  const entry = WINDOW_REGISTRY[w.route];
3038
3030
  if (!entry) return null;
3039
3031
  const Component = entry.component;
3040
3032
  return /* @__PURE__ */ jsx(
3041
3033
  "div",
3042
3034
  {
3043
- className: "relative rounded-2xl bg-white/85 backdrop-blur border border-white/40 shadow-md overflow-hidden aspect-square",
3035
+ className: "col-span-2 relative rounded-2xl bg-white/85 backdrop-blur border border-white/40 shadow-md overflow-hidden aspect-square",
3044
3036
  children: /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-full", children: /* @__PURE__ */ jsx(LoadingSpinner, {}) }), children: /* @__PURE__ */ jsx(Component, {}) })
3045
3037
  },
3046
3038
  w.id
3047
3039
  );
3048
3040
  }) }) }),
3049
- homeIcons.length > 0 && /* @__PURE__ */ jsx("section", { children: /* @__PURE__ */ jsx("div", { className: "grid grid-cols-4 gap-3", children: homeIcons.map((icon) => {
3041
+ homeIcons.length > 0 && /* @__PURE__ */ jsx("section", { children: /* @__PURE__ */ jsx("div", { className: "grid grid-cols-4 gap-4", children: homeIcons.map((icon) => {
3050
3042
  const isFolder = icon.kind === "folder";
3051
- const folderOpen = isFolder ? openInFolder(icon.section).length : 0;
3052
- const appOpen = !isFolder ? openCountByRoute.get(icon.route) ?? 0 : 0;
3053
3043
  const isBeingDragged = dragId === icon.id;
3054
3044
  return /* @__PURE__ */ jsxs(
3055
3045
  "button",
@@ -3063,14 +3053,14 @@ function MobileHome({
3063
3053
  style: { touchAction: "none", visibility: isBeingDragged ? "hidden" : "visible" },
3064
3054
  className: `flex flex-col items-center gap-1 py-1 rounded-lg active:bg-white/20 ${dragId && !isBeingDragged ? "transition-transform" : ""}`,
3065
3055
  children: [
3066
- isFolder ? /* @__PURE__ */ jsx(FolderTile, { section: icon.section, navIcons: navIcons2, badge: folderOpen }) : /* @__PURE__ */ jsx(AppTile, { route: icon.route, icon: navIcons2[icon.route], badge: appOpen > 0 }),
3056
+ isFolder ? /* @__PURE__ */ jsx(FolderTile, { section: icon.section, navIcons: navIcons2 }) : /* @__PURE__ */ jsx(AppTile, { route: icon.route, icon: navIcons2[icon.route] }),
3067
3057
  /* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium text-white drop-shadow-sm truncate w-full text-center", children: icon.label })
3068
3058
  ]
3069
3059
  },
3070
3060
  icon.id
3071
3061
  );
3072
3062
  }) }) })
3073
- ]
3063
+ ] })
3074
3064
  }
3075
3065
  ),
3076
3066
  draggedIcon && dragPos && /* @__PURE__ */ jsx(
@@ -3093,16 +3083,10 @@ function MobileHome({
3093
3083
  {
3094
3084
  folder: selectedFolder,
3095
3085
  navIcons: navIcons2,
3096
- openInFolder: openInFolder(selectedFolder),
3097
- openCountByRoute,
3098
3086
  onClose: () => setSelectedFolder(null),
3099
3087
  onOpenApp: (path) => {
3100
3088
  setSelectedFolder(null);
3101
3089
  onOpenApp(path);
3102
- },
3103
- onActivateWindow: (id) => {
3104
- setSelectedFolder(null);
3105
- onActivateWindow(id);
3106
3090
  }
3107
3091
  }
3108
3092
  )
@@ -3111,74 +3095,70 @@ function MobileHome({
3111
3095
  function FolderPopup({
3112
3096
  folder,
3113
3097
  navIcons: navIcons2,
3114
- openInFolder,
3115
- openCountByRoute,
3116
3098
  onClose,
3117
- onOpenApp,
3118
- onActivateWindow
3099
+ onOpenApp
3119
3100
  }) {
3101
+ const [closing, setClosing] = useState(false);
3102
+ const beginClose = () => {
3103
+ if (closing) return;
3104
+ setClosing(true);
3105
+ setTimeout(onClose, 200);
3106
+ };
3107
+ const closeThen = (after) => {
3108
+ if (closing) return;
3109
+ setClosing(true);
3110
+ setTimeout(after, 200);
3111
+ };
3120
3112
  return /* @__PURE__ */ jsxs(
3121
3113
  "div",
3122
3114
  {
3123
3115
  className: "fixed inset-0 z-[210] flex flex-col items-center justify-center px-6 bg-black/45 backdrop-blur-xl select-none",
3124
3116
  style: {
3125
3117
  paddingBottom: "calc(var(--mobile-bottom-nav, 70px) + 16px)",
3126
- animation: "folder-fade-in 220ms ease-out",
3118
+ animation: closing ? "folder-fade-out 200ms ease-in forwards" : "folder-fade-in 220ms ease-out",
3127
3119
  WebkitUserSelect: "none",
3128
3120
  WebkitTouchCallout: "none"
3129
3121
  },
3130
- onClick: onClose,
3122
+ onClick: beginClose,
3131
3123
  children: [
3132
3124
  /* @__PURE__ */ jsx("style", { children: `
3133
3125
  @keyframes folder-fade-in { from { opacity: 0; } to { opacity: 1; } }
3126
+ @keyframes folder-fade-out { from { opacity: 1; } to { opacity: 0; } }
3134
3127
  @keyframes folder-pop-in { from { opacity: 0; transform: scale(0.86) translateY(8px); } to { opacity: 1; transform: scale(1) translateY(0); } }
3128
+ @keyframes folder-pop-out { from { opacity: 1; transform: scale(1) translateY(0); } to { opacity: 0; transform: scale(0.9) translateY(4px); } }
3135
3129
  ` }),
3136
- /* @__PURE__ */ jsx("h2", { className: "text-2xl font-semibold text-white drop-shadow-md mb-4 self-start", children: folder.label }),
3137
- /* @__PURE__ */ jsx(
3138
- "div",
3139
- {
3140
- className: "w-full max-w-md max-h-[70vh] flex flex-col rounded-3xl bg-white/15 backdrop-blur-xl border border-white/25 shadow-2xl overflow-hidden",
3141
- style: { animation: "folder-pop-in 240ms cubic-bezier(0.34, 1.56, 0.64, 1)" },
3142
- onClick: (e) => e.stopPropagation(),
3143
- children: /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto px-4 py-5", children: [
3144
- openInFolder.length > 0 && /* @__PURE__ */ jsxs("section", { className: "mb-4", children: [
3145
- /* @__PURE__ */ jsx("h3", { className: "text-[11px] font-semibold uppercase tracking-wide text-white/70 mb-2", children: "Open" }),
3146
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-2", children: openInFolder.map((w) => /* @__PURE__ */ jsxs(
3147
- "button",
3148
- {
3149
- onClick: () => onActivateWindow(w.id),
3150
- className: "flex items-center gap-2 p-2 rounded-lg bg-white/15 active:bg-white/25 text-left",
3151
- children: [
3152
- /* @__PURE__ */ jsx("span", { className: `h-7 w-7 rounded bg-gradient-to-br ${hashGradient(w.route ?? "")} flex items-center justify-center text-white shrink-0`, children: sizeIcon(w.route ? navIcons2[w.route] : null, FALLBACK_APP_ICON, "h-4 w-4") }),
3153
- /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-white truncate flex-1", children: w.label })
3154
- ]
3155
- },
3156
- w.id
3157
- )) })
3158
- ] }),
3159
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-3", children: folder.items.map((item) => {
3160
- const openCount = openCountByRoute.get(item.to) ?? 0;
3130
+ /* @__PURE__ */ jsxs("div", { className: "w-full max-w-[304px]", children: [
3131
+ /* @__PURE__ */ jsx("h2", { className: "text-2xl font-semibold text-white drop-shadow-md mb-4 ml-4", children: folder.label }),
3132
+ /* @__PURE__ */ jsx(
3133
+ "div",
3134
+ {
3135
+ className: "max-h-[70vh] flex flex-col rounded-3xl bg-white/15 backdrop-blur-xl border border-white/25 shadow-2xl overflow-hidden",
3136
+ style: {
3137
+ animation: closing ? "folder-pop-out 180ms ease-in forwards" : "folder-pop-in 240ms cubic-bezier(0.34, 1.56, 0.64, 1)"
3138
+ },
3139
+ onClick: (e) => e.stopPropagation(),
3140
+ children: /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto px-4 py-5", children: /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-4", children: folder.items.map((item) => {
3161
3141
  return /* @__PURE__ */ jsxs(
3162
3142
  "button",
3163
3143
  {
3164
- onClick: () => onOpenApp(item.to),
3165
- className: "flex flex-col items-center gap-1.5 p-1 rounded-lg active:bg-white/15",
3144
+ onClick: () => closeThen(() => onOpenApp(item.to)),
3145
+ className: "flex flex-col items-center gap-1 py-1 rounded-lg active:bg-white/15",
3166
3146
  children: [
3167
- /* @__PURE__ */ jsx(AppTile, { route: item.to, icon: navIcons2[item.to], badge: openCount > 0 }),
3147
+ /* @__PURE__ */ jsx(AppTile, { route: item.to, icon: navIcons2[item.to] }),
3168
3148
  /* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium text-white drop-shadow-sm truncate w-full text-center", children: item.label })
3169
3149
  ]
3170
3150
  },
3171
3151
  item.to
3172
3152
  );
3173
- }) })
3174
- ] })
3175
- }
3176
- )
3153
+ }) }) })
3154
+ }
3155
+ )
3156
+ ] })
3177
3157
  ]
3178
3158
  }
3179
3159
  );
3180
3160
  }
3181
- function MobileSwitcher({ windows, onActivate, onClose }) {
3161
+ function MobileSwitcher({ windows, onActivate, onClose, onCloseAll }) {
3182
3162
  const [cardSize, setCardSize] = useState(() => computeCardSize());
3183
3163
  useEffect(() => {
3184
3164
  const onResize = () => setCardSize(computeCardSize());
@@ -3197,26 +3177,36 @@ function MobileSwitcher({ windows, onActivate, onClose }) {
3197
3177
  /* @__PURE__ */ jsx("p", { className: "text-xs text-white/50", children: "Tap Home to launch one." })
3198
3178
  ] });
3199
3179
  }
3200
- return /* @__PURE__ */ jsxs("div", { className: "h-full overflow-y-auto px-3 pt-4 pb-4", children: [
3201
- /* @__PURE__ */ jsxs("h1", { className: "text-white text-base font-semibold mb-3 px-1", children: [
3202
- "Open apps \xB7 ",
3203
- windows.length
3180
+ return /* @__PURE__ */ jsxs("div", { className: "h-full flex flex-col", children: [
3181
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto px-3 pt-4 pb-4", children: [
3182
+ /* @__PURE__ */ jsxs("h1", { className: "text-white text-base font-semibold mb-3 px-1", children: [
3183
+ "Open apps \xB7 ",
3184
+ windows.length
3185
+ ] }),
3186
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-3", children: windows.map((w) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-stretch gap-1", children: [
3187
+ /* @__PURE__ */ jsx(
3188
+ ThumbCard,
3189
+ {
3190
+ id: w.id,
3191
+ label: w.label,
3192
+ maxW: cardSize.w,
3193
+ maxH: cardSize.h,
3194
+ titleAbove: true,
3195
+ onClick: () => onActivate(w.id),
3196
+ onClose: () => onClose(w.id)
3197
+ }
3198
+ ),
3199
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] text-white/80 truncate px-1", children: w.label })
3200
+ ] }, w.id)) })
3204
3201
  ] }),
3205
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-3", children: windows.map((w) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-stretch gap-1", children: [
3206
- /* @__PURE__ */ jsx(
3207
- ThumbCard,
3208
- {
3209
- id: w.id,
3210
- label: w.label,
3211
- maxW: cardSize.w,
3212
- maxH: cardSize.h,
3213
- titleAbove: true,
3214
- onClick: () => onActivate(w.id),
3215
- onClose: () => onClose(w.id)
3216
- }
3217
- ),
3218
- /* @__PURE__ */ jsx("span", { className: "text-[11px] text-white/80 truncate px-1", children: w.label })
3219
- ] }, w.id)) })
3202
+ /* @__PURE__ */ jsx("div", { className: "shrink-0 px-3 py-3 flex justify-center", children: /* @__PURE__ */ jsx(
3203
+ "button",
3204
+ {
3205
+ onClick: onCloseAll,
3206
+ className: "px-5 py-2.5 rounded-full bg-white/15 backdrop-blur-md border border-white/25 text-white text-sm font-medium active:bg-white/25 shadow-lg",
3207
+ children: "Close All"
3208
+ }
3209
+ ) })
3220
3210
  ] });
3221
3211
  }
3222
3212
  function computeCardSize() {
@@ -3447,7 +3437,8 @@ function MobileShell({
3447
3437
  {
3448
3438
  windows: switcherWindows,
3449
3439
  onActivate: handleActivateWindow,
3450
- onClose: (id) => closeEntity(id)
3440
+ onClose: (id) => closeEntity(id),
3441
+ onCloseAll: () => switcherWindows.forEach((w) => closeEntity(w.id))
3451
3442
  }
3452
3443
  ) }),
3453
3444
  sheet === "notifications" && notifications && /* @__PURE__ */ jsx(MobileNotificationSheet, { config: notifications, onClose: closeSheet }),
@@ -3467,7 +3458,6 @@ function MobileShell({
3467
3458
  MobileBottomNav,
3468
3459
  {
3469
3460
  mode,
3470
- openCount: switcherWindows.length,
3471
3461
  unreadCount,
3472
3462
  showNotifications: !!notifications,
3473
3463
  profileAvatar: profile?.avatar_url,
@@ -3488,7 +3478,6 @@ function MobileShell({
3488
3478
  }
3489
3479
  function MobileBottomNav({
3490
3480
  mode,
3491
- openCount,
3492
3481
  unreadCount,
3493
3482
  showNotifications,
3494
3483
  profileAvatar,
@@ -3513,31 +3502,28 @@ function MobileBottomNav({
3513
3502
  },
3514
3503
  children: [
3515
3504
  /* @__PURE__ */ jsxs("button", { onClick: onHome, className: btnClass(mode === "home"), "aria-label": "Home", children: [
3516
- /* @__PURE__ */ jsx("svg", { className: "h-6 w-6", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.7, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" }) }),
3517
- /* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium", children: "Home" })
3505
+ /* @__PURE__ */ jsx("svg", { className: "h-8 w-8", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.7, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" }) }),
3506
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium", children: "Home" })
3518
3507
  ] }),
3519
3508
  /* @__PURE__ */ jsxs("button", { onClick: onSwitcher, className: btnClass(mode === "switcher"), "aria-label": "App switcher", children: [
3520
- /* @__PURE__ */ jsxs("span", { className: "relative", children: [
3521
- /* @__PURE__ */ jsxs("svg", { className: "h-6 w-6", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.7, children: [
3522
- /* @__PURE__ */ jsx("rect", { x: "3.5", y: "3.5", width: "7", height: "7", rx: "1.25" }),
3523
- /* @__PURE__ */ jsx("rect", { x: "13.5", y: "3.5", width: "7", height: "7", rx: "1.25" }),
3524
- /* @__PURE__ */ jsx("rect", { x: "3.5", y: "13.5", width: "7", height: "7", rx: "1.25" }),
3525
- /* @__PURE__ */ jsx("rect", { x: "13.5", y: "13.5", width: "7", height: "7", rx: "1.25" })
3526
- ] }),
3527
- openCount > 0 && /* @__PURE__ */ jsx("span", { className: "absolute -top-1 -right-2 min-w-[16px] h-4 px-1 rounded-full bg-blue-500 text-white text-[10px] font-bold leading-4 text-center", children: openCount })
3509
+ /* @__PURE__ */ jsxs("svg", { className: "h-8 w-8", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.7, children: [
3510
+ /* @__PURE__ */ jsx("rect", { x: "3.5", y: "3.5", width: "7", height: "7", rx: "1.25" }),
3511
+ /* @__PURE__ */ jsx("rect", { x: "13.5", y: "3.5", width: "7", height: "7", rx: "1.25" }),
3512
+ /* @__PURE__ */ jsx("rect", { x: "3.5", y: "13.5", width: "7", height: "7", rx: "1.25" }),
3513
+ /* @__PURE__ */ jsx("rect", { x: "13.5", y: "13.5", width: "7", height: "7", rx: "1.25" })
3528
3514
  ] }),
3529
- /* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium", children: "Apps" })
3515
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium", children: "Apps" })
3530
3516
  ] }),
3531
3517
  showNotifications && /* @__PURE__ */ jsxs("button", { onClick: onNotifications, className: btnClass(false), "aria-label": "Notifications", children: [
3532
3518
  /* @__PURE__ */ jsxs("span", { className: "relative", children: [
3533
- /* @__PURE__ */ jsx("svg", { className: "h-6 w-6", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.7, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75v-.7V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0m5.714 0a3 3 0 11-5.714 0" }) }),
3534
- unreadCount > 0 && /* @__PURE__ */ jsx("span", { className: "absolute -top-1 -right-2 min-w-[16px] h-4 px-1 rounded-full bg-red-500 text-white text-[10px] font-bold leading-4 text-center", children: unreadCount > 99 ? "99+" : unreadCount })
3519
+ /* @__PURE__ */ jsx("svg", { className: "h-8 w-8", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.7, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75v-.7V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0m5.714 0a3 3 0 11-5.714 0" }) }),
3520
+ unreadCount > 0 && /* @__PURE__ */ jsx("span", { className: "absolute -top-1.5 -right-2 min-w-[18px] h-[18px] px-1 rounded-full bg-red-500 text-white text-[10px] font-bold leading-[18px] text-center", children: unreadCount > 99 ? "99+" : unreadCount })
3535
3521
  ] }),
3536
- /* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium", children: "Alerts" })
3522
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium", children: "Alerts" })
3537
3523
  ] }),
3538
3524
  /* @__PURE__ */ jsxs("button", { onClick: onProfile, className: btnClass(false), "aria-label": "Profile", children: [
3539
- profileAvatar ? /* @__PURE__ */ jsx("img", { src: profileAvatar, alt: "", className: "h-6 w-6 rounded-full object-cover border border-gray-200" }) : /* @__PURE__ */ jsx("div", { className: "h-6 w-6 rounded-full bg-blue-100 flex items-center justify-center text-[10px] font-bold text-blue-700", children: profileInitial }),
3540
- /* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium", children: "Profile" })
3525
+ profileAvatar ? /* @__PURE__ */ jsx("img", { src: profileAvatar, alt: "", className: "h-8 w-8 rounded-full object-cover border border-gray-200" }) : /* @__PURE__ */ jsx("div", { className: "h-8 w-8 rounded-full bg-blue-100 flex items-center justify-center text-xs font-bold text-blue-700", children: profileInitial }),
3526
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium", children: "Profile" })
3541
3527
  ] })
3542
3528
  ]
3543
3529
  }
@@ -3878,7 +3864,7 @@ function Layout({
3878
3864
  root.style.setProperty("--window-tab-font-size", sv.tabFont);
3879
3865
  }, [inactiveHeaderOpacity, inactiveContentOpacity, activeHeaderOpacity, activeContentOpacity, taskbarH, taskbarPosition, prefs.default_window_size, prefs.window_position, prefs.menu_density, prefs.start_menu_size]);
3880
3866
  useEffect(() => {
3881
- document.documentElement.style.setProperty("--mobile-bottom-nav", isMobile ? "70px" : "0px");
3867
+ document.documentElement.style.setProperty("--mobile-bottom-nav", isMobile ? "100px" : "0px");
3882
3868
  }, [isMobile]);
3883
3869
  const [balloonDismissed, setBalloonDismissed] = useState(false);
3884
3870
  const [isFullscreen, setIsFullscreen] = useState(!!document.fullscreenElement);