react-os-shell 0.2.2 → 0.2.20

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-RJZLTAJQ.js} +3 -3
  2. package/dist/{Browser-IAQ5N3LL.js.map → Browser-RJZLTAJQ.js.map} +1 -1
  3. package/dist/{Calculator-7MIONNJK.js → Calculator-DMROKOY2.js} +4 -4
  4. package/dist/{Calculator-7MIONNJK.js.map → Calculator-DMROKOY2.js.map} +1 -1
  5. package/dist/{Calendar-2R2DWXTG.js → Calendar-24TAKCAJ.js} +3 -3
  6. package/dist/{Calendar-2R2DWXTG.js.map → Calendar-24TAKCAJ.js.map} +1 -1
  7. package/dist/{CurrencyConverter-BMTB7FLA.js → CurrencyConverter-KMBBKLHZ.js} +4 -4
  8. package/dist/{CurrencyConverter-BMTB7FLA.js.map → CurrencyConverter-KMBBKLHZ.js.map} +1 -1
  9. package/dist/{Documents-CBNJAM3Q.js → Documents-QMP6QN3C.js} +3 -3
  10. package/dist/{Documents-CBNJAM3Q.js.map → Documents-QMP6QN3C.js.map} +1 -1
  11. package/dist/{Email-BS6MESSZ.js → Email-XTFUEIE5.js} +3 -3
  12. package/dist/{Email-BS6MESSZ.js.map → Email-XTFUEIE5.js.map} +1 -1
  13. package/dist/Files-GMIYQPAA.js +7 -0
  14. package/dist/{Files-U3BSTCC3.js.map → Files-GMIYQPAA.js.map} +1 -1
  15. package/dist/{Minesweeper-XOSH6BW2.js → Minesweeper-YPAR6SPJ.js} +3 -3
  16. package/dist/{Minesweeper-XOSH6BW2.js.map → Minesweeper-YPAR6SPJ.js.map} +1 -1
  17. package/dist/{Notepad-DMSBGGMS.js → Notepad-VEEUZROP.js} +3 -3
  18. package/dist/{Notepad-DMSBGGMS.js.map → Notepad-VEEUZROP.js.map} +1 -1
  19. package/dist/{PomodoroTimer-3XLLIDV3.js → PomodoroTimer-HWHMQZO6.js} +4 -4
  20. package/dist/{PomodoroTimer-3XLLIDV3.js.map → PomodoroTimer-HWHMQZO6.js.map} +1 -1
  21. package/dist/Preview-Z447TOCE.js +6 -0
  22. package/dist/{Preview-Y2LMO2DL.js.map → Preview-Z447TOCE.js.map} +1 -1
  23. package/dist/{Spreadsheet-IRPGCABR.js → Spreadsheet-W76QOD42.js} +3 -3
  24. package/dist/{Spreadsheet-IRPGCABR.js.map → Spreadsheet-W76QOD42.js.map} +1 -1
  25. package/dist/{Weather-5IW43PAQ.js → Weather-DIKN7BOT.js} +4 -4
  26. package/dist/{Weather-5IW43PAQ.js.map → Weather-DIKN7BOT.js.map} +1 -1
  27. package/dist/apps/index.js +16 -16
  28. package/dist/{chunk-IQV6QQBQ.js → chunk-4J466VCS.js} +3 -3
  29. package/dist/{chunk-IQV6QQBQ.js.map → chunk-4J466VCS.js.map} +1 -1
  30. package/dist/{chunk-JEJHECSO.js → chunk-7ZUE7PEH.js} +4 -4
  31. package/dist/{chunk-JEJHECSO.js.map → chunk-7ZUE7PEH.js.map} +1 -1
  32. package/dist/{chunk-DIJ46HNS.js → chunk-HVZUPS3P.js} +3 -3
  33. package/dist/{chunk-DIJ46HNS.js.map → chunk-HVZUPS3P.js.map} +1 -1
  34. package/dist/{chunk-7P6DO3NC.js → chunk-QXY6ZHRX.js} +73 -35
  35. package/dist/chunk-QXY6ZHRX.js.map +1 -0
  36. package/dist/index.d.ts +19 -3
  37. package/dist/index.js +124 -113
  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.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-4J466VCS.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, WINDOW_REGISTRY, isPageEntry, LoadingSpinner, ThumbCard, activateModal } from './chunk-QXY6ZHRX.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-QXY6ZHRX.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.19" ;
710
710
  var APP_VERSION = VERSION;
711
711
 
712
712
  // src/changelog.ts
