uploaderkit 1.0.0 → 2.0.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.
Files changed (86) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +16 -0
  3. package/README.es.md +1140 -0
  4. package/README.md +1135 -0
  5. package/dist/FileViewer-ChTf7-uY.d.cts +49 -0
  6. package/dist/FileViewer-nuD-GEdJ.d.ts +49 -0
  7. package/dist/adapters/gcs.cjs +83 -0
  8. package/dist/adapters/gcs.cjs.map +1 -0
  9. package/dist/adapters/gcs.d.cts +31 -0
  10. package/dist/adapters/gcs.d.ts +31 -0
  11. package/dist/adapters/gcs.js +81 -0
  12. package/dist/adapters/gcs.js.map +1 -0
  13. package/dist/adapters/memory.cjs +27 -0
  14. package/dist/adapters/memory.cjs.map +1 -0
  15. package/dist/adapters/memory.d.cts +18 -0
  16. package/dist/adapters/memory.d.ts +18 -0
  17. package/dist/adapters/memory.js +25 -0
  18. package/dist/adapters/memory.js.map +1 -0
  19. package/dist/adapters/s3.cjs +75 -0
  20. package/dist/adapters/s3.cjs.map +1 -0
  21. package/dist/adapters/s3.d.cts +27 -0
  22. package/dist/adapters/s3.d.ts +27 -0
  23. package/dist/adapters/s3.js +73 -0
  24. package/dist/adapters/s3.js.map +1 -0
  25. package/dist/chunk-3FI44IOW.js +150 -0
  26. package/dist/chunk-3FI44IOW.js.map +1 -0
  27. package/dist/chunk-H7BRW5IY.js +308 -0
  28. package/dist/chunk-H7BRW5IY.js.map +1 -0
  29. package/dist/chunk-PDKAF4GX.js +707 -0
  30. package/dist/chunk-PDKAF4GX.js.map +1 -0
  31. package/dist/chunk-PTEX7F4R.js +309 -0
  32. package/dist/chunk-PTEX7F4R.js.map +1 -0
  33. package/dist/chunk-T5YZG6JW.js +511 -0
  34. package/dist/chunk-T5YZG6JW.js.map +1 -0
  35. package/dist/index.cjs +220 -27
  36. package/dist/index.cjs.map +1 -1
  37. package/dist/index.d.cts +57 -170
  38. package/dist/index.d.ts +57 -170
  39. package/dist/index.js +1 -324
  40. package/dist/index.js.map +1 -1
  41. package/dist/presets.cjs +1665 -0
  42. package/dist/presets.cjs.map +1 -0
  43. package/dist/presets.d.cts +100 -0
  44. package/dist/presets.d.ts +100 -0
  45. package/dist/presets.js +405 -0
  46. package/dist/presets.js.map +1 -0
  47. package/dist/react.cjs +981 -0
  48. package/dist/react.cjs.map +1 -0
  49. package/dist/react.d.cts +106 -0
  50. package/dist/react.d.ts +106 -0
  51. package/dist/react.js +5 -0
  52. package/dist/react.js.map +1 -0
  53. package/dist/server/express.cjs +179 -0
  54. package/dist/server/express.cjs.map +1 -0
  55. package/dist/server/express.d.cts +64 -0
  56. package/dist/server/express.d.ts +64 -0
  57. package/dist/server/express.js +111 -0
  58. package/dist/server/express.js.map +1 -0
  59. package/dist/server/next.cjs +165 -0
  60. package/dist/server/next.cjs.map +1 -0
  61. package/dist/server/next.d.cts +38 -0
  62. package/dist/server/next.d.ts +38 -0
  63. package/dist/server/next.js +107 -0
  64. package/dist/server/next.js.map +1 -0
  65. package/dist/server.cjs +550 -0
  66. package/dist/server.cjs.map +1 -0
  67. package/dist/server.d.cts +19 -0
  68. package/dist/server.d.ts +19 -0
  69. package/dist/server.js +80 -0
  70. package/dist/server.js.map +1 -0
  71. package/dist/storage-CYkSHWZX.d.cts +133 -0
  72. package/dist/storage-Qc9epG0G.d.ts +133 -0
  73. package/dist/types-BSlJJwti.d.cts +341 -0
  74. package/dist/types-BSlJJwti.d.ts +341 -0
  75. package/dist/ui.cjs +2325 -0
  76. package/dist/ui.cjs.map +1 -0
  77. package/dist/ui.d.cts +331 -0
  78. package/dist/ui.d.ts +331 -0
  79. package/dist/ui.js +749 -0
  80. package/dist/ui.js.map +1 -0
  81. package/dist/useSlottedUploader-BzT5jV8c.d.cts +139 -0
  82. package/dist/useSlottedUploader-IkG5RhEO.d.ts +139 -0
  83. package/dist/useUploader-BiBdS-7y.d.cts +117 -0
  84. package/dist/useUploader-CQHpj_oI.d.ts +117 -0
  85. package/package.json +189 -3
  86. package/tailwind.css +80 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ui/cn.ts","../src/ui/Kbd.tsx","../src/ui/useCoarsePointer.ts","../src/ui/useShortcut.ts","../src/ui/Dropzone.tsx","../src/ui/icons.tsx","../src/ui/overlayStack.ts","../src/ui/scrollLock.ts","../src/ui/useFocusTrap.ts","../src/ui/useOverlayTransition.ts","../src/ui/FileViewer.tsx"],"names":["useEffect","useRef","useState","jsx","jsxs","active","useCallback"],"mappings":"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","file":"chunk-PDKAF4GX.js","sourcesContent":["/** Minimal class joiner — the package has no runtime deps to spend on clsx. */\nexport const cn = (...values: (string | false | null | undefined)[]): string =>\n\tvalues.filter(Boolean).join(' ')\n","import type { ReactNode } from 'react'\n\n/** The little key hint every shortcut announces itself with. */\nexport const Kbd = ({ children }: { children: ReactNode }) => (\n\t<kbd className='border-ui-border bg-ui-surface text-ui-muted rounded-md border px-1.5 py-0.5 font-sans text-[10px] font-medium shadow-sm'>\n\t\t{children}\n\t</kbd>\n)\n","import { useEffect, useState } from 'react'\n\n/**\n * `true` on touch-first devices (`pointer: coarse`), where drag-and-drop\n * barely exists and the zone should read as a tap target. SSR-safe: first\n * paint assumes a fine pointer and corrects in the effect.\n */\nexport const useCoarsePointer = (): boolean => {\n\tconst [coarse, setCoarse] = useState(false)\n\n\tuseEffect(() => {\n\t\tconst query = window.matchMedia('(pointer: coarse)')\n\t\tsetCoarse(query.matches)\n\t\tconst onChange = (event: MediaQueryListEvent) => setCoarse(event.matches)\n\t\tquery.addEventListener('change', onChange)\n\t\treturn () => query.removeEventListener('change', onChange)\n\t}, [])\n\n\treturn coarse\n}\n","import { useEffect, useRef } from 'react'\n\nimport { warnDev } from '../warn'\n\ntype ShortcutSpec = {\n\tmod: boolean\n\tshift: boolean\n\talt: boolean\n\tkey: string\n}\n\n/** `'mod+u'` → ⌘U on mac, Ctrl+U elsewhere. `mod`, `shift`, `alt` + one key. */\nconst parseShortcut = (combo: string): ShortcutSpec => {\n\tconst parts = combo.toLowerCase().split('+')\n\treturn {\n\t\tmod: parts.includes('mod'),\n\t\tshift: parts.includes('shift'),\n\t\talt: parts.includes('alt'),\n\t\tkey: parts[parts.length - 1]!,\n\t}\n}\n\nconst IS_MAC =\n\ttypeof navigator !== 'undefined' && /mac/i.test(navigator.platform)\n\n/** Human hint for a combo: `'mod+u'` → `⌘U` / `Ctrl+U`. */\nexport const formatShortcut = (combo: string): string => {\n\tconst spec = parseShortcut(combo)\n\tconst key = spec.key.length === 1 ? spec.key.toUpperCase() : spec.key\n\tconst parts: string[] = []\n\tif (spec.mod) parts.push(IS_MAC ? '⌘' : 'Ctrl')\n\tif (spec.alt) parts.push(IS_MAC ? '⌥' : 'Alt')\n\tif (spec.shift) parts.push(IS_MAC ? '⇧' : 'Shift')\n\tparts.push(key)\n\treturn parts.join(IS_MAC ? '' : '+')\n}\n\n/** Combos currently bound, to catch two zones claiming the same keys. */\nconst active = new Map<string, number>()\n\n/**\n * Binds a global shortcut while mounted. Never fires from an input, textarea\n * or contenteditable — typing keeps priority — and warns in development when\n * two surfaces register the same combo at once, because only both firing or\n * an arbitrary winner can come out of that.\n */\nexport const useShortcut = (\n\tcombo: string | undefined,\n\thandler: () => void,\n\tdisabled = false\n): void => {\n\tconst handlerRef = useRef(handler)\n\thandlerRef.current = handler\n\n\tuseEffect(() => {\n\t\tif (!combo || disabled) return\n\t\tconst spec = parseShortcut(combo)\n\n\t\tactive.set(combo, (active.get(combo) ?? 0) + 1)\n\t\tif ((active.get(combo) ?? 0) > 1) {\n\t\t\twarnDev(\n\t\t\t\t`shortcut-collision:${combo}`,\n\t\t\t\t`two surfaces registered the shortcut \"${combo}\" at the same time — give each its own combo or scope one out.`\n\t\t\t)\n\t\t}\n\n\t\tconst onKey = (event: KeyboardEvent) => {\n\t\t\tif (event.repeat) return\n\t\t\t// `target` can be `window` itself; only elements can host a field.\n\t\t\tconst target = event.target\n\t\t\tif (\n\t\t\t\ttarget instanceof Element &&\n\t\t\t\ttarget.closest('input, textarea, select, [contenteditable]')\n\t\t\t) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (event.key.toLowerCase() !== spec.key) return\n\t\t\tif (spec.mod !== (event.metaKey || event.ctrlKey)) return\n\t\t\tif (spec.shift !== event.shiftKey) return\n\t\t\tif (spec.alt !== event.altKey) return\n\t\t\tevent.preventDefault()\n\t\t\thandlerRef.current()\n\t\t}\n\n\t\twindow.addEventListener('keydown', onKey)\n\t\treturn () => {\n\t\t\twindow.removeEventListener('keydown', onKey)\n\t\t\tconst count = (active.get(combo) ?? 1) - 1\n\t\t\tif (count <= 0) active.delete(combo)\n\t\t\telse active.set(combo, count)\n\t\t}\n\t}, [combo, disabled])\n}\n","import {\n\ttype ClipboardEvent,\n\ttype DragEvent,\n\ttype ReactNode,\n\tuseCallback,\n\tuseRef,\n\tuseState,\n} from 'react'\n\nimport { cn } from './cn'\nimport { Kbd } from './Kbd'\nimport { useCoarsePointer } from './useCoarsePointer'\nimport { formatShortcut, useShortcut } from './useShortcut'\n\nexport type DropzoneProps = {\n\taccept: string\n\tmultiple?: boolean\n\tdisabled?: boolean\n\t/**\n\t * Mobile camera shortcut: `'environment'` (rear) or `'user'` (front) makes\n\t * the file picker offer the camera directly on touch devices. Desktop\n\t * browsers ignore it.\n\t */\n\tcapture?: 'user' | 'environment'\n\t/** @defaultValue 'md' */\n\tsize?: 'sm' | 'md'\n\t/**\n\t * Global keyboard shortcut that opens this zone's picker, e.g. `'mod+u'`\n\t * (⌘U / Ctrl+U). A small key hint renders under the prompt on fine\n\t * pointers. With several zones on screen, give each its own combo.\n\t */\n\tshortcut?: string\n\tonFiles: (files: File[]) => void\n\tchildren: ReactNode\n\tclassName?: string\n}\n\n/**\n * The one drag-and-drop surface both uploaders share: drag state, click and\n * keyboard to browse, paste while focused (screenshots land as files), and a\n * hidden input. Presentation comes from `children`.\n */\nexport const Dropzone = ({\n\taccept,\n\tmultiple = false,\n\tdisabled = false,\n\tcapture,\n\tsize = 'md',\n\tshortcut,\n\tonFiles,\n\tchildren,\n\tclassName,\n}: DropzoneProps) => {\n\tconst inputRef = useRef<HTMLInputElement>(null)\n\tconst [dragging, setDragging] = useState(false)\n\t// Depth counter, not a boolean: entering a child fires `dragleave` on this\n\t// wrapper. See AGENTS.md §6.\n\tconst depth = useRef(0)\n\tconst coarse = useCoarsePointer()\n\tuseShortcut(shortcut, () => inputRef.current?.click(), disabled)\n\n\tconst emit = useCallback(\n\t\t(list: FileList | null) => {\n\t\t\tif (disabled || !list || list.length === 0) return\n\t\t\tonFiles(Array.from(list))\n\t\t},\n\t\t[disabled, onFiles]\n\t)\n\n\tconst onDrop = useCallback(\n\t\t(event: DragEvent) => {\n\t\t\tevent.preventDefault()\n\t\t\tdepth.current = 0\n\t\t\tsetDragging(false)\n\t\t\temit(event.dataTransfer.files)\n\t\t},\n\t\t[emit]\n\t)\n\n\tconst onPaste = useCallback(\n\t\t(event: ClipboardEvent) => {\n\t\t\t// A pasted screenshot arrives as a file; pasted text has none and\n\t\t\t// falls through untouched. Validation rejects what the scope won't.\n\t\t\tif (event.clipboardData.files.length === 0) return\n\t\t\tevent.preventDefault()\n\t\t\temit(event.clipboardData.files)\n\t\t},\n\t\t[emit]\n\t)\n\n\treturn (\n\t\t<div\n\t\t\trole='button'\n\t\t\ttabIndex={disabled ? -1 : 0}\n\t\t\taria-disabled={disabled}\n\t\t\tdata-dragging={dragging || undefined}\n\t\t\tonClick={() => !disabled && inputRef.current?.click()}\n\t\t\tonKeyDown={event => {\n\t\t\t\tif (disabled) return\n\t\t\t\tif (event.key === 'Enter' || event.key === ' ') {\n\t\t\t\t\tevent.preventDefault()\n\t\t\t\t\tinputRef.current?.click()\n\t\t\t\t}\n\t\t\t}}\n\t\t\tonDragEnter={() => {\n\t\t\t\tdepth.current += 1\n\t\t\t\tif (!disabled) setDragging(true)\n\t\t\t}}\n\t\t\tonDragOver={event => event.preventDefault()}\n\t\t\tonDragLeave={() => {\n\t\t\t\tdepth.current = Math.max(0, depth.current - 1)\n\t\t\t\tif (depth.current === 0) setDragging(false)\n\t\t\t}}\n\t\t\tonDrop={onDrop}\n\t\t\tonPaste={onPaste}\n\t\t\tclassName={cn(\n\t\t\t\t// `group/dz` + `data-dragging` let the children (the icon, the\n\t\t\t\t// prompt) react to hover and drag without owning any state.\n\t\t\t\t// `touch-manipulation` kills the 300ms tap delay; the active scale\n\t\t\t\t// is the press feedback a touch screen needs in place of hover.\n\t\t\t\t'group/dz rounded-ui-lg focus-visible:ring-ui-ring block w-full cursor-pointer touch-manipulation border-2 border-dashed text-center transition-all duration-200 focus-visible:ring-2 focus-visible:outline-none',\n\t\t\t\tsize === 'sm' ? 'p-3' : 'p-5',\n\t\t\t\tdragging\n\t\t\t\t\t? 'border-ui-primary bg-ui-primary-soft shadow-ui-primary/15 scale-[1.02] shadow-lg'\n\t\t\t\t\t: 'border-ui-border-strong hover:border-ui-primary/60 hover:bg-ui-primary-soft/40 hover:shadow-ui-primary/10 bg-ui-surface-subtle/50 hover:shadow-md',\n\t\t\t\t!disabled && 'active:scale-[0.98]',\n\t\t\t\tdisabled && 'cursor-not-allowed opacity-50',\n\t\t\t\tclassName\n\t\t\t)}\n\t\t>\n\t\t\t<input\n\t\t\t\tref={inputRef}\n\t\t\t\ttype='file'\n\t\t\t\taccept={accept}\n\t\t\t\tmultiple={multiple}\n\t\t\t\tdisabled={disabled}\n\t\t\t\t{...(capture ? { capture } : {})}\n\t\t\t\tclassName='hidden'\n\t\t\t\t// The wrapper's own `input.click()` bubbles back here and would\n\t\t\t\t// re-enter it. Load-bearing — see AGENTS.md §6.\n\t\t\t\tonClick={event => event.stopPropagation()}\n\t\t\t\tonChange={event => {\n\t\t\t\t\temit(event.target.files)\n\t\t\t\t\t// Same file picked twice must fire change again.\n\t\t\t\t\tevent.target.value = ''\n\t\t\t\t}}\n\t\t\t/>\n\t\t\t{children}\n\t\t\t{shortcut && !coarse && (\n\t\t\t\t<span className='mt-2 block'>\n\t\t\t\t\t<Kbd>{formatShortcut(shortcut)}</Kbd>\n\t\t\t\t</span>\n\t\t\t)}\n\t\t</div>\n\t)\n}\n","/**\n * The built-in glyphs, kept to the few the package's own chrome needs — the\n * dropzone default and the viewer's header actions, which have no text on a\n * touch screen. Consumers replace the dropzone one per uploader via the `icon`\n * prop (any ReactNode); none of these is exported, so the package never grows\n * into an icon library.\n */\nexport const UploadCloudIcon = ({ className }: { className?: string }) => (\n\t<svg\n\t\tviewBox='0 0 24 24'\n\t\tfill='none'\n\t\tstroke='currentColor'\n\t\tstrokeWidth='1.75'\n\t\tstrokeLinecap='round'\n\t\tstrokeLinejoin='round'\n\t\tclassName={className}\n\t\taria-hidden\n\t>\n\t\t<path d='M4 14.9A7 7 0 1 1 15.7 8h1.8a4.5 4.5 0 0 1 2.5 8.2' />\n\t\t<path d='M12 12v9' />\n\t\t<path d='m8 16 4-4 4 4' />\n\t</svg>\n)\n\n/** Viewer header: save the file being previewed. */\nexport const DownloadIcon = ({ className }: { className?: string }) => (\n\t<svg\n\t\tviewBox='0 0 24 24'\n\t\tfill='none'\n\t\tstroke='currentColor'\n\t\tstrokeWidth='1.75'\n\t\tstrokeLinecap='round'\n\t\tstrokeLinejoin='round'\n\t\tclassName={className}\n\t\taria-hidden\n\t>\n\t\t<path d='M12 3v12' />\n\t\t<path d='m7 11 5 5 5-5' />\n\t\t<path d='M4 20h16' />\n\t</svg>\n)\n\n/** Viewer header: open the file in its own tab. */\nexport const ExternalLinkIcon = ({ className }: { className?: string }) => (\n\t<svg\n\t\tviewBox='0 0 24 24'\n\t\tfill='none'\n\t\tstroke='currentColor'\n\t\tstrokeWidth='1.75'\n\t\tstrokeLinecap='round'\n\t\tstrokeLinejoin='round'\n\t\tclassName={className}\n\t\taria-hidden\n\t>\n\t\t<path d='M14 4h6v6' />\n\t\t<path d='M20 4 10 14' />\n\t\t<path d='M18 14v5a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h5' />\n\t</svg>\n)\n\n/** Viewer: the preview could not be rendered. */\nexport const FileWarningIcon = ({ className }: { className?: string }) => (\n\t<svg\n\t\tviewBox='0 0 24 24'\n\t\tfill='none'\n\t\tstroke='currentColor'\n\t\tstrokeWidth='1.5'\n\t\tstrokeLinecap='round'\n\t\tstrokeLinejoin='round'\n\t\tclassName={className}\n\t\taria-hidden\n\t>\n\t\t<path d='M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z' />\n\t\t<path d='M14 3v5h5' />\n\t\t<path d='M12 11v4' />\n\t\t<path d='M12 18h.01' />\n\t</svg>\n)\n","import { useEffect, useMemo, useRef } from 'react'\n\n/**\n * Which open overlay answers a dismiss key.\n *\n * `FileViewer` and `ConfirmDialog` both claim Escape on `window` in the\n * CAPTURE phase, so they beat a host app's own dialog (a typical Modal\n * listens on `document` in bubble). But two listeners on the same node\n * in the same phase both run — `stopPropagation` only stops other NODES — so\n * a confirm opened over the viewer would close both on one press.\n *\n * Ordering by registration would not help either: that is open order, and the\n * layer that must answer is the innermost, which opened last.\n *\n * Package-local on purpose: a design system that ships its own dismiss\n * stack keeps it, and neither has to depend on the other (invariant §4).\n */\nlet sequence = 0\nconst stack: number[] = []\n\nexport type OverlayLayer = {\n\t/** `true` while this layer is the innermost open one. */\n\tisTopmost: () => boolean\n}\n\n/** Joins the stack while `active`, and leaves it on close or unmount. */\nexport const useOverlayLayer = (active: boolean): OverlayLayer => {\n\tconst idRef = useRef(0)\n\tif (idRef.current === 0) idRef.current = ++sequence\n\n\tuseEffect(() => {\n\t\tif (!active) return\n\t\tconst id = idRef.current\n\t\tstack.push(id)\n\t\treturn () => {\n\t\t\tconst at = stack.lastIndexOf(id)\n\t\t\tif (at !== -1) stack.splice(at, 1)\n\t\t}\n\t}, [active])\n\n\treturn useMemo(\n\t\t() => ({ isTopmost: () => stack[stack.length - 1] === idRef.current }),\n\t\t[]\n\t)\n}\n","/**\n * Body scroll lock, reference counted across overlays AND across packages —\n * the state lives on `globalThis` so every copy converges on one counter.\n *\n * @see AGENTS.md §4 — the key and the shape are an invariant, not a detail\n */\ntype ScrollLockState = { count: number; saved: string }\n\nconst KEY = Symbol.for('uploaderkit.bodyScrollLock')\n\nconst state = (): ScrollLockState => {\n\tconst host = globalThis as { [KEY]?: ScrollLockState }\n\treturn (host[KEY] ??= { count: 0, saved: '' })\n}\n\nexport const lockBodyScroll = (): void => {\n\tconst lock = state()\n\tif (lock.count === 0) {\n\t\tlock.saved = document.body.style.overflow\n\t\tdocument.body.style.overflow = 'hidden'\n\t}\n\tlock.count += 1\n}\n\nexport const unlockBodyScroll = (): void => {\n\tconst lock = state()\n\t// An unbalanced unlock must be a no-op — decrementing past zero would\n\t// re-apply a stale saved value onto a page nobody locked.\n\tif (lock.count === 0) return\n\tlock.count -= 1\n\tif (lock.count === 0) document.body.style.overflow = lock.saved\n}\n","import { type RefObject, useEffect } from 'react'\n\nconst FOCUSABLE =\n\t'a[href],button:not([disabled]),textarea:not([disabled]),input:not([disabled]),select:not([disabled]),[tabindex]:not([tabindex=\"-1\"])'\n\n/**\n * Keeps `Tab` inside `ref` while `active`, focuses the panel (or its first\n * `[data-autofocus]`) on activation, and restores focus to whatever had it\n * before — the contract a modal owes the keyboard.\n */\nexport const useFocusTrap = (\n\tactive: boolean,\n\tref: RefObject<HTMLElement | null>\n): void => {\n\tuseEffect(() => {\n\t\tif (!active) return\n\t\tconst restoreTo = document.activeElement as HTMLElement | null\n\n\t\tconst panel = ref.current\n\t\tconst initial =\n\t\t\tpanel?.querySelector<HTMLElement>('[data-autofocus]') ?? panel\n\t\tinitial?.focus({ preventScroll: true })\n\n\t\tconst onKeyDown = (event: KeyboardEvent) => {\n\t\t\tif (event.key !== 'Tab' || !ref.current) return\n\t\t\tconst focusables = Array.from(\n\t\t\t\tref.current.querySelectorAll<HTMLElement>(FOCUSABLE)\n\t\t\t).filter(el => el.offsetParent !== null)\n\t\t\tif (focusables.length === 0) {\n\t\t\t\tevent.preventDefault()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst first = focusables[0]!\n\t\t\tconst last = focusables[focusables.length - 1]!\n\t\t\tif (event.shiftKey && document.activeElement === first) {\n\t\t\t\tevent.preventDefault()\n\t\t\t\tlast.focus()\n\t\t\t} else if (!event.shiftKey && document.activeElement === last) {\n\t\t\t\tevent.preventDefault()\n\t\t\t\tfirst.focus()\n\t\t\t}\n\t\t}\n\n\t\tdocument.addEventListener('keydown', onKeyDown)\n\t\treturn () => {\n\t\t\tdocument.removeEventListener('keydown', onKeyDown)\n\t\t\trestoreTo?.focus({ preventScroll: true })\n\t\t}\n\t}, [active, ref])\n}\n","import { useEffect, useRef, useState } from 'react'\n\n/** Kept in sync with the transition classes the overlays use. */\nconst OVERLAY_ANIMATION_MS = 200\n\nexport type OverlayTransition = {\n\t/** Render while `true` — spans the exit animation past `open = false`. */\n\tmounted: boolean\n\t/** Drives the transition classes: `false` on both edges of the animation. */\n\tentered: boolean\n}\n\n/**\n * The enter/exit choreography every overlay shares: render on open, flip\n * `entered` a frame later so the transition runs, keep rendering\n * `OVERLAY_ANIMATION_MS` past close so the exit is visible.\n *\n * @see AGENTS.md §3\n */\nexport const useOverlayTransition = (open: boolean): OverlayTransition => {\n\tconst [entered, setEntered] = useState(false)\n\tconst [exiting, setExiting] = useState(false)\n\tconst wasOpen = useRef(false)\n\n\tuseEffect(() => {\n\t\tif (open) {\n\t\t\twasOpen.current = true\n\t\t\tsetExiting(false)\n\t\t\tconst raf = requestAnimationFrame(() => setEntered(true))\n\t\t\treturn () => cancelAnimationFrame(raf)\n\t\t}\n\t\tsetEntered(false)\n\t\tif (!wasOpen.current) return\n\t\twasOpen.current = false\n\t\tsetExiting(true)\n\t\tconst timer = setTimeout(() => setExiting(false), OVERLAY_ANIMATION_MS)\n\t\treturn () => clearTimeout(timer)\n\t}, [open])\n\n\treturn { mounted: open || exiting, entered }\n}\n","import { type ReactNode, useCallback, useEffect, useRef, useState } from 'react'\nimport { createPortal } from 'react-dom'\n\nimport { getMimeType } from '../file'\nimport type { UploaderLabels } from '../labels'\nimport { useUploaderLabels } from '../react/UploaderProvider'\nimport { cn } from './cn'\nimport { DownloadIcon, ExternalLinkIcon, FileWarningIcon } from './icons'\nimport { Kbd } from './Kbd'\nimport { useOverlayLayer } from './overlayStack'\nimport { lockBodyScroll, unlockBodyScroll } from './scrollLock'\nimport { useCoarsePointer } from './useCoarsePointer'\nimport { useFocusTrap } from './useFocusTrap'\nimport { useOverlayTransition } from './useOverlayTransition'\n\n/**\n * The viewer's empty states — failure and \"no preview\" — speak the overlay's\n * own language: no card, white on the black scrim, the same translucent\n * controls as the header. Theme tokens are deliberately absent: this component\n * ships inside many apps, and a surface-coloured panel would read as a stray\n * card from whichever one is hosting it.\n */\nconst EMPTY_STATE_CLASS = 'mx-4 flex max-w-sm flex-col items-center text-center'\nconst EMPTY_TITLE_CLASS = 'text-sm font-medium text-white'\nconst EMPTY_HINT_CLASS = 'mt-1.5 text-xs leading-relaxed text-white/60'\n/** Primary sits one step brighter than the header actions, never coloured. */\nconst EMPTY_PRIMARY_CLASS =\n\t'focus-visible:ring-ui-ring rounded-ui cursor-pointer bg-white/20 px-4 py-2 text-sm text-white transition-colors hover:bg-white/30 focus-visible:ring-2 focus-visible:outline-none'\nconst EMPTY_SECONDARY_CLASS =\n\t'focus-visible:ring-ui-ring rounded-ui cursor-pointer border border-white/20 px-4 py-2 text-sm text-white/80 transition-colors hover:bg-white/10 hover:text-white focus-visible:ring-2 focus-visible:outline-none'\n\n/** Shared chrome of the header actions, so the three stay one control strip. */\nconst ACTION_CLASS =\n\t'focus-visible:ring-ui-ring rounded-ui inline-flex shrink-0 cursor-pointer items-center gap-1.5 bg-white/10 px-3 py-1.5 text-xs whitespace-nowrap text-white transition-colors hover:bg-white/20 focus-visible:ring-2 focus-visible:outline-none'\n\n/** What the viewer needs to render a file — a StoredFile satisfies it. */\nexport type ViewableFile = {\n\turl: string\n\tfileName?: string\n\tmimeType?: string\n}\n\nexport type FileViewerProps = {\n\t/** File to show; `null` keeps the viewer closed. */\n\tfile: ViewableFile | null\n\tonClose: () => void\n\t/**\n\t * The collection to browse. With more than one entry the viewer gains side\n\t * arrows and ←/→ keyboard navigation, starting at `file`'s position.\n\t */\n\tfiles?: ViewableFile[]\n\t/**\n\t * Resolve a fresh URL right before rendering — the hook for private scopes,\n\t * where the persisted signed URL may have expired and the app re-signs via\n\t * its /storage signed-url endpoint.\n\t */\n\tresolveUrl?: (file: ViewableFile) => Promise<string>\n\t/**\n\t * Replaces the built-in \"could not load\" panel. Receives the file that\n\t * failed and a `retry` that re-resolves and re-renders it, so a custom\n\t * panel keeps the recovery the default one offers.\n\t */\n\trenderError?: (context: {\n\t\tfile: ViewableFile\n\t\tretry: () => void\n\t}) => ReactNode\n\tlabels?: Partial<UploaderLabels>\n}\n\ntype Kind = 'image' | 'pdf' | 'other'\n\nconst kindOf = (file: ViewableFile): Kind => {\n\tconst mime = file.mimeType || getMimeType(file.fileName ?? file.url)\n\tif (mime.startsWith('image/')) return 'image'\n\tif (mime === 'application/pdf') return 'pdf'\n\treturn 'other'\n}\n\n/**\n * iPadOS 13+ reports itself as a Mac, so the platform string alone misses it;\n * a Mac with a touchscreen is the tell.\n */\nconst isIosWebkit = (): boolean => {\n\tconst ua = navigator.userAgent\n\treturn (\n\t\t/iPad|iPhone|iPod/.test(ua) ||\n\t\t(ua.includes('Macintosh') && navigator.maxTouchPoints > 1)\n\t)\n}\n\n/**\n * Whether an `<iframe>` will actually RENDER a PDF here.\n *\n * This used to be a `max-width: 640px` media query standing in for \"iOS\n * Safari\". It punished every narrow viewport — a half-width desktop window, a\n * phone on Chrome — with the \"no preview\" card, while iPadOS, which is wider\n * than the breakpoint AND genuinely broken, sailed past it into a blank frame.\n *\n * `pdfViewerEnabled` is the browser answering the question directly (it is\n * false on Android Chrome, which downloads instead of embedding). WebKit on iOS\n * answers yes and then paints a frozen first page, so it stays excluded by\n * name.\n */\nconst browserEmbedsPdf = (): boolean => {\n\tif (typeof navigator === 'undefined') return false\n\tif (navigator.pdfViewerEnabled === false) return false\n\treturn !isIosWebkit()\n}\n\nconst Chevron = ({ direction }: { direction: 'left' | 'right' }) => (\n\t<svg\n\t\tviewBox='0 0 24 24'\n\t\tfill='none'\n\t\tstroke='currentColor'\n\t\tstrokeWidth='2.5'\n\t\tstrokeLinecap='round'\n\t\tstrokeLinejoin='round'\n\t\tclassName='h-5 w-5'\n\t\taria-hidden\n\t>\n\t\t{direction === 'left' ? (\n\t\t\t<path d='m15 18-6-6 6-6' />\n\t\t) : (\n\t\t\t<path d='m9 18 6-6-6-6' />\n\t\t)}\n\t</svg>\n)\n\n/**\n * Full-screen file preview: images zoomed to fit, PDFs embedded, anything\n * else offered as a download — no more blind new-tab jumps. Pass `files` and\n * it becomes a gallery: side arrows, ←/→ on the keyboard, a position counter.\n * Used internally by `Uploader` and `SlottedUploader`, and exported standalone\n * so any screen can preview the `StoredFile`s it persisted.\n *\n * Portals to `<body>` so an ancestor's stacking context can never trap it,\n * and traps focus while open.\n */\nexport const FileViewer = ({\n\tfile,\n\tonClose,\n\tfiles,\n\tresolveUrl,\n\trenderError,\n\tlabels,\n}: FileViewerProps) => {\n\tconst copy = useUploaderLabels(labels)\n\tconst coarse = useCoarsePointer()\n\tconst [url, setUrl] = useState<string | null>(null)\n\tconst [failed, setFailed] = useState(false)\n\t// Bumping it re-runs the resolution — the retry button after a failure.\n\tconst [attempt, setAttempt] = useState(0)\n\tconst retry = useCallback(() => setAttempt(previous => previous + 1), [])\n\tconst panelRef = useRef<HTMLDivElement>(null)\n\tconst open = file !== null\n\tconst { mounted, entered } = useOverlayTransition(open)\n\tconst layer = useOverlayLayer(open)\n\tuseFocusTrap(open, panelRef)\n\n\t// Gallery cursor. Synced to `file`'s position whenever the viewer opens.\n\tconst list = files && files.length > 0 ? files : null\n\tconst [cursor, setCursor] = useState(0)\n\tuseEffect(() => {\n\t\tif (!file || !list) return\n\t\tconst index = list.findIndex(entry => entry.url === file.url)\n\t\tsetCursor(index >= 0 ? index : 0)\n\t\t// Only the opening file may reposition the cursor, not a list rebuild.\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\t}, [file])\n\n\tconst current = open ? (list ? (list[cursor] ?? file) : file) : null\n\n\t// The exit animation still needs something to draw after `file` nulls out.\n\tconst lastViewRef = useRef<ViewableFile | null>(null)\n\tif (current) lastViewRef.current = current\n\n\tconst navigate = useCallback(\n\t\t(delta: number) => {\n\t\t\tif (!list) return\n\t\t\tsetCursor(previous =>\n\t\t\t\tMath.min(list.length - 1, Math.max(0, previous + delta))\n\t\t\t)\n\t\t},\n\t\t[list]\n\t)\n\n\t// The viewer resolves, so the viewer owns what the resolution minted: a\n\t// `createBlobUrlResolver` hands back a `blob:` whose BYTES stay alive until\n\t// something revokes it, and browsing a gallery mints one per file.\n\tconst objectUrlRef = useRef<string | null>(null)\n\tconst releaseObjectUrl = useCallback(() => {\n\t\tif (objectUrlRef.current) URL.revokeObjectURL(objectUrlRef.current)\n\t\tobjectUrlRef.current = null\n\t}, [])\n\n\t// In a ref, not the deps: callers pass an inline arrow and re-signing is a\n\t// round trip. Only the viewed file (or an explicit retry) may re-run it.\n\tconst resolveRef = useRef(resolveUrl)\n\tresolveRef.current = resolveUrl\n\tconst currentUrl = current?.url\n\n\tuseEffect(() => {\n\t\t// Before the early return: closing the viewer nulls `current`, and that\n\t\t// is exactly when the last blob has to go.\n\t\treleaseObjectUrl()\n\t\tif (!current) {\n\t\t\tsetUrl(null)\n\t\t\tsetFailed(false)\n\t\t\treturn\n\t\t}\n\t\tlet alive = true\n\t\tsetUrl(null)\n\t\tsetFailed(false)\n\t\tvoid (async () => {\n\t\t\ttry {\n\t\t\t\tconst resolve = resolveRef.current\n\t\t\t\tconst resolved = resolve ? await resolve(current) : current.url\n\t\t\t\tconst minted = resolved.startsWith('blob:')\n\t\t\t\tif (!alive) {\n\t\t\t\t\t// Superseded mid-flight: nothing will ever render it, so it has\n\t\t\t\t\t// to go now or it is unreachable AND alive.\n\t\t\t\t\tif (minted) URL.revokeObjectURL(resolved)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\t// A signed url belongs to the server; only a blob is ours to free.\n\t\t\t\tif (minted) objectUrlRef.current = resolved\n\t\t\t\tsetUrl(resolved)\n\t\t\t} catch {\n\t\t\t\t// An expired signature or a dropped connection must surface as a\n\t\t\t\t// retryable state, never as an eternal \"Cargando…\".\n\t\t\t\tif (alive) {\n\t\t\t\t\tsetUrl(null)\n\t\t\t\t\tsetFailed(true)\n\t\t\t\t}\n\t\t\t}\n\t\t})()\n\t\treturn () => {\n\t\t\talive = false\n\t\t}\n\t\t// `currentUrl` stands in for `current`: a rebuilt array with the same\n\t\t// entry must not re-sign.\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\t}, [currentUrl, attempt])\n\n\t// The effect above frees on every change; this catches the last one.\n\tuseEffect(() => releaseObjectUrl, [releaseObjectUrl])\n\n\tconst closeRef = useRef(onClose)\n\tcloseRef.current = onClose\n\n\t// Clicking the anchors is what downloads/opens: replicating their behaviour\n\t// from a handler would mean re-deriving `download` and popup rules.\n\tconst downloadRef = useRef<HTMLAnchorElement>(null)\n\tconst openTabRef = useRef<HTMLAnchorElement>(null)\n\n\tuseEffect(() => {\n\t\tif (!open) return\n\t\tconst onKey = (event: KeyboardEvent) => {\n\t\t\t// A modifier means the key belongs to the browser (⌘D bookmarks,\n\t\t\t// ⌘O opens a file): claiming it would fire our action AND theirs.\n\t\t\tif (event.metaKey || event.ctrlKey || event.altKey) return\n\t\t\tif (event.key === 'Escape') {\n\t\t\t\t// Only the innermost overlay answers, then claims the key so a\n\t\t\t\t// host dialog underneath does not close on the same press.\n\t\t\t\tif (!layer.isTopmost()) return\n\t\t\t\tevent.stopPropagation()\n\t\t\t\tcloseRef.current()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (event.key === 'ArrowLeft') navigate(-1)\n\t\t\tif (event.key === 'ArrowRight') navigate(1)\n\t\t\t// Bare letters, like the arrows above: the viewer is modal and\n\t\t\t// traps focus, so nothing else can be listening.\n\t\t\tconst key = event.key.toLowerCase()\n\t\t\tif (key === 'd') downloadRef.current?.click()\n\t\t\tif (key === 'o') openTabRef.current?.click()\n\t\t}\n\t\t// Capture phase: it must run BEFORE any bubble-phase dialog handler.\n\t\twindow.addEventListener('keydown', onKey, true)\n\t\tlockBodyScroll()\n\t\treturn () => {\n\t\t\twindow.removeEventListener('keydown', onKey, true)\n\t\t\tunlockBodyScroll()\n\t\t}\n\t}, [open, navigate, layer])\n\n\t// Lazy initializer, not an effect: `navigator` cannot change under the\n\t// component, and starting at `true` painted one iframe frame on iOS before\n\t// correcting to the download card.\n\tconst [canEmbedPdf] = useState(browserEmbedsPdf)\n\n\tconst view = current ?? lastViewRef.current\n\tif (!mounted || !view) return null\n\tconst kind = kindOf(view)\n\t// Drives BOTH the branch and the stretch class: the iframe is the only\n\t// child that fills its container, and letting the two drift is what pinned\n\t// a failed PDF's message to the top edge.\n\tconst showsPdf = kind === 'pdf' && canEmbedPdf && !failed && !!url\n\tconst hasGallery = list !== null && list.length > 1\n\n\tconst arrow =\n\t\t'absolute top-1/2 z-10 flex h-11 w-11 -translate-y-1/2 cursor-pointer items-center justify-center rounded-full bg-white/10 text-white backdrop-blur-sm transition-all duration-200 hover:bg-white/25 focus-visible:ring-2 focus-visible:ring-ui-ring focus-visible:outline-none disabled:pointer-events-none disabled:opacity-25'\n\n\treturn createPortal(\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\t'fixed inset-0 z-1000 flex flex-col bg-black/85 transition-opacity duration-200 ease-out',\n\t\t\t\tentered ? 'opacity-100' : 'opacity-0',\n\t\t\t\t!open && 'pointer-events-none'\n\t\t\t)}\n\t\t\tonClick={onClose}\n\t\t\trole='dialog'\n\t\t\taria-modal='true'\n\t\t\taria-label={view.fileName ?? copy.filePreview}\n\t\t>\n\t\t\t<div\n\t\t\t\tclassName='flex items-center justify-between gap-2 px-3 py-2.5 sm:gap-3 sm:px-4 sm:py-3'\n\t\t\t\tonClick={event => event.stopPropagation()}\n\t\t\t>\n\t\t\t\t<p className='min-w-0 flex-1 truncate text-xs font-medium text-white sm:text-sm'>\n\t\t\t\t\t{view.fileName ?? copy.filePreview}\n\t\t\t\t</p>\n\t\t\t\t{hasGallery && (\n\t\t\t\t\t<span className='shrink-0 rounded-full bg-white/10 px-2.5 py-1 text-[11px] font-medium text-white tabular-nums'>\n\t\t\t\t\t\t{cursor + 1} / {list.length}\n\t\t\t\t\t</span>\n\t\t\t\t)}\n\t\t\t\t{/* Three actions plus a title do not fit a phone, so a coarse\n\t\t\t\t pointer gets glyphs and the labels move to `aria-label`. */}\n\t\t\t\t{url && (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\tref={downloadRef}\n\t\t\t\t\t\t\thref={url}\n\t\t\t\t\t\t\tdownload={view.fileName}\n\t\t\t\t\t\t\taria-label={copy.downloadShort}\n\t\t\t\t\t\t\ttitle={copy.downloadShort}\n\t\t\t\t\t\t\tclassName={ACTION_CLASS}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{coarse ? (\n\t\t\t\t\t\t\t\t<DownloadIcon className='h-4 w-4' />\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\tcopy.downloadShort\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</a>\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\tref={openTabRef}\n\t\t\t\t\t\t\thref={url}\n\t\t\t\t\t\t\ttarget='_blank'\n\t\t\t\t\t\t\trel='noreferrer'\n\t\t\t\t\t\t\taria-label={copy.openInTab}\n\t\t\t\t\t\t\ttitle={copy.openInTab}\n\t\t\t\t\t\t\tclassName={ACTION_CLASS}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{coarse ? (\n\t\t\t\t\t\t\t\t<ExternalLinkIcon className='h-4 w-4' />\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\tcopy.openInTab\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</>\n\t\t\t\t)}\n\t\t\t\t<button\n\t\t\t\t\ttype='button'\n\t\t\t\t\tonClick={onClose}\n\t\t\t\t\taria-label={copy.close}\n\t\t\t\t\ttitle={copy.close}\n\t\t\t\t\tclassName={ACTION_CLASS}\n\t\t\t\t>\n\t\t\t\t\t{coarse ? '✕' : <>✕ {copy.close}</>}\n\t\t\t\t</button>\n\t\t\t</div>\n\n\t\t\t<div\n\t\t\t\tref={panelRef}\n\t\t\t\ttabIndex={-1}\n\t\t\t\tclassName={cn(\n\t\t\t\t\t'relative flex min-h-0 flex-1 items-center justify-center p-3 outline-none sm:p-4',\n\t\t\t\t\tshowsPdf && 'items-stretch'\n\t\t\t\t)}\n\t\t\t\tonClick={event => event.stopPropagation()}\n\t\t\t>\n\t\t\t\t{hasGallery && (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype='button'\n\t\t\t\t\t\t\tonClick={() => navigate(-1)}\n\t\t\t\t\t\t\tdisabled={cursor === 0}\n\t\t\t\t\t\t\taria-label={copy.previous}\n\t\t\t\t\t\t\tclassName={cn(arrow, 'left-2 sm:left-4')}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Chevron direction='left' />\n\t\t\t\t\t\t</button>\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype='button'\n\t\t\t\t\t\t\tonClick={() => navigate(1)}\n\t\t\t\t\t\t\tdisabled={cursor === list.length - 1}\n\t\t\t\t\t\t\taria-label={copy.next}\n\t\t\t\t\t\t\tclassName={cn(arrow, 'right-2 sm:right-4')}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Chevron direction='right' />\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</>\n\t\t\t\t)}\n\n\t\t\t\t{failed ? (\n\t\t\t\t\t(renderError?.({ file: view, retry }) ?? (\n\t\t\t\t\t\t<div className={EMPTY_STATE_CLASS}>\n\t\t\t\t\t\t\t<span className='mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-white/5 text-white/70 ring-1 ring-white/10'>\n\t\t\t\t\t\t\t\t<FileWarningIcon className='h-8 w-8' />\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<p className={EMPTY_TITLE_CLASS}>{copy.viewerError}</p>\n\t\t\t\t\t\t\t<p className={EMPTY_HINT_CLASS}>{copy.viewerErrorHint}</p>\n\t\t\t\t\t\t\t{view.fileName && (\n\t\t\t\t\t\t\t\t<p className='mt-3 max-w-full truncate font-mono text-[11px] text-white/40'>\n\t\t\t\t\t\t\t\t\t{view.fileName}\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t<div className='mt-5 flex flex-wrap justify-center gap-2'>\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype='button'\n\t\t\t\t\t\t\t\t\tonClick={retry}\n\t\t\t\t\t\t\t\t\tclassName={EMPTY_PRIMARY_CLASS}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{copy.viewerRetry}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t{/* The bytes may still be reachable even when\n\t\t\t\t\t\t\t\t the preview is not — never a dead end. */}\n\t\t\t\t\t\t\t\t{url && (\n\t\t\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t\t\thref={url}\n\t\t\t\t\t\t\t\t\t\tdownload={view.fileName}\n\t\t\t\t\t\t\t\t\t\tclassName={EMPTY_SECONDARY_CLASS}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{copy.downloadShort}\n\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t))\n\t\t\t\t) : !url ? (\n\t\t\t\t\t<p className='text-sm text-white/70'>{copy.viewerLoading}</p>\n\t\t\t\t) : kind === 'image' ? (\n\t\t\t\t\t<img\n\t\t\t\t\t\tsrc={url}\n\t\t\t\t\t\talt={view.fileName ?? ''}\n\t\t\t\t\t\t// A resolved url only proves the address; the object\n\t\t\t\t\t\t// behind it can still 404, and an untracked failure\n\t\t\t\t\t\t// renders as an invisible image.\n\t\t\t\t\t\tonError={() => setFailed(true)}\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t'rounded-ui-lg max-h-full max-w-full object-contain shadow-2xl',\n\t\t\t\t\t\t\t'transition-transform duration-200 ease-out',\n\t\t\t\t\t\t\tentered ? 'scale-100' : 'scale-[0.97]'\n\t\t\t\t\t\t)}\n\t\t\t\t\t/>\n\t\t\t\t) : showsPdf ? (\n\t\t\t\t\t<iframe\n\t\t\t\t\t\tsrc={url}\n\t\t\t\t\t\ttitle={view.fileName ?? 'PDF'}\n\t\t\t\t\t\t// Best effort: a cross-origin 404 usually renders the\n\t\t\t\t\t\t// browser's own error page inside the frame and fires\n\t\t\t\t\t\t// `load`, so this catches only the cases it can.\n\t\t\t\t\t\tonError={() => setFailed(true)}\n\t\t\t\t\t\tclassName='rounded-ui-lg h-full w-full max-w-4xl bg-white shadow-2xl'\n\t\t\t\t\t/>\n\t\t\t\t) : (\n\t\t\t\t\t<div className={EMPTY_STATE_CLASS}>\n\t\t\t\t\t\t<span className='mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-white/5 text-white/70 ring-1 ring-white/10'>\n\t\t\t\t\t\t\t<FileWarningIcon className='h-8 w-8' />\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<p className={EMPTY_TITLE_CLASS}>{copy.noPreview}</p>\n\t\t\t\t\t\t{view.fileName && (\n\t\t\t\t\t\t\t<p className='mt-3 max-w-full truncate font-mono text-[11px] text-white/40'>\n\t\t\t\t\t\t\t\t{view.fileName}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\thref={url}\n\t\t\t\t\t\t\tdownload={view.fileName}\n\t\t\t\t\t\t\ttarget='_blank'\n\t\t\t\t\t\t\trel='noreferrer'\n\t\t\t\t\t\t\tclassName={cn(EMPTY_PRIMARY_CLASS, 'mt-5 inline-block')}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{copy.download(view.fileName ?? copy.filePreview)}\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</div>\n\n\t\t\t{/* Shortcut hints — pointless on touch, so fine pointers only. */}\n\t\t\t{!coarse && (\n\t\t\t\t<div\n\t\t\t\t\tclassName='flex items-center justify-center gap-4 pb-3 text-[11px] text-white/60'\n\t\t\t\t\tonClick={event => event.stopPropagation()}\n\t\t\t\t>\n\t\t\t\t\t<span className='flex items-center gap-1.5'>\n\t\t\t\t\t\t<Kbd>Esc</Kbd> {copy.close}\n\t\t\t\t\t</span>\n\t\t\t\t\t{url && (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t<span className='flex items-center gap-1.5'>\n\t\t\t\t\t\t\t\t<Kbd>D</Kbd> {copy.downloadShort}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span className='flex items-center gap-1.5'>\n\t\t\t\t\t\t\t\t<Kbd>O</Kbd> {copy.openInTab}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</>\n\t\t\t\t\t)}\n\t\t\t\t\t{hasGallery && (\n\t\t\t\t\t\t<span className='flex items-center gap-1.5'>\n\t\t\t\t\t\t\t<Kbd>←</Kbd>\n\t\t\t\t\t\t\t<Kbd>→</Kbd> {copy.previous} / {copy.next}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>,\n\t\tdocument.body\n\t)\n}\n"]}
@@ -0,0 +1,309 @@
1
+ import { useUploaderLabels, warnDev, useUploader } from './chunk-H7BRW5IY.js';
2
+ import { getFileExtension, resolveLabels, ScopeError, resolveReplaceMode, toAcceptAttribute } from './chunk-T5YZG6JW.js';
3
+ import { useMemo, useRef, useCallback } from 'react';
4
+
5
+ // src/react/slots.ts
6
+ var matchSlotByExtension = (filled) => (slots, fileName) => {
7
+ const extension = getFileExtension(fileName);
8
+ const candidates = slots.filter((slot) => slot.extensions.includes(extension));
9
+ if (candidates.length === 0) return null;
10
+ return (candidates.find((slot) => !filled.has(slot.id)) ?? candidates[0]).id;
11
+ };
12
+ var matchSlotByName = (slots, fileName) => {
13
+ const base = fileName.toLowerCase();
14
+ return slots.find((slot) => base.startsWith(slot.id.toLowerCase()))?.id ?? null;
15
+ };
16
+ var slotFileName = (slotId, originalName) => `${slotId}.${getFileExtension(originalName)}`;
17
+ var slotOfStored = (slots, stored) => slots.find((slot) => stored.fileName.startsWith(`${slot.id}.`))?.id ?? null;
18
+
19
+ // src/react/strategy.ts
20
+ var createXhrUploadStrategy = ({
21
+ endpoint,
22
+ headers,
23
+ fieldName = "file",
24
+ credentials,
25
+ labels
26
+ }) => (file, scope, entityId, { onProgress, signal }) => new Promise((resolve, reject) => {
27
+ const copy = resolveLabels(labels);
28
+ const xhr = new XMLHttpRequest();
29
+ const url = `${endpoint.replace(/\/$/, "")}/${encodeURIComponent(scope)}/${encodeURIComponent(entityId)}/upload`;
30
+ xhr.open("POST", url);
31
+ xhr.responseType = "json";
32
+ xhr.withCredentials = credentials === "include";
33
+ for (const [name, value] of Object.entries(headers?.() ?? {})) {
34
+ xhr.setRequestHeader(name, value);
35
+ }
36
+ const abort = () => xhr.abort();
37
+ signal.addEventListener("abort", abort, { once: true });
38
+ xhr.upload.onprogress = (event) => {
39
+ if (event.lengthComputable) {
40
+ onProgress(Math.round(event.loaded / event.total * 100));
41
+ }
42
+ };
43
+ xhr.onload = () => {
44
+ signal.removeEventListener("abort", abort);
45
+ if (xhr.status >= 200 && xhr.status < 300) {
46
+ resolve(xhr.response);
47
+ return;
48
+ }
49
+ const message = xhr.response?.message ?? copy.uploadFailed;
50
+ reject(new Error(message));
51
+ };
52
+ xhr.onerror = () => {
53
+ signal.removeEventListener("abort", abort);
54
+ reject(new Error(copy.uploadNetworkFailed));
55
+ };
56
+ xhr.onabort = () => {
57
+ signal.removeEventListener("abort", abort);
58
+ const error = new Error("Upload cancelled");
59
+ error.name = "AbortError";
60
+ reject(error);
61
+ };
62
+ const form = new FormData();
63
+ form.append(fieldName, file);
64
+ xhr.send(form);
65
+ });
66
+ var createRemoveStrategy = ({ endpoint, headers, credentials }) => async (stored, scope, entityId) => {
67
+ const url = `${endpoint.replace(/\/$/, "")}/${encodeURIComponent(scope)}/${encodeURIComponent(entityId)}`;
68
+ const response = await fetch(url, {
69
+ method: "DELETE",
70
+ credentials,
71
+ headers: { "Content-Type": "application/json", ...headers?.() },
72
+ body: JSON.stringify({ key: stored.key })
73
+ });
74
+ if (!response.ok) return false;
75
+ const body = await response.json().catch(() => null);
76
+ return body?.deleted === true;
77
+ };
78
+ var useSlottedUploader = ({
79
+ scopes,
80
+ scope,
81
+ entityId,
82
+ strategy,
83
+ slots,
84
+ value,
85
+ onChange,
86
+ matchBy = "extension",
87
+ match,
88
+ uploadOn = "select",
89
+ onUploadStart,
90
+ removeStrategy,
91
+ onRemoveStored,
92
+ onError,
93
+ retry,
94
+ concurrency,
95
+ labels
96
+ }) => {
97
+ const copy = useUploaderLabels(labels);
98
+ useMemo(() => {
99
+ const ids = /* @__PURE__ */ new Set();
100
+ for (const slot of slots) {
101
+ if (ids.has(slot.id)) {
102
+ throw new ScopeError(
103
+ `useSlottedUploader: duplicate slot id "${slot.id}" \u2014 slot ids are storage keys and must be unique`
104
+ );
105
+ }
106
+ ids.add(slot.id);
107
+ }
108
+ if (slots.length > 1 && resolveReplaceMode(scopes.get(scope)) === "entity") {
109
+ throw new ScopeError(
110
+ `useSlottedUploader: scope "${scope}" replaces per entity, so filling one of its ${slots.length} slots would delete the others. Declare "maxFiles" on the scope (one per slot, at least), or set replace: 'key'.`
111
+ );
112
+ }
113
+ const accepted = scopes.get(scope).accept;
114
+ for (const slot of slots) {
115
+ const outside = slot.extensions.filter(
116
+ (extension) => !accepted.includes(extension)
117
+ );
118
+ if (outside.length > 0) {
119
+ warnDev(
120
+ `slot-outside-scope:${slot.id}`,
121
+ `slot "${slot.id}" accepts ${outside.join(", ")} but scope "${scope}" does not \u2014 those uploads will be rejected by validation. Align the slot's extensions with the scope's accept list.`
122
+ );
123
+ }
124
+ }
125
+ }, [slots, scopes, scope]);
126
+ const valueRef = useRef(value);
127
+ valueRef.current = value;
128
+ const onChangeRef = useRef(onChange);
129
+ onChangeRef.current = onChange;
130
+ const landedRef = useRef(/* @__PURE__ */ new Map());
131
+ for (const [slot, item] of landedRef.current) {
132
+ const absorbed = value.some(
133
+ (entry) => entry.slot === slot && entry.stored.key === item.stored.key && entry.stored.uploadedAt === item.stored.uploadedAt
134
+ );
135
+ if (absorbed) landedRef.current.delete(slot);
136
+ }
137
+ const uploader = useUploader({
138
+ scopes,
139
+ scope,
140
+ entityId,
141
+ strategy,
142
+ multiple: true,
143
+ uploadOn,
144
+ onUploadStart,
145
+ onError,
146
+ ...retry !== void 0 ? { retry } : {},
147
+ ...concurrency !== void 0 ? { concurrency } : {},
148
+ ...labels ? { labels } : {},
149
+ onUploaded: (stored) => {
150
+ for (const item of stored) {
151
+ const slot = slots.find((s) => item.fileName.startsWith(`${s.id}.`));
152
+ if (slot)
153
+ landedRef.current.set(slot.id, { slot: slot.id, stored: item });
154
+ }
155
+ const merged = new Map(valueRef.current.map((entry) => [entry.slot, entry]));
156
+ landedRef.current.forEach((entry, slot) => merged.set(slot, entry));
157
+ void onChangeRef.current([...merged.values()]);
158
+ }
159
+ });
160
+ const resolveSlot = useMemo(() => {
161
+ if (match) return match;
162
+ if (matchBy === "name") return matchSlotByName;
163
+ return (all, fileName) => matchSlotByExtension(new Set(valueRef.current.map((item) => item.slot)))(
164
+ all,
165
+ fileName
166
+ );
167
+ }, [match, matchBy]);
168
+ const assign = useCallback(
169
+ (slotId, file) => {
170
+ const slot = slots.find((s) => s.id === slotId);
171
+ if (!slot) return null;
172
+ const extension = getFileExtension(file.name);
173
+ if (!slot.extensions.includes(extension)) {
174
+ onError?.(copy.slotFormatNotAllowed(slot.label, slot.extensions));
175
+ return null;
176
+ }
177
+ return new File([file], slotFileName(slotId, file.name), {
178
+ type: file.type
179
+ });
180
+ },
181
+ [slots, onError, copy]
182
+ );
183
+ const addToSlot = useCallback(
184
+ async (slotId, file) => {
185
+ const renamed = assign(slotId, file);
186
+ if (renamed) await uploader.addFiles([renamed]);
187
+ },
188
+ [assign, uploader]
189
+ );
190
+ const addFiles = useCallback(
191
+ async (incoming) => {
192
+ const batch = [];
193
+ for (const file of Array.from(incoming)) {
194
+ const slotId = resolveSlot(slots, file.name);
195
+ if (!slotId) {
196
+ onError?.(copy.fileWithoutSlot(file.name));
197
+ continue;
198
+ }
199
+ const renamed = assign(slotId, file);
200
+ if (renamed) batch.push(renamed);
201
+ }
202
+ if (batch.length > 0) await uploader.addFiles(batch);
203
+ },
204
+ [slots, resolveSlot, assign, uploader, onError, copy]
205
+ );
206
+ const removeSlot = useCallback(
207
+ (slotId) => {
208
+ uploader.files.filter((file) => file.file.name.startsWith(`${slotId}.`)).forEach((file) => uploader.removeFile(file.id));
209
+ landedRef.current.delete(slotId);
210
+ const forgotten = valueRef.current.find((item) => item.slot === slotId);
211
+ if (forgotten) {
212
+ if (removeStrategy && !scopes.get(scope).keepOnRemove) {
213
+ removeStrategy(forgotten.stored, scope, entityId).then((deleted) => {
214
+ if (!deleted) onError?.(copy.removeFailed);
215
+ }).catch(() => onError?.(copy.removeFailed));
216
+ }
217
+ onRemoveStored?.(forgotten);
218
+ }
219
+ void onChangeRef.current(
220
+ valueRef.current.filter((item) => item.slot !== slotId)
221
+ );
222
+ },
223
+ [
224
+ uploader,
225
+ removeStrategy,
226
+ scopes,
227
+ scope,
228
+ entityId,
229
+ onRemoveStored,
230
+ onError,
231
+ copy
232
+ ]
233
+ );
234
+ const abort = useCallback(
235
+ (slotId) => {
236
+ if (!slotId) {
237
+ uploader.abort();
238
+ return;
239
+ }
240
+ uploader.files.filter((file) => file.file.name.startsWith(`${slotId}.`)).forEach((file) => uploader.abort(file.id));
241
+ },
242
+ [uploader]
243
+ );
244
+ const slotStates = useMemo(
245
+ () => slots.map((definition) => ({
246
+ definition,
247
+ filled: value.find((item) => item.slot === definition.id),
248
+ pending: uploader.files.find(
249
+ (file) => file.file.name.startsWith(`${definition.id}.`) && file.status !== "success"
250
+ )
251
+ })),
252
+ [slots, value, uploader.files]
253
+ );
254
+ const accept = useMemo(
255
+ () => toAcceptAttribute([...new Set(slots.flatMap((s) => s.extensions))]),
256
+ [slots]
257
+ );
258
+ const upload = useCallback(async () => {
259
+ await uploader.upload();
260
+ }, [uploader]);
261
+ return {
262
+ slots: slotStates,
263
+ accept,
264
+ isUploading: uploader.isUploading,
265
+ hasPending: uploader.hasPending,
266
+ addFiles,
267
+ addToSlot,
268
+ removeSlot,
269
+ abort,
270
+ upload
271
+ };
272
+ };
273
+
274
+ // src/react/viewResolver.ts
275
+ var isOwnUrl = (url, baseUrl) => {
276
+ if (url.startsWith("/")) return true;
277
+ try {
278
+ return new URL(url).origin === new URL(baseUrl).origin;
279
+ } catch {
280
+ return false;
281
+ }
282
+ };
283
+ var fetchStored = async (url, { baseUrl, headers, credentials, labels }) => {
284
+ const own = isOwnUrl(url, baseUrl);
285
+ const response = await fetch(
286
+ url.startsWith("/") ? `${baseUrl.replace(/\/$/, "")}${url}` : url,
287
+ own ? { headers: headers?.(), credentials } : void 0
288
+ );
289
+ if (!response.ok) {
290
+ throw new Error(resolveLabels(labels).readFailed);
291
+ }
292
+ return response;
293
+ };
294
+ var retyped = (blob, mimeType) => mimeType && (!blob.type || blob.type === "application/octet-stream") ? new Blob([blob], { type: mimeType }) : blob;
295
+ var createBlobUrlResolver = (options) => async (file) => {
296
+ if (!isOwnUrl(file.url, options.baseUrl)) return file.url;
297
+ const blob = await (await fetchStored(file.url, options)).blob();
298
+ return URL.createObjectURL(retyped(blob, file.mimeType));
299
+ };
300
+ var createBytesResolver = (options) => async (url) => new Uint8Array(await (await fetchStored(url, options)).arrayBuffer());
301
+ var viewUrlFileName = (url) => {
302
+ const query = url.split("?")[1];
303
+ const key = query ? new URLSearchParams(query).get("key") : null;
304
+ return (key ?? url.split("?")[0]).split("/").pop() || void 0;
305
+ };
306
+
307
+ export { createBlobUrlResolver, createBytesResolver, createRemoveStrategy, createXhrUploadStrategy, matchSlotByExtension, matchSlotByName, slotFileName, slotOfStored, useSlottedUploader, viewUrlFileName };
308
+ //# sourceMappingURL=chunk-PTEX7F4R.js.map
309
+ //# sourceMappingURL=chunk-PTEX7F4R.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/react/slots.ts","../src/react/strategy.ts","../src/react/useSlottedUploader.ts","../src/react/viewResolver.ts"],"names":[],"mappings":"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","file":"chunk-PTEX7F4R.js","sourcesContent":["import { getFileExtension } from '../file'\nimport type { FileExtension, StoredFile } from '../types'\n/** A named position a single file can fill (letterhead, CSF, acta, …). */\nexport type SlotDefinition = {\n\t/** Canonical id — becomes the stored file name (`{id}.{ext}`), so the\n\t * scope's `path` yields a stable key and re-uploads overwrite. */\n\tid: string\n\t/** Human label shown in the row. */\n\tlabel: string\n\t/** Lower-case extensions this slot takes. Narrower than the scope, never wider. */\n\textensions: FileExtension[]\n\thint?: string\n}\n\n/** A stored file tagged with the slot it fills. The app persists these. */\nexport type SlottedFile = {\n\tslot: string\n\tstored: StoredFile\n}\n\n/** Resolves which slot an incoming file belongs to, or `null` for none. */\nexport type SlotMatcher = (\n\tslots: SlotDefinition[],\n\tfileName: string\n) => string | null\n\n/**\n * Default matcher: the file's extension picks the slot. Prefers an empty slot\n * so dropping three files fills three positions; falls back to the first\n * extension match, which reads as \"replace what is there\".\n */\nexport const matchSlotByExtension =\n\t(filled: ReadonlySet<string>): SlotMatcher =>\n\t(slots, fileName) => {\n\t\tconst extension = getFileExtension(fileName) as FileExtension\n\t\tconst candidates = slots.filter(slot => slot.extensions.includes(extension))\n\t\tif (candidates.length === 0) return null\n\t\treturn (candidates.find(slot => !filled.has(slot.id)) ?? candidates[0]!).id\n\t}\n\n/** Matcher for uploads named after their slot (`letterhead-a4.pdf` → slot `letterhead-a4`). */\nexport const matchSlotByName: SlotMatcher = (slots, fileName) => {\n\tconst base = fileName.toLowerCase()\n\treturn slots.find(slot => base.startsWith(slot.id.toLowerCase()))?.id ?? null\n}\n\n/** The name a file adopts when it fills a slot. */\nexport const slotFileName = (slotId: string, originalName: string): string =>\n\t`${slotId}.${getFileExtension(originalName)}`\n\n/** The slot a stored file fills, recovered from its name. `null` when foreign. */\nexport const slotOfStored = (\n\tslots: SlotDefinition[],\n\tstored: StoredFile\n): string | null =>\n\tslots.find(slot => stored.fileName.startsWith(`${slot.id}.`))?.id ?? null\n","import { resolveLabels, type UploaderLabels } from '../labels'\nimport type { StoredFile } from '../types'\nimport type { RemoveStrategy, UploadStrategy } from './types'\n\nexport type XhrUploadStrategyOptions = {\n\t/** Base URL of the storage router, e.g. `${apiUrl}/storage`. */\n\tendpoint: string\n\t/** Evaluated per upload so a rotating JWT is read at send time, not at setup. */\n\theaders?: () => Record<string, string>\n\t/** Multipart field name the server reads. @defaultValue 'file' */\n\tfieldName?: string\n\t/**\n\t * `'include'` sends the session cookie cross-origin, which a cookie-session\n\t * app needs: its api answers on a different origin than the SPA, and the\n\t * browser drops the cookie otherwise. Maps to `XMLHttpRequest.withCredentials`.\n\t *\n\t * @defaultValue 'same-origin'\n\t */\n\tcredentials?: RequestCredentials\n\t/**\n\t * Copy for the two failures the transport itself words. Pass the same\n\t * object the uploader renders with, or the request that fails speaks a\n\t * different language than the row reporting it.\n\t */\n\tlabels?: Partial<UploaderLabels>\n}\n\n/**\n * Default transport: `POST {endpoint}/{scope}/{entityId}/upload` as multipart.\n *\n * XHR instead of `fetch` on purpose — `fetch` still has no usable upload\n * progress in browsers, and per-file progress is half the point of the hook.\n */\nexport const createXhrUploadStrategy =\n\t({\n\t\tendpoint,\n\t\theaders,\n\t\tfieldName = 'file',\n\t\tcredentials,\n\t\tlabels,\n\t}: XhrUploadStrategyOptions): UploadStrategy =>\n\t(file, scope, entityId, { onProgress, signal }) =>\n\t\tnew Promise<StoredFile>((resolve, reject) => {\n\t\t\tconst copy = resolveLabels(labels)\n\t\t\tconst xhr = new XMLHttpRequest()\n\t\t\tconst url = `${endpoint.replace(/\\/$/, '')}/${encodeURIComponent(scope)}/${encodeURIComponent(entityId)}/upload`\n\t\t\txhr.open('POST', url)\n\t\t\txhr.responseType = 'json'\n\t\t\txhr.withCredentials = credentials === 'include'\n\t\t\tfor (const [name, value] of Object.entries(headers?.() ?? {})) {\n\t\t\t\txhr.setRequestHeader(name, value)\n\t\t\t}\n\n\t\t\tconst abort = () => xhr.abort()\n\t\t\tsignal.addEventListener('abort', abort, { once: true })\n\n\t\t\txhr.upload.onprogress = event => {\n\t\t\t\tif (event.lengthComputable) {\n\t\t\t\t\tonProgress(Math.round((event.loaded / event.total) * 100))\n\t\t\t\t}\n\t\t\t}\n\n\t\t\txhr.onload = () => {\n\t\t\t\tsignal.removeEventListener('abort', abort)\n\t\t\t\tif (xhr.status >= 200 && xhr.status < 300) {\n\t\t\t\t\tresolve(xhr.response as StoredFile)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\t// The server handler answers { message } already worded for the\n\t\t\t\t// user; the label is only the fallback when it answered nothing.\n\t\t\t\tconst message =\n\t\t\t\t\t(xhr.response as { message?: string } | null)?.message ??\n\t\t\t\t\tcopy.uploadFailed\n\t\t\t\treject(new Error(message))\n\t\t\t}\n\n\t\t\txhr.onerror = () => {\n\t\t\t\tsignal.removeEventListener('abort', abort)\n\t\t\t\treject(new Error(copy.uploadNetworkFailed))\n\t\t\t}\n\n\t\t\txhr.onabort = () => {\n\t\t\t\tsignal.removeEventListener('abort', abort)\n\t\t\t\tconst error = new Error('Upload cancelled')\n\t\t\t\terror.name = 'AbortError'\n\t\t\t\treject(error)\n\t\t\t}\n\n\t\t\tconst form = new FormData()\n\t\t\tform.append(fieldName, file)\n\t\t\txhr.send(form)\n\t\t})\n\nexport type RemoveStrategyOptions = Omit<XhrUploadStrategyOptions, 'fieldName'>\n\n/**\n * Default deletion transport: `DELETE {endpoint}/{scope}/{entityId}` with\n * `{ key }` as JSON — the route the package's own Express/Next handlers\n * document (`router.delete('/:scope/:entityId', handlers.remove)`).\n *\n * Plain `fetch`, not XHR: a delete has no body worth a progress bar, which is\n * the only reason the upload half needs XMLHttpRequest.\n */\nexport const createRemoveStrategy =\n\t({ endpoint, headers, credentials }: RemoveStrategyOptions): RemoveStrategy =>\n\tasync (stored, scope, entityId) => {\n\t\tconst url = `${endpoint.replace(/\\/$/, '')}/${encodeURIComponent(scope)}/${encodeURIComponent(entityId)}`\n\t\tconst response = await fetch(url, {\n\t\t\tmethod: 'DELETE',\n\t\t\tcredentials,\n\t\t\theaders: { 'Content-Type': 'application/json', ...headers?.() },\n\t\t\tbody: JSON.stringify({ key: stored.key }),\n\t\t})\n\t\tif (!response.ok) return false\n\t\tconst body = (await response.json().catch(() => null)) as {\n\t\t\tdeleted?: boolean\n\t\t} | null\n\t\treturn body?.deleted === true\n\t}\n","import { useCallback, useMemo, useRef } from 'react'\n\nimport { getFileExtension, toAcceptAttribute } from '../file'\nimport type { UploaderLabels } from '../labels'\nimport { resolveReplaceMode } from '../scopes'\nimport { ScopeError } from '../scopes'\nimport type { ScopeConfig, ScopeRegistry } from '../types'\nimport type { FileExtension } from '../types'\nimport { warnDev } from '../warn'\nimport {\n\tmatchSlotByExtension,\n\tmatchSlotByName,\n\ttype SlotDefinition,\n\tslotFileName,\n\ttype SlotMatcher,\n\ttype SlottedFile,\n} from './slots'\nimport type { RemoveStrategy, UploaderFile, UploadStrategy } from './types'\nimport { useUploaderLabels } from './UploaderProvider'\nimport {\n\ttype RetryOptions,\n\ttype UploadTrigger,\n\tuseUploader,\n} from './useUploader'\n\nexport type UseSlottedUploaderOptions<T extends Record<string, ScopeConfig>> = {\n\tscopes: ScopeRegistry<T>\n\tscope: keyof T & string\n\tentityId: string\n\tstrategy?: UploadStrategy\n\t/**\n\t * Deletion transport. With it, a confirmed `removeSlot` deletes the object\n\t * from storage BY ITSELF — unless the scope is `keepOnRemove`, the history\n\t * contract — so no consumer can orphan by forgetting a callback.\n\t * `onRemoveStored` still fires for the app's own bookkeeping.\n\t */\n\tremoveStrategy?: RemoveStrategy\n\tslots: SlotDefinition[]\n\t/** Currently persisted files. The caller owns persistence — controlled. */\n\tvalue: SlottedFile[]\n\t/** Called with the next full set after an upload lands or a slot empties. */\n\tonChange: (next: SlottedFile[]) => void | Promise<void>\n\t/** How dropped files find their slot. @defaultValue 'extension' */\n\tmatchBy?: 'extension' | 'name'\n\t/** Custom matcher for special rules; overrides `matchBy`. */\n\tmatch?: SlotMatcher\n\t/** @defaultValue 'select' — a slot names its file, so it usually travels at once. */\n\tuploadOn?: UploadTrigger\n\t/** Fired when a batch starts travelling. Same contract as `useUploader`. */\n\tonUploadStart?: (files: UploaderFile[]) => void\n\t/**\n\t * Fired with the PERSISTED file a `removeSlot` just forgot, before\n\t * `onChange` delivers the filtered set. This is where the app deletes the\n\t * object from storage — without it the consumer had to diff old-vs-new\n\t * arrays to even learn which key vanished, and every stable-key scope\n\t * orphaned on remove.\n\t */\n\tonRemoveStored?: (stored: SlottedFile) => void\n\tonError?: (message: string) => void\n\t/** Forwarded to the underlying `useUploader`. */\n\tretry?: number | RetryOptions\n\t/** Forwarded to the underlying `useUploader`. */\n\tconcurrency?: number\n\tlabels?: Partial<UploaderLabels>\n}\n\nexport type SlotState = {\n\tdefinition: SlotDefinition\n\t/** Persisted file occupying the slot, if any. */\n\tfilled?: SlottedFile\n\t/** In-flight or failed upload for the slot, if any. */\n\tpending?: UploaderFile\n}\n\nexport type UseSlottedUploaderReturn = {\n\tslots: SlotState[]\n\t/** `<input accept>` derived from the union of the slots' extensions. */\n\taccept: string\n\tisUploading: boolean\n\t/** Route a dropped/browsed batch to slots via the matcher. */\n\taddFiles: (incoming: FileList | File[]) => Promise<void>\n\t/** Put one file in one specific slot (per-row browse button). */\n\taddToSlot: (slotId: string, file: File) => Promise<void>\n\t/**\n\t * Forget the persisted file of a slot. Remote deletion stays the app's\n\t * call — made through `onRemoveStored`, which receives the forgotten file.\n\t */\n\tremoveSlot: (slotId: string) => void\n\tabort: (slotId?: string) => void\n\t/** Uploads every slot still holding an `idle` file (`uploadOn: 'manual'`). */\n\tupload: () => Promise<void>\n\t/** `true` while any slot holds an `idle` file waiting for `upload()`. */\n\thasPending: boolean\n}\n\n/**\n * Named-slot collection over the SAME machine as the plain uploader: this hook\n * only decides *which slot* a file lands in and renames it to `{slot}.{ext}`;\n * validation, compression, progress and abort are `useUploader` verbatim. One\n * behavior, two presentations — a fix in the machine reaches both.\n */\nexport const useSlottedUploader = <T extends Record<string, ScopeConfig>>({\n\tscopes,\n\tscope,\n\tentityId,\n\tstrategy,\n\tslots,\n\tvalue,\n\tonChange,\n\tmatchBy = 'extension',\n\tmatch,\n\tuploadOn = 'select',\n\tonUploadStart,\n\tremoveStrategy,\n\tonRemoveStored,\n\tonError,\n\tretry,\n\tconcurrency,\n\tlabels,\n}: UseSlottedUploaderOptions<T>): UseSlottedUploaderReturn => {\n\tconst copy = useUploaderLabels(labels)\n\t// Impossible configs throw, \"will fail at upload time\" configs warn once.\n\t// See AGENTS.md §3.1.\n\tuseMemo(() => {\n\t\tconst ids = new Set<string>()\n\t\tfor (const slot of slots) {\n\t\t\tif (ids.has(slot.id)) {\n\t\t\t\tthrow new ScopeError(\n\t\t\t\t\t`useSlottedUploader: duplicate slot id \"${slot.id}\" — slot ids are storage keys and must be unique`\n\t\t\t\t)\n\t\t\t}\n\t\t\tids.add(slot.id)\n\t\t}\n\t\t// A slot per position means the scope holds several files at once, so a\n\t\t// sweep would delete the sibling slots on every upload — silently, and\n\t\t// only in production. Same tier as a duplicate slot id: it can never\n\t\t// work, so it fails at import.\n\t\tif (\n\t\t\tslots.length > 1 &&\n\t\t\tresolveReplaceMode(scopes.get(scope)) === 'entity'\n\t\t) {\n\t\t\tthrow new ScopeError(\n\t\t\t\t`useSlottedUploader: scope \"${scope}\" replaces per entity, so filling one of its ` +\n\t\t\t\t\t`${slots.length} slots would delete the others. Declare \"maxFiles\" on the scope ` +\n\t\t\t\t\t\"(one per slot, at least), or set replace: 'key'.\"\n\t\t\t)\n\t\t}\n\n\t\tconst accepted = scopes.get(scope).accept\n\t\tfor (const slot of slots) {\n\t\t\tconst outside = slot.extensions.filter(\n\t\t\t\textension => !accepted.includes(extension)\n\t\t\t)\n\t\t\tif (outside.length > 0) {\n\t\t\t\twarnDev(\n\t\t\t\t\t`slot-outside-scope:${slot.id}`,\n\t\t\t\t\t`slot \"${slot.id}\" accepts ${outside.join(', ')} but scope \"${scope}\" does not — those uploads will be rejected by validation. Align the slot's extensions with the scope's accept list.`\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}, [slots, scopes, scope])\n\n\t// Latest persisted set, readable from inside onUploaded without re-binding\n\t// the uploader callbacks on every render.\n\tconst valueRef = useRef(value)\n\tvalueRef.current = value\n\tconst onChangeRef = useRef(onChange)\n\tonChangeRef.current = onChange\n\n\t// Uploads the parent has not absorbed into `value` yet. Two quick drops\n\t// would otherwise race the controlled state: the second onChange reads a\n\t// `value` that misses the first drop's entry and silently loses it.\n\tconst landedRef = useRef(new Map<string, SlottedFile>())\n\tfor (const [slot, item] of landedRef.current) {\n\t\tconst absorbed = value.some(\n\t\t\tentry =>\n\t\t\t\tentry.slot === slot &&\n\t\t\t\tentry.stored.key === item.stored.key &&\n\t\t\t\tentry.stored.uploadedAt === item.stored.uploadedAt\n\t\t)\n\t\tif (absorbed) landedRef.current.delete(slot)\n\t}\n\n\tconst uploader = useUploader({\n\t\tscopes,\n\t\tscope,\n\t\tentityId,\n\t\tstrategy,\n\t\tmultiple: true,\n\t\tuploadOn,\n\t\tonUploadStart,\n\t\tonError,\n\t\t...(retry !== undefined ? { retry } : {}),\n\t\t...(concurrency !== undefined ? { concurrency } : {}),\n\t\t...(labels ? { labels } : {}),\n\t\tonUploaded: stored => {\n\t\t\t// Files were renamed to `{slot}.{ext}` before entering the machine, so\n\t\t\t// the slot is recoverable from the confirmed name.\n\t\t\tfor (const item of stored) {\n\t\t\t\tconst slot = slots.find(s => item.fileName.startsWith(`${s.id}.`))\n\t\t\t\tif (slot)\n\t\t\t\t\tlandedRef.current.set(slot.id, { slot: slot.id, stored: item })\n\t\t\t}\n\t\t\tconst merged = new Map(valueRef.current.map(entry => [entry.slot, entry]))\n\t\t\tlandedRef.current.forEach((entry, slot) => merged.set(slot, entry))\n\t\t\tvoid onChangeRef.current([...merged.values()])\n\t\t},\n\t})\n\n\tconst resolveSlot = useMemo<SlotMatcher>(() => {\n\t\tif (match) return match\n\t\tif (matchBy === 'name') return matchSlotByName\n\t\treturn (all, fileName) =>\n\t\t\tmatchSlotByExtension(new Set(valueRef.current.map(item => item.slot)))(\n\t\t\t\tall,\n\t\t\t\tfileName\n\t\t\t)\n\t}, [match, matchBy])\n\n\t// Renames a file after its slot, or reports why it cannot fill it.\n\tconst assign = useCallback(\n\t\t(slotId: string, file: File): File | null => {\n\t\t\tconst slot = slots.find(s => s.id === slotId)\n\t\t\tif (!slot) return null\n\n\t\t\tconst extension = getFileExtension(file.name) as FileExtension\n\t\t\tif (!slot.extensions.includes(extension)) {\n\t\t\t\tonError?.(copy.slotFormatNotAllowed(slot.label, slot.extensions))\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn new File([file], slotFileName(slotId, file.name), {\n\t\t\t\ttype: file.type,\n\t\t\t})\n\t\t},\n\t\t[slots, onError, copy]\n\t)\n\n\tconst addToSlot = useCallback(\n\t\tasync (slotId: string, file: File) => {\n\t\t\tconst renamed = assign(slotId, file)\n\t\t\tif (renamed) await uploader.addFiles([renamed])\n\t\t},\n\t\t[assign, uploader]\n\t)\n\n\tconst addFiles = useCallback(\n\t\tasync (incoming: FileList | File[]) => {\n\t\t\t// One batch for the whole drop: a single onUploaded → a single\n\t\t\t// onChange carrying every slot that landed.\n\t\t\tconst batch: File[] = []\n\t\t\tfor (const file of Array.from(incoming)) {\n\t\t\t\tconst slotId = resolveSlot(slots, file.name)\n\t\t\t\tif (!slotId) {\n\t\t\t\t\tonError?.(copy.fileWithoutSlot(file.name))\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst renamed = assign(slotId, file)\n\t\t\t\tif (renamed) batch.push(renamed)\n\t\t\t}\n\t\t\tif (batch.length > 0) await uploader.addFiles(batch)\n\t\t},\n\t\t[slots, resolveSlot, assign, uploader, onError, copy]\n\t)\n\n\tconst removeSlot = useCallback(\n\t\t(slotId: string) => {\n\t\t\t// Drop any in-flight attempt for the slot too, or a slow upload would\n\t\t\t// resurrect the file the user just removed.\n\t\t\tuploader.files\n\t\t\t\t.filter(file => file.file.name.startsWith(`${slotId}.`))\n\t\t\t\t.forEach(file => uploader.removeFile(file.id))\n\t\t\tlandedRef.current.delete(slotId)\n\t\t\tconst forgotten = valueRef.current.find(item => item.slot === slotId)\n\t\t\tif (forgotten) {\n\t\t\t\t// The package deletes; the app only observes. Fire-and-forget\n\t\t\t\t// with the reference ordering: forget the reference regardless,\n\t\t\t\t// because a dangling pointer is worse than an orphan — and\n\t\t\t\t// surface a refused delete instead of swallowing it.\n\t\t\t\tif (removeStrategy && !scopes.get(scope).keepOnRemove) {\n\t\t\t\t\tremoveStrategy(forgotten.stored, scope, entityId)\n\t\t\t\t\t\t.then(deleted => {\n\t\t\t\t\t\t\tif (!deleted) onError?.(copy.removeFailed)\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(() => onError?.(copy.removeFailed))\n\t\t\t\t}\n\t\t\t\tonRemoveStored?.(forgotten)\n\t\t\t}\n\t\t\tvoid onChangeRef.current(\n\t\t\t\tvalueRef.current.filter(item => item.slot !== slotId)\n\t\t\t)\n\t\t},\n\t\t[\n\t\t\tuploader,\n\t\t\tremoveStrategy,\n\t\t\tscopes,\n\t\t\tscope,\n\t\t\tentityId,\n\t\t\tonRemoveStored,\n\t\t\tonError,\n\t\t\tcopy,\n\t\t]\n\t)\n\n\tconst abort = useCallback(\n\t\t(slotId?: string) => {\n\t\t\tif (!slotId) {\n\t\t\t\tuploader.abort()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tuploader.files\n\t\t\t\t.filter(file => file.file.name.startsWith(`${slotId}.`))\n\t\t\t\t.forEach(file => uploader.abort(file.id))\n\t\t},\n\t\t[uploader]\n\t)\n\n\tconst slotStates = useMemo<SlotState[]>(\n\t\t() =>\n\t\t\tslots.map(definition => ({\n\t\t\t\tdefinition,\n\t\t\t\tfilled: value.find(item => item.slot === definition.id),\n\t\t\t\tpending: uploader.files.find(\n\t\t\t\t\tfile =>\n\t\t\t\t\t\tfile.file.name.startsWith(`${definition.id}.`) &&\n\t\t\t\t\t\tfile.status !== 'success'\n\t\t\t\t),\n\t\t\t})),\n\t\t[slots, value, uploader.files]\n\t)\n\n\tconst accept = useMemo(\n\t\t() => toAcceptAttribute([...new Set(slots.flatMap(s => s.extensions))]),\n\t\t[slots]\n\t)\n\n\tconst upload = useCallback(async () => {\n\t\tawait uploader.upload()\n\t}, [uploader])\n\n\treturn {\n\t\tslots: slotStates,\n\t\taccept,\n\t\tisUploading: uploader.isUploading,\n\t\thasPending: uploader.hasPending,\n\t\taddFiles,\n\t\taddToSlot,\n\t\tremoveSlot,\n\t\tabort,\n\t\tupload,\n\t}\n}\n","import { resolveLabels, type UploaderLabels } from '../labels'\n\nexport type BlobUrlResolverOptions = {\n\t/** Prefix for app-relative urls (`/storage/...`) — usually the api origin. */\n\tbaseUrl: string\n\t/** Evaluated per request, so a rotating JWT is read at fetch time. */\n\theaders?: () => Record<string, string>\n\t/**\n\t * Forwarded to `fetch` for urls the app owns. Cookie-session apps need\n\t * `'include'`: their api lives on a different origin than the SPA, so the\n\t * browser omits the session cookie under the default.\n\t *\n\t * @defaultValue `fetch`'s own (`'same-origin'`)\n\t */\n\tcredentials?: RequestCredentials\n\t/** Copy for the failure this throws. Defaults to English. */\n\tlabels?: Partial<UploaderLabels>\n}\n\n/**\n * True when `baseUrl` serves this url: app-relative, or absolute on the same\n * origin. The persisted url of an encrypted scope is whatever the server's\n * `encryptedUrl` returned, which is commonly absolute — treating it as foreign\n * would fetch the app's own authenticated endpoint with no credentials.\n */\nconst isOwnUrl = (url: string, baseUrl: string): boolean => {\n\tif (url.startsWith('/')) return true\n\ttry {\n\t\treturn new URL(url).origin === new URL(baseUrl).origin\n\t} catch {\n\t\treturn false\n\t}\n}\n\n/**\n * The one rule both resolvers follow — a change to the `/view` shape lands\n * here, not once per output format.\n *\n * @see AGENTS.md §3 — why a foreign url must never carry the app's headers\n */\nconst fetchStored = async (\n\turl: string,\n\t{ baseUrl, headers, credentials, labels }: BlobUrlResolverOptions\n): Promise<Response> => {\n\tconst own = isOwnUrl(url, baseUrl)\n\tconst response = await fetch(\n\t\turl.startsWith('/') ? `${baseUrl.replace(/\\/$/, '')}${url}` : url,\n\t\town ? { headers: headers?.(), credentials } : undefined\n\t)\n\tif (!response.ok) {\n\t\tthrow new Error(resolveLabels(labels).readFailed)\n\t}\n\treturn response\n}\n\n/**\n * An object URL carries the response's Content-Type, and that alone decides how\n * a browser renders a `blob:` — a PDF served as `octet-stream` never reaches\n * the built-in viewer, it silently downloads. When the file declares a type and\n * the response does not, believe the file.\n */\nconst retyped = (blob: Blob, mimeType?: string): Blob =>\n\tmimeType && (!blob.type || blob.type === 'application/octet-stream')\n\t\t? new Blob([blob], { type: mimeType })\n\t\t: blob\n\n/**\n * `resolveUrl` for the FileViewer over authenticated endpoints. An `<img>` or\n * `<iframe>` cannot send an Authorization header or a cross-site cookie, so\n * protected content (the decrypting `/view` route of encrypted scopes) is\n * fetched here with the app's credentials and handed to the viewer as an\n * object URL.\n *\n * Urls on a foreign origin pass through untouched — public objects and legacy\n * hosts render directly. `FileViewer` owns what this mints: it revokes the\n * object URL when it moves to another file and when it closes.\n */\nexport const createBlobUrlResolver =\n\t(options: BlobUrlResolverOptions) =>\n\tasync (file: { url: string; mimeType?: string }): Promise<string> => {\n\t\tif (!isOwnUrl(file.url, options.baseUrl)) return file.url\n\t\tconst blob = await (await fetchStored(file.url, options)).blob()\n\t\treturn URL.createObjectURL(retyped(blob, file.mimeType))\n\t}\n\n/**\n * The same read, as BYTES — for code that processes a stored file rather than\n * displaying it: a pdf-lib render, a canvas, a parser.\n *\n * Takes the url directly, not a `{ url }`: its sibling's shape is dictated by\n * the viewer's `resolveUrl` prop, while this one is called from app code that\n * usually holds a `StoredFile.url` and nothing else.\n */\nexport const createBytesResolver =\n\t(options: BlobUrlResolverOptions) =>\n\tasync (url: string): Promise<Uint8Array> =>\n\t\tnew Uint8Array(await (await fetchStored(url, options)).arrayBuffer())\n\n/**\n * Display name of a persisted url. Understands the `/view?key=…` shape the\n * decrypting endpoint uses (the file name is the key's last segment) and\n * falls back to the url's own last segment for direct links.\n */\nexport const viewUrlFileName = (url: string): string | undefined => {\n\tconst query = url.split('?')[1]\n\tconst key = query ? new URLSearchParams(query).get('key') : null\n\treturn (key ?? url.split('?')[0]!).split('/').pop() || undefined\n}\n"]}