@particle-academy/react-fancy 5.19.0 → 5.21.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 (53) hide show
  1. package/dist/{AudioViewer.types-VWhRx6XG.d.cts → AudioViewer.types-CyhmQVQd.d.cts} +13 -0
  2. package/dist/{AudioViewer.types-VWhRx6XG.d.ts → AudioViewer.types-CyhmQVQd.d.ts} +13 -0
  3. package/dist/{VideoViewer.types-CTwS6PcM.d.cts → VideoViewer.types-_AwQ8DQr.d.cts} +13 -0
  4. package/dist/{VideoViewer.types-CTwS6PcM.d.ts → VideoViewer.types-_AwQ8DQr.d.ts} +13 -0
  5. package/dist/audio-viewer.cjs +2 -2
  6. package/dist/audio-viewer.d.cts +1 -1
  7. package/dist/audio-viewer.d.ts +1 -1
  8. package/dist/audio-viewer.js +1 -1
  9. package/dist/{chunk-3GRC5J2L.cjs → chunk-3MHVFEIP.cjs} +4 -2
  10. package/dist/chunk-3MHVFEIP.cjs.map +1 -0
  11. package/dist/{chunk-EIBW5YLS.js → chunk-74ZN5CNE.js} +4 -2
  12. package/dist/chunk-74ZN5CNE.js.map +1 -0
  13. package/dist/{chunk-KE7KXY5C.cjs → chunk-BWTFK2GV.cjs} +6 -6
  14. package/dist/{chunk-KE7KXY5C.cjs.map → chunk-BWTFK2GV.cjs.map} +1 -1
  15. package/dist/chunk-FWCREAIT.js +121 -0
  16. package/dist/chunk-FWCREAIT.js.map +1 -0
  17. package/dist/chunk-HPNIEYSJ.cjs +131 -0
  18. package/dist/chunk-HPNIEYSJ.cjs.map +1 -0
  19. package/dist/{chunk-3ETOEEVN.cjs → chunk-I462NM2A.cjs} +4 -2
  20. package/dist/chunk-I462NM2A.cjs.map +1 -0
  21. package/dist/{chunk-J3XS4HHX.js → chunk-QWSTG5MF.js} +4 -4
  22. package/dist/{chunk-J3XS4HHX.js.map → chunk-QWSTG5MF.js.map} +1 -1
  23. package/dist/{chunk-JKO2Y5SH.js → chunk-YVDAXYHP.js} +4 -2
  24. package/dist/chunk-YVDAXYHP.js.map +1 -0
  25. package/dist/editor.cjs +1 -1
  26. package/dist/editor.js +1 -1
  27. package/dist/{index-B71wa9AW.d.cts → index-CgPpIw_O.d.cts} +2 -2
  28. package/dist/{index-bIntFOl5.d.ts → index-IeivGsNg.d.ts} +2 -2
  29. package/dist/index.cjs +38 -10
  30. package/dist/index.d.cts +75 -4
  31. package/dist/index.d.ts +75 -4
  32. package/dist/index.js +4 -4
  33. package/dist/media-viewer.cjs +4 -4
  34. package/dist/media-viewer.d.cts +3 -3
  35. package/dist/media-viewer.d.ts +3 -3
  36. package/dist/media-viewer.js +3 -3
  37. package/dist/mobile-menu.cjs +1 -1
  38. package/dist/mobile-menu.js +1 -1
  39. package/dist/sidebar.cjs +1 -1
  40. package/dist/sidebar.js +1 -1
  41. package/dist/video-viewer.cjs +2 -2
  42. package/dist/video-viewer.d.cts +1 -1
  43. package/dist/video-viewer.d.ts +1 -1
  44. package/dist/video-viewer.js +1 -1
  45. package/package.json +2 -2
  46. package/dist/chunk-3ETOEEVN.cjs.map +0 -1
  47. package/dist/chunk-3GRC5J2L.cjs.map +0 -1
  48. package/dist/chunk-EIBW5YLS.js.map +0 -1
  49. package/dist/chunk-G34OBTU3.cjs +0 -27
  50. package/dist/chunk-G34OBTU3.cjs.map +0 -1
  51. package/dist/chunk-JKO2Y5SH.js.map +0 -1
  52. package/dist/chunk-RNPFSVFN.js +0 -24
  53. package/dist/chunk-RNPFSVFN.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/VideoViewer/VideoViewer.tsx"],"names":["forwardRef","useState","jsx","cn","jsxs"],"mappings":";;;;;;AAYO,IAAM,WAAA,GAAcA,gBAAA;AAAA,EACzB,CACE;AAAA,IACE,GAAA;AAAA,IACA,MAAA;AAAA,IACA,QAAA,GAAW,IAAA;AAAA,IACX,QAAA,GAAW,KAAA;AAAA,IACX,IAAA,GAAO,KAAA;AAAA,IACP,OAAA,GAAU,UAAA;AAAA,IACV,KAAA,GAAQ,KAAA;AAAA,IACR,GAAA,GAAM,SAAA;AAAA,IACN,OAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,KAEF,GAAA,KACG;AACH,IAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAIC,eAAS,KAAK,CAAA;AAE5C,IAAA,uBACEC,cAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,+BAAA,EAA8B,EAAA;AAAA,QAC9B,SAAA,EAAWC,oBAAA;AAAA,UACT,+EAAA;AAAA,UACA;AAAA,SACF;AAAA,QACA,KAAA;AAAA,QAEC,QAAA,EAAA,OAAA,mBACCC,eAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,uFAAA,EACb,QAAA,EAAA;AAAA,0BAAAF,cAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,aAAA,EAAc,QAAA,EAAA,qBAAA,EAAwB,CAAA;AAAA,0BACtDA,cAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,wCAAA,EAA0C,QAAA,EAAA,GAAA,EAAI;AAAA,SAAA,EAChE,CAAA,mBAEAA,cAAA;AAAA,UAAC,OAAA;AAAA,UAAA;AAAA,YACC,GAAA;AAAA,YACA,OAAA;AAAA,YACA,MAAA;AAAA,YACA,QAAA;AAAA,YACA,QAAA;AAAA,YACA,IAAA;AAAA,YACA,KAAA;AAAA,YACA,WAAA,EAAW,IAAA;AAAA,YACX,SAAS,MAAM;AACb,cAAA,UAAA,CAAW,IAAI,CAAA;AACf,cAAA,OAAA,IAAU;AAAA,YACZ,CAAA;AAAA,YACA,SAAA,EAAWC,oBAAA;AAAA,cACT,uBAAA;AAAA,cACA,GAAA,KAAQ,UAAU,4BAAA,GAA+B;AAAA;AACnD;AAAA;AACF;AAAA,KAEJ;AAAA,EAEJ;AACF;AAEA,WAAA,CAAY,WAAA,GAAc,aAAA","file":"chunk-I462NM2A.cjs","sourcesContent":["import { forwardRef, useState } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport type { VideoViewerProps } from \"./VideoViewer.types\";\n\n/**\n * A standalone video viewer — native controls, optional poster, fit-to-box, and\n * dark-aware framing. Renders for video sources inside `<MediaViewer>`.\n *\n * ```tsx\n * <VideoViewer src=\"/clip.mp4\" poster=\"/clip.jpg\" style={{ height: 360 }} />\n * ```\n */\nexport const VideoViewer = forwardRef<HTMLDivElement, VideoViewerProps>(\n (\n {\n src,\n poster,\n controls = true,\n autoPlay = false,\n loop = false,\n preload = \"metadata\",\n muted = false,\n fit = \"contain\",\n onError,\n className,\n style,\n },\n ref,\n ) => {\n const [errored, setErrored] = useState(false);\n\n return (\n <div\n ref={ref}\n data-react-fancy-video-viewer=\"\"\n className={cn(\n \"relative flex items-center justify-center overflow-hidden rounded-md bg-black\",\n className,\n )}\n style={style}\n >\n {errored ? (\n <div className=\"flex flex-col items-center justify-center gap-1 p-4 text-center text-sm text-zinc-400\">\n <span className=\"font-medium\">Couldn&apos;t load video</span>\n <span className=\"max-w-full truncate text-xs opacity-70\">{src}</span>\n </div>\n ) : (\n <video\n src={src}\n preload={preload}\n poster={poster}\n controls={controls}\n autoPlay={autoPlay}\n loop={loop}\n muted={muted}\n playsInline\n onError={() => {\n setErrored(true);\n onError?.();\n }}\n className={cn(\n \"max-h-full max-w-full\",\n fit === \"cover\" ? \"h-full w-full object-cover\" : \"object-contain\",\n )}\n />\n )}\n </div>\n );\n },\n);\n\nVideoViewer.displayName = \"VideoViewer\";\n"]}
