efficient-shell 0.0.0 → 4.126.3
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/LICENSE +21 -0
- package/README.md +758 -3
- package/dist/Browser-LM4RRZIL.js +478 -0
- package/dist/Browser-LM4RRZIL.js.map +1 -0
- package/dist/Calculator-HCKFTSJR.js +188 -0
- package/dist/Calculator-HCKFTSJR.js.map +1 -0
- package/dist/ConfirmDialog-LJXMB45A.js +3 -0
- package/dist/ConfirmDialog-LJXMB45A.js.map +1 -0
- package/dist/CurrencyConverter-BKKORN6K.js +232 -0
- package/dist/CurrencyConverter-BKKORN6K.js.map +1 -0
- package/dist/Documents-DD3QI5ER.js +437 -0
- package/dist/Documents-DD3QI5ER.js.map +1 -0
- package/dist/Files-BOPCPMJE.js +801 -0
- package/dist/Files-BOPCPMJE.js.map +1 -0
- package/dist/Notepad-KJOLF63O.js +406 -0
- package/dist/Notepad-KJOLF63O.js.map +1 -0
- package/dist/PdfViewer-LSP3YNFC.js +4 -0
- package/dist/PdfViewer-LSP3YNFC.js.map +1 -0
- package/dist/PomodoroTimer-SVJ62RXM.js +629 -0
- package/dist/PomodoroTimer-SVJ62RXM.js.map +1 -0
- package/dist/Preview-5UOC7ZRU.js +2955 -0
- package/dist/Preview-5UOC7ZRU.js.map +1 -0
- package/dist/Sidebar-J76PV4QF.js +290 -0
- package/dist/Sidebar-J76PV4QF.js.map +1 -0
- package/dist/Spreadsheet-J7BH7UHC.js +515 -0
- package/dist/Spreadsheet-J7BH7UHC.js.map +1 -0
- package/dist/Stock-CDUS4VCZ.js +154 -0
- package/dist/Stock-CDUS4VCZ.js.map +1 -0
- package/dist/Weather-EEDBWBPQ.js +427 -0
- package/dist/Weather-EEDBWBPQ.js.map +1 -0
- package/dist/WorldClock-FFFULVTJ.js +198 -0
- package/dist/WorldClock-FFFULVTJ.js.map +1 -0
- package/dist/apps/index.d.ts +267 -0
- package/dist/apps/index.js +147 -0
- package/dist/apps/index.js.map +1 -0
- package/dist/brand.css +2 -0
- package/dist/chunk-2VM2FRZJ.js +215 -0
- package/dist/chunk-2VM2FRZJ.js.map +1 -0
- package/dist/chunk-32W6VY2J.js +549 -0
- package/dist/chunk-32W6VY2J.js.map +1 -0
- package/dist/chunk-3CUSBZWG.js +59 -0
- package/dist/chunk-3CUSBZWG.js.map +1 -0
- package/dist/chunk-3L463T5H.js +26 -0
- package/dist/chunk-3L463T5H.js.map +1 -0
- package/dist/chunk-4TVV2BIU.js +13334 -0
- package/dist/chunk-4TVV2BIU.js.map +1 -0
- package/dist/chunk-6XQHH7D2.js +105 -0
- package/dist/chunk-6XQHH7D2.js.map +1 -0
- package/dist/chunk-7CCHEEYC.js +94 -0
- package/dist/chunk-7CCHEEYC.js.map +1 -0
- package/dist/chunk-7KIOEKHS.js +617 -0
- package/dist/chunk-7KIOEKHS.js.map +1 -0
- package/dist/chunk-AXWC3332.js +1103 -0
- package/dist/chunk-AXWC3332.js.map +1 -0
- package/dist/chunk-BFBWLCO6.js +16 -0
- package/dist/chunk-BFBWLCO6.js.map +1 -0
- package/dist/chunk-CT3HVZRZ.js +26 -0
- package/dist/chunk-CT3HVZRZ.js.map +1 -0
- package/dist/chunk-D7PYW2QS.js +265 -0
- package/dist/chunk-D7PYW2QS.js.map +1 -0
- package/dist/chunk-DMCFOWOJ.js +153 -0
- package/dist/chunk-DMCFOWOJ.js.map +1 -0
- package/dist/chunk-FQB24B42.js +416 -0
- package/dist/chunk-FQB24B42.js.map +1 -0
- package/dist/chunk-GA35NEND.js +56 -0
- package/dist/chunk-GA35NEND.js.map +1 -0
- package/dist/chunk-HNH7FOYB.js +69 -0
- package/dist/chunk-HNH7FOYB.js.map +1 -0
- package/dist/chunk-HYZ5QMVO.js +462 -0
- package/dist/chunk-HYZ5QMVO.js.map +1 -0
- package/dist/chunk-I464GYJJ.js +17 -0
- package/dist/chunk-I464GYJJ.js.map +1 -0
- package/dist/chunk-JFQGPY7W.js +819 -0
- package/dist/chunk-JFQGPY7W.js.map +1 -0
- package/dist/chunk-MGGY74RY.js +14 -0
- package/dist/chunk-MGGY74RY.js.map +1 -0
- package/dist/chunk-N3CG5HG5.js +144 -0
- package/dist/chunk-N3CG5HG5.js.map +1 -0
- package/dist/chunk-QRGFTK2X.js +79 -0
- package/dist/chunk-QRGFTK2X.js.map +1 -0
- package/dist/chunk-SZ4WMHUX.js +7 -0
- package/dist/chunk-SZ4WMHUX.js.map +1 -0
- package/dist/chunk-W6XTG23K.js +3708 -0
- package/dist/chunk-W6XTG23K.js.map +1 -0
- package/dist/chunk-YG45DAUT.js +96 -0
- package/dist/chunk-YG45DAUT.js.map +1 -0
- package/dist/chunk-YNGPTOL6.js +46 -0
- package/dist/chunk-YNGPTOL6.js.map +1 -0
- package/dist/chunk-YNHLQ24V.js +400 -0
- package/dist/chunk-YNHLQ24V.js.map +1 -0
- package/dist/chunk-YNKK7NE3.js +171 -0
- package/dist/chunk-YNKK7NE3.js.map +1 -0
- package/dist/chunk-YPO6RDMX.js +20 -0
- package/dist/chunk-YPO6RDMX.js.map +1 -0
- package/dist/file-intake/index.d.ts +87 -0
- package/dist/file-intake/index.js +3 -0
- package/dist/file-intake/index.js.map +1 -0
- package/dist/index-dQia6NpB.d.ts +5904 -0
- package/dist/index.d.ts +2495 -0
- package/dist/index.js +7460 -0
- package/dist/index.js.map +1 -0
- package/dist/markdown/index.d.ts +118 -0
- package/dist/markdown/index.js +184 -0
- package/dist/markdown/index.js.map +1 -0
- package/dist/markup/index.d.ts +235 -0
- package/dist/markup/index.js +135 -0
- package/dist/markup/index.js.map +1 -0
- package/dist/page-tabs/index.d.ts +154 -0
- package/dist/page-tabs/index.js +706 -0
- package/dist/page-tabs/index.js.map +1 -0
- package/dist/shell.css +105 -0
- package/dist/sounds-NT4DEZGD.js +3 -0
- package/dist/sounds-NT4DEZGD.js.map +1 -0
- package/dist/styles.css +22 -0
- package/dist/themes.css +249 -0
- package/dist/types-BOinJXUf.d.ts +118 -0
- package/dist/ui/index.d.ts +4 -0
- package/dist/ui/index.js +15 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui.css +1518 -0
- package/package.json +172 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/apps/_wasmBytes.ts","../src/apps/_panelActions.tsx","../src/apps/PdfViewer.tsx"],"names":["jsx"],"mappings":"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","file":"chunk-FQB24B42.js","sourcesContent":["/**\n * Is what came back over the wire actually a WebAssembly module?\n *\n * `Preview`'s wasm factory used to accept any `res.ok` response. That is not\n * enough on a single-page app: an SPA host answers a path its router does not\n * recognise with its own index.html under HTTP 200, so a build that failed to\n * emit a decoder returns a PAGE rather than a 404 (confirmed against the live\n * console on 2026-08-30 — both `/assets/jbig2.wasm` and a made-up asset path\n * answered 200 with `content-type: text/html`). pdf.js then instantiates the\n * page inside a `try` that only warns before dropping to its JS fallback, which\n * is the same quiet degrading the factory was added to remove — except now\n * nothing in the console points at the emit as the cause.\n *\n * This module is deliberately free of React and of pdfjs-dist so it can be\n * driven directly from a spec. Importing `Preview.tsx` in a Node test is not\n * possible: pdfjs builds a `new DOMMatrix()` at module scope and throws.\n */\n\n/** Every WebAssembly module opens with the four bytes `\\0asm`. Anything else —\n * an HTML fallback page, most often — is not one. */\nexport function hasWasmPreamble(bytes: Uint8Array): boolean {\n return (\n bytes.length >= 4 &&\n bytes[0] === 0x00 &&\n bytes[1] === 0x61 &&\n bytes[2] === 0x73 &&\n bytes[3] === 0x6d\n );\n}\n\n/** A short, safe description of what arrived instead, for the error message.\n * The opening bytes of an HTML page name the problem far better than a length\n * does, so they are worth quoting; the cap keeps a stray binary out of the\n * console. */\nexport function describeNonWasm(bytes: Uint8Array): string {\n if (bytes.length === 0) return 'an empty response';\n const head = Array.from(bytes.subarray(0, 24))\n .map(b => (b >= 0x20 && b < 0x7f ? String.fromCharCode(b) : '.'))\n .join('');\n return `${bytes.length} bytes starting \"${head}\"`;\n}\n\n/** The message a developer reads when the emit went missing. It has to name the\n * emit, because the symptom they are looking at is a preview that renders\n * everything except the scanned page, with a silent console. */\nexport function wasmBytesError(href: string, filename: string, bytes: Uint8Array): Error {\n return new Error(\n `The response for ${href} is not a WebAssembly module (got ` +\n `${describeNonWasm(bytes)}). The bundler did not emit \"${filename}\" as ` +\n `an asset, and the host answered with a fallback page instead of a 404. ` +\n `Serve pdfjs-dist/wasm/ yourself and point ` +\n `window.__REACT_OS_SHELL_PDF_WASM__ at it.`,\n );\n}\n","/**\n * Where a format panel's toolbar buttons go.\n *\n * Inside the Preview window there is one toolbar row — Open, the filename,\n * then a slot at its right end — and each panel (PDF, DXF, 3D, image) portals\n * its own page nav / zoom / layer / download buttons into that slot rather\n * than stacking a second row under it. `ToolbarSlotContext` carries the slot\n * element down.\n *\n * Outside Preview there is no slot, and there is no outer toolbar to merge\n * with either: an embedded `PdfViewer` is the whole widget. So the fallback\n * renders the buttons in place as the panel's own toolbar. It used to return\n * `null`, which was right while every panel lived in a window and wrong the\n * moment one did not — an embedded viewer with no page nav and no zoom.\n */\nimport { createContext, useContext } from 'react';\nimport { createPortal } from 'react-dom';\n\nexport const ToolbarSlotContext = createContext<HTMLElement | null>(null);\n\nexport function PanelActions({ children }: { children: React.ReactNode }) {\n const slot = useContext(ToolbarSlotContext);\n if (slot) return createPortal(children, slot);\n return (\n <div className=\"flex shrink-0 items-center gap-1 border-b border-gray-200 bg-gray-50 px-3 py-2 text-xs\">\n {children}\n </div>\n );\n}\n","/**\n * PdfViewer — the pdf.js reader, on its own so it can be embedded.\n *\n * It was the Preview window's private `PdfPanel` until a consumer needed a PDF\n * inside a dialog rather than in a window of its own. The two consumers that\n * needed one had each reached for `<iframe src={blobUrl}>`, which hands the\n * page to the BROWSER's built-in plugin: its own toolbar, a ~250px thumbnail\n * rail, and its own idea of the zoom — roughly a third of an already small\n * pane spent on chrome nobody asked for, and a portrait page cropped at the\n * fold. Nothing about that is themeable or testable, and it is not this\n * package's viewer.\n *\n * Import it lazily (`react-os-shell/apps` exports it wrapped in `lazy`, like\n * the other document apps) and render it inside a `<Suspense>`: the static\n * `pdfjs-dist` import below must not reach a host's startup bundle. Give it a\n * parent with a resolved height — it fills its container and scrolls inside.\n */\nimport { useState, useEffect, useRef } from 'react';\nimport * as pdfjsLib from 'pdfjs-dist';\nimport toast from '../shell/toast';\nimport { hasWasmPreamble, wasmBytesError } from './_wasmBytes';\nimport { PanelActions } from './_panelActions';\n\n// Default the worker to the matching unpkg build (mirrors the consumer's\n// installed npm version exactly). Consumers can override by setting\n// pdfjsLib.GlobalWorkerOptions.workerSrc themselves before this module loads.\nif (typeof window !== 'undefined' && !pdfjsLib.GlobalWorkerOptions.workerSrc) {\n pdfjsLib.GlobalWorkerOptions.workerSrc =\n `https://unpkg.com/pdfjs-dist@${pdfjsLib.version}/build/pdf.worker.min.mjs`;\n}\n\n// pdf.js does not ship its JBIG2 and JPEG 2000 decoders as JavaScript any\n// more: both are WebAssembly modules it loads at decode time, and it will only\n// load them from a location the caller supplies. With neither `wasmUrl` nor a\n// `BinaryDataFactory` configured — this package's state until now — the\n// decoder asks for one, pdf.js's own base factory throws \"Ensure that the\n// `wasmUrl` API parameter is provided\", and the worker swallows that as a\n// `warn()`. The page still renders; the scanned or JPEG-2000 image on it is\n// just missing. Nothing reaches the user, which is why it went unnoticed.\n//\n// The fix follows the mechanism the admin portal already uses for the pdf\n// WORKER (`import ... from 'pdfjs-dist/build/pdf.worker.min.mjs?url'`, which\n// Vite emits as /assets/pdf.worker.min-<hash>.mjs): let the CONSUMER'S bundler\n// emit the file and hand pdf.js the URL it produced. `new URL(spec,\n// import.meta.url)` is the bundler-neutral spelling of that — Vite resolves\n// the bare specifier and emits the binary as its own hashed asset (verified on\n// Vite 5 and 7), and a bundler that does not understand the form leaves it\n// alone and lands back on today's warn-and-degrade rather than on an error.\n//\n// The specifier points at the CONSUMER'S installed `pdfjs-dist`, not at a copy\n// vendored into this package. That is deliberate: these binaries are built\n// alongside the worker that instantiates them, and a vendored copy would pin\n// them to THIS package's devDependency instead of to the peer they actually\n// run against.\nconst PDF_WASM_MODULES: Record<string, string> = {\n 'jbig2.wasm': new URL('pdfjs-dist/wasm/jbig2.wasm', import.meta.url).href,\n 'openjpeg.wasm': new URL('pdfjs-dist/wasm/openjpeg.wasm', import.meta.url).href,\n};\n\n/** pdf.js instantiates this itself (it is passed to `getDocument` as a class,\n * not an instance) and calls `fetch` once per binary it needs.\n *\n * Only the `wasmUrl` kind is served. cMap and standard-font data are the other\n * two kinds pdf.js can ask for, and this package has never configured a URL\n * for either — so those keep the exact message pdf.js's own base factory\n * raises for an unconfigured URL, and behave as they always have. */\nclass BundledPdfWasmFactory {\n async fetch({ kind, filename }: { kind: string; filename: string }): Promise<Uint8Array> {\n if (kind !== 'wasmUrl') {\n throw new Error(`Ensure that the \\`${kind}\\` API parameter is provided.`);\n }\n const href = PDF_WASM_MODULES[filename];\n if (!href) {\n throw new Error(`No bundled pdf.js wasm module for \"${filename}\".`);\n }\n const res = await fetch(href);\n if (!res.ok) {\n throw new Error(`Unable to load wasm data at: ${href}`);\n }\n // A 200 is not proof the bundler emitted the binary — see _wasmBytes.ts.\n const bytes = new Uint8Array(await res.arrayBuffer());\n if (!hasWasmPreamble(bytes)) {\n throw wasmBytesError(href, filename, bytes);\n }\n return bytes;\n }\n}\n\n/** The `getDocument` parameters that give pdf.js somewhere to load its wasm\n * decoders from.\n *\n * A consumer that serves the whole `pdfjs-dist/wasm/` directory itself (copied\n * into `public/`, say, or on an air-gapped host) points us at it with\n * `window.__REACT_OS_SHELL_PDF_WASM__` — same escape hatch as\n * `__REACT_OS_SHELL_O3DV_LIBS__` below. pdf.js requires a trailing slash on\n * that value and throws \"Invalid factory url\" without one, so add it here\n * rather than making every consumer remember. Everyone else gets the\n * bundler-emitted modules and configures nothing. */\nfunction pdfWasmParams(): { wasmUrl?: string; BinaryDataFactory?: object } {\n const dir = typeof window !== 'undefined'\n ? (window as unknown as { __REACT_OS_SHELL_PDF_WASM__?: unknown }).__REACT_OS_SHELL_PDF_WASM__\n : undefined;\n if (typeof dir === 'string' && dir) {\n return { wasmUrl: dir.endsWith('/') ? dir : `${dir}/` };\n }\n return { BinaryDataFactory: BundledPdfWasmFactory };\n}\n\nexport interface PdfViewerProps {\n /** Object URL or remote URL of the PDF. */\n url: string;\n /** Display name — also the filename the built-in Download uses. */\n filename: string;\n /**\n * What the auto-fit scale tracks until the reader picks a manual zoom.\n *\n * `'width'` fills the container's width and lets a tall page scroll — right\n * for a viewer that owns a whole window, and what the Preview app uses.\n * `'page'` fits the WHOLE page, which is what an embedded preview pane\n * wants: someone checking a document before sending it needs to see that it\n * is the right document, and a page cropped at the fold reads as broken.\n */\n fit?: 'width' | 'page';\n /** Replaces the built-in \"save `url` as `filename`\" download. */\n onDownload?: () => void;\n /** An Email button joins the toolbar only when this is supplied. */\n onEmail?: () => void;\n}\n\nconst ZOOM_PRESETS = [50, 75, 100, 125, 150, 200, 300, 400];\n\n/** The `p-4` gutter the page sits in, on both axes. */\nconst PAGE_GUTTER = 40;\nconst MIN_SCALE = 0.3;\nconst MAX_SCALE = 4;\n\n/**\n * The scale auto-fit lands on.\n *\n * @internal Exported for the spec. The effect that calls it needs a rendered\n * document and a laid-out container to reach, and the wrong answer here does\n * not throw — it crops the page at the fold, which reads as a broken preview\n * rather than as a bug in a formula.\n */\nexport function fitScale(\n fit: 'width' | 'page',\n container: { width: number; height: number },\n page: { width: number; height: number },\n): number {\n const byWidth = (container.width - PAGE_GUTTER) / page.width;\n // Fitting the PAGE means neither axis may overflow, so take whichever\n // constraint binds first. Width alone is what crops a portrait page in a\n // pane wider than it is tall.\n const wanted = fit === 'page'\n ? Math.min(byWidth, (container.height - PAGE_GUTTER) / page.height)\n : byWidth;\n return Math.min(Math.max(wanted, MIN_SCALE), MAX_SCALE);\n}\n\n// Trimmed copy of pdf.js's text-layer rules (pdf_viewer.css), under our own\n// class name so a consumer that loads the full viewer CSS doesn't\n// double-apply. The layer is a sheet of transparent spans positioned over\n// the canvas glyphs — that's what makes the rendered page selectable. Span\n// positions are % of page size; font-size derives from\n// --total-scale-factor, which the render effect sets to the viewport scale.\n// The .endOfContent sentinel plus the .selecting toggle keep a\n// drag-selection alive between lines and past the last line (same trick as\n// pdf.js's own TextLayerBuilder).\nconst TEXT_LAYER_CSS = `\n.preview-pdf-textlayer {\n position: absolute;\n inset: 0;\n overflow: clip;\n line-height: 1;\n text-align: initial;\n text-size-adjust: none;\n forced-color-adjust: none;\n transform-origin: 0 0;\n caret-color: CanvasText;\n --min-font-size: 1;\n --text-scale-factor: calc(var(--total-scale-factor, 1) * var(--min-font-size));\n --min-font-size-inv: calc(1 / var(--min-font-size));\n}\n.preview-pdf-textlayer :is(span, br) {\n color: transparent;\n position: absolute;\n white-space: pre;\n cursor: text;\n transform-origin: 0% 0%;\n}\n.preview-pdf-textlayer > :not(.markedContent),\n.preview-pdf-textlayer .markedContent span:not(.markedContent) {\n z-index: 1;\n --font-height: 0;\n font-size: calc(var(--text-scale-factor) * var(--font-height));\n --scale-x: 1;\n --rotate: 0deg;\n transform: rotate(var(--rotate)) scaleX(var(--scale-x)) scale(var(--min-font-size-inv));\n}\n.preview-pdf-textlayer .markedContent {\n display: contents;\n}\n.preview-pdf-textlayer span[role=\"img\"] {\n user-select: none;\n cursor: default;\n}\n.preview-pdf-textlayer ::selection {\n background: rgba(0, 80, 255, 0.25);\n}\n.preview-pdf-textlayer br::selection {\n background: transparent;\n}\n.preview-pdf-textlayer .endOfContent {\n display: block;\n position: absolute;\n inset: 100% 0 0;\n z-index: 0;\n cursor: default;\n user-select: none;\n}\n.preview-pdf-textlayer.selecting .endOfContent {\n top: 0;\n}\n`;\n\nexport default function PdfViewer({ url, filename, fit = 'width', onDownload, onEmail }: PdfViewerProps) {\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const textLayerRef = useRef<HTMLDivElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const [pdf, setPdf] = useState<pdfjsLib.PDFDocumentProxy | null>(null);\n const [page, setPage] = useState(1);\n const [totalPages, setTotalPages] = useState(0);\n const [scale, setScale] = useState(1);\n // Fit mode is the default — scale stays auto-tracking the container width\n // until the user picks a manual zoom (− / + / dropdown). Re-armed by the\n // Fit toolbar button.\n const [fitMode, setFitMode] = useState(true);\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n let cancelled = false;\n setLoading(true);\n // A new document starts at its first page. The page number belongs to the\n // document being read, not to the viewer, and it used to survive a change\n // of `url`: sitting on page 3 and being handed a one-page document asked\n // pdf.js for a page that does not exist, and the canvas went blank with\n // nothing thrown. The Preview window never saw it because it keys its\n // panels by url, but an embedder that re-renders one viewer with a new\n // document — a preview pane next to the options that regenerate it — is\n // exactly the shape this component was extracted for.\n //\n // Reset before the fetch, not in its `.then`: the manual page controls are\n // live while a document loads, and a reset that landed afterwards would\n // undo a page the reader had already picked.\n //\n // Zoom is deliberately NOT reset. It is the reader's own preference for\n // how large they want text, and it holds across documents the way it does\n // in every reader; only the page is a fact about the file.\n setPage(1);\n // Parameter-object form, not the bare string: pdfjs-dist 6.x removed the\n // string overload (\"expected either `data`, `range`, or `url` parameter\"),\n // so a consumer on 6.x had every preview die on this line. The object form\n // works on 5.x too. tests/pdfjsContract.test.ts fails if it reverts.\n pdfjsLib.getDocument({ url, ...pdfWasmParams() }).promise.then(doc => {\n if (cancelled) return;\n setPdf(doc);\n setTotalPages(doc.numPages);\n setLoading(false);\n }).catch(() => {\n if (!cancelled) { toast.error('Failed to load PDF'); setLoading(false); }\n });\n return () => { cancelled = true; };\n }, [url]);\n\n // Auto-fit: re-compute scale from the container whenever fit mode is on, the\n // document changes, the page changes, or the container resizes. Window\n // resize and Modal drag-resize both trigger ResizeObserver. Manual zoom\n // turns this off until the user clicks Fit again.\n useEffect(() => {\n if (!pdf || !fitMode || !containerRef.current) return;\n const recompute = () => {\n const el = containerRef.current;\n if (!el) return;\n pdf.getPage(page).then(p => {\n if (!containerRef.current) return;\n const viewport = p.getViewport({ scale: 1 });\n const next = fitScale(\n fit,\n { width: el.clientWidth, height: el.clientHeight },\n { width: viewport.width, height: viewport.height },\n );\n setScale(prev => (Math.abs(prev - next) < 0.005 ? prev : next));\n });\n };\n recompute();\n const ro = new ResizeObserver(recompute);\n ro.observe(containerRef.current);\n return () => ro.disconnect();\n }, [pdf, page, fitMode, fit]);\n\n useEffect(() => {\n if (!pdf || !canvasRef.current) return;\n let cancelled = false;\n let task: { cancel: () => void; promise: Promise<void> } | null = null;\n let textLayer: pdfjsLib.TextLayer | null = null;\n pdf.getPage(page).then(p => {\n if (cancelled || !canvasRef.current) return;\n const viewport = p.getViewport({ scale });\n const canvas = canvasRef.current;\n canvas.width = viewport.width;\n canvas.height = viewport.height;\n // Lock the displayed (CSS) size to the viewport. pdf.js stamps inline\n // style.width/height on render and inline values stick across re-renders\n // even when canvas.width shrinks — without this, zoom changes only\n // affect resolution, not visible size.\n canvas.style.width = `${viewport.width}px`;\n canvas.style.height = `${viewport.height}px`;\n const ctx = canvas.getContext('2d')!;\n task = p.render({ canvas, canvasContext: ctx, viewport });\n task.promise.catch(() => {});\n // Selectable text layer — transparent spans over the canvas glyphs.\n // Rebuilt per page/zoom render; pdf.js reads --total-scale-factor for\n // span font-size and the container's own width/height.\n const textEl = textLayerRef.current;\n if (textEl) {\n textEl.replaceChildren();\n textEl.style.setProperty('--total-scale-factor', String(viewport.scale));\n textLayer = new pdfjsLib.TextLayer({\n textContentSource: p.streamTextContent(),\n container: textEl,\n viewport,\n });\n textLayer.render().then(() => {\n if (cancelled) return;\n const end = document.createElement('div');\n end.className = 'endOfContent';\n textEl.append(end);\n }).catch(() => {});\n }\n });\n return () => {\n cancelled = true;\n task?.cancel();\n textLayer?.cancel();\n };\n }, [pdf, page, scale]);\n\n // End a selection drag wherever the pointer is released — the .selecting\n // class is added on pointerdown in the layer itself (see JSX below).\n useEffect(() => {\n const up = () => textLayerRef.current?.classList.remove('selecting');\n window.addEventListener('pointerup', up);\n return () => window.removeEventListener('pointerup', up);\n }, []);\n\n const handlePrint = () => {\n if (!pdf) return;\n const win = window.open('', '_blank');\n if (!win) { toast.error('Allow popups to print'); return; }\n const promises: Promise<string>[] = [];\n for (let i = 1; i <= totalPages; i++) {\n promises.push(pdf.getPage(i).then(p => {\n const vp = p.getViewport({ scale: 2 });\n const c = document.createElement('canvas');\n c.width = vp.width; c.height = vp.height;\n return p.render({ canvas: c, canvasContext: c.getContext('2d')!, viewport: vp }).promise.then(() => c.toDataURL());\n }));\n }\n Promise.all(promises).then(images => {\n win.document.write(`<html><head><title>${filename}</title><style>@media print{body{margin:0}img{width:100%;page-break-after:always}}</style></head><body>`);\n win.document.write(images.map(src => `<img src=\"${src}\"/>`).join(''));\n win.document.write('</body></html>');\n win.document.close();\n setTimeout(() => { win.print(); win.close(); }, 300);\n });\n };\n\n const handleDefaultDownload = () => {\n const a = document.createElement('a');\n a.href = url;\n a.download = filename;\n a.click();\n };\n\n // Re-arm fit mode. The auto-fit effect picks the new scale on its next\n // tick once `fitMode` flips to true.\n const reFit = () => setFitMode(true);\n const fitLabel = fit === 'page' ? 'Fit page' : 'Fit width';\n\n // Wheel and keyboard page navigation. Only takes the wheel event when\n // the container is at the top/bottom edge — otherwise normal scroll\n // moves through a tall page first, matching native PDF readers.\n const onWheelPage = (e: React.WheelEvent<HTMLDivElement>) => {\n if (!pdf) return;\n if (e.ctrlKey || e.metaKey) return; // browser-zoom shortcut, leave alone\n const el = containerRef.current;\n if (!el) return;\n const atTop = el.scrollTop <= 0;\n const atBottom = el.scrollTop + el.clientHeight >= el.scrollHeight - 1;\n if (e.deltaY > 0 && atBottom && page < totalPages) {\n e.preventDefault();\n setPage(p => Math.min(totalPages, p + 1));\n // Land at the top of the next page so wheel-down keeps reading\n // forward. requestAnimationFrame so the canvas has a tick to\n // resize before we scroll.\n requestAnimationFrame(() => { if (containerRef.current) containerRef.current.scrollTop = 0; });\n } else if (e.deltaY < 0 && atTop && page > 1) {\n e.preventDefault();\n setPage(p => Math.max(1, p - 1));\n requestAnimationFrame(() => {\n if (containerRef.current) containerRef.current.scrollTop = containerRef.current.scrollHeight;\n });\n }\n };\n const onKeyPage = (e: React.KeyboardEvent<HTMLDivElement>) => {\n if (!pdf) return;\n // Ignore keyboard nav when an input/textarea has focus.\n const tag = (document.activeElement?.tagName || '').toLowerCase();\n if (tag === 'input' || tag === 'textarea' || (document.activeElement as HTMLElement)?.isContentEditable) return;\n if (e.key === 'PageDown' || e.key === 'ArrowRight') {\n if (page < totalPages) { e.preventDefault(); setPage(p => Math.min(totalPages, p + 1)); }\n } else if (e.key === 'PageUp' || e.key === 'ArrowLeft') {\n if (page > 1) { e.preventDefault(); setPage(p => Math.max(1, p - 1)); }\n } else if (e.key === 'Home') {\n e.preventDefault(); setPage(1);\n } else if (e.key === 'End') {\n e.preventDefault(); setPage(totalPages);\n }\n };\n\n const btn = 'px-2 py-1 rounded hover:bg-gray-200 transition-colors text-gray-600 flex items-center gap-1';\n\n return (\n <div className=\"flex flex-col h-full\">\n <PanelActions>\n <button aria-label=\"Previous page\" onClick={() => setPage(p => Math.max(1, p - 1))} disabled={page <= 1} className=\"px-1 py-1 rounded hover:bg-gray-200 disabled:opacity-30 text-gray-600\">\n <svg className=\"h-3.5 w-3.5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M15.75 19.5L8.25 12l7.5-7.5\" /></svg>\n </button>\n <span className=\"text-gray-600 font-medium tabular-nums\">{page} / {totalPages}</span>\n <button aria-label=\"Next page\" onClick={() => setPage(p => Math.min(totalPages, p + 1))} disabled={page >= totalPages} className=\"px-1 py-1 rounded hover:bg-gray-200 disabled:opacity-30 text-gray-600\">\n <svg className=\"h-3.5 w-3.5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M8.25 4.5l7.5 7.5-7.5 7.5\" /></svg>\n </button>\n <div className=\"h-4 w-px bg-gray-300 mx-1\" />\n <button\n onClick={() => { setFitMode(false); setScale(s => Math.max(0.3, Math.round((s - 0.25) * 100) / 100)); }}\n className={btn}\n >−</button>\n <select\n value={fitMode ? 'fit' : (ZOOM_PRESETS.includes(Math.round(scale * 100)) ? Math.round(scale * 100) : 'custom')}\n onChange={e => {\n const v = e.target.value;\n if (v === 'fit') { setFitMode(true); return; }\n if (v !== 'custom') { setFitMode(false); setScale(Number(v) / 100); }\n }}\n className=\"bg-transparent hover:bg-gray-200 rounded px-1 py-1 text-gray-600 tabular-nums cursor-pointer focus:outline-none focus:ring-1 focus:ring-blue-400\"\n title=\"Zoom\"\n >\n <option value=\"fit\">{fitLabel}</option>\n {!fitMode && !ZOOM_PRESETS.includes(Math.round(scale * 100)) && (\n <option value=\"custom\">{Math.round(scale * 100)}%</option>\n )}\n {ZOOM_PRESETS.map(p => <option key={p} value={p}>{p}%</option>)}\n </select>\n <button\n onClick={() => { setFitMode(false); setScale(s => Math.min(4, Math.round((s + 0.25) * 100) / 100)); }}\n className={btn}\n >+</button>\n <button\n onClick={reFit}\n className={btn + (fitMode ? ' bg-gray-200 text-gray-900' : '')}\n title={`${fitLabel} — auto-tracks the container size until you zoom manually`}\n >Fit</button>\n <div className=\"h-4 w-px bg-gray-300 mx-1\" />\n <button onClick={handlePrint} className={btn}>\n <svg className=\"h-3.5 w-3.5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.5}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M6.72 13.829c-.24.03-.48.062-.72.096m.72-.096a42.415 42.415 0 0110.56 0m-10.56 0L6.34 18m10.94-4.171c.24.03.48.062.72.096m-.72-.096L17.66 18m0 0l.229 2.523a1.125 1.125 0 01-1.12 1.227H7.231c-.662 0-1.18-.568-1.12-1.227L6.34 18m11.318 0h1.091A2.25 2.25 0 0021 15.75V9.456c0-1.081-.768-2.015-1.837-2.175a48.055 48.055 0 00-1.913-.247M6.34 18H5.25A2.25 2.25 0 013 15.75V9.456c0-1.081.768-2.015 1.837-2.175a48.041 48.041 0 011.913-.247m10.5 0a48.536 48.536 0 00-10.5 0m10.5 0V3.375c0-.621-.504-1.125-1.125-1.125h-8.25c-.621 0-1.125.504-1.125 1.125v3.659M18 10.5h.008v.008H18V10.5zm-3 0h.008v.008H15V10.5z\" /></svg>\n Print\n </button>\n <button onClick={onDownload ?? handleDefaultDownload} className={btn}>\n <svg className=\"h-3.5 w-3.5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.5}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5M16.5 12L12 16.5m0 0L7.5 12m4.5 4.5V3\" /></svg>\n Download\n </button>\n {onEmail && (\n <button onClick={onEmail} className={btn}>\n <svg className=\"h-3.5 w-3.5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.5}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M21.75 6.75v10.5a2.25 2.25 0 01-2.25 2.25h-15a2.25 2.25 0 01-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0019.5 4.5h-15a2.25 2.25 0 00-2.25 2.25m19.5 0v.243a2.25 2.25 0 01-1.07 1.916l-7.5 4.615a2.25 2.25 0 01-2.36 0L3.32 8.91a2.25 2.25 0 01-1.07-1.916V6.75\" /></svg>\n Email\n </button>\n )}\n </PanelActions>\n\n <div\n ref={containerRef}\n className=\"flex-1 overflow-auto bg-gray-100 outline-none\"\n // tabIndex makes the scroll container itself focusable so PageUp /\n // PageDown / Arrow keys are captured even when no inner element\n // has focus (e.g. right after the panel mounts). Wheel handler\n // page-flips when the user reaches the top/bottom of a long page.\n tabIndex={0}\n onWheel={onWheelPage}\n onKeyDown={onKeyPage}\n >\n {loading ? (\n <div className=\"flex items-center justify-center h-full text-gray-400 text-sm\">Loading PDF...</div>\n ) : (\n <div className=\"min-h-full flex items-center justify-center p-4\">\n <style>{TEXT_LAYER_CSS}</style>\n <div className=\"relative shadow-lg rounded\">\n <canvas ref={canvasRef} className=\"block rounded\" />\n <div\n ref={textLayerRef}\n className=\"preview-pdf-textlayer\"\n onPointerDown={e => e.currentTarget.classList.add('selecting')}\n />\n </div>\n </div>\n )}\n </div>\n </div>\n );\n}\n"]}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { createContext, useContext } from 'react';
|
|
2
|
+
import { jsx } from 'react/jsx-runtime';
|
|
3
|
+
|
|
4
|
+
// src/shell/nav-types.ts
|
|
5
|
+
function isSection(item) {
|
|
6
|
+
return "items" in item;
|
|
7
|
+
}
|
|
8
|
+
function navVisible(entry, hasAnyPerm) {
|
|
9
|
+
if (entry.perms?.length && !hasAnyPerm(entry.perms)) return false;
|
|
10
|
+
if (entry.allPerms?.length && !entry.allPerms.every((p) => hasAnyPerm([p]))) return false;
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
function visibleChildren(item, hasAnyPerm) {
|
|
14
|
+
return (item.children ?? []).filter((c) => navVisible(c, hasAnyPerm));
|
|
15
|
+
}
|
|
16
|
+
function isReachable(item, hasAnyPerm) {
|
|
17
|
+
if (!item.children?.length) return true;
|
|
18
|
+
return visibleChildren(item, hasAnyPerm).some((child) => isReachable(child, hasAnyPerm));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// src/shell-config/nav.tsx
|
|
22
|
+
var _navIcons = {};
|
|
23
|
+
var navIcons = new Proxy(_navIcons, {
|
|
24
|
+
get(_t, k) {
|
|
25
|
+
return _navIcons[k];
|
|
26
|
+
},
|
|
27
|
+
has(_t, k) {
|
|
28
|
+
return k in _navIcons;
|
|
29
|
+
},
|
|
30
|
+
ownKeys() {
|
|
31
|
+
return Object.keys(_navIcons);
|
|
32
|
+
},
|
|
33
|
+
getOwnPropertyDescriptor(_t, k) {
|
|
34
|
+
if (k in _navIcons) return { configurable: true, enumerable: true, value: _navIcons[k] };
|
|
35
|
+
return void 0;
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
function setShellNavIcons(icons) {
|
|
39
|
+
for (const k of Object.keys(_navIcons)) delete _navIcons[k];
|
|
40
|
+
Object.assign(_navIcons, icons);
|
|
41
|
+
}
|
|
42
|
+
var sectionIcons = {};
|
|
43
|
+
var navSections = [];
|
|
44
|
+
var startMenuCategories = { erp: [], system: [], virtual: [], footer: [] };
|
|
45
|
+
var DEFAULT = { hasAnyPerm: () => true };
|
|
46
|
+
var ShellAuthContext = createContext(DEFAULT);
|
|
47
|
+
function ShellAuthProvider({ value, children }) {
|
|
48
|
+
return /* @__PURE__ */ jsx(ShellAuthContext.Provider, { value, children });
|
|
49
|
+
}
|
|
50
|
+
function useShellAuth() {
|
|
51
|
+
return useContext(ShellAuthContext);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export { ShellAuthProvider, isReachable, isSection, navIcons, navSections, navVisible, sectionIcons, setShellNavIcons, startMenuCategories, useShellAuth, visibleChildren };
|
|
55
|
+
//# sourceMappingURL=chunk-GA35NEND.js.map
|
|
56
|
+
//# sourceMappingURL=chunk-GA35NEND.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shell/nav-types.ts","../src/shell-config/nav.tsx","../src/shell/ShellAuth.tsx"],"names":[],"mappings":";;;;AA6EO,SAAS,UAAU,IAAA,EAAgD;AACxE,EAAA,OAAO,OAAA,IAAW,IAAA;AACpB;AAoBO,SAAS,UAAA,CACd,OACA,UAAA,EACS;AACT,EAAA,IAAI,KAAA,CAAM,OAAO,MAAA,IAAU,CAAC,WAAW,KAAA,CAAM,KAAK,GAAG,OAAO,KAAA;AAC5D,EAAA,IAAI,KAAA,CAAM,QAAA,EAAU,MAAA,IAAU,CAAC,MAAM,QAAA,CAAS,KAAA,CAAM,CAAA,CAAA,KAAK,UAAA,CAAW,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,KAAA;AAClF,EAAA,OAAO,IAAA;AACT;AAWO,SAAS,eAAA,CAAgB,MAAe,UAAA,EAAmC;AAChF,EAAA,OAAA,CAAQ,IAAA,CAAK,YAAY,EAAC,EAAG,OAAO,CAAA,CAAA,KAAK,UAAA,CAAW,CAAA,EAAG,UAAU,CAAC,CAAA;AACpE;AAcO,SAAS,WAAA,CAAY,MAAe,UAAA,EAAiC;AAC1E,EAAA,IAAI,CAAC,IAAA,CAAK,QAAA,EAAU,MAAA,EAAQ,OAAO,IAAA;AACnC,EAAA,OAAO,eAAA,CAAgB,MAAM,UAAU,CAAA,CAAE,KAAK,CAAA,KAAA,KAAS,WAAA,CAAY,KAAA,EAAO,UAAU,CAAC,CAAA;AACvF;;;ACxHA,IAAM,YAAuC,EAAC;AACvC,IAAM,QAAA,GAAsC,IAAI,KAAA,CAAM,SAAA,EAAW;AAAA,EACtE,GAAA,CAAI,IAAI,CAAA,EAAW;AAAE,IAAA,OAAO,UAAU,CAAC,CAAA;AAAA,EAAG,CAAA;AAAA,EAC1C,GAAA,CAAI,IAAI,CAAA,EAAW;AAAE,IAAA,OAAO,CAAA,IAAK,SAAA;AAAA,EAAW,CAAA;AAAA,EAC5C,OAAA,GAAU;AAAE,IAAA,OAAO,MAAA,CAAO,KAAK,SAAS,CAAA;AAAA,EAAG,CAAA;AAAA,EAC3C,wBAAA,CAAyB,IAAI,CAAA,EAAW;AACtC,IAAA,IAAI,CAAA,IAAK,SAAA,EAAW,OAAO,EAAE,YAAA,EAAc,IAAA,EAAM,UAAA,EAAY,IAAA,EAAM,KAAA,EAAO,SAAA,CAAU,CAAC,CAAA,EAAE;AACvF,IAAA,OAAO,MAAA;AAAA,EACT;AACF,CAAC;AACM,SAAS,iBAAiB,KAAA,EAAwC;AACvE,EAAA,KAAA,MAAW,KAAK,MAAA,CAAO,IAAA,CAAK,SAAS,CAAA,EAAG,OAAO,UAAU,CAAC,CAAA;AAC1D,EAAA,MAAA,CAAO,MAAA,CAAO,WAAW,KAAK,CAAA;AAChC;AAEO,IAAM,eAA0C;AAChD,IAAM,cAAwC;AAC9C,IAAM,mBAAA,GAA2C,EAAE,GAAA,EAAK,EAAC,EAAG,MAAA,EAAQ,EAAC,EAAG,OAAA,EAAS,EAAC,EAAG,MAAA,EAAQ,EAAC;ACZrG,IAAM,OAAA,GAAqB,EAAE,UAAA,EAAY,MAAM,IAAA,EAAK;AAEpD,IAAM,gBAAA,GAAmB,cAAyB,OAAO,CAAA;AAElD,SAAS,iBAAA,CAAkB,EAAE,KAAA,EAAO,QAAA,EAAS,EAA8C;AAChG,EAAA,uBAAO,GAAA,CAAC,gBAAA,CAAiB,QAAA,EAAjB,EAA0B,OAAe,QAAA,EAAS,CAAA;AAC5D;AAEO,SAAS,YAAA,GAA0B;AACxC,EAAA,OAAO,WAAW,gBAAgB,CAAA;AACpC","file":"chunk-GA35NEND.js","sourcesContent":["/**\n * Nav data types used by <Layout> + <StartMenu>. The actual sections, icons,\n * and categories are consumer-supplied via Layout props — the package never\n * ships nav DATA, only the shape it expects.\n */\nimport type { ReactNode } from 'react';\n\nexport interface NavItem {\n to: string;\n label: string;\n /** Optional compact wording used only inside navigation menus. Keep `label`\n * as the full semantic name for windows, search and assistive technology. */\n menuLabel?: string;\n perms?: string[];\n /** Permissions the user must hold EVERY one of, unlike `perms` which needs\n * only one. For an entry whose page is gated on a narrower permission than\n * the one that puts it in the menu: listing both in `perms` would widen the\n * audience (it's an OR), so the extra one goes here instead. */\n allPerms?: string[];\n dividerAfter?: boolean;\n /** An action instead of a destination. When set, choosing the row calls this\n * and opens no window — `to` is then only the row's key (and its `navIcons`\n * lookup), so give it one no page uses. The menu closes as it would after\n * opening a page. For rows whose target is not a window: signing in to\n * another tenant, say, which Sign Out is the built-in precedent for. */\n onSelect?: () => void;\n /** Optional sub-items, to any depth — a child may have children of its own.\n * Hovering the parent in <StartMenu> opens a nested flyout; in <Sidebar>\n * the parent becomes an inline sub-accordion. */\n children?: NavItem[];\n}\n\nexport interface NavSection {\n label: string;\n /** Optional compact wording used only inside navigation menus. */\n menuLabel?: string;\n items: NavItem[];\n perms?: string[];\n /** Same all-of rule as `NavItem.allPerms`, for a whole section. */\n allPerms?: string[];\n /** Optional landing route for clicks on the section title itself\n * (e.g. R&D's `/rd` dashboard). */\n to?: string;\n /** Draw a divider under this section's row, so a consumer can split one\n * flat group into visual bands (e.g. an operator console pinned above the\n * ERP sections). Same knob as `NavItem.dividerAfter`, and drawn INSIDE the\n * section's wrapper so it stays below the row in the `flex-col-reverse`\n * Start menu a side/top taskbar renders. */\n dividerAfter?: boolean;\n}\n\nexport interface VirtualSection {\n label: string;\n /** Optional compact wording used only inside navigation menus. */\n menuLabel?: string;\n items: NavItem[];\n icon?: ReactNode;\n}\n\nexport interface StartMenuCategories {\n /** Section labels rendered in the \"ERP\" group (bold, with section icon). */\n erp: string[];\n /** Section labels rendered in the \"system\" group. */\n system: string[];\n /** Optional virtual flyouts (e.g. a \"Utilities\" tray). */\n virtual?: VirtualSection[];\n /** Optional section labels pinned to the bottom of the menu, next to the user\n * profile, separated from the ERP group by a divider. */\n footer?: string[];\n /** Optional flat top-level items pinned to the bottom of the menu, next to\n * the user profile, separated from the ERP group by a divider. Unlike\n * `footer` (section labels rendered as flyouts), these render as direct\n * clickable rows — use for standalone destinations like System Preferences\n * or a bug-report link. */\n footerItems?: NavItem[];\n}\n\nexport function isSection(item: NavSection | NavItem): item is NavSection {\n return 'items' in item;\n}\n\n/** The host app's permission predicate — `useAuth().hasAnyPerm`. */\ntype HasAnyPerm = (perms: string[]) => boolean;\n\n/**\n * Whether this user may see a nav entry, honouring BOTH permission rules.\n *\n * Every visibility test in the shell goes through here. It used to be an inline\n * `!x.perms || hasAnyPerm(x.perms)` repeated at nine call sites, which made the\n * all-of rule impossible to add without missing one — and a missed site shows a\n * row that refuses on click (SG#00214: a price-blind supplier role kept the\n * Price Sheets entry, because the entry is gated on the permission that lists\n * price sheets while the page is gated on the narrower one that reveals prices).\n *\n * `perms` keeps its any-of meaning, so existing nav data is untouched.\n * `allPerms` requires every entry, and is tested one permission at a time\n * because `hasAnyPerm` is the only predicate a host supplies — asking for all\n * of them in one call would be an OR and quietly grant the row.\n */\nexport function navVisible(\n entry: { perms?: string[]; allPerms?: string[] },\n hasAnyPerm: HasAnyPerm,\n): boolean {\n if (entry.perms?.length && !hasAnyPerm(entry.perms)) return false;\n if (entry.allPerms?.length && !entry.allPerms.every(p => hasAnyPerm([p]))) return false;\n return true;\n}\n\n/**\n * The sub-items of `item` this user may actually see.\n *\n * Every affordance for a nested group has to be derived from THIS list — the\n * chevron that advertises it, the hover handler that opens it, and the flyout\n * itself. Reading the raw `item.children` for the chevron while filtering only\n * for the flyout put an arrow on groups whose children were all\n * permission-hidden: hovering one highlighted the row and opened nothing.\n */\nexport function visibleChildren(item: NavItem, hasAnyPerm: HasAnyPerm): NavItem[] {\n return (item.children ?? []).filter(c => navVisible(c, hasAnyPerm));\n}\n\n/**\n * Whether a nav row leads anywhere for this user: either it's a plain\n * destination, or it's a group with at least one child that itself leads\n * somewhere. A group whose children are all hidden has nothing to open — and\n * by convention a group's `to` is a synthetic key that never navigates, so\n * there is nothing to fall back to either. Such a row is dropped rather than\n * rendered inert.\n *\n * The test recurses because nesting does: a group holding one sub-group whose\n * own children are all permission-hidden is just as inert as a group holding\n * nothing, and at four levels deep that shape is easy to build by accident.\n */\nexport function isReachable(item: NavItem, hasAnyPerm: HasAnyPerm): boolean {\n if (!item.children?.length) return true;\n return visibleChildren(item, hasAnyPerm).some(child => isReachable(child, hasAnyPerm));\n}\n","/**\n * INTERNAL stub — package-side compatibility for files that legacy-imported\n * default nav data from `shell-config/nav`. The package never ships nav\n * DATA; consumers always supply `navSections` / `navIcons` / `sectionIcons` /\n * `categories` as Layout props.\n *\n * Empty defaults exported here so the copied files compile during the\n * extraction transition. Each consumer-facing field is also re-exported as\n * a TYPE alias for code that only needed the shape.\n */\nimport type { ReactNode } from 'react';\nimport type { NavItem, NavSection, StartMenuCategories, VirtualSection } from '../shell/nav-types';\n\n// Live proxy: WindowManager reads window-title icons from this module-level\n// map. Consumers register their full icon set once at app startup so the\n// title bars show the same glyphs as the start menu.\nconst _navIcons: Record<string, ReactNode> = {};\nexport const navIcons: Record<string, ReactNode> = new Proxy(_navIcons, {\n get(_t, k: string) { return _navIcons[k]; },\n has(_t, k: string) { return k in _navIcons; },\n ownKeys() { return Object.keys(_navIcons); },\n getOwnPropertyDescriptor(_t, k: string) {\n if (k in _navIcons) return { configurable: true, enumerable: true, value: _navIcons[k] };\n return undefined;\n },\n});\nexport function setShellNavIcons(icons: Record<string, ReactNode>): void {\n for (const k of Object.keys(_navIcons)) delete _navIcons[k];\n Object.assign(_navIcons, icons);\n}\n\nexport const sectionIcons: Record<string, ReactNode> = {};\nexport const navSections: (NavSection | NavItem)[] = [];\nexport const startMenuCategories: StartMenuCategories = { erp: [], system: [], virtual: [], footer: [] };\n\nexport { isSection } from '../shell/nav-types';\nexport type { NavItem, NavSection, StartMenuCategories, VirtualSection };\n","/**\n * Shell-side auth surface — JUST permission checking. The shell never\n * authenticates anyone or holds tokens; it only asks \"is the current user\n * allowed to see this nav item?\".\n *\n * Consumer wraps the app:\n *\n * <ShellAuthProvider value={{ hasAnyPerm: (perms) => myAuth.has(...perms) }}>\n * <Layout ... />\n * </ShellAuthProvider>\n *\n * When no provider is mounted, every permission check returns true — useful\n * for demos and consumers that don't gate their UI.\n */\nimport { createContext, useContext, type ReactNode } from 'react';\n\nexport interface ShellAuth {\n /** Returns true if the current user has any of the listed permission codes. */\n hasAnyPerm: (perms: string[]) => boolean;\n}\n\nconst DEFAULT: ShellAuth = { hasAnyPerm: () => true };\n\nconst ShellAuthContext = createContext<ShellAuth>(DEFAULT);\n\nexport function ShellAuthProvider({ value, children }: { value: ShellAuth; children: ReactNode }) {\n return <ShellAuthContext.Provider value={value}>{children}</ShellAuthContext.Provider>;\n}\n\nexport function useShellAuth(): ShellAuth {\n return useContext(ShellAuthContext);\n}\n"]}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Fragment } from 'react';
|
|
2
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
3
|
+
|
|
4
|
+
// src/shell/Breadcrumbs.tsx
|
|
5
|
+
var DEFAULT_SEPARATOR = /* @__PURE__ */ jsx("svg", { className: "h-3.5 w-3.5 shrink-0 text-gray-300", viewBox: "0 0 20 20", fill: "currentColor", "aria-hidden": true, children: /* @__PURE__ */ jsx(
|
|
6
|
+
"path",
|
|
7
|
+
{
|
|
8
|
+
fillRule: "evenodd",
|
|
9
|
+
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",
|
|
10
|
+
clipRule: "evenodd"
|
|
11
|
+
}
|
|
12
|
+
) });
|
|
13
|
+
var CRUMB_LINK = "inline-flex min-w-0 items-center gap-1 truncate rounded px-1 -mx-1 text-gray-500 transition-colors hover:text-gray-900 hover:underline focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-400";
|
|
14
|
+
function Breadcrumbs({ items, separator, maxItems = 0, className }) {
|
|
15
|
+
if (items.length === 0) return null;
|
|
16
|
+
const sep = separator ?? DEFAULT_SEPARATOR;
|
|
17
|
+
const crumbs = [];
|
|
18
|
+
const lastIndex = items.length - 1;
|
|
19
|
+
if (maxItems > 0 && items.length > maxItems && maxItems >= 2) {
|
|
20
|
+
const tailCount = Math.max(1, maxItems - 1);
|
|
21
|
+
crumbs.push({ kind: "item", item: items[0], isLast: false });
|
|
22
|
+
crumbs.push({ kind: "ellipsis" });
|
|
23
|
+
for (let i = items.length - tailCount; i <= lastIndex; i++) {
|
|
24
|
+
crumbs.push({ kind: "item", item: items[i], isLast: i === lastIndex });
|
|
25
|
+
}
|
|
26
|
+
} else {
|
|
27
|
+
items.forEach((item, i) => crumbs.push({ kind: "item", item, isLast: i === lastIndex }));
|
|
28
|
+
}
|
|
29
|
+
return /* @__PURE__ */ jsx("nav", { "aria-label": "Breadcrumb", className: `min-w-0 ${className ?? ""}`, children: /* @__PURE__ */ jsx("ol", { className: "flex items-center gap-1.5 text-sm", children: crumbs.map((crumb, i) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
30
|
+
i > 0 && /* @__PURE__ */ jsx("li", { "aria-hidden": true, className: "flex items-center", children: sep }),
|
|
31
|
+
/* @__PURE__ */ jsx("li", { className: "flex min-w-0 items-center", children: crumb.kind === "ellipsis" ? /* @__PURE__ */ jsx("span", { className: "px-0.5 text-gray-400 select-none", "aria-label": "Hidden crumbs", children: "\u2026" }) : crumb.isLast || !(crumb.item.href || crumb.item.onClick) ? /* @__PURE__ */ jsxs(
|
|
32
|
+
"span",
|
|
33
|
+
{
|
|
34
|
+
"aria-current": crumb.isLast ? "page" : void 0,
|
|
35
|
+
className: `inline-flex min-w-0 items-center gap-1 truncate ${crumb.isLast ? "font-medium text-gray-900" : "text-gray-500"}`,
|
|
36
|
+
children: [
|
|
37
|
+
crumb.item.icon,
|
|
38
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: crumb.item.label })
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
) : crumb.item.href ? /* @__PURE__ */ jsxs(
|
|
42
|
+
"a",
|
|
43
|
+
{
|
|
44
|
+
href: crumb.item.href,
|
|
45
|
+
onClick: crumb.item.onClick,
|
|
46
|
+
className: CRUMB_LINK,
|
|
47
|
+
children: [
|
|
48
|
+
crumb.item.icon,
|
|
49
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: crumb.item.label })
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
) : /* @__PURE__ */ jsxs(
|
|
53
|
+
"button",
|
|
54
|
+
{
|
|
55
|
+
type: "button",
|
|
56
|
+
onClick: crumb.item.onClick,
|
|
57
|
+
className: CRUMB_LINK,
|
|
58
|
+
children: [
|
|
59
|
+
crumb.item.icon,
|
|
60
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: crumb.item.label })
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
) })
|
|
64
|
+
] }, i)) }) });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export { Breadcrumbs };
|
|
68
|
+
//# sourceMappingURL=chunk-HNH7FOYB.js.map
|
|
69
|
+
//# sourceMappingURL=chunk-HNH7FOYB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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"]}
|