react-os-shell 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@ import { playNotification, playStartup, soundsEnabled, getSoundConfig, SOUND_PAC
8
8
  import { setPdfPreview } from './chunk-IQV6QQBQ.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, setMobileMode, WINDOW_REGISTRY, isPageEntry, LoadingSpinner, ThumbCard, activateModal } 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-7P6DO3NC.js';
12
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';
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';
@@ -2794,6 +2794,28 @@ function StartMenu({
2794
2794
  var MOBILE_WIDGET_ORDER_KEY = "erp_mobile_widget_order";
2795
2795
  var MOBILE_HOME_ORDER_KEY = "erp_mobile_home_order";
2796
2796
  var LONG_PRESS_MS = 400;
2797
+ var ICON_GRADIENTS = [
2798
+ "from-blue-500 to-blue-700",
2799
+ "from-indigo-500 to-purple-600",
2800
+ "from-purple-500 to-pink-600",
2801
+ "from-pink-500 to-rose-600",
2802
+ "from-red-500 to-rose-600",
2803
+ "from-orange-500 to-red-600",
2804
+ "from-amber-500 to-orange-600",
2805
+ "from-yellow-500 to-amber-500",
2806
+ "from-lime-500 to-green-600",
2807
+ "from-green-500 to-emerald-600",
2808
+ "from-emerald-500 to-teal-600",
2809
+ "from-teal-500 to-cyan-600",
2810
+ "from-cyan-500 to-sky-600",
2811
+ "from-sky-500 to-blue-600",
2812
+ "from-violet-500 to-fuchsia-600"
2813
+ ];
2814
+ function hashGradient(seed) {
2815
+ let h = 0;
2816
+ for (let i = 0; i < seed.length; i++) h = (h << 5) - h + seed.charCodeAt(i);
2817
+ return ICON_GRADIENTS[Math.abs(h) % ICON_GRADIENTS.length];
2818
+ }
2797
2819
  function loadOrder(key) {
2798
2820
  try {
2799
2821
  const raw = localStorage.getItem(key);
@@ -2811,7 +2833,6 @@ function saveOrder(key, order) {
2811
2833
  } catch {
2812
2834
  }
2813
2835
  }
2814
- var FALLBACK_FOLDER_ICON = /* @__PURE__ */ jsx("svg", { className: "h-10 w-10", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3.75 9.776c.112-.017.227-.026.344-.026h15.812c.117 0 .232.009.344.026m-16.5 0a2.25 2.25 0 00-1.883 2.542l.857 6a2.25 2.25 0 002.227 1.932H19.05a2.25 2.25 0 002.227-1.932l.857-6a2.25 2.25 0 00-1.883-2.542m-16.5 0V6A2.25 2.25 0 016 3.75h3.879a1.5 1.5 0 011.06.44l2.122 2.12a1.5 1.5 0 001.06.44H18A2.25 2.25 0 0120.25 9v.776" }) });
2815
2836
  var FALLBACK_APP_ICON = /* @__PURE__ */ jsx("svg", { className: "h-10 w-10", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3.75 6.75A2.25 2.25 0 016 4.5h12a2.25 2.25 0 012.25 2.25v10.5A2.25 2.25 0 0118 19.5H6a2.25 2.25 0 01-2.25-2.25V6.75z" }) });
2816
2837
  function sizeIcon(node, fallback, sizeClass = "h-10 w-10") {
2817
2838
  if (!node) return fallback;
@@ -2822,6 +2843,27 @@ function sizeIcon(node, fallback, sizeClass = "h-10 w-10") {
2822
2843
  }
2823
2844
  return node;
2824
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
+ ] });
2851
+ }
2852
+ function FolderTile({ section, navIcons: navIcons2, badge }) {
2853
+ 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: [
2855
+ previewItems.map((item) => /* @__PURE__ */ jsx(
2856
+ "span",
2857
+ {
2858
+ className: `rounded-md bg-gradient-to-br ${hashGradient(item.to)} flex items-center justify-center text-white`,
2859
+ children: sizeIcon(navIcons2[item.to], FALLBACK_APP_ICON, "h-3.5 w-3.5")
2860
+ },
2861
+ item.to
2862
+ )),
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 })
2865
+ ] });
2866
+ }
2825
2867
  function MobileHome({
2826
2868
  navSections: navSections2,
2827
2869
  navIcons: navIcons2,
@@ -2943,7 +2985,7 @@ function MobileHome({
2943
2985
  if (icon.kind === "app") onOpenApp(icon.route);
2944
2986
  else setSelectedFolder(icon.section);
2945
2987
  };
2946
- const [widgetOrder, setWidgetOrder] = useState(() => loadOrder(MOBILE_WIDGET_ORDER_KEY));
2988
+ const [widgetOrder, _setWidgetOrder] = useState(() => loadOrder(MOBILE_WIDGET_ORDER_KEY));
2947
2989
  const widgetWindows = useMemo(() => {
2948
2990
  const widgets = openWindows.filter((w) => {
2949
2991
  if (!w.route) return false;
@@ -2963,22 +3005,10 @@ function MobileHome({
2963
3005
  ...visibleRoutes.filter((r) => !widgetOrder.includes(r))
2964
3006
  ];
2965
3007
  if (next.length !== widgetOrder.length || next.some((r, i) => r !== widgetOrder[i])) {
2966
- setWidgetOrder(next);
3008
+ _setWidgetOrder(next);
2967
3009
  saveOrder(MOBILE_WIDGET_ORDER_KEY, next);
2968
3010
  }
2969
3011
  }, [widgetWindows, widgetOrder]);
2970
- const moveWidget = (route, dir) => {
2971
- setWidgetOrder((prev) => {
2972
- const i = prev.indexOf(route);
2973
- if (i === -1) return prev;
2974
- const j = i + dir;
2975
- if (j < 0 || j >= prev.length) return prev;
2976
- const next = prev.slice();
2977
- [next[i], next[j]] = [next[j], next[i]];
2978
- saveOrder(MOBILE_WIDGET_ORDER_KEY, next);
2979
- return next;
2980
- });
2981
- };
2982
3012
  const openCountByRoute = useMemo(() => {
2983
3013
  const map = /* @__PURE__ */ new Map();
2984
3014
  for (const w of openWindows) {
@@ -2993,79 +3023,56 @@ function MobileHome({
2993
3023
  };
2994
3024
  const draggedIcon = dragId ? homeIcons.find((i) => i.id === dragId) : null;
2995
3025
  return /* @__PURE__ */ jsxs(Fragment, { children: [
2996
- /* @__PURE__ */ jsxs("div", { className: "h-full overflow-y-auto px-3 pt-4 pb-4", children: [
2997
- widgetWindows.length > 0 && /* @__PURE__ */ jsx("section", { className: "mb-4", children: /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-3", children: widgetWindows.map((w, i) => {
2998
- const entry = WINDOW_REGISTRY[w.route];
2999
- if (!entry) return null;
3000
- const Component = entry.component;
3001
- const isFirst = i === 0;
3002
- const isLast = i === widgetWindows.length - 1;
3003
- return /* @__PURE__ */ jsxs(
3004
- "div",
3005
- {
3006
- className: "relative rounded-2xl bg-white/85 backdrop-blur border border-white/40 shadow-md overflow-hidden aspect-square",
3007
- children: [
3008
- /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-full", children: /* @__PURE__ */ jsx(LoadingSpinner, {}) }), children: /* @__PURE__ */ jsx(Component, {}) }),
3009
- widgetWindows.length > 1 && /* @__PURE__ */ jsxs("div", { className: "absolute top-1.5 right-1.5 flex flex-col gap-0.5 z-10", children: [
3010
- /* @__PURE__ */ jsx(
3011
- "button",
3012
- {
3013
- onClick: () => moveWidget(w.route, -1),
3014
- disabled: isFirst,
3015
- className: "h-6 w-6 rounded-md bg-white/90 backdrop-blur border border-gray-200 shadow-sm flex items-center justify-center text-gray-700 disabled:opacity-30 active:bg-gray-100",
3016
- "aria-label": "Move up",
3017
- children: /* @__PURE__ */ jsx("svg", { className: "h-3.5 w-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2.2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M4.5 15.75l7.5-7.5 7.5 7.5" }) })
3018
- }
3019
- ),
3020
- /* @__PURE__ */ jsx(
3021
- "button",
3022
- {
3023
- onClick: () => moveWidget(w.route, 1),
3024
- disabled: isLast,
3025
- className: "h-6 w-6 rounded-md bg-white/90 backdrop-blur border border-gray-200 shadow-sm flex items-center justify-center text-gray-700 disabled:opacity-30 active:bg-gray-100",
3026
- "aria-label": "Move down",
3027
- children: /* @__PURE__ */ jsx("svg", { className: "h-3.5 w-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2.2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19.5 8.25l-7.5 7.5-7.5-7.5" }) })
3028
- }
3029
- )
3030
- ] })
3031
- ]
3032
- },
3033
- w.id
3034
- );
3035
- }) }) }),
3036
- homeIcons.length > 0 && /* @__PURE__ */ jsx("section", { children: /* @__PURE__ */ jsx("div", { className: "grid grid-cols-4 gap-3", children: homeIcons.map((icon) => {
3037
- const isFolder = icon.kind === "folder";
3038
- const openCount = isFolder ? openInFolder(icon.section).length : openCountByRoute.get(icon.route) ?? 0;
3039
- const isBeingDragged = dragId === icon.id;
3040
- return /* @__PURE__ */ jsxs(
3041
- "button",
3042
- {
3043
- "data-home-icon-id": icon.id,
3044
- onPointerDown: (e) => beginLongPress(icon.id, e),
3045
- onPointerUp: cancelLongPress,
3046
- onPointerCancel: cancelLongPress,
3047
- onPointerLeave: cancelLongPress,
3048
- onClick: () => handleIconClick(icon),
3049
- style: { touchAction: "none", visibility: isBeingDragged ? "hidden" : "visible" },
3050
- className: `flex flex-col items-center gap-1 py-1 rounded-lg active:bg-white/40 ${dragId && !isBeingDragged ? "transition-transform" : ""}`,
3051
- children: [
3052
- /* @__PURE__ */ jsxs(
3053
- "span",
3054
- {
3055
- className: `relative h-[72px] w-[72px] rounded-2xl flex items-center justify-center shadow-sm border ${isFolder ? "bg-white/70 backdrop-blur border-white/40 text-blue-700" : "bg-white/85 backdrop-blur border-white/40 text-gray-800"}`,
3056
- children: [
3057
- isFolder ? sizeIcon(sectionIcons2[icon.label], FALLBACK_FOLDER_ICON) : sizeIcon(navIcons2[icon.route], FALLBACK_APP_ICON),
3058
- openCount > 0 && (isFolder ? /* @__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: openCount }) : /* @__PURE__ */ jsx("span", { className: "absolute -top-1 -right-1 h-2 w-2 rounded-full bg-blue-500 border-2 border-white" }))
3059
- ]
3060
- }
3061
- ),
3062
- /* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium text-white drop-shadow-sm truncate w-full text-center", children: icon.label })
3063
- ]
3064
- },
3065
- icon.id
3066
- );
3067
- }) }) })
3068
- ] }),
3026
+ /* @__PURE__ */ jsxs(
3027
+ "div",
3028
+ {
3029
+ className: "h-full overflow-y-auto px-3 pt-4 pb-4 select-none",
3030
+ style: {
3031
+ // Disable iOS long-press text-selection / "Copy" callout on icon labels.
3032
+ WebkitUserSelect: "none",
3033
+ WebkitTouchCallout: "none"
3034
+ },
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) => {
3037
+ const entry = WINDOW_REGISTRY[w.route];
3038
+ if (!entry) return null;
3039
+ const Component = entry.component;
3040
+ return /* @__PURE__ */ jsx(
3041
+ "div",
3042
+ {
3043
+ className: "relative rounded-2xl bg-white/85 backdrop-blur border border-white/40 shadow-md overflow-hidden aspect-square",
3044
+ 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
+ },
3046
+ w.id
3047
+ );
3048
+ }) }) }),
3049
+ homeIcons.length > 0 && /* @__PURE__ */ jsx("section", { children: /* @__PURE__ */ jsx("div", { className: "grid grid-cols-4 gap-3", children: homeIcons.map((icon) => {
3050
+ 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
+ const isBeingDragged = dragId === icon.id;
3054
+ return /* @__PURE__ */ jsxs(
3055
+ "button",
3056
+ {
3057
+ "data-home-icon-id": icon.id,
3058
+ onPointerDown: (e) => beginLongPress(icon.id, e),
3059
+ onPointerUp: cancelLongPress,
3060
+ onPointerCancel: cancelLongPress,
3061
+ onPointerLeave: cancelLongPress,
3062
+ onClick: () => handleIconClick(icon),
3063
+ style: { touchAction: "none", visibility: isBeingDragged ? "hidden" : "visible" },
3064
+ className: `flex flex-col items-center gap-1 py-1 rounded-lg active:bg-white/20 ${dragId && !isBeingDragged ? "transition-transform" : ""}`,
3065
+ 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 }),
3067
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium text-white drop-shadow-sm truncate w-full text-center", children: icon.label })
3068
+ ]
3069
+ },
3070
+ icon.id
3071
+ );
3072
+ }) }) })
3073
+ ]
3074
+ }
3075
+ ),
3069
3076
  draggedIcon && dragPos && /* @__PURE__ */ jsx(
3070
3077
  "div",
3071
3078
  {
@@ -3075,15 +3082,9 @@ function MobileHome({
3075
3082
  top: dragPos.y - dragOffsetRef.current.y,
3076
3083
  transform: "scale(1.12)"
3077
3084
  },
3078
- children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-1 py-1", children: [
3079
- /* @__PURE__ */ jsx(
3080
- "span",
3081
- {
3082
- className: `relative h-[72px] w-[72px] rounded-2xl flex items-center justify-center shadow-2xl border ${draggedIcon.kind === "folder" ? "bg-white backdrop-blur border-white/40 text-blue-700" : "bg-white backdrop-blur border-white/40 text-gray-800"}`,
3083
- children: draggedIcon.kind === "folder" ? sizeIcon(sectionIcons2[draggedIcon.label], FALLBACK_FOLDER_ICON) : sizeIcon(navIcons2[draggedIcon.route], FALLBACK_APP_ICON)
3084
- }
3085
- ),
3086
- /* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium text-white drop-shadow-md truncate max-w-[80px] text-center", children: draggedIcon.label })
3085
+ children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-1 py-1 w-20", children: [
3086
+ draggedIcon.kind === "folder" ? /* @__PURE__ */ jsx(FolderTile, { section: draggedIcon.section, navIcons: navIcons2 }) : /* @__PURE__ */ jsx(AppTile, { route: draggedIcon.route, icon: navIcons2[draggedIcon.route] }),
3087
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium text-white drop-shadow-md truncate w-full text-center", children: draggedIcon.label })
3087
3088
  ] })
3088
3089
  }
3089
3090
  ),
@@ -3119,15 +3120,25 @@ function FolderPopup({
3119
3120
  return /* @__PURE__ */ jsxs(
3120
3121
  "div",
3121
3122
  {
3122
- className: "fixed inset-0 z-[210] flex flex-col items-center justify-center px-6 bg-black/45 backdrop-blur-xl",
3123
- style: { paddingBottom: "calc(var(--mobile-bottom-nav, 56px) + 16px)" },
3123
+ className: "fixed inset-0 z-[210] flex flex-col items-center justify-center px-6 bg-black/45 backdrop-blur-xl select-none",
3124
+ style: {
3125
+ paddingBottom: "calc(var(--mobile-bottom-nav, 70px) + 16px)",
3126
+ animation: "folder-fade-in 220ms ease-out",
3127
+ WebkitUserSelect: "none",
3128
+ WebkitTouchCallout: "none"
3129
+ },
3124
3130
  onClick: onClose,
3125
3131
  children: [
3132
+ /* @__PURE__ */ jsx("style", { children: `
3133
+ @keyframes folder-fade-in { from { opacity: 0; } to { opacity: 1; } }
3134
+ @keyframes folder-pop-in { from { opacity: 0; transform: scale(0.86) translateY(8px); } to { opacity: 1; transform: scale(1) translateY(0); } }
3135
+ ` }),
3126
3136
  /* @__PURE__ */ jsx("h2", { className: "text-2xl font-semibold text-white drop-shadow-md mb-4 self-start", children: folder.label }),
3127
3137
  /* @__PURE__ */ jsx(
3128
3138
  "div",
3129
3139
  {
3130
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)" },
3131
3142
  onClick: (e) => e.stopPropagation(),
3132
3143
  children: /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto px-4 py-5", children: [
3133
3144
  openInFolder.length > 0 && /* @__PURE__ */ jsxs("section", { className: "mb-4", children: [
@@ -3138,7 +3149,7 @@ function FolderPopup({
3138
3149
  onClick: () => onActivateWindow(w.id),
3139
3150
  className: "flex items-center gap-2 p-2 rounded-lg bg-white/15 active:bg-white/25 text-left",
3140
3151
  children: [
3141
- /* @__PURE__ */ jsx("span", { className: "h-7 w-7 rounded bg-white/20 flex items-center justify-center text-white shrink-0", children: sizeIcon(w.route ? navIcons2[w.route] : null, FALLBACK_APP_ICON, "h-5 w-5") }),
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") }),
3142
3153
  /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-white truncate flex-1", children: w.label })
3143
3154
  ]
3144
3155
  },
@@ -3153,10 +3164,7 @@ function FolderPopup({
3153
3164
  onClick: () => onOpenApp(item.to),
3154
3165
  className: "flex flex-col items-center gap-1.5 p-1 rounded-lg active:bg-white/15",
3155
3166
  children: [
3156
- /* @__PURE__ */ jsxs("span", { className: "relative h-16 w-16 rounded-2xl bg-white/85 backdrop-blur border border-white/40 flex items-center justify-center text-gray-800 shadow-sm", children: [
3157
- sizeIcon(navIcons2[item.to], FALLBACK_APP_ICON),
3158
- openCount > 0 && /* @__PURE__ */ jsx("span", { className: "absolute -top-1 -right-1 h-2 w-2 rounded-full bg-blue-500 border-2 border-white" })
3159
- ] }),
3167
+ /* @__PURE__ */ jsx(AppTile, { route: item.to, icon: navIcons2[item.to], badge: openCount > 0 }),
3160
3168
  /* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium text-white drop-shadow-sm truncate w-full text-center", children: item.label })
3161
3169
  ]
3162
3170
  },
@@ -3383,6 +3391,14 @@ function MobileShell({
3383
3391
  const mode = useSyncExternalStore(subscribeMobileMode, getMobileMode);
3384
3392
  const [sheet, setSheet] = useState(null);
3385
3393
  const unreadCount = notifications?.useUnreadCount() ?? 0;
3394
+ const switcherWindows = useMemo(() => {
3395
+ return openWindows.filter((w) => {
3396
+ if (!w.route) return true;
3397
+ const entry = WINDOW_REGISTRY[w.route];
3398
+ if (!entry || !isPageEntry(entry)) return true;
3399
+ return !entry.widget;
3400
+ });
3401
+ }, [openWindows]);
3386
3402
  const prevOpenCountRef = useRef(openWindows.length);
3387
3403
  useEffect(() => {
3388
3404
  if (openWindows.length < prevOpenCountRef.current && mode === "app") {
@@ -3429,7 +3445,7 @@ function MobileShell({
3429
3445
  mode === "switcher" && /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-[200] bg-gray-900/95 backdrop-blur-sm", style: { paddingBottom: "var(--mobile-bottom-nav, 70px)" }, children: /* @__PURE__ */ jsx(
3430
3446
  MobileSwitcher,
3431
3447
  {
3432
- windows: openWindows,
3448
+ windows: switcherWindows,
3433
3449
  onActivate: handleActivateWindow,
3434
3450
  onClose: (id) => closeEntity(id)
3435
3451
  }
@@ -3451,7 +3467,7 @@ function MobileShell({
3451
3467
  MobileBottomNav,
3452
3468
  {
3453
3469
  mode,
3454
- openCount: openWindows.length,
3470
+ openCount: switcherWindows.length,
3455
3471
  unreadCount,
3456
3472
  showNotifications: !!notifications,
3457
3473
  profileAvatar: profile?.avatar_url,
@@ -3482,12 +3498,19 @@ function MobileBottomNav({
3482
3498
  onNotifications,
3483
3499
  onProfile
3484
3500
  }) {
3485
- const btnClass = (active) => `flex-1 flex flex-col items-center justify-center gap-0.5 py-2 transition-colors ${active ? "text-blue-600" : "text-gray-500 active:text-gray-700"}`;
3501
+ const btnClass = (active) => `flex-1 flex flex-col items-center justify-center gap-0.5 py-2 transition-colors select-none ${active ? "text-blue-600" : "text-gray-700 active:text-gray-900"}`;
3486
3502
  return /* @__PURE__ */ jsxs(
3487
3503
  "nav",
3488
3504
  {
3489
- className: "fixed bottom-0 inset-x-0 z-[300] flex items-stretch bg-white/95 backdrop-blur border-t border-gray-200",
3490
- style: { height: "var(--mobile-bottom-nav, 70px)", paddingBottom: "env(safe-area-inset-bottom)" },
3505
+ className: "fixed bottom-0 inset-x-0 z-[300] flex items-stretch border-t border-white/40",
3506
+ style: {
3507
+ height: "var(--mobile-bottom-nav, 70px)",
3508
+ paddingBottom: "env(safe-area-inset-bottom)",
3509
+ WebkitBackdropFilter: "blur(28px) saturate(1.8)",
3510
+ backdropFilter: "blur(28px) saturate(1.8)",
3511
+ background: "linear-gradient(135deg, rgba(255,255,255,0.65) 0%, rgba(255,255,255,0.45) 50%, rgba(255,255,255,0.55) 100%)",
3512
+ boxShadow: "inset 0 1px 0 rgba(255,255,255,0.6), 0 -2px 12px rgba(0,0,0,0.08)"
3513
+ },
3491
3514
  children: [
3492
3515
  /* @__PURE__ */ jsxs("button", { onClick: onHome, className: btnClass(mode === "home"), "aria-label": "Home", children: [
3493
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" }) }),