@@ -1,7 +1,7 @@
1
- import { VideoViewer } from './chunk-JKO2Y5SH.js';
1
+ import { VideoViewer } from './chunk-YVDAXYHP.js';
2
2
  import { PdfViewer } from './chunk-DZD2RJVD.js';
3
3
  import { ImageViewer } from './chunk-A3CNYKT3.js';
4
- import { AudioViewer } from './chunk-EIBW5YLS.js';
4
+ import { AudioViewer } from './chunk-74ZN5CNE.js';
5
5
  import { cn } from './chunk-DSUHZO6T.js';
6
6
  import { forwardRef } from 'react';
7
7
  import { jsx, jsxs } from 'react/jsx-runtime';
@@ -226,5 +226,5 @@ var MediaViewer = forwardRef(
226
226
  MediaViewer.displayName = "MediaViewer";
227
227
 
228
228
  export { MediaViewer, resolveMediaType };
229
- //# sourceMappingURL=chunk-J3XS4HHX.js.map
230
- //# sourceMappingURL=chunk-J3XS4HHX.js.map
229
+ //# sourceMappingURL=chunk-QWSTG5MF.js.map
230
+ //# sourceMappingURL=chunk-QWSTG5MF.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/media-type.ts","../src/components/MediaViewer/MediaFallback.tsx","../src/components/MediaViewer/MediaViewer.tsx"],"names":["jsx"],"mappings":"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","file":"chunk-J3XS4HHX.js","sourcesContent":["/**\n * Resolve what kind of media a source is, from its MIME type (preferred) or its\n * URL — a file extension, or the type embedded in a `data:` URI.\n *\n * Shared by `<MediaViewer>` to pick the right per-type viewer, and exported so\n * downstream packages (e.g. fancy-code's file viewer) can make the same\n * text-vs-media decision without re-deriving the table.\n */\n\nexport type MediaKind = \"image\" | \"video\" | \"audio\" | \"pdf\" | \"unknown\";\n\nexport interface ResolveMediaTypeInput {\n /** MIME type — preferred when known (e.g. `\"image/png\"`, `\"video/mp4\"`). */\n mime?: string | null;\n /**\n * Source URL — used to sniff the kind from a file extension, or from the type\n * embedded in a `data:` URI, when no `mime` is given. `http(s):` / `data:` /\n * `blob:` are all accepted (a bare `blob:` URL carries no type, so pass\n * `mime` alongside it).\n */\n src?: string | null;\n}\n\nconst IMAGE_EXTS = new Set([\n \"png\", \"jpg\", \"jpeg\", \"jfif\", \"pjpeg\", \"gif\", \"webp\", \"avif\", \"bmp\", \"ico\",\n \"cur\", \"svg\", \"svgz\", \"apng\", \"tif\", \"tiff\", \"heic\", \"heif\",\n]);\nconst VIDEO_EXTS = new Set([\n \"mp4\", \"m4v\", \"webm\", \"ogv\", \"ogm\", \"mov\", \"mkv\", \"avi\", \"wmv\", \"flv\", \"3gp\",\n \"3g2\", \"mpg\", \"mpeg\",\n]);\nconst AUDIO_EXTS = new Set([\n \"mp3\", \"wav\", \"wave\", \"ogg\", \"oga\", \"m4a\", \"m4b\", \"aac\", \"flac\", \"opus\",\n \"weba\", \"mid\", \"midi\", \"aiff\", \"aif\",\n]);\n\n/** Map a (possibly parameterized) MIME type to a media kind. */\nfunction mimeToKind(mime: string): MediaKind {\n // Strip any parameters (`; charset=…`) and normalize.\n const m = mime.toLowerCase().split(\";\")[0].trim();\n if (!m) return \"unknown\";\n if (m === \"application/pdf\" || m === \"application/x-pdf\") return \"pdf\";\n if (m.startsWith(\"image/\")) return \"image\";\n if (m.startsWith(\"video/\")) return \"video\";\n if (m.startsWith(\"audio/\")) return \"audio\";\n // `application/ogg` is audio in practice; treat it as such.\n if (m === \"application/ogg\") return \"audio\";\n return \"unknown\";\n}\n\n/** Map a bare file extension (no leading dot) to a media kind. */\nfunction extToKind(ext: string): MediaKind {\n const e = ext.toLowerCase();\n if (e === \"pdf\") return \"pdf\";\n if (IMAGE_EXTS.has(e)) return \"image\";\n if (VIDEO_EXTS.has(e)) return \"video\";\n if (AUDIO_EXTS.has(e)) return \"audio\";\n return \"unknown\";\n}\n\n/** Pull the file extension out of a URL, ignoring its query/hash. */\nfunction extFromSrc(src: string): string | null {\n const clean = src.split(/[?#]/, 1)[0];\n const lastSeg = clean.split(\"/\").pop() ?? \"\";\n const dot = lastSeg.lastIndexOf(\".\");\n if (dot <= 0 || dot === lastSeg.length - 1) return null;\n return lastSeg.slice(dot + 1);\n}\n\n/** Extract the MIME type embedded in a `data:` URI, if present. */\nfunction mimeFromDataUri(src: string): string | null {\n if (!/^data:/i.test(src)) return null;\n const header = src.slice(5).split(\",\", 1)[0]; // text after `data:`, before the comma\n const mt = header.split(\";\")[0].trim();\n return mt || null;\n}\n\n/**\n * Resolve a {@link MediaKind} from a MIME type and/or source URL. MIME wins\n * when it's conclusive; otherwise the `src` is sniffed (a `data:` URI's\n * embedded type first, then the file extension). Returns `\"unknown\"` when\n * nothing matches — callers treat that as \"not previewable media\" (e.g. text).\n */\nexport function resolveMediaType({ mime, src }: ResolveMediaTypeInput): MediaKind {\n if (mime) {\n const k = mimeToKind(mime);\n if (k !== \"unknown\") return k;\n }\n if (src) {\n const dataMime = mimeFromDataUri(src);\n if (dataMime) {\n const k = mimeToKind(dataMime);\n if (k !== \"unknown\") return k;\n }\n const ext = extFromSrc(src);\n if (ext) return extToKind(ext);\n }\n return \"unknown\";\n}\n","import { cn } from \"../../utils/cn\";\n\ninterface MediaFallbackProps {\n src: string;\n mime?: string;\n label?: string;\n className?: string;\n}\n\n/** Filename guess from a URL, ignoring query/hash and `data:`/`blob:` URLs. */\nfunction basename(src: string): string | null {\n if (/^(data|blob):/i.test(src)) return null;\n const seg = src.split(/[?#]/, 1)[0].split(\"/\").pop();\n return seg ? decodeURIComponent(seg) : null;\n}\n\n/**\n * Shown by `<MediaViewer>` when a source isn't a previewable media type — a\n * small themed card naming the file and offering a download.\n */\nexport function MediaFallback({ src, mime, label, className }: MediaFallbackProps) {\n const name = label || basename(src);\n return (\n <div\n data-react-fancy-media-fallback=\"\"\n className={cn(\n \"flex flex-col items-center justify-center gap-2 rounded-lg border border-zinc-200 bg-white p-6 text-center dark:border-zinc-700 dark:bg-zinc-900\",\n className,\n )}\n >\n <span className=\"text-sm font-medium text-zinc-700 dark:text-zinc-200\">\n No preview available\n </span>\n {(name || mime) && (\n <span className=\"max-w-full truncate text-xs text-zinc-500 dark:text-zinc-400\">\n {name}\n {name && mime ? \" · \" : \"\"}\n {mime}\n </span>\n )}\n <a\n href={src}\n download={name ?? undefined}\n className=\"text-xs font-medium text-blue-600 hover:underline dark:text-blue-400\"\n >\n Download\n </a>\n </div>\n );\n}\n","import { forwardRef } from \"react\";\nimport { resolveMediaType } from \"../../utils/media-type\";\nimport { ImageViewer } from \"../ImageViewer\";\nimport { VideoViewer } from \"../VideoViewer\";\nimport { AudioViewer } from \"../AudioViewer\";\nimport { PdfViewer } from \"../PdfViewer\";\nimport { MediaFallback } from \"./MediaFallback\";\nimport type { MediaViewerProps } from \"./MediaViewer.types\";\n\n/**\n * Renders the right viewer for a media file, picked from its `mime` (preferred)\n * or `src` — image, video, audio, or PDF — and a download fallback for anything\n * else. SVG renders through `<ImageViewer>`. The single entry point for showing\n * a file from a tree/workspace when you don't know its type ahead of time.\n *\n * ```tsx\n * <MediaViewer src={url} mime=\"image/png\" alt=\"logo.png\" style={{ height: 320 }} />\n * ```\n */\nexport const MediaViewer = forwardRef<HTMLDivElement, MediaViewerProps>(\n (\n {\n src,\n mime,\n alt,\n kind,\n imageProps,\n videoProps,\n audioProps,\n pdfProps,\n fallback,\n onError,\n className,\n style,\n },\n ref,\n ) => {\n const resolved = kind ?? resolveMediaType({ mime, src });\n\n switch (resolved) {\n case \"image\":\n return (\n <ImageViewer\n ref={ref}\n src={src}\n alt={alt ?? \"\"}\n onError={onError}\n className={className}\n style={style}\n {...imageProps}\n />\n );\n case \"video\":\n return (\n <VideoViewer\n ref={ref}\n src={src}\n onError={onError}\n className={className}\n style={style}\n {...videoProps}\n />\n );\n case \"audio\":\n return (\n <AudioViewer\n ref={ref}\n src={src}\n title={alt}\n onError={onError}\n className={className}\n style={style}\n {...audioProps}\n />\n );\n case \"pdf\":\n return (\n <PdfViewer\n ref={ref}\n src={src}\n title={alt}\n className={className}\n style={style}\n {...pdfProps}\n />\n );\n default:\n return (\n <div\n ref={ref}\n data-react-fancy-media-viewer=\"\"\n className={className}\n style={style}\n >\n {fallback ?? <MediaFallback src={src} mime={mime} label={alt} />}\n </div>\n );\n }\n },\n);\n\nMediaViewer.displayName = \"MediaViewer\";\n"]}
1
+ {"version":3,"sources":["../src/utils/media-type.ts","../src/components/MediaViewer/MediaFallback.tsx","../src/components/MediaViewer/MediaViewer.tsx"],"names":["jsx"],"mappings":"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","file":"chunk-QWSTG5MF.js","sourcesContent":["/**\n * Resolve what kind of media a source is, from its MIME type (preferred) or its\n * URL — a file extension, or the type embedded in a `data:` URI.\n *\n * Shared by `<MediaViewer>` to pick the right per-type viewer, and exported so\n * downstream packages (e.g. fancy-code's file viewer) can make the same\n * text-vs-media decision without re-deriving the table.\n */\n\nexport type MediaKind = \"image\" | \"video\" | \"audio\" | \"pdf\" | \"unknown\";\n\nexport interface ResolveMediaTypeInput {\n /** MIME type — preferred when known (e.g. `\"image/png\"`, `\"video/mp4\"`). */\n mime?: string | null;\n /**\n * Source URL — used to sniff the kind from a file extension, or from the type\n * embedded in a `data:` URI, when no `mime` is given. `http(s):` / `data:` /\n * `blob:` are all accepted (a bare `blob:` URL carries no type, so pass\n * `mime` alongside it).\n */\n src?: string | null;\n}\n\nconst IMAGE_EXTS = new Set([\n \"png\", \"jpg\", \"jpeg\", \"jfif\", \"pjpeg\", \"gif\", \"webp\", \"avif\", \"bmp\", \"ico\",\n \"cur\", \"svg\", \"svgz\", \"apng\", \"tif\", \"tiff\", \"heic\", \"heif\",\n]);\nconst VIDEO_EXTS = new Set([\n \"mp4\", \"m4v\", \"webm\", \"ogv\", \"ogm\", \"mov\", \"mkv\", \"avi\", \"wmv\", \"flv\", \"3gp\",\n \"3g2\", \"mpg\", \"mpeg\",\n]);\nconst AUDIO_EXTS = new Set([\n \"mp3\", \"wav\", \"wave\", \"ogg\", \"oga\", \"m4a\", \"m4b\", \"aac\", \"flac\", \"opus\",\n \"weba\", \"mid\", \"midi\", \"aiff\", \"aif\",\n]);\n\n/** Map a (possibly parameterized) MIME type to a media kind. */\nfunction mimeToKind(mime: string): MediaKind {\n // Strip any parameters (`; charset=…`) and normalize.\n const m = mime.toLowerCase().split(\";\")[0].trim();\n if (!m) return \"unknown\";\n if (m === \"application/pdf\" || m === \"application/x-pdf\") return \"pdf\";\n if (m.startsWith(\"image/\")) return \"image\";\n if (m.startsWith(\"video/\")) return \"video\";\n if (m.startsWith(\"audio/\")) return \"audio\";\n // `application/ogg` is audio in practice; treat it as such.\n if (m === \"application/ogg\") return \"audio\";\n return \"unknown\";\n}\n\n/** Map a bare file extension (no leading dot) to a media kind. */\nfunction extToKind(ext: string): MediaKind {\n const e = ext.toLowerCase();\n if (e === \"pdf\") return \"pdf\";\n if (IMAGE_EXTS.has(e)) return \"image\";\n if (VIDEO_EXTS.has(e)) return \"video\";\n if (AUDIO_EXTS.has(e)) return \"audio\";\n return \"unknown\";\n}\n\n/** Pull the file extension out of a URL, ignoring its query/hash. */\nfunction extFromSrc(src: string): string | null {\n const clean = src.split(/[?#]/, 1)[0];\n const lastSeg = clean.split(\"/\").pop() ?? \"\";\n const dot = lastSeg.lastIndexOf(\".\");\n if (dot <= 0 || dot === lastSeg.length - 1) return null;\n return lastSeg.slice(dot + 1);\n}\n\n/** Extract the MIME type embedded in a `data:` URI, if present. */\nfunction mimeFromDataUri(src: string): string | null {\n if (!/^data:/i.test(src)) return null;\n const header = src.slice(5).split(\",\", 1)[0]; // text after `data:`, before the comma\n const mt = header.split(\";\")[0].trim();\n return mt || null;\n}\n\n/**\n * Resolve a {@link MediaKind} from a MIME type and/or source URL. MIME wins\n * when it's conclusive; otherwise the `src` is sniffed (a `data:` URI's\n * embedded type first, then the file extension). Returns `\"unknown\"` when\n * nothing matches — callers treat that as \"not previewable media\" (e.g. text).\n */\nexport function resolveMediaType({ mime, src }: ResolveMediaTypeInput): MediaKind {\n if (mime) {\n const k = mimeToKind(mime);\n if (k !== \"unknown\") return k;\n }\n if (src) {\n const dataMime = mimeFromDataUri(src);\n if (dataMime) {\n const k = mimeToKind(dataMime);\n if (k !== \"unknown\") return k;\n }\n const ext = extFromSrc(src);\n if (ext) return extToKind(ext);\n }\n return \"unknown\";\n}\n","import { cn } from \"../../utils/cn\";\n\ninterface MediaFallbackProps {\n src: string;\n mime?: string;\n label?: string;\n className?: string;\n}\n\n/** Filename guess from a URL, ignoring query/hash and `data:`/`blob:` URLs. */\nfunction basename(src: string): string | null {\n if (/^(data|blob):/i.test(src)) return null;\n const seg = src.split(/[?#]/, 1)[0].split(\"/\").pop();\n return seg ? decodeURIComponent(seg) : null;\n}\n\n/**\n * Shown by `<MediaViewer>` when a source isn't a previewable media type — a\n * small themed card naming the file and offering a download.\n */\nexport function MediaFallback({ src, mime, label, className }: MediaFallbackProps) {\n const name = label || basename(src);\n return (\n <div\n data-react-fancy-media-fallback=\"\"\n className={cn(\n \"flex flex-col items-center justify-center gap-2 rounded-lg border border-zinc-200 bg-white p-6 text-center dark:border-zinc-700 dark:bg-zinc-900\",\n className,\n )}\n >\n <span className=\"text-sm font-medium text-zinc-700 dark:text-zinc-200\">\n No preview available\n </span>\n {(name || mime) && (\n <span className=\"max-w-full truncate text-xs text-zinc-500 dark:text-zinc-400\">\n {name}\n {name && mime ? \" · \" : \"\"}\n {mime}\n </span>\n )}\n <a\n href={src}\n download={name ?? undefined}\n className=\"text-xs font-medium text-blue-600 hover:underline dark:text-blue-400\"\n >\n Download\n </a>\n </div>\n );\n}\n","import { forwardRef } from \"react\";\nimport { resolveMediaType } from \"../../utils/media-type\";\nimport { ImageViewer } from \"../ImageViewer\";\nimport { VideoViewer } from \"../VideoViewer\";\nimport { AudioViewer } from \"../AudioViewer\";\nimport { PdfViewer } from \"../PdfViewer\";\nimport { MediaFallback } from \"./MediaFallback\";\nimport type { MediaViewerProps } from \"./MediaViewer.types\";\n\n/**\n * Renders the right viewer for a media file, picked from its `mime` (preferred)\n * or `src` — image, video, audio, or PDF — and a download fallback for anything\n * else. SVG renders through `<ImageViewer>`. The single entry point for showing\n * a file from a tree/workspace when you don't know its type ahead of time.\n *\n * ```tsx\n * <MediaViewer src={url} mime=\"image/png\" alt=\"logo.png\" style={{ height: 320 }} />\n * ```\n */\nexport const MediaViewer = forwardRef<HTMLDivElement, MediaViewerProps>(\n (\n {\n src,\n mime,\n alt,\n kind,\n imageProps,\n videoProps,\n audioProps,\n pdfProps,\n fallback,\n onError,\n className,\n style,\n },\n ref,\n ) => {\n const resolved = kind ?? resolveMediaType({ mime, src });\n\n switch (resolved) {\n case \"image\":\n return (\n <ImageViewer\n ref={ref}\n src={src}\n alt={alt ?? \"\"}\n onError={onError}\n className={className}\n style={style}\n {...imageProps}\n />\n );\n case \"video\":\n return (\n <VideoViewer\n ref={ref}\n src={src}\n onError={onError}\n className={className}\n style={style}\n {...videoProps}\n />\n );\n case \"audio\":\n return (\n <AudioViewer\n ref={ref}\n src={src}\n title={alt}\n onError={onError}\n className={className}\n style={style}\n {...audioProps}\n />\n );\n case \"pdf\":\n return (\n <PdfViewer\n ref={ref}\n src={src}\n title={alt}\n className={className}\n style={style}\n {...pdfProps}\n />\n );\n default:\n return (\n <div\n ref={ref}\n data-react-fancy-media-viewer=\"\"\n className={className}\n style={style}\n >\n {fallback ?? <MediaFallback src={src} mime={mime} label={alt} />}\n </div>\n );\n }\n },\n);\n\nMediaViewer.displayName = \"MediaViewer\";\n"]}
@@ -9,6 +9,7 @@ var VideoViewer = forwardRef(
9
9
  controls = true,
10
10
  autoPlay = false,
11
11
  loop = false,
12
+ preload = "metadata",
12
13
  muted = false,
13
14
  fit = "contain",
14
15
  onError,
@@ -33,6 +34,7 @@ var VideoViewer = forwardRef(
33
34
  "video",
34
35
  {
35
36
  src,
37
+ preload,
36
38
  poster,
37
39
  controls,
38
40
  autoPlay,
@@ -56,5 +58,5 @@ var VideoViewer = forwardRef(
56
58
  VideoViewer.displayName = "VideoViewer";
57
59
 
58
60
  export { VideoViewer };
59
- //# sourceMappingURL=chunk-JKO2Y5SH.js.map
60
- //# sourceMappingURL=chunk-JKO2Y5SH.js.map
61
+ //# sourceMappingURL=chunk-YVDAXYHP.js.map
62
+ //# sourceMappingURL=chunk-YVDAXYHP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/VideoViewer/VideoViewer.tsx"],"names":[],"mappings":";;;;AAYO,IAAM,WAAA,GAAc,UAAA;AAAA,EACzB,CACE;AAAA,IACE,GAAA;AAAA,IACA,MAAA;AAAA,IACA,QAAA,GAAW,IAAA;AAAA,IACX,QAAA,GAAW,KAAA;AAAA,IACX,IAAA,GAAO,KAAA;AAAA,IACP,OAAA,GAAU,UAAA;AAAA,IACV,KAAA,GAAQ,KAAA;AAAA,IACR,GAAA,GAAM,SAAA;AAAA,IACN,OAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,KAEF,GAAA,KACG;AACH,IAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAS,KAAK,CAAA;AAE5C,IAAA,uBACE,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,+BAAA,EAA8B,EAAA;AAAA,QAC9B,SAAA,EAAW,EAAA;AAAA,UACT,+EAAA;AAAA,UACA;AAAA,SACF;AAAA,QACA,KAAA;AAAA,QAEC,QAAA,EAAA,OAAA,mBACC,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,uFAAA,EACb,QAAA,EAAA;AAAA,0BAAA,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,aAAA,EAAc,QAAA,EAAA,qBAAA,EAAwB,CAAA;AAAA,0BACtD,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,wCAAA,EAA0C,QAAA,EAAA,GAAA,EAAI;AAAA,SAAA,EAChE,CAAA,mBAEA,GAAA;AAAA,UAAC,OAAA;AAAA,UAAA;AAAA,YACC,GAAA;AAAA,YACA,OAAA;AAAA,YACA,MAAA;AAAA,YACA,QAAA;AAAA,YACA,QAAA;AAAA,YACA,IAAA;AAAA,YACA,KAAA;AAAA,YACA,WAAA,EAAW,IAAA;AAAA,YACX,SAAS,MAAM;AACb,cAAA,UAAA,CAAW,IAAI,CAAA;AACf,cAAA,OAAA,IAAU;AAAA,YACZ,CAAA;AAAA,YACA,SAAA,EAAW,EAAA;AAAA,cACT,uBAAA;AAAA,cACA,GAAA,KAAQ,UAAU,4BAAA,GAA+B;AAAA;AACnD;AAAA;AACF;AAAA,KAEJ;AAAA,EAEJ;AACF;AAEA,WAAA,CAAY,WAAA,GAAc,aAAA","file":"chunk-YVDAXYHP.js","sourcesContent":["import { forwardRef, useState } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport type { VideoViewerProps } from \"./VideoViewer.types\";\n\n/**\n * A standalone video viewer — native controls, optional poster, fit-to-box, and\n * dark-aware framing. Renders for video sources inside `<MediaViewer>`.\n *\n * ```tsx\n * <VideoViewer src=\"/clip.mp4\" poster=\"/clip.jpg\" style={{ height: 360 }} />\n * ```\n */\nexport const VideoViewer = forwardRef<HTMLDivElement, VideoViewerProps>(\n (\n {\n src,\n poster,\n controls = true,\n autoPlay = false,\n loop = false,\n preload = \"metadata\",\n muted = false,\n fit = \"contain\",\n onError,\n className,\n style,\n },\n ref,\n ) => {\n const [errored, setErrored] = useState(false);\n\n return (\n <div\n ref={ref}\n data-react-fancy-video-viewer=\"\"\n className={cn(\n \"relative flex items-center justify-center overflow-hidden rounded-md bg-black\",\n className,\n )}\n style={style}\n >\n {errored ? (\n <div className=\"flex flex-col items-center justify-center gap-1 p-4 text-center text-sm text-zinc-400\">\n <span className=\"font-medium\">Couldn&apos;t load video</span>\n <span className=\"max-w-full truncate text-xs opacity-70\">{src}</span>\n </div>\n ) : (\n <video\n src={src}\n preload={preload}\n poster={poster}\n controls={controls}\n autoPlay={autoPlay}\n loop={loop}\n muted={muted}\n playsInline\n onError={() => {\n setErrored(true);\n onError?.();\n }}\n className={cn(\n \"max-h-full max-w-full\",\n fit === \"cover\" ? \"h-full w-full object-cover\" : \"object-contain\",\n )}\n />\n )}\n </div>\n );\n },\n);\n\nVideoViewer.displayName = \"VideoViewer\";\n"]}
package/dist/editor.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var chunkWT72KW5M_cjs = require('./chunk-WT72KW5M.cjs');
4
- require('./chunk-G34OBTU3.cjs');
4
+ require('./chunk-HPNIEYSJ.cjs');
5
5
  require('./chunk-T6YELZPJ.cjs');
6
6
  require('./chunk-RNHIDZLE.cjs');
7
7
  require('./chunk-TXTRTTG7.cjs');
package/dist/editor.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export { Editor, useEditor } from './chunk-RID7CYGD.js';
2
- import './chunk-RNPFSVFN.js';
2
+ import './chunk-FWCREAIT.js';
3
3
  import './chunk-CPY7NHXM.js';
4
4
  import './chunk-FVV76TRF.js';
5
5
  import './chunk-5U2DZ7AV.js';
@@ -1,8 +1,8 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode, CSSProperties } from 'react';
3
3
  import { I as ImageViewerProps } from './ImageViewer.types-BaqlDeuo.cjs';
4
- import { V as VideoViewerProps } from './VideoViewer.types-CTwS6PcM.cjs';
5
- import { A as AudioViewerProps } from './AudioViewer.types-VWhRx6XG.cjs';
4
+ import { V as VideoViewerProps } from './VideoViewer.types-_AwQ8DQr.cjs';
5
+ import { A as AudioViewerProps } from './AudioViewer.types-CyhmQVQd.cjs';
6
6
  import { P as PdfViewerProps } from './PdfViewer.types-_u6vboSl.cjs';
7
7
 
8
8
  /**
@@ -1,8 +1,8 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode, CSSProperties } from 'react';
3
3
  import { I as ImageViewerProps } from './ImageViewer.types-BaqlDeuo.js';
4
- import { V as VideoViewerProps } from './VideoViewer.types-CTwS6PcM.js';
5
- import { A as AudioViewerProps } from './AudioViewer.types-VWhRx6XG.js';
4
+ import { V as VideoViewerProps } from './VideoViewer.types-_AwQ8DQr.js';
5
+ import { A as AudioViewerProps } from './AudioViewer.types-CyhmQVQd.js';
6
6
  import { P as PdfViewerProps } from './PdfViewer.types-_u6vboSl.js';
7
7
 
8
8
  /**
package/dist/index.cjs CHANGED
@@ -55,8 +55,8 @@ var chunk6BW5BQMN_cjs = require('./chunk-6BW5BQMN.cjs');
55
55
  var chunkPPLR2DNC_cjs = require('./chunk-PPLR2DNC.cjs');
56
56
  require('./chunk-YLGTDBB2.cjs');
57
57
  var chunkORPYY2MO_cjs = require('./chunk-ORPYY2MO.cjs');
58
- var chunkKE7KXY5C_cjs = require('./chunk-KE7KXY5C.cjs');
59
- var chunk3ETOEEVN_cjs = require('./chunk-3ETOEEVN.cjs');
58
+ var chunkBWTFK2GV_cjs = require('./chunk-BWTFK2GV.cjs');
59
+ var chunkI462NM2A_cjs = require('./chunk-I462NM2A.cjs');
60
60
  var chunkJHEM6IGT_cjs = require('./chunk-JHEM6IGT.cjs');
61
61
  var chunkEERAEPQO_cjs = require('./chunk-EERAEPQO.cjs');
62
62
  var chunkKUKYJZIU_cjs = require('./chunk-KUKYJZIU.cjs');
@@ -71,7 +71,7 @@ var chunk5HOQ2PHR_cjs = require('./chunk-5HOQ2PHR.cjs');
71
71
  var chunkF6NEPU67_cjs = require('./chunk-F6NEPU67.cjs');
72
72
  var chunkORFHF6PT_cjs = require('./chunk-ORFHF6PT.cjs');
73
73
  var chunkWT72KW5M_cjs = require('./chunk-WT72KW5M.cjs');
74
- var chunkG34OBTU3_cjs = require('./chunk-G34OBTU3.cjs');
74
+ var chunkHPNIEYSJ_cjs = require('./chunk-HPNIEYSJ.cjs');
75
75
  var chunkT6YELZPJ_cjs = require('./chunk-T6YELZPJ.cjs');
76
76
  var chunkMDUSUJWY_cjs = require('./chunk-MDUSUJWY.cjs');
77
77
  var chunk6FBFE66M_cjs = require('./chunk-6FBFE66M.cjs');
@@ -106,7 +106,7 @@ var chunkKC6QBKKB_cjs = require('./chunk-KC6QBKKB.cjs');
106
106
  var chunk2PELJ4VA_cjs = require('./chunk-2PELJ4VA.cjs');
107
107
  var chunkCZZH45GQ_cjs = require('./chunk-CZZH45GQ.cjs');
108
108
  var chunkJPTWZ3RF_cjs = require('./chunk-JPTWZ3RF.cjs');
109
- var chunk3GRC5J2L_cjs = require('./chunk-3GRC5J2L.cjs');
109
+ var chunk3MHVFEIP_cjs = require('./chunk-3MHVFEIP.cjs');
110
110
  var chunkIF25CE6S_cjs = require('./chunk-IF25CE6S.cjs');
111
111
  var chunkJ2DI6RMR_cjs = require('./chunk-J2DI6RMR.cjs');
112
112
  require('./chunk-KJ22KE56.cjs');
@@ -390,15 +390,15 @@ Object.defineProperty(exports, "Marquee", {
390
390
  });
391
391
  Object.defineProperty(exports, "MediaViewer", {
392
392
  enumerable: true,
393
- get: function () { return chunkKE7KXY5C_cjs.MediaViewer; }
393
+ get: function () { return chunkBWTFK2GV_cjs.MediaViewer; }
394
394
  });
395
395
  Object.defineProperty(exports, "resolveMediaType", {
396
396
  enumerable: true,
397
- get: function () { return chunkKE7KXY5C_cjs.resolveMediaType; }
397
+ get: function () { return chunkBWTFK2GV_cjs.resolveMediaType; }
398
398
  });
399
399
  Object.defineProperty(exports, "VideoViewer", {
400
400
  enumerable: true,
401
- get: function () { return chunk3ETOEEVN_cjs.VideoViewer; }
401
+ get: function () { return chunkI462NM2A_cjs.VideoViewer; }
402
402
  });
403
403
  Object.defineProperty(exports, "PdfViewer", {
404
404
  enumerable: true,
@@ -512,13 +512,41 @@ Object.defineProperty(exports, "useEditor", {
512
512
  enumerable: true,
513
513
  get: function () { return chunkWT72KW5M_cjs.useEditor; }
514
514
  });
515
+ Object.defineProperty(exports, "THEME_STORAGE_KEY", {
516
+ enumerable: true,
517
+ get: function () { return chunkHPNIEYSJ_cjs.THEME_STORAGE_KEY; }
518
+ });
519
+ Object.defineProperty(exports, "getThemePreference", {
520
+ enumerable: true,
521
+ get: function () { return chunkHPNIEYSJ_cjs.getThemePreference; }
522
+ });
523
+ Object.defineProperty(exports, "initTheme", {
524
+ enumerable: true,
525
+ get: function () { return chunkHPNIEYSJ_cjs.initTheme; }
526
+ });
527
+ Object.defineProperty(exports, "resolveTheme", {
528
+ enumerable: true,
529
+ get: function () { return chunkHPNIEYSJ_cjs.resolveTheme; }
530
+ });
531
+ Object.defineProperty(exports, "setThemePreference", {
532
+ enumerable: true,
533
+ get: function () { return chunkHPNIEYSJ_cjs.setThemePreference; }
534
+ });
535
+ Object.defineProperty(exports, "subscribeTheme", {
536
+ enumerable: true,
537
+ get: function () { return chunkHPNIEYSJ_cjs.subscribeTheme; }
538
+ });
515
539
  Object.defineProperty(exports, "useId", {
516
540
  enumerable: true,
517
- get: function () { return chunkG34OBTU3_cjs.useId; }
541
+ get: function () { return chunkHPNIEYSJ_cjs.useId; }
518
542
  });
519
543
  Object.defineProperty(exports, "useNodeRegistry", {
520
544
  enumerable: true,
521
- get: function () { return chunkG34OBTU3_cjs.useNodeRegistry; }
545
+ get: function () { return chunkHPNIEYSJ_cjs.useNodeRegistry; }
546
+ });
547
+ Object.defineProperty(exports, "useTheme", {
548
+ enumerable: true,
549
+ get: function () { return chunkHPNIEYSJ_cjs.useTheme; }
522
550
  });
523
551
  Object.defineProperty(exports, "usePanZoom", {
524
552
  enumerable: true,
@@ -738,7 +766,7 @@ Object.defineProperty(exports, "useAccordionSection", {
738
766
  });
739
767
  Object.defineProperty(exports, "AudioViewer", {
740
768
  enumerable: true,
741
- get: function () { return chunk3GRC5J2L_cjs.AudioViewer; }
769
+ get: function () { return chunk3MHVFEIP_cjs.AudioViewer; }
742
770
  });
743
771
  Object.defineProperty(exports, "Autocomplete", {
744
772
  enumerable: true,
package/dist/index.d.cts CHANGED
@@ -77,7 +77,7 @@ export { Kanban, KanbanCardMoveHandler, KanbanCardProps, KanbanColumnHandleProps
77
77
  export { DropPosition, TreeNav, TreeNavContextValue, TreeNavProps, TreeNodeData, TreeNodeProps, useTreeNav } from './tree-nav.cjs';
78
78
  export { FileBrowser, FileBrowserContextValue, FileBrowserNewFolder, FileBrowserNodeProps, FileBrowserPathBarProps, FileBrowserProps, FileBrowserProvider, FileBrowserRow, FileBrowserToolbarProps, FileBrowserTreeProps, FileEntry, FileKind, FileLoadStatus, FileSelectMode, FileSnapshotNode, FileSort, FileSortDirection, FileSortField, useFileBrowser, validateFolderName } from './file-browser.cjs';
79
79
  import { ClassValue } from 'clsx';
80
- export { M as MediaKind, a as MediaViewer, b as MediaViewerProps, R as ResolveMediaTypeInput, r as resolveMediaType } from './index-B71wa9AW.cjs';
80
+ export { M as MediaKind, a as MediaViewer, b as MediaViewerProps, R as ResolveMediaTypeInput, r as resolveMediaType } from './index-CgPpIw_O.cjs';
81
81
  import { P as Placement } from './types-8Sv0rCjw.cjs';
82
82
  export { A as ActionColor, B as ButtonColor, C as Color, S as Size, V as Variant } from './types-8Sv0rCjw.cjs';
83
83
  import { RefObject } from 'react';
@@ -90,9 +90,9 @@ export { MagicWand, MagicWandAction, MagicWandAppearance, MagicWandProps, MagicW
90
90
  export { ImageViewer } from './image-viewer.cjs';
91
91
  export { I as ImageViewerProps, a as ImageViewerViewport } from './ImageViewer.types-BaqlDeuo.cjs';
92
92
  export { VideoViewer } from './video-viewer.cjs';
93
- export { V as VideoViewerProps } from './VideoViewer.types-CTwS6PcM.cjs';
93
+ export { V as VideoViewerProps } from './VideoViewer.types-_AwQ8DQr.cjs';
94
94
  export { AudioViewer } from './audio-viewer.cjs';
95
- export { A as AudioViewerProps } from './AudioViewer.types-VWhRx6XG.cjs';
95
+ export { A as AudioViewerProps } from './AudioViewer.types-CyhmQVQd.cjs';
96
96
  export { PdfViewer } from './pdf-viewer.cjs';
97
97
  export { P as PdfViewerProps } from './PdfViewer.types-_u6vboSl.cjs';
98
98
  export { J as JSON_FIELD_TYPES, a as JsonArray, b as JsonEditor, c as JsonEditorActivity, d as JsonEditorEdit, e as JsonEditorIssue, f as JsonEditorNode, g as JsonEditorPendingEdit, h as JsonEditorProps, i as JsonFieldType, j as JsonKeyMapEntry, k as JsonKeyRule, l as JsonObject, m as JsonPath, n as JsonPrimitive, o as JsonValue, P as ParsedKeyMap, p as applyJsonEdit, q as describeEdit, r as findJsonConflicts, s as getAtPath, t as inferType, u as parseKeyMap, v as parsePath, w as pathToString, x as resolveKeyRule, y as typeMatches } from './index-BypPN_6l.cjs';
@@ -200,4 +200,75 @@ interface UseNodeRegistryReturn {
200
200
  }
201
201
  declare function useNodeRegistry(): UseNodeRegistryReturn;
202
202
 
203
- export { type NodeRect, Placement, cn, sanitizeHref, sanitizeHtml, useAnimation, useControllableState, useEscapeKey, useFloatingPosition, useFocusTrap, useId, useNodeRegistry, useOutsideClick, usePanZoom };
203
+ /**
204
+ * Light / dark / **system** theming.
205
+ *
206
+ * `styles.css` in this package defines the dark contract — it redefines the
207
+ * whole `--color-secondary-*` scale under `:where(.dark)` so that "900" always
208
+ * means strongest foreground and "50" always means faintest surface. This module
209
+ * is the other half of that contract: the thing that decides when `.dark` is on.
210
+ *
211
+ * It lives here rather than in each app because every consumer was hand-rolling
212
+ * it, and every hand-rolled copy had the same two holes:
213
+ *
214
+ * - **No live listener.** The OS preference was read once at boot, so changing
215
+ * the system theme with the page open did nothing. That is a default, not a
216
+ * "system" mode.
217
+ * - **No way back.** Choosing light or dark wrote to storage forever, so
218
+ * "follow my system" was unreachable after a single click.
219
+ *
220
+ * Both are fixed by treating **"system" as the absence of a stored choice**
221
+ * rather than as a third stored value.
222
+ *
223
+ * Every export is safe to call during SSR, where it is a no-op that resolves to
224
+ * `"light"`. Pair it with `useTheme`, which deliberately renders `system/light`
225
+ * on the server and syncs in an effect — this project has paid for hydration
226
+ * mismatches caused by reading the real theme during render.
227
+ */
228
+ type ThemePreference = "light" | "dark" | "system";
229
+ type ResolvedTheme = "light" | "dark";
230
+ /**
231
+ * Deliberately the key the showcase already used, so adopting this module keeps
232
+ * a returning visitor's saved choice instead of silently resetting it.
233
+ */
234
+ declare const THEME_STORAGE_KEY = "fancy-ui.theme";
235
+ type ThemeListener = (resolved: ResolvedTheme, preference: ThemePreference) => void;
236
+ /** The stored choice, or `"system"` when there isn't one. */
237
+ declare function getThemePreference(): ThemePreference;
238
+ /** What is actually on screen, as opposed to what the user asked for. */
239
+ declare function resolveTheme(preference?: ThemePreference): ResolvedTheme;
240
+ /**
241
+ * Choose a theme. `"system"` REMOVES the stored value rather than storing the
242
+ * string — an absent choice is what lets the OS keep speaking for the user.
243
+ */
244
+ declare function setThemePreference(preference: ThemePreference): void;
245
+ /** Observe theme changes. Returns an unsubscribe. */
246
+ declare function subscribeTheme(listener: ThemeListener): () => void;
247
+ /**
248
+ * Apply the current theme and start following the OS. Call once, as early as
249
+ * possible — before first paint if you want to avoid a flash of the wrong
250
+ * theme. Returns a disposer; calling it again is harmless.
251
+ */
252
+ declare function initTheme(options?: {
253
+ storageKey?: string;
254
+ }): () => void;
255
+
256
+ type UseThemeResult = {
257
+ /** What the user asked for, including `"system"`. */
258
+ preference: ThemePreference;
259
+ /** What is actually on screen. */
260
+ resolved: ResolvedTheme;
261
+ setPreference: (preference: ThemePreference) => void;
262
+ };
263
+ /**
264
+ * Read and set the theme from a component, re-rendering when it changes —
265
+ * including when the OS theme changes underneath a `"system"` preference.
266
+ *
267
+ * The initial state is deliberately `system` / `light` rather than the real
268
+ * theme: the server has neither storage nor an OS preference, so reading the
269
+ * truth during render is exactly the hydration mismatch this project has
270
+ * already chased down more than once. The effect syncs immediately on mount.
271
+ */
272
+ declare function useTheme(): UseThemeResult;
273
+
274
+ export { type NodeRect, Placement, type ResolvedTheme, THEME_STORAGE_KEY, type ThemePreference, type UseThemeResult, cn, getThemePreference, initTheme, resolveTheme, sanitizeHref, sanitizeHtml, setThemePreference, subscribeTheme, useAnimation, useControllableState, useEscapeKey, useFloatingPosition, useFocusTrap, useId, useNodeRegistry, useOutsideClick, usePanZoom, useTheme };
package/dist/index.d.ts CHANGED
@@ -77,7 +77,7 @@ export { Kanban, KanbanCardMoveHandler, KanbanCardProps, KanbanColumnHandleProps
77
77
  export { DropPosition, TreeNav, TreeNavContextValue, TreeNavProps, TreeNodeData, TreeNodeProps, useTreeNav } from './tree-nav.js';
78
78
  export { FileBrowser, FileBrowserContextValue, FileBrowserNewFolder, FileBrowserNodeProps, FileBrowserPathBarProps, FileBrowserProps, FileBrowserProvider, FileBrowserRow, FileBrowserToolbarProps, FileBrowserTreeProps, FileEntry, FileKind, FileLoadStatus, FileSelectMode, FileSnapshotNode, FileSort, FileSortDirection, FileSortField, useFileBrowser, validateFolderName } from './file-browser.js';
79
79
  import { ClassValue } from 'clsx';
80
- export { M as MediaKind, a as MediaViewer, b as MediaViewerProps, R as ResolveMediaTypeInput, r as resolveMediaType } from './index-bIntFOl5.js';
80
+ export { M as MediaKind, a as MediaViewer, b as MediaViewerProps, R as ResolveMediaTypeInput, r as resolveMediaType } from './index-IeivGsNg.js';
81
81
  import { P as Placement } from './types-8Sv0rCjw.js';
82
82
  export { A as ActionColor, B as ButtonColor, C as Color, S as Size, V as Variant } from './types-8Sv0rCjw.js';
83
83
  import { RefObject } from 'react';
@@ -90,9 +90,9 @@ export { MagicWand, MagicWandAction, MagicWandAppearance, MagicWandProps, MagicW
90
90
  export { ImageViewer } from './image-viewer.js';
91
91
  export { I as ImageViewerProps, a as ImageViewerViewport } from './ImageViewer.types-BaqlDeuo.js';
92
92
  export { VideoViewer } from './video-viewer.js';
93
- export { V as VideoViewerProps } from './VideoViewer.types-CTwS6PcM.js';
93
+ export { V as VideoViewerProps } from './VideoViewer.types-_AwQ8DQr.js';
94
94
  export { AudioViewer } from './audio-viewer.js';
95
- export { A as AudioViewerProps } from './AudioViewer.types-VWhRx6XG.js';
95
+ export { A as AudioViewerProps } from './AudioViewer.types-CyhmQVQd.js';
96
96
  export { PdfViewer } from './pdf-viewer.js';
97
97
  export { P as PdfViewerProps } from './PdfViewer.types-_u6vboSl.js';
98
98
  export { J as JSON_FIELD_TYPES, a as JsonArray, b as JsonEditor, c as JsonEditorActivity, d as JsonEditorEdit, e as JsonEditorIssue, f as JsonEditorNode, g as JsonEditorPendingEdit, h as JsonEditorProps, i as JsonFieldType, j as JsonKeyMapEntry, k as JsonKeyRule, l as JsonObject, m as JsonPath, n as JsonPrimitive, o as JsonValue, P as ParsedKeyMap, p as applyJsonEdit, q as describeEdit, r as findJsonConflicts, s as getAtPath, t as inferType, u as parseKeyMap, v as parsePath, w as pathToString, x as resolveKeyRule, y as typeMatches } from './index-BfFk-CIv.js';
@@ -200,4 +200,75 @@ interface UseNodeRegistryReturn {
200
200
  }
201
201
  declare function useNodeRegistry(): UseNodeRegistryReturn;
202
202
 
203
- export { type NodeRect, Placement, cn, sanitizeHref, sanitizeHtml, useAnimation, useControllableState, useEscapeKey, useFloatingPosition, useFocusTrap, useId, useNodeRegistry, useOutsideClick, usePanZoom };
203
+ /**
204
+ * Light / dark / **system** theming.
205
+ *
206
+ * `styles.css` in this package defines the dark contract — it redefines the
207
+ * whole `--color-secondary-*` scale under `:where(.dark)` so that "900" always
208
+ * means strongest foreground and "50" always means faintest surface. This module
209
+ * is the other half of that contract: the thing that decides when `.dark` is on.
210
+ *
211
+ * It lives here rather than in each app because every consumer was hand-rolling
212
+ * it, and every hand-rolled copy had the same two holes:
213
+ *
214
+ * - **No live listener.** The OS preference was read once at boot, so changing
215
+ * the system theme with the page open did nothing. That is a default, not a
216
+ * "system" mode.
217
+ * - **No way back.** Choosing light or dark wrote to storage forever, so
218
+ * "follow my system" was unreachable after a single click.
219
+ *
220
+ * Both are fixed by treating **"system" as the absence of a stored choice**
221
+ * rather than as a third stored value.
222
+ *
223
+ * Every export is safe to call during SSR, where it is a no-op that resolves to
224
+ * `"light"`. Pair it with `useTheme`, which deliberately renders `system/light`
225
+ * on the server and syncs in an effect — this project has paid for hydration
226
+ * mismatches caused by reading the real theme during render.
227
+ */
228
+ type ThemePreference = "light" | "dark" | "system";
229
+ type ResolvedTheme = "light" | "dark";
230
+ /**
231
+ * Deliberately the key the showcase already used, so adopting this module keeps
232
+ * a returning visitor's saved choice instead of silently resetting it.
233
+ */
234
+ declare const THEME_STORAGE_KEY = "fancy-ui.theme";
235
+ type ThemeListener = (resolved: ResolvedTheme, preference: ThemePreference) => void;
236
+ /** The stored choice, or `"system"` when there isn't one. */
237
+ declare function getThemePreference(): ThemePreference;
238
+ /** What is actually on screen, as opposed to what the user asked for. */
239
+ declare function resolveTheme(preference?: ThemePreference): ResolvedTheme;
240
+ /**
241
+ * Choose a theme. `"system"` REMOVES the stored value rather than storing the
242
+ * string — an absent choice is what lets the OS keep speaking for the user.
243
+ */
244
+ declare function setThemePreference(preference: ThemePreference): void;
245
+ /** Observe theme changes. Returns an unsubscribe. */
246
+ declare function subscribeTheme(listener: ThemeListener): () => void;
247
+ /**
248
+ * Apply the current theme and start following the OS. Call once, as early as
249
+ * possible — before first paint if you want to avoid a flash of the wrong
250
+ * theme. Returns a disposer; calling it again is harmless.
251
+ */
252
+ declare function initTheme(options?: {
253
+ storageKey?: string;
254
+ }): () => void;
255
+
256
+ type UseThemeResult = {
257
+ /** What the user asked for, including `"system"`. */
258
+ preference: ThemePreference;
259
+ /** What is actually on screen. */
260
+ resolved: ResolvedTheme;
261
+ setPreference: (preference: ThemePreference) => void;
262
+ };
263
+ /**
264
+ * Read and set the theme from a component, re-rendering when it changes —
265
+ * including when the OS theme changes underneath a `"system"` preference.
266
+ *
267
+ * The initial state is deliberately `system` / `light` rather than the real
268
+ * theme: the server has neither storage nor an OS preference, so reading the
269
+ * truth during render is exactly the hydration mismatch this project has
270
+ * already chased down more than once. The effect syncs immediately on mount.
271
+ */
272
+ declare function useTheme(): UseThemeResult;
273
+
274
+ export { type NodeRect, Placement, type ResolvedTheme, THEME_STORAGE_KEY, type ThemePreference, type UseThemeResult, cn, getThemePreference, initTheme, resolveTheme, sanitizeHref, sanitizeHtml, setThemePreference, subscribeTheme, useAnimation, useControllableState, useEscapeKey, useFloatingPosition, useFocusTrap, useId, useNodeRegistry, useOutsideClick, usePanZoom, useTheme };
package/dist/index.js CHANGED
@@ -53,8 +53,8 @@ export { DisplayValue } from './chunk-K7JGOQJG.js';
53
53
  export { Field } from './chunk-3DJGPNUI.js';
54
54
  import './chunk-GFYSELJF.js';
55
55
  export { Marquee } from './chunk-ATFXU6R6.js';
56
- export { MediaViewer, resolveMediaType } from './chunk-J3XS4HHX.js';
57
- export { VideoViewer } from './chunk-JKO2Y5SH.js';
56
+ export { MediaViewer, resolveMediaType } from './chunk-QWSTG5MF.js';
57
+ export { VideoViewer } from './chunk-YVDAXYHP.js';
58
58
  export { PdfViewer } from './chunk-DZD2RJVD.js';
59
59
  export { Menu, useMenu } from './chunk-IGYIMS6J.js';
60
60
  export { MobileMenu, useMobileMenu } from './chunk-F6SCZCJE.js';
@@ -69,7 +69,7 @@ export { ContextMenu, useContextMenu } from './chunk-NLJGNPNN.js';
69
69
  export { Drawer, useDrawer } from './chunk-PRDWJ4AR.js';
70
70
  export { Dropdown, useDropdown } from './chunk-JIYP73OJ.js';
71
71
  export { Editor, useEditor } from './chunk-RID7CYGD.js';
72
- export { useId, useNodeRegistry } from './chunk-RNPFSVFN.js';
72
+ export { THEME_STORAGE_KEY, getThemePreference, initTheme, resolveTheme, setThemePreference, subscribeTheme, useId, useNodeRegistry, useTheme } from './chunk-FWCREAIT.js';
73
73
  export { usePanZoom } from './chunk-CPY7NHXM.js';
74
74
  export { Emoji } from './chunk-O4MKOSU2.js';
75
75
  export { EmojiSelect } from './chunk-GKGL7F36.js';
@@ -104,7 +104,7 @@ export { Card } from './chunk-NH7NRRII.js';
104
104
  export { Carousel, useCarousel } from './chunk-HG6XUJOT.js';
105
105
  export { Accordion, useAccordion } from './chunk-AKIUDPJU.js';
106
106
  export { AccordionPanel, AccordionPanelContent, AccordionPanelSection, AccordionPanelTrigger, useAccordionPanel, useAccordionSection } from './chunk-IF5LOWUL.js';
107
- export { AudioViewer } from './chunk-EIBW5YLS.js';
107
+ export { AudioViewer } from './chunk-74ZN5CNE.js';
108
108
  export { Autocomplete } from './chunk-RM4QWJS3.js';
109
109
  export { useFloatingPosition } from './chunk-MQ6VGG62.js';
110
110
  import './chunk-OL53XYHF.js';
@@ -1,18 +1,18 @@
1
1
  'use strict';
2
2
 
3
- var chunkKE7KXY5C_cjs = require('./chunk-KE7KXY5C.cjs');
4
- require('./chunk-3ETOEEVN.cjs');
3
+ var chunkBWTFK2GV_cjs = require('./chunk-BWTFK2GV.cjs');
4
+ require('./chunk-I462NM2A.cjs');
5
5
  require('./chunk-JHEM6IGT.cjs');
6
6
  require('./chunk-AC7M7JDO.cjs');
7
7
  require('./chunk-T6YELZPJ.cjs');
8
- require('./chunk-3GRC5J2L.cjs');
8
+ require('./chunk-3MHVFEIP.cjs');
9
9
  require('./chunk-3Z6ALUWP.cjs');
10
10
 
11
11
 
12
12
 
13
13
  Object.defineProperty(exports, "MediaViewer", {
14
14
  enumerable: true,
15
- get: function () { return chunkKE7KXY5C_cjs.MediaViewer; }
15
+ get: function () { return chunkBWTFK2GV_cjs.MediaViewer; }
16
16
  });
17
17
  //# sourceMappingURL=media-viewer.cjs.map
18
18
  //# sourceMappingURL=media-viewer.cjs.map
@@ -1,6 +1,6 @@
1
- export { a as MediaViewer, b as MediaViewerProps } from './index-B71wa9AW.cjs';
1
+ export { a as MediaViewer, b as MediaViewerProps } from './index-CgPpIw_O.cjs';
2
2
  import 'react';
3
3
  import './ImageViewer.types-BaqlDeuo.cjs';
4
- import './VideoViewer.types-CTwS6PcM.cjs';
5
- import './AudioViewer.types-VWhRx6XG.cjs';
4
+ import './VideoViewer.types-_AwQ8DQr.cjs';
5
+ import './AudioViewer.types-CyhmQVQd.cjs';
6
6
  import './PdfViewer.types-_u6vboSl.cjs';
@@ -1,6 +1,6 @@
1
- export { a as MediaViewer, b as MediaViewerProps } from './index-bIntFOl5.js';
1
+ export { a as MediaViewer, b as MediaViewerProps } from './index-IeivGsNg.js';
2
2
  import 'react';
3
3
  import './ImageViewer.types-BaqlDeuo.js';
4
- import './VideoViewer.types-CTwS6PcM.js';
5
- import './AudioViewer.types-VWhRx6XG.js';
4
+ import './VideoViewer.types-_AwQ8DQr.js';
5
+ import './AudioViewer.types-CyhmQVQd.js';
6
6
  import './PdfViewer.types-_u6vboSl.js';
@@ -1,9 +1,9 @@
1
- export { MediaViewer } from './chunk-J3XS4HHX.js';
2
- import './chunk-JKO2Y5SH.js';
1
+ export { MediaViewer } from './chunk-QWSTG5MF.js';
2
+ import './chunk-YVDAXYHP.js';
3
3
  import './chunk-DZD2RJVD.js';
4
4
  import './chunk-A3CNYKT3.js';
5
5
  import './chunk-CPY7NHXM.js';
6
- import './chunk-EIBW5YLS.js';
6
+ import './chunk-74ZN5CNE.js';
7
7
  import './chunk-DSUHZO6T.js';
8
8
  //# sourceMappingURL=media-viewer.js.map
9
9
  //# sourceMappingURL=media-viewer.js.map
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var chunkKUKYJZIU_cjs = require('./chunk-KUKYJZIU.cjs');
4
- require('./chunk-G34OBTU3.cjs');
4
+ require('./chunk-HPNIEYSJ.cjs');
5
5
  require('./chunk-T6YELZPJ.cjs');
6
6
  require('./chunk-TXTRTTG7.cjs');
7
7
  require('./chunk-3WGRVDQR.cjs');
@@ -1,5 +1,5 @@
1
1
  export { MobileMenu, useMobileMenu } from './chunk-F6SCZCJE.js';
2
- import './chunk-RNPFSVFN.js';
2
+ import './chunk-FWCREAIT.js';
3
3
  import './chunk-CPY7NHXM.js';
4
4
  import './chunk-5U2DZ7AV.js';
5
5
  import './chunk-CHADHN6M.js';
package/dist/sidebar.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var chunkM2S5GCXY_cjs = require('./chunk-M2S5GCXY.cjs');
4
- require('./chunk-G34OBTU3.cjs');
4
+ require('./chunk-HPNIEYSJ.cjs');
5
5
  require('./chunk-T6YELZPJ.cjs');
6
6
  require('./chunk-TXTRTTG7.cjs');
7
7
  require('./chunk-3WGRVDQR.cjs');
package/dist/sidebar.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export { Sidebar, useSidebar } from './chunk-4CM2VMLR.js';
2
- import './chunk-RNPFSVFN.js';
2
+ import './chunk-FWCREAIT.js';
3
3
  import './chunk-CPY7NHXM.js';
4
4
  import './chunk-5U2DZ7AV.js';
5
5
  import './chunk-CHADHN6M.js';
@@ -1,13 +1,13 @@
1
1
  'use strict';
2
2
 
3
- var chunk3ETOEEVN_cjs = require('./chunk-3ETOEEVN.cjs');
3
+ var chunkI462NM2A_cjs = require('./chunk-I462NM2A.cjs');
4
4
  require('./chunk-3Z6ALUWP.cjs');
5
5
 
6
6
 
7
7
 
8
8
  Object.defineProperty(exports, "VideoViewer", {
9
9
  enumerable: true,
10
- get: function () { return chunk3ETOEEVN_cjs.VideoViewer; }
10
+ get: function () { return chunkI462NM2A_cjs.VideoViewer; }
11
11
  });
12
12
  //# sourceMappingURL=video-viewer.cjs.map
13
13
  //# sourceMappingURL=video-viewer.cjs.map
@@ -1,5 +1,5 @@
1
1
  import * as react from 'react';
2
- import { V as VideoViewerProps } from './VideoViewer.types-CTwS6PcM.cjs';
2
+ import { V as VideoViewerProps } from './VideoViewer.types-_AwQ8DQr.cjs';
3
3
 
4
4
  /**
5
5
  * A standalone video viewer — native controls, optional poster, fit-to-box, and