efficient-shell 4.127.0 → 4.132.0
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/README.md +938 -5
- package/dist/{Browser-F5TKMP4K.js → Browser-XPVJ2K7B.js} +13 -10
- package/dist/Browser-XPVJ2K7B.js.map +1 -0
- package/dist/{Calculator-T3XG7S73.js → Calculator-ZXJPXG7F.js} +12 -9
- package/dist/Calculator-ZXJPXG7F.js.map +1 -0
- package/dist/ConfirmDialog-MTC7QMUJ.js +4 -0
- package/dist/{ConfirmDialog-LJXMB45A.js.map → ConfirmDialog-MTC7QMUJ.js.map} +1 -1
- package/dist/{CurrencyConverter-CASQMKLO.js → CurrencyConverter-WSJ2BBSX.js} +12 -9
- package/dist/CurrencyConverter-WSJ2BBSX.js.map +1 -0
- package/dist/{Documents-YX67YTZS.js → Documents-ONGSXUJI.js} +14 -11
- package/dist/Documents-ONGSXUJI.js.map +1 -0
- package/dist/{Files-QB63GO6J.js → Files-GQZMFWXQ.js} +17 -14
- package/dist/Files-GQZMFWXQ.js.map +1 -0
- package/dist/{Notepad-3SUD5XUE.js → Notepad-HM73XAM4.js} +15 -12
- package/dist/Notepad-HM73XAM4.js.map +1 -0
- package/dist/PdfViewer-M2YGZIYX.js +5 -0
- package/dist/{PdfViewer-LSP3YNFC.js.map → PdfViewer-M2YGZIYX.js.map} +1 -1
- package/dist/{PomodoroTimer-4EHD2QC4.js → PomodoroTimer-DG3LI5CX.js} +14 -11
- package/dist/PomodoroTimer-DG3LI5CX.js.map +1 -0
- package/dist/{Preview-4ZHR4SKP.js → Preview-TUSV4UMO.js} +16 -13
- package/dist/Preview-TUSV4UMO.js.map +1 -0
- package/dist/{Sidebar-J76PV4QF.js → Sidebar-BPU7RCZP.js} +5 -4
- package/dist/Sidebar-BPU7RCZP.js.map +1 -0
- package/dist/{Spreadsheet-G3SC7XJ5.js → Spreadsheet-3WTXCHYQ.js} +14 -11
- package/dist/Spreadsheet-3WTXCHYQ.js.map +1 -0
- package/dist/{Stock-TLT4VN2R.js → Stock-YMPOMUAW.js} +12 -9
- package/dist/Stock-YMPOMUAW.js.map +1 -0
- package/dist/{Weather-FRKX3DAK.js → Weather-NZO3CWLH.js} +12 -9
- package/dist/Weather-NZO3CWLH.js.map +1 -0
- package/dist/{WorldClock-GKCITO3M.js → WorldClock-HUC6D3AD.js} +12 -9
- package/dist/WorldClock-HUC6D3AD.js.map +1 -0
- package/dist/apps/index.js +26 -22
- package/dist/apps/index.js.map +1 -1
- package/dist/brand.css +1 -1
- package/dist/chunk-7IN7YZAL.js +547 -0
- package/dist/chunk-7IN7YZAL.js.map +1 -0
- package/dist/{chunk-YNGPTOL6.js → chunk-7MA5XBWD.js} +3 -3
- package/dist/{chunk-YNGPTOL6.js.map → chunk-7MA5XBWD.js.map} +1 -1
- package/dist/{chunk-WK7FV7VV.js → chunk-BJ34QOR3.js} +827 -625
- package/dist/chunk-BJ34QOR3.js.map +1 -0
- package/dist/{chunk-HNH7FOYB.js → chunk-CGXMWHQW.js} +7 -6
- package/dist/chunk-CGXMWHQW.js.map +1 -0
- package/dist/{chunk-XUCIYMN7.js → chunk-CJYO4RVV.js} +4 -4
- package/dist/{chunk-XUCIYMN7.js.map → chunk-CJYO4RVV.js.map} +1 -1
- package/dist/{chunk-N3CG5HG5.js → chunk-CLGNTGC5.js} +12 -11
- package/dist/chunk-CLGNTGC5.js.map +1 -0
- package/dist/chunk-D276EMXS.js +57 -0
- package/dist/chunk-D276EMXS.js.map +1 -0
- package/dist/{chunk-7KIOEKHS.js → chunk-E3TWIN7Z.js} +21 -135
- package/dist/chunk-E3TWIN7Z.js.map +1 -0
- package/dist/{chunk-2VM2FRZJ.js → chunk-EENVXIVU.js} +47 -67
- package/dist/chunk-EENVXIVU.js.map +1 -0
- package/dist/{chunk-YG45DAUT.js → chunk-FONEQRBH.js} +17 -11
- package/dist/chunk-FONEQRBH.js.map +1 -0
- package/dist/chunk-IPXN646P.js +14 -0
- package/dist/chunk-IPXN646P.js.map +1 -0
- package/dist/chunk-IRWSRJ4K.js +48 -0
- package/dist/chunk-IRWSRJ4K.js.map +1 -0
- package/dist/chunk-JMKYYPOF.js +7 -0
- package/dist/{chunk-CJZXR7MG.js.map → chunk-JMKYYPOF.js.map} +1 -1
- package/dist/{chunk-JFQGPY7W.js → chunk-KWEX4ZAF.js} +20 -17
- package/dist/chunk-KWEX4ZAF.js.map +1 -0
- package/dist/{chunk-OKEFO6TI.js → chunk-LQTVTOSY.js} +10 -10
- package/dist/chunk-LQTVTOSY.js.map +1 -0
- package/dist/{chunk-G4KA3SAX.js → chunk-MQPX6ERJ.js} +4 -4
- package/dist/{chunk-G4KA3SAX.js.map → chunk-MQPX6ERJ.js.map} +1 -1
- package/dist/{chunk-FQB24B42.js → chunk-SEWF4O3H.js} +3 -3
- package/dist/{chunk-FQB24B42.js.map → chunk-SEWF4O3H.js.map} +1 -1
- package/dist/{chunk-AXWC3332.js → chunk-TFXJRHJV.js} +3 -3
- package/dist/{chunk-AXWC3332.js.map → chunk-TFXJRHJV.js.map} +1 -1
- package/dist/{chunk-G3HQLQYZ.js → chunk-TQURTLK2.js} +59 -34
- package/dist/chunk-TQURTLK2.js.map +1 -0
- package/dist/{chunk-6XQHH7D2.js → chunk-TRCWINHF.js} +5 -5
- package/dist/{chunk-6XQHH7D2.js.map → chunk-TRCWINHF.js.map} +1 -1
- package/dist/{chunk-NPJCMM6V.js → chunk-VWZIGZFF.js} +4 -4
- package/dist/{chunk-NPJCMM6V.js.map → chunk-VWZIGZFF.js.map} +1 -1
- package/dist/{chunk-HYZ5QMVO.js → chunk-XH3ELPQZ.js} +22 -19
- package/dist/chunk-XH3ELPQZ.js.map +1 -0
- package/dist/{chunk-PPUZJFCI.js → chunk-ZKHD4J73.js} +8 -7
- package/dist/chunk-ZKHD4J73.js.map +1 -0
- package/dist/file-intake/index.js +3 -1
- package/dist/file-intake/index.js.map +1 -1
- package/dist/{index-dQia6NpB.d.ts → index-COwksV6G.d.ts} +699 -199
- package/dist/index.d.ts +2 -2
- package/dist/index.js +37 -32
- package/dist/index.js.map +1 -1
- package/dist/map/index.js +5 -3
- package/dist/map/index.js.map +1 -1
- package/dist/markdown/index.js +4 -3
- package/dist/markdown/index.js.map +1 -1
- package/dist/page-tabs/index.js +12 -8
- package/dist/page-tabs/index.js.map +1 -1
- package/dist/shell.css +17 -17
- package/dist/themes.css +15 -0
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.js +17 -13
- package/dist/ui/index.js.map +1 -1
- package/dist/ui.css +691 -283
- package/package.json +1 -1
- package/dist/Browser-F5TKMP4K.js.map +0 -1
- package/dist/Calculator-T3XG7S73.js.map +0 -1
- package/dist/ConfirmDialog-LJXMB45A.js +0 -3
- package/dist/CurrencyConverter-CASQMKLO.js.map +0 -1
- package/dist/Documents-YX67YTZS.js.map +0 -1
- package/dist/Files-QB63GO6J.js.map +0 -1
- package/dist/Notepad-3SUD5XUE.js.map +0 -1
- package/dist/PdfViewer-LSP3YNFC.js +0 -4
- package/dist/PomodoroTimer-4EHD2QC4.js.map +0 -1
- package/dist/Preview-4ZHR4SKP.js.map +0 -1
- package/dist/Sidebar-J76PV4QF.js.map +0 -1
- package/dist/Spreadsheet-G3SC7XJ5.js.map +0 -1
- package/dist/Stock-TLT4VN2R.js.map +0 -1
- package/dist/Weather-FRKX3DAK.js.map +0 -1
- package/dist/WorldClock-GKCITO3M.js.map +0 -1
- package/dist/chunk-2VM2FRZJ.js.map +0 -1
- package/dist/chunk-3CUSBZWG.js +0 -59
- package/dist/chunk-3CUSBZWG.js.map +0 -1
- package/dist/chunk-7KIOEKHS.js.map +0 -1
- package/dist/chunk-CJZXR7MG.js +0 -7
- package/dist/chunk-G3HQLQYZ.js.map +0 -1
- package/dist/chunk-HNH7FOYB.js.map +0 -1
- package/dist/chunk-HYZ5QMVO.js.map +0 -1
- package/dist/chunk-JFQGPY7W.js.map +0 -1
- package/dist/chunk-N3CG5HG5.js.map +0 -1
- package/dist/chunk-OKEFO6TI.js.map +0 -1
- package/dist/chunk-PPUZJFCI.js.map +0 -1
- package/dist/chunk-WK7FV7VV.js.map +0 -1
- package/dist/chunk-YG45DAUT.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shell/strings.tsx","../src/shell/contextMenuTarget.ts","../src/shell/menuPath.ts","../src/shell/PopupMenu.tsx"],"names":["createContext","useMemo","jsx","PopupMenuItem","useContext"],"mappings":"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","file":"chunk-7KIOEKHS.js","sourcesContent":["/**\n * ShellStrings — the shell's user-facing strings, in one typed catalog.\n *\n * Every string here was hardcoded English at its call site, which made the\n * shell untranslatable no matter what the consuming portal did. The catalog\n * is the fix's whole shape: components read strings through `useShellStrings`\n * (which works with NO provider — the English defaults are the context\n * default), and a consumer that wants another language mounts\n * `ShellStringsProvider` once with a partial override. Overrides merge per\n * SECTION, so translating the window controls does not oblige anyone to\n * translate the help viewer.\n *\n * This is deliberately not an i18n library: no message IDs, no interpolation\n * DSL, no plural rules. The shell's strings are labels and short sentences;\n * a typed object keeps them discoverable and lets TypeScript flag a\n * translation that goes stale when a string is added.\n *\n * Prop-level text (a `emptyText` prop, a `placeholder`) always wins over the\n * catalog — the catalog replaces hardcoded DEFAULTS, never a caller's words.\n *\n * Kit-safe: no window-manager import, so `react-os-shell/ui` consumers get it\n * too.\n */\nimport { createContext, useContext, useMemo, type ReactNode } from 'react';\n\nexport interface ShellStrings {\n /** Window chrome: control tooltips and the title bar's accessible name. */\n window: {\n minimize: string;\n /** Bring a hand-minimized window back — the tab's own control. */\n restore: string;\n maximize: string;\n windowed: string;\n pinOnTop: string;\n unpin: string;\n close: string;\n /** Suffix of the title bar's aria-label: `\"{title} — {windowSuffix}\"`. */\n windowSuffix: string;\n prev: string;\n next: string;\n snapLayouts: string;\n snapLeft: string;\n snapRight: string;\n snapTopLeft: string;\n snapTopRight: string;\n snapBottomLeft: string;\n snapBottomRight: string;\n };\n /** Taskbar and exposé. */\n taskbar: {\n closeWindow: string;\n offscreenHint: string;\n offscreenDot: string;\n expose: string;\n addToDesktop: string;\n removeFromDesktop: string;\n /** Group menu — a taskbar tab standing for more than one window. The\n * count is appended by the caller, e.g. \"Close all (3)\". */\n minimizeAllWindows: string;\n restoreAllWindows: string;\n closeAllWindows: string;\n };\n /** The logout cover. */\n logout: {\n goodbye: string;\n seeYou: string;\n };\n /** About + What's New dialogs. */\n about: {\n aboutPrefix: string;\n thisApp: string;\n openSourceLicenses: string;\n whatsNew: string;\n noChangelog: string;\n };\n /** Data-display defaults (DataTable, lists). */\n table: {\n empty: string;\n loading: string;\n selectAll: string;\n selectRow: string;\n };\n /** Picker defaults (SearchableSelect, TagInput). */\n select: {\n none: string;\n noMatches: string;\n allSelected: string;\n clear: string;\n /** aria-label of a TagInput chip's remove button: `\"{remove} {label}\"`. */\n remove: string;\n };\n /** Notification bell + popup. */\n notifications: {\n title: string;\n markAllRead: string;\n dnd: string;\n dndOn: string;\n caughtUp: string;\n empty: string;\n viewAll: string;\n cardLabel: string;\n dismiss: string;\n };\n /** Form-level messages. */\n form: {\n errorSummaryTitle: string;\n };\n /** HelpCenter chrome. */\n help: {\n title: string;\n searchPlaceholder: string;\n empty: string;\n noResults: string;\n loading: string;\n pickArticle: string;\n noBody: string;\n draft: string;\n newArticle: string;\n edit: string;\n };\n /** The app-update banner (a deploy happened under a long-lived tab). */\n update: {\n available: string;\n refreshNow: string;\n };\n /** The page-tab strip (`react-os-shell/page-tabs`). */\n pageTabs: {\n /** The strip's accessible name. */\n label: string;\n /** A tab's close button, and the menu item: `\"{close} {title}\"` for the button. */\n close: string;\n closeOthers: string;\n closeRight: string;\n closeAll: string;\n /** Marks a tab with unsaved changes (the dot's accessible text). */\n unsaved: string;\n /** Asked before a tab with unsaved changes closes. */\n discardTitle: string;\n discardOne: string;\n /** Several at once — the count is prefixed by the caller: \"3 pages have…\". */\n discardMany: string;\n discardConfirm: string;\n keepEditing: string;\n };\n}\n\nexport const DEFAULT_SHELL_STRINGS: ShellStrings = {\n window: {\n minimize: 'Minimize',\n restore: 'Restore',\n maximize: 'Maximize',\n windowed: 'Windowed',\n pinOnTop: 'Pin on top',\n unpin: 'Unpin from top',\n close: 'Close',\n windowSuffix: 'window',\n prev: 'Prev',\n next: 'Next',\n snapLayouts: 'Snap layouts',\n snapLeft: 'Snap left half',\n snapRight: 'Snap right half',\n snapTopLeft: 'Snap top-left quarter',\n snapTopRight: 'Snap top-right quarter',\n snapBottomLeft: 'Snap bottom-left quarter',\n snapBottomRight: 'Snap bottom-right quarter',\n },\n taskbar: {\n closeWindow: 'Close window',\n offscreenHint: 'This window is off screen — click to bring it back',\n offscreenDot: 'Off screen — click to bring this window back',\n expose: 'Exposé — show all open windows as thumbnails',\n addToDesktop: 'Add to desktop',\n removeFromDesktop: 'Remove from desktop',\n minimizeAllWindows: 'Minimize all',\n restoreAllWindows: 'Restore all',\n closeAllWindows: 'Close all',\n },\n logout: {\n goodbye: 'Goodbye',\n seeYou: 'See you next time',\n },\n about: {\n aboutPrefix: 'About',\n thisApp: 'this app',\n openSourceLicenses: 'Open Source Licenses',\n whatsNew: \"What's New\",\n noChangelog: 'No changelog available.',\n },\n table: {\n empty: 'Nothing to show',\n loading: 'Loading',\n selectAll: 'Select all rows',\n selectRow: 'Select row',\n },\n select: {\n none: '— None —',\n noMatches: 'No matches',\n allSelected: 'All options selected',\n clear: 'Clear selection',\n remove: 'Remove',\n },\n notifications: {\n title: 'Notifications',\n markAllRead: 'Mark all read',\n dnd: 'Do not disturb',\n dndOn: 'Do not disturb is on',\n caughtUp: 'All caught up',\n empty: 'No notifications yet',\n viewAll: 'View all notifications',\n cardLabel: 'Notification',\n dismiss: 'Dismiss notification',\n },\n form: {\n errorSummaryTitle: 'There is a problem',\n },\n help: {\n title: 'Help',\n searchPlaceholder: 'Search help…',\n empty: 'No help articles yet.',\n noResults: 'No articles match your search.',\n loading: 'Loading…',\n pickArticle: 'Pick a help article from the left.',\n noBody: 'This article has no body yet.',\n draft: 'Draft',\n newArticle: '+ New',\n edit: 'Edit',\n },\n update: {\n available: 'A new version is available.',\n refreshNow: 'Refresh now',\n },\n pageTabs: {\n label: 'Open pages',\n close: 'Close',\n closeOthers: 'Close other tabs',\n closeRight: 'Close tabs to the right',\n closeAll: 'Close all tabs',\n unsaved: 'Unsaved changes',\n discardTitle: 'Discard unsaved changes?',\n discardOne: 'This page has changes that have not been saved. Close it anyway?',\n discardMany: 'pages have changes that have not been saved. Close them anyway?',\n discardConfirm: 'Discard and close',\n keepEditing: 'Keep editing',\n },\n};\n\n/** A per-section partial: override any subset of a section's strings. */\nexport type ShellStringsOverride = {\n [S in keyof ShellStrings]?: Partial<ShellStrings[S]>;\n};\n\nconst ShellStringsContext = createContext<ShellStrings>(DEFAULT_SHELL_STRINGS);\n\n/**\n * Mount once, near the top of the app. `value` is a partial — sections and\n * keys you do not translate keep their English defaults, so a catalog never\n * breaks by falling behind a release.\n */\nexport function ShellStringsProvider({ value, children }: { value: ShellStringsOverride; children: ReactNode }) {\n const merged = useMemo<ShellStrings>(() => {\n const out = {} as ShellStrings;\n for (const key of Object.keys(DEFAULT_SHELL_STRINGS) as (keyof ShellStrings)[]) {\n out[key] = { ...DEFAULT_SHELL_STRINGS[key], ...(value[key] ?? {}) } as never;\n }\n return out;\n }, [value]);\n return <ShellStringsContext.Provider value={merged}>{children}</ShellStringsContext.Provider>;\n}\n\n/** The active catalog — English defaults when no provider is mounted. */\nexport function useShellStrings(): ShellStrings {\n return useContext(ShellStringsContext);\n}\n","/**\n * What the shell's global context menu should offer for a given right-click.\n *\n * Kept free of React (and of the DOM beyond the element it is handed) so the\n * two rules that matter can be tested directly: which targets keep the\n * browser's own menu, and which items a click produces.\n */\n\n/** Input types that put a text caret on screen. Everything else — a checkbox,\n * a range, a color swatch, a file button — has no spellcheck and no Paste,\n * so there is nothing in the browser's menu worth keeping for it. */\nconst TEXT_INPUT_TYPES = new Set([\n 'text', 'search', 'url', 'tel', 'email', 'password', 'number',\n 'date', 'datetime-local', 'month', 'week', 'time',\n]);\n\n/** Elements whose browser menu does what a page cannot: Save image as and Copy\n * image — the picture itself, not its address — on an image or a canvas, and\n * the playback controls on video and audio. */\nconst NATIVE_MEDIA = 'img, picture, canvas, video, audio';\n\n/** Opt-out attribute: anything inside `[data-native-context-menu]` keeps the\n * browser's menu. For surfaces the shell has no business re-skinning — an\n * embedded viewer, a third-party widget. */\nexport const NATIVE_MENU_ATTR = 'data-native-context-menu';\n\nfunction closest(el: Element | null, selector: string): Element | null {\n if (!el || typeof (el as Element).closest !== 'function') return null;\n return el.closest(selector);\n}\n\n/**\n * Does this target keep the browser's native menu?\n *\n * Text inputs, textareas and contenteditable do, deliberately: the browser's\n * spellcheck suggestions and \"Add to dictionary\" cannot be rebuilt by a web\n * page, and a Paste item of our own would need a clipboard permission prompt.\n * Images, canvases and media do for the same reason — Save / Copy image and\n * the playback controls are the browser's. The shell menu is for everywhere\n * else.\n */\nexport function keepsNativeMenu(target: EventTarget | null): boolean {\n const el = target as Element | null;\n if (!el || typeof (el as Element).closest !== 'function') return false;\n\n if (closest(el, `[${NATIVE_MENU_ATTR}]`)) return true;\n if (closest(el, 'textarea')) return true;\n if (closest(el, NATIVE_MEDIA)) return true;\n\n const input = closest(el, 'input') as HTMLInputElement | null;\n if (input) {\n // The property, not the attribute: the browser normalizes it, so a type it\n // does not recognise (`type=\"currency\"`, a typo) reads as the text box it\n // actually renders.\n const type = (input.type || 'text').toLowerCase();\n if (TEXT_INPUT_TYPES.has(type)) return true;\n }\n\n // `isContentEditable` is the property to ask, but jsdom does not implement\n // it, so walk the attribute — which is also what the nesting rules are\n // written in (`contenteditable=\"false\"` re-enables our menu inside an\n // editable region).\n const editable = closest(el, '[contenteditable]');\n if (editable) {\n const value = (editable.getAttribute('contenteditable') || '').toLowerCase();\n if (value === '' || value === 'true' || value === 'plaintext-only') return true;\n }\n\n return false;\n}\n\nexport type ShellContextKind = 'selection' | 'link' | 'default';\n\nexport interface ShellContextTarget {\n /** The most specific thing under the pointer — what the menu leads with. */\n kind: ShellContextKind;\n /** Trimmed selection text, when something is selected. */\n selectionText?: string;\n /** Absolute href of the nearest enclosing link. */\n linkUrl?: string;\n}\n\n/** Schemes worth an Open / Copy link address. A `javascript:` URL is a button\n * wearing a link's clothes, and `href=\"#\"` is the same trick. */\nconst LINK_SCHEMES = new Set(['http:', 'https:', 'mailto:', 'tel:', 'blob:']);\n\nfunction linkAddress(href: string | null | undefined, base?: string): string | undefined {\n if (!href || href === '#') return undefined;\n try {\n // `document.baseURI`, not `location.href`: a page with a `<base>` resolves\n // this the way its own links do.\n const url = new URL(href, base ?? (typeof document !== 'undefined' ? document.baseURI : undefined));\n return LINK_SCHEMES.has(url.protocol) ? url.href : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Describe a right-click: what was under the pointer, and what is selected.\n *\n * A selection inside a link reports both, so the menu can offer both sections\n * the way a browser's does; `kind` names whichever the menu should lead with.\n */\nexport function describeContextTarget(\n target: EventTarget | null,\n selectionText?: string | null,\n baseUrl?: string,\n): ShellContextTarget {\n const el = target as Element | null;\n const link = closest(el, 'a[href]');\n\n const text = (selectionText || '').trim();\n const linkUrl = linkAddress(link?.getAttribute('href'), baseUrl);\n\n const kind: ShellContextKind = text ? 'selection' : linkUrl ? 'link' : 'default';\n\n return {\n kind,\n ...(text ? { selectionText: text } : {}),\n ...(linkUrl ? { linkUrl } : {}),\n };\n}\n","/**\n * The open-submenu path of a hover menu — the whole state machine <StartMenu>\n * uses to decide which flyouts are open, at any depth.\n *\n * ## Why this exists\n *\n * The menu used to have TWO mechanisms: one that opened a section's flyout\n * (`hoveredSection` + its timer) and a second, separately written one that\n * opened a flyout under an item inside that flyout (`hoveredChild` + its own\n * timer). They looked alike but behaved differently, and the second one had\n * the bugs — a stale close-timer from a sibling row could fire *after* a\n * submenu had opened and shut it under the cursor, which is what \"the next\n * level is slow, or sometimes doesn't open at all\" actually was. There was\n * also no third mechanism, so a 4th level simply never rendered.\n *\n * One path, one set of rules, any depth. Panels are numbered from the root\n * menu: panel 0 is the menu itself, and `path[i]` is the row that opened panel\n * `i + 1`. So `path.length` is the number of open flyouts, and closing\n * everything under panel `d` is just truncating the path to `d`.\n *\n * All of it is pure: hover geometry comes in as numbers, so the rules can be\n * tested without a DOM.\n */\n\n/** One open flyout: which row opened it, and where it goes. */\nexport interface MenuAnchor {\n /** Identity of the row that opened this panel — a section label or an item\n * route. Also what marks that row as the open one. */\n key: string;\n /** Viewport Y of the opening row's centre; the panel is centred on it. */\n y: number;\n /** Viewport X of the panel's left edge. Resolved when the row is hovered,\n * because it depends on where the panel that owns the row ended up. */\n left: number;\n /** Whether this panel had to open to the LEFT of the one that owns its row.\n * Carried so the next level down can keep going the same way. */\n flipped: boolean;\n}\n\n/**\n * Open `anchor` as the submenu of a row in panel `depth`, closing anything\n * deeper (the previous branch the user was down).\n *\n * Returns the SAME array when that panel is already open for that key and\n * nothing deeper is open, so a repeated `mouseenter` on a row the user is\n * already on can't re-render the menu — and can't reset the panel's open\n * animation.\n */\nexport function openMenuLevel(path: MenuAnchor[], depth: number, anchor: MenuAnchor): MenuAnchor[] {\n if (path.length === depth + 1 && path[depth].key === anchor.key) return path;\n return [...path.slice(0, depth), anchor];\n}\n\n/**\n * Close panel `depth + 1` and everything under it — what hovering a row with\n * no submenu, or leaving a panel, eventually leads to.\n *\n * Also identity-stable: nothing to close means the same array back.\n */\nexport function closeMenuBelow(path: MenuAnchor[], depth: number): MenuAnchor[] {\n const keep = Math.max(0, depth);\n return path.length > keep ? path.slice(0, keep) : path;\n}\n\n/**\n * The items each open panel shows: `[0]` is the flyout the root row opened,\n * `[1]` its child, and so on for as long as the path keeps resolving.\n *\n * Stops early rather than rendering an empty panel, so a path left over from a\n * branch that has since disappeared (a permission change, nav data swapped\n * under the menu) degrades to the levels that still exist.\n */\nexport function resolveMenuLevels<T extends { to: string }>(\n rootItems: T[],\n path: MenuAnchor[],\n childrenOf: (item: T) => T[],\n): T[][] {\n if (path.length === 0 || rootItems.length === 0) return [];\n const levels: T[][] = [rootItems];\n for (let i = 1; i < path.length; i++) {\n const parent = levels[i - 1].find(item => item.to === path[i].key);\n const kids = parent ? childrenOf(parent) : [];\n if (kids.length === 0) break;\n levels.push(kids);\n }\n return levels;\n}\n\n/**\n * Where a panel of height `height` sits when centred on `anchorY`, kept inside\n * the usable vertical span (`minTop`..`maxBottom` — the viewport minus the\n * taskbar edge and a gutter).\n *\n * A panel taller than the span is pinned to the top of it and scrolls; that is\n * why the height is clamped before it is centred, and not after.\n */\nexport function clampMenuTop(anchorY: number, height: number, minTop: number, maxBottom: number): number {\n const span = Math.max(0, maxBottom - minTop);\n const h = Math.min(height, span);\n const top = anchorY - h / 2;\n if (top < minTop) return minTop;\n if (top + h > maxBottom) return Math.max(minTop, maxBottom - h);\n return top;\n}\n\n/**\n * Where a panel of width `width` sits horizontally: to the right of the panel\n * that owns the hovered row, or flipped to its left when there isn't room.\n *\n * The flip is what makes depth open-ended. Every level costs another panel\n * width, so a deep enough branch always runs out of screen — and a menu opened\n * from a right-hand taskbar runs out on the very first one.\n *\n * `preferLeft` keeps a branch that has already turned around going the same\n * way. Without it the levels ping-pong: one flips left over its grandparent,\n * the next finds room on the right again and lands back on top of its\n * grandparent, and the chain folds into two stacks of panels.\n */\nexport function menuPanelLeft(\n ownerLeft: number,\n ownerRight: number,\n width: number,\n viewportW: number,\n gap: number,\n preferLeft = false,\n margin = 8,\n): { left: number; flipped: boolean } {\n const onRight = ownerRight + gap;\n const onLeft = ownerLeft - gap - width;\n const rightFits = onRight + width <= viewportW - margin;\n const leftFits = onLeft >= margin;\n if (preferLeft && leftFits) return { left: onLeft, flipped: true };\n if (rightFits) return { left: onRight, flipped: false };\n if (leftFits) return { left: onLeft, flipped: true };\n // Neither side fits: hug the edge the panel was heading for rather than\n // hang off the screen.\n return preferLeft\n ? { left: margin, flipped: true }\n : { left: Math.max(margin, viewportW - margin - width), flipped: false };\n}\n","import {\n createContext, forwardRef, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState,\n type ReactNode, type CSSProperties,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { glassStyle, GLASS_DIVIDER } from '../utils/glass';\nimport { keepsNativeMenu } from './contextMenuTarget';\nimport { registerModalEscapeInterceptor } from './escapeInterceptors';\nimport { onOverlayOpen } from './overlayEvents';\nimport { Z_LAYERS } from './zLayers';\nimport { clampMenuTop, closeMenuBelow, menuPanelLeft, openMenuLevel, type MenuAnchor } from './menuPath';\n\n/**\n * Unified popup menu component — used for all context menus, dropdowns, and flyouts.\n * Reads --menu-density CSS variable: 'tight', 'normal' (default), or 'large'.\n */\n\nexport interface PopupMenuProps {\n children: ReactNode;\n style?: CSSProperties;\n className?: string;\n onClose?: () => void;\n minWidth?: number;\n /** Render into document.body instead of in place. Required when the menu\n * is opened from INSIDE a window: the window panel is a transformed /\n * backdrop-filtered `overflow-hidden` container, which both re-anchors\n * `position: fixed` descendants to itself and clips them — a menu\n * positioned at viewport coordinates ends up offset or invisible.\n * Portaling restores true viewport positioning. */\n portal?: boolean;\n}\n\nfunction getDensity(): 'tight' | 'normal' | 'large' {\n return (getComputedStyle(document.documentElement).getPropertyValue('--menu-density')?.trim() as any) || 'normal';\n}\n\n/** Grace period before a submenu closes, so the pointer can cut the corner\n * across sibling rows on its way into the panel. The Start menu's value. */\nconst SUBMENU_CLOSE_DELAY = 200;\n/** How long the pointer rests on a submenu row before it opens. Short enough\n * to feel immediate, long enough that sweeping down a menu past the row does\n * not flash its panel open. */\nconst SUBMENU_OPEN_DELAY = 120;\n/** Gap between a submenu and the menu it opened from. */\nconst SUBMENU_GAP = 4;\n\n/**\n * What every submenu under one `PopupMenu` shares.\n *\n * The open submenus are ONE path for the whole tree — the same state machine\n * the Start menu runs (`menuPath.ts`): `path[d]` is the submenu open from a row\n * in panel `d`, and there is one close timer. That is what makes sibling\n * submenus exclusive, and it is what stops a stale timer from a row the\n * pointer merely passed over from closing a submenu that has since opened —\n * the bug the Start menu had while it ran a timer per level.\n */\ninterface MenuTree {\n /** Marks every panel of this tree, so a click inside a portalled submenu is\n * not \"outside\" the menu. */\n id: string;\n /** 0 for rows in the root menu, 1 inside its submenu, and so on. */\n depth: number;\n path: MenuAnchor[];\n open: (depth: number, anchor: MenuAnchor) => void;\n /** Close the submenus under panel `depth`, after the grace period. */\n scheduleClose: (depth: number) => void;\n /** Close the submenus under panel `depth` now. */\n closeBelow: (depth: number) => void;\n cancelClose: () => void;\n /** Close every panel — an item inside a submenu was chosen. */\n closeTree: () => void;\n /** The root panel, for measuring what it is layered at. */\n root: React.RefObject<HTMLDivElement | null>;\n}\n\nconst MenuTreeContext = createContext<MenuTree | null>(null);\n\n/** Container for a popup menu — auto-clamps to stay within viewport */\nexport function PopupMenu({ children, style, className = '', onClose, minWidth = 180, portal = false }: PopupMenuProps) {\n const ref = useRef<HTMLDivElement>(null);\n const treeId = useId();\n const [path, setPath] = useState<MenuAnchor[]>([]);\n const closeTimer = useRef<ReturnType<typeof setTimeout>>();\n useEffect(() => () => clearTimeout(closeTimer.current), []);\n\n const tree = useMemo<MenuTree>(() => {\n const cancelClose = () => clearTimeout(closeTimer.current);\n return {\n id: treeId,\n depth: 0,\n path,\n open: (depth, anchor) => { cancelClose(); setPath(prev => openMenuLevel(prev, depth, anchor)); },\n scheduleClose: depth => {\n cancelClose();\n closeTimer.current = setTimeout(() => setPath(prev => closeMenuBelow(prev, depth)), SUBMENU_CLOSE_DELAY);\n },\n closeBelow: depth => { cancelClose(); setPath(prev => closeMenuBelow(prev, depth)); },\n cancelClose,\n closeTree: () => { cancelClose(); setPath([]); onClose?.(); },\n root: ref,\n };\n }, [treeId, path, onClose]);\n\n useEffect(() => {\n if (!onClose) return;\n const handleClick = (e: PointerEvent | MouseEvent) => {\n if ((e.target as HTMLElement).closest('[data-menu-toggle]')) return;\n // A submenu is portalled to <body>, so it is outside `ref` in the DOM\n // while still being part of this menu.\n if ((e.target as HTMLElement).closest?.(`[data-popup-menu-tree=\"${treeId}\"]`)) return;\n if (ref.current && !ref.current.contains(e.target as Node)) onClose();\n };\n const handleKey = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); };\n window.addEventListener('pointerdown', handleClick);\n window.addEventListener('keydown', handleKey);\n // An overlay opening (⌘K's palette, a Dialog) is an outside interaction\n // the pointer listener never sees; left open, the menu would sit over the\n // overlay's backdrop or behind it (see `overlayEvents.ts`).\n const offOverlay = onOverlayOpen(onClose);\n return () => {\n window.removeEventListener('pointerdown', handleClick);\n window.removeEventListener('keydown', handleKey);\n offOverlay();\n };\n }, [onClose, treeId]);\n\n // After render, clamp position to viewport boundaries\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n requestAnimationFrame(() => {\n const rect = el.getBoundingClientRect();\n const margin = 8;\n // Clamp right edge\n if (rect.right > window.innerWidth - margin) {\n const overflow = rect.right - window.innerWidth + margin;\n if (el.style.left) {\n el.style.left = `${parseFloat(el.style.left) - overflow}px`;\n } else if (!el.style.right) {\n el.style.left = `${rect.left - overflow}px`;\n }\n }\n // Clamp left edge\n if (rect.left < margin) {\n if (el.style.left) {\n el.style.left = `${margin}px`;\n } else if (el.style.right) {\n el.style.right = `${window.innerWidth - rect.width - margin}px`;\n }\n }\n // Clamp bottom edge\n if (rect.bottom > window.innerHeight - margin) {\n const overflow = rect.bottom - window.innerHeight + margin;\n if (el.style.top) {\n el.style.top = `${parseFloat(el.style.top) - overflow}px`;\n } else if (el.style.bottom) {\n // Already anchored to bottom, just clamp\n el.style.bottom = `${margin}px`;\n } else {\n el.style.top = `${rect.top - overflow}px`;\n }\n }\n // Clamp top edge\n if (rect.top < margin) {\n if (el.style.top) {\n el.style.top = `${margin}px`;\n } else if (el.style.bottom) {\n el.style.bottom = `${window.innerHeight - rect.height - margin}px`;\n }\n }\n });\n });\n\n const density = getDensity();\n const ownsZ = /(^|[\\s:])!?z-/.test(className);\n\n // A right-click on an open menu is not a request for another menu. Claiming\n // it stops the shell-wide `ShellContextMenu` (which stands down on a\n // prevented event) and the browser's from opening on top of this one. A text\n // field inside a menu still keeps the browser's, as it does everywhere else.\n const menu = (\n <div ref={ref}\n data-popup-menu-tree={treeId}\n className={`fixed rounded-2xl ${density === 'tight' ? 'py-1' : density === 'large' ? 'py-2' : 'py-1.5'} ${className}`}\n onContextMenu={e => { if (!keepsNativeMenu(e.target)) e.preventDefault(); }}\n // The menu layer is inline now, and an inline z-index beats any class —\n // so a caller that layered its menu with a `z-…` class keeps its own.\n style={{ ...(ownsZ ? {} : { zIndex: Z_LAYERS.menu }), minWidth, animation: 'popup-in 0.12s ease-out', ...glassStyle(), ...style }}>\n <MenuTreeContext.Provider value={tree}>{children}</MenuTreeContext.Provider>\n <style>{`@keyframes popup-in { from { opacity: 0; transform: scale(0.96); } to { opacity: 1; transform: scale(1); } }`}</style>\n </div>\n );\n\n return portal ? createPortal(menu, document.body) : menu;\n}\n\n/** A clickable menu item */\nexport interface PopupMenuItemProps {\n onClick?: () => void;\n children: ReactNode;\n className?: string;\n danger?: boolean;\n disabled?: boolean;\n /**\n * The menu semantics, when this item is part of one.\n *\n * A context menu on the desktop is a list of buttons and needs none of this;\n * a dropdown attached to a trigger is a `menu` whose items are `menuitem`s\n * with one tab stop between them. Both draw the same item, so the styling\n * lives here rather than being copied into whichever one is newer.\n */\n role?: 'menuitem';\n tabIndex?: number;\n id?: string;\n onMouseEnter?: () => void;\n}\n\n/**\n * `forwardRef`, not a `ref` prop.\n *\n * Taking `ref` as an ordinary prop is React 19 only. This package's peer range\n * is `react: \">=18\"`, and React 18 strips `ref` from a function component's\n * props and warns — so on an 18 consumer the ref would silently never attach,\n * `DropdownMenu`'s `itemRefs` would all be null, and its arrow keys would move\n * `tabIndex` while DOM focus stayed put. A keyboard-dead menu with no error.\n */\n/** The row look shared by an item and a submenu's row. */\nfunction itemLook(danger: boolean | undefined, disabled: boolean | undefined, className: string, highlighted = false) {\n const density = getDensity();\n // Vertical gap between items. `normal` sits a little tighter than the raw\n // size padding; `large` adds a bit more room. Floored at the tight value so\n // the reduced `normal` never drops below `tight` at the smallest menu size.\n const itemPadY = density === 'tight' ? '0.25rem'\n : density === 'large' ? '0.6rem'\n : 'max(0.25rem, calc(var(--menu-padding-y, 0.5rem) - 0.1rem))';\n const tone = danger ? 'text-red-600 hover:bg-red-50'\n : highlighted ? 'bg-blue-50 text-blue-700'\n : 'text-gray-700 hover:bg-blue-50 hover:text-blue-700';\n return {\n className: `w-full flex items-center gap-2 text-left transition-colors rounded-lg mx-auto\n ${tone}\n ${disabled ? 'opacity-40 cursor-not-allowed' : ''}\n ${className}`,\n style: {\n width: 'calc(100% - 8px)',\n marginLeft: 4,\n marginRight: 4,\n fontSize: 'var(--menu-font-size, 14px)',\n paddingLeft: 'var(--menu-padding-x, 1rem)',\n paddingRight: 'var(--menu-padding-x, 1rem)',\n paddingTop: itemPadY,\n paddingBottom: itemPadY,\n } satisfies CSSProperties,\n };\n}\n\nexport const PopupMenuItem = forwardRef<HTMLButtonElement, PopupMenuItemProps>(function PopupMenuItem({\n onClick, children, className = '', danger, disabled,\n role, tabIndex, id, onMouseEnter,\n}, ref) {\n const tree = useContext(MenuTreeContext);\n const look = itemLook(danger, disabled, className);\n return (\n <button\n ref={ref}\n id={id}\n role={role}\n tabIndex={tabIndex}\n onClick={() => {\n onClick?.();\n // Inside a submenu, choosing an item is the end of the whole menu —\n // not just the panel it sat in. A root item keeps its old contract:\n // the caller's own onClick decides.\n if (tree && tree.depth > 0) tree.closeTree();\n }}\n onMouseEnter={() => {\n // Resting on a plain row retires any submenu open beside it, after the\n // same grace the Start menu gives.\n tree?.scheduleClose(tree.depth);\n onMouseEnter?.();\n }}\n disabled={disabled}\n className={look.className}\n style={look.style}\n >\n {children}\n </button>\n );\n});\n\nexport interface PopupSubmenuProps {\n /** The row's text. */\n label: ReactNode;\n /** Drawn before the label, as in a `PopupMenuItem`. */\n icon?: ReactNode;\n /** The submenu's contents — `PopupMenuItem`, `PopupMenuLabel`,\n * `PopupMenuDivider`, or another `PopupSubmenu`. */\n children: ReactNode;\n disabled?: boolean;\n className?: string;\n /** The submenu panel's minimum width. */\n minWidth?: number;\n /** The tallest the panel may grow before its items scroll — a long target\n * list, say `'min(60vh, 420px)'`. Never taller than the viewport less its\n * 8px gutters, whatever is passed. */\n maxHeight?: number | string;\n}\n\nconst chevron = (\n <svg aria-hidden className=\"h-3.5 w-3.5 shrink-0 ml-auto text-gray-500\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M8.25 4.5l7.5 7.5-7.5 7.5\" />\n </svg>\n);\n\n/** The z-index a panel must beat: the outermost stacking level `el` sits in\n * at <body>, since a portalled submenu is compared against that, not against\n * whatever the root menu is nested inside. */\nfunction layerOf(el: HTMLElement | null): number {\n let z: number = Z_LAYERS.menu;\n for (let node = el; node && node !== document.body; node = node.parentElement) {\n const value = parseInt(getComputedStyle(node).zIndex, 10);\n if (!Number.isNaN(value)) z = value;\n }\n return z;\n}\n\n/** Enabled items of a panel, in order — what the arrow keys walk. */\nfunction focusables(panel: HTMLElement | null): HTMLElement[] {\n if (!panel) return [];\n return [...panel.querySelectorAll<HTMLElement>('button:not([disabled])')]\n .filter(b => b.closest('[data-popup-submenu-panel]') === panel);\n}\n\n/**\n * A row in a `PopupMenu` that opens a nested menu.\n *\n * Opens on hover after a short rest, and at once on click, Enter, Space or\n * ArrowRight (which also move focus to its first item). ArrowLeft and Escape\n * close it and give focus back to the row. It opens to the right of the menu,\n * or to the left when there is no room, and moves up when it would run off the\n * bottom — the Start menu's placement rules, from `menuPath.ts`. Choosing an\n * item inside closes the whole menu (the root `PopupMenu`'s `onClose`).\n */\nexport function PopupSubmenu({ label, icon, children, disabled, className = '', minWidth = 180, maxHeight }: PopupSubmenuProps) {\n const tree = useContext(MenuTreeContext);\n const key = useId();\n const rowRef = useRef<HTMLButtonElement>(null);\n const panelRef = useRef<HTMLDivElement>(null);\n const openTimer = useRef<ReturnType<typeof setTimeout>>();\n /** Whether the current opening came from the keyboard — then focus moves in. */\n const focusOnOpen = useRef(false);\n const panelId = `${key}-menu`;\n useEffect(() => () => clearTimeout(openTimer.current), []);\n\n const depth = tree?.depth ?? 0;\n const anchor = tree?.path[depth];\n const isOpen = !!anchor && anchor.key === key && !disabled;\n\n const openNow = useCallback((withFocus: boolean) => {\n if (!tree || disabled || !rowRef.current) return;\n clearTimeout(openTimer.current);\n focusOnOpen.current = withFocus;\n const row = rowRef.current.getBoundingClientRect();\n const owner = (rowRef.current.closest('[data-popup-submenu-panel]')\n ?? rowRef.current.closest('[data-popup-menu-tree]')) as HTMLElement | null;\n const ownerRect = owner?.getBoundingClientRect() ?? row;\n const preferLeft = depth > 0 && (tree.path[depth - 1]?.flipped ?? false);\n const place = menuPanelLeft(ownerRect.left, ownerRect.right, minWidth, window.innerWidth, SUBMENU_GAP, preferLeft);\n tree.open(depth, { key, y: row.top, ...place });\n }, [tree, disabled, depth, key, minWidth]);\n\n const closeSelf = (restoreFocus: boolean) => {\n tree?.closeBelow(depth);\n if (restoreFocus) rowRef.current?.focus();\n };\n\n // Escape through the shell's interceptor seam: inside a window, `Modal`\n // takes Escape in the capture phase and would close the whole window.\n useEffect(() => {\n if (!isOpen) return;\n return registerModalEscapeInterceptor(e => {\n if (e.key !== 'Escape') return false;\n // Registration order cannot say which submenu is deepest: React runs a\n // parent's effects after its children's, so once both re-render the\n // outer submenu is the newest registrant and is asked first. Defer to\n // the open path instead — a deeper level open under this one takes it.\n if (tree && tree.path.length > depth + 1) return false;\n closeSelf(true);\n return true;\n });\n });\n\n // Place the panel against its real size before paint — the estimate used\n // `minWidth` and the row's top — then move focus in if a key opened it.\n useLayoutEffect(() => {\n if (!isOpen || !anchor) return;\n const place = () => {\n const panel = panelRef.current;\n if (!panel || !rowRef.current) return;\n const margin = 8;\n const owner = (rowRef.current.closest('[data-popup-submenu-panel]')\n ?? rowRef.current.closest('[data-popup-menu-tree]')) as HTMLElement | null;\n const ownerRect = owner?.getBoundingClientRect() ?? rowRef.current.getBoundingClientRect();\n const preferLeft = depth > 0 && (tree?.path[depth - 1]?.flipped ?? false);\n const width = panel.offsetWidth || minWidth;\n const spot = menuPanelLeft(ownerRect.left, ownerRect.right, width, window.innerWidth, SUBMENU_GAP, preferLeft, margin);\n const height = panel.offsetHeight;\n // Top-aligned with the row: centring the clamp on `row top + half the\n // panel` gives exactly that, and still moves the panel up at the bottom.\n const rowTop = rowRef.current.getBoundingClientRect().top;\n const top = clampMenuTop(rowTop + height / 2, height, margin, window.innerHeight - margin);\n panel.style.left = `${spot.left}px`;\n panel.style.top = `${top}px`;\n panel.dataset.flipped = String(spot.flipped);\n };\n place();\n // Again after the root menu's own viewport clamp, which runs in a frame\n // AFTER this effect: on a re-render it can still move the root a few\n // pixels, and the submenu has to follow the menu it hangs off.\n let second = 0;\n const first = requestAnimationFrame(() => { second = requestAnimationFrame(place); });\n return () => { cancelAnimationFrame(first); cancelAnimationFrame(second); };\n });\n\n useEffect(() => {\n if (!isOpen || !focusOnOpen.current) return;\n focusOnOpen.current = false;\n focusables(panelRef.current)[0]?.focus();\n }, [isOpen]);\n\n const childTree = useMemo<MenuTree | null>(() => tree && { ...tree, depth: depth + 1 }, [tree, depth]);\n\n if (!tree) throw new Error('PopupSubmenu must be rendered inside a PopupMenu');\n\n const onRowKeyDown = (e: React.KeyboardEvent<HTMLButtonElement>) => {\n if (e.key === 'ArrowRight') {\n e.preventDefault();\n e.stopPropagation();\n openNow(true);\n }\n };\n\n const onPanelKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {\n // React bubbles through portals, so a key pressed in a deeper submenu\n // reaches this handler too. That panel handled it already.\n if ((e.target as HTMLElement).closest('[data-popup-submenu-panel]') !== e.currentTarget) return;\n const items = focusables(panelRef.current);\n const at = items.indexOf(document.activeElement as HTMLElement);\n const move = (to: number) => { e.preventDefault(); e.stopPropagation(); items[(to + items.length) % items.length]?.focus(); };\n if (e.key === 'ArrowDown') move(at + 1);\n else if (e.key === 'ArrowUp') move(at < 0 ? items.length - 1 : at - 1);\n else if (e.key === 'Home') move(0);\n else if (e.key === 'End') move(items.length - 1);\n else if (e.key === 'ArrowLeft') { e.preventDefault(); e.stopPropagation(); closeSelf(true); }\n };\n\n const look = itemLook(false, disabled, className, isOpen);\n const density = getDensity();\n\n return (\n <>\n <button\n ref={rowRef}\n type=\"button\"\n role=\"menuitem\"\n aria-haspopup=\"menu\"\n aria-expanded={isOpen}\n aria-controls={isOpen ? panelId : undefined}\n disabled={disabled}\n data-popup-submenu-row\n className={look.className}\n style={look.style}\n onClick={e => {\n // `detail` is 0 for a click the keyboard made (Enter, Space).\n const byKey = e.detail === 0;\n if (!isOpen) openNow(byKey);\n else if (byKey) focusables(panelRef.current)[0]?.focus();\n }}\n onKeyDown={onRowKeyDown}\n onMouseEnter={() => {\n if (disabled) return;\n tree.cancelClose();\n if (isOpen) { tree.closeBelow(depth + 1); return; }\n clearTimeout(openTimer.current);\n openTimer.current = setTimeout(() => openNow(false), SUBMENU_OPEN_DELAY);\n }}\n onMouseLeave={() => {\n clearTimeout(openTimer.current);\n tree.scheduleClose(depth);\n }}\n >\n {icon}\n <span className=\"min-w-0 flex-1 truncate\">{label}</span>\n {chevron}\n </button>\n {isOpen && createPortal(\n <div\n ref={panelRef}\n id={panelId}\n role=\"menu\"\n data-popup-menu-tree={tree.id}\n data-popup-submenu-panel\n className={`fixed rounded-2xl overflow-x-hidden overflow-y-auto overscroll-contain ${density === 'tight' ? 'py-1' : density === 'large' ? 'py-2' : 'py-1.5'}`}\n style={{\n left: anchor!.left, top: anchor!.y, minWidth,\n // The viewport cap always applies, so a long list scrolls rather\n // than running off the screen even when no maxHeight is passed.\n maxHeight: maxHeight === undefined\n ? 'calc(100vh - 16px)'\n : `min(${typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight}, calc(100vh - 16px))`,\n zIndex: layerOf(tree.root.current) + depth + 1,\n animation: `${anchor!.flipped ? 'popup-submenu-in-left' : 'popup-submenu-in'} 0.1s ease-out`,\n ...glassStyle(),\n }}\n onKeyDown={onPanelKeyDown}\n onMouseEnter={() => { tree.cancelClose(); clearTimeout(openTimer.current); }}\n onMouseLeave={() => tree.scheduleClose(depth + 1)}\n onContextMenu={e => { if (!keepsNativeMenu(e.target)) e.preventDefault(); }}\n >\n <MenuTreeContext.Provider value={childTree}>{children}</MenuTreeContext.Provider>\n <style>{`@keyframes popup-submenu-in { from { opacity: 0; transform: translateX(-4px); } to { opacity: 1; transform: translateX(0); } }\n@keyframes popup-submenu-in-left { from { opacity: 0; transform: translateX(4px); } to { opacity: 1; transform: translateX(0); } }`}</style>\n </div>,\n document.body,\n )}\n </>\n );\n}\n\n/** A divider line between menu items */\nexport function PopupMenuDivider() {\n const density = getDensity();\n return <div className={`border-t ${GLASS_DIVIDER} ${density === 'tight' ? 'my-0.5' : density === 'large' ? 'my-1.5' : 'my-1'} mx-3`} />;\n}\n\n/** A section header label */\nexport function PopupMenuLabel({ children }: { children: ReactNode }) {\n const density = getDensity();\n const labelPadY = density === 'tight' ? '0.125rem' : density === 'large' ? '0.375rem' : '0.25rem';\n return (\n <div\n className=\"text-[10px] font-medium text-gray-400 uppercase tracking-wider\"\n style={{\n paddingLeft: 'var(--menu-padding-x, 1rem)',\n paddingRight: 'var(--menu-padding-x, 1rem)',\n paddingTop: labelPadY,\n paddingBottom: labelPadY,\n }}\n >\n {children}\n </div>\n );\n}\n\n/** An icon helper */\nexport function MenuIcon({ d, className = 'h-4 w-4 text-gray-400' }: { d: string; className?: string }) {\n return (\n <svg className={className} fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.5}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d={d} />\n </svg>\n );\n}\n"]}
|
package/dist/chunk-CJZXR7MG.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shell/useIsMobile.ts","../src/shell/Result.tsx","../src/shell/Spinner.tsx","../src/shell/ShellPrefs.tsx","../src/shell/staleChunk.ts","../src/shell/Kbd.tsx"],"names":["jsx","jsxs"],"mappings":"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","file":"chunk-G3HQLQYZ.js","sourcesContent":["/**\n * Mobile breakpoint hook — single source of truth for \"is this a phone/tablet\n * portrait viewport?\" Drives whether <Layout> renders the desktop chrome\n * (windows / taskbar / start menu sidebar) or the <MobileAppLanding> screen\n * that points the user at the consumer-wired mobile app. A handful of\n * primitives (Modal, ResizableTable) also read it to adapt touch behaviour.\n *\n * Locked at 768 px so iPad portrait counts as mobile. Also OR's with\n * `pointer: coarse` so touch-only devices that happen to be wider (e.g. an\n * Android tablet in landscape) still get touch-appropriate UI.\n */\nimport { useSyncExternalStore } from 'react';\n\nconst QUERY = '(max-width: 767px), (pointer: coarse)';\n\nfunction getSnapshot(): boolean {\n if (typeof window === 'undefined' || !window.matchMedia) return false;\n return window.matchMedia(QUERY).matches;\n}\n\nfunction getServerSnapshot(): boolean {\n return false;\n}\n\nfunction subscribe(cb: () => void): () => void {\n if (typeof window === 'undefined' || !window.matchMedia) return () => {};\n const mql = window.matchMedia(QUERY);\n // Older Safari uses addListener; modern browsers use addEventListener.\n if (mql.addEventListener) {\n mql.addEventListener('change', cb);\n return () => mql.removeEventListener('change', cb);\n }\n mql.addListener(cb);\n return () => mql.removeListener(cb);\n}\n\nexport function useIsMobile(): boolean {\n return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n}\n","/**\n * Result — the full-page outcome state: a 404, a 403, a crashed boundary, a\n * completed checkout.\n *\n * `EmptyState` is the neighbour to know about. That one means \"this list has\n * nothing in it yet\", which is normal and usually actionable. This one means\n * something ENDED — succeeded, failed, or was refused — and it is the whole\n * page rather than a region of one. `ErrorPage` is this component wrapped in a\n * full-screen frame.\n */\nimport { type ReactNode } from 'react';\n\nexport type ResultStatus = 'success' | 'error' | 'warning' | 'info' | '404' | '403' | '500';\n\nexport interface ResultProps {\n status?: ResultStatus;\n title?: ReactNode;\n subTitle?: ReactNode;\n /** Actions — usually one primary route out. Always give the user one. */\n extra?: ReactNode;\n /** Replace the status glyph entirely. */\n icon?: ReactNode;\n children?: ReactNode;\n className?: string;\n}\n\ntype Tone = 'success' | 'error' | 'warning' | 'info';\n\nconst STATUS_TONE: Record<ResultStatus, Tone> = {\n success: 'success', error: 'error', warning: 'warning', info: 'info',\n // A missing page is not an error the user made, and colouring it red says it\n // was. Refused and broken are genuinely different from not-found.\n 404: 'info', 403: 'warning', 500: 'error',\n};\n\nconst TONE_CLASS: Record<Tone, string> = {\n success: 'text-green-600 bg-green-100',\n error: 'text-red-600 bg-red-100',\n warning: 'text-amber-600 bg-amber-100',\n info: 'text-blue-600 bg-blue-100',\n};\n\n/** Default copy, so the common cases need only `status`. */\nconst DEFAULT_TITLE: Partial<Record<ResultStatus, string>> = {\n 404: 'Page not found',\n 403: \"You don't have access to this\",\n 500: 'Something went wrong',\n};\n\nfunction StatusIcon({ tone }: { tone: Tone }) {\n const common = { className: 'h-6 w-6', fill: 'none', viewBox: '0 0 24 24', stroke: 'currentColor', strokeWidth: 1.5, 'aria-hidden': true } as const;\n switch (tone) {\n case 'success':\n return <svg {...common}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M4.5 12.75l6 6 9-13.5\" /></svg>;\n case 'error':\n return <svg {...common}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\" /></svg>;\n case 'warning':\n return <svg {...common}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M12 9v3.75m9-.75a9 9 0 11-18 0 9 9 0 0118 0zm-9 3.75h.008v.008H12v-.008z\" /></svg>;\n default:\n return <svg {...common}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z\" /></svg>;\n }\n}\n\nexport default function Result({\n status = 'info', title, subTitle, extra, icon, children, className = '',\n}: ResultProps) {\n const tone = STATUS_TONE[status];\n // WCAG 4.1.3. A Result that reports a failure is a status message: it appears\n // without the user moving focus, so a screen-reader user is told nothing\n // unless it announces itself. `alert` is assertive, which is right for a\n // failure and wrong for everything else — an outcome someone asked for\n // (a completed checkout) or a neutral one (an empty cart) would interrupt\n // whatever they were reading to say something they already expected.\n const announces = tone === 'error';\n return (\n <div\n role={announces ? 'alert' : undefined}\n className={`flex flex-col items-center justify-center px-6 py-12 text-center ${className}`.trim()}\n >\n <div className={`flex h-14 w-14 items-center justify-center rounded-full ${TONE_CLASS[tone]}`}>\n {icon ?? <StatusIcon tone={tone} />}\n </div>\n <h2 className=\"mt-4 text-lg font-semibold text-gray-900\">\n {title ?? DEFAULT_TITLE[status] ?? ''}\n </h2>\n {subTitle && <p className=\"mt-1 max-w-md text-sm text-gray-500\">{subTitle}</p>}\n {children && <div className=\"mt-4 w-full max-w-md text-left\">{children}</div>}\n {extra && <div className=\"mt-6 flex flex-wrap items-center justify-center gap-3\">{extra}</div>}\n </div>\n );\n}\n","import { type ReactNode } from 'react';\n\nexport interface LoadingSpinnerProps {\n /** Ring diameter: `sm` = 20px, `md` = 32px (default), `lg` = 48px. */\n size?: 'sm' | 'md' | 'lg';\n /** Wrapper padding utility (default `py-12`). Pass `''` to remove. */\n padding?: string;\n /**\n * Caption under the ring, e.g. \"Signing in…\". Naming what is happening beats\n * a bare ring on a full-screen wait, where there is nothing else on the page\n * to infer it from. Adds `role=\"status\"` so it is announced.\n */\n label?: ReactNode;\n /** Extra classes on the centering wrapper. */\n className?: string;\n}\n\n/**\n * LoadingSpinner — a centered animated ring for pending/loading regions.\n * (Distinct from the shell's internal \"Loading…\" text used inside data grids.)\n */\nexport default function LoadingSpinner({ size = 'md', padding = 'py-12', label, className = '' }: LoadingSpinnerProps) {\n const dim = size === 'sm' ? 'h-5 w-5' : size === 'lg' ? 'h-12 w-12' : 'h-8 w-8';\n const ring = <div className={`${dim} animate-spin rounded-full border-2 border-gray-200 border-t-blue-600`} />;\n // Without a label the markup is exactly what it has always been — including\n // no `role`, which would otherwise have changed the a11y tree for every\n // existing caller. A labelled spinner is a status; a bare ring is decoration.\n if (label == null) {\n return <div className={`flex items-center justify-center ${padding} ${className}`}>{ring}</div>;\n }\n return (\n <div role=\"status\" className={`flex flex-col items-center justify-center gap-3 ${padding} ${className}`}>\n {ring}\n <p className=\"text-sm text-gray-500\">{label}</p>\n </div>\n );\n}\n","/**\n * Shell-side user-preferences surface — the package never owns where prefs\n * live. The consumer supplies a hook returning the current prefs object plus\n * a save callback; the shell reads keys it knows about (theme,\n * taskbar_position, taskbar_size, world_clocks, favorite_documents,\n * desktop_folders, desktop_snap, notepad_notes, …) and patches them.\n *\n * When no provider is mounted, the shell falls back to localStorage scoped\n * by `storageKey` — so the package works out of the box for a backend-less\n * consumer.\n */\nimport { createContext, useContext, useState, useCallback, useMemo, type ReactNode } from 'react';\n\nexport interface ShellPrefsAdapter {\n /** The current prefs object. The shell reads its known keys directly off\n * this; consumer-private keys are ignored. */\n prefs: Record<string, any>;\n /** Patch a subset of prefs. The shell calls this with shallow diffs. */\n save: (patch: Record<string, any>) => Promise<void> | void;\n}\n\nconst ShellPrefsContext = createContext<ShellPrefsAdapter | null>(null);\n\nexport function ShellPrefsProvider({\n value,\n children,\n}: { value: ShellPrefsAdapter; children: ReactNode }) {\n return <ShellPrefsContext.Provider value={value}>{children}</ShellPrefsContext.Provider>;\n}\n\n/** Default localStorage-backed adapter — useful when the consumer doesn't\n * ship a backend. Pass the result into <ShellPrefsProvider value={…}>.\n *\n * `defaults` are merged behind whatever's already stored, so they only\n * apply for keys the user hasn't set yet. Useful for opting out of bundled\n * features (e.g. `{ restore_windows: false }`). Note this is the only way to\n * turn a default-ON feature off up front; a default-OFF one like\n * `show_desktop_version` is already off until someone ticks its box. */\nexport function useLocalStoragePrefs(\n storageKey = 'react-os-shell:prefs',\n defaults?: Record<string, any>,\n): ShellPrefsAdapter {\n const [prefs, setPrefs] = useState<Record<string, any>>(() => {\n try {\n const stored = JSON.parse(localStorage.getItem(storageKey) || '{}');\n return { ...(defaults ?? {}), ...stored };\n } catch { return { ...(defaults ?? {}) }; }\n });\n const save = useCallback((patch: Record<string, any>) => {\n setPrefs(prev => {\n const next = { ...prev, ...patch };\n try { localStorage.setItem(storageKey, JSON.stringify(next)); } catch {}\n return next;\n });\n }, [storageKey]);\n return { prefs, save };\n}\n\n/** Stable no-provider adapter: reads as empty, drops saves. One instance, so\n * a component with no provider above it is not handed a new `save` (and a new\n * effect dependency) on every render. */\nconst NO_PROVIDER: ShellPrefsAdapter = { prefs: {}, save: () => {} };\n\n/** The adapter exactly as the consumer supplied it — its `save` MAY reject.\n *\n * A consumer whose adapter reports a failed write is how the shell knows a\n * write did not land, which is what lets `SessionWindowRestore` retry one\n * rather than record a lost write as saved. That component is the only caller\n * that wants this; everything else wants `useShellPrefs`. Exported from the\n * module for it and for the spec, not from the package index. */\nexport function useShellPrefsAdapter(): ShellPrefsAdapter {\n return useContext(ShellPrefsContext) ?? NO_PROVIDER;\n}\n\n/** Returns the active prefs adapter. When no provider is mounted, reads as\n * empty and silently drops saves — components still render, but persistence\n * is a no-op.\n *\n * `save` here NEVER REJECTS. Nearly every caller fires and forgets —\n * `save({ desktop_bg: bg })` inside an onChange, with the result discarded —\n * and an adapter that reports a failed write turns each of those into an\n * unhandled rejection, one per failed PATCH, across the shell and every\n * consumer. So the rejection is swallowed once here rather than at a dozen\n * call sites that would each have to remember. `await save(…)` still works;\n * it just resolves whether or not the write landed.\n *\n * A caller that must know reads `useShellPrefsAdapter` instead. */\nexport function useShellPrefs(): ShellPrefsAdapter {\n const { prefs, save: rawSave } = useShellPrefsAdapter();\n const save = useCallback((patch: Record<string, any>) => {\n const result = rawSave(patch);\n // Stay thenable for `await save(…)`, but settled either way. Duck-typed:\n // an adapter may hand back any thenable, not a native Promise.\n return result && typeof (result as Promise<void>).then === 'function'\n ? (result as Promise<void>).catch(() => {})\n : result;\n }, [rawSave]);\n return useMemo(() => ({ prefs, save }), [prefs, save]);\n}\n","/**\n * Stale-chunk recovery — reload once when a lazy chunk the running page asks\n * for is no longer on the server.\n *\n * A build names every lazy chunk by its content hash, and a deploy replaces\n * the whole set. A tab opened before the deploy still runs the old entry, and\n * the first lazy window it opens asks for a chunk the server no longer has.\n * The SPA fallback answers with `index.html`, which is not the script the\n * page asked for, and the import fails. Nothing inside the page can satisfy\n * that import — only a fresh document, which names the new chunks, can — so\n * an error boundary's \"try again\" remounts into the same failure, and the\n * only recovery that works is a reload.\n *\n * Two bundlers, two ways to hear it:\n *\n * - **Vite** dispatches `vite:preloadError` on `window` (cancelable;\n * `preventDefault()` swallows the throw), and the import rejects with\n * \"Failed to fetch dynamically imported module\".\n * - **Next.js (Turbopack) and webpack** load a chunk by appending a `<script>`\n * or a stylesheet `<link>`, and a failed one rejects the import with an\n * Error named `ChunkLoadError`. There is no bundler event, but one capture\n * listener on `window`, matching the build's chunk path (`/_next/static/`\n * by default), hears every chunk — the app's lazy pages, the shell's lazy\n * apps, CSS — in one of two ways:\n * - the element's own `error`: a 404, or `index.html` refused under\n * `X-Content-Type-Options: nosniff`. A resource error does not bubble,\n * but it does pass `window` in the capture phase.\n * - without `nosniff` the browser RUNS `index.html` as the script, and a\n * `SyntaxError` whose `filename` is the chunk reaches `window`. Nothing\n * else ever would: Turbopack's loader waits for the chunk to register\n * itself, so the import neither resolves nor rejects.\n *\n * Neither proves a deploy. A dropped connection fails the element too, and\n * Turbopack retries a failed chunk once before it gives up. So before\n * reloading, a `HEAD` request (`no-store`) asks the server whether the file\n * is gone: a 404 or 410, or HTML where a chunk should be (the SPA\n * fallback). A chunk that is still there, a 5xx, or no answer at all\n * (offline) is left to the loader's retry and the crash fallback — a reload\n * would throw the page away for nothing, or land on the browser's offline\n * page.\n *\n * `installStaleChunkReload` listens for both and reloads the page — once per\n * cooldown, remembered in `sessionStorage` under one key whichever signal\n * fired, so a chunk that is STILL missing after the reload (a broken deploy)\n * surfaces as the crash it is rather than as a reload loop that eats the\n * user's open windows.\n *\n * Kit-safe: imports nothing. Call it once, before the app mounts.\n */\n\n/** sessionStorage key holding the epoch-ms of the last reload this made. */\nexport const STALE_CHUNK_RELOAD_KEY = 'react-os-shell:stale-chunk-reload-at';\n\n/**\n * How soon after a reload a second missing chunk is allowed to crash instead\n * of reloading again. A reload plus the shell restoring its windows takes a\n * few seconds; anything under this is the same failure, not a new deploy.\n */\nexport const STALE_CHUNK_RELOAD_COOLDOWN_MS = 30_000;\n\n/** The event name Vite's preload helper dispatches. */\nexport const VITE_PRELOAD_ERROR_EVENT = 'vite:preloadError';\n\n/** Where Next.js serves a build's hashed chunks, under Turbopack or webpack. */\nexport const NEXT_CHUNK_PATH_PREFIX = '/_next/static/';\n\nexport interface StaleChunkReloadOptions {\n /** Defaults to {@link STALE_CHUNK_RELOAD_COOLDOWN_MS}. */\n cooldownMs?: number;\n /** Replaced in tests; the default is `window.location.reload()`. */\n reload?: () => void;\n /**\n * Where the last-reload timestamp is kept. Defaults to `sessionStorage`\n * (guarded — a browser that throws on access, such as a private window\n * with storage disabled, degrades to \"always reload\"). `null` disables the\n * cooldown outright.\n */\n storage?: Pick<Storage, 'getItem' | 'setItem'> | null;\n /** Clock, for tests. */\n now?: () => number;\n /**\n * Where the build serves its chunks: a `<script>` or stylesheet `<link>`\n * under it that fails to load — and that the server confirms is gone —\n * reloads the page. A path, resolved from this page's origin, or a full URL\n * for chunks on another host (Next's `assetPrefix` on a CDN, which must then\n * allow the cross-origin `HEAD` that confirms it). End it with `/`: it is a\n * plain prefix match. Defaults to {@link NEXT_CHUNK_PATH_PREFIX}; a\n * `basePath` app passes `'/<basePath>/_next/static/'`, a webpack app its\n * `output.publicPath`. `null` listens for Vite's event only.\n *\n * Never point it at Vite's `/assets/`: Vite's own event already covers\n * those chunks, and hearing the same failure twice would spend the\n * cooldown on the first and let Vite throw on the second.\n */\n chunkPathPrefix?: string | null;\n}\n\n/**\n * The messages the three engines produce for a dynamic `import()` whose\n * module the server did not return, plus Vite's own line for a CSS chunk it\n * could not preload. Used by the shell's crash fallback to offer \"Reload\n * page\" instead of a remount that cannot succeed.\n */\nconst STALE_CHUNK_MESSAGE =\n /Failed to fetch dynamically imported module|error loading dynamically imported module|Importing a module script failed|Unable to preload CSS/i;\n\n/**\n * The name Turbopack and webpack give the Error a failed chunk rejects with\n * (\"Failed to load chunk /_next/static/chunks/… from module …\", \"Loading\n * chunk 42 failed.\").\n */\nconst CHUNK_LOAD_ERROR_NAME = 'ChunkLoadError';\n\n/** True when `error` is a lazy chunk the server no longer serves. */\nexport function isStaleChunkError(error: unknown): boolean {\n if (error instanceof Error && error.name === CHUNK_LOAD_ERROR_NAME) return true;\n const message =\n error instanceof Error ? error.message : typeof error === 'string' ? error : '';\n return STALE_CHUNK_MESSAGE.test(message);\n}\n\n/**\n * Whether a failed `<link>` was loading code or styles the page needs now: a\n * stylesheet, a module preload, or a preload of a script or a style. A\n * prefetch is speculative — for a page the user may never open — and a font,\n * an image or an icon is not a chunk; reloading for any of those would throw\n * away what the user is doing for nothing. `rel` is matched without regard\n * to case, as the browser matches it.\n */\nfunction linkLoadsChunk(link: HTMLLinkElement): boolean {\n const rels = link.rel.toLowerCase().split(/\\s+/);\n if (rels.includes('stylesheet') || rels.includes('modulepreload')) return true;\n const as = (link.getAttribute('as') ?? '').toLowerCase();\n return rels.includes('preload') && (as === 'script' || as === 'style');\n}\n\n/**\n * The address of the chunk an `error` on `window` is about, when it is one of\n * the build's chunks under `prefix`: a failed `<script>` or chunk `<link>`,\n * or a `SyntaxError` thrown by running a chunk that came back as HTML. Null\n * for anything else — an image, a third party, a runtime error in the app.\n * The constructors are read off `window`, so a document without them as bare\n * globals (a test DOM) or an element from another frame cannot throw here.\n */\nfunction failedChunkUrl(event: Event, prefix: string): string | null {\n const target = event.target;\n let src = '';\n if (target instanceof window.HTMLScriptElement) src = target.src;\n else if (target instanceof window.HTMLLinkElement && linkLoadsChunk(target)) src = target.href;\n else if (event instanceof window.ErrorEvent && (event.error as { name?: unknown } | null)?.name === 'SyntaxError') {\n src = event.filename;\n }\n if (!src) return null;\n try {\n // A path prefix lands on this origin, from its root; a full-URL prefix\n // stays where it points. Then one comparison serves both.\n const base = new URL(prefix, window.location.origin).href;\n const url = new URL(src, window.location.href).href;\n return url.startsWith(base) ? url : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Whether the server no longer has `url`: a 404 or 410, or the SPA fallback's\n * HTML where a chunk should be. Anything else — the file is there, a 5xx, no\n * answer at all — means the load failed for a reason a reload would not fix.\n * `no-store`, so a cached copy cannot answer for the server.\n */\nasync function isGoneFromServer(url: string): Promise<boolean> {\n try {\n const response = await fetch(url, { method: 'HEAD', cache: 'no-store' });\n if (response.status === 404 || response.status === 410) return true;\n return response.ok && /\\btext\\/html\\b/i.test(response.headers.get('content-type') ?? '');\n } catch {\n return false;\n }\n}\n\nfunction defaultStorage(): Pick<Storage, 'getItem' | 'setItem'> | null {\n try {\n const s = window.sessionStorage;\n // Touch it: some browsers expose the object and throw on first use.\n s.getItem(STALE_CHUNK_RELOAD_KEY);\n return s;\n } catch {\n return null;\n }\n}\n\nfunction readLastReload(storage: Pick<Storage, 'getItem' | 'setItem'> | null): number | null {\n if (!storage) return null;\n try {\n const raw = storage.getItem(STALE_CHUNK_RELOAD_KEY);\n const at = raw === null ? NaN : Number(raw);\n return Number.isFinite(at) ? at : null;\n } catch {\n return null;\n }\n}\n\nfunction writeLastReload(storage: Pick<Storage, 'getItem' | 'setItem'> | null, at: number): void {\n if (!storage) return;\n try {\n storage.setItem(STALE_CHUNK_RELOAD_KEY, String(at));\n } catch {\n /* storage full or disabled — the reload still happens, just without the cooldown */\n }\n}\n\n/**\n * Reload the page the first time a lazy chunk fails to load, and let the\n * failure through on a repeat within the cooldown.\n *\n * Returns the uninstaller. Server-side (no `window`) it installs nothing.\n */\nexport function installStaleChunkReload(options: StaleChunkReloadOptions = {}): () => void {\n if (typeof window === 'undefined') return () => {};\n\n const cooldownMs = options.cooldownMs ?? STALE_CHUNK_RELOAD_COOLDOWN_MS;\n const reload = options.reload ?? (() => window.location.reload());\n const now = options.now ?? Date.now;\n const storage = options.storage === undefined ? defaultStorage() : options.storage;\n const chunkPathPrefix =\n options.chunkPathPrefix === undefined ? NEXT_CHUNK_PATH_PREFIX : options.chunkPathPrefix;\n\n /**\n * Whether to reload for this missing chunk — false inside the cooldown.\n * Claims the cooldown when it says yes. Both listeners ask here, so they\n * share one record.\n */\n const claimReload = (chunk: unknown): boolean => {\n const at = now();\n const last = readLastReload(storage);\n if (last !== null && at - last < cooldownMs) {\n // Reloaded moments ago and the chunk is still gone: this is not a\n // deploy the page missed, it is a server that cannot serve the build.\n // Let the import fail, so the boundary shows what is wrong.\n console.error(\n '[react-os-shell] a lazy chunk is still missing after a reload; letting the failure through:',\n chunk,\n );\n return false;\n }\n writeLastReload(storage, at);\n console.warn(\n '[react-os-shell] a lazy chunk is no longer on the server (new deploy?); reloading onto the current build:',\n chunk,\n );\n return true;\n };\n\n const onPreloadError = (event: Event) => {\n const payload = (event as Event & { payload?: unknown }).payload;\n // Vite dispatches this for ANY rejected dynamic import it wraps, not only\n // a missing chunk — an optional peer probed by its bare package name\n // rejects the same way, and its caller already catches that.\n // Reloading on that threw the whole portal away on every DXF preview.\n if (!isStaleChunkError(payload) || !claimReload(payload)) return;\n event.preventDefault();\n reload();\n };\n\n // The chunk listener asks the server before it reloads, so its decision is\n // async. One question per chunk at a time (Turbopack's retry fails the same\n // file again while the first is out), none once a reload has started (the\n // page is going), and none acted on after uninstall.\n let active = true;\n let reloading = false;\n const asking = new Set<string>();\n\n // Capture phase: a resource error does not bubble.\n const onChunkError = chunkPathPrefix === null ? null : (event: Event) => {\n const chunk = failedChunkUrl(event, chunkPathPrefix);\n if (!chunk || reloading || asking.has(chunk)) return;\n asking.add(chunk);\n void isGoneFromServer(chunk).then((gone) => {\n asking.delete(chunk);\n if (!gone || !active || reloading || !claimReload(chunk)) return;\n reloading = true;\n reload();\n });\n };\n\n window.addEventListener(VITE_PRELOAD_ERROR_EVENT, onPreloadError);\n if (onChunkError) window.addEventListener('error', onChunkError, true);\n return () => {\n active = false;\n window.removeEventListener(VITE_PRELOAD_ERROR_EVENT, onPreloadError);\n if (onChunkError) window.removeEventListener('error', onChunkError, true);\n };\n}\n","/**\n * Keyboard shortcuts: the platform-aware STRINGS a shortcut is written with,\n * and the badge that renders one.\n *\n * Both live here because they are one concept — a shortcut nobody can see is\n * not a shortcut — and because the strings came first: the constants have\n * shipped for years and the badge around them was left to each consumer, which\n * is how the admin portal ended up with 109 hand-written `<kbd>` elements\n * across 80 files in two different sizes.\n */\nexport const isMac = typeof navigator !== 'undefined' && /Mac|iPhone|iPad/.test(navigator.platform || navigator.userAgent);\n\n/** Platform-aware modifier symbols */\nexport const MOD = isMac ? '\\u2318' : 'Ctrl';\nexport const ALT = isMac ? '\\u2325' : 'Alt';\nexport const SHIFT = '\\u21E7';\nexport const ENTER = '\\u23CE';\n\n/** Common shortcut labels */\nexport const CMD_ENTER = isMac ? '\\u2318\\u23CE' : 'Ctrl\\u23CE';\nexport const CMD_S = isMac ? '\\u2318S' : 'Ctrl+S';\nexport const CMD_K = isMac ? '\\u2318K' : 'Ctrl+K';\nexport const CMD_DOT = isMac ? '\\u2318.' : 'Ctrl+.';\nexport const CMD_A = isMac ? '\\u2318A' : 'Ctrl+A';\nexport const CMD_Z = isMac ? '\\u2318Z' : 'Ctrl+Z';\n/** Redo. Mac has no \\u2318Y convention, so it is the shifted undo there. */\nexport const CMD_SHIFT_Z = isMac ? '\\u2318\\u21e7Z' : 'Ctrl+Shift+Z';\nexport const ALT_SHIFT_D = isMac ? '\\u2325\\u21E7D' : 'Alt+Shift+D';\nexport const ALT_SHIFT_E = isMac ? '\\u2325\\u21E7E' : 'Alt+Shift+E';\nexport const ALT_SHIFT_N = isMac ? '\\u2325\\u21E7N' : 'Alt+Shift+N';\n\nexport type KbdSize = 'sm' | 'md';\n\nexport interface KbdProps {\n /**\n * The shortcut, already written \\u2014 one of the constants above, or any string\n * (`'Esc'`, `'\\u2190'`). This component does NOT assemble a shortcut from\n * modifier flags: which symbol a platform uses is what the constants are\n * for, and two answers to that question is one too many.\n */\n keys: string;\n /**\n * `sm` is the quiet badge that rides inside another control's label \\u2014 the\n * `\\u2325\\u21E7E` on an Edit button. `md` (the default) is the one beside a\n * primary action, where it has to be readable at a glance.\n */\n size?: KbdSize;\n /**\n * Layered on, not replacing: a consumer that themes the badge (the admin\n * portal tints the submit one with its accent) adds its own class here and\n * keeps the shape.\n */\n className?: string;\n}\n\nconst KBD_BASE =\n 'inline-flex items-center justify-center rounded border border-gray-200 bg-gray-50 font-medium text-gray-500';\n\n// A Record keyed by the union, so a new rung without an entry is a compile\n// error rather than an undefined class string.\nconst KBD_SIZES: Record<KbdSize, string> = {\n sm: 'px-1 py-0.5 text-[10px]',\n md: 'px-1.5 py-0.5 text-[11px]',\n};\n\n/**\n * The shortcut badge: a real `<kbd>`, which is what tells a screen reader that\n * `\\u2318\\u23CE` is a key combination and not two pieces of punctuation.\n *\n * It renders the shortcut and nothing else \\u2014 no `aria-label` naming it \"Command\n * Enter\", because the badge is almost always INSIDE the control it belongs to\n * (\"Save \\u2318\\u23CE\") and a second name there would be read as part of the\n * button's own.\n */\nexport function Kbd({ keys, size = 'md', className = '' }: KbdProps) {\n return <kbd className={[KBD_BASE, KBD_SIZES[size], className].filter(Boolean).join(' ')}>{keys}</kbd>;\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shell/Breadcrumbs.tsx"],"names":[],"mappings":"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","file":"chunk-HNH7FOYB.js","sourcesContent":["import { Fragment } from 'react';\nimport type { MouseEvent, 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 /**\n * Navigate to this crumb. Omitted on the current (last) crumb.\n *\n * Receives the event, which is what makes `href` usable from a routed app:\n * the handler calls `preventDefault()` on a plain click and hands the\n * navigation to its router, while ctrl/middle/right clicks fall through to\n * the browser untouched. Without the argument an anchor could only ever do a\n * full page load, which is the thing a single-page app is avoiding.\n */\n onClick?: (event: MouseEvent<HTMLAnchorElement | HTMLButtonElement>) => void;\n /**\n * Where this crumb goes, when it is a real destination.\n *\n * A routed app needs this rather than `onClick` alone: an `<a href>` can be\n * middle-clicked into a new tab, copied, and read off the status bar before\n * committing to it — none of which a button offers, however well it behaves\n * once pressed. Given both, the anchor still calls `onClick`, so a client-side\n * router can intercept the plain-click case and leave the rest to the browser.\n */\n href?: string;\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\n/** Shared by the anchor and the button forms so the two cannot drift apart. */\nconst CRUMB_LINK =\n 'inline-flex min-w-0 items-center gap-1 truncate rounded px-1 -mx-1 text-gray-500 ' +\n 'transition-colors hover:text-gray-900 hover:underline focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-400';\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.href || 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 ) : crumb.item.href ? (\n <a\n href={crumb.item.href}\n onClick={crumb.item.onClick}\n className={CRUMB_LINK}\n >\n {crumb.item.icon}\n <span className=\"truncate\">{crumb.item.label}</span>\n </a>\n ) : (\n <button\n type=\"button\"\n onClick={crumb.item.onClick}\n className={CRUMB_LINK}\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"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shell/focusTrap.ts","../src/shell/escapeInterceptors.ts","../src/shell/overlayEvents.ts","../src/shell/useDismissPopupsOnOpen.ts","../src/shell/zLayers.ts","../src/shell/Dialog.tsx","../src/shell/ConfirmDialog.tsx"],"names":["useEffect","jsx","useRef","jsxs"],"mappings":"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","file":"chunk-HYZ5QMVO.js","sourcesContent":["/**\n * Focus containment and scroll locking for a modal surface — the two things\n * Headless UI was doing for `ConfirmDialog` before it was rewritten to import\n * nothing.\n *\n * Both are small, and both are the kind of small that is wrong in a way nobody\n * notices until someone navigates by keyboard: focus tabbing out of a dialog\n * onto the page behind it reads as the dialog having closed, and a page that\n * scrolls under a fixed overlay loses the user's place when it reopens.\n */\nimport { useEffect, type RefObject } from 'react';\n\n/**\n * Focusable candidates, in DOM order. Deliberately excludes anything with\n * `tabindex=\"-1\"` — that attribute means \"focusable by script, skipped by Tab\",\n * and treating it as a stop is the usual bug in a hand-rolled trap.\n */\nconst FOCUSABLE = [\n 'a[href]',\n 'button:not([disabled])',\n 'input:not([disabled])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n '[tabindex]:not([tabindex=\"-1\"])',\n].join(',');\n\nfunction focusable(root: HTMLElement): HTMLElement[] {\n return Array.from(root.querySelectorAll<HTMLElement>(FOCUSABLE))\n // A hidden element still matches the selector. offsetParent is null for\n // anything display:none or inside it — cheap, and right for our markup.\n .filter(el => el.offsetParent !== null || el === document.activeElement);\n}\n\n/**\n * While `active`, keep Tab inside `ref`, move focus in on open, and restore it\n * to wherever it came from on close.\n *\n * Restoring matters more than trapping: a dialog opened from a button in a\n * toolbar should hand focus back to that button, or a keyboard user is dumped\n * at the top of the document and has to find their place again.\n */\nexport function useFocusTrap(\n ref: RefObject<HTMLElement | null>,\n active: boolean,\n initialFocus?: RefObject<HTMLElement | null>,\n): void {\n useEffect(() => {\n if (!active) return;\n const root = ref.current;\n if (!root) return;\n\n const previous = document.activeElement as HTMLElement | null;\n\n // Focus the requested element, else the first natural stop, else the\n // container itself so the trap has somewhere to hold focus at all.\n const first = initialFocus?.current ?? focusable(root)[0] ?? root;\n if (first === root && !root.hasAttribute('tabindex')) root.setAttribute('tabindex', '-1');\n first.focus();\n\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key !== 'Tab') return;\n const stops = focusable(root);\n if (stops.length === 0) { e.preventDefault(); return; }\n const firstStop = stops[0];\n const lastStop = stops[stops.length - 1];\n const current = document.activeElement;\n\n // Wrap at both ends. Also catches focus having escaped the dialog\n // entirely (current is outside root), which happens if something inside\n // was removed while focused.\n if (e.shiftKey && (current === firstStop || !root.contains(current))) {\n e.preventDefault();\n lastStop.focus();\n } else if (!e.shiftKey && (current === lastStop || !root.contains(current))) {\n e.preventDefault();\n firstStop.focus();\n }\n };\n\n document.addEventListener('keydown', onKeyDown, true);\n return () => {\n document.removeEventListener('keydown', onKeyDown, true);\n // Only take focus back if it is still inside the dialog. If something\n // else claimed it in the meantime, stealing it back is the rude option.\n if (!root.contains(document.activeElement) && document.activeElement !== document.body) return;\n previous?.focus?.();\n };\n }, [ref, active, initialFocus]);\n}\n\n/**\n * While `active`, stop the document behind the overlay from scrolling.\n *\n * Compensates for the scrollbar's width so the page does not visibly jump\n * sideways as it locks — the artefact that makes a dialog feel cheap. Nested\n * locks are counted, so two stacked dialogs unlock exactly once.\n */\nlet lockCount = 0;\nlet restore: { overflow: string; paddingRight: string } | null = null;\n\nexport function useScrollLock(active: boolean): void {\n useEffect(() => {\n if (!active) return;\n const body = document.body;\n if (lockCount === 0) {\n restore = { overflow: body.style.overflow, paddingRight: body.style.paddingRight };\n const gap = window.innerWidth - document.documentElement.clientWidth;\n body.style.overflow = 'hidden';\n if (gap > 0) body.style.paddingRight = `${gap}px`;\n }\n lockCount += 1;\n return () => {\n lockCount -= 1;\n if (lockCount === 0 && restore) {\n body.style.overflow = restore.overflow;\n body.style.paddingRight = restore.paddingRight;\n restore = null;\n }\n };\n }, [active]);\n}\n","/**\n * The Escape-interceptor seam — deliberately a leaf that imports NOTHING.\n *\n * This lived inside `shell/Modal.tsx` until 4.16.0, and that one detail decided\n * what the whole package cost to import. `forms/Select.tsx` registers an\n * interceptor so Escape closes its portaled listbox instead of the window\n * around it — a two-line need — and reaching it through Modal pulled the entire\n * window manager into any bundle containing a dropdown: react-router-dom,\n * @tanstack/react-query, axios, @headlessui/react and @heroicons/react, plus\n * Modal's module-scope `localStorage` reads, which run at import time. An app\n * that wanted a text field and a select paid for a desktop it never rendered.\n *\n * Splitting the Set out is the whole fix: Modal and Select now both import this\n * file, and neither imports the other. `registerModalEscapeInterceptor` is\n * re-exported from Modal (and so from the package root) with the same binding\n * identity, so nothing downstream changes.\n *\n * NOTHING may be imported here — not React, not a type, not a sibling. The\n * moment it has an edge, every consumer of `react-os-shell/ui` inherits that\n * edge's transitive graph, which is exactly the failure this module exists to\n * prevent. `tests/uiEntryIsPeerFree.test.ts` enforces it.\n *\n * ── Escape interceptors ────────────────────────────────────────────────────\n * Window content can claim an Escape press before the topmost-modal handler\n * closes the window — e.g. the DXF Preview's measure tool exits AutoCAD-style\n * (clear the command input, then the tool, and only a further Esc closes the\n * window). Interceptors run in REVERSE registration order — most recently\n * registered first — and the first to return true consumes the event (the\n * modal neither closes nor sees it). An interceptor is global, so it must\n * check it belongs to the *active* modal itself (via `getActiveModalId()`)\n * before consuming.\n *\n * Reverse, because registration order tracks stacking order and Escape belongs\n * to whatever is on top. Two stacked `Dialog`s each register one; oldest-first\n * would hand Escape to the dialog UNDERNEATH and dismiss the wrong surface,\n * leaving the one the user is looking at on screen. This was invisible while\n * `ConfirmDialog` was the only multi-dialog registrant, because it registered\n * a single interceptor and hand-ordered its three dialogs inside it.\n *\n * Registrants that check `getActiveModalId()` are unaffected: at most one of\n * them can consume, so the order they are offered the event in cannot change\n * which one takes it.\n *\n * ── Draining the Set with no shell present ────────────────────────────────\n * This used to say that nothing draining the Set was fine, because a component\n * would handle Escape itself. That holds for `Select`, which has its own\n * `onKeyDown`. It did NOT hold for `Dialog`, which registers here and has no\n * other handler — so on a till or a routed portal, the two places `Dialog`\n * exists to serve, Escape did nothing at all.\n *\n * So the Set drains itself. The first registration attaches one document-level\n * capture listener; the last removal takes it away.\n *\n * It cannot double-fire when a shell IS present. `Modal` listens on `window`\n * in the capture phase, and capture runs window before document: Modal's\n * handler goes first, calls `runEscapeInterceptors`, and stops propagation when\n * one consumes — so this listener never sees the event. When Modal declines\n * (it is not the topmost window), this runs and the dialog floating above it\n * still gets its Escape, which is the right outcome anyway.\n *\n * Ordering across stacked dialogs is unchanged, because it is still the same\n * reverse walk deciding who consumes — not the listener that started it.\n */\nconst escapeInterceptors = new Set<(e: KeyboardEvent) => boolean>();\n\n/**\n * The self-drain listener, attached while at least one interceptor is\n * registered. Kept in module scope rather than per-registration so N dialogs\n * share one listener and the reverse walk stays the only thing deciding who\n * consumes.\n */\nlet detachDrain: (() => void) | null = null;\n\nfunction attachDrain(): void {\n // `document` is absent when this module is imported on a server; there is no\n // Escape to handle there, and registration still works for when it hydrates.\n if (detachDrain || typeof document === 'undefined') return;\n const onKeyDown = (e: KeyboardEvent): void => {\n if (e.key !== 'Escape') return;\n if (runEscapeInterceptors(e)) {\n e.preventDefault();\n e.stopPropagation();\n }\n };\n document.addEventListener('keydown', onKeyDown, true);\n detachDrain = () => document.removeEventListener('keydown', onKeyDown, true);\n}\n\n/** Register an Escape interceptor; returns an unregister function. */\nexport function registerModalEscapeInterceptor(fn: (e: KeyboardEvent) => boolean): () => void {\n escapeInterceptors.add(fn);\n attachDrain();\n return () => {\n escapeInterceptors.delete(fn);\n if (escapeInterceptors.size === 0 && detachDrain) {\n detachDrain();\n detachDrain = null;\n }\n };\n}\n\n/** Run the registered interceptors, most recent first; true if one consumed\n * the event. Internal to the shell — the window key handler calls this,\n * consumers never do. */\nexport function runEscapeInterceptors(e: KeyboardEvent): boolean {\n // A Set iterates in insertion order and has no reverse iterator, so snapshot\n // it. The copy also makes the walk safe against an interceptor that\n // unregisters itself while handling the event — which is exactly what a\n // dialog closing on Escape does.\n const ordered = Array.from(escapeInterceptors);\n for (let i = ordered.length - 1; i >= 0; i--) {\n try {\n if (ordered[i](e)) return true;\n } catch { /* a broken interceptor must not block closing */ }\n }\n return false;\n}\n","/**\n * The overlay-open seam — deliberately a leaf that imports NOTHING, for the same\n * reason as `escapeInterceptors.ts`: every popup in `react-os-shell/ui` reaches\n * it, so any edge added here lands in every consumer's bundle.\n *\n * ── Why popups need telling ──\n * A popup (a listbox, a calendar, a context menu) is portalled to `<body>` and\n * layered above the modal layer, so it can escape the window or dialog that\n * owns it. It closes on an outside pointer-down, on its own keys, and on a\n * pick. None of those happen when a keyboard shortcut opens a full-screen\n * overlay: ⌘K raised the search palette over an open `SearchableSelect`, and\n * the list stayed open, painted crisp ABOVE the palette's blurred backdrop.\n * Raising the palette cannot fix that — a popup opened INSIDE the palette or a\n * dialog must still paint above it, so the popup layer has to stay on top.\n *\n * So an overlay announces itself as it opens, and every open popup closes.\n * An overlay dispatches BEFORE its own content subscribes (a popup subscribes\n * only while open, in a passive effect), so a popup inside the overlay being\n * opened is never the one dismissed.\n *\n * Consumers with an overlay of their own call `dismissPopups()` when it opens;\n * consumers with a popup of their own listen for `OVERLAY_OPEN_EVENT` on\n * `window`.\n */\n\n/** Dispatched on `window` when a full-screen overlay (the search palette, a\n * `Dialog`, a `Drawer`, the shortcut sheet) opens. Open popups close on it. */\nexport const OVERLAY_OPEN_EVENT = 'react-os-shell:overlay-open';\n\n/** Close every open popup — call it as an overlay opens. A no-op on a server. */\nexport function dismissPopups(): void {\n if (typeof window === 'undefined') return;\n window.dispatchEvent(new Event(OVERLAY_OPEN_EVENT));\n}\n\n/** Subscribe to `OVERLAY_OPEN_EVENT`; returns the unsubscribe. Internal to the\n * kit's popups, which call it from the effect that runs while they are open. */\nexport function onOverlayOpen(handler: () => void): () => void {\n if (typeof window === 'undefined') return () => {};\n window.addEventListener(OVERLAY_OPEN_EVENT, handler);\n return () => window.removeEventListener(OVERLAY_OPEN_EVENT, handler);\n}\n","import { useEffect, useLayoutEffect } from 'react';\nimport { dismissPopups } from './overlayEvents';\n\n/**\n * A layout effect in the browser, a passive one on a server — where a layout\n * effect never runs anyway and React 18 warns for calling one.\n */\nconst useBrowserLayoutEffect = typeof document === 'undefined' ? useEffect : useLayoutEffect;\n\n/**\n * For an overlay: close every open popup as `open` turns true.\n *\n * A LAYOUT effect, not a passive one, so the popups close in the same frame the\n * overlay first paints. A state update made from a passive effect is scheduled\n * as a later task, and the browser lane caught the gap: the palette was on\n * screen with the list still open over it, for a frame or more, one run in\n * three.\n *\n * It runs before any popup inside the overlay can have subscribed — those\n * subscribe from a passive effect, and only once open — so the overlay never\n * dismisses its own content (see `overlayEvents.ts`).\n */\nexport function useDismissPopupsOnOpen(open: boolean): void {\n useBrowserLayoutEffect(() => {\n if (open) dismissPopups();\n }, [open]);\n}\n","/**\n * The shell's z-index scale — one map instead of literals scattered across the\n * components that stack against each other. A leaf that imports nothing, like\n * `escapeInterceptors.ts`, because the kit's popups read it.\n *\n * The numbers are the ones those components already used; collecting them\n * changed the order of exactly one thing, the ⌘K search palette. It sat at 200,\n * which is below the window stack once a dozen windows are open\n * (`window + index * windowStep`), below every pinned window, and below every\n * dialog — so it now sits on the overlay layer with `Dialog` and `Drawer`, and\n * is portalled to `<body>` so no window's stacking context can hold it down.\n *\n * Applied as inline `zIndex`, never as a Tailwind `z-[…]` class built from\n * these numbers: an interpolated class name is invisible to the consumer's\n * Tailwind scan and would generate no rule at all.\n *\n * Bottom to top:\n *\n * window 50 base of the window stack; each window above adds\n * windowStep 10 (`Modal`'s activation order)\n * menu 400 context menus and flyouts that stay in their layer\n * (`PopupMenu`)\n * pinnedWindow 999 an always-on-top window\n * overlay 9999 full-screen surfaces that interrupt: the search palette,\n * `Dialog`, `Drawer`, the shortcut sheet\n * popup 10000 portalled popups (listboxes, calendars, the server\n * status card) — above the overlay layer, so one opened\n * from inside a dialog or the palette is not drawn behind\n * it. Popups close when an overlay opens (`overlayEvents`),\n * which is what stops one from floating over an overlay\n * that was opened on top of it.\n */\nexport const Z_LAYERS = {\n window: 50,\n windowStep: 10,\n menu: 400,\n pinnedWindow: 999,\n overlay: 9999,\n popup: 10000,\n} as const;\n\nexport type ZLayer = keyof typeof Z_LAYERS;\n","/**\n * Dialog — a modal sheet that is NOT a shell window.\n *\n * The distinction matters. `Modal` is a window: it has a title bar, it can be\n * minimised, dragged, stacked and restored, and it lives in the window manager's\n * activation order. This is the other thing — an overlay that interrupts, is\n * answered, and goes away. A till has no desktop to put a window on, and a\n * routed portal page has no window manager either, so both need this one.\n *\n * It imports nothing but React, which is the point: it is what lets\n * `ConfirmDialog` — and every consumer of `react-os-shell/ui` — stop depending\n * on Headless UI and Heroicons. Focus containment and scroll locking come from\n * `./focusTrap` rather than a library.\n */\nimport { useEffect, useId, useRef, type ReactNode, type RefObject } from 'react';\nimport { useFocusTrap, useScrollLock } from './focusTrap';\nimport { registerModalEscapeInterceptor } from './escapeInterceptors';\nimport { useDismissPopupsOnOpen } from './useDismissPopupsOnOpen';\nimport { Z_LAYERS } from './zLayers';\n\nexport type DialogSize = 'sm' | 'md' | 'lg';\n\nexport interface DialogProps {\n open: boolean;\n /** Called for Escape, a backdrop click, and the close control. */\n onClose: () => void;\n title?: ReactNode;\n children?: ReactNode;\n /** Action row, right-aligned below the body. */\n footer?: ReactNode;\n /**\n * Escape and backdrop clicks do nothing — the only way out is an action in\n * the footer. For a state the user must actually resolve rather than dismiss,\n * such as a sale whose outcome is unknown. Use it sparingly: a dialog that\n * cannot be dismissed is a dead end if its actions ever fail to render.\n */\n blocking?: boolean;\n size?: DialogSize;\n /**\n * Element to focus on open. Defaults to the first focusable thing in the\n * dialog — which for a destructive confirm should be Cancel, not the button\n * that does the irreversible thing.\n */\n initialFocus?: RefObject<HTMLElement | null>;\n /**\n * Names the dialog when it carries no visible `title`. Without a name it is\n * announced as just \"dialog\", which tells a screen reader user nothing about\n * what opened. Ignored when `title` is set — a visible label always wins over\n * a parallel invisible one, or the two drift.\n */\n 'aria-label'?: string;\n className?: string;\n}\n\nconst SIZES: Record<DialogSize, string> = {\n sm: 'max-w-sm',\n md: 'max-w-md',\n lg: 'max-w-lg',\n};\n\nexport default function Dialog({\n open, onClose, title, children, footer, blocking = false, size = 'md', initialFocus,\n 'aria-label': ariaLabel, className = '',\n}: DialogProps) {\n const panelRef = useRef<HTMLDivElement>(null);\n const bodyId = useId();\n // One source of truth for the close control: the button and the title's\n // gutter are the same decision, and computing it twice is how they drift.\n const showClose = !blocking && !footer;\n const titleId = useId();\n\n // Opening closes every popup still open elsewhere: they sit on the popup\n // layer, above this one, and would float over the scrim. A popup inside this\n // dialog is unaffected — it cannot be open yet, and it only subscribes while\n // open (see `overlayEvents.ts`).\n useDismissPopupsOnOpen(open);\n\n useFocusTrap(panelRef, open, initialFocus);\n useScrollLock(open);\n\n // Escape goes through the shell's interceptor rather than a local listener.\n // A dialog floats above every shell window but is absent from the window\n // activation order, so an unclaimed Escape reaches the frontmost Modal and\n // closes the WINDOW BEHIND the dialog. Claiming it here means the dialog\n // closes and the window does not. With no shell present nothing else is\n // listening and this is simply where Escape is handled.\n useEffect(() => {\n if (!open) return;\n return registerModalEscapeInterceptor(() => {\n if (blocking) return true; // claimed, but deliberately does nothing\n onClose();\n return true;\n });\n }, [open, blocking, onClose]);\n\n if (!open) return null;\n\n return (\n <div className=\"fixed inset-0\" style={{ zIndex: Z_LAYERS.overlay }} role=\"presentation\">\n {/* No handler here. The dismissal layer below is also `fixed inset-0`\n and sits on top of this, so a click in this region never reaches the\n scrim — an onClick here would be unreachable in every case, and two\n dismissal paths where only one can fire is how the next reader loses\n an afternoon. */}\n <div className=\"fixed inset-0 bg-black/30\" aria-hidden=\"true\" />\n {/* The scrim above covers the viewport, and so does this — which sits on\n top of it. A click \"outside the dialog\" therefore landed HERE and\n never reached the scrim's handler, so clicking away never dismissed\n anything and a dialog with no buttons could only be left with Escape.\n The dismissal lives on this layer as well, gated on the target being\n the layer itself so a click inside the panel does not bubble into it. */}\n <div\n className=\"fixed inset-0 flex items-center justify-center p-4\"\n onClick={blocking ? undefined : (e => { if (e.target === e.currentTarget) onClose(); })}\n >\n <div\n ref={panelRef}\n role=\"dialog\"\n aria-modal=\"true\"\n // Points at the rendered heading rather than re-deriving the name\n // from the prop: `title` is a ReactNode, so an element title — an\n // icon beside a word, a count in a badge — used to fall through the\n // string check and leave the dialog with no name at all.\n aria-labelledby={title ? titleId : undefined}\n aria-label={title ? undefined : ariaLabel}\n // The body is the dialog's description, and saying so is what makes a\n // screen reader read the question along with the title when focus\n // lands here. Without it the user hears \"Cancel order, dialog\" and\n // has to go looking for what cancelling actually does.\n aria-describedby={children ? bodyId : undefined}\n className={`relative w-full ${SIZES[size]} rounded-lg bg-white p-6 shadow-xl ${className}`.trim()}\n >\n {/* `id` for the accessible name, and a right gutter ONLY when the\n close button is actually there to need it — a dialog with a\n footer draws no cross, and reserving 2rem beside its title just\n indents the heading away from the edge for nothing. */}\n {title && (\n <h2 id={titleId} className={`${showClose ? 'pr-8 ' : ''}text-base font-semibold text-gray-900`}>\n {title}\n </h2>\n )}\n {children && <div id={bodyId} className=\"mt-2 text-sm text-gray-600\">{children}</div>}\n {footer && <div className=\"mt-6 flex justify-end gap-3\">{footer}</div>}\n {/* A way out that is visible, for a dialog that offers none of its\n own. Escape and the backdrop both work, but neither is on screen,\n so a dialog whose body is just an image could be left only by a\n keyboard shortcut nothing announces.\n\n NOT when there is a footer. A confirm already offers two labelled\n choices, and an unlabelled cross beside \"Discard\" and \"Keep\n Editing\" is a third exit that says nothing about which one it\n means — worse on the decision where it matters most. It also put\n a stop in the middle of that dialog's Tab cycle, which is the\n thing the browser test guards.\n\n LAST in the DOM though drawn top-right: first, it became the\n dialog's first tab stop and the first button a focus trap finds. */}\n {showClose && (\n <button\n type=\"button\"\n onClick={onClose}\n aria-label=\"Close\"\n className=\"absolute right-3 top-3 rounded-md p-1 text-gray-400 hover:bg-gray-100 hover:text-gray-600 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-400\"\n >\n <svg className=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" aria-hidden=\"true\">\n <path d=\"M6 6l8 8M14 6l-8 8\" />\n </svg>\n </button>\n )}\n </div>\n </div>\n </div>\n );\n}\n","/**\n * confirm / confirmDestructive / prompt — the imperative dialogs, callable from\n * any click handler without a hook.\n *\n * Rebuilt on `./Dialog` in 4.18.0. It previously rendered Headless UI, which\n * meant every consumer of `react-os-shell/ui` inherited `@headlessui/react` and\n * `@heroicons/react` to ask someone a yes/no question. Focus containment and\n * scroll locking now come from `./focusTrap`; the icons are inline SVG. The\n * three dialogs' behaviour is otherwise unchanged, with two deliberate\n * exceptions noted below.\n *\n * FAIL-CLOSED. Each global starts as a function resolving to false/null, so a\n * `confirm()` with no provider mounted answers \"no\" rather than hanging or\n * throwing. A dialog nobody can see must never resolve true — that is the\n * difference between a no-op and a deletion.\n *\n * CONFIRMS QUEUE, they do not drop. A second `confirm()` while one is open\n * waits its turn and shows afterwards. Dropping it would silently resolve a\n * question the user was never asked, and the caller cannot tell that from a\n * genuine \"no\".\n */\nimport { createContext, useContext, useState, useCallback, useRef, useEffect } from 'react';\nimport Dialog from './Dialog';\n\ninterface ConfirmOptions {\n title?: string;\n message: string;\n confirmLabel?: string;\n cancelLabel?: string;\n variant?: 'danger' | 'warning' | 'info';\n}\n\ntype ConfirmFn = (options: ConfirmOptions | string) => Promise<boolean>;\ninterface PendingConfirm {\n id: number;\n options: ConfirmOptions;\n resolve: (value: boolean) => void;\n}\n\nconst ConfirmContext = createContext<ConfirmFn>(() => Promise.resolve(false));\n\nexport const useConfirm = () => useContext(ConfirmContext);\n\n// Global callable — works without a hook, usable from any click handler\nlet globalConfirmFn: ConfirmFn = () => Promise.resolve(false);\nexport const confirm = (opts: ConfirmOptions | string) => globalConfirmFn(opts);\n\ninterface DestructiveConfirmOptions {\n title?: string;\n message: string;\n /**\n * Require typing this word (case-sensitive) before the action is enabled —\n * for something genuinely irreversible at scale, like dropping a tenant.\n *\n * OPTIONAL since 4.18.0. Omit it for a plain two-button destructive confirm.\n * Type-to-confirm assumes a keyboard, and a touch device that has none (a\n * till, a warehouse scanner) cannot satisfy it at all — the dialog becomes an\n * unanswerable question. Ask for a word when the cost of a mis-tap is high\n * enough to justify making the user work; not by default.\n */\n confirmWord?: string;\n /** Label for the action button when there is no `confirmWord`. */\n confirmLabel?: string;\n cancelLabel?: string;\n variant?: 'danger' | 'warning';\n}\ntype DestructiveConfirmFn = (options: DestructiveConfirmOptions) => Promise<boolean>;\nlet globalDestructiveConfirmFn: DestructiveConfirmFn = () => Promise.resolve(false);\nexport const confirmDestructive = (opts: DestructiveConfirmOptions) => globalDestructiveConfirmFn(opts);\n\n// Prompt — windowed replacement for native window.prompt(). Resolves to the\n// trimmed string, or null if the user cancelled. Empty input counts as\n// cancel by default; pass `allowEmpty: true` to opt in.\ninterface PromptOptions {\n title?: string;\n message?: string;\n defaultValue?: string;\n placeholder?: string;\n confirmLabel?: string;\n cancelLabel?: string;\n allowEmpty?: boolean;\n}\ntype PromptFn = (options: PromptOptions | string) => Promise<string | null>;\nlet globalPromptFn: PromptFn = () => Promise.resolve(null);\nexport const prompt = (opts: PromptOptions | string) => globalPromptFn(opts);\n\nconst TONE: Record<'danger' | 'warning' | 'info', { icon: string; button: string }> = {\n danger: { icon: 'text-red-600 bg-red-100', button: 'bg-red-600 hover:bg-red-700 text-white' },\n warning: { icon: 'text-yellow-600 bg-yellow-100', button: 'bg-yellow-600 hover:bg-yellow-700 text-white' },\n info: { icon: 'text-blue-600 bg-blue-100', button: 'bg-blue-600 hover:bg-blue-700 text-white' },\n};\n\nconst CANCEL_BTN =\n 'bg-white text-gray-700 border border-gray-300 px-4 py-2 text-sm font-medium rounded-lg hover:bg-gray-50';\n\n/** Inline replacements for the two Heroicons this file used to import. */\nfunction WarningIcon() {\n return (\n <svg className=\"h-5 w-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.5} aria-hidden=\"true\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z\" />\n </svg>\n );\n}\n\nfunction CloseIcon() {\n return (\n <svg className=\"h-5 w-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.5} aria-hidden=\"true\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n );\n}\n\nexport function ConfirmProvider({ children }: { children: React.ReactNode }) {\n const [open, setOpen] = useState(false);\n const [options, setOptions] = useState<ConfirmOptions>({ message: '' });\n const [confirmId, setConfirmId] = useState(0);\n const resolveRef = useRef<(value: boolean) => void>();\n const activeConfirmIdRef = useRef<number | null>(null);\n const nextConfirmIdRef = useRef(0);\n const confirmQueueRef = useRef<PendingConfirm[]>([]);\n\n // Cancel is the default focus target in every dialog here. The destructive\n // action sits on the RIGHT and is never what Enter or a stray keypress hits.\n const cancelRef = useRef<HTMLButtonElement>(null);\n const dCancelRef = useRef<HTMLButtonElement>(null);\n\n const showConfirm = useCallback((request: PendingConfirm) => {\n activeConfirmIdRef.current = request.id;\n resolveRef.current = request.resolve;\n setConfirmId(request.id);\n setOptions(request.options);\n setOpen(true);\n }, []);\n\n const confirmFn: ConfirmFn = useCallback((opts) => {\n const normalized = typeof opts === 'string' ? { message: opts } : opts;\n return new Promise<boolean>((resolve) => {\n const request = { id: ++nextConfirmIdRef.current, options: normalized, resolve };\n if (activeConfirmIdRef.current === null) showConfirm(request);\n else confirmQueueRef.current.push(request);\n });\n }, [showConfirm]);\n\n // Destructive confirm state\n const [dOpen, setDOpen] = useState(false);\n const [dOptions, setDOptions] = useState<DestructiveConfirmOptions>({ message: '' });\n const [dInput, setDInput] = useState('');\n const dResolveRef = useRef<(value: boolean) => void>();\n\n const destructiveConfirmFn: DestructiveConfirmFn = useCallback((opts) => {\n setDOptions(opts);\n setDInput('');\n setDOpen(true);\n return new Promise<boolean>((resolve) => {\n dResolveRef.current = resolve;\n });\n }, []);\n\n // Prompt state\n const [pOpen, setPOpen] = useState(false);\n const [pOptions, setPOptions] = useState<PromptOptions>({});\n const [pInput, setPInput] = useState('');\n const pResolveRef = useRef<(value: string | null) => void>();\n\n const promptFn: PromptFn = useCallback((opts) => {\n const normalized = typeof opts === 'string' ? { message: opts } : opts;\n setPOptions(normalized);\n setPInput(normalized.defaultValue ?? '');\n setPOpen(true);\n return new Promise<string | null>((resolve) => {\n pResolveRef.current = resolve;\n });\n }, []);\n\n useEffect(() => {\n globalConfirmFn = confirmFn;\n globalDestructiveConfirmFn = destructiveConfirmFn;\n globalPromptFn = promptFn;\n }, [confirmFn, destructiveConfirmFn, promptFn]);\n\n const handleClose = (id: number, result: boolean) => {\n // A callback captured by the previous dialog must never settle the next\n // queued request after the active request advances.\n if (activeConfirmIdRef.current !== id) return;\n const resolve = resolveRef.current;\n const next = confirmQueueRef.current.shift();\n if (next) showConfirm(next);\n else {\n activeConfirmIdRef.current = null;\n resolveRef.current = undefined;\n setOpen(false);\n }\n resolve?.(result);\n };\n\n const handleDClose = (result: boolean) => {\n setDOpen(false);\n setDInput('');\n dResolveRef.current?.(result);\n };\n\n const handlePClose = (commit: boolean) => {\n if (commit) {\n const trimmed = pInput.trim();\n if (!trimmed && !pOptions.allowEmpty) {\n // Empty + not opted-in to allow empty: treat Save as Cancel.\n setPOpen(false);\n pResolveRef.current?.(null);\n return;\n }\n setPOpen(false);\n pResolveRef.current?.(trimmed);\n } else {\n setPOpen(false);\n pResolveRef.current?.(null);\n }\n };\n\n // Escape is claimed by each Dialog itself now, and interceptors run most\n // recent first, so a stack of these dismisses top-down without the provider\n // hand-ordering them the way it used to.\n\n const variant = options.variant || (options.confirmLabel?.toLowerCase().includes('delete') || options.message.toLowerCase().includes('delete') ? 'danger' : 'info');\n const tone = TONE[variant];\n const dTone = TONE[dOptions.variant === 'warning' ? 'warning' : 'danger'];\n const dWord = dOptions.confirmWord;\n const dSatisfied = dWord == null || dInput === dWord;\n\n return (\n <ConfirmContext.Provider value={confirmFn}>\n {children}\n\n <Dialog\n open={open}\n onClose={() => handleClose(confirmId, false)}\n initialFocus={cancelRef}\n footer={\n <>\n <button ref={cancelRef} type=\"button\" onClick={() => handleClose(confirmId, false)} className={CANCEL_BTN}>\n {options.cancelLabel || 'Cancel'}\n </button>\n <button type=\"button\" onClick={() => handleClose(confirmId, true)}\n className={`px-4 py-2 text-sm font-medium rounded-lg ${tone.button}`}>\n {options.confirmLabel || 'OK'}\n </button>\n </>\n }\n >\n <div className=\"flex gap-4\">\n <div className={`shrink-0 w-10 h-10 rounded-full flex items-center justify-center ${tone.icon}`}>\n <WarningIcon />\n </div>\n <div className=\"flex-1\">\n <h2 className=\"text-base font-semibold text-gray-900\">{options.title || 'Confirm'}</h2>\n <p className=\"mt-2 text-sm text-gray-600\">{options.message}</p>\n </div>\n </div>\n </Dialog>\n\n {/* Destructive confirm — optionally gated on typing a word */}\n <Dialog\n open={dOpen}\n onClose={() => handleDClose(false)}\n // With a word to type, focus the input: that is the task. Without one,\n // focus Cancel, so the irreversible button is never one Enter away.\n initialFocus={dWord != null ? undefined : dCancelRef}\n footer={\n <>\n <button ref={dCancelRef} type=\"button\" onClick={() => handleDClose(false)} className={CANCEL_BTN}>\n {dOptions.cancelLabel || 'Dismiss'}\n </button>\n <button type=\"button\" onClick={() => handleDClose(true)} disabled={!dSatisfied}\n className={`px-4 py-2 text-sm font-medium rounded-lg disabled:opacity-40 ${dTone.button}`}>\n {dWord ?? dOptions.confirmLabel ?? 'Delete'}\n </button>\n </>\n }\n >\n <button\n type=\"button\"\n onClick={() => handleDClose(false)}\n aria-label=\"Close\"\n className=\"absolute right-4 top-4 rounded-md p-1 text-gray-400 hover:bg-gray-100 hover:text-gray-600\"\n >\n <CloseIcon />\n </button>\n <div className=\"flex gap-4\">\n <div className={`shrink-0 w-10 h-10 rounded-full flex items-center justify-center ${dTone.icon}`}>\n <WarningIcon />\n </div>\n <div className=\"flex-1\">\n <h2 className=\"text-base font-semibold text-gray-900 pr-6\">{dOptions.title || 'Confirm Action'}</h2>\n <p className=\"mt-2 text-sm text-gray-600\">{dOptions.message}</p>\n {dWord != null && (\n <>\n <p className=\"mt-3 text-sm text-gray-700\">\n Type <kbd className=\"rounded border border-gray-300 bg-gray-50 px-1.5 py-0.5 text-xs font-bold text-red-600 font-mono\">{dWord}</kbd> to confirm:\n </p>\n <input\n autoFocus\n type=\"text\"\n value={dInput}\n onChange={e => setDInput(e.target.value)}\n onKeyDown={e => { if (e.key === 'Enter' && dInput === dWord) handleDClose(true); }}\n placeholder={dWord}\n className=\"mt-2 w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-red-500 focus:ring-red-500\"\n />\n </>\n )}\n </div>\n </div>\n </Dialog>\n\n {/* Prompt — windowed replacement for window.prompt() */}\n <Dialog\n open={pOpen}\n onClose={() => handlePClose(false)}\n title={pOptions.title || 'Enter a value'}\n footer={\n <>\n <button type=\"button\" onClick={() => handlePClose(false)} className={CANCEL_BTN}>\n {pOptions.cancelLabel || 'Cancel'}\n </button>\n <button type=\"button\" onClick={() => handlePClose(true)}\n disabled={!pOptions.allowEmpty && !pInput.trim()}\n className=\"bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 text-sm font-medium rounded-lg disabled:opacity-40\">\n {pOptions.confirmLabel || 'OK'}\n </button>\n </>\n }\n >\n {pOptions.message && <p className=\"text-sm text-gray-600\">{pOptions.message}</p>}\n <input\n autoFocus\n type=\"text\"\n value={pInput}\n onChange={(e) => setPInput(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') handlePClose(true);\n else if (e.key === 'Escape') handlePClose(false);\n }}\n onFocus={(e) => e.target.select()}\n placeholder={pOptions.placeholder}\n className=\"mt-3 w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 focus:outline-none\"\n />\n </Dialog>\n </ConfirmContext.Provider>\n );\n}\n"]}
|