react-os-shell 3.11.1 → 3.11.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-OMB2YWT7.js → Browser-FX577ZG2.js} +4 -4
- package/dist/{Browser-OMB2YWT7.js.map → Browser-FX577ZG2.js.map} +1 -1
- package/dist/{Documents-FNUDJADL.js → Documents-JJKFUSJ6.js} +3 -3
- package/dist/{Documents-FNUDJADL.js.map → Documents-JJKFUSJ6.js.map} +1 -1
- package/dist/Files-L5SN24XK.js +12 -0
- package/dist/{Files-YVG267R2.js.map → Files-L5SN24XK.js.map} +1 -1
- package/dist/{Notepad-ZOF2A7Q7.js → Notepad-S2EZKPZQ.js} +3 -3
- package/dist/{Notepad-ZOF2A7Q7.js.map → Notepad-S2EZKPZQ.js.map} +1 -1
- package/dist/Preview-BSQJ3GGL.js +8 -0
- package/dist/{Preview-PNMJG2WL.js.map → Preview-BSQJ3GGL.js.map} +1 -1
- package/dist/{Spreadsheet-BPJIYAXF.js → Spreadsheet-HF47TRAV.js} +4 -4
- package/dist/{Spreadsheet-BPJIYAXF.js.map → Spreadsheet-HF47TRAV.js.map} +1 -1
- package/dist/apps/index.js +12 -12
- package/dist/{chunk-MSP7WNTK.js → chunk-AW2BPHVS.js} +3 -3
- package/dist/{chunk-MSP7WNTK.js.map → chunk-AW2BPHVS.js.map} +1 -1
- package/dist/{chunk-PQYTYQ62.js → chunk-BI5XBNI5.js} +3 -3
- package/dist/{chunk-PQYTYQ62.js.map → chunk-BI5XBNI5.js.map} +1 -1
- package/dist/{chunk-76SEPQRP.js → chunk-CCIP3ADU.js} +4 -4
- package/dist/{chunk-76SEPQRP.js.map → chunk-CCIP3ADU.js.map} +1 -1
- package/dist/{chunk-5HBGCZKG.js → chunk-NZLE6YR6.js} +3 -3
- package/dist/{chunk-5HBGCZKG.js.map → chunk-NZLE6YR6.js.map} +1 -1
- package/dist/{chunk-GOT6PRT2.js → chunk-OKFJBJY6.js} +3 -3
- package/dist/{chunk-GOT6PRT2.js.map → chunk-OKFJBJY6.js.map} +1 -1
- package/dist/{chunk-XORQ5BQL.js → chunk-P3DVY4XJ.js} +4 -4
- package/dist/{chunk-XORQ5BQL.js.map → chunk-P3DVY4XJ.js.map} +1 -1
- package/dist/index.js +107 -56
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/Files-YVG267R2.js +0 -12
- package/dist/Preview-PNMJG2WL.js +0 -8
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { setPdfPreview } from './chunk-
|
|
1
|
+
import { setPdfPreview } from './chunk-BI5XBNI5.js';
|
|
2
2
|
import { toast_default } from './chunk-VENYVK3L.js';
|
|
3
|
-
import { setSpreadsheetPreview } from './chunk-
|
|
3
|
+
import { setSpreadsheetPreview } from './chunk-OKFJBJY6.js';
|
|
4
4
|
import { navIcons } from './chunk-ZF6AYO4G.js';
|
|
5
5
|
import { Fragment, isValidElement, cloneElement } from 'react';
|
|
6
6
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
@@ -258,5 +258,5 @@ function consumeFilesViewRequest() {
|
|
|
258
258
|
}
|
|
259
259
|
|
|
260
260
|
export { Breadcrumbs, DesktopItemMiniIcon, FILES_OPEN_DESKTOP_FOLDER_EVENT, FILES_SHOW_TRASH_EVENT, FileIconTile, FolderGlyph, PREVIEW_OPENED_EVENT, consumeFilesViewRequest, desktopItemTypeLabel, getDesktopFoldersSnapshot, hashGradient, openPreviewFile, peekFilesViewRequest, publishDesktopFolders, requestFilesDesktopFolderView, requestFilesTrashView, subscribeDesktopFolders };
|
|
261
|
-
//# sourceMappingURL=chunk-
|
|
262
|
-
//# sourceMappingURL=chunk-
|
|
261
|
+
//# sourceMappingURL=chunk-P3DVY4XJ.js.map
|
|
262
|
+
//# sourceMappingURL=chunk-P3DVY4XJ.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shell/Breadcrumbs.tsx","../src/utils/openPreviewFile.ts","../src/shell/desktopIcons.tsx"],"names":["jsx","jsxs"],"mappings":"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","file":"chunk-XORQ5BQL.js","sourcesContent":["import { Fragment } from 'react';\nimport type { ReactNode } from 'react';\n\n/**\n * Generic breadcrumb trail. Self-contained and styled with the same Tailwind\n * utilities the shell already ships, so consumers get it for free.\n *\n * Pass an ordered list of `items` from root → current. Every crumb except the\n * last renders as a button when it has an `onClick`; the last crumb is treated\n * as the current location — rendered inert with `aria-current=\"page\"`. When the\n * trail is long, set `maxItems` to collapse the middle into an ellipsis\n * (`first … last-n` ), keeping the first and the tail visible.\n */\nexport interface BreadcrumbItem {\n /** Visible label. */\n label: ReactNode;\n /** Optional leading icon (typically a 3.5×3.5 svg). */\n icon?: ReactNode;\n /** Navigate to this crumb. Omitted on the current (last) crumb. */\n onClick?: () => void;\n}\n\nexport interface BreadcrumbsProps {\n items: BreadcrumbItem[];\n /** Node rendered between crumbs. Defaults to a chevron. */\n separator?: ReactNode;\n /** Collapse the middle to an ellipsis when there are more than this many\n * crumbs. `0` (default) never collapses. */\n maxItems?: number;\n className?: string;\n}\n\nconst DEFAULT_SEPARATOR = (\n <svg className=\"h-3.5 w-3.5 shrink-0 text-gray-300\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden>\n <path\n fillRule=\"evenodd\"\n d=\"M7.21 14.77a.75.75 0 0 1 .02-1.06L11.168 10 7.23 6.29a.75.75 0 1 1 1.04-1.08l4.5 4.25a.75.75 0 0 1 0 1.08l-4.5 4.25a.75.75 0 0 1-1.06-.02Z\"\n clipRule=\"evenodd\"\n />\n </svg>\n);\n\ntype Crumb = { kind: 'item'; item: BreadcrumbItem; isLast: boolean } | { kind: 'ellipsis' };\n\nexport default function Breadcrumbs({ items, separator, maxItems = 0, className }: BreadcrumbsProps) {\n if (items.length === 0) return null;\n const sep = separator ?? DEFAULT_SEPARATOR;\n\n // Build the display list, collapsing the middle if `maxItems` is exceeded.\n const crumbs: Crumb[] = [];\n const lastIndex = items.length - 1;\n if (maxItems > 0 && items.length > maxItems && maxItems >= 2) {\n const tailCount = Math.max(1, maxItems - 1); // keep the first + this many from the end\n crumbs.push({ kind: 'item', item: items[0], isLast: false });\n crumbs.push({ kind: 'ellipsis' });\n for (let i = items.length - tailCount; i <= lastIndex; i++) {\n crumbs.push({ kind: 'item', item: items[i], isLast: i === lastIndex });\n }\n } else {\n items.forEach((item, i) => crumbs.push({ kind: 'item', item, isLast: i === lastIndex }));\n }\n\n return (\n <nav aria-label=\"Breadcrumb\" className={`min-w-0 ${className ?? ''}`}>\n <ol className=\"flex items-center gap-1.5 text-sm\">\n {crumbs.map((crumb, i) => (\n <Fragment key={i}>\n {i > 0 && <li aria-hidden className=\"flex items-center\">{sep}</li>}\n <li className=\"flex min-w-0 items-center\">\n {crumb.kind === 'ellipsis' ? (\n <span className=\"px-0.5 text-gray-400 select-none\" aria-label=\"Hidden crumbs\">…</span>\n ) : crumb.isLast || !crumb.item.onClick ? (\n <span\n aria-current={crumb.isLast ? 'page' : undefined}\n className={`inline-flex min-w-0 items-center gap-1 truncate ${\n crumb.isLast ? 'font-medium text-gray-900' : 'text-gray-500'\n }`}\n >\n {crumb.item.icon}\n <span className=\"truncate\">{crumb.item.label}</span>\n </span>\n ) : (\n <button\n type=\"button\"\n onClick={crumb.item.onClick}\n className=\"inline-flex min-w-0 items-center gap-1 truncate rounded px-1 -mx-1 text-gray-500 transition-colors hover:text-gray-900 hover:underline focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-400\"\n >\n {crumb.item.icon}\n <span className=\"truncate\">{crumb.item.label}</span>\n </button>\n )}\n </li>\n </Fragment>\n ))}\n </ol>\n </nav>\n );\n}\n","/**\n * Fetch a file from the user's file-server and route it into the right\n * preview window. Shared by the Files app and the desktop Documents\n * folder's shortcut click-through.\n *\n * On success, dispatches `react-os-shell:preview-opened` so Desktop can\n * record the file as a shortcut without this util needing access to the\n * prefs adapter.\n */\nimport toast from '../shell/toast';\nimport { setPdfPreview } from '../apps/Preview';\nimport { setSpreadsheetPreview } from '../apps/Spreadsheet';\n\nexport type PreviewFileKind = 'pdf' | 'dxf' | '3d' | 'image' | 'csv';\n\nexport interface OpenPreviewFileOpts {\n /** Server-relative path, e.g. \"/reports/Q1.pdf\". */\n filePath: string;\n /** Display name (also used as the download filename). */\n filename: string;\n /** Which viewer to route into. CSV opens in Spreadsheet; the rest open in Preview. */\n kind: PreviewFileKind;\n /** Optional callback invoked after staging the preview, with the route to\n * open (e.g. '/preview' or '/spreadsheet'). The caller is responsible\n * for actually opening the page since the window manager hook is\n * React-scoped. */\n onStaged?: (route: '/preview' | '/spreadsheet') => void;\n}\n\nexport const PREVIEW_OPENED_EVENT = 'react-os-shell:preview-opened';\n\nexport interface PreviewOpenedDetail {\n filePath: string;\n filename: string;\n kind: PreviewFileKind;\n}\n\nfunction getServer() {\n const override = (typeof window !== 'undefined' && (window as any).__REACT_OS_SHELL_FILE_SERVER__) as string | undefined;\n return (override || 'http://localhost:4000').replace(/\\/$/, '');\n}\n\nexport async function openPreviewFile(opts: OpenPreviewFileOpts): Promise<boolean> {\n const { filePath, filename, kind, onStaged } = opts;\n try {\n const res = await fetch(\n `${getServer()}/api/file?path=${encodeURIComponent(filePath)}`,\n { credentials: 'include' },\n );\n if (!res.ok) {\n toast.error(`Download failed (${res.status})`);\n return false;\n }\n const blob = await res.blob();\n if (kind === 'csv') {\n const text = await blob.text();\n setSpreadsheetPreview({ csv: text, filename });\n onStaged?.('/spreadsheet');\n } else {\n const url = URL.createObjectURL(blob);\n setPdfPreview({ url, filename, kind });\n onStaged?.('/preview');\n }\n if (typeof window !== 'undefined') {\n window.dispatchEvent(new CustomEvent<PreviewOpenedDetail>(PREVIEW_OPENED_EVENT, {\n detail: { filePath, filename, kind },\n }));\n }\n return true;\n } catch (e: any) {\n toast.error(e?.message || 'Open failed');\n return false;\n }\n}\n","/**\n * Shared desktop-icon building blocks.\n *\n * Desktop.tsx renders the desktop surface; Files.tsx renders desktop shortcut\n * folders inside the Files app; MobileHome.tsx renders the mobile home grid.\n * All three import the icon tiles, entity-icon config and the desktop-folder\n * bridge from here so the surfaces never visually diverge — and so Desktop\n * and Files never import each other (that would be an import cycle).\n */\nimport { isValidElement, cloneElement, type ReactElement, type ReactNode } from 'react';\nimport { navIcons } from '../shell-config/nav';\nimport type { PreviewFileKind } from '../utils/openPreviewFile';\n\n// ── Gradient tiles ──────────────────────────────────────────────────────────\n// Per-app colored tile background, shared by the mobile home grid and the\n// desktop \"page\" shortcuts. Tailwind's JIT scans the source so each gradient\n// class string must appear in full somewhere — keep them inline.\nexport const ICON_GRADIENTS = [\n 'from-blue-500 to-blue-700',\n 'from-indigo-500 to-purple-600',\n 'from-purple-500 to-pink-600',\n 'from-pink-500 to-rose-600',\n 'from-red-500 to-rose-600',\n 'from-orange-500 to-red-600',\n 'from-amber-500 to-orange-600',\n 'from-yellow-500 to-amber-500',\n 'from-lime-500 to-green-600',\n 'from-green-500 to-emerald-600',\n 'from-emerald-500 to-teal-600',\n 'from-teal-500 to-cyan-600',\n 'from-cyan-500 to-sky-600',\n 'from-sky-500 to-blue-600',\n 'from-violet-500 to-fuchsia-600',\n];\n\nexport function hashGradient(seed: string): string {\n let h = 0;\n for (let i = 0; i < seed.length; i++) h = (h << 5) - h + seed.charCodeAt(i);\n return ICON_GRADIENTS[Math.abs(h) % ICON_GRADIENTS.length];\n}\n\n// ── Entity icon config ──────────────────────────────────────────────────────\nexport const ENTITY_ICON_COLORS: Record<string, string> = {\n order: 'text-blue-600', purchase_order: 'text-purple-600', invoice: 'text-green-600',\n client: 'text-indigo-600', manufacturer: 'text-orange-600', shipment: 'text-teal-600',\n part_number: 'text-gray-600', project: 'text-pink-600', mould: 'text-red-600',\n design: 'text-cyan-600', brand: 'text-amber-600', price_sheet: 'text-emerald-600',\n folder: 'text-yellow-600', page: 'text-blue-500',\n};\nexport const ENTITY_ICONS: Record<string, string> = {\n order: 'SO', purchase_order: 'PO', invoice: 'INV', client: 'CLI',\n manufacturer: 'MFR', shipment: 'DN', part_number: 'PN', project: 'PRJ',\n mould: 'MLD', design: 'DSN', brand: 'BRD', price_sheet: 'PS',\n vendor_invoice: 'VI', vendor_payment: 'VP', warranty_claim: 'WC',\n qc_report: 'QC', vendor_shipment: 'GRN', bank_account: 'BA',\n wheel_finish: 'WF', weight_log: 'WL', production_progress: 'PP',\n vendor_price_sheet: 'VPS', proposal: 'PR', folder: 'FLD',\n};\n\n// Glyphs and colors for the auto-tracked preview shortcuts that live in the\n// Documents folder. Keyed by `fileKind`, not `entityType`.\nexport const PREVIEW_FILE_CODES: Record<PreviewFileKind, string> = {\n pdf: 'PDF', dxf: 'DXF', '3d': 'STP', image: 'IMG', csv: 'CSV',\n};\nexport const PREVIEW_FILE_COLORS: Record<PreviewFileKind, string> = {\n pdf: 'text-red-600', dxf: 'text-blue-600', '3d': 'text-purple-600',\n image: 'text-emerald-600', csv: 'text-green-600',\n};\n\nexport interface DesktopItem {\n entityType: string;\n entityId: string;\n label: string;\n x?: number;\n y?: number;\n folderId?: string; // if inside a folder\n // Legacy free-form position inside the old folder window (pixels, relative\n // to the folder body). Folders now open in the Files app, which lists items\n // in a table — kept so previously-persisted shortcuts keep parsing.\n folderX?: number;\n folderY?: number;\n // Set for `entityType: 'preview-file'` shortcuts auto-recorded when the\n // user previews a file. `filePath` is the server-relative path passed\n // back into `openPreviewFile` on shortcut click.\n filePath?: string;\n fileKind?: PreviewFileKind;\n}\n\n/** Solid amber folder — the exact glyph the Files app uses for folders, so\n * desktop folders and Files-app folders read as the same object. */\nexport function FolderGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} fill=\"currentColor\" viewBox=\"0 0 24 24\">\n <path d=\"M2.25 7.125A2.25 2.25 0 014.5 4.875h4.504c.61 0 1.193.243 1.624.673l1.494 1.494a.75.75 0 00.53.22h7.098A2.25 2.25 0 0122 9.51v8.366A2.25 2.25 0 0119.75 20.125H4.25A2.25 2.25 0 012 17.875V7.125z\" />\n </svg>\n );\n}\n\n/** Page shortcuts reuse the consumer-registered nav icons; historically the\n * label was tried first, then the route. */\nfunction pageGlyph(entityId?: string, label?: string): ReactNode {\n return (label && navIcons[label]) || (entityId ? navIcons[entityId] : undefined);\n}\n\nconst PAGE_FALLBACK_GLYPH = (\n <svg className=\"h-7 w-7\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.5}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M3.75 6A2.25 2.25 0 016 3.75h2.25A2.25 2.25 0 0110.5 6v2.25a2.25 2.25 0 01-2.25 2.25H6a2.25 2.25 0 01-2.25-2.25V6z\" />\n </svg>\n);\n\n// Shared 48×48 tile used by the desktop icon renderer (and anything else\n// that needs a full-size shortcut tile), so all surfaces stay in sync.\nexport function FileIconTile({ entityType, isSelected, entityId, label, fileKind }: {\n entityType: string;\n isSelected: boolean;\n entityId?: string;\n label?: string;\n fileKind?: PreviewFileKind;\n}) {\n const isPreviewFile = entityType === 'preview-file' && fileKind;\n const previewColor = isPreviewFile ? PREVIEW_FILE_COLORS[fileKind!] : null;\n const previewCode = isPreviewFile ? PREVIEW_FILE_CODES[fileKind!] : null;\n if (entityType === 'folder') {\n return (\n <div className={`w-12 h-12 flex items-center justify-center ${isSelected ? 'rounded-lg bg-blue-400/30 ring-2 ring-blue-500' : ''}`}>\n <FolderGlyph className=\"h-12 w-12 text-amber-500 drop-shadow-[0_2px_3px_rgba(0,0,0,0.3)]\" />\n </div>\n );\n }\n if (entityType === 'page') {\n // App shortcut — colored gradient tile with the white nav glyph, same\n // treatment as the mobile home grid (and the same hash seed, the route,\n // so an app keeps its color across surfaces).\n const icon = pageGlyph(entityId, label);\n const gradient = hashGradient(entityId || label || 'page');\n return (\n <div className={`w-12 h-12 flex items-center justify-center ${isSelected ? 'rounded-lg bg-blue-400/30 ring-2 ring-blue-500' : ''}`}>\n <span className={`h-11 w-11 rounded-xl bg-gradient-to-br ${gradient} flex items-center justify-center text-white border border-white/30 drop-shadow-[0_2px_3px_rgba(0,0,0,0.3)]`}>\n {icon && isValidElement(icon)\n ? cloneElement(icon as ReactElement, { className: 'h-7 w-7 text-white' })\n : PAGE_FALLBACK_GLYPH}\n </span>\n </div>\n );\n }\n return (\n <div className={`w-12 h-12 relative flex items-center justify-center ${isSelected ? 'rounded-lg bg-blue-400/30 ring-2 ring-blue-500' : ''}`}>\n <svg className={`w-10 h-12 drop-shadow-[0_2px_3px_rgba(0,0,0,0.3)] ${previewColor ?? ENTITY_ICON_COLORS[entityType] ?? 'text-gray-500'}`} viewBox=\"0 0 40 48\" fill=\"none\">\n <path d=\"M4 0h22l10 10v34a4 4 0 01-4 4H4a4 4 0 01-4-4V4a4 4 0 014-4z\" fill=\"white\" fillOpacity=\"0.92\" />\n <path d=\"M26 0l10 10H30a4 4 0 01-4-4V0z\" fill=\"currentColor\" fillOpacity=\"0.2\" />\n <path d=\"M4 0h22l10 10v34a4 4 0 01-4 4H4a4 4 0 01-4-4V4a4 4 0 014-4z\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeOpacity=\"0.5\" />\n </svg>\n <span className={`absolute inset-0 flex items-center justify-center text-[9px] font-bold pt-2 ${previewColor ?? ENTITY_ICON_COLORS[entityType] ?? 'text-gray-600'}`}>\n {previewCode ?? ENTITY_ICONS[entityType] ?? entityType.slice(0, 3).toUpperCase()}\n </span>\n </div>\n );\n}\n\n/** Compact row icon for desktop shortcuts listed inside the Files app. */\nexport function DesktopItemMiniIcon({ item }: { item: DesktopItem }) {\n if (item.entityType === 'folder') {\n return <FolderGlyph className=\"h-4 w-4 shrink-0 text-amber-500\" />;\n }\n if (item.entityType === 'page') {\n const icon = pageGlyph(item.entityId, item.label);\n return (\n <span className={`h-4 w-4 rounded bg-gradient-to-br ${hashGradient(item.entityId || item.label || 'page')} flex items-center justify-center text-white shrink-0`}>\n {icon && isValidElement(icon)\n ? cloneElement(icon as ReactElement, { className: 'h-3 w-3 text-white' })\n : <span className=\"h-1.5 w-1.5 rounded-[2px] border border-white/80\" />}\n </span>\n );\n }\n const color = (item.entityType === 'preview-file' && item.fileKind\n ? PREVIEW_FILE_COLORS[item.fileKind]\n : ENTITY_ICON_COLORS[item.entityType]) ?? 'text-gray-400';\n return (\n <svg className={`h-4 w-4 shrink-0 ${color}`} fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.5}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z\" />\n </svg>\n );\n}\n\n/** Short type tag shown in the Files-app listing of a desktop folder. */\nexport function desktopItemTypeLabel(item: DesktopItem): string {\n if (item.entityType === 'preview-file' && item.fileKind) return PREVIEW_FILE_CODES[item.fileKind];\n if (item.entityType === 'page') return 'App';\n if (item.entityType === 'folder') return 'Folder';\n return ENTITY_ICONS[item.entityType] ?? item.entityType.slice(0, 3).toUpperCase();\n}\n\n// ── Desktop-folder bridge ───────────────────────────────────────────────────\n//\n// Desktop folders are virtual — shortcut collections persisted in the user's\n// prefs, not directories on the file server. The Files app shows them anyway\n// (sidebar section + folder listing), so Desktop publishes its live folder\n// state here and Files subscribes via useSyncExternalStore. Mutations route\n// back through the callbacks so persistence stays in Desktop's host-aware\n// save path.\n\nexport interface DesktopFoldersSnapshot {\n folders: { id: string; name: string; itemCount: number }[];\n itemsByFolder: Record<string, DesktopItem[]>;\n /** Clear `folderId` on these shortcuts — they reappear on the desktop. */\n moveToDesktop: (items: DesktopItem[]) => void;\n /** Delete these shortcuts entirely. */\n removeShortcuts: (items: DesktopItem[]) => void;\n}\n\nlet desktopFoldersSnapshot: DesktopFoldersSnapshot | null = null;\nconst desktopFoldersListeners = new Set<() => void>();\n\nexport function publishDesktopFolders(next: DesktopFoldersSnapshot | null) {\n desktopFoldersSnapshot = next;\n desktopFoldersListeners.forEach(l => l());\n}\n\nexport function subscribeDesktopFolders(listener: () => void): () => void {\n desktopFoldersListeners.add(listener);\n return () => { desktopFoldersListeners.delete(listener); };\n}\n\nexport function getDesktopFoldersSnapshot(): DesktopFoldersSnapshot | null {\n return desktopFoldersSnapshot;\n}\n\n// ── Files-app view side channel ─────────────────────────────────────────────\n//\n// Used by the desktop Trash icon and desktop folders to open the Files app\n// on a specific view. Two cases handled:\n// 1) Files isn't open yet — the flag below is read once on first mount.\n// 2) Files is already open — the event tells the live instance to flip view.\n// The caller follows up with `openPage('/files')` either way.\n\nexport type FilesViewRequest = 'trash' | { view: 'desktop-folder'; folderId: string };\n\nexport const FILES_VIEW_FLAG = '__REACT_OS_SHELL_FILES_VIEW__';\nexport const FILES_SHOW_TRASH_EVENT = 'react-os-shell:files-show-trash';\nexport const FILES_OPEN_DESKTOP_FOLDER_EVENT = 'react-os-shell:files-open-desktop-folder';\n\nexport function requestFilesTrashView() {\n if (typeof window === 'undefined') return;\n (window as any)[FILES_VIEW_FLAG] = 'trash' satisfies FilesViewRequest;\n window.dispatchEvent(new CustomEvent(FILES_SHOW_TRASH_EVENT));\n}\n\nexport function requestFilesDesktopFolderView(folderId: string) {\n if (typeof window === 'undefined') return;\n (window as any)[FILES_VIEW_FLAG] = { view: 'desktop-folder', folderId } satisfies FilesViewRequest;\n window.dispatchEvent(new CustomEvent(FILES_OPEN_DESKTOP_FOLDER_EVENT, { detail: { folderId } }));\n}\n\n/** Read the pending view request without clearing it. Side-effect free so\n * it is safe inside a `useState` initializer (StrictMode double-invokes\n * those; a read-and-clear there would hand the second invocation null). */\nexport function peekFilesViewRequest(): FilesViewRequest | null {\n if (typeof window === 'undefined') return null;\n const raw = (window as any)[FILES_VIEW_FLAG];\n if (raw === 'trash') return 'trash';\n if (raw && typeof raw === 'object' && raw.view === 'desktop-folder' && typeof raw.folderId === 'string') return raw;\n return null;\n}\n\n/** Read-and-clear the pending view request (Files calls this once mounted). */\nexport function consumeFilesViewRequest(): FilesViewRequest | null {\n const req = peekFilesViewRequest();\n if (typeof window !== 'undefined') (window as any)[FILES_VIEW_FLAG] = null;\n return req;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/shell/Breadcrumbs.tsx","../src/utils/openPreviewFile.ts","../src/shell/desktopIcons.tsx"],"names":["jsx","jsxs"],"mappings":"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","file":"chunk-P3DVY4XJ.js","sourcesContent":["import { Fragment } from 'react';\nimport type { ReactNode } from 'react';\n\n/**\n * Generic breadcrumb trail. Self-contained and styled with the same Tailwind\n * utilities the shell already ships, so consumers get it for free.\n *\n * Pass an ordered list of `items` from root → current. Every crumb except the\n * last renders as a button when it has an `onClick`; the last crumb is treated\n * as the current location — rendered inert with `aria-current=\"page\"`. When the\n * trail is long, set `maxItems` to collapse the middle into an ellipsis\n * (`first … last-n` ), keeping the first and the tail visible.\n */\nexport interface BreadcrumbItem {\n /** Visible label. */\n label: ReactNode;\n /** Optional leading icon (typically a 3.5×3.5 svg). */\n icon?: ReactNode;\n /** Navigate to this crumb. Omitted on the current (last) crumb. */\n onClick?: () => void;\n}\n\nexport interface BreadcrumbsProps {\n items: BreadcrumbItem[];\n /** Node rendered between crumbs. Defaults to a chevron. */\n separator?: ReactNode;\n /** Collapse the middle to an ellipsis when there are more than this many\n * crumbs. `0` (default) never collapses. */\n maxItems?: number;\n className?: string;\n}\n\nconst DEFAULT_SEPARATOR = (\n <svg className=\"h-3.5 w-3.5 shrink-0 text-gray-300\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden>\n <path\n fillRule=\"evenodd\"\n d=\"M7.21 14.77a.75.75 0 0 1 .02-1.06L11.168 10 7.23 6.29a.75.75 0 1 1 1.04-1.08l4.5 4.25a.75.75 0 0 1 0 1.08l-4.5 4.25a.75.75 0 0 1-1.06-.02Z\"\n clipRule=\"evenodd\"\n />\n </svg>\n);\n\ntype Crumb = { kind: 'item'; item: BreadcrumbItem; isLast: boolean } | { kind: 'ellipsis' };\n\nexport default function Breadcrumbs({ items, separator, maxItems = 0, className }: BreadcrumbsProps) {\n if (items.length === 0) return null;\n const sep = separator ?? DEFAULT_SEPARATOR;\n\n // Build the display list, collapsing the middle if `maxItems` is exceeded.\n const crumbs: Crumb[] = [];\n const lastIndex = items.length - 1;\n if (maxItems > 0 && items.length > maxItems && maxItems >= 2) {\n const tailCount = Math.max(1, maxItems - 1); // keep the first + this many from the end\n crumbs.push({ kind: 'item', item: items[0], isLast: false });\n crumbs.push({ kind: 'ellipsis' });\n for (let i = items.length - tailCount; i <= lastIndex; i++) {\n crumbs.push({ kind: 'item', item: items[i], isLast: i === lastIndex });\n }\n } else {\n items.forEach((item, i) => crumbs.push({ kind: 'item', item, isLast: i === lastIndex }));\n }\n\n return (\n <nav aria-label=\"Breadcrumb\" className={`min-w-0 ${className ?? ''}`}>\n <ol className=\"flex items-center gap-1.5 text-sm\">\n {crumbs.map((crumb, i) => (\n <Fragment key={i}>\n {i > 0 && <li aria-hidden className=\"flex items-center\">{sep}</li>}\n <li className=\"flex min-w-0 items-center\">\n {crumb.kind === 'ellipsis' ? (\n <span className=\"px-0.5 text-gray-400 select-none\" aria-label=\"Hidden crumbs\">…</span>\n ) : crumb.isLast || !crumb.item.onClick ? (\n <span\n aria-current={crumb.isLast ? 'page' : undefined}\n className={`inline-flex min-w-0 items-center gap-1 truncate ${\n crumb.isLast ? 'font-medium text-gray-900' : 'text-gray-500'\n }`}\n >\n {crumb.item.icon}\n <span className=\"truncate\">{crumb.item.label}</span>\n </span>\n ) : (\n <button\n type=\"button\"\n onClick={crumb.item.onClick}\n className=\"inline-flex min-w-0 items-center gap-1 truncate rounded px-1 -mx-1 text-gray-500 transition-colors hover:text-gray-900 hover:underline focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-400\"\n >\n {crumb.item.icon}\n <span className=\"truncate\">{crumb.item.label}</span>\n </button>\n )}\n </li>\n </Fragment>\n ))}\n </ol>\n </nav>\n );\n}\n","/**\n * Fetch a file from the user's file-server and route it into the right\n * preview window. Shared by the Files app and the desktop Documents\n * folder's shortcut click-through.\n *\n * On success, dispatches `react-os-shell:preview-opened` so Desktop can\n * record the file as a shortcut without this util needing access to the\n * prefs adapter.\n */\nimport toast from '../shell/toast';\nimport { setPdfPreview } from '../apps/Preview';\nimport { setSpreadsheetPreview } from '../apps/Spreadsheet';\n\nexport type PreviewFileKind = 'pdf' | 'dxf' | '3d' | 'image' | 'csv';\n\nexport interface OpenPreviewFileOpts {\n /** Server-relative path, e.g. \"/reports/Q1.pdf\". */\n filePath: string;\n /** Display name (also used as the download filename). */\n filename: string;\n /** Which viewer to route into. CSV opens in Spreadsheet; the rest open in Preview. */\n kind: PreviewFileKind;\n /** Optional callback invoked after staging the preview, with the route to\n * open (e.g. '/preview' or '/spreadsheet'). The caller is responsible\n * for actually opening the page since the window manager hook is\n * React-scoped. */\n onStaged?: (route: '/preview' | '/spreadsheet') => void;\n}\n\nexport const PREVIEW_OPENED_EVENT = 'react-os-shell:preview-opened';\n\nexport interface PreviewOpenedDetail {\n filePath: string;\n filename: string;\n kind: PreviewFileKind;\n}\n\nfunction getServer() {\n const override = (typeof window !== 'undefined' && (window as any).__REACT_OS_SHELL_FILE_SERVER__) as string | undefined;\n return (override || 'http://localhost:4000').replace(/\\/$/, '');\n}\n\nexport async function openPreviewFile(opts: OpenPreviewFileOpts): Promise<boolean> {\n const { filePath, filename, kind, onStaged } = opts;\n try {\n const res = await fetch(\n `${getServer()}/api/file?path=${encodeURIComponent(filePath)}`,\n { credentials: 'include' },\n );\n if (!res.ok) {\n toast.error(`Download failed (${res.status})`);\n return false;\n }\n const blob = await res.blob();\n if (kind === 'csv') {\n const text = await blob.text();\n setSpreadsheetPreview({ csv: text, filename });\n onStaged?.('/spreadsheet');\n } else {\n const url = URL.createObjectURL(blob);\n setPdfPreview({ url, filename, kind });\n onStaged?.('/preview');\n }\n if (typeof window !== 'undefined') {\n window.dispatchEvent(new CustomEvent<PreviewOpenedDetail>(PREVIEW_OPENED_EVENT, {\n detail: { filePath, filename, kind },\n }));\n }\n return true;\n } catch (e: any) {\n toast.error(e?.message || 'Open failed');\n return false;\n }\n}\n","/**\n * Shared desktop-icon building blocks.\n *\n * Desktop.tsx renders the desktop surface; Files.tsx renders desktop shortcut\n * folders inside the Files app; MobileHome.tsx renders the mobile home grid.\n * All three import the icon tiles, entity-icon config and the desktop-folder\n * bridge from here so the surfaces never visually diverge — and so Desktop\n * and Files never import each other (that would be an import cycle).\n */\nimport { isValidElement, cloneElement, type ReactElement, type ReactNode } from 'react';\nimport { navIcons } from '../shell-config/nav';\nimport type { PreviewFileKind } from '../utils/openPreviewFile';\n\n// ── Gradient tiles ──────────────────────────────────────────────────────────\n// Per-app colored tile background, shared by the mobile home grid and the\n// desktop \"page\" shortcuts. Tailwind's JIT scans the source so each gradient\n// class string must appear in full somewhere — keep them inline.\nexport const ICON_GRADIENTS = [\n 'from-blue-500 to-blue-700',\n 'from-indigo-500 to-purple-600',\n 'from-purple-500 to-pink-600',\n 'from-pink-500 to-rose-600',\n 'from-red-500 to-rose-600',\n 'from-orange-500 to-red-600',\n 'from-amber-500 to-orange-600',\n 'from-yellow-500 to-amber-500',\n 'from-lime-500 to-green-600',\n 'from-green-500 to-emerald-600',\n 'from-emerald-500 to-teal-600',\n 'from-teal-500 to-cyan-600',\n 'from-cyan-500 to-sky-600',\n 'from-sky-500 to-blue-600',\n 'from-violet-500 to-fuchsia-600',\n];\n\nexport function hashGradient(seed: string): string {\n let h = 0;\n for (let i = 0; i < seed.length; i++) h = (h << 5) - h + seed.charCodeAt(i);\n return ICON_GRADIENTS[Math.abs(h) % ICON_GRADIENTS.length];\n}\n\n// ── Entity icon config ──────────────────────────────────────────────────────\nexport const ENTITY_ICON_COLORS: Record<string, string> = {\n order: 'text-blue-600', purchase_order: 'text-purple-600', invoice: 'text-green-600',\n client: 'text-indigo-600', manufacturer: 'text-orange-600', shipment: 'text-teal-600',\n part_number: 'text-gray-600', project: 'text-pink-600', mould: 'text-red-600',\n design: 'text-cyan-600', brand: 'text-amber-600', price_sheet: 'text-emerald-600',\n folder: 'text-yellow-600', page: 'text-blue-500',\n};\nexport const ENTITY_ICONS: Record<string, string> = {\n order: 'SO', purchase_order: 'PO', invoice: 'INV', client: 'CLI',\n manufacturer: 'MFR', shipment: 'DN', part_number: 'PN', project: 'PRJ',\n mould: 'MLD', design: 'DSN', brand: 'BRD', price_sheet: 'PS',\n vendor_invoice: 'VI', vendor_payment: 'VP', warranty_claim: 'WC',\n qc_report: 'QC', vendor_shipment: 'GRN', bank_account: 'BA',\n wheel_finish: 'WF', weight_log: 'WL', production_progress: 'PP',\n vendor_price_sheet: 'VPS', proposal: 'PR', folder: 'FLD',\n};\n\n// Glyphs and colors for the auto-tracked preview shortcuts that live in the\n// Documents folder. Keyed by `fileKind`, not `entityType`.\nexport const PREVIEW_FILE_CODES: Record<PreviewFileKind, string> = {\n pdf: 'PDF', dxf: 'DXF', '3d': 'STP', image: 'IMG', csv: 'CSV',\n};\nexport const PREVIEW_FILE_COLORS: Record<PreviewFileKind, string> = {\n pdf: 'text-red-600', dxf: 'text-blue-600', '3d': 'text-purple-600',\n image: 'text-emerald-600', csv: 'text-green-600',\n};\n\nexport interface DesktopItem {\n entityType: string;\n entityId: string;\n label: string;\n x?: number;\n y?: number;\n folderId?: string; // if inside a folder\n // Legacy free-form position inside the old folder window (pixels, relative\n // to the folder body). Folders now open in the Files app, which lists items\n // in a table — kept so previously-persisted shortcuts keep parsing.\n folderX?: number;\n folderY?: number;\n // Set for `entityType: 'preview-file'` shortcuts auto-recorded when the\n // user previews a file. `filePath` is the server-relative path passed\n // back into `openPreviewFile` on shortcut click.\n filePath?: string;\n fileKind?: PreviewFileKind;\n}\n\n/** Solid amber folder — the exact glyph the Files app uses for folders, so\n * desktop folders and Files-app folders read as the same object. */\nexport function FolderGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} fill=\"currentColor\" viewBox=\"0 0 24 24\">\n <path d=\"M2.25 7.125A2.25 2.25 0 014.5 4.875h4.504c.61 0 1.193.243 1.624.673l1.494 1.494a.75.75 0 00.53.22h7.098A2.25 2.25 0 0122 9.51v8.366A2.25 2.25 0 0119.75 20.125H4.25A2.25 2.25 0 012 17.875V7.125z\" />\n </svg>\n );\n}\n\n/** Page shortcuts reuse the consumer-registered nav icons; historically the\n * label was tried first, then the route. */\nfunction pageGlyph(entityId?: string, label?: string): ReactNode {\n return (label && navIcons[label]) || (entityId ? navIcons[entityId] : undefined);\n}\n\nconst PAGE_FALLBACK_GLYPH = (\n <svg className=\"h-7 w-7\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.5}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M3.75 6A2.25 2.25 0 016 3.75h2.25A2.25 2.25 0 0110.5 6v2.25a2.25 2.25 0 01-2.25 2.25H6a2.25 2.25 0 01-2.25-2.25V6z\" />\n </svg>\n);\n\n// Shared 48×48 tile used by the desktop icon renderer (and anything else\n// that needs a full-size shortcut tile), so all surfaces stay in sync.\nexport function FileIconTile({ entityType, isSelected, entityId, label, fileKind }: {\n entityType: string;\n isSelected: boolean;\n entityId?: string;\n label?: string;\n fileKind?: PreviewFileKind;\n}) {\n const isPreviewFile = entityType === 'preview-file' && fileKind;\n const previewColor = isPreviewFile ? PREVIEW_FILE_COLORS[fileKind!] : null;\n const previewCode = isPreviewFile ? PREVIEW_FILE_CODES[fileKind!] : null;\n if (entityType === 'folder') {\n return (\n <div className={`w-12 h-12 flex items-center justify-center ${isSelected ? 'rounded-lg bg-blue-400/30 ring-2 ring-blue-500' : ''}`}>\n <FolderGlyph className=\"h-12 w-12 text-amber-500 drop-shadow-[0_2px_3px_rgba(0,0,0,0.3)]\" />\n </div>\n );\n }\n if (entityType === 'page') {\n // App shortcut — colored gradient tile with the white nav glyph, same\n // treatment as the mobile home grid (and the same hash seed, the route,\n // so an app keeps its color across surfaces).\n const icon = pageGlyph(entityId, label);\n const gradient = hashGradient(entityId || label || 'page');\n return (\n <div className={`w-12 h-12 flex items-center justify-center ${isSelected ? 'rounded-lg bg-blue-400/30 ring-2 ring-blue-500' : ''}`}>\n <span className={`h-11 w-11 rounded-xl bg-gradient-to-br ${gradient} flex items-center justify-center text-white border border-white/30 drop-shadow-[0_2px_3px_rgba(0,0,0,0.3)]`}>\n {icon && isValidElement(icon)\n ? cloneElement(icon as ReactElement, { className: 'h-7 w-7 text-white' })\n : PAGE_FALLBACK_GLYPH}\n </span>\n </div>\n );\n }\n return (\n <div className={`w-12 h-12 relative flex items-center justify-center ${isSelected ? 'rounded-lg bg-blue-400/30 ring-2 ring-blue-500' : ''}`}>\n <svg className={`w-10 h-12 drop-shadow-[0_2px_3px_rgba(0,0,0,0.3)] ${previewColor ?? ENTITY_ICON_COLORS[entityType] ?? 'text-gray-500'}`} viewBox=\"0 0 40 48\" fill=\"none\">\n <path d=\"M4 0h22l10 10v34a4 4 0 01-4 4H4a4 4 0 01-4-4V4a4 4 0 014-4z\" fill=\"white\" fillOpacity=\"0.92\" />\n <path d=\"M26 0l10 10H30a4 4 0 01-4-4V0z\" fill=\"currentColor\" fillOpacity=\"0.2\" />\n <path d=\"M4 0h22l10 10v34a4 4 0 01-4 4H4a4 4 0 01-4-4V4a4 4 0 014-4z\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeOpacity=\"0.5\" />\n </svg>\n <span className={`absolute inset-0 flex items-center justify-center text-[9px] font-bold pt-2 ${previewColor ?? ENTITY_ICON_COLORS[entityType] ?? 'text-gray-600'}`}>\n {previewCode ?? ENTITY_ICONS[entityType] ?? entityType.slice(0, 3).toUpperCase()}\n </span>\n </div>\n );\n}\n\n/** Compact row icon for desktop shortcuts listed inside the Files app. */\nexport function DesktopItemMiniIcon({ item }: { item: DesktopItem }) {\n if (item.entityType === 'folder') {\n return <FolderGlyph className=\"h-4 w-4 shrink-0 text-amber-500\" />;\n }\n if (item.entityType === 'page') {\n const icon = pageGlyph(item.entityId, item.label);\n return (\n <span className={`h-4 w-4 rounded bg-gradient-to-br ${hashGradient(item.entityId || item.label || 'page')} flex items-center justify-center text-white shrink-0`}>\n {icon && isValidElement(icon)\n ? cloneElement(icon as ReactElement, { className: 'h-3 w-3 text-white' })\n : <span className=\"h-1.5 w-1.5 rounded-[2px] border border-white/80\" />}\n </span>\n );\n }\n const color = (item.entityType === 'preview-file' && item.fileKind\n ? PREVIEW_FILE_COLORS[item.fileKind]\n : ENTITY_ICON_COLORS[item.entityType]) ?? 'text-gray-400';\n return (\n <svg className={`h-4 w-4 shrink-0 ${color}`} fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.5}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z\" />\n </svg>\n );\n}\n\n/** Short type tag shown in the Files-app listing of a desktop folder. */\nexport function desktopItemTypeLabel(item: DesktopItem): string {\n if (item.entityType === 'preview-file' && item.fileKind) return PREVIEW_FILE_CODES[item.fileKind];\n if (item.entityType === 'page') return 'App';\n if (item.entityType === 'folder') return 'Folder';\n return ENTITY_ICONS[item.entityType] ?? item.entityType.slice(0, 3).toUpperCase();\n}\n\n// ── Desktop-folder bridge ───────────────────────────────────────────────────\n//\n// Desktop folders are virtual — shortcut collections persisted in the user's\n// prefs, not directories on the file server. The Files app shows them anyway\n// (sidebar section + folder listing), so Desktop publishes its live folder\n// state here and Files subscribes via useSyncExternalStore. Mutations route\n// back through the callbacks so persistence stays in Desktop's host-aware\n// save path.\n\nexport interface DesktopFoldersSnapshot {\n folders: { id: string; name: string; itemCount: number }[];\n itemsByFolder: Record<string, DesktopItem[]>;\n /** Clear `folderId` on these shortcuts — they reappear on the desktop. */\n moveToDesktop: (items: DesktopItem[]) => void;\n /** Delete these shortcuts entirely. */\n removeShortcuts: (items: DesktopItem[]) => void;\n}\n\nlet desktopFoldersSnapshot: DesktopFoldersSnapshot | null = null;\nconst desktopFoldersListeners = new Set<() => void>();\n\nexport function publishDesktopFolders(next: DesktopFoldersSnapshot | null) {\n desktopFoldersSnapshot = next;\n desktopFoldersListeners.forEach(l => l());\n}\n\nexport function subscribeDesktopFolders(listener: () => void): () => void {\n desktopFoldersListeners.add(listener);\n return () => { desktopFoldersListeners.delete(listener); };\n}\n\nexport function getDesktopFoldersSnapshot(): DesktopFoldersSnapshot | null {\n return desktopFoldersSnapshot;\n}\n\n// ── Files-app view side channel ─────────────────────────────────────────────\n//\n// Used by the desktop Trash icon and desktop folders to open the Files app\n// on a specific view. Two cases handled:\n// 1) Files isn't open yet — the flag below is read once on first mount.\n// 2) Files is already open — the event tells the live instance to flip view.\n// The caller follows up with `openPage('/files')` either way.\n\nexport type FilesViewRequest = 'trash' | { view: 'desktop-folder'; folderId: string };\n\nexport const FILES_VIEW_FLAG = '__REACT_OS_SHELL_FILES_VIEW__';\nexport const FILES_SHOW_TRASH_EVENT = 'react-os-shell:files-show-trash';\nexport const FILES_OPEN_DESKTOP_FOLDER_EVENT = 'react-os-shell:files-open-desktop-folder';\n\nexport function requestFilesTrashView() {\n if (typeof window === 'undefined') return;\n (window as any)[FILES_VIEW_FLAG] = 'trash' satisfies FilesViewRequest;\n window.dispatchEvent(new CustomEvent(FILES_SHOW_TRASH_EVENT));\n}\n\nexport function requestFilesDesktopFolderView(folderId: string) {\n if (typeof window === 'undefined') return;\n (window as any)[FILES_VIEW_FLAG] = { view: 'desktop-folder', folderId } satisfies FilesViewRequest;\n window.dispatchEvent(new CustomEvent(FILES_OPEN_DESKTOP_FOLDER_EVENT, { detail: { folderId } }));\n}\n\n/** Read the pending view request without clearing it. Side-effect free so\n * it is safe inside a `useState` initializer (StrictMode double-invokes\n * those; a read-and-clear there would hand the second invocation null). */\nexport function peekFilesViewRequest(): FilesViewRequest | null {\n if (typeof window === 'undefined') return null;\n const raw = (window as any)[FILES_VIEW_FLAG];\n if (raw === 'trash') return 'trash';\n if (raw && typeof raw === 'object' && raw.view === 'desktop-folder' && typeof raw.folderId === 'string') return raw;\n return null;\n}\n\n/** Read-and-clear the pending view request (Files calls this once mounted). */\nexport function consumeFilesViewRequest(): FilesViewRequest | null {\n const req = peekFilesViewRequest();\n if (typeof window !== 'undefined') (window as any)[FILES_VIEW_FLAG] = null;\n return req;\n}\n"]}
|
package/dist/index.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { subscribePomo, getPomoSnapshot } from './chunk-VGJ3KCOP.js';
|
|
2
2
|
export { setShellTodoProvider } from './chunk-VGJ3KCOP.js';
|
|
3
|
-
import { PREVIEW_OPENED_EVENT, publishDesktopFolders, requestFilesTrashView, FolderGlyph, openPreviewFile, requestFilesDesktopFolderView, FileIconTile, hashGradient } from './chunk-
|
|
4
|
-
export { Breadcrumbs } from './chunk-
|
|
3
|
+
import { PREVIEW_OPENED_EVENT, publishDesktopFolders, requestFilesTrashView, FolderGlyph, openPreviewFile, requestFilesDesktopFolderView, FileIconTile, hashGradient } from './chunk-P3DVY4XJ.js';
|
|
4
|
+
export { Breadcrumbs } from './chunk-P3DVY4XJ.js';
|
|
5
5
|
import { SidebarLayout } from './chunk-YG45DAUT.js';
|
|
6
6
|
export { SidebarLayout } from './chunk-YG45DAUT.js';
|
|
7
7
|
import { playNotification, playStartup, soundsEnabled, getSoundConfig, SOUND_PACK_KEYS, SOUND_PACKS, SOUND_TYPES, SOUND_TYPE_LABELS, setSoundForType, previewSound, setAllSounds, playLogout } from './chunk-D7PYW2QS.js';
|
|
8
|
-
import { setPdfPreview } from './chunk-
|
|
9
|
-
export { ImageAnnotator_default as ImageAnnotator } from './chunk-
|
|
8
|
+
import { setPdfPreview } from './chunk-BI5XBNI5.js';
|
|
9
|
+
export { ImageAnnotator_default as ImageAnnotator } from './chunk-BI5XBNI5.js';
|
|
10
10
|
import { toast_default } from './chunk-VENYVK3L.js';
|
|
11
11
|
export { toast_default as toast } from './chunk-VENYVK3L.js';
|
|
12
|
-
import { EditableGrid } from './chunk-
|
|
13
|
-
export { EditableGrid } from './chunk-
|
|
12
|
+
import { EditableGrid } from './chunk-OKFJBJY6.js';
|
|
13
|
+
export { EditableGrid } from './chunk-OKFJBJY6.js';
|
|
14
14
|
import { useAuth, useShellAuth } from './chunk-ADJ3CERD.js';
|
|
15
15
|
export { ShellAuthProvider, setShellAuthBridge, useShellAuth } from './chunk-ADJ3CERD.js';
|
|
16
|
-
import { APP_VERSION, VERSION } from './chunk-
|
|
17
|
-
export { VERSION } from './chunk-
|
|
16
|
+
import { APP_VERSION, VERSION } from './chunk-NZLE6YR6.js';
|
|
17
|
+
export { VERSION } from './chunk-NZLE6YR6.js';
|
|
18
18
|
import { useWindowManager, PopupMenu, PopupMenuLabel, PopupMenuDivider, PopupMenuItem, WINDOW_REGISTRY, isPageEntry, Modal, useShellPrefs, useIsMobile, ModalActions, useModalActive, client_default, LoadingSpinner, CancelButton, setWindowPosition, ThumbCard, activateModal } from './chunk-HLWEKEKD.js';
|
|
19
19
|
export { CancelButton, CopyButton, DocFavStar, Modal, ModalActions, PopupMenu, PopupMenuDivider, PopupMenuItem, PopupMenuLabel, ShellPrefsProvider, WindowCrashedFallback, WindowErrorBoundary, WindowManagerProvider, WindowTitle, commitExposeHighlight, exitExposeMode, getActiveWindowRoute, getExposeHighlight, getWindowPosition, isEntityEntry, isPageEntry, registerModalEscapeInterceptor, setExposeHighlight, setShellApiClient, setShellWindowRegistry, setWindowDefaultPosition, setWindowPosition, subscribeExposeHighlight, toggleExposeMode, useLocalStoragePrefs, useModalActive, useShellPrefs, useWidgetSettings, useWindowManager, useWindowMenuItem, useWindowTitle } from './chunk-HLWEKEKD.js';
|
|
20
20
|
export { ConfirmProvider, confirm, confirmDestructive, prompt } from './chunk-UBN4IUDE.js';
|
|
@@ -591,15 +591,6 @@ function SidebarActionButton({
|
|
|
591
591
|
}
|
|
592
592
|
);
|
|
593
593
|
}
|
|
594
|
-
function useClickOutside(ref, onClose) {
|
|
595
|
-
useEffect(() => {
|
|
596
|
-
const handler = (e) => {
|
|
597
|
-
if (ref.current && !ref.current.contains(e.target)) onClose();
|
|
598
|
-
};
|
|
599
|
-
document.addEventListener("mousedown", handler);
|
|
600
|
-
return () => document.removeEventListener("mousedown", handler);
|
|
601
|
-
}, [ref, onClose]);
|
|
602
|
-
}
|
|
603
594
|
|
|
604
595
|
// src/forms/styles.ts
|
|
605
596
|
var INPUT_BASE = "block w-full rounded-md border border-gray-300 bg-white px-3 py-1.5 text-sm text-gray-800 placeholder:text-gray-400 shadow-sm focus:border-blue-400 focus:outline-none focus:ring-2 focus:ring-blue-400/30";
|
|
@@ -608,15 +599,43 @@ var INPUT_DISABLED = "disabled:cursor-not-allowed disabled:opacity-60";
|
|
|
608
599
|
function inputClasses(opts) {
|
|
609
600
|
return [INPUT_BASE, opts?.invalid ? INPUT_INVALID : "", INPUT_DISABLED, opts?.className ?? ""].filter(Boolean).join(" ");
|
|
610
601
|
}
|
|
611
|
-
|
|
612
|
-
|
|
602
|
+
var POPUP_MAX_WIDTH = 448;
|
|
603
|
+
var MENU_MAX_HEIGHT = 240;
|
|
604
|
+
var MENU_GAP = 4;
|
|
605
|
+
var VIEWPORT_MARGIN = 8;
|
|
606
|
+
function useDropdownPosition(triggerRef, open) {
|
|
607
|
+
const [pos, setPos] = useState(null);
|
|
613
608
|
useLayoutEffect(() => {
|
|
614
|
-
if (!open)
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
609
|
+
if (!open) {
|
|
610
|
+
setPos(null);
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
const compute = () => {
|
|
614
|
+
const rect = triggerRef.current?.getBoundingClientRect();
|
|
615
|
+
if (!rect) return;
|
|
616
|
+
const spaceBelow = window.innerHeight - rect.bottom - MENU_GAP - VIEWPORT_MARGIN;
|
|
617
|
+
const spaceAbove = rect.top - MENU_GAP - VIEWPORT_MARGIN;
|
|
618
|
+
const placeAbove = spaceBelow < Math.min(MENU_MAX_HEIGHT, 160) && spaceAbove > spaceBelow;
|
|
619
|
+
const maxHeight = Math.max(96, Math.min(MENU_MAX_HEIGHT, placeAbove ? spaceAbove : spaceBelow));
|
|
620
|
+
const next = { minWidth: rect.width, maxHeight };
|
|
621
|
+
if (window.innerWidth - rect.left < POPUP_MAX_WIDTH) {
|
|
622
|
+
next.right = Math.max(VIEWPORT_MARGIN, window.innerWidth - rect.right);
|
|
623
|
+
} else {
|
|
624
|
+
next.left = Math.max(VIEWPORT_MARGIN, rect.left);
|
|
625
|
+
}
|
|
626
|
+
if (placeAbove) next.bottom = window.innerHeight - rect.top + MENU_GAP;
|
|
627
|
+
else next.top = rect.bottom + MENU_GAP;
|
|
628
|
+
setPos(next);
|
|
629
|
+
};
|
|
630
|
+
compute();
|
|
631
|
+
window.addEventListener("scroll", compute, true);
|
|
632
|
+
window.addEventListener("resize", compute);
|
|
633
|
+
return () => {
|
|
634
|
+
window.removeEventListener("scroll", compute, true);
|
|
635
|
+
window.removeEventListener("resize", compute);
|
|
636
|
+
};
|
|
637
|
+
}, [open, triggerRef]);
|
|
638
|
+
return pos;
|
|
620
639
|
}
|
|
621
640
|
function ClearButton({ onClear, ariaLabel = "Clear selection" }) {
|
|
622
641
|
return /* @__PURE__ */ jsx(
|
|
@@ -655,6 +674,7 @@ function SearchableSelect({
|
|
|
655
674
|
};
|
|
656
675
|
const wrapRef = useRef(null);
|
|
657
676
|
const triggerRef = useRef(null);
|
|
677
|
+
const menuRef = useRef(null);
|
|
658
678
|
const adornRef = useRef(null);
|
|
659
679
|
const [adornPad, setAdornPad] = useState(null);
|
|
660
680
|
useLayoutEffect(() => {
|
|
@@ -665,20 +685,26 @@ function SearchableSelect({
|
|
|
665
685
|
const w = adornRef.current?.offsetWidth ?? 0;
|
|
666
686
|
setAdornPad(w ? w + (value && !disabled ? 32 : 8) + 6 : null);
|
|
667
687
|
});
|
|
668
|
-
const
|
|
669
|
-
const alignRight = useDropdownAlignment(triggerRef, open, POPUP_MAX_WIDTH);
|
|
688
|
+
const menuPos = useDropdownPosition(triggerRef, open);
|
|
670
689
|
const selectedLabel = useMemo(() => {
|
|
671
690
|
const match = options.find((o) => o.value === value);
|
|
672
691
|
if (match) return match.label;
|
|
673
692
|
return allowFreeText ? value : "";
|
|
674
693
|
}, [options, value, allowFreeText]);
|
|
675
|
-
|
|
676
|
-
if (
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
694
|
+
useEffect(() => {
|
|
695
|
+
if (!open) return;
|
|
696
|
+
const handler = (e) => {
|
|
697
|
+
const t = e.target;
|
|
698
|
+
if (wrapRef.current?.contains(t) || menuRef.current?.contains(t)) return;
|
|
699
|
+
if (allowFreeText && search.trim() && search.trim() !== value) {
|
|
700
|
+
onChange(search.trim());
|
|
701
|
+
}
|
|
702
|
+
setOpen(false);
|
|
703
|
+
setSearch("");
|
|
704
|
+
};
|
|
705
|
+
document.addEventListener("mousedown", handler);
|
|
706
|
+
return () => document.removeEventListener("mousedown", handler);
|
|
707
|
+
}, [open, allowFreeText, search, value, onChange]);
|
|
682
708
|
const dedupedOptions = useMemo(() => {
|
|
683
709
|
const seen = /* @__PURE__ */ new Set();
|
|
684
710
|
const out = [];
|
|
@@ -749,29 +775,45 @@ function SearchableSelect({
|
|
|
749
775
|
setSearch("");
|
|
750
776
|
} }),
|
|
751
777
|
rightAdornment && !open && /* @__PURE__ */ jsx("div", { ref: adornRef, className: `absolute top-1/2 -translate-y-1/2 ${value && !disabled ? "right-8" : "right-2"} flex items-center gap-1 flex-nowrap justify-end pointer-events-none`, children: rightAdornment }),
|
|
752
|
-
open &&
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
]
|
|
778
|
+
open && createPortal(
|
|
779
|
+
/* @__PURE__ */ jsx(
|
|
780
|
+
"div",
|
|
781
|
+
{
|
|
782
|
+
ref: menuRef,
|
|
783
|
+
className: "fixed z-[400] rounded-2xl overflow-hidden",
|
|
784
|
+
style: {
|
|
785
|
+
left: menuPos?.left,
|
|
786
|
+
right: menuPos?.right,
|
|
787
|
+
top: menuPos?.top,
|
|
788
|
+
bottom: menuPos?.bottom,
|
|
789
|
+
minWidth: menuPos?.minWidth,
|
|
790
|
+
maxWidth: POPUP_MAX_WIDTH,
|
|
791
|
+
width: "max-content",
|
|
792
|
+
// Hidden for the first paint until the layout effect measures the
|
|
793
|
+
// trigger, so the menu never flashes at (0,0).
|
|
794
|
+
visibility: menuPos ? void 0 : "hidden",
|
|
795
|
+
...glassStyle()
|
|
771
796
|
},
|
|
772
|
-
o
|
|
773
|
-
|
|
774
|
-
|
|
797
|
+
children: /* @__PURE__ */ jsx("div", { className: "overflow-y-auto", style: { maxHeight: menuPos?.maxHeight ?? MENU_MAX_HEIGHT }, children: filtered.length === 0 ? /* @__PURE__ */ jsx("p", { className: "px-3 py-3 text-sm text-gray-400 text-center", children: "No matches" }) : filtered.map((o) => /* @__PURE__ */ jsxs(
|
|
798
|
+
"button",
|
|
799
|
+
{
|
|
800
|
+
type: "button",
|
|
801
|
+
onMouseDown: () => {
|
|
802
|
+
onChange(o.value);
|
|
803
|
+
setOpen(false);
|
|
804
|
+
setSearch("");
|
|
805
|
+
},
|
|
806
|
+
className: `w-full text-left px-3 py-1.5 text-sm hover:bg-gray-50 flex items-center justify-between gap-2 whitespace-nowrap ${value === o.value ? "text-blue-600 font-medium bg-blue-50/50" : "text-gray-700"}`,
|
|
807
|
+
children: [
|
|
808
|
+
/* @__PURE__ */ jsx("span", { children: o.label }),
|
|
809
|
+
o.sublabel && /* @__PURE__ */ jsx("span", { className: "text-xs text-gray-400 shrink-0", children: o.sublabel })
|
|
810
|
+
]
|
|
811
|
+
},
|
|
812
|
+
o.value
|
|
813
|
+
)) })
|
|
814
|
+
}
|
|
815
|
+
),
|
|
816
|
+
document.body
|
|
775
817
|
)
|
|
776
818
|
] });
|
|
777
819
|
}
|
|
@@ -2574,6 +2616,15 @@ function useTheme() {
|
|
|
2574
2616
|
}, [accentColor, customBgColor, customTitleColor, customWindowColor, customButtonColor]);
|
|
2575
2617
|
return { theme: saved, resolved };
|
|
2576
2618
|
}
|
|
2619
|
+
function useClickOutside(ref, onClose) {
|
|
2620
|
+
useEffect(() => {
|
|
2621
|
+
const handler = (e) => {
|
|
2622
|
+
if (ref.current && !ref.current.contains(e.target)) onClose();
|
|
2623
|
+
};
|
|
2624
|
+
document.addEventListener("mousedown", handler);
|
|
2625
|
+
return () => document.removeEventListener("mousedown", handler);
|
|
2626
|
+
}, [ref, onClose]);
|
|
2627
|
+
}
|
|
2577
2628
|
function StartupAnimation({ onComplete, ready = false, productName = "react-os-shell", subtitle }) {
|
|
2578
2629
|
const [phase, setPhase] = useState("logo");
|
|
2579
2630
|
const [minTimePassed, setMinTimePassed] = useState(false);
|