@@ -2791,6 +2791,24 @@ function StartMenu({
2791
2791
  ` })
2792
2792
  ] });
2793
2793
  }
2794
+
2795
+ // src/shell/mobileShellStore.ts
2796
+ var _mode = "home";
2797
+ var listeners = /* @__PURE__ */ new Set();
2798
+ function getMobileMode() {
2799
+ return _mode;
2800
+ }
2801
+ function setMobileMode(mode) {
2802
+ if (_mode === mode) return;
2803
+ _mode = mode;
2804
+ listeners.forEach((fn) => fn());
2805
+ }
2806
+ function subscribeMobileMode(cb) {
2807
+ listeners.add(cb);
2808
+ return () => {
2809
+ listeners.delete(cb);
2810
+ };
2811
+ }
2794
2812
  var MOBILE_WIDGET_ORDER_KEY = "erp_mobile_widget_order";
2795
2813
  var MOBILE_HOME_ORDER_KEY = "erp_mobile_home_order";
2796
2814
  var LONG_PRESS_MS = 400;
@@ -2843,15 +2861,12 @@ function sizeIcon(node, fallback, sizeClass = "h-10 w-10") {
2843
2861
  }
2844
2862
  return node;
2845
2863
  }
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
- ] });
2864
+ function AppTile({ route, icon }) {
2865
+ 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
2866
  }
2852
- function FolderTile({ section, navIcons: navIcons2, badge }) {
2867
+ function FolderTile({ section, navIcons: navIcons2 }) {
2853
2868
  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: [
2869
+ 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
2870
  previewItems.map((item) => /* @__PURE__ */ jsx(
2856
2871
  "span",
2857
2872
  {
@@ -2860,8 +2875,7 @@ function FolderTile({ section, navIcons: navIcons2, badge }) {
2860
2875
  },
2861
2876
  item.to
2862
2877
  )),
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 })
2878
+ Array.from({ length: Math.max(0, 4 - previewItems.length) }).map((_, i) => /* @__PURE__ */ jsx("span", { className: "rounded-md bg-white/20" }, `empty-${i}`))
2865
2879
  ] });
2866
2880
  }
2867
2881
  function MobileHome({
@@ -3009,7 +3023,7 @@ function MobileHome({
3009
3023
  saveOrder(MOBILE_WIDGET_ORDER_KEY, next);
3010
3024
  }
3011
3025
  }, [widgetWindows, widgetOrder]);
3012
- const openCountByRoute = useMemo(() => {
3026
+ useMemo(() => {
3013
3027
  const map = /* @__PURE__ */ new Map();
3014
3028
  for (const w of openWindows) {
3015
3029
  if (!w.route) continue;
@@ -3017,39 +3031,33 @@ function MobileHome({
3017
3031
  }
3018
3032
  return map;
3019
3033
  }, [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
3034
  const draggedIcon = dragId ? homeIcons.find((i) => i.id === dragId) : null;
3025
3035
  return /* @__PURE__ */ jsxs(Fragment, { children: [
3026
- /* @__PURE__ */ jsxs(
3036
+ /* @__PURE__ */ jsx(
3027
3037
  "div",
3028
3038
  {
3029
- className: "h-full overflow-y-auto px-3 pt-4 pb-4 select-none",
3039
+ className: "h-full overflow-y-auto px-4 pt-4 pb-4 select-none",
3030
3040
  style: {
3031
3041
  // Disable iOS long-press text-selection / "Copy" callout on icon labels.
3032
3042
  WebkitUserSelect: "none",
3033
3043
  WebkitTouchCallout: "none"
3034
3044
  },
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) => {
3045
+ children: /* @__PURE__ */ jsxs("div", { children: [
3046
+ 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
3047
  const entry = WINDOW_REGISTRY[w.route];
3038
3048
  if (!entry) return null;
3039
3049
  const Component = entry.component;
3040
3050
  return /* @__PURE__ */ jsx(
3041
3051
  "div",
3042
3052
  {
3043
- className: "relative rounded-2xl bg-white/85 backdrop-blur border border-white/40 shadow-md overflow-hidden aspect-square",
3053
+ className: "col-span-2 relative rounded-2xl bg-white/85 backdrop-blur border border-white/40 shadow-md overflow-hidden aspect-square",
3044
3054
  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
3055
  },
3046
3056
  w.id
3047
3057
  );
3048
3058
  }) }) }),
3049
- homeIcons.length > 0 && /* @__PURE__ */ jsx("section", { children: /* @__PURE__ */ jsx("div", { className: "grid grid-cols-4 gap-3", children: homeIcons.map((icon) => {
3059
+ homeIcons.length > 0 && /* @__PURE__ */ jsx("section", { children: /* @__PURE__ */ jsx("div", { className: "grid grid-cols-4 gap-4", children: homeIcons.map((icon) => {
3050
3060
  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
3061
  const isBeingDragged = dragId === icon.id;
3054
3062
  return /* @__PURE__ */ jsxs(
3055
3063
  "button",
@@ -3063,14 +3071,14 @@ function MobileHome({
3063
3071
  style: { touchAction: "none", visibility: isBeingDragged ? "hidden" : "visible" },
3064
3072
  className: `flex flex-col items-center gap-1 py-1 rounded-lg active:bg-white/20 ${dragId && !isBeingDragged ? "transition-transform" : ""}`,
3065
3073
  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 }),
3074
+ isFolder ? /* @__PURE__ */ jsx(FolderTile, { section: icon.section, navIcons: navIcons2 }) : /* @__PURE__ */ jsx(AppTile, { route: icon.route, icon: navIcons2[icon.route] }),
3067
3075
  /* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium text-white drop-shadow-sm truncate w-full text-center", children: icon.label })
3068
3076
  ]
3069
3077
  },
3070
3078
  icon.id
3071
3079
  );
3072
3080
  }) }) })
3073
- ]
3081
+ ] })
3074
3082
  }
3075
3083
  ),
3076
3084
  draggedIcon && dragPos && /* @__PURE__ */ jsx(
@@ -3093,16 +3101,10 @@ function MobileHome({
3093
3101
  {
3094
3102
  folder: selectedFolder,
3095
3103
  navIcons: navIcons2,
3096
- openInFolder: openInFolder(selectedFolder),
3097
- openCountByRoute,
3098
3104
  onClose: () => setSelectedFolder(null),
3099
3105
  onOpenApp: (path) => {
3100
3106
  setSelectedFolder(null);
3101
3107
  onOpenApp(path);
3102
- },
3103
- onActivateWindow: (id) => {
3104
- setSelectedFolder(null);
3105
- onActivateWindow(id);
3106
3108
  }
3107
3109
  }
3108
3110
  )
@@ -3111,74 +3113,70 @@ function MobileHome({
3111
3113
  function FolderPopup({
3112
3114
  folder,
3113
3115
  navIcons: navIcons2,
3114
- openInFolder,
3115
- openCountByRoute,
3116
3116
  onClose,
3117
- onOpenApp,
3118
- onActivateWindow
3117
+ onOpenApp
3119
3118
  }) {
3119
+ const [closing, setClosing] = useState(false);
3120
+ const beginClose = () => {
3121
+ if (closing) return;
3122
+ setClosing(true);
3123
+ setTimeout(onClose, 200);
3124
+ };
3125
+ const closeThen = (after) => {
3126
+ if (closing) return;
3127
+ setClosing(true);
3128
+ setTimeout(after, 200);
3129
+ };
3120
3130
  return /* @__PURE__ */ jsxs(
3121
3131
  "div",
3122
3132
  {
3123
3133
  className: "fixed inset-0 z-[210] flex flex-col items-center justify-center px-6 bg-black/45 backdrop-blur-xl select-none",
3124
3134
  style: {
3125
3135
  paddingBottom: "calc(var(--mobile-bottom-nav, 70px) + 16px)",
3126
- animation: "folder-fade-in 220ms ease-out",
3136
+ animation: closing ? "folder-fade-out 200ms ease-in forwards" : "folder-fade-in 220ms ease-out",
3127
3137
  WebkitUserSelect: "none",
3128
3138
  WebkitTouchCallout: "none"
3129
3139
  },
3130
- onClick: onClose,
3140
+ onClick: beginClose,
3131
3141
  children: [
3132
3142
  /* @__PURE__ */ jsx("style", { children: `
3133
3143
  @keyframes folder-fade-in { from { opacity: 0; } to { opacity: 1; } }
3144
+ @keyframes folder-fade-out { from { opacity: 1; } to { opacity: 0; } }
3134
3145
  @keyframes folder-pop-in { from { opacity: 0; transform: scale(0.86) translateY(8px); } to { opacity: 1; transform: scale(1) translateY(0); } }
3146
+ @keyframes folder-pop-out { from { opacity: 1; transform: scale(1) translateY(0); } to { opacity: 0; transform: scale(0.9) translateY(4px); } }
3135
3147
  ` }),
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;
3148
+ /* @__PURE__ */ jsxs("div", { className: "w-full max-w-[304px]", children: [
3149
+ /* @__PURE__ */ jsx("h2", { className: "text-2xl font-semibold text-white drop-shadow-md mb-4 ml-4", children: folder.label }),
3150
+ /* @__PURE__ */ jsx(
3151
+ "div",
3152
+ {
3153
+ className: "max-h-[70vh] flex flex-col rounded-3xl bg-white/15 backdrop-blur-xl border border-white/25 shadow-2xl overflow-hidden",
3154
+ style: {
3155
+ animation: closing ? "folder-pop-out 180ms ease-in forwards" : "folder-pop-in 240ms cubic-bezier(0.34, 1.56, 0.64, 1)"
3156
+ },
3157
+ onClick: (e) => e.stopPropagation(),
3158
+ 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
3159
  return /* @__PURE__ */ jsxs(
3162
3160
  "button",
3163
3161
  {
3164
- onClick: () => onOpenApp(item.to),
3165
- className: "flex flex-col items-center gap-1.5 p-1 rounded-lg active:bg-white/15",
3162
+ onClick: () => closeThen(() => onOpenApp(item.to)),
3163
+ className: "flex flex-col items-center gap-1 py-1 rounded-lg active:bg-white/15",
3166
3164
  children: [
3167
- /* @__PURE__ */ jsx(AppTile, { route: item.to, icon: navIcons2[item.to], badge: openCount > 0 }),
3165
+ /* @__PURE__ */ jsx(AppTile, { route: item.to, icon: navIcons2[item.to] }),
3168
3166
  /* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium text-white drop-shadow-sm truncate w-full text-center", children: item.label })
3169
3167
  ]
3170
3168
  },
3171
3169
  item.to
3172
3170
  );
3173
- }) })
3174
- ] })
3175
- }
3176
- )
3171
+ }) }) })
3172
+ }
3173
+ )
3174
+ ] })
3177
3175
  ]
3178
3176
  }
3179
3177
  );
3180
3178
  }
3181
- function MobileSwitcher({ windows, onActivate, onClose }) {
3179
+ function MobileSwitcher({ windows, onActivate, onClose, onCloseAll }) {
3182
3180
  const [cardSize, setCardSize] = useState(() => computeCardSize());
3183
3181
  useEffect(() => {
3184
3182
  const onResize = () => setCardSize(computeCardSize());
@@ -3197,26 +3195,36 @@ function MobileSwitcher({ windows, onActivate, onClose }) {
3197
3195
  /* @__PURE__ */ jsx("p", { className: "text-xs text-white/50", children: "Tap Home to launch one." })
3198
3196
  ] });
3199
3197
  }
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
3198
+ return /* @__PURE__ */ jsxs("div", { className: "h-full flex flex-col", children: [
3199
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto px-3 pt-4 pb-4", children: [
3200
+ /* @__PURE__ */ jsxs("h1", { className: "text-white text-base font-semibold mb-3 px-1", children: [
3201
+ "Open apps \xB7 ",
3202
+ windows.length
3203
+ ] }),
3204
+ /* @__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: [
3205
+ /* @__PURE__ */ jsx(
3206
+ ThumbCard,
3207
+ {
3208
+ id: w.id,
3209
+ label: w.label,
3210
+ maxW: cardSize.w,
3211
+ maxH: cardSize.h,
3212
+ titleAbove: true,
3213
+ onClick: () => onActivate(w.id),
3214
+ onClose: () => onClose(w.id)
3215
+ }
3216
+ ),
3217
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] text-white/80 truncate px-1", children: w.label })
3218
+ ] }, w.id)) })
3204
3219
  ] }),
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)) })
3220
+ /* @__PURE__ */ jsx("div", { className: "shrink-0 px-3 py-3 flex justify-center", children: /* @__PURE__ */ jsx(
3221
+ "button",
3222
+ {
3223
+ onClick: onCloseAll,
3224
+ 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",
3225
+ children: "Close All"
3226
+ }
3227
+ ) })
3220
3228
  ] });
3221
3229
  }
3222
3230
  function computeCardSize() {
@@ -3401,7 +3409,7 @@ function MobileShell({
3401
3409
  }, [openWindows]);
3402
3410
  const prevOpenCountRef = useRef(openWindows.length);
3403
3411
  useEffect(() => {
3404
- if (openWindows.length < prevOpenCountRef.current && mode === "app") {
3412
+ if (openWindows.length === 0 && mode === "app") {
3405
3413
  setMobileMode("home");
3406
3414
  }
3407
3415
  prevOpenCountRef.current = openWindows.length;
@@ -3421,6 +3429,13 @@ function MobileShell({
3421
3429
  };
3422
3430
  const closeSheet = () => setSheet(null);
3423
3431
  return /* @__PURE__ */ jsxs(Fragment, { children: [
3432
+ /* @__PURE__ */ jsx(
3433
+ "div",
3434
+ {
3435
+ className: "fixed inset-0 z-0 pointer-events-none",
3436
+ style: wallpaperStyle
3437
+ }
3438
+ ),
3424
3439
  mode === "home" && /* @__PURE__ */ jsx(
3425
3440
  "div",
3426
3441
  {
@@ -3447,7 +3462,8 @@ function MobileShell({
3447
3462
  {
3448
3463
  windows: switcherWindows,
3449
3464
  onActivate: handleActivateWindow,
3450
- onClose: (id) => closeEntity(id)
3465
+ onClose: (id) => closeEntity(id),
3466
+ onCloseAll: () => switcherWindows.forEach((w) => closeEntity(w.id))
3451
3467
  }
3452
3468
  ) }),
3453
3469
  sheet === "notifications" && notifications && /* @__PURE__ */ jsx(MobileNotificationSheet, { config: notifications, onClose: closeSheet }),
@@ -3467,7 +3483,6 @@ function MobileShell({
3467
3483
  MobileBottomNav,
3468
3484
  {
3469
3485
  mode,
3470
- openCount: switcherWindows.length,
3471
3486
  unreadCount,
3472
3487
  showNotifications: !!notifications,
3473
3488
  profileAvatar: profile?.avatar_url,
@@ -3488,7 +3503,6 @@ function MobileShell({
3488
3503
  }
3489
3504
  function MobileBottomNav({
3490
3505
  mode,
3491
- openCount,
3492
3506
  unreadCount,
3493
3507
  showNotifications,
3494
3508
  profileAvatar,
@@ -3513,31 +3527,28 @@ function MobileBottomNav({
3513
3527
  },
3514
3528
  children: [
3515
3529
  /* @__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" })
3530
+ /* @__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" }) }),
3531
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium", children: "Home" })
3518
3532
  ] }),
3519
3533
  /* @__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 })
3534
+ /* @__PURE__ */ jsxs("svg", { className: "h-8 w-8", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.7, children: [
3535
+ /* @__PURE__ */ jsx("rect", { x: "3.5", y: "3.5", width: "7", height: "7", rx: "1.25" }),
3536
+ /* @__PURE__ */ jsx("rect", { x: "13.5", y: "3.5", width: "7", height: "7", rx: "1.25" }),
3537
+ /* @__PURE__ */ jsx("rect", { x: "3.5", y: "13.5", width: "7", height: "7", rx: "1.25" }),
3538
+ /* @__PURE__ */ jsx("rect", { x: "13.5", y: "13.5", width: "7", height: "7", rx: "1.25" })
3528
3539
  ] }),
3529
- /* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium", children: "Apps" })
3540
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium", children: "Apps" })
3530
3541
  ] }),
3531
3542
  showNotifications && /* @__PURE__ */ jsxs("button", { onClick: onNotifications, className: btnClass(false), "aria-label": "Notifications", children: [
3532
3543
  /* @__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 })
3544
+ /* @__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" }) }),
3545
+ 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
3546
  ] }),
3536
- /* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium", children: "Alerts" })
3547
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium", children: "Alerts" })
3537
3548
  ] }),
3538
3549
  /* @__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" })
3550
+ 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 }),
3551
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium", children: "Profile" })
3541
3552
  ] })
3542
3553
  ]
3543
3554
  }
@@ -3878,7 +3889,7 @@ function Layout({
3878
3889
  root.style.setProperty("--window-tab-font-size", sv.tabFont);
3879
3890
  }, [inactiveHeaderOpacity, inactiveContentOpacity, activeHeaderOpacity, activeContentOpacity, taskbarH, taskbarPosition, prefs.default_window_size, prefs.window_position, prefs.menu_density, prefs.start_menu_size]);
3880
3891
  useEffect(() => {
3881
- document.documentElement.style.setProperty("--mobile-bottom-nav", isMobile ? "70px" : "0px");
3892
+ document.documentElement.style.setProperty("--mobile-bottom-nav", isMobile ? "100px" : "0px");
3882
3893
  }, [isMobile]);
3883
3894
  const [balloonDismissed, setBalloonDismissed] = useState(false);
3884
3895
  const [isFullscreen, setIsFullscreen] = useState(!!document.fullscreenElement);