react-os-shell 0.1.63 → 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/{Browser-DJL2TT56.js → Browser-IAQ5N3LL.js} +3 -3
- package/dist/{Browser-DJL2TT56.js.map → Browser-IAQ5N3LL.js.map} +1 -1
- package/dist/{Calculator-CIAX4UDE.js → Calculator-7MIONNJK.js} +4 -4
- package/dist/{Calculator-CIAX4UDE.js.map → Calculator-7MIONNJK.js.map} +1 -1
- package/dist/{Calendar-LMKNYJ7J.js → Calendar-2R2DWXTG.js} +3 -3
- package/dist/{Calendar-LMKNYJ7J.js.map → Calendar-2R2DWXTG.js.map} +1 -1
- package/dist/{CurrencyConverter-QSMM26HX.js → CurrencyConverter-BMTB7FLA.js} +4 -4
- package/dist/{CurrencyConverter-QSMM26HX.js.map → CurrencyConverter-BMTB7FLA.js.map} +1 -1
- package/dist/{Documents-OCP3FEKB.js → Documents-CBNJAM3Q.js} +3 -3
- package/dist/{Documents-OCP3FEKB.js.map → Documents-CBNJAM3Q.js.map} +1 -1
- package/dist/{Email-E7FTMUDO.js → Email-BS6MESSZ.js} +3 -3
- package/dist/{Email-E7FTMUDO.js.map → Email-BS6MESSZ.js.map} +1 -1
- package/dist/Files-U3BSTCC3.js +7 -0
- package/dist/{Files-BYZQMLJJ.js.map → Files-U3BSTCC3.js.map} +1 -1
- package/dist/{Minesweeper-YUQVBS2S.js → Minesweeper-XOSH6BW2.js} +3 -3
- package/dist/{Minesweeper-YUQVBS2S.js.map → Minesweeper-XOSH6BW2.js.map} +1 -1
- package/dist/{Notepad-MJRVAKGR.js → Notepad-DMSBGGMS.js} +3 -3
- package/dist/{Notepad-MJRVAKGR.js.map → Notepad-DMSBGGMS.js.map} +1 -1
- package/dist/{PomodoroTimer-P5PLQAO7.js → PomodoroTimer-3XLLIDV3.js} +4 -4
- package/dist/{PomodoroTimer-P5PLQAO7.js.map → PomodoroTimer-3XLLIDV3.js.map} +1 -1
- package/dist/Preview-Y2LMO2DL.js +6 -0
- package/dist/{Preview-R2V2HL6V.js.map → Preview-Y2LMO2DL.js.map} +1 -1
- package/dist/{Spreadsheet-KL2TS6GG.js → Spreadsheet-IRPGCABR.js} +3 -3
- package/dist/{Spreadsheet-KL2TS6GG.js.map → Spreadsheet-IRPGCABR.js.map} +1 -1
- package/dist/{Weather-WPMGZ2QY.js → Weather-5IW43PAQ.js} +4 -4
- package/dist/{Weather-WPMGZ2QY.js.map → Weather-5IW43PAQ.js.map} +1 -1
- package/dist/apps/index.js +16 -16
- package/dist/{chunk-GBQOM343.js → chunk-7P6DO3NC.js} +126 -10
- package/dist/chunk-7P6DO3NC.js.map +1 -0
- package/dist/{chunk-AVP2E5H5.js → chunk-DIJ46HNS.js} +3 -3
- package/dist/{chunk-AVP2E5H5.js.map → chunk-DIJ46HNS.js.map} +1 -1
- package/dist/{chunk-7S2XQTV7.js → chunk-IQV6QQBQ.js} +3 -3
- package/dist/{chunk-7S2XQTV7.js.map → chunk-IQV6QQBQ.js.map} +1 -1
- package/dist/{chunk-RZUFEZWB.js → chunk-JEJHECSO.js} +4 -4
- package/dist/{chunk-RZUFEZWB.js.map → chunk-JEJHECSO.js.map} +1 -1
- package/dist/index.js +1052 -155
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/Files-BYZQMLJJ.js +0 -7
- package/dist/Preview-R2V2HL6V.js +0 -6
- package/dist/chunk-GBQOM343.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -4,14 +4,14 @@ export { formatDate } from './chunk-NSU7OHPC.js';
|
|
|
4
4
|
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
|
-
import { 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-
|
|
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';
|
|
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, navSections, isSection, GLASS_INPUT_BG, navIcons, sectionIcons, ModalActions, useModalActive } from './chunk-
|
|
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-
|
|
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';
|
|
13
13
|
export { ConfirmProvider, confirm, confirmDestructive, prompt } from './chunk-PLGHQ7QW.js';
|
|
14
|
-
import { createContext, useState, useRef, useEffect, useCallback, useLayoutEffect, useContext, isValidElement, cloneElement, useSyncExternalStore } from 'react';
|
|
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';
|
|
16
16
|
import { Dialog, DialogBackdrop, DialogPanel, DialogTitle } from '@headlessui/react';
|
|
17
17
|
import { createPortal } from 'react-dom';
|
|
@@ -274,6 +274,39 @@ function NotificationBell({
|
|
|
274
274
|
useEffect(() => {
|
|
275
275
|
if ("Notification" in window && Notification.permission === "default") Notification.requestPermission();
|
|
276
276
|
}, []);
|
|
277
|
+
const handleClick = useCallback((notif) => {
|
|
278
|
+
if (!notif.is_read) {
|
|
279
|
+
queryClient.setQueryData(["notification-unread-count"], (old) => old ? { ...old, count: Math.max(0, (old.count || 0) - 1) } : old);
|
|
280
|
+
queryClient.setQueryData(["notifications-dropdown"], (old) => {
|
|
281
|
+
if (!old?.results) return old;
|
|
282
|
+
return { ...old, results: old.results.map((n) => n.id === notif.id ? { ...n, is_read: true } : n) };
|
|
283
|
+
});
|
|
284
|
+
markRead(notif.id).catch(() => {
|
|
285
|
+
queryClient.invalidateQueries({ queryKey: ["notification-unread-count"] });
|
|
286
|
+
queryClient.invalidateQueries({ queryKey: ["notifications-dropdown"] });
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
setOpen(false);
|
|
290
|
+
onItemClick(notif);
|
|
291
|
+
}, [queryClient, markRead, onItemClick]);
|
|
292
|
+
const [inlineNotif, setInlineNotif] = useState(null);
|
|
293
|
+
const inlineTimerRef = useRef(null);
|
|
294
|
+
const dismissInline = useCallback(() => {
|
|
295
|
+
if (inlineTimerRef.current) {
|
|
296
|
+
clearTimeout(inlineTimerRef.current);
|
|
297
|
+
inlineTimerRef.current = null;
|
|
298
|
+
}
|
|
299
|
+
setInlineNotif(null);
|
|
300
|
+
}, []);
|
|
301
|
+
const showInlineNotif = useCallback((notif, durationMs = 5e3) => {
|
|
302
|
+
setInlineNotif(notif);
|
|
303
|
+
playNotification();
|
|
304
|
+
if (inlineTimerRef.current) clearTimeout(inlineTimerRef.current);
|
|
305
|
+
inlineTimerRef.current = setTimeout(() => setInlineNotif(null), durationMs);
|
|
306
|
+
}, []);
|
|
307
|
+
useEffect(() => () => {
|
|
308
|
+
if (inlineTimerRef.current) clearTimeout(inlineTimerRef.current);
|
|
309
|
+
}, []);
|
|
277
310
|
const prevCountRef = useRef(null);
|
|
278
311
|
useEffect(() => {
|
|
279
312
|
if (prevCountRef.current === null) {
|
|
@@ -291,18 +324,18 @@ function NotificationBell({
|
|
|
291
324
|
const n = new Notification(title, { body, icon: "/favicon.svg", tag: `notif-${latest.id}` });
|
|
292
325
|
n.onclick = () => {
|
|
293
326
|
window.focus();
|
|
294
|
-
|
|
327
|
+
handleClick(latest);
|
|
295
328
|
n.close();
|
|
296
329
|
};
|
|
297
330
|
}
|
|
298
331
|
} else {
|
|
299
|
-
|
|
332
|
+
showInlineNotif(latest);
|
|
300
333
|
}
|
|
301
334
|
}).catch(() => {
|
|
302
335
|
});
|
|
303
336
|
}
|
|
304
337
|
prevCountRef.current = unreadCount;
|
|
305
|
-
}, [unreadCount, list,
|
|
338
|
+
}, [unreadCount, list, showInlineNotif]);
|
|
306
339
|
const { data: notifData } = useQuery({
|
|
307
340
|
queryKey: ["notifications-dropdown"],
|
|
308
341
|
queryFn: () => list({ page_size: 30 }),
|
|
@@ -310,21 +343,6 @@ function NotificationBell({
|
|
|
310
343
|
refetchInterval: 3e4
|
|
311
344
|
});
|
|
312
345
|
const notifications = notifData?.results ?? [];
|
|
313
|
-
const handleClick = (notif) => {
|
|
314
|
-
if (!notif.is_read) {
|
|
315
|
-
queryClient.setQueryData(["notification-unread-count"], (old) => old ? { ...old, count: Math.max(0, (old.count || 0) - 1) } : old);
|
|
316
|
-
queryClient.setQueryData(["notifications-dropdown"], (old) => {
|
|
317
|
-
if (!old?.results) return old;
|
|
318
|
-
return { ...old, results: old.results.map((n) => n.id === notif.id ? { ...n, is_read: true } : n) };
|
|
319
|
-
});
|
|
320
|
-
markRead(notif.id).catch(() => {
|
|
321
|
-
queryClient.invalidateQueries({ queryKey: ["notification-unread-count"] });
|
|
322
|
-
queryClient.invalidateQueries({ queryKey: ["notifications-dropdown"] });
|
|
323
|
-
});
|
|
324
|
-
}
|
|
325
|
-
setOpen(false);
|
|
326
|
-
onItemClick(notif);
|
|
327
|
-
};
|
|
328
346
|
const handleMarkAllRead = () => {
|
|
329
347
|
queryClient.setQueryData(["notification-unread-count"], (old) => old ? { ...old, count: 0 } : old);
|
|
330
348
|
queryClient.setQueryData(["notifications-dropdown"], (old) => {
|
|
@@ -407,6 +425,43 @@ function NotificationBell({
|
|
|
407
425
|
] })
|
|
408
426
|
] }),
|
|
409
427
|
document.body
|
|
428
|
+
),
|
|
429
|
+
inlineNotif && createPortal(
|
|
430
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
431
|
+
/* @__PURE__ */ jsx("style", { children: `@keyframes notif-in { from { opacity: 0; transform: translateX(30px) scale(0.95); } to { opacity: 1; transform: translateX(0) scale(1); } }` }),
|
|
432
|
+
/* @__PURE__ */ jsxs(
|
|
433
|
+
"div",
|
|
434
|
+
{
|
|
435
|
+
onClick: () => {
|
|
436
|
+
handleClick(inlineNotif);
|
|
437
|
+
dismissInline();
|
|
438
|
+
},
|
|
439
|
+
className: "fixed top-4 right-4 z-[9999] w-[320px] max-w-[calc(100vw-2rem)] cursor-pointer rounded-2xl bg-white/85 backdrop-blur-md border border-white/40 shadow-2xl flex items-start gap-3 px-4 py-3",
|
|
440
|
+
style: { animation: "notif-in 300ms cubic-bezier(0.4,0,0.2,1)" },
|
|
441
|
+
children: [
|
|
442
|
+
/* @__PURE__ */ jsx("div", { className: "h-9 w-9 shrink-0 rounded-lg bg-blue-500/15 flex items-center justify-center", children: /* @__PURE__ */ jsx("svg", { className: "h-5 w-5 text-blue-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, 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" }) }) }),
|
|
443
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
444
|
+
/* @__PURE__ */ jsx("div", { className: "text-[11px] font-semibold uppercase tracking-wide text-blue-600 mb-0.5", children: "Notification" }),
|
|
445
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-gray-800 leading-snug truncate", children: inlineNotif.title }),
|
|
446
|
+
inlineNotif.message && /* @__PURE__ */ jsx("div", { className: "text-xs text-gray-500 leading-snug truncate mt-0.5", children: inlineNotif.message })
|
|
447
|
+
] }),
|
|
448
|
+
/* @__PURE__ */ jsx(
|
|
449
|
+
"button",
|
|
450
|
+
{
|
|
451
|
+
onClick: (e) => {
|
|
452
|
+
e.stopPropagation();
|
|
453
|
+
dismissInline();
|
|
454
|
+
},
|
|
455
|
+
className: "shrink-0 p-1 -mr-1 -mt-1 text-gray-400 hover:text-gray-600 transition-colors",
|
|
456
|
+
"aria-label": "Dismiss notification",
|
|
457
|
+
children: /* @__PURE__ */ jsx("svg", { className: "h-4 w-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2.2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" }) })
|
|
458
|
+
}
|
|
459
|
+
)
|
|
460
|
+
]
|
|
461
|
+
}
|
|
462
|
+
)
|
|
463
|
+
] }),
|
|
464
|
+
document.body
|
|
410
465
|
)
|
|
411
466
|
] });
|
|
412
467
|
}
|
|
@@ -651,7 +706,7 @@ function StatusBadge({ status }) {
|
|
|
651
706
|
}
|
|
652
707
|
|
|
653
708
|
// src/version.ts
|
|
654
|
-
var VERSION = "0.1
|
|
709
|
+
var VERSION = "0.2.1" ;
|
|
655
710
|
var APP_VERSION = VERSION;
|
|
656
711
|
|
|
657
712
|
// src/changelog.ts
|
|
@@ -2395,6 +2450,7 @@ function StartMenu({
|
|
|
2395
2450
|
virtualSections.map((v) => [v.label, v])
|
|
2396
2451
|
);
|
|
2397
2452
|
const { hasAnyPerm } = useAuth();
|
|
2453
|
+
const isMobile = useIsMobile();
|
|
2398
2454
|
const [hoveredSection, setHoveredSection] = useState(null);
|
|
2399
2455
|
const [hoveredY, setHoveredY] = useState(0);
|
|
2400
2456
|
const [search, setSearch] = useState("");
|
|
@@ -2429,6 +2485,68 @@ function StartMenu({
|
|
|
2429
2485
|
openPage(path);
|
|
2430
2486
|
onClose();
|
|
2431
2487
|
};
|
|
2488
|
+
if (isMobile) {
|
|
2489
|
+
const allItems = [];
|
|
2490
|
+
for (const entry of navSections2) {
|
|
2491
|
+
if (isSection(entry)) {
|
|
2492
|
+
const sec = entry;
|
|
2493
|
+
if (sec.perms && !hasAnyPerm(sec.perms)) continue;
|
|
2494
|
+
for (const it of sec.items) {
|
|
2495
|
+
if (it.perms && !hasAnyPerm(it.perms)) continue;
|
|
2496
|
+
allItems.push({ item: it, sectionLabel: sec.label });
|
|
2497
|
+
}
|
|
2498
|
+
} else {
|
|
2499
|
+
const it = entry;
|
|
2500
|
+
if (it.perms && !hasAnyPerm(it.perms)) continue;
|
|
2501
|
+
allItems.push({ item: it });
|
|
2502
|
+
}
|
|
2503
|
+
}
|
|
2504
|
+
const filtered = search.length >= 1 ? allItems.filter(({ item }) => item.label.toLowerCase().includes(search.toLowerCase())) : allItems;
|
|
2505
|
+
return /* @__PURE__ */ jsxs(
|
|
2506
|
+
"div",
|
|
2507
|
+
{
|
|
2508
|
+
ref: menuRef,
|
|
2509
|
+
className: "fixed inset-0 z-[260] flex flex-col bg-white",
|
|
2510
|
+
style: { paddingBottom: "var(--mobile-bottom-nav, 56px)" },
|
|
2511
|
+
children: [
|
|
2512
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-3 py-2 border-b border-gray-200", children: [
|
|
2513
|
+
/* @__PURE__ */ jsx("button", { onClick: onClose, className: "p-2 -ml-1 rounded-full active:bg-gray-200 text-gray-700", "aria-label": "Close menu", children: /* @__PURE__ */ jsx("svg", { className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" }) }) }),
|
|
2514
|
+
/* @__PURE__ */ jsxs("div", { className: `flex-1 flex items-center gap-2 ${GLASS_INPUT_BG} rounded-lg px-3 py-2`, children: [
|
|
2515
|
+
/* @__PURE__ */ jsx("svg", { className: "h-4 w-4 text-gray-400 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z" }) }),
|
|
2516
|
+
/* @__PURE__ */ jsx(
|
|
2517
|
+
"input",
|
|
2518
|
+
{
|
|
2519
|
+
value: search,
|
|
2520
|
+
onChange: (e) => setSearch(e.target.value),
|
|
2521
|
+
placeholder: "Search apps...",
|
|
2522
|
+
className: "flex-1 bg-transparent text-sm outline-none placeholder-gray-400",
|
|
2523
|
+
autoFocus: true
|
|
2524
|
+
}
|
|
2525
|
+
)
|
|
2526
|
+
] })
|
|
2527
|
+
] }),
|
|
2528
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto", children: filtered.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-400 text-center py-12", children: "No matching apps" }) : filtered.map(({ item, sectionLabel }, i) => {
|
|
2529
|
+
const icon = navIcons2[item.to];
|
|
2530
|
+
return /* @__PURE__ */ jsxs(
|
|
2531
|
+
"button",
|
|
2532
|
+
{
|
|
2533
|
+
onClick: () => handleClick(item.to),
|
|
2534
|
+
className: "w-full flex items-center gap-3 px-4 py-3 active:bg-gray-100 border-b border-gray-100 text-left",
|
|
2535
|
+
children: [
|
|
2536
|
+
/* @__PURE__ */ jsx("span", { className: "h-8 w-8 rounded-lg bg-gray-100 flex items-center justify-center text-gray-600 shrink-0", children: icon && isValidElement(icon) ? cloneElement(icon, { className: "h-5 w-5" }) : null }),
|
|
2537
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
2538
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-gray-900 truncate", children: item.label }),
|
|
2539
|
+
sectionLabel && /* @__PURE__ */ jsx("div", { className: "text-[11px] text-gray-500 truncate", children: sectionLabel })
|
|
2540
|
+
] })
|
|
2541
|
+
]
|
|
2542
|
+
},
|
|
2543
|
+
`${item.to}-${i}`
|
|
2544
|
+
);
|
|
2545
|
+
}) })
|
|
2546
|
+
]
|
|
2547
|
+
}
|
|
2548
|
+
);
|
|
2549
|
+
}
|
|
2432
2550
|
const isVertical = taskbarPosition !== "bottom";
|
|
2433
2551
|
const topItems = navSections2.filter((item) => !isSection(item));
|
|
2434
2552
|
const erpSections = navSections2.filter((item) => isSection(item) && erpLabels.has(item.label));
|
|
@@ -2673,6 +2791,758 @@ function StartMenu({
|
|
|
2673
2791
|
` })
|
|
2674
2792
|
] });
|
|
2675
2793
|
}
|
|
2794
|
+
var MOBILE_WIDGET_ORDER_KEY = "erp_mobile_widget_order";
|
|
2795
|
+
var MOBILE_HOME_ORDER_KEY = "erp_mobile_home_order";
|
|
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
|
+
}
|
|
2819
|
+
function loadOrder(key) {
|
|
2820
|
+
try {
|
|
2821
|
+
const raw = localStorage.getItem(key);
|
|
2822
|
+
if (raw) {
|
|
2823
|
+
const parsed = JSON.parse(raw);
|
|
2824
|
+
if (Array.isArray(parsed)) return parsed.filter((s) => typeof s === "string");
|
|
2825
|
+
}
|
|
2826
|
+
} catch {
|
|
2827
|
+
}
|
|
2828
|
+
return [];
|
|
2829
|
+
}
|
|
2830
|
+
function saveOrder(key, order) {
|
|
2831
|
+
try {
|
|
2832
|
+
localStorage.setItem(key, JSON.stringify(order));
|
|
2833
|
+
} catch {
|
|
2834
|
+
}
|
|
2835
|
+
}
|
|
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" }) });
|
|
2837
|
+
function sizeIcon(node, fallback, sizeClass = "h-10 w-10") {
|
|
2838
|
+
if (!node) return fallback;
|
|
2839
|
+
if (isValidElement(node)) {
|
|
2840
|
+
return cloneElement(node, {
|
|
2841
|
+
className: `${sizeClass} ${node.props?.className ?? ""}`.trim()
|
|
2842
|
+
});
|
|
2843
|
+
}
|
|
2844
|
+
return node;
|
|
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
|
+
}
|
|
2867
|
+
function MobileHome({
|
|
2868
|
+
navSections: navSections2,
|
|
2869
|
+
navIcons: navIcons2,
|
|
2870
|
+
sectionIcons: sectionIcons2,
|
|
2871
|
+
openWindows,
|
|
2872
|
+
onOpenApp,
|
|
2873
|
+
onActivateWindow
|
|
2874
|
+
}) {
|
|
2875
|
+
const [selectedFolder, setSelectedFolder] = useState(null);
|
|
2876
|
+
const homeIconsRaw = useMemo(() => {
|
|
2877
|
+
const list = [];
|
|
2878
|
+
for (const entry of navSections2) {
|
|
2879
|
+
if (isSection(entry)) {
|
|
2880
|
+
const sec = entry;
|
|
2881
|
+
list.push({ kind: "folder", id: `folder:${sec.label}`, label: sec.label, section: sec });
|
|
2882
|
+
} else {
|
|
2883
|
+
const it = entry;
|
|
2884
|
+
list.push({ kind: "app", id: `app:${it.to}`, label: it.label, route: it.to });
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2887
|
+
return list;
|
|
2888
|
+
}, [navSections2]);
|
|
2889
|
+
const [homeOrder, setHomeOrder] = useState(() => loadOrder(MOBILE_HOME_ORDER_KEY));
|
|
2890
|
+
const homeIcons = useMemo(() => {
|
|
2891
|
+
const indexFor = (id) => {
|
|
2892
|
+
const i = homeOrder.indexOf(id);
|
|
2893
|
+
return i === -1 ? Number.MAX_SAFE_INTEGER : i;
|
|
2894
|
+
};
|
|
2895
|
+
return homeIconsRaw.slice().sort((a, b) => {
|
|
2896
|
+
const ia = indexFor(a.id);
|
|
2897
|
+
const ib = indexFor(b.id);
|
|
2898
|
+
if (ia !== ib) return ia - ib;
|
|
2899
|
+
return homeIconsRaw.indexOf(a) - homeIconsRaw.indexOf(b);
|
|
2900
|
+
});
|
|
2901
|
+
}, [homeIconsRaw, homeOrder]);
|
|
2902
|
+
useEffect(() => {
|
|
2903
|
+
const visibleIds = homeIconsRaw.map((i) => i.id);
|
|
2904
|
+
const next = [
|
|
2905
|
+
...homeOrder.filter((id) => visibleIds.includes(id)),
|
|
2906
|
+
...visibleIds.filter((id) => !homeOrder.includes(id))
|
|
2907
|
+
];
|
|
2908
|
+
if (next.length !== homeOrder.length || next.some((id, i) => id !== homeOrder[i])) {
|
|
2909
|
+
setHomeOrder(next);
|
|
2910
|
+
saveOrder(MOBILE_HOME_ORDER_KEY, next);
|
|
2911
|
+
}
|
|
2912
|
+
}, [homeIconsRaw, homeOrder]);
|
|
2913
|
+
const [dragId, setDragId] = useState(null);
|
|
2914
|
+
const [dragPos, setDragPos] = useState(null);
|
|
2915
|
+
const dragOffsetRef = useRef({ x: 0, y: 0 });
|
|
2916
|
+
const longPressTimerRef = useRef(null);
|
|
2917
|
+
const longPressFiredRef = useRef(false);
|
|
2918
|
+
const cancelLongPress = () => {
|
|
2919
|
+
if (longPressTimerRef.current) {
|
|
2920
|
+
clearTimeout(longPressTimerRef.current);
|
|
2921
|
+
longPressTimerRef.current = null;
|
|
2922
|
+
}
|
|
2923
|
+
};
|
|
2924
|
+
const beginLongPress = (id, e) => {
|
|
2925
|
+
if (dragId) return;
|
|
2926
|
+
const x = e.clientX;
|
|
2927
|
+
const y = e.clientY;
|
|
2928
|
+
const target = e.currentTarget;
|
|
2929
|
+
longPressFiredRef.current = false;
|
|
2930
|
+
cancelLongPress();
|
|
2931
|
+
longPressTimerRef.current = setTimeout(() => {
|
|
2932
|
+
if (!target.isConnected) return;
|
|
2933
|
+
const rect = target.getBoundingClientRect();
|
|
2934
|
+
dragOffsetRef.current = { x: x - rect.left, y: y - rect.top };
|
|
2935
|
+
longPressFiredRef.current = true;
|
|
2936
|
+
setDragId(id);
|
|
2937
|
+
setDragPos({ x, y });
|
|
2938
|
+
try {
|
|
2939
|
+
navigator.vibrate?.(15);
|
|
2940
|
+
} catch {
|
|
2941
|
+
}
|
|
2942
|
+
}, LONG_PRESS_MS);
|
|
2943
|
+
};
|
|
2944
|
+
useEffect(() => {
|
|
2945
|
+
if (!dragId) return;
|
|
2946
|
+
const onMove = (e) => {
|
|
2947
|
+
setDragPos({ x: e.clientX, y: e.clientY });
|
|
2948
|
+
const el = document.elementFromPoint(e.clientX, e.clientY);
|
|
2949
|
+
const targetEl = el?.closest("[data-home-icon-id]");
|
|
2950
|
+
if (!targetEl) return;
|
|
2951
|
+
const targetId = targetEl.dataset.homeIconId;
|
|
2952
|
+
if (targetId === dragId) return;
|
|
2953
|
+
setHomeOrder((prev) => {
|
|
2954
|
+
const fromIdx = prev.indexOf(dragId);
|
|
2955
|
+
const toIdx = prev.indexOf(targetId);
|
|
2956
|
+
if (fromIdx === -1 || toIdx === -1) return prev;
|
|
2957
|
+
const next = prev.slice();
|
|
2958
|
+
next.splice(fromIdx, 1);
|
|
2959
|
+
next.splice(toIdx, 0, dragId);
|
|
2960
|
+
return next;
|
|
2961
|
+
});
|
|
2962
|
+
};
|
|
2963
|
+
const onUp = () => {
|
|
2964
|
+
setDragId(null);
|
|
2965
|
+
setDragPos(null);
|
|
2966
|
+
setHomeOrder((prev) => {
|
|
2967
|
+
saveOrder(MOBILE_HOME_ORDER_KEY, prev);
|
|
2968
|
+
return prev;
|
|
2969
|
+
});
|
|
2970
|
+
};
|
|
2971
|
+
window.addEventListener("pointermove", onMove);
|
|
2972
|
+
window.addEventListener("pointerup", onUp);
|
|
2973
|
+
window.addEventListener("pointercancel", onUp);
|
|
2974
|
+
return () => {
|
|
2975
|
+
window.removeEventListener("pointermove", onMove);
|
|
2976
|
+
window.removeEventListener("pointerup", onUp);
|
|
2977
|
+
window.removeEventListener("pointercancel", onUp);
|
|
2978
|
+
};
|
|
2979
|
+
}, [dragId]);
|
|
2980
|
+
const handleIconClick = (icon) => {
|
|
2981
|
+
if (longPressFiredRef.current) {
|
|
2982
|
+
longPressFiredRef.current = false;
|
|
2983
|
+
return;
|
|
2984
|
+
}
|
|
2985
|
+
if (icon.kind === "app") onOpenApp(icon.route);
|
|
2986
|
+
else setSelectedFolder(icon.section);
|
|
2987
|
+
};
|
|
2988
|
+
const [widgetOrder, _setWidgetOrder] = useState(() => loadOrder(MOBILE_WIDGET_ORDER_KEY));
|
|
2989
|
+
const widgetWindows = useMemo(() => {
|
|
2990
|
+
const widgets = openWindows.filter((w) => {
|
|
2991
|
+
if (!w.route) return false;
|
|
2992
|
+
const entry = WINDOW_REGISTRY[w.route];
|
|
2993
|
+
return entry && isPageEntry(entry) && entry.widget;
|
|
2994
|
+
});
|
|
2995
|
+
const indexFor = (route) => {
|
|
2996
|
+
const i = widgetOrder.indexOf(route);
|
|
2997
|
+
return i === -1 ? Number.MAX_SAFE_INTEGER : i;
|
|
2998
|
+
};
|
|
2999
|
+
return widgets.slice().sort((a, b) => indexFor(a.route) - indexFor(b.route));
|
|
3000
|
+
}, [openWindows, widgetOrder]);
|
|
3001
|
+
useEffect(() => {
|
|
3002
|
+
const visibleRoutes = widgetWindows.map((w) => w.route).filter(Boolean);
|
|
3003
|
+
const next = [
|
|
3004
|
+
...widgetOrder.filter((r) => visibleRoutes.includes(r)),
|
|
3005
|
+
...visibleRoutes.filter((r) => !widgetOrder.includes(r))
|
|
3006
|
+
];
|
|
3007
|
+
if (next.length !== widgetOrder.length || next.some((r, i) => r !== widgetOrder[i])) {
|
|
3008
|
+
_setWidgetOrder(next);
|
|
3009
|
+
saveOrder(MOBILE_WIDGET_ORDER_KEY, next);
|
|
3010
|
+
}
|
|
3011
|
+
}, [widgetWindows, widgetOrder]);
|
|
3012
|
+
const openCountByRoute = useMemo(() => {
|
|
3013
|
+
const map = /* @__PURE__ */ new Map();
|
|
3014
|
+
for (const w of openWindows) {
|
|
3015
|
+
if (!w.route) continue;
|
|
3016
|
+
map.set(w.route, (map.get(w.route) ?? 0) + 1);
|
|
3017
|
+
}
|
|
3018
|
+
return map;
|
|
3019
|
+
}, [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
|
+
const draggedIcon = dragId ? homeIcons.find((i) => i.id === dragId) : null;
|
|
3025
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
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
|
+
),
|
|
3076
|
+
draggedIcon && dragPos && /* @__PURE__ */ jsx(
|
|
3077
|
+
"div",
|
|
3078
|
+
{
|
|
3079
|
+
className: "fixed pointer-events-none z-[400] transition-none",
|
|
3080
|
+
style: {
|
|
3081
|
+
left: dragPos.x - dragOffsetRef.current.x,
|
|
3082
|
+
top: dragPos.y - dragOffsetRef.current.y,
|
|
3083
|
+
transform: "scale(1.12)"
|
|
3084
|
+
},
|
|
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 })
|
|
3088
|
+
] })
|
|
3089
|
+
}
|
|
3090
|
+
),
|
|
3091
|
+
selectedFolder && /* @__PURE__ */ jsx(
|
|
3092
|
+
FolderPopup,
|
|
3093
|
+
{
|
|
3094
|
+
folder: selectedFolder,
|
|
3095
|
+
navIcons: navIcons2,
|
|
3096
|
+
openInFolder: openInFolder(selectedFolder),
|
|
3097
|
+
openCountByRoute,
|
|
3098
|
+
onClose: () => setSelectedFolder(null),
|
|
3099
|
+
onOpenApp: (path) => {
|
|
3100
|
+
setSelectedFolder(null);
|
|
3101
|
+
onOpenApp(path);
|
|
3102
|
+
},
|
|
3103
|
+
onActivateWindow: (id) => {
|
|
3104
|
+
setSelectedFolder(null);
|
|
3105
|
+
onActivateWindow(id);
|
|
3106
|
+
}
|
|
3107
|
+
}
|
|
3108
|
+
)
|
|
3109
|
+
] });
|
|
3110
|
+
}
|
|
3111
|
+
function FolderPopup({
|
|
3112
|
+
folder,
|
|
3113
|
+
navIcons: navIcons2,
|
|
3114
|
+
openInFolder,
|
|
3115
|
+
openCountByRoute,
|
|
3116
|
+
onClose,
|
|
3117
|
+
onOpenApp,
|
|
3118
|
+
onActivateWindow
|
|
3119
|
+
}) {
|
|
3120
|
+
return /* @__PURE__ */ jsxs(
|
|
3121
|
+
"div",
|
|
3122
|
+
{
|
|
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
|
+
},
|
|
3130
|
+
onClick: onClose,
|
|
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
|
+
` }),
|
|
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;
|
|
3161
|
+
return /* @__PURE__ */ jsxs(
|
|
3162
|
+
"button",
|
|
3163
|
+
{
|
|
3164
|
+
onClick: () => onOpenApp(item.to),
|
|
3165
|
+
className: "flex flex-col items-center gap-1.5 p-1 rounded-lg active:bg-white/15",
|
|
3166
|
+
children: [
|
|
3167
|
+
/* @__PURE__ */ jsx(AppTile, { route: item.to, icon: navIcons2[item.to], badge: openCount > 0 }),
|
|
3168
|
+
/* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium text-white drop-shadow-sm truncate w-full text-center", children: item.label })
|
|
3169
|
+
]
|
|
3170
|
+
},
|
|
3171
|
+
item.to
|
|
3172
|
+
);
|
|
3173
|
+
}) })
|
|
3174
|
+
] })
|
|
3175
|
+
}
|
|
3176
|
+
)
|
|
3177
|
+
]
|
|
3178
|
+
}
|
|
3179
|
+
);
|
|
3180
|
+
}
|
|
3181
|
+
function MobileSwitcher({ windows, onActivate, onClose }) {
|
|
3182
|
+
const [cardSize, setCardSize] = useState(() => computeCardSize());
|
|
3183
|
+
useEffect(() => {
|
|
3184
|
+
const onResize = () => setCardSize(computeCardSize());
|
|
3185
|
+
window.addEventListener("resize", onResize);
|
|
3186
|
+
return () => window.removeEventListener("resize", onResize);
|
|
3187
|
+
}, []);
|
|
3188
|
+
if (windows.length === 0) {
|
|
3189
|
+
return /* @__PURE__ */ jsxs("div", { className: "h-full flex flex-col items-center justify-center gap-3 text-white/80 px-6 text-center", children: [
|
|
3190
|
+
/* @__PURE__ */ jsxs("svg", { className: "h-12 w-12 text-white/40", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.4, children: [
|
|
3191
|
+
/* @__PURE__ */ jsx("rect", { x: "3.5", y: "3.5", width: "7", height: "7", rx: "1.25" }),
|
|
3192
|
+
/* @__PURE__ */ jsx("rect", { x: "13.5", y: "3.5", width: "7", height: "7", rx: "1.25" }),
|
|
3193
|
+
/* @__PURE__ */ jsx("rect", { x: "3.5", y: "13.5", width: "7", height: "7", rx: "1.25" }),
|
|
3194
|
+
/* @__PURE__ */ jsx("rect", { x: "13.5", y: "13.5", width: "7", height: "7", rx: "1.25" })
|
|
3195
|
+
] }),
|
|
3196
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm", children: "No open apps." }),
|
|
3197
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-white/50", children: "Tap Home to launch one." })
|
|
3198
|
+
] });
|
|
3199
|
+
}
|
|
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
|
|
3204
|
+
] }),
|
|
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
|
+
] });
|
|
3221
|
+
}
|
|
3222
|
+
function computeCardSize() {
|
|
3223
|
+
const w = typeof window !== "undefined" ? window.innerWidth : 360;
|
|
3224
|
+
const h = typeof window !== "undefined" ? window.innerHeight : 640;
|
|
3225
|
+
const cardW = Math.max(120, Math.floor((w - 36) / 2));
|
|
3226
|
+
const cardH = Math.min(cardW * 1.4, Math.floor(h * 0.4));
|
|
3227
|
+
return { w: cardW, h: cardH };
|
|
3228
|
+
}
|
|
3229
|
+
function timeAgo2(dateStr) {
|
|
3230
|
+
const diff = Date.now() - new Date(dateStr).getTime();
|
|
3231
|
+
const mins = Math.floor(diff / 6e4);
|
|
3232
|
+
if (mins < 1) return "just now";
|
|
3233
|
+
if (mins < 60) return `${mins}m ago`;
|
|
3234
|
+
const hrs = Math.floor(mins / 60);
|
|
3235
|
+
if (hrs < 24) return `${hrs}h ago`;
|
|
3236
|
+
const days = Math.floor(hrs / 24);
|
|
3237
|
+
if (days < 7) return `${days}d ago`;
|
|
3238
|
+
return formatDate(dateStr);
|
|
3239
|
+
}
|
|
3240
|
+
function MobileNotificationSheet({ config, onClose }) {
|
|
3241
|
+
const { list, markRead, markAllRead, onItemClick } = config;
|
|
3242
|
+
const queryClient = useQueryClient();
|
|
3243
|
+
const unreadCount = config.useUnreadCount();
|
|
3244
|
+
const { data: notifData } = useQuery({
|
|
3245
|
+
queryKey: ["notifications-dropdown"],
|
|
3246
|
+
queryFn: () => list({ page_size: 30 }),
|
|
3247
|
+
staleTime: 3e4
|
|
3248
|
+
});
|
|
3249
|
+
const notifications = notifData?.results ?? [];
|
|
3250
|
+
const handleClick = useCallback((notif) => {
|
|
3251
|
+
if (!notif.is_read) {
|
|
3252
|
+
queryClient.setQueryData(["notification-unread-count"], (old) => old ? { ...old, count: Math.max(0, (old.count || 0) - 1) } : old);
|
|
3253
|
+
queryClient.setQueryData(["notifications-dropdown"], (old) => {
|
|
3254
|
+
if (!old?.results) return old;
|
|
3255
|
+
return { ...old, results: old.results.map((n) => n.id === notif.id ? { ...n, is_read: true } : n) };
|
|
3256
|
+
});
|
|
3257
|
+
markRead(notif.id).catch(() => {
|
|
3258
|
+
queryClient.invalidateQueries({ queryKey: ["notification-unread-count"] });
|
|
3259
|
+
queryClient.invalidateQueries({ queryKey: ["notifications-dropdown"] });
|
|
3260
|
+
});
|
|
3261
|
+
}
|
|
3262
|
+
onClose();
|
|
3263
|
+
onItemClick(notif);
|
|
3264
|
+
}, [queryClient, markRead, onItemClick, onClose]);
|
|
3265
|
+
const handleMarkAllRead = () => {
|
|
3266
|
+
queryClient.setQueryData(["notification-unread-count"], (old) => old ? { ...old, count: 0 } : old);
|
|
3267
|
+
queryClient.setQueryData(["notifications-dropdown"], (old) => {
|
|
3268
|
+
if (!old?.results) return old;
|
|
3269
|
+
return { ...old, results: old.results.map((n) => ({ ...n, is_read: true })) };
|
|
3270
|
+
});
|
|
3271
|
+
markAllRead().then(() => {
|
|
3272
|
+
queryClient.invalidateQueries({ queryKey: ["notification-unread-count"] });
|
|
3273
|
+
queryClient.invalidateQueries({ queryKey: ["notifications-dropdown"] });
|
|
3274
|
+
});
|
|
3275
|
+
};
|
|
3276
|
+
return /* @__PURE__ */ jsxs(
|
|
3277
|
+
"div",
|
|
3278
|
+
{
|
|
3279
|
+
className: "fixed inset-0 z-[210] flex flex-col bg-white",
|
|
3280
|
+
style: { paddingBottom: "var(--mobile-bottom-nav, 70px)" },
|
|
3281
|
+
children: [
|
|
3282
|
+
/* @__PURE__ */ jsxs("header", { className: "flex items-center justify-between px-3 py-3 border-b border-gray-200 shrink-0", children: [
|
|
3283
|
+
/* @__PURE__ */ jsx("button", { onClick: onClose, className: "p-2 -ml-1 rounded-full active:bg-gray-200 text-gray-700", "aria-label": "Close", children: /* @__PURE__ */ jsx("svg", { className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" }) }) }),
|
|
3284
|
+
/* @__PURE__ */ jsx("h1", { className: "text-base font-semibold text-gray-900", children: "Notifications" }),
|
|
3285
|
+
unreadCount > 0 ? /* @__PURE__ */ jsx("button", { onClick: handleMarkAllRead, className: "text-xs text-blue-600 font-medium px-2 py-1 active:bg-blue-50 rounded", children: "Mark all read" }) : /* @__PURE__ */ jsx("span", { className: "w-[88px]" })
|
|
3286
|
+
] }),
|
|
3287
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto", children: notifications.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-16 px-6 text-center", children: [
|
|
3288
|
+
/* @__PURE__ */ jsx("svg", { className: "h-10 w-10 text-gray-300 mb-3", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, 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" }) }),
|
|
3289
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-700 font-medium", children: "All caught up" }),
|
|
3290
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-gray-400 mt-1", children: "No notifications yet" })
|
|
3291
|
+
] }) : notifications.map((notif) => /* @__PURE__ */ jsxs(
|
|
3292
|
+
"button",
|
|
3293
|
+
{
|
|
3294
|
+
onClick: () => handleClick(notif),
|
|
3295
|
+
className: "w-full flex items-start gap-3 px-4 py-3 active:bg-gray-100 border-b border-gray-100 text-left",
|
|
3296
|
+
children: [
|
|
3297
|
+
/* @__PURE__ */ jsx("div", { className: "pt-1 shrink-0", children: !notif.is_read ? /* @__PURE__ */ jsx("div", { className: "h-2.5 w-2.5 rounded-full bg-blue-500" }) : /* @__PURE__ */ jsx("div", { className: "h-2.5 w-2.5" }) }),
|
|
3298
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
3299
|
+
/* @__PURE__ */ jsx("p", { className: `text-sm leading-snug ${!notif.is_read ? "font-medium text-gray-900" : "text-gray-700"}`, children: notif.title }),
|
|
3300
|
+
notif.message && /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 mt-1 line-clamp-2", children: notif.message }),
|
|
3301
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mt-1.5", children: [
|
|
3302
|
+
/* @__PURE__ */ jsx("span", { className: "text-[11px] text-gray-400", children: timeAgo2(notif.created_at) }),
|
|
3303
|
+
notif.actor_name && /* @__PURE__ */ jsxs("span", { className: "text-[11px] text-gray-400", children: [
|
|
3304
|
+
"by ",
|
|
3305
|
+
notif.actor_name
|
|
3306
|
+
] })
|
|
3307
|
+
] })
|
|
3308
|
+
] })
|
|
3309
|
+
]
|
|
3310
|
+
},
|
|
3311
|
+
notif.id
|
|
3312
|
+
)) })
|
|
3313
|
+
]
|
|
3314
|
+
}
|
|
3315
|
+
);
|
|
3316
|
+
}
|
|
3317
|
+
function MobileProfileSheet({ profile, user, onClose, onNavigate, onLogout }) {
|
|
3318
|
+
const initial = (profile?.first_name?.charAt(0) || user?.email?.charAt(0) || "?").toUpperCase();
|
|
3319
|
+
const displayName = profile?.first_name ? `${profile.first_name} ${profile.last_name || ""}`.trim() : user?.email ?? "Account";
|
|
3320
|
+
const groups = profile?.group_names ?? [];
|
|
3321
|
+
const handleNav = (path) => {
|
|
3322
|
+
onNavigate(path);
|
|
3323
|
+
onClose();
|
|
3324
|
+
};
|
|
3325
|
+
return /* @__PURE__ */ jsxs(
|
|
3326
|
+
"div",
|
|
3327
|
+
{
|
|
3328
|
+
className: "fixed inset-0 z-[210] flex flex-col bg-white",
|
|
3329
|
+
style: { paddingBottom: "var(--mobile-bottom-nav, 70px)" },
|
|
3330
|
+
children: [
|
|
3331
|
+
/* @__PURE__ */ jsxs("header", { className: "flex items-center justify-between px-3 py-3 border-b border-gray-200 shrink-0", children: [
|
|
3332
|
+
/* @__PURE__ */ jsx("button", { onClick: onClose, className: "p-2 -ml-1 rounded-full active:bg-gray-200 text-gray-700", "aria-label": "Close", children: /* @__PURE__ */ jsx("svg", { className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" }) }) }),
|
|
3333
|
+
/* @__PURE__ */ jsx("h1", { className: "text-base font-semibold text-gray-900", children: "Profile" }),
|
|
3334
|
+
/* @__PURE__ */ jsx("span", { className: "w-10" })
|
|
3335
|
+
] }),
|
|
3336
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto", children: [
|
|
3337
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center text-center px-6 py-8 border-b border-gray-100", children: [
|
|
3338
|
+
profile?.avatar_url ? /* @__PURE__ */ jsx("img", { src: profile.avatar_url, alt: "", className: "h-20 w-20 rounded-full object-cover border border-gray-200 shadow-sm" }) : /* @__PURE__ */ jsx("div", { className: "h-20 w-20 rounded-full bg-blue-100 flex items-center justify-center text-2xl font-bold text-blue-700 shadow-sm", children: initial }),
|
|
3339
|
+
/* @__PURE__ */ jsx("div", { className: "mt-3 text-base font-semibold text-gray-900 truncate max-w-full", children: displayName }),
|
|
3340
|
+
user?.email && profile?.first_name && /* @__PURE__ */ jsx("div", { className: "text-sm text-gray-500 truncate max-w-full", children: user.email }),
|
|
3341
|
+
groups.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-2 flex flex-wrap justify-center gap-1.5", children: groups.map((g) => /* @__PURE__ */ jsx("span", { className: "text-[11px] px-2 py-0.5 rounded-full bg-gray-100 text-gray-700", children: g }, g)) })
|
|
3342
|
+
] }),
|
|
3343
|
+
/* @__PURE__ */ jsxs("div", { className: "py-2", children: [
|
|
3344
|
+
/* @__PURE__ */ jsxs(
|
|
3345
|
+
"button",
|
|
3346
|
+
{
|
|
3347
|
+
onClick: () => handleNav("/customization"),
|
|
3348
|
+
className: "w-full flex items-center gap-3 px-4 py-4 active:bg-gray-100 border-b border-gray-100 text-left",
|
|
3349
|
+
children: [
|
|
3350
|
+
/* @__PURE__ */ jsx("span", { className: "h-9 w-9 rounded-lg bg-gray-100 flex items-center justify-center text-gray-700 shrink-0", children: /* @__PURE__ */ jsxs("svg", { className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: [
|
|
3351
|
+
/* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z" }),
|
|
3352
|
+
/* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M15 12a3 3 0 11-6 0 3 3 0 016 0z" })
|
|
3353
|
+
] }) }),
|
|
3354
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 text-sm font-medium text-gray-800", children: "Customization" }),
|
|
3355
|
+
/* @__PURE__ */ jsx("svg", { className: "h-4 w-4 text-gray-400 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M8.25 4.5l7.5 7.5-7.5 7.5" }) })
|
|
3356
|
+
]
|
|
3357
|
+
}
|
|
3358
|
+
),
|
|
3359
|
+
/* @__PURE__ */ jsxs(
|
|
3360
|
+
"button",
|
|
3361
|
+
{
|
|
3362
|
+
onClick: () => {
|
|
3363
|
+
onLogout();
|
|
3364
|
+
onClose();
|
|
3365
|
+
},
|
|
3366
|
+
className: "w-full flex items-center gap-3 px-4 py-4 active:bg-red-50 text-left",
|
|
3367
|
+
children: [
|
|
3368
|
+
/* @__PURE__ */ jsx("span", { className: "h-9 w-9 rounded-lg bg-red-50 flex items-center justify-center text-red-600 shrink-0", children: /* @__PURE__ */ jsx("svg", { className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M15.75 9V5.25A2.25 2.25 0 0013.5 3h-6a2.25 2.25 0 00-2.25 2.25v13.5A2.25 2.25 0 007.5 21h6a2.25 2.25 0 002.25-2.25V15M12 9l-3 3m0 0l3 3m-3-3h12.75" }) }) }),
|
|
3369
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 text-sm font-medium text-red-600", children: "Sign out" })
|
|
3370
|
+
]
|
|
3371
|
+
}
|
|
3372
|
+
)
|
|
3373
|
+
] })
|
|
3374
|
+
] })
|
|
3375
|
+
]
|
|
3376
|
+
}
|
|
3377
|
+
);
|
|
3378
|
+
}
|
|
3379
|
+
function MobileShell({
|
|
3380
|
+
navSections: navSections2,
|
|
3381
|
+
navIcons: navIcons2,
|
|
3382
|
+
sectionIcons: sectionIcons2,
|
|
3383
|
+
wallpaperStyle,
|
|
3384
|
+
notifications,
|
|
3385
|
+
profile,
|
|
3386
|
+
user,
|
|
3387
|
+
onNavigate,
|
|
3388
|
+
onLogout
|
|
3389
|
+
}) {
|
|
3390
|
+
const { openWindows, openPage, closeEntity } = useWindowManager();
|
|
3391
|
+
const mode = useSyncExternalStore(subscribeMobileMode, getMobileMode);
|
|
3392
|
+
const [sheet, setSheet] = useState(null);
|
|
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]);
|
|
3402
|
+
const prevOpenCountRef = useRef(openWindows.length);
|
|
3403
|
+
useEffect(() => {
|
|
3404
|
+
if (openWindows.length < prevOpenCountRef.current && mode === "app") {
|
|
3405
|
+
setMobileMode("home");
|
|
3406
|
+
}
|
|
3407
|
+
prevOpenCountRef.current = openWindows.length;
|
|
3408
|
+
}, [openWindows.length, mode]);
|
|
3409
|
+
const activateWindowById = (windowId) => {
|
|
3410
|
+
const panel = document.querySelector(`[data-modal-panel][data-window-key="${windowId}"]`);
|
|
3411
|
+
const mid = panel?.getAttribute("data-modal-id");
|
|
3412
|
+
if (mid) activateModal(mid);
|
|
3413
|
+
};
|
|
3414
|
+
const handleOpenApp = (path) => {
|
|
3415
|
+
openPage(path);
|
|
3416
|
+
setMobileMode("app");
|
|
3417
|
+
};
|
|
3418
|
+
const handleActivateWindow = (id) => {
|
|
3419
|
+
activateWindowById(id);
|
|
3420
|
+
setMobileMode("app");
|
|
3421
|
+
};
|
|
3422
|
+
const closeSheet = () => setSheet(null);
|
|
3423
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3424
|
+
mode === "home" && /* @__PURE__ */ jsx(
|
|
3425
|
+
"div",
|
|
3426
|
+
{
|
|
3427
|
+
className: "fixed inset-0 z-[200]",
|
|
3428
|
+
style: {
|
|
3429
|
+
...wallpaperStyle,
|
|
3430
|
+
paddingBottom: "var(--mobile-bottom-nav, 70px)"
|
|
3431
|
+
},
|
|
3432
|
+
children: /* @__PURE__ */ jsx(
|
|
3433
|
+
MobileHome,
|
|
3434
|
+
{
|
|
3435
|
+
navSections: navSections2,
|
|
3436
|
+
navIcons: navIcons2,
|
|
3437
|
+
sectionIcons: sectionIcons2,
|
|
3438
|
+
openWindows,
|
|
3439
|
+
onOpenApp: handleOpenApp,
|
|
3440
|
+
onActivateWindow: handleActivateWindow
|
|
3441
|
+
}
|
|
3442
|
+
)
|
|
3443
|
+
}
|
|
3444
|
+
),
|
|
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(
|
|
3446
|
+
MobileSwitcher,
|
|
3447
|
+
{
|
|
3448
|
+
windows: switcherWindows,
|
|
3449
|
+
onActivate: handleActivateWindow,
|
|
3450
|
+
onClose: (id) => closeEntity(id)
|
|
3451
|
+
}
|
|
3452
|
+
) }),
|
|
3453
|
+
sheet === "notifications" && notifications && /* @__PURE__ */ jsx(MobileNotificationSheet, { config: notifications, onClose: closeSheet }),
|
|
3454
|
+
sheet === "profile" && /* @__PURE__ */ jsx(
|
|
3455
|
+
MobileProfileSheet,
|
|
3456
|
+
{
|
|
3457
|
+
profile,
|
|
3458
|
+
user,
|
|
3459
|
+
onClose: closeSheet,
|
|
3460
|
+
onNavigate: (path) => {
|
|
3461
|
+
onNavigate?.(path);
|
|
3462
|
+
},
|
|
3463
|
+
onLogout: () => onLogout?.()
|
|
3464
|
+
}
|
|
3465
|
+
),
|
|
3466
|
+
/* @__PURE__ */ jsx(
|
|
3467
|
+
MobileBottomNav,
|
|
3468
|
+
{
|
|
3469
|
+
mode,
|
|
3470
|
+
openCount: switcherWindows.length,
|
|
3471
|
+
unreadCount,
|
|
3472
|
+
showNotifications: !!notifications,
|
|
3473
|
+
profileAvatar: profile?.avatar_url,
|
|
3474
|
+
profileInitial: (profile?.first_name?.charAt(0) || user?.email?.charAt(0) || "?").toUpperCase(),
|
|
3475
|
+
onHome: () => {
|
|
3476
|
+
closeSheet();
|
|
3477
|
+
setMobileMode("home");
|
|
3478
|
+
},
|
|
3479
|
+
onSwitcher: () => {
|
|
3480
|
+
closeSheet();
|
|
3481
|
+
setMobileMode("switcher");
|
|
3482
|
+
},
|
|
3483
|
+
onNotifications: () => setSheet(sheet === "notifications" ? null : "notifications"),
|
|
3484
|
+
onProfile: () => setSheet(sheet === "profile" ? null : "profile")
|
|
3485
|
+
}
|
|
3486
|
+
)
|
|
3487
|
+
] });
|
|
3488
|
+
}
|
|
3489
|
+
function MobileBottomNav({
|
|
3490
|
+
mode,
|
|
3491
|
+
openCount,
|
|
3492
|
+
unreadCount,
|
|
3493
|
+
showNotifications,
|
|
3494
|
+
profileAvatar,
|
|
3495
|
+
profileInitial,
|
|
3496
|
+
onHome,
|
|
3497
|
+
onSwitcher,
|
|
3498
|
+
onNotifications,
|
|
3499
|
+
onProfile
|
|
3500
|
+
}) {
|
|
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"}`;
|
|
3502
|
+
return /* @__PURE__ */ jsxs(
|
|
3503
|
+
"nav",
|
|
3504
|
+
{
|
|
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
|
+
},
|
|
3514
|
+
children: [
|
|
3515
|
+
/* @__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" })
|
|
3518
|
+
] }),
|
|
3519
|
+
/* @__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 })
|
|
3528
|
+
] }),
|
|
3529
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium", children: "Apps" })
|
|
3530
|
+
] }),
|
|
3531
|
+
showNotifications && /* @__PURE__ */ jsxs("button", { onClick: onNotifications, className: btnClass(false), "aria-label": "Notifications", children: [
|
|
3532
|
+
/* @__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 })
|
|
3535
|
+
] }),
|
|
3536
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium", children: "Alerts" })
|
|
3537
|
+
] }),
|
|
3538
|
+
/* @__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" })
|
|
3541
|
+
] })
|
|
3542
|
+
]
|
|
3543
|
+
}
|
|
3544
|
+
);
|
|
3545
|
+
}
|
|
2676
3546
|
function useFavorites(wallpapers) {
|
|
2677
3547
|
const { prefs, save } = useShellPrefs();
|
|
2678
3548
|
const favorites = prefs.favorite_pages || [];
|
|
@@ -2962,6 +3832,7 @@ function Layout({
|
|
|
2962
3832
|
const { openPage, openEntity, openWindows } = useWindowManager();
|
|
2963
3833
|
const [menuOpen, setMenuOpen] = useState(false);
|
|
2964
3834
|
useEmailUnreadCount();
|
|
3835
|
+
const isMobile = useIsMobile();
|
|
2965
3836
|
const profile = user || {};
|
|
2966
3837
|
useTheme();
|
|
2967
3838
|
const { desktopBg} = useFavorites(wallpapers);
|
|
@@ -3006,6 +3877,9 @@ function Layout({
|
|
|
3006
3877
|
root.style.setProperty("--window-tab-width", sv.tabW);
|
|
3007
3878
|
root.style.setProperty("--window-tab-font-size", sv.tabFont);
|
|
3008
3879
|
}, [inactiveHeaderOpacity, inactiveContentOpacity, activeHeaderOpacity, activeContentOpacity, taskbarH, taskbarPosition, prefs.default_window_size, prefs.window_position, prefs.menu_density, prefs.start_menu_size]);
|
|
3880
|
+
useEffect(() => {
|
|
3881
|
+
document.documentElement.style.setProperty("--mobile-bottom-nav", isMobile ? "70px" : "0px");
|
|
3882
|
+
}, [isMobile]);
|
|
3009
3883
|
const [balloonDismissed, setBalloonDismissed] = useState(false);
|
|
3010
3884
|
const [isFullscreen, setIsFullscreen] = useState(!!document.fullscreenElement);
|
|
3011
3885
|
const [taskbarMenu, setTaskbarMenu] = useState(null);
|
|
@@ -3136,146 +4010,169 @@ function Layout({
|
|
|
3136
4010
|
categories
|
|
3137
4011
|
}
|
|
3138
4012
|
),
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
4013
|
+
(() => {
|
|
4014
|
+
const wallpaperStyle = {
|
|
4015
|
+
backgroundColor: desktopBg?.startsWith("#") ? desktopBg : desktopBg === "none" ? (() => {
|
|
4016
|
+
const customBg = getComputedStyle(document.documentElement).getPropertyValue("--custom-bg-color")?.trim();
|
|
4017
|
+
if (customBg) return customBg;
|
|
4018
|
+
const t = document.documentElement.getAttribute("data-theme") || "light";
|
|
4019
|
+
const map = { light: "#f3f4f6", dark: "#1e1e2e", pink: "#fdf2f8", green: "#f0fdf4", grey: "#d1d5db", blue: "#eff6ff" };
|
|
4020
|
+
return map[t] || "#f3f4f6";
|
|
4021
|
+
})() : "#1a1a2e",
|
|
4022
|
+
backgroundImage: desktopBg && desktopBg !== "none" && !desktopBg.startsWith("#") ? `url(${desktopBg})` : "none",
|
|
4023
|
+
backgroundSize: "cover",
|
|
4024
|
+
backgroundPosition: "center",
|
|
4025
|
+
backgroundRepeat: "no-repeat"
|
|
4026
|
+
};
|
|
4027
|
+
if (isMobile) {
|
|
4028
|
+
return /* @__PURE__ */ jsx(
|
|
4029
|
+
MobileShell,
|
|
4030
|
+
{
|
|
4031
|
+
productName,
|
|
4032
|
+
productIcon,
|
|
4033
|
+
navSections: navSections2,
|
|
4034
|
+
navIcons: navIcons2,
|
|
4035
|
+
sectionIcons: sectionIcons2,
|
|
4036
|
+
wallpaperStyle,
|
|
4037
|
+
notifications,
|
|
4038
|
+
profile,
|
|
4039
|
+
user,
|
|
4040
|
+
onNavigate: (path) => openPage(path),
|
|
4041
|
+
onLogout: () => setShowLogout(true)
|
|
3147
4042
|
}
|
|
3148
|
-
|
|
3149
|
-
style: {
|
|
3150
|
-
backgroundColor: desktopBg?.startsWith("#") ? desktopBg : desktopBg === "none" ? (() => {
|
|
3151
|
-
const customBg = getComputedStyle(document.documentElement).getPropertyValue("--custom-bg-color")?.trim();
|
|
3152
|
-
if (customBg) return customBg;
|
|
3153
|
-
const t = document.documentElement.getAttribute("data-theme") || "light";
|
|
3154
|
-
const map = { light: "#f3f4f6", dark: "#1e1e2e", pink: "#fdf2f8", green: "#f0fdf4", grey: "#d1d5db", blue: "#eff6ff" };
|
|
3155
|
-
return map[t] || "#f3f4f6";
|
|
3156
|
-
})() : "#1a1a2e",
|
|
3157
|
-
backgroundImage: desktopBg && desktopBg !== "none" && !desktopBg.startsWith("#") ? `url(${desktopBg})` : "none",
|
|
3158
|
-
backgroundSize: "cover",
|
|
3159
|
-
backgroundPosition: "center",
|
|
3160
|
-
backgroundRepeat: "no-repeat"
|
|
3161
|
-
},
|
|
3162
|
-
children: /* @__PURE__ */ jsx(Desktop, { profile })
|
|
4043
|
+
);
|
|
3163
4044
|
}
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
4045
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4046
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-1 min-h-0", children: /* @__PURE__ */ jsx(
|
|
4047
|
+
"main",
|
|
4048
|
+
{
|
|
4049
|
+
className: "flex-1 flex flex-col overflow-hidden cursor-default",
|
|
4050
|
+
onDoubleClick: () => {
|
|
4051
|
+
if (desktopDblClick === "deactivate") {
|
|
4052
|
+
setMenuOpen(false);
|
|
4053
|
+
window.dispatchEvent(new CustomEvent("deactivate-all-modals"));
|
|
4054
|
+
}
|
|
4055
|
+
},
|
|
4056
|
+
style: wallpaperStyle,
|
|
4057
|
+
children: /* @__PURE__ */ jsx(Desktop, { profile })
|
|
4058
|
+
}
|
|
4059
|
+
) }),
|
|
4060
|
+
/* @__PURE__ */ jsxs(
|
|
4061
|
+
"div",
|
|
4062
|
+
{
|
|
4063
|
+
className: `flex backdrop-blur-sm border-gray-200 z-[250] fixed ${isVerticalTaskbar ? `flex-col items-center ${taskbarWClass} py-3 gap-2 top-0 bottom-0 ${taskbarPosition === "left" ? "left-0 border-r" : "right-0 border-l"}` : `items-center ${taskbarHClass} px-3 gap-2 left-0 right-0 ${taskbarPosition === "top" ? "top-0 border-b" : "bottom-0 border-t"}`}`,
|
|
4064
|
+
style: { backgroundColor: `rgb(var(--taskbar-bg-rgb, 243 244 246) / ${taskbarOpacity})` },
|
|
4065
|
+
onContextMenu: (e) => {
|
|
4066
|
+
e.preventDefault();
|
|
4067
|
+
setTaskbarMenu({ x: e.clientX, y: e.clientY });
|
|
4068
|
+
},
|
|
4069
|
+
children: [
|
|
4070
|
+
/* @__PURE__ */ jsxs("div", { className: "relative shrink-0", children: [
|
|
4071
|
+
openWindows.length === 0 && !menuOpen && !balloonDismissed && /* @__PURE__ */ jsxs(
|
|
4072
|
+
"div",
|
|
4073
|
+
{
|
|
4074
|
+
className: `absolute left-1/2 -translate-x-1/2 whitespace-nowrap text-white text-[10px] font-medium pl-3 pr-2 py-1 rounded-full shadow-lg animate-bounce flex items-center gap-1 ${taskbarPosition === "top" ? "top-full mt-2" : "-top-8"}`,
|
|
4075
|
+
style: { backgroundColor: "var(--accent-600, #7c3aed)" },
|
|
4076
|
+
children: [
|
|
4077
|
+
"Click here to start",
|
|
4078
|
+
/* @__PURE__ */ jsx("button", { onClick: (e) => {
|
|
4079
|
+
e.stopPropagation();
|
|
4080
|
+
setBalloonDismissed(true);
|
|
4081
|
+
}, className: "text-white/60 hover:text-white ml-1.5", children: /* @__PURE__ */ jsx("svg", { className: "h-3 w-3", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" }) }) }),
|
|
4082
|
+
/* @__PURE__ */ jsx(
|
|
4083
|
+
"div",
|
|
4084
|
+
{
|
|
4085
|
+
className: `absolute left-1/2 -translate-x-1/2 border-[6px] border-transparent ${taskbarPosition === "top" ? "bottom-full border-b-[var(--accent-color,#7c3aed)]" : "top-full border-t-[var(--accent-color,#7c3aed)]"}`,
|
|
4086
|
+
style: taskbarPosition === "top" ? { borderBottomColor: "var(--accent-600, #7c3aed)" } : { borderTopColor: "var(--accent-600, #7c3aed)" }
|
|
4087
|
+
}
|
|
4088
|
+
)
|
|
4089
|
+
]
|
|
4090
|
+
}
|
|
4091
|
+
),
|
|
4092
|
+
/* @__PURE__ */ jsxs(
|
|
4093
|
+
"button",
|
|
4094
|
+
{
|
|
4095
|
+
"data-menu-toggle": true,
|
|
4096
|
+
onClick: () => setMenuOpen((prev) => !prev),
|
|
4097
|
+
title: menuOpen ? "Close menu" : "Open menu",
|
|
4098
|
+
className: `group/erp relative flex items-center gap-1.5 rounded-lg px-4 py-2 text-xs font-medium border overflow-hidden transition-all ${isVerticalTaskbar ? "w-full" : "min-w-[140px]"} ${menuOpen ? "bg-gray-200/40 border-gray-300/40 text-gray-800" : "bg-gray-50/40 border-gray-200/40 text-gray-600 hover:text-gray-800"}`,
|
|
4099
|
+
style: { transition: "box-shadow 0.3s, border-color 0.3s" },
|
|
4100
|
+
onMouseMove: (e) => {
|
|
4101
|
+
const r = e.currentTarget.getBoundingClientRect();
|
|
4102
|
+
e.currentTarget.style.setProperty("--mx", `${e.clientX - r.left}px`);
|
|
4103
|
+
e.currentTarget.style.setProperty("--my", `${e.clientY - r.top}px`);
|
|
4104
|
+
},
|
|
4105
|
+
onMouseEnter: (e) => {
|
|
4106
|
+
e.currentTarget.style.boxShadow = "0 0 15px rgba(255,255,255,0.2), 0 0 30px rgba(255,255,255,0.1)";
|
|
4107
|
+
e.currentTarget.style.borderColor = "rgba(255,255,255,0.4)";
|
|
4108
|
+
},
|
|
4109
|
+
onMouseLeave: (e) => {
|
|
4110
|
+
e.currentTarget.style.boxShadow = "";
|
|
4111
|
+
e.currentTarget.style.borderColor = "";
|
|
4112
|
+
},
|
|
4113
|
+
children: [
|
|
4114
|
+
/* @__PURE__ */ jsx(
|
|
4115
|
+
"span",
|
|
4116
|
+
{
|
|
4117
|
+
className: "absolute inset-0 opacity-0 group-hover/erp:opacity-100 transition-opacity duration-200 pointer-events-none",
|
|
4118
|
+
style: { background: "radial-gradient(circle 60px at var(--mx, 50%) var(--my, 50%), rgba(255,255,255,0.25) 0%, transparent 100%)" }
|
|
4119
|
+
}
|
|
4120
|
+
),
|
|
4121
|
+
productIcon && /* @__PURE__ */ jsx("img", { src: productIcon, alt: "", className: "relative z-10 h-3.5 w-3.5 shrink-0 opacity-60" }),
|
|
4122
|
+
/* @__PURE__ */ jsx("span", { className: "relative z-10 truncate", children: productName })
|
|
4123
|
+
]
|
|
4124
|
+
}
|
|
4125
|
+
)
|
|
4126
|
+
] }),
|
|
4127
|
+
/* @__PURE__ */ jsx("div", { className: isVerticalTaskbar ? "h-px w-6 bg-gray-300 my-1" : "w-px h-6 bg-gray-300 mx-1" }),
|
|
4128
|
+
/* @__PURE__ */ jsx("div", { id: "taskbar-windows", className: `flex-1 flex ${isVerticalTaskbar ? "flex-col items-center gap-1 min-h-0 overflow-y-auto w-full" : "items-center gap-1.5 min-w-0 overflow-x-auto"}` }),
|
|
4129
|
+
/* @__PURE__ */ jsx("div", { className: isVerticalTaskbar ? "h-px w-6 bg-gray-300 my-1" : "w-px h-6 bg-gray-300 mx-1" }),
|
|
4130
|
+
isVerticalTaskbar ? (
|
|
4131
|
+
/* Vertical: clock + bell + google evenly spaced in a row */
|
|
4132
|
+
/* @__PURE__ */ jsx("div", { className: "w-full px-2", children: /* @__PURE__ */ jsxs("div", { className: `flex items-center justify-center gap-2 ${taskbarPosition === "right" ? "flex-row-reverse" : ""}`, children: [
|
|
4133
|
+
/* @__PURE__ */ jsx(TaskbarClock, {}),
|
|
3187
4134
|
/* @__PURE__ */ jsx(
|
|
3188
|
-
"
|
|
4135
|
+
"button",
|
|
3189
4136
|
{
|
|
3190
|
-
|
|
3191
|
-
|
|
4137
|
+
onClick: () => setGoogleConnectOpen(true),
|
|
4138
|
+
title: googleConnected ? "Google Connected" : "Connect Google",
|
|
4139
|
+
className: `shrink-0 rounded-md p-1.5 transition-colors ${googleConnected ? "hover:bg-green-50" : "text-gray-500 hover:text-gray-900 hover:bg-gray-200"}`,
|
|
4140
|
+
children: /* @__PURE__ */ jsxs("svg", { className: "h-3.5 w-3.5", viewBox: "0 0 24 24", children: [
|
|
4141
|
+
/* @__PURE__ */ jsx("path", { d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 01-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z", fill: googleConnected ? "#16a34a" : "#9ca3af" }),
|
|
4142
|
+
/* @__PURE__ */ jsx("path", { d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z", fill: googleConnected ? "#16a34a" : "#9ca3af" }),
|
|
4143
|
+
/* @__PURE__ */ jsx("path", { d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z", fill: googleConnected ? "#16a34a" : "#9ca3af" }),
|
|
4144
|
+
/* @__PURE__ */ jsx("path", { d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z", fill: googleConnected ? "#16a34a" : "#9ca3af" })
|
|
4145
|
+
] })
|
|
3192
4146
|
}
|
|
3193
|
-
)
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
"data-menu-toggle": true,
|
|
3201
|
-
onClick: () => setMenuOpen((prev) => !prev),
|
|
3202
|
-
title: menuOpen ? "Close menu" : "Open menu",
|
|
3203
|
-
className: `group/erp relative flex items-center gap-1.5 rounded-lg px-4 py-2 text-xs font-medium border overflow-hidden transition-all ${isVerticalTaskbar ? "w-full" : "min-w-[140px]"} ${menuOpen ? "bg-gray-200/40 border-gray-300/40 text-gray-800" : "bg-gray-50/40 border-gray-200/40 text-gray-600 hover:text-gray-800"}`,
|
|
3204
|
-
style: { transition: "box-shadow 0.3s, border-color 0.3s" },
|
|
3205
|
-
onMouseMove: (e) => {
|
|
3206
|
-
const r = e.currentTarget.getBoundingClientRect();
|
|
3207
|
-
e.currentTarget.style.setProperty("--mx", `${e.clientX - r.left}px`);
|
|
3208
|
-
e.currentTarget.style.setProperty("--my", `${e.clientY - r.top}px`);
|
|
3209
|
-
},
|
|
3210
|
-
onMouseEnter: (e) => {
|
|
3211
|
-
e.currentTarget.style.boxShadow = "0 0 15px rgba(255,255,255,0.2), 0 0 30px rgba(255,255,255,0.1)";
|
|
3212
|
-
e.currentTarget.style.borderColor = "rgba(255,255,255,0.4)";
|
|
3213
|
-
},
|
|
3214
|
-
onMouseLeave: (e) => {
|
|
3215
|
-
e.currentTarget.style.boxShadow = "";
|
|
3216
|
-
e.currentTarget.style.borderColor = "";
|
|
3217
|
-
},
|
|
3218
|
-
children: [
|
|
4147
|
+
),
|
|
4148
|
+
notifications && /* @__PURE__ */ jsx(NotificationBell, { ...notifications, popDirection: taskbarPosition === "right" ? "left" : "right" })
|
|
4149
|
+
] }) })
|
|
4150
|
+
) : (
|
|
4151
|
+
/* Horizontal: icons then clock */
|
|
4152
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4153
|
+
notifications && /* @__PURE__ */ jsx(NotificationBell, { ...notifications }),
|
|
3219
4154
|
/* @__PURE__ */ jsx(
|
|
3220
|
-
"
|
|
4155
|
+
"button",
|
|
3221
4156
|
{
|
|
3222
|
-
|
|
3223
|
-
|
|
4157
|
+
onClick: () => setGoogleConnectOpen(true),
|
|
4158
|
+
title: googleConnected ? "Google Connected" : "Connect Google",
|
|
4159
|
+
className: `shrink-0 rounded-md p-2 transition-colors ${googleConnected ? "hover:bg-green-50" : "text-gray-500 hover:text-gray-900 hover:bg-gray-200"}`,
|
|
4160
|
+
children: /* @__PURE__ */ jsxs("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", children: [
|
|
4161
|
+
/* @__PURE__ */ jsx("path", { d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 01-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z", fill: googleConnected ? "#16a34a" : "#9ca3af" }),
|
|
4162
|
+
/* @__PURE__ */ jsx("path", { d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z", fill: googleConnected ? "#16a34a" : "#9ca3af" }),
|
|
4163
|
+
/* @__PURE__ */ jsx("path", { d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z", fill: googleConnected ? "#16a34a" : "#9ca3af" }),
|
|
4164
|
+
/* @__PURE__ */ jsx("path", { d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z", fill: googleConnected ? "#16a34a" : "#9ca3af" })
|
|
4165
|
+
] })
|
|
3224
4166
|
}
|
|
3225
4167
|
),
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
/* @__PURE__ */ jsx("div", { className: isVerticalTaskbar ? "h-px w-6 bg-gray-300 my-1" : "w-px h-6 bg-gray-300 mx-1" }),
|
|
3235
|
-
isVerticalTaskbar ? (
|
|
3236
|
-
/* Vertical: clock + bell + google evenly spaced in a row */
|
|
3237
|
-
/* @__PURE__ */ jsx("div", { className: "w-full px-2", children: /* @__PURE__ */ jsxs("div", { className: `flex items-center justify-center gap-2 ${taskbarPosition === "right" ? "flex-row-reverse" : ""}`, children: [
|
|
3238
|
-
/* @__PURE__ */ jsx(TaskbarClock, {}),
|
|
3239
|
-
/* @__PURE__ */ jsx(
|
|
3240
|
-
"button",
|
|
3241
|
-
{
|
|
3242
|
-
onClick: () => setGoogleConnectOpen(true),
|
|
3243
|
-
title: googleConnected ? "Google Connected" : "Connect Google",
|
|
3244
|
-
className: `shrink-0 rounded-md p-1.5 transition-colors ${googleConnected ? "hover:bg-green-50" : "text-gray-500 hover:text-gray-900 hover:bg-gray-200"}`,
|
|
3245
|
-
children: /* @__PURE__ */ jsxs("svg", { className: "h-3.5 w-3.5", viewBox: "0 0 24 24", children: [
|
|
3246
|
-
/* @__PURE__ */ jsx("path", { d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 01-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z", fill: googleConnected ? "#16a34a" : "#9ca3af" }),
|
|
3247
|
-
/* @__PURE__ */ jsx("path", { d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z", fill: googleConnected ? "#16a34a" : "#9ca3af" }),
|
|
3248
|
-
/* @__PURE__ */ jsx("path", { d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z", fill: googleConnected ? "#16a34a" : "#9ca3af" }),
|
|
3249
|
-
/* @__PURE__ */ jsx("path", { d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z", fill: googleConnected ? "#16a34a" : "#9ca3af" })
|
|
3250
|
-
] })
|
|
3251
|
-
}
|
|
3252
|
-
),
|
|
3253
|
-
notifications && /* @__PURE__ */ jsx(NotificationBell, { ...notifications, popDirection: taskbarPosition === "right" ? "left" : "right" })
|
|
3254
|
-
] }) })
|
|
3255
|
-
) : (
|
|
3256
|
-
/* Horizontal: icons then clock */
|
|
3257
|
-
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3258
|
-
notifications && /* @__PURE__ */ jsx(NotificationBell, { ...notifications }),
|
|
3259
|
-
/* @__PURE__ */ jsx(
|
|
3260
|
-
"button",
|
|
3261
|
-
{
|
|
3262
|
-
onClick: () => setGoogleConnectOpen(true),
|
|
3263
|
-
title: googleConnected ? "Google Connected" : "Connect Google",
|
|
3264
|
-
className: `shrink-0 rounded-md p-2 transition-colors ${googleConnected ? "hover:bg-green-50" : "text-gray-500 hover:text-gray-900 hover:bg-gray-200"}`,
|
|
3265
|
-
children: /* @__PURE__ */ jsxs("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", children: [
|
|
3266
|
-
/* @__PURE__ */ jsx("path", { d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 01-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z", fill: googleConnected ? "#16a34a" : "#9ca3af" }),
|
|
3267
|
-
/* @__PURE__ */ jsx("path", { d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z", fill: googleConnected ? "#16a34a" : "#9ca3af" }),
|
|
3268
|
-
/* @__PURE__ */ jsx("path", { d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z", fill: googleConnected ? "#16a34a" : "#9ca3af" }),
|
|
3269
|
-
/* @__PURE__ */ jsx("path", { d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z", fill: googleConnected ? "#16a34a" : "#9ca3af" })
|
|
3270
|
-
] })
|
|
3271
|
-
}
|
|
3272
|
-
),
|
|
3273
|
-
/* @__PURE__ */ jsx(TaskbarClock, {})
|
|
3274
|
-
] })
|
|
3275
|
-
)
|
|
3276
|
-
]
|
|
3277
|
-
}
|
|
3278
|
-
),
|
|
4168
|
+
/* @__PURE__ */ jsx(TaskbarClock, {})
|
|
4169
|
+
] })
|
|
4170
|
+
)
|
|
4171
|
+
]
|
|
4172
|
+
}
|
|
4173
|
+
)
|
|
4174
|
+
] });
|
|
4175
|
+
})(),
|
|
3279
4176
|
taskbarMenu && /* @__PURE__ */ jsx(
|
|
3280
4177
|
TaskbarContextMenu,
|
|
3281
4178
|
{
|