@particle-academy/react-fancy 6.5.0 → 6.6.1

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 (52) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/{chunk-L5AI4MGV.cjs → chunk-25HRO7HQ.cjs} +4 -4
  3. package/dist/{chunk-L5AI4MGV.cjs.map → chunk-25HRO7HQ.cjs.map} +1 -1
  4. package/dist/{chunk-MF2DCMOA.cjs → chunk-2TLAZ5MX.cjs} +7 -5
  5. package/dist/chunk-2TLAZ5MX.cjs.map +1 -0
  6. package/dist/chunk-2X54BVXG.js +58 -0
  7. package/dist/chunk-2X54BVXG.js.map +1 -0
  8. package/dist/{chunk-ARNFT23K.js → chunk-5IXOQGPX.js} +7 -5
  9. package/dist/chunk-5IXOQGPX.js.map +1 -0
  10. package/dist/chunk-5WXY3Q6X.cjs +63 -0
  11. package/dist/chunk-5WXY3Q6X.cjs.map +1 -0
  12. package/dist/{chunk-YRI6OFLY.js → chunk-MI57NAKF.js} +24 -21
  13. package/dist/chunk-MI57NAKF.js.map +1 -0
  14. package/dist/{chunk-QH4YSG7C.cjs → chunk-PM5I5ZHK.cjs} +23 -20
  15. package/dist/chunk-PM5I5ZHK.cjs.map +1 -0
  16. package/dist/{chunk-PVHZO4T3.js → chunk-TFX4LL7V.js} +17 -12
  17. package/dist/chunk-TFX4LL7V.js.map +1 -0
  18. package/dist/{chunk-RE4BYTVM.cjs → chunk-THMFLRDW.cjs} +4 -4
  19. package/dist/{chunk-RE4BYTVM.cjs.map → chunk-THMFLRDW.cjs.map} +1 -1
  20. package/dist/{chunk-YYNUTUJO.js → chunk-VSTUGU7W.js} +3 -3
  21. package/dist/{chunk-YYNUTUJO.js.map → chunk-VSTUGU7W.js.map} +1 -1
  22. package/dist/{chunk-TQL3QW7F.cjs → chunk-YQZB5Z7C.cjs} +16 -11
  23. package/dist/chunk-YQZB5Z7C.cjs.map +1 -0
  24. package/dist/{chunk-LYG2NLSV.js → chunk-ZPBKRJMH.js} +3 -3
  25. package/dist/{chunk-LYG2NLSV.js.map → chunk-ZPBKRJMH.js.map} +1 -1
  26. package/dist/code-block.cjs +3 -3
  27. package/dist/code-block.js +2 -2
  28. package/dist/code-view.cjs +3 -3
  29. package/dist/code-view.js +2 -2
  30. package/dist/editor.cjs +5 -5
  31. package/dist/editor.js +3 -3
  32. package/dist/index.cjs +13 -13
  33. package/dist/index.js +6 -6
  34. package/dist/styles.css +20 -0
  35. package/dist/styles.css.map +1 -1
  36. package/dist/tree-nav.cjs +4 -4
  37. package/dist/tree-nav.js +2 -2
  38. package/dist/virtual-list.cjs +2 -2
  39. package/dist/virtual-list.js +1 -1
  40. package/docs/CodeBlock.md +11 -3
  41. package/docs/CodeView.md +7 -3
  42. package/package.json +2 -2
  43. package/dist/chunk-6BB32POL.js +0 -29
  44. package/dist/chunk-6BB32POL.js.map +0 -1
  45. package/dist/chunk-ARNFT23K.js.map +0 -1
  46. package/dist/chunk-HZUKPYR3.cjs +0 -31
  47. package/dist/chunk-HZUKPYR3.cjs.map +0 -1
  48. package/dist/chunk-MF2DCMOA.cjs.map +0 -1
  49. package/dist/chunk-PVHZO4T3.js.map +0 -1
  50. package/dist/chunk-QH4YSG7C.cjs.map +0 -1
  51. package/dist/chunk-TQL3QW7F.cjs.map +0 -1
  52. package/dist/chunk-YRI6OFLY.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/styles.css"],"sourcesContent":["/* @particle-academy/react-fancy — base styles + animations */\n\n/* ─── Brand colour scheme ────────────────────────────────────────────────────\n Three token families every Fancy package can rely on, so a component never\n has to invent a palette or hardcode a literal:\n\n brand one accent — the thing a consumer rebrands first\n primary-* the accent as a full 50→950 scale\n secondary-* the NEUTRAL scale for surfaces, borders and body text\n\n Why this exists: packages were already writing `bg-brand`, `text-primary-600`\n and `border-secondary-200` against tokens nothing defined. Tailwind generated\n no such utility, the class silently resolved to nothing, and the component\n rendered unstyled — correct markup, no colour, no error. Defining them here\n turns those classes into a contract instead of a hope.\n\n SECONDARY INVERTS WITH THE THEME. `text-secondary-900` means \"strongest\n foreground\", not \"nearly black\" — so it stays readable on a dark surface.\n That distinction is the whole point: mixing a literal background (`bg-white`)\n with a semantic foreground (`text-secondary-900`) is what produced\n white-on-white in a dark host.\n\n OVERRIDE IT. Redefine any of these in your own `@theme` (or on `:root`) and\n yours wins — nothing here is `!important`, and the utilities read the variable\n rather than baking a value in:\n\n @theme { --color-brand: var(--color-emerald-600); }\n ──────────────────────────────────────────────────────────────────────────── */\n@theme {\n /* THE HOST'S OWN COLOUR. `color=\"brand\"` on any component resolves to these.\n Three variables, not one: a generated hover assumes \"same hue, darker\",\n and a brand palette need not work that way — a navy whose hover is gold\n cannot be derived from navy. So the host states the hover instead of the\n kit guessing it.\n\n :root {\n --color-brand: #003057;\n --color-brand-contrast: #ffffff; text/icon ON the brand colour\n --color-brand-hover: #D4A017; need not be a shade of brand\n }\n\n PER MODE, SWAP THE PAIR. `--color-brand` and `--color-brand-contrast` only\n mean anything together. Repointing one alone fails QUIETLY -- the button\n still renders, it is just unreadable (a pale surface still carrying white\n text; or, on ghost/outline where brand is the LABEL, dark text on a dark\n background):\n\n .dark { --color-brand: #BFD7EA; --color-brand-contrast: #003057; }\n\n `--color-brand-hover` usually needs no per-mode value: an accent chosen to\n stand against the brand tends to stand against it in both modes.\n\n NOTE: this comment does not survive the build -- `@theme` is emitted\n without it. The consumer-facing copy is in README.md, and that is the one\n to keep correct.\n\n Defaults keep the kit's violet so nothing changes for a host that never\n sets them. Soft/outline/ghost variants derive their tints with opacity\n modifiers (`bg-brand/10`), which is what lets three variables cover four\n variants without asking for a whole scale. */\n --color-brand: var(--color-violet-600);\n --color-brand-contrast: var(--color-white);\n --color-brand-hover: var(--color-violet-700);\n\n --color-primary-50: var(--color-violet-50);\n --color-primary-100: var(--color-violet-100);\n --color-primary-200: var(--color-violet-200);\n --color-primary-300: var(--color-violet-300);\n --color-primary-400: var(--color-violet-400);\n --color-primary-500: var(--color-violet-500);\n --color-primary-600: var(--color-violet-600);\n --color-primary-700: var(--color-violet-700);\n --color-primary-800: var(--color-violet-800);\n --color-primary-900: var(--color-violet-900);\n --color-primary-950: var(--color-violet-950);\n\n /* Light-theme defaults: 50 = faintest surface, 900/950 = strongest text. */\n --color-secondary-50: var(--color-zinc-50);\n --color-secondary-100: var(--color-zinc-100);\n --color-secondary-200: var(--color-zinc-200);\n --color-secondary-300: var(--color-zinc-300);\n --color-secondary-400: var(--color-zinc-400);\n --color-secondary-500: var(--color-zinc-500);\n --color-secondary-600: var(--color-zinc-600);\n --color-secondary-700: var(--color-zinc-700);\n --color-secondary-800: var(--color-zinc-800);\n --color-secondary-900: var(--color-zinc-900);\n --color-secondary-950: var(--color-zinc-950);\n}\n\n/* ─── Full screen in the top layer — neutralise the UA popover box ───────────\n An overlay-mode FullScreen is promoted with popover=\"manual\" so ancestor\n transforms / filters cannot trap it, and the UA sheet then styles it as a\n popover: padding .25em, a solid border, Canvas / CanvasText colours,\n overflow:auto. Any author rule outranks the UA origin, so this zero-\n specificity rule in the base layer puts those back to what a plain box has,\n while every utility a consumer passes through className still wins. Inline\n styles could not do that: they would beat the consumer's own classes. The\n box geometry (inset, width, height, margin) is set inline by the hook. */\n@layer base {\n :where([data-fullscreen-layer=\"top\"]) {\n padding: 0;\n border: 0 solid;\n background-color: transparent;\n color: inherit;\n overflow: visible;\n }\n}\n\n@layer base {\n\n/* ─── Progress (circular) — named diameters ──────────────────────────────────\n These ship as real CSS rather than Tailwind classes on the element, because a\n consumer who has not @sourced this package gets no generated utilities and\n the ring would render with NO SIZE at all. They were inline styles before,\n which always worked but could not be overridden by className (issue #17).\n\n Being inside @layer base, a className utility now beats them — which is the\n whole point, and the reason #20 had to land first. */\n[data-react-fancy-progress][data-size=\"sm\"] { width: 2rem; height: 2rem; }\n[data-react-fancy-progress][data-size=\"md\"] { width: 3rem; height: 3rem; }\n[data-react-fancy-progress][data-size=\"lg\"] { width: 4rem; height: 4rem; }\n\n\n\n/* Dark: the neutral scale flips, so \"900\" still means strongest foreground and\n \"50\" still means faintest surface. Wrapped in :where() for zero specificity,\n matching the rest of this file — any consumer override beats it.\n\n EVERY dark selector in this file names `[data-theme=\"dark\"]` beside `.dark`\n (5.53.0). A consumer's `@custom-variant dark` cannot reach these: it rewrites\n `dark:` utilities, never a selector with `.dark` written into it.\n tests/data-theme.test.ts fails on any `.dark` selector without its twin. */\n:where(.dark, [data-theme=\"dark\"]) {\n --color-secondary-50: var(--color-zinc-950);\n --color-secondary-100: var(--color-zinc-900);\n --color-secondary-200: var(--color-zinc-800);\n --color-secondary-300: var(--color-zinc-700);\n --color-secondary-400: var(--color-zinc-600);\n --color-secondary-500: var(--color-zinc-500);\n --color-secondary-600: var(--color-zinc-400);\n --color-secondary-700: var(--color-zinc-300);\n --color-secondary-800: var(--color-zinc-200);\n --color-secondary-900: var(--color-zinc-100);\n --color-secondary-950: var(--color-zinc-50);\n}\n\n/* ─── Input & Field defaults ─────────────────────────────────────────────────\n Better UX out of the box: spacing between stacked fields, smooth transitions,\n and polished dark-mode defaults. All overridable via data-attribute selectors.\n ──────────────────────────────────────────────────────────────────────────── */\n\n/* Field spacing is the CONTAINER's job, not the field's.\n ────────────────────────────────────────────────────────────────────────────\n This used to be:\n\n [data-react-fancy-field] + [data-react-fancy-field] { margin-top: 1rem; }\n\n An adjacent-sibling selector cannot know which way its parent lays children\n out. Stacked vertically it read as spacing; inside a `grid-cols-2` row it\n pushed the right-hand cell 16px down, and `items-start` could not fix it\n because a margin is not alignment. The only escape was wrapping every field\n in a plain <div> so each became an only-child.\n\n Space fields with the container instead — `space-y-4`, or `gap-4` on a flex\n or grid parent. A container knows its own axis; a sibling selector never\n can. See CHANGELOG 5.17.0 for the migration.\n ──────────────────────────────────────────────────────────────────────────── */\n\n/* Smooth transitions on all text inputs, selects, and textareas */\n[data-react-fancy-input] input,\n[data-react-fancy-textarea],\n[data-react-fancy-select],\n[data-react-fancy-date-picker],\n[data-react-fancy-autocomplete] input,\n[data-react-fancy-pillbox],\n[data-react-fancy-otp-input] input,\n[data-react-fancy-time-picker] input,\n[data-react-fancy-color-picker] input {\n transition: border-color 0.15s ease, box-shadow 0.15s ease,\n background-color 0.15s ease;\n}\n\n/* Dark-mode input refinements — slightly darker bg for contrast against page */\n/* ─── Inherent dark-mode defaults ────────────────────────────────────────────\n Every rule below is wrapped in :where() so it carries ZERO specificity, which\n buys two properties at once:\n 1. Inherent — a sensible dark appearance ships out of the box, even when the\n consumer's Tailwind build doesn't generate react-fancy's `dark:` utility\n classes (e.g. they didn't @source the package). This is what lets the\n overlays (Dropdown, Menu, Popover, …) go dark on their own.\n 2. Override-friendly — because specificity is 0, ANY consumer override wins:\n a plain `dark:bg-…` utility, a custom class, or the component's own\n `dark:` class (when it IS generated) all take precedence — no !important,\n no need to target the data-attribute.\n ──────────────────────────────────────────────────────────────────────────── */\n\n/* Every colour below reads a semantic token (`--fancy-<role>-dark`, see\n THEME_TOKENS / README \"Semantic tokens\") with today's value as the fallback,\n so a host that sets nothing sees exactly what it saw before. */\n\n/* Inputs / selects / textareas — darker bg for contrast against the page */\n:where(.dark [data-react-fancy-input] input, [data-theme=\"dark\"] [data-react-fancy-input] input),\n:where(.dark [data-react-fancy-textarea], [data-theme=\"dark\"] [data-react-fancy-textarea]),\n:where(.dark [data-react-fancy-select], [data-theme=\"dark\"] [data-react-fancy-select]),\n:where(.dark [data-react-fancy-date-picker], [data-theme=\"dark\"] [data-react-fancy-date-picker]),\n:where(.dark [data-react-fancy-autocomplete] input, [data-theme=\"dark\"] [data-react-fancy-autocomplete] input),\n:where(.dark [data-react-fancy-pillbox], [data-theme=\"dark\"] [data-react-fancy-pillbox]),\n:where(.dark [data-react-fancy-otp-input] input, [data-theme=\"dark\"] [data-react-fancy-otp-input] input),\n:where(.dark [data-react-fancy-time-picker] input, [data-theme=\"dark\"] [data-react-fancy-time-picker] input) {\n background-color: var(--fancy-input-dark, #1e1e24);\n border-color: var(--fancy-border-strong-dark, #3f3f46);\n}\n\n/* Focus ring — brighter border + subtle glow */\n:where(.dark [data-react-fancy-input] input:focus, [data-theme=\"dark\"] [data-react-fancy-input] input:focus),\n:where(.dark [data-react-fancy-textarea]:focus, [data-theme=\"dark\"] [data-react-fancy-textarea]:focus),\n:where(.dark [data-react-fancy-select]:focus, [data-theme=\"dark\"] [data-react-fancy-select]:focus),\n:where(.dark [data-react-fancy-date-picker]:focus, [data-theme=\"dark\"] [data-react-fancy-date-picker]:focus),\n:where(.dark [data-react-fancy-autocomplete] input:focus, [data-theme=\"dark\"] [data-react-fancy-autocomplete] input:focus),\n:where(.dark [data-react-fancy-otp-input] input:focus, [data-theme=\"dark\"] [data-react-fancy-otp-input] input:focus),\n:where(.dark [data-react-fancy-time-picker] input:focus, [data-theme=\"dark\"] [data-react-fancy-time-picker] input:focus) {\n border-color: var(--fancy-focus-dark, #60a5fa);\n /* = rgba(96, 165, 250, 0.15) when the token is unset */\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--fancy-focus-dark, #60a5fa) 15%, transparent);\n}\n\n/* Field labels — brighter for readability */\n:where(.dark [data-react-fancy-field] > label:first-child, [data-theme=\"dark\"] [data-react-fancy-field] > label:first-child) {\n color: var(--fancy-text-dark, #f4f4f5);\n}\n\n/* Checkbox & radio */\n:where(.dark [data-react-fancy-checkbox] input, [data-theme=\"dark\"] [data-react-fancy-checkbox] input),\n:where(.dark [data-react-fancy-radio-group] input, [data-theme=\"dark\"] [data-react-fancy-radio-group] input) {\n background-color: var(--fancy-input-dark, #1e1e24);\n border-color: var(--fancy-border-strong-dark, #3f3f46);\n}\n\n/* Switch unchecked track */\n:where(.dark [data-react-fancy-switch] button[aria-checked=\"false\"], [data-theme=\"dark\"] [data-react-fancy-switch] button[aria-checked=\"false\"]) {\n background-color: var(--fancy-border-dark, #3f3f46);\n}\n\n/* Input wrapper affixes */\n:where(.dark [data-react-fancy-input-wrapper] > span:first-child, [data-theme=\"dark\"] [data-react-fancy-input-wrapper] > span:first-child),\n:where(.dark [data-react-fancy-input-wrapper] > span:last-child, [data-theme=\"dark\"] [data-react-fancy-input-wrapper] > span:last-child) {\n border-color: var(--fancy-border-strong-dark, #3f3f46);\n background-color: var(--fancy-surface-muted-dark, #27272a);\n}\n\n/* Date picker icon (calendar, clock) */\n:where(.dark [data-react-fancy-date-picker]::-webkit-calendar-picker-indicator, [data-theme=\"dark\"] [data-react-fancy-date-picker]::-webkit-calendar-picker-indicator) {\n filter: invert(0.8);\n}\n\n/* ─── Overlays — inherent dark surfaces ──────────────────────────────────────\n Dropdown / Menu / Popover / ContextMenu / MobileMenu flyout / Modal / Toast.\n Mirrors the components' own `dark:` utilities (zinc-900 surface, zinc-700\n border, zinc-300 text) so overlays render correctly in dark mode WITHOUT the\n consumer @sourcing react-fancy. When those utilities ARE generated they win\n (specificity 0 here), so there's no drift or double-styling. */\n:where(.dark [data-react-fancy-dropdown], [data-theme=\"dark\"] [data-react-fancy-dropdown]),\n:where(.dark [data-react-fancy-menu], [data-theme=\"dark\"] [data-react-fancy-menu]),\n:where(.dark [data-react-fancy-popover], [data-theme=\"dark\"] [data-react-fancy-popover]),\n:where(.dark [data-react-fancy-context-menu], [data-theme=\"dark\"] [data-react-fancy-context-menu]),\n:where(.dark [data-react-fancy-context-menu-sub-content], [data-theme=\"dark\"] [data-react-fancy-context-menu-sub-content]),\n:where(.dark [data-react-fancy-mobile-menu-flyout], [data-theme=\"dark\"] [data-react-fancy-mobile-menu-flyout]),\n:where(.dark [data-react-fancy-modal], [data-theme=\"dark\"] [data-react-fancy-modal]),\n:where(.dark [data-react-fancy-toast-item], [data-theme=\"dark\"] [data-react-fancy-toast-item]) {\n background-color: var(--fancy-surface-dark, #18181b); /* zinc-900 */\n border-color: var(--fancy-border-dark, #3f3f46); /* zinc-700 */\n color: var(--fancy-text-soft-dark, #d4d4d8); /* zinc-300 */\n}\n\n/* Overlay item hover states */\n:where(.dark [data-react-fancy-dropdown-item]:hover, [data-theme=\"dark\"] [data-react-fancy-dropdown-item]:hover),\n:where(.dark [data-react-fancy-menu-item]:hover, [data-theme=\"dark\"] [data-react-fancy-menu-item]:hover),\n:where(.dark [data-react-fancy-context-menu-item]:hover, [data-theme=\"dark\"] [data-react-fancy-context-menu-item]:hover) {\n background-color: var(--fancy-surface-muted-dark, #27272a); /* zinc-800 */\n}\n\n/* Overlay separators */\n:where(.dark [data-react-fancy-dropdown-separator], [data-theme=\"dark\"] [data-react-fancy-dropdown-separator]),\n:where(.dark [data-react-fancy-context-menu-separator], [data-theme=\"dark\"] [data-react-fancy-context-menu-separator]) {\n background-color: var(--fancy-border-dark, #3f3f46); /* zinc-700 */\n}\n\n/* ─── Media viewer — transparency checkerboard ───────────────────────────────\n Backdrop for <ImageViewer> / <MediaViewer> so transparent images read clearly.\n Light + dark variants; the dark rule is wrapped in :where() (zero specificity)\n so it ships out of the box without the consumer @sourcing react-fancy. */\n.fancy-checkerboard {\n background-color: #ffffff;\n background-image:\n linear-gradient(45deg, #e5e7eb 25%, transparent 25%),\n linear-gradient(-45deg, #e5e7eb 25%, transparent 25%),\n linear-gradient(45deg, transparent 75%, #e5e7eb 75%),\n linear-gradient(-45deg, transparent 75%, #e5e7eb 75%);\n background-size: 16px 16px;\n background-position: 0 0, 0 8px, 8px -8px, -8px 0;\n}\n\n:where(.dark, [data-theme=\"dark\"]) .fancy-checkerboard {\n background-color: #18181b; /* zinc-900 */\n background-image:\n linear-gradient(45deg, #27272a 25%, transparent 25%),\n linear-gradient(-45deg, #27272a 25%, transparent 25%),\n linear-gradient(45deg, transparent 75%, #27272a 75%),\n linear-gradient(-45deg, transparent 75%, #27272a 75%);\n}\n}\n\n\n@keyframes fancy-fade-in {\n from {\n opacity: 0;\n transform: translateY(4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n@keyframes fancy-slide-left {\n from {\n transform: translateX(100%);\n }\n to {\n transform: translateX(0);\n }\n}\n\n@keyframes fancy-slide-right {\n from {\n transform: translateX(-100%);\n }\n to {\n transform: translateX(0);\n }\n}\n\n@layer base {\n\n\n.fancy-fade-in {\n animation: fancy-fade-in 0.2s ease-out;\n}\n\n.fancy-slide-left {\n animation: fancy-slide-left 0.3s ease-out;\n}\n\n.fancy-slide-right {\n animation: fancy-slide-right 0.3s ease-out;\n}\n}\n\n\n/* Overlay / floating animations */\n\n@keyframes fancy-scale-in {\n from {\n opacity: 0;\n transform: scale(0.95);\n }\n to {\n opacity: 1;\n transform: scale(1);\n }\n}\n\n@keyframes fancy-slide-up {\n from {\n opacity: 0;\n transform: translateY(16px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n@keyframes fancy-fade-out {\n from {\n opacity: 1;\n }\n to {\n opacity: 0;\n }\n}\n\n@keyframes fancy-toast-in {\n from {\n opacity: 0;\n transform: translateX(100%);\n }\n to {\n opacity: 1;\n transform: translateX(0);\n }\n}\n\n@keyframes fancy-collapse-open {\n from {\n grid-template-rows: 0fr;\n }\n to {\n grid-template-rows: 1fr;\n }\n}\n\n@keyframes fancy-collapse-close {\n from {\n grid-template-rows: 1fr;\n }\n to {\n grid-template-rows: 0fr;\n }\n}\n\n@layer base {\n\n\n.fancy-scale-in {\n animation: fancy-scale-in 0.15s ease-out;\n}\n\n.fancy-slide-up {\n animation: fancy-slide-up 0.2s ease-out;\n}\n\n.fancy-fade-out {\n animation: fancy-fade-out 0.15s ease-in forwards;\n}\n\n.fancy-toast-in {\n animation: fancy-toast-in 0.3s ease-out;\n}\n\n.fancy-collapse-open {\n animation: fancy-collapse-open 0.2s ease-out forwards;\n}\n\n.fancy-collapse-close {\n animation: fancy-collapse-close 0.15s ease-in forwards;\n}\n}\n\n\n/* Mobile flyout slide animations */\n\n@keyframes fancy-slide-in-left {\n from {\n transform: translateX(-100%);\n }\n to {\n transform: translateX(0);\n }\n}\n\n@keyframes fancy-slide-out-left {\n from {\n transform: translateX(0);\n }\n to {\n transform: translateX(-100%);\n }\n}\n\n@keyframes fancy-slide-in-right {\n from {\n transform: translateX(100%);\n }\n to {\n transform: translateX(0);\n }\n}\n\n/* Avatar glow — a looping halo ring (box-shadow only, no reflow). Color comes\n from the --fancy-glow custom property set on the element. */\n@keyframes fancy-avatar-glow {\n 0% {\n box-shadow: 0 0 0 0 color-mix(in srgb, var(--fancy-glow, #a855f7) 55%, transparent);\n opacity: 0.9;\n }\n 70% {\n box-shadow: 0 0 0 6px color-mix(in srgb, var(--fancy-glow, #a855f7) 0%, transparent);\n opacity: 0.5;\n }\n 100% {\n box-shadow: 0 0 0 0 color-mix(in srgb, var(--fancy-glow, #a855f7) 0%, transparent);\n opacity: 0.9;\n }\n}\n\n@layer base {\n\n\n.fancy-avatar-glow {\n position: absolute;\n inset: 0;\n border-radius: 9999px;\n pointer-events: none;\n box-shadow:\n 0 0 0 2px color-mix(in srgb, var(--fancy-glow, #a855f7) 70%, transparent),\n 0 0 10px 0 color-mix(in srgb, var(--fancy-glow, #a855f7) 45%, transparent);\n animation: fancy-avatar-glow 1.6s ease-out infinite;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .fancy-avatar-glow {\n animation: none;\n }\n}\n}\n\n\n@keyframes fancy-slide-out-right {\n from {\n transform: translateX(0);\n }\n to {\n transform: translateX(100%);\n }\n}\n\n@layer base {\n\n\n.fancy-slide-in-left {\n animation: fancy-slide-in-left 0.25s ease-out;\n}\n\n.fancy-slide-out-left {\n animation: fancy-slide-out-left 0.2s ease-in forwards;\n}\n\n.fancy-slide-in-right {\n animation: fancy-slide-in-right 0.25s ease-out;\n}\n\n.fancy-slide-out-right {\n animation: fancy-slide-out-right 0.2s ease-in forwards;\n}\n}\n\n\n/* Vertical drawer edges. The horizontal pair above predates Drawer (it was\n built for MobileMenu.Flyout, which only ever opened left/right); these\n complete the set so `side=\"top\"`/`side=\"bottom\"` animate rather than pop. */\n\n@keyframes fancy-slide-in-top {\n from {\n transform: translateY(-100%);\n }\n to {\n transform: translateY(0);\n }\n}\n\n@keyframes fancy-slide-out-top {\n from {\n transform: translateY(0);\n }\n to {\n transform: translateY(-100%);\n }\n}\n\n@keyframes fancy-slide-in-bottom {\n from {\n transform: translateY(100%);\n }\n to {\n transform: translateY(0);\n }\n}\n\n@keyframes fancy-slide-out-bottom {\n from {\n transform: translateY(0);\n }\n to {\n transform: translateY(100%);\n }\n}\n\n@layer base {\n\n\n.fancy-slide-in-top {\n animation: fancy-slide-in-top 0.25s ease-out;\n}\n\n.fancy-slide-out-top {\n animation: fancy-slide-out-top 0.2s ease-in forwards;\n}\n\n.fancy-slide-in-bottom {\n animation: fancy-slide-in-bottom 0.25s ease-out;\n}\n\n.fancy-slide-out-bottom {\n animation: fancy-slide-out-bottom 0.2s ease-in forwards;\n}\n\n/* Reduced motion collapses the travel to a ~0 duration. Since 5.51.0\n `useAnimation` also unmounts when no animation runs at all, so this is no\n longer what keeps a closed drawer from staying in the DOM — but a ~0 duration\n keeps the exit on the same path as every other close. */\n@media (prefers-reduced-motion: reduce) {\n .fancy-slide-in-left,\n .fancy-slide-out-left,\n .fancy-slide-in-right,\n .fancy-slide-out-right,\n .fancy-slide-in-top,\n .fancy-slide-out-top,\n .fancy-slide-in-bottom,\n .fancy-slide-out-bottom {\n animation-duration: 0.01ms;\n }\n}\n\n/* ─── Marquee ────────────────────────────────────────────────────────────────\n Edge-to-edge auto-scrolling strip. The track holds two identical copies of\n the content; translateX(-50%) over one loop shifts exactly one copy, so the\n wrap is seamless. Duration/gap/fade arrive as custom properties set by the\n component. No animation under prefers-reduced-motion.\n ──────────────────────────────────────────────────────────────────────────── */\n\n[data-react-fancy-marquee] {\n overflow: hidden;\n width: 100%;\n}\n\n.fancy-marquee--fade {\n -webkit-mask-image: linear-gradient(\n to right,\n transparent,\n black var(--fancy-marquee-fade, 48px),\n black calc(100% - var(--fancy-marquee-fade, 48px)),\n transparent\n );\n mask-image: linear-gradient(\n to right,\n transparent,\n black var(--fancy-marquee-fade, 48px),\n black calc(100% - var(--fancy-marquee-fade, 48px)),\n transparent\n );\n}\n\n.fancy-marquee__track {\n display: flex;\n width: max-content;\n animation: fancy-marquee var(--fancy-marquee-duration, 40s) linear infinite;\n will-change: transform;\n}\n\n[data-react-fancy-marquee][data-direction=\"right\"] .fancy-marquee__track {\n animation-direction: reverse;\n}\n\n.fancy-marquee--pause-hover:hover .fancy-marquee__track,\n[data-react-fancy-marquee][data-paused] .fancy-marquee__track {\n animation-play-state: paused;\n}\n\n.fancy-marquee__group {\n display: flex;\n flex: none;\n}\n\n.fancy-marquee__unit {\n display: flex;\n flex: none;\n align-items: center;\n gap: var(--fancy-marquee-gap, 40px);\n padding-inline-end: var(--fancy-marquee-gap, 40px);\n white-space: nowrap;\n}\n\n.fancy-marquee__item,\n.fancy-marquee__separator {\n flex: none;\n}\n}\n\n\n@keyframes fancy-marquee {\n from {\n transform: translateX(0);\n }\n to {\n transform: translateX(-50%);\n }\n}\n\n@layer base {\n\n\n@media (prefers-reduced-motion: reduce) {\n .fancy-marquee__track {\n animation: none;\n }\n}\n}\n\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/styles.css"],"sourcesContent":["/* @particle-academy/react-fancy — base styles + animations */\n\n/* ─── Brand colour scheme ────────────────────────────────────────────────────\n Three token families every Fancy package can rely on, so a component never\n has to invent a palette or hardcode a literal:\n\n brand one accent — the thing a consumer rebrands first\n primary-* the accent as a full 50→950 scale\n secondary-* the NEUTRAL scale for surfaces, borders and body text\n\n Why this exists: packages were already writing `bg-brand`, `text-primary-600`\n and `border-secondary-200` against tokens nothing defined. Tailwind generated\n no such utility, the class silently resolved to nothing, and the component\n rendered unstyled — correct markup, no colour, no error. Defining them here\n turns those classes into a contract instead of a hope.\n\n SECONDARY INVERTS WITH THE THEME. `text-secondary-900` means \"strongest\n foreground\", not \"nearly black\" — so it stays readable on a dark surface.\n That distinction is the whole point: mixing a literal background (`bg-white`)\n with a semantic foreground (`text-secondary-900`) is what produced\n white-on-white in a dark host.\n\n OVERRIDE IT. Redefine any of these in your own `@theme` (or on `:root`) and\n yours wins — nothing here is `!important`, and the utilities read the variable\n rather than baking a value in:\n\n @theme { --color-brand: var(--color-emerald-600); }\n ──────────────────────────────────────────────────────────────────────────── */\n@theme {\n /* THE HOST'S OWN COLOUR. `color=\"brand\"` on any component resolves to these.\n Three variables, not one: a generated hover assumes \"same hue, darker\",\n and a brand palette need not work that way — a navy whose hover is gold\n cannot be derived from navy. So the host states the hover instead of the\n kit guessing it.\n\n :root {\n --color-brand: #003057;\n --color-brand-contrast: #ffffff; text/icon ON the brand colour\n --color-brand-hover: #D4A017; need not be a shade of brand\n }\n\n PER MODE, SWAP THE PAIR. `--color-brand` and `--color-brand-contrast` only\n mean anything together. Repointing one alone fails QUIETLY -- the button\n still renders, it is just unreadable (a pale surface still carrying white\n text; or, on ghost/outline where brand is the LABEL, dark text on a dark\n background):\n\n .dark { --color-brand: #BFD7EA; --color-brand-contrast: #003057; }\n\n `--color-brand-hover` usually needs no per-mode value: an accent chosen to\n stand against the brand tends to stand against it in both modes.\n\n NOTE: this comment does not survive the build -- `@theme` is emitted\n without it. The consumer-facing copy is in README.md, and that is the one\n to keep correct.\n\n Defaults keep the kit's violet so nothing changes for a host that never\n sets them. Soft/outline/ghost variants derive their tints with opacity\n modifiers (`bg-brand/10`), which is what lets three variables cover four\n variants without asking for a whole scale. */\n --color-brand: var(--color-violet-600);\n --color-brand-contrast: var(--color-white);\n --color-brand-hover: var(--color-violet-700);\n\n --color-primary-50: var(--color-violet-50);\n --color-primary-100: var(--color-violet-100);\n --color-primary-200: var(--color-violet-200);\n --color-primary-300: var(--color-violet-300);\n --color-primary-400: var(--color-violet-400);\n --color-primary-500: var(--color-violet-500);\n --color-primary-600: var(--color-violet-600);\n --color-primary-700: var(--color-violet-700);\n --color-primary-800: var(--color-violet-800);\n --color-primary-900: var(--color-violet-900);\n --color-primary-950: var(--color-violet-950);\n\n /* Light-theme defaults: 50 = faintest surface, 900/950 = strongest text. */\n --color-secondary-50: var(--color-zinc-50);\n --color-secondary-100: var(--color-zinc-100);\n --color-secondary-200: var(--color-zinc-200);\n --color-secondary-300: var(--color-zinc-300);\n --color-secondary-400: var(--color-zinc-400);\n --color-secondary-500: var(--color-zinc-500);\n --color-secondary-600: var(--color-zinc-600);\n --color-secondary-700: var(--color-zinc-700);\n --color-secondary-800: var(--color-zinc-800);\n --color-secondary-900: var(--color-zinc-900);\n --color-secondary-950: var(--color-zinc-950);\n}\n\n/* ─── Full screen in the top layer — neutralise the UA popover box ───────────\n An overlay-mode FullScreen is promoted with popover=\"manual\" so ancestor\n transforms / filters cannot trap it, and the UA sheet then styles it as a\n popover: padding .25em, a solid border, Canvas / CanvasText colours,\n overflow:auto. Any author rule outranks the UA origin, so this zero-\n specificity rule in the base layer puts those back to what a plain box has,\n while every utility a consumer passes through className still wins. Inline\n styles could not do that: they would beat the consumer's own classes. The\n box geometry (inset, width, height, margin) is set inline by the hook. */\n@layer base {\n :where([data-fullscreen-layer=\"top\"]) {\n padding: 0;\n border: 0 solid;\n background-color: transparent;\n color: inherit;\n overflow: visible;\n }\n}\n\n@layer base {\n\n/* ─── Progress (circular) — named diameters ──────────────────────────────────\n These ship as real CSS rather than Tailwind classes on the element, because a\n consumer who has not @sourced this package gets no generated utilities and\n the ring would render with NO SIZE at all. They were inline styles before,\n which always worked but could not be overridden by className (issue #17).\n\n Being inside @layer base, a className utility now beats them — which is the\n whole point, and the reason #20 had to land first. */\n[data-react-fancy-progress][data-size=\"sm\"] { width: 2rem; height: 2rem; }\n[data-react-fancy-progress][data-size=\"md\"] { width: 3rem; height: 3rem; }\n[data-react-fancy-progress][data-size=\"lg\"] { width: 4rem; height: 4rem; }\n\n\n\n/* Dark: the neutral scale flips, so \"900\" still means strongest foreground and\n \"50\" still means faintest surface. Wrapped in :where() for zero specificity,\n matching the rest of this file — any consumer override beats it.\n\n EVERY dark selector in this file names `[data-theme=\"dark\"]` beside `.dark`\n (5.53.0). A consumer's `@custom-variant dark` cannot reach these: it rewrites\n `dark:` utilities, never a selector with `.dark` written into it.\n tests/data-theme.test.ts fails on any `.dark` selector without its twin. */\n:where(.dark, [data-theme=\"dark\"]) {\n --color-secondary-50: var(--color-zinc-950);\n --color-secondary-100: var(--color-zinc-900);\n --color-secondary-200: var(--color-zinc-800);\n --color-secondary-300: var(--color-zinc-700);\n --color-secondary-400: var(--color-zinc-600);\n --color-secondary-500: var(--color-zinc-500);\n --color-secondary-600: var(--color-zinc-400);\n --color-secondary-700: var(--color-zinc-300);\n --color-secondary-800: var(--color-zinc-200);\n --color-secondary-900: var(--color-zinc-100);\n --color-secondary-950: var(--color-zinc-50);\n}\n\n/* ─── CodeBlock / CodeView palette ───────────────────────────────────────────\n Both components declare the light AND dark syntax palette on their root\n (`--fc-<key>-l` / `--fc-<key>-d`) and paint with\n `var(--fc-<key>, var(--fc-<key>-l))`, so server-rendered HTML is right in\n either theme. Their `dark:[--fc-<key>:…]` classes switch it under the\n consumer's Tailwind `dark` variant; this rule does the same under `.dark` /\n `[data-theme=\"dark\"]` for a host whose Tailwind does not scan the package.\n Keep the key list in step with CODE_PALETTE_KEYS (a test checks). */\n:where(\n .dark [data-react-fancy-code-palette],\n [data-theme=\"dark\"] [data-react-fancy-code-palette],\n .dark[data-react-fancy-code-palette],\n [data-theme=\"dark\"][data-react-fancy-code-palette]\n) {\n --fc-background: var(--fc-background-d);\n --fc-foreground: var(--fc-foreground-d);\n --fc-gutterBackground: var(--fc-gutterBackground-d);\n --fc-gutterForeground: var(--fc-gutterForeground-d);\n --fc-gutterBorder: var(--fc-gutterBorder-d);\n --fc-cursorColor: var(--fc-cursorColor-d);\n --fc-keyword: var(--fc-keyword-d);\n --fc-string: var(--fc-string-d);\n --fc-comment: var(--fc-comment-d);\n --fc-number: var(--fc-number-d);\n --fc-operator: var(--fc-operator-d);\n --fc-function: var(--fc-function-d);\n --fc-type: var(--fc-type-d);\n --fc-tag: var(--fc-tag-d);\n --fc-attribute: var(--fc-attribute-d);\n --fc-attributeValue: var(--fc-attributeValue-d);\n --fc-punctuation: var(--fc-punctuation-d);\n --fc-variable: var(--fc-variable-d);\n}\n\n/* ─── Input & Field defaults ─────────────────────────────────────────────────\n Better UX out of the box: spacing between stacked fields, smooth transitions,\n and polished dark-mode defaults. All overridable via data-attribute selectors.\n ──────────────────────────────────────────────────────────────────────────── */\n\n/* Field spacing is the CONTAINER's job, not the field's.\n ────────────────────────────────────────────────────────────────────────────\n This used to be:\n\n [data-react-fancy-field] + [data-react-fancy-field] { margin-top: 1rem; }\n\n An adjacent-sibling selector cannot know which way its parent lays children\n out. Stacked vertically it read as spacing; inside a `grid-cols-2` row it\n pushed the right-hand cell 16px down, and `items-start` could not fix it\n because a margin is not alignment. The only escape was wrapping every field\n in a plain <div> so each became an only-child.\n\n Space fields with the container instead — `space-y-4`, or `gap-4` on a flex\n or grid parent. A container knows its own axis; a sibling selector never\n can. See CHANGELOG 5.17.0 for the migration.\n ──────────────────────────────────────────────────────────────────────────── */\n\n/* Smooth transitions on all text inputs, selects, and textareas */\n[data-react-fancy-input] input,\n[data-react-fancy-textarea],\n[data-react-fancy-select],\n[data-react-fancy-date-picker],\n[data-react-fancy-autocomplete] input,\n[data-react-fancy-pillbox],\n[data-react-fancy-otp-input] input,\n[data-react-fancy-time-picker] input,\n[data-react-fancy-color-picker] input {\n transition: border-color 0.15s ease, box-shadow 0.15s ease,\n background-color 0.15s ease;\n}\n\n/* Dark-mode input refinements — slightly darker bg for contrast against page */\n/* ─── Inherent dark-mode defaults ────────────────────────────────────────────\n Every rule below is wrapped in :where() so it carries ZERO specificity, which\n buys two properties at once:\n 1. Inherent — a sensible dark appearance ships out of the box, even when the\n consumer's Tailwind build doesn't generate react-fancy's `dark:` utility\n classes (e.g. they didn't @source the package). This is what lets the\n overlays (Dropdown, Menu, Popover, …) go dark on their own.\n 2. Override-friendly — because specificity is 0, ANY consumer override wins:\n a plain `dark:bg-…` utility, a custom class, or the component's own\n `dark:` class (when it IS generated) all take precedence — no !important,\n no need to target the data-attribute.\n ──────────────────────────────────────────────────────────────────────────── */\n\n/* Every colour below reads a semantic token (`--fancy-<role>-dark`, see\n THEME_TOKENS / README \"Semantic tokens\") with today's value as the fallback,\n so a host that sets nothing sees exactly what it saw before. */\n\n/* Inputs / selects / textareas — darker bg for contrast against the page */\n:where(.dark [data-react-fancy-input] input, [data-theme=\"dark\"] [data-react-fancy-input] input),\n:where(.dark [data-react-fancy-textarea], [data-theme=\"dark\"] [data-react-fancy-textarea]),\n:where(.dark [data-react-fancy-select], [data-theme=\"dark\"] [data-react-fancy-select]),\n:where(.dark [data-react-fancy-date-picker], [data-theme=\"dark\"] [data-react-fancy-date-picker]),\n:where(.dark [data-react-fancy-autocomplete] input, [data-theme=\"dark\"] [data-react-fancy-autocomplete] input),\n:where(.dark [data-react-fancy-pillbox], [data-theme=\"dark\"] [data-react-fancy-pillbox]),\n:where(.dark [data-react-fancy-otp-input] input, [data-theme=\"dark\"] [data-react-fancy-otp-input] input),\n:where(.dark [data-react-fancy-time-picker] input, [data-theme=\"dark\"] [data-react-fancy-time-picker] input) {\n background-color: var(--fancy-input-dark, #1e1e24);\n border-color: var(--fancy-border-strong-dark, #3f3f46);\n}\n\n/* Focus ring — brighter border + subtle glow */\n:where(.dark [data-react-fancy-input] input:focus, [data-theme=\"dark\"] [data-react-fancy-input] input:focus),\n:where(.dark [data-react-fancy-textarea]:focus, [data-theme=\"dark\"] [data-react-fancy-textarea]:focus),\n:where(.dark [data-react-fancy-select]:focus, [data-theme=\"dark\"] [data-react-fancy-select]:focus),\n:where(.dark [data-react-fancy-date-picker]:focus, [data-theme=\"dark\"] [data-react-fancy-date-picker]:focus),\n:where(.dark [data-react-fancy-autocomplete] input:focus, [data-theme=\"dark\"] [data-react-fancy-autocomplete] input:focus),\n:where(.dark [data-react-fancy-otp-input] input:focus, [data-theme=\"dark\"] [data-react-fancy-otp-input] input:focus),\n:where(.dark [data-react-fancy-time-picker] input:focus, [data-theme=\"dark\"] [data-react-fancy-time-picker] input:focus) {\n border-color: var(--fancy-focus-dark, #60a5fa);\n /* = rgba(96, 165, 250, 0.15) when the token is unset */\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--fancy-focus-dark, #60a5fa) 15%, transparent);\n}\n\n/* Field labels — brighter for readability */\n:where(.dark [data-react-fancy-field] > label:first-child, [data-theme=\"dark\"] [data-react-fancy-field] > label:first-child) {\n color: var(--fancy-text-dark, #f4f4f5);\n}\n\n/* Checkbox & radio */\n:where(.dark [data-react-fancy-checkbox] input, [data-theme=\"dark\"] [data-react-fancy-checkbox] input),\n:where(.dark [data-react-fancy-radio-group] input, [data-theme=\"dark\"] [data-react-fancy-radio-group] input) {\n background-color: var(--fancy-input-dark, #1e1e24);\n border-color: var(--fancy-border-strong-dark, #3f3f46);\n}\n\n/* Switch unchecked track */\n:where(.dark [data-react-fancy-switch] button[aria-checked=\"false\"], [data-theme=\"dark\"] [data-react-fancy-switch] button[aria-checked=\"false\"]) {\n background-color: var(--fancy-border-dark, #3f3f46);\n}\n\n/* Input wrapper affixes */\n:where(.dark [data-react-fancy-input-wrapper] > span:first-child, [data-theme=\"dark\"] [data-react-fancy-input-wrapper] > span:first-child),\n:where(.dark [data-react-fancy-input-wrapper] > span:last-child, [data-theme=\"dark\"] [data-react-fancy-input-wrapper] > span:last-child) {\n border-color: var(--fancy-border-strong-dark, #3f3f46);\n background-color: var(--fancy-surface-muted-dark, #27272a);\n}\n\n/* Date picker icon (calendar, clock) */\n:where(.dark [data-react-fancy-date-picker]::-webkit-calendar-picker-indicator, [data-theme=\"dark\"] [data-react-fancy-date-picker]::-webkit-calendar-picker-indicator) {\n filter: invert(0.8);\n}\n\n/* ─── Overlays — inherent dark surfaces ──────────────────────────────────────\n Dropdown / Menu / Popover / ContextMenu / MobileMenu flyout / Modal / Toast.\n Mirrors the components' own `dark:` utilities (zinc-900 surface, zinc-700\n border, zinc-300 text) so overlays render correctly in dark mode WITHOUT the\n consumer @sourcing react-fancy. When those utilities ARE generated they win\n (specificity 0 here), so there's no drift or double-styling. */\n:where(.dark [data-react-fancy-dropdown], [data-theme=\"dark\"] [data-react-fancy-dropdown]),\n:where(.dark [data-react-fancy-menu], [data-theme=\"dark\"] [data-react-fancy-menu]),\n:where(.dark [data-react-fancy-popover], [data-theme=\"dark\"] [data-react-fancy-popover]),\n:where(.dark [data-react-fancy-context-menu], [data-theme=\"dark\"] [data-react-fancy-context-menu]),\n:where(.dark [data-react-fancy-context-menu-sub-content], [data-theme=\"dark\"] [data-react-fancy-context-menu-sub-content]),\n:where(.dark [data-react-fancy-mobile-menu-flyout], [data-theme=\"dark\"] [data-react-fancy-mobile-menu-flyout]),\n:where(.dark [data-react-fancy-modal], [data-theme=\"dark\"] [data-react-fancy-modal]),\n:where(.dark [data-react-fancy-toast-item], [data-theme=\"dark\"] [data-react-fancy-toast-item]) {\n background-color: var(--fancy-surface-dark, #18181b); /* zinc-900 */\n border-color: var(--fancy-border-dark, #3f3f46); /* zinc-700 */\n color: var(--fancy-text-soft-dark, #d4d4d8); /* zinc-300 */\n}\n\n/* Overlay item hover states */\n:where(.dark [data-react-fancy-dropdown-item]:hover, [data-theme=\"dark\"] [data-react-fancy-dropdown-item]:hover),\n:where(.dark [data-react-fancy-menu-item]:hover, [data-theme=\"dark\"] [data-react-fancy-menu-item]:hover),\n:where(.dark [data-react-fancy-context-menu-item]:hover, [data-theme=\"dark\"] [data-react-fancy-context-menu-item]:hover) {\n background-color: var(--fancy-surface-muted-dark, #27272a); /* zinc-800 */\n}\n\n/* Overlay separators */\n:where(.dark [data-react-fancy-dropdown-separator], [data-theme=\"dark\"] [data-react-fancy-dropdown-separator]),\n:where(.dark [data-react-fancy-context-menu-separator], [data-theme=\"dark\"] [data-react-fancy-context-menu-separator]) {\n background-color: var(--fancy-border-dark, #3f3f46); /* zinc-700 */\n}\n\n/* ─── Media viewer — transparency checkerboard ───────────────────────────────\n Backdrop for <ImageViewer> / <MediaViewer> so transparent images read clearly.\n Light + dark variants; the dark rule is wrapped in :where() (zero specificity)\n so it ships out of the box without the consumer @sourcing react-fancy. */\n.fancy-checkerboard {\n background-color: #ffffff;\n background-image:\n linear-gradient(45deg, #e5e7eb 25%, transparent 25%),\n linear-gradient(-45deg, #e5e7eb 25%, transparent 25%),\n linear-gradient(45deg, transparent 75%, #e5e7eb 75%),\n linear-gradient(-45deg, transparent 75%, #e5e7eb 75%);\n background-size: 16px 16px;\n background-position: 0 0, 0 8px, 8px -8px, -8px 0;\n}\n\n:where(.dark, [data-theme=\"dark\"]) .fancy-checkerboard {\n background-color: #18181b; /* zinc-900 */\n background-image:\n linear-gradient(45deg, #27272a 25%, transparent 25%),\n linear-gradient(-45deg, #27272a 25%, transparent 25%),\n linear-gradient(45deg, transparent 75%, #27272a 75%),\n linear-gradient(-45deg, transparent 75%, #27272a 75%);\n}\n}\n\n\n@keyframes fancy-fade-in {\n from {\n opacity: 0;\n transform: translateY(4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n@keyframes fancy-slide-left {\n from {\n transform: translateX(100%);\n }\n to {\n transform: translateX(0);\n }\n}\n\n@keyframes fancy-slide-right {\n from {\n transform: translateX(-100%);\n }\n to {\n transform: translateX(0);\n }\n}\n\n@layer base {\n\n\n.fancy-fade-in {\n animation: fancy-fade-in 0.2s ease-out;\n}\n\n.fancy-slide-left {\n animation: fancy-slide-left 0.3s ease-out;\n}\n\n.fancy-slide-right {\n animation: fancy-slide-right 0.3s ease-out;\n}\n}\n\n\n/* Overlay / floating animations */\n\n@keyframes fancy-scale-in {\n from {\n opacity: 0;\n transform: scale(0.95);\n }\n to {\n opacity: 1;\n transform: scale(1);\n }\n}\n\n@keyframes fancy-slide-up {\n from {\n opacity: 0;\n transform: translateY(16px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n@keyframes fancy-fade-out {\n from {\n opacity: 1;\n }\n to {\n opacity: 0;\n }\n}\n\n@keyframes fancy-toast-in {\n from {\n opacity: 0;\n transform: translateX(100%);\n }\n to {\n opacity: 1;\n transform: translateX(0);\n }\n}\n\n@keyframes fancy-collapse-open {\n from {\n grid-template-rows: 0fr;\n }\n to {\n grid-template-rows: 1fr;\n }\n}\n\n@keyframes fancy-collapse-close {\n from {\n grid-template-rows: 1fr;\n }\n to {\n grid-template-rows: 0fr;\n }\n}\n\n@layer base {\n\n\n.fancy-scale-in {\n animation: fancy-scale-in 0.15s ease-out;\n}\n\n.fancy-slide-up {\n animation: fancy-slide-up 0.2s ease-out;\n}\n\n.fancy-fade-out {\n animation: fancy-fade-out 0.15s ease-in forwards;\n}\n\n.fancy-toast-in {\n animation: fancy-toast-in 0.3s ease-out;\n}\n\n.fancy-collapse-open {\n animation: fancy-collapse-open 0.2s ease-out forwards;\n}\n\n.fancy-collapse-close {\n animation: fancy-collapse-close 0.15s ease-in forwards;\n}\n}\n\n\n/* Mobile flyout slide animations */\n\n@keyframes fancy-slide-in-left {\n from {\n transform: translateX(-100%);\n }\n to {\n transform: translateX(0);\n }\n}\n\n@keyframes fancy-slide-out-left {\n from {\n transform: translateX(0);\n }\n to {\n transform: translateX(-100%);\n }\n}\n\n@keyframes fancy-slide-in-right {\n from {\n transform: translateX(100%);\n }\n to {\n transform: translateX(0);\n }\n}\n\n/* Avatar glow — a looping halo ring (box-shadow only, no reflow). Color comes\n from the --fancy-glow custom property set on the element. */\n@keyframes fancy-avatar-glow {\n 0% {\n box-shadow: 0 0 0 0 color-mix(in srgb, var(--fancy-glow, #a855f7) 55%, transparent);\n opacity: 0.9;\n }\n 70% {\n box-shadow: 0 0 0 6px color-mix(in srgb, var(--fancy-glow, #a855f7) 0%, transparent);\n opacity: 0.5;\n }\n 100% {\n box-shadow: 0 0 0 0 color-mix(in srgb, var(--fancy-glow, #a855f7) 0%, transparent);\n opacity: 0.9;\n }\n}\n\n@layer base {\n\n\n.fancy-avatar-glow {\n position: absolute;\n inset: 0;\n border-radius: 9999px;\n pointer-events: none;\n box-shadow:\n 0 0 0 2px color-mix(in srgb, var(--fancy-glow, #a855f7) 70%, transparent),\n 0 0 10px 0 color-mix(in srgb, var(--fancy-glow, #a855f7) 45%, transparent);\n animation: fancy-avatar-glow 1.6s ease-out infinite;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .fancy-avatar-glow {\n animation: none;\n }\n}\n}\n\n\n@keyframes fancy-slide-out-right {\n from {\n transform: translateX(0);\n }\n to {\n transform: translateX(100%);\n }\n}\n\n@layer base {\n\n\n.fancy-slide-in-left {\n animation: fancy-slide-in-left 0.25s ease-out;\n}\n\n.fancy-slide-out-left {\n animation: fancy-slide-out-left 0.2s ease-in forwards;\n}\n\n.fancy-slide-in-right {\n animation: fancy-slide-in-right 0.25s ease-out;\n}\n\n.fancy-slide-out-right {\n animation: fancy-slide-out-right 0.2s ease-in forwards;\n}\n}\n\n\n/* Vertical drawer edges. The horizontal pair above predates Drawer (it was\n built for MobileMenu.Flyout, which only ever opened left/right); these\n complete the set so `side=\"top\"`/`side=\"bottom\"` animate rather than pop. */\n\n@keyframes fancy-slide-in-top {\n from {\n transform: translateY(-100%);\n }\n to {\n transform: translateY(0);\n }\n}\n\n@keyframes fancy-slide-out-top {\n from {\n transform: translateY(0);\n }\n to {\n transform: translateY(-100%);\n }\n}\n\n@keyframes fancy-slide-in-bottom {\n from {\n transform: translateY(100%);\n }\n to {\n transform: translateY(0);\n }\n}\n\n@keyframes fancy-slide-out-bottom {\n from {\n transform: translateY(0);\n }\n to {\n transform: translateY(100%);\n }\n}\n\n@layer base {\n\n\n.fancy-slide-in-top {\n animation: fancy-slide-in-top 0.25s ease-out;\n}\n\n.fancy-slide-out-top {\n animation: fancy-slide-out-top 0.2s ease-in forwards;\n}\n\n.fancy-slide-in-bottom {\n animation: fancy-slide-in-bottom 0.25s ease-out;\n}\n\n.fancy-slide-out-bottom {\n animation: fancy-slide-out-bottom 0.2s ease-in forwards;\n}\n\n/* Reduced motion collapses the travel to a ~0 duration. Since 5.51.0\n `useAnimation` also unmounts when no animation runs at all, so this is no\n longer what keeps a closed drawer from staying in the DOM — but a ~0 duration\n keeps the exit on the same path as every other close. */\n@media (prefers-reduced-motion: reduce) {\n .fancy-slide-in-left,\n .fancy-slide-out-left,\n .fancy-slide-in-right,\n .fancy-slide-out-right,\n .fancy-slide-in-top,\n .fancy-slide-out-top,\n .fancy-slide-in-bottom,\n .fancy-slide-out-bottom {\n animation-duration: 0.01ms;\n }\n}\n\n/* ─── Marquee ────────────────────────────────────────────────────────────────\n Edge-to-edge auto-scrolling strip. The track holds two identical copies of\n the content; translateX(-50%) over one loop shifts exactly one copy, so the\n wrap is seamless. Duration/gap/fade arrive as custom properties set by the\n component. No animation under prefers-reduced-motion.\n ──────────────────────────────────────────────────────────────────────────── */\n\n[data-react-fancy-marquee] {\n overflow: hidden;\n width: 100%;\n}\n\n.fancy-marquee--fade {\n -webkit-mask-image: linear-gradient(\n to right,\n transparent,\n black var(--fancy-marquee-fade, 48px),\n black calc(100% - var(--fancy-marquee-fade, 48px)),\n transparent\n );\n mask-image: linear-gradient(\n to right,\n transparent,\n black var(--fancy-marquee-fade, 48px),\n black calc(100% - var(--fancy-marquee-fade, 48px)),\n transparent\n );\n}\n\n.fancy-marquee__track {\n display: flex;\n width: max-content;\n animation: fancy-marquee var(--fancy-marquee-duration, 40s) linear infinite;\n will-change: transform;\n}\n\n[data-react-fancy-marquee][data-direction=\"right\"] .fancy-marquee__track {\n animation-direction: reverse;\n}\n\n.fancy-marquee--pause-hover:hover .fancy-marquee__track,\n[data-react-fancy-marquee][data-paused] .fancy-marquee__track {\n animation-play-state: paused;\n}\n\n.fancy-marquee__group {\n display: flex;\n flex: none;\n}\n\n.fancy-marquee__unit {\n display: flex;\n flex: none;\n align-items: center;\n gap: var(--fancy-marquee-gap, 40px);\n padding-inline-end: var(--fancy-marquee-gap, 40px);\n white-space: nowrap;\n}\n\n.fancy-marquee__item,\n.fancy-marquee__separator {\n flex: none;\n}\n}\n\n\n@keyframes fancy-marquee {\n from {\n transform: translateX(0);\n }\n to {\n transform: translateX(-50%);\n }\n}\n\n@layer base {\n\n\n@media (prefers-reduced-motion: reduce) {\n .fancy-marquee__track {\n animation: none;\n }\n}\n}\n\n"],"mappings":";AA4BA,OAAO,EAgCL,aAAa,EAAE,IAAI,mBAAmB,EACtC,sBAAsB,EAAE,IAAI,cAAc,EAC1C,mBAAmB,EAAE,IAAI,mBAAmB,EAE5C,kBAAkB,EAAE,IAAI,kBAAkB,EAC1C,mBAAmB,EAAE,IAAI,mBAAmB,EAC5C,mBAAmB,EAAE,IAAI,mBAAmB,EAC5C,mBAAmB,EAAE,IAAI,mBAAmB,EAC5C,mBAAmB,EAAE,IAAI,mBAAmB,EAC5C,mBAAmB,EAAE,IAAI,mBAAmB,EAC5C,mBAAmB,EAAE,IAAI,mBAAmB,EAC5C,mBAAmB,EAAE,IAAI,mBAAmB,EAC5C,mBAAmB,EAAE,IAAI,mBAAmB,EAC5C,mBAAmB,EAAE,IAAI,mBAAmB,EAC5C,mBAAmB,EAAE,IAAI,mBAAmB,EAG5C,oBAAoB,EAAE,IAAI,gBAAgB,EAC1C,qBAAqB,EAAE,IAAI,iBAAiB,EAC5C,qBAAqB,EAAE,IAAI,iBAAiB,EAC5C,qBAAqB,EAAE,IAAI,iBAAiB,EAC5C,qBAAqB,EAAE,IAAI,iBAAiB,EAC5C,qBAAqB,EAAE,IAAI,iBAAiB,EAC5C,qBAAqB,EAAE,IAAI,iBAAiB,EAC5C,qBAAqB,EAAE,IAAI,iBAAiB,EAC5C,qBAAqB,EAAE,IAAI,iBAAiB,EAC5C,qBAAqB,EAAE,IAAI,iBAAiB,EAC5C,qBAAqB,EAAE,IAAI,iBAAiB;AAY9C;AACE,SAAO,CAAC;AACN,aAAS;AACT,YAAQ,EAAE;AACV,sBAAkB;AAClB,WAAO;AACP,cAAU;AACZ;AACF;AAEA;AAUA,GAAC,0BAA0B,CAAC;AAAkB,WAAO;AAAM,YAAQ;AAAM;AACzE,GAAC,0BAA0B,CAAC;AAAkB,WAAO;AAAM,YAAQ;AAAM;AACzE,GAAC,0BAA0B,CAAC;AAAkB,WAAO;AAAM,YAAQ;AAAM;AAYzE,SAAO,CAAC,MAAM,CAAC;AACb,0BAAsB,IAAI;AAC1B,2BAAuB,IAAI;AAC3B,2BAAuB,IAAI;AAC3B,2BAAuB,IAAI;AAC3B,2BAAuB,IAAI;AAC3B,2BAAuB,IAAI;AAC3B,2BAAuB,IAAI;AAC3B,2BAAuB,IAAI;AAC3B,2BAAuB,IAAI;AAC3B,2BAAuB,IAAI;AAC3B,2BAAuB,IAAI;AAC7B;AAUA,SACE,CAvBM,KAuBA,CAAC,gCACP,CAAC,iBAAmB,CAAC,gCACrB,CAzBM,IAyBD,CAAC,gCACN,CAAC,gBAAkB,CAAC;AAEpB,qBAAiB,IAAI;AACrB,qBAAiB,IAAI;AACrB,2BAAuB,IAAI;AAC3B,2BAAuB,IAAI;AAC3B,uBAAmB,IAAI;AACvB,sBAAkB,IAAI;AACtB,kBAAc,IAAI;AAClB,iBAAa,IAAI;AACjB,kBAAc,IAAI;AAClB,iBAAa,IAAI;AACjB,mBAAe,IAAI;AACnB,mBAAe,IAAI;AACnB,eAAW,IAAI;AACf,cAAU,IAAI;AACd,oBAAgB,IAAI;AACpB,yBAAqB,IAAI;AACzB,sBAAkB,IAAI;AACtB,mBAAe,IAAI;AACrB;AAyBA,GAAC,wBAAwB;AAAA,EACzB,CAAC;AAAA,EACD,CAAC;AAAA,EACD,CAAC;AAAA,EACD,CAAC,+BAA+B;AAAA,EAChC,CAAC;AAAA,EACD,CAAC,4BAA4B;AAAA,EAC7B,CAAC,8BAA8B;AAAA,EAC/B,CAAC,+BAA+B;AAC9B;AAAA,MAAY,aAAa,MAAM,IAAI;AAAA,MAAE,WAAW,MAAM,IAAI;AAAA,MACxD,iBAAiB,MAAM;AAC3B;AAqBA,SAAO,CAvGC,KAuGK,CAAC,wBAAwB,OAAO,CAAC,iBAAmB,CAAC,wBAAwB;AAAA,EAC1F,OAAO,CAxGC,KAwGK,CAAC,4BAA4B,CAAC,iBAAmB,CAAC;AAAA,EAC/D,OAAO,CAzGC,KAyGK,CAAC,0BAA0B,CAAC,iBAAmB,CAAC;AAAA,EAC7D,OAAO,CA1GC,KA0GK,CAAC,+BAA+B,CAAC,iBAAmB,CAAC;AAAA,EAClE,OAAO,CA3GC,KA2GK,CAAC,+BAA+B,OAAO,CAAC,iBAAmB,CAAC,+BAA+B;AAAA,EACxG,OAAO,CA5GC,KA4GK,CAAC,2BAA2B,CAAC,iBAAmB,CAAC;AAAA,EAC9D,OAAO,CA7GC,KA6GK,CAAC,4BAA4B,OAAO,CAAC,iBAAmB,CAAC,4BAA4B;AAAA,EAClG,OAAO,CA9GC,KA8GK,CAAC,8BAA8B,OAAO,CAAC,iBAAmB,CAAC,8BAA8B;AACpG,sBAAkB,IAAI,kBAAkB,EAAE;AAC1C,kBAAc,IAAI,0BAA0B,EAAE;AAChD;AAGA,SAAO,CApHC,KAoHK,CAAC,wBAAwB,KAAK,QAAQ,CAAC,iBAAmB,CAAC,wBAAwB,KAAK;AAAA,EACrG,OAAO,CArHC,KAqHK,CAAC,0BAA0B,QAAQ,CAAC,iBAAmB,CAAC,0BAA0B;AAAA,EAC/F,OAAO,CAtHC,KAsHK,CAAC,wBAAwB,QAAQ,CAAC,iBAAmB,CAAC,wBAAwB;AAAA,EAC3F,OAAO,CAvHC,KAuHK,CAAC,6BAA6B,QAAQ,CAAC,iBAAmB,CAAC,6BAA6B;AAAA,EACrG,OAAO,CAxHC,KAwHK,CAAC,+BAA+B,KAAK,QAAQ,CAAC,iBAAmB,CAAC,+BAA+B,KAAK;AAAA,EACnH,OAAO,CAzHC,KAyHK,CAAC,4BAA4B,KAAK,QAAQ,CAAC,iBAAmB,CAAC,4BAA4B,KAAK;AAAA,EAC7G,OAAO,CA1HC,KA0HK,CAAC,8BAA8B,KAAK,QAAQ,CAAC,iBAAmB,CAAC,8BAA8B,KAAK;AAC/G,kBAAc,IAAI,kBAAkB,EAAE;AAEtC,gBAAY,EAAE,EAAE,EAAE,IAAI,UAAU,GAAG,IAAI,EAAE,IAAI,kBAAkB,EAAE,SAAS,GAAG,EAAE;AACjF;AAGA,SAAO,CAjIC,KAiIK,CAAC,wBAAwB,EAAE,KAAK,cAAc,CAAC,iBAAmB,CAAC,wBAAwB,EAAE,KAAK;AAC7G,WAAO,IAAI,iBAAiB,EAAE;AAChC;AAGA,SAAO,CAtIC,KAsIK,CAAC,2BAA2B,OAAO,CAAC,iBAAmB,CAAC,2BAA2B;AAAA,EAChG,OAAO,CAvIC,KAuIK,CAAC,8BAA8B,OAAO,CAAC,iBAAmB,CAAC,8BAA8B;AACpG,sBAAkB,IAAI,kBAAkB,EAAE;AAC1C,kBAAc,IAAI,0BAA0B,EAAE;AAChD;AAGA,SAAO,CA7IC,KA6IK,CAAC,yBAAyB,MAAM,CAAC,qBAAuB,CAAC,iBAAmB,CAAC,yBAAyB,MAAM,CAAC;AACxH,sBAAkB,IAAI,mBAAmB,EAAE;AAC7C;AAGA,SAAO,CAlJC,KAkJK,CAAC,gCAAgC,EAAE,IAAI,cAAc,CAAC,iBAAmB,CAAC,gCAAgC,EAAE,IAAI;AAAA,EAC7H,OAAO,CAnJC,KAmJK,CAAC,gCAAgC,EAAE,IAAI,aAAa,CAAC,iBAAmB,CAAC,gCAAgC,EAAE,IAAI;AAC1H,kBAAc,IAAI,0BAA0B,EAAE;AAC9C,sBAAkB,IAAI,0BAA0B,EAAE;AACpD;AAGA,SAAO,CAzJC,KAyJK,CAAC,6BAA6B,qCAAqC,CAAC,iBAAmB,CAAC,6BAA6B;AAChI,YAAQ,OAAO;AACjB;AAQA,SAAO,CAnKC,KAmKK,CAAC,4BAA4B,CAAC,iBAAmB,CAAC;AAAA,EAC/D,OAAO,CApKC,KAoKK,CAAC,wBAAwB,CAAC,iBAAmB,CAAC;AAAA,EAC3D,OAAO,CArKC,KAqKK,CAAC,2BAA2B,CAAC,iBAAmB,CAAC;AAAA,EAC9D,OAAO,CAtKC,KAsKK,CAAC,gCAAgC,CAAC,iBAAmB,CAAC;AAAA,EACnE,OAAO,CAvKC,KAuKK,CAAC,4CAA4C,CAAC,iBAAmB,CAAC;AAAA,EAC/E,OAAO,CAxKC,KAwKK,CAAC,sCAAsC,CAAC,iBAAmB,CAAC;AAAA,EACzE,OAAO,CAzKC,KAyKK,CAAC,yBAAyB,CAAC,iBAAmB,CAAC;AAAA,EAC5D,OAAO,CA1KC,KA0KK,CAAC,8BAA8B,CAAC,iBAAmB,CAAC;AAC/D,sBAAkB,IAAI,oBAAoB,EAAE;AAC5C,kBAAc,IAAI,mBAAmB,EAAE;AACvC,WAAO,IAAI,sBAAsB,EAAE;AACrC;AAGA,SAAO,CAjLC,KAiLK,CAAC,+BAA+B,QAAQ,CAAC,iBAAmB,CAAC,+BAA+B;AAAA,EACzG,OAAO,CAlLC,KAkLK,CAAC,2BAA2B,QAAQ,CAAC,iBAAmB,CAAC,2BAA2B;AAAA,EACjG,OAAO,CAnLC,KAmLK,CAAC,mCAAmC,QAAQ,CAAC,iBAAmB,CAAC,mCAAmC;AAC/G,sBAAkB,IAAI,0BAA0B,EAAE;AACpD;AAGA,SAAO,CAxLC,KAwLK,CAAC,sCAAsC,CAAC,iBAAmB,CAAC;AAAA,EACzE,OAAO,CAzLC,KAyLK,CAAC,0CAA0C,CAAC,iBAAmB,CAAC;AAC3E,sBAAkB,IAAI,mBAAmB,EAAE;AAC7C;AAMA,GAAC;AACC,sBAAkB;AAClB;AAAA,MACE;AAAA,QAAgB,KAAK;AAAA,QAAE,QAAQ,GAAG;AAAA,QAAE,YAAY,IAAI;AAAA,MACpD;AAAA,QAAgB,MAAM;AAAA,QAAE,QAAQ,GAAG;AAAA,QAAE,YAAY,IAAI;AAAA,MACrD;AAAA,QAAgB,KAAK;AAAA,QAAE,YAAY,GAAG;AAAA,QAAE,QAAQ,IAAI;AAAA,MACpD;AAAA,QAAgB,MAAM;AAAA,QAAE,YAAY,GAAG;AAAA,QAAE,QAAQ;AACnD,qBAAiB,KAAK;AACtB;AAAA,MAAqB,EAAE,CAAC;AAAA,MAAE,EAAE,GAAG;AAAA,MAAE,IAAI,IAAI;AAAA,MAAE,KAAK;AAClD;AAEA,SAAO,CA5MC,MA4MM,CAAC,kBAAoB,CAXlC;AAYC,sBAAkB;AAClB;AAAA,MACE;AAAA,QAAgB,KAAK;AAAA,QAAE,QAAQ,GAAG;AAAA,QAAE,YAAY,IAAI;AAAA,MACpD;AAAA,QAAgB,MAAM;AAAA,QAAE,QAAQ,GAAG;AAAA,QAAE,YAAY,IAAI;AAAA,MACrD;AAAA,QAAgB,KAAK;AAAA,QAAE,YAAY,GAAG;AAAA,QAAE,QAAQ,IAAI;AAAA,MACpD;AAAA,QAAgB,MAAM;AAAA,QAAE,YAAY,GAAG;AAAA,QAAE,QAAQ;AACrD;AACA;AAGA,WAAW;AACT;AACE,aAAS;AACT,eAAW,WAAW;AACxB;AACA;AACE,aAAS;AACT,eAAW,WAAW;AACxB;AACF;AAEA,WAAW;AACT;AACE,eAAW,WAAW;AACxB;AACA;AACE,eAAW,WAAW;AACxB;AACF;AAEA,WAAW;AACT;AACE,eAAW,WAAW;AACxB;AACA;AACE,eAAW,WAAW;AACxB;AACF;AAEA;AAGA,GAhCW;AAiCT,eAAW,cAAc,KAAK;AAChC;AAEA,GAzBW;AA0BT,eAAW,iBAAiB,KAAK;AACnC;AAEA,GApBW;AAqBT,eAAW,kBAAkB,KAAK;AACpC;AACA;AAKA,WAAW;AACT;AACE,aAAS;AACT,eAAW,MAAM;AACnB;AACA;AACE,aAAS;AACT,eAAW,MAAM;AACnB;AACF;AAEA,WAAW;AACT;AACE,aAAS;AACT,eAAW,WAAW;AACxB;AACA;AACE,aAAS;AACT,eAAW,WAAW;AACxB;AACF;AAEA,WAAW;AACT;AACE,aAAS;AACX;AACA;AACE,aAAS;AACX;AACF;AAEA,WAAW;AACT;AACE,aAAS;AACT,eAAW,WAAW;AACxB;AACA;AACE,aAAS;AACT,eAAW,WAAW;AACxB;AACF;AAEA,WAAW;AACT;AACE,wBAAoB;AACtB;AACA;AACE,wBAAoB;AACtB;AACF;AAEA,WAAW;AACT;AACE,wBAAoB;AACtB;AACA;AACE,wBAAoB;AACtB;AACF;AAEA;AAGA,GA/DW;AAgET,eAAW,eAAe,MAAM;AAClC;AAEA,GAxDW;AAyDT,eAAW,eAAe,KAAK;AACjC;AAEA,GAjDW;AAkDT,eAAW,eAAe,MAAM,QAAQ;AAC1C;AAEA,GA5CW;AA6CT,eAAW,eAAe,KAAK;AACjC;AAEA,GArCW;AAsCT,eAAW,oBAAoB,KAAK,SAAS;AAC/C;AAEA,GAhCW;AAiCT,eAAW,qBAAqB,MAAM,QAAQ;AAChD;AACA;AAKA,WAAW;AACT;AACE,eAAW,WAAW;AACxB;AACA;AACE,eAAW,WAAW;AACxB;AACF;AAEA,WAAW;AACT;AACE,eAAW,WAAW;AACxB;AACA;AACE,eAAW,WAAW;AACxB;AACF;AAEA,WAAW;AACT;AACE,eAAW,WAAW;AACxB;AACA;AACE,eAAW,WAAW;AACxB;AACF;AAIA,WAAW;AACT;AACE,gBAAY,EAAE,EAAE,EAAE,EAAE,UAAU,GAAG,IAAI,EAAE,IAAI,YAAY,EAAE,SAAS,GAAG,EAAE;AACvE,aAAS;AACX;AACA;AACE,gBAAY,EAAE,EAAE,EAAE,IAAI,UAAU,GAAG,IAAI,EAAE,IAAI,YAAY,EAAE,SAAS,EAAE,EAAE;AACxE,aAAS;AACX;AACA;AACE,gBAAY,EAAE,EAAE,EAAE,EAAE,UAAU,GAAG,IAAI,EAAE,IAAI,YAAY,EAAE,SAAS,EAAE,EAAE;AACtE,aAAS;AACX;AACF;AAEA;AAGA,GAlBW;AAmBT,cAAU;AACV,WAAO;AACP,mBAAe;AACf,oBAAgB;AAChB,gBACE,EAAE,EAAE,EAAE,IAAI,UAAU,GAAG,IAAI,EAAE,IAAI,YAAY,EAAE,SAAS,GAAG,EAAE,YAAY,EACzE,EAAE,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,EAAE,IAAI,YAAY,EAAE,SAAS,GAAG,EAAE;AAChE,eAAW,kBAAkB,KAAK,SAAS;AAC7C;AAEA,UAAO,wBAAyB;AAC9B,KA9BS;AA+BP,iBAAW;AACb;AACF;AACA;AAGA,WAAW;AACT;AACE,eAAW,WAAW;AACxB;AACA;AACE,eAAW,WAAW;AACxB;AACF;AAEA;AAGA,GA9EW;AA+ET,eAAW,oBAAoB,MAAM;AACvC;AAEA,GAzEW;AA0ET,eAAW,qBAAqB,KAAK,QAAQ;AAC/C;AAEA,GApEW;AAqET,eAAW,qBAAqB,MAAM;AACxC;AAEA,GAxBW;AAyBT,eAAW,sBAAsB,KAAK,QAAQ;AAChD;AACA;AAOA,WAAW;AACT;AACE,eAAW,WAAW;AACxB;AACA;AACE,eAAW,WAAW;AACxB;AACF;AAEA,WAAW;AACT;AACE,eAAW,WAAW;AACxB;AACA;AACE,eAAW,WAAW;AACxB;AACF;AAEA,WAAW;AACT;AACE,eAAW,WAAW;AACxB;AACA;AACE,eAAW,WAAW;AACxB;AACF;AAEA,WAAW;AACT;AACE,eAAW,WAAW;AACxB;AACA;AACE,eAAW,WAAW;AACxB;AACF;AAEA;AAGA,GAvCW;AAwCT,eAAW,mBAAmB,MAAM;AACtC;AAEA,GAlCW;AAmCT,eAAW,oBAAoB,KAAK,QAAQ;AAC9C;AAEA,GA7BW;AA8BT,eAAW,sBAAsB,MAAM;AACzC;AAEA,GAxBW;AAyBT,eAAW,uBAAuB,KAAK,QAAQ;AACjD;AAMA,UAAO,wBAAyB;AAC9B,KAhKS;AAAA,IAiKT,CAxJS;AAAA,IAyJT,CAhJS;AAAA,IAiJT,CAjGS;AAAA,IAkGT,CAhES;AAAA,IAiET,CAxDS;AAAA,IAyDT,CAhDS;AAAA,IAiDT,CAxCS;AAyCP,0BAAoB;AACtB;AACF;AASA,GAAC;AACC,cAAU;AACV,WAAO;AACT;AAEA,GAAC;AACC;AAAA,MAAoB;AAAA,QAClB,GAAG,KAAK;AAAA,QACR,WAAW;AAAA,QACX,MAAM,IAAI,oBAAoB,EAAE,KAAK;AAAA,QACrC,MAAM,KAAK,KAAK,EAAE,IAAI,oBAAoB,EAAE,MAAM;AAAA,QAClD;AAEF;AAAA,MAAY;AAAA,QACV,GAAG,KAAK;AAAA,QACR,WAAW;AAAA,QACX,MAAM,IAAI,oBAAoB,EAAE,KAAK;AAAA,QACrC,MAAM,KAAK,KAAK,EAAE,IAAI,oBAAoB,EAAE,MAAM;AAAA,QAClD;AAEJ;AAEA,GAAC;AACC,aAAS;AACT,WAAO;AACP,eAAW,cAAc,IAAI,wBAAwB,EAAE,KAAK,OAAO;AACnE,iBAAa;AACf;AAEA,GAAC,yBAAyB,CAAC,sBAAwB,CAPlD;AAQC,yBAAqB;AACvB;AAEA,GAAC,0BAA0B,OAAO,CAXjC;AAAA,EAYD,CAAC,yBAAyB,CAAC,aAAa,CAZvC;AAaC,0BAAsB;AACxB;AAEA,GAAC;AACC,aAAS;AACT,UAAM;AACR;AAEA,GAAC;AACC,aAAS;AACT,UAAM;AACN,iBAAa;AACb,SAAK,IAAI,mBAAmB,EAAE;AAC9B,wBAAoB,IAAI,mBAAmB,EAAE;AAC7C,iBAAa;AACf;AAEA,GAAC;AAAA,EACD,CAAC;AACC,UAAM;AACR;AACA;AAGA,WAlCa;AAmCX;AACE,eAAW,WAAW;AACxB;AACA;AACE,eAAW,WAAW;AACxB;AACF;AAEA;AAGA,UAAO,wBAAyB;AAC9B,KAlDD;AAmDG,iBAAW;AACb;AACF;AACA;","names":[]}
package/dist/tree-nav.cjs CHANGED
@@ -1,18 +1,18 @@
1
1
  'use strict';
2
2
 
3
- var chunkL5AI4MGV_cjs = require('./chunk-L5AI4MGV.cjs');
4
- require('./chunk-MF2DCMOA.cjs');
3
+ var chunk25HRO7HQ_cjs = require('./chunk-25HRO7HQ.cjs');
4
+ require('./chunk-2TLAZ5MX.cjs');
5
5
  require('./chunk-ZU5Q77OT.cjs');
6
6
 
7
7
 
8
8
 
9
9
  Object.defineProperty(exports, "TreeNav", {
10
10
  enumerable: true,
11
- get: function () { return chunkL5AI4MGV_cjs.TreeNav; }
11
+ get: function () { return chunk25HRO7HQ_cjs.TreeNav; }
12
12
  });
13
13
  Object.defineProperty(exports, "useTreeNav", {
14
14
  enumerable: true,
15
- get: function () { return chunkL5AI4MGV_cjs.useTreeNav; }
15
+ get: function () { return chunk25HRO7HQ_cjs.useTreeNav; }
16
16
  });
17
17
  //# sourceMappingURL=tree-nav.cjs.map
18
18
  //# sourceMappingURL=tree-nav.cjs.map
package/dist/tree-nav.js CHANGED
@@ -1,5 +1,5 @@
1
- export { TreeNav, useTreeNav } from './chunk-LYG2NLSV.js';
2
- import './chunk-ARNFT23K.js';
1
+ export { TreeNav, useTreeNav } from './chunk-ZPBKRJMH.js';
2
+ import './chunk-5IXOQGPX.js';
3
3
  import './chunk-MMW2JGKZ.js';
4
4
  //# sourceMappingURL=tree-nav.js.map
5
5
  //# sourceMappingURL=tree-nav.js.map
@@ -1,13 +1,13 @@
1
1
  'use strict';
2
2
 
3
- var chunkMF2DCMOA_cjs = require('./chunk-MF2DCMOA.cjs');
3
+ var chunk2TLAZ5MX_cjs = require('./chunk-2TLAZ5MX.cjs');
4
4
  require('./chunk-ZU5Q77OT.cjs');
5
5
 
6
6
 
7
7
 
8
8
  Object.defineProperty(exports, "VirtualList", {
9
9
  enumerable: true,
10
- get: function () { return chunkMF2DCMOA_cjs.VirtualList; }
10
+ get: function () { return chunk2TLAZ5MX_cjs.VirtualList; }
11
11
  });
12
12
  //# sourceMappingURL=virtual-list.cjs.map
13
13
  //# sourceMappingURL=virtual-list.cjs.map
@@ -1,4 +1,4 @@
1
- export { VirtualList } from './chunk-ARNFT23K.js';
1
+ export { VirtualList } from './chunk-5IXOQGPX.js';
2
2
  import './chunk-MMW2JGKZ.js';
3
3
  //# sourceMappingURL=virtual-list.js.map
4
4
  //# sourceMappingURL=virtual-list.js.map
package/docs/CodeBlock.md CHANGED
@@ -76,15 +76,23 @@ The button copies **exactly the `code` prop**: no highlight markup, no line numb
76
76
 
77
77
  ## Theming
78
78
 
79
- The same palette as `CodeView` (`LIGHT_COLORS` / `DARK_COLORS` from `fancy-file-commons`), so the two sit together. Dark is chosen when `<html>` has the `dark` class or the OS prefers dark. On the server, and during hydration, the light palette is used, then the real theme.
79
+ The same palette as `CodeView` (`LIGHT_COLORS` / `DARK_COLORS` from `fancy-file-commons`), so the two sit together. **The markup carries both palettes and CSS picks one**, so server-rendered HTML is right in either theme and a dark site does not flash light on hydration:
80
+
81
+ - the root declares `--fc-<slot>-l` and `--fc-<slot>-d` (for example `--fc-keyword-l`, `--fc-keyword-d`), and everything coloured reads `var(--fc-<slot>, var(--fc-<slot>-l))`;
82
+ - the dark value applies under **your Tailwind `dark` variant** (the root's `dark:[--fc-<slot>:...]` classes, compiled from the `@source`d dist: class strategy, `dark-variant.css`, or Tailwind's default media query), **or** under a `.dark` / `[data-theme="dark"]` ancestor through `styles.css`, for a host that does not scan the package;
83
+ - with neither, it renders the light palette.
84
+
85
+ To change a colour, override its variable on the block: `style={{ "--fc-keyword-l": "#7c3aed", "--fc-keyword-d": "#a78bfa" }}`.
86
+
87
+ PHP is read as a **snippet**: code with no `<?php` open tag (`use Foo\Bar;`) is highlighted as PHP, and a `?>` switches to HTML. Code that opens with `<?php` highlights exactly as a file does.
80
88
 
81
89
  ## Security
82
90
 
83
- Code is escaped before it reaches `dangerouslySetInnerHTML`: `highlightCode` escapes `&`, `<` and `>` in every slice it emits, highlighted or plain, and token colours pass an allow-list. `<script>` in `code` renders as the text `<script>`.
91
+ Code is escaped before it reaches `dangerouslySetInnerHTML`: `highlightCodeWithVariables` escapes `&`, `<` and `>` in every slice it emits, highlighted or plain, and writes no colour value into the token markup; the palette values on the root pass `fancy-file-commons`' colour allow-list. `<script>` in `code` renders as the text `<script>`.
84
92
 
85
93
  ## Server rendering
86
94
 
87
- No `window`, `document` or `navigator` access during render. Tested with `renderToString` with all three undefined.
95
+ No `window`, `document` or `navigator` access during render. Tested with `renderToString` with all three undefined. The server output is identical to the client's in both themes.
88
96
 
89
97
  ## Agent handles
90
98
 
package/docs/CodeView.md CHANGED
@@ -75,6 +75,10 @@ Read-only (omit `onChange` or pass `readOnly`):
75
75
 
76
76
  ## Theme
77
77
 
78
- The view resolves light/dark itself (the highlight colors are inlined). It
79
- follows both the `.dark` class and the `prefers-color-scheme` media query, so it
80
- matches whatever drives your Tailwind `dark:` variants.
78
+ The view carries both palettes as CSS variables (`--fc-<slot>-l` /
79
+ `--fc-<slot>-d`) and CSS picks one, so server-rendered HTML is right in either
80
+ theme with no flash. The dark palette applies under your Tailwind `dark` variant
81
+ (its `dark:` classes compile from the `@source`d dist, so it matches whatever
82
+ drives your other `dark:` utilities) or under a `.dark` / `[data-theme="dark"]`
83
+ ancestor via `styles.css`. See `CodeBlock`'s Theming section for overrides. PHP
84
+ without a `<?php` open tag is highlighted as PHP (snippet mode).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@particle-academy/react-fancy",
3
- "version": "6.5.0",
3
+ "version": "6.6.1",
4
4
  "description": "React UI component library for Human+ UX — controlled, agent-bridgeable primitives",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1054,7 +1054,7 @@
1054
1054
  "tailwindcss": "^4.0.0"
1055
1055
  },
1056
1056
  "dependencies": {
1057
- "@particle-academy/fancy-file-commons": ">=0.4 <2.0",
1057
+ "@particle-academy/fancy-file-commons": ">=0.5 <2.0",
1058
1058
  "lucide-react": "^0.511.0",
1059
1059
  "marked": "^15.0.0",
1060
1060
  "tailwind-merge": "^3.0.0"
@@ -1,29 +0,0 @@
1
- import { useSyncExternalStore } from 'react';
2
-
3
- // src/components/CodeView/use-is-dark.ts
4
- function darkQuery() {
5
- return typeof window.matchMedia === "function" ? window.matchMedia("(prefers-color-scheme: dark)") : null;
6
- }
7
- function subscribeDark(callback) {
8
- const mq = darkQuery();
9
- mq?.addEventListener?.("change", callback);
10
- const observer = new MutationObserver(callback);
11
- observer.observe(document.documentElement, {
12
- attributes: true,
13
- attributeFilter: ["class"]
14
- });
15
- return () => {
16
- mq?.removeEventListener?.("change", callback);
17
- observer.disconnect();
18
- };
19
- }
20
- function darkSnapshot() {
21
- return document.documentElement.classList.contains("dark") || (darkQuery()?.matches ?? false);
22
- }
23
- function useIsDark() {
24
- return useSyncExternalStore(subscribeDark, darkSnapshot, () => false);
25
- }
26
-
27
- export { useIsDark };
28
- //# sourceMappingURL=chunk-6BB32POL.js.map
29
- //# sourceMappingURL=chunk-6BB32POL.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/CodeView/use-is-dark.ts"],"names":[],"mappings":";;;AAWA,SAAS,SAAA,GAAmC;AAC1C,EAAA,OAAO,OAAO,MAAA,CAAO,UAAA,KAAe,aAChC,MAAA,CAAO,UAAA,CAAW,8BAA8B,CAAA,GAChD,IAAA;AACN;AAEA,SAAS,cAAc,QAAA,EAAkC;AACvD,EAAA,MAAM,KAAK,SAAA,EAAU;AACrB,EAAA,EAAA,EAAI,gBAAA,GAAmB,UAAU,QAAQ,CAAA;AACzC,EAAA,MAAM,QAAA,GAAW,IAAI,gBAAA,CAAiB,QAAQ,CAAA;AAC9C,EAAA,QAAA,CAAS,OAAA,CAAQ,SAAS,eAAA,EAAiB;AAAA,IACzC,UAAA,EAAY,IAAA;AAAA,IACZ,eAAA,EAAiB,CAAC,OAAO;AAAA,GAC1B,CAAA;AACD,EAAA,OAAO,MAAM;AACX,IAAA,EAAA,EAAI,mBAAA,GAAsB,UAAU,QAAQ,CAAA;AAC5C,IAAA,QAAA,CAAS,UAAA,EAAW;AAAA,EACtB,CAAA;AACF;AAEA,SAAS,YAAA,GAAwB;AAC/B,EAAA,OAAO,QAAA,CAAS,gBAAgB,SAAA,CAAU,QAAA,CAAS,MAAM,CAAA,KAAM,SAAA,IAAa,OAAA,IAAW,KAAA,CAAA;AACzF;AAGO,SAAS,SAAA,GAAqB;AACnC,EAAA,OAAO,oBAAA,CAAqB,aAAA,EAAe,YAAA,EAAc,MAAM,KAAK,CAAA;AACtE","file":"chunk-6BB32POL.js","sourcesContent":["import { useSyncExternalStore } from \"react\";\n\n// ── Dark-mode detection ────────────────────────────────────────────────────\n// The highlight palette is baked in as inline colors, so we resolve light/dark\n// ourselves. Follow BOTH signals so we match whatever drives the surrounding\n// `dark:` Tailwind variants: the `.dark` class (class strategy) OR the\n// prefers-color-scheme media query (media strategy). Shared by CodeView and\n// CodeBlock so the two always agree on the palette.\n\n// `matchMedia` is optional: jsdom (and so a consumer's component tests) has\n// none, and a missing media API must cost the media signal, not the render.\nfunction darkQuery(): MediaQueryList | null {\n return typeof window.matchMedia === \"function\"\n ? window.matchMedia(\"(prefers-color-scheme: dark)\")\n : null;\n}\n\nfunction subscribeDark(callback: () => void): () => void {\n const mq = darkQuery();\n mq?.addEventListener?.(\"change\", callback);\n const observer = new MutationObserver(callback);\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: [\"class\"],\n });\n return () => {\n mq?.removeEventListener?.(\"change\", callback);\n observer.disconnect();\n };\n}\n\nfunction darkSnapshot(): boolean {\n return document.documentElement.classList.contains(\"dark\") || (darkQuery()?.matches ?? false);\n}\n\n/** Light on the server (and during hydration), then the real theme. */\nexport function useIsDark(): boolean {\n return useSyncExternalStore(subscribeDark, darkSnapshot, () => false);\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/VirtualList/VirtualList.tsx"],"names":[],"mappings":";;;;AA4DO,SAAS,WAAA,CAAsC;AAAA,EACpD,KAAA;AAAA,EACA,SAAA;AAAA,EACA,MAAA,GAAS,MAAA;AAAA,EACT,iBAAA,GAAoB,EAAA;AAAA,EACpB,QAAA,GAAW,CAAA;AAAA,EACX,UAAA,GAAa,KAAA;AAAA,EACb,cAAA;AAAA,EACA,cAAA;AAAA,EACA,UAAA,GAAa,IAAA;AAAA,EACb,oBAAA;AAAA,EACA,SAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAG;AACL,CAAA,EAAwB;AACtB,EAAA,MAAM,WAAA,GAAc,OAA8B,IAAI,CAAA;AACtD,EAAA,MAAM,SAAA,GAAY,OAA8B,IAAI,CAAA;AAGpD,EAAA,MAAM,OAAA,GAAU,MAAA,iBAAO,IAAI,GAAA,EAAqB,CAAA;AAEhD,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,SAAS,CAAC,CAAA;AAE1C,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,SAAS,CAAC,CAAA;AAC5C,EAAA,MAAM,CAAC,YAAA,EAAc,eAAe,CAAA,GAAI,SAAS,CAAC,CAAA;AASlD,EAAA,MAAM,QAAA,GAAW,OAAO,CAAC,CAAA;AAEzB,EAAA,MAAM,cAAA,GAAiB,WAAA;AAAA,IACrB,CAAC,EAAA,KAA8B;AAC7B,MAAA,WAAA,CAAY,OAAA,GAAU,EAAA;AACtB,MAAA,IAAI,OAAO,GAAA,KAAQ,UAAA,EAAY,GAAA,CAAI,EAAE,CAAA;AAAA,WAAA,IAC5B,GAAA,EAAM,GAAA,CAA2C,OAAA,GAAU,EAAA;AAAA,IACtE,CAAA;AAAA,IACA,CAAC,GAAG;AAAA,GACN;AAKA,EAAA,MAAM,QAAA,GACJ,eAAe,CAAA,GAAI,YAAA,GAAe,OAAO,MAAA,KAAW,QAAA,GAAW,SAAS,iBAAA,GAAoB,EAAA;AAQ9F,EAAA,MAAM,EAAE,OAAA,EAAS,KAAA,EAAM,GAAI,QAAQ,MAAM;AACvC,IAAA,MAAM,IAAA,GAAO,IAAI,YAAA,CAAa,KAAA,CAAM,SAAS,CAAC,CAAA;AAC9C,IAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,MAAA,EAAQ,KAAK,CAAA,EAAG;AACxC,MAAA,IAAA,CAAK,CAAA,GAAI,CAAC,CAAA,GAAI,IAAA,CAAK,CAAC,CAAA,IAAK,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,CAAC,CAAA,CAAE,EAAE,CAAA,IAAK,iBAAA,CAAA;AAAA,IAC/D;AAEA,IAAA,OAAO,EAAE,OAAA,EAAS,IAAA,EAAM,KAAA,EAAO,KAAA,CAAM,MAAA,KAAW,CAAA,GAAI,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,MAAM,CAAA,EAAE;AAAA,EAC7E,CAAA,EAAG,CAAC,KAAA,EAAO,QAAA,EAAU,iBAAiB,CAAC,CAAA;AAGvC,EAAA,MAAM,OAAA,GAAU,WAAA;AAAA,IACd,CAAC,CAAA,KAAsB;AACrB,MAAA,IAAI,EAAA,GAAK,CAAA;AACT,MAAA,IAAI,EAAA,GAAK,MAAM,MAAA,GAAS,CAAA;AACxB,MAAA,OAAO,KAAK,EAAA,EAAI;AACd,QAAA,MAAM,GAAA,GAAO,EAAA,GAAK,EAAA,GAAK,CAAA,IAAM,CAAA;AAC7B,QAAA,IAAI,OAAA,CAAQ,GAAG,CAAA,IAAK,CAAA,EAAG,EAAA,GAAK,GAAA;AAAA,kBAClB,GAAA,GAAM,CAAA;AAAA,MAClB;AAEA,MAAA,OAAO,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,EAAE,CAAA;AAAA,IACvB,CAAA;AAAA,IACA,CAAC,KAAA,CAAM,MAAA,EAAQ,OAAO;AAAA,GACxB;AAEA,EAAA,MAAM,QAAQ,IAAA,CAAK,GAAA,CAAI,GAAG,OAAA,CAAQ,SAAS,IAAI,QAAQ,CAAA;AACvD,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,GAAA,CAAI,KAAA,CAAM,MAAA,EAAQ,QAAQ,SAAA,GAAY,QAAQ,CAAA,GAAI,CAAA,GAAI,QAAQ,CAAA;AAC/E,EAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,MAAM,KAAA,CAAM,KAAA,CAAM,KAAA,EAAO,GAAG,CAAA,EAAG,CAAC,KAAA,EAAO,KAAA,EAAO,GAAG,CAAC,CAAA;AAa1E,EAAA,MAAM,QAAA,GAAW,OAA8B,IAAI,CAAA;AAEnD,EAAA,MAAM,QAAA,GAAW,MAAA,iBAAO,IAAI,GAAA,EAAc,CAAA;AAI1C,EAAA,eAAA,CAAgB,MAAM;AAIpB,IAAA,IAAI,OAAO,mBAAmB,WAAA,EAAa;AAE3C,IAAA,MAAM,EAAA,GAAK,IAAI,cAAA,CAAe,CAAC,OAAA,KAAY;AACzC,MAAA,IAAI,OAAA,GAAU,KAAA;AACd,MAAA,KAAA,MAAW,SAAS,OAAA,EAAS;AAC3B,QAAA,MAAM,KAAK,KAAA,CAAM,MAAA;AAEjB,QAAA,IAAI,EAAA,KAAO,YAAY,OAAA,EAAS;AAG9B,UAAA,MAAM,KAAK,EAAA,CAAG,YAAA;AACd,UAAA,IAAI,EAAA,GAAK,CAAA,EAAG,eAAA,CAAgB,EAAE,CAAA;AAC9B,UAAA;AAAA,QACF;AAEA,QAAA,MAAM,EAAA,GAAK,EAAA,CAAG,YAAA,CAAa,WAAW,CAAA;AACtC,QAAA,IAAI,OAAO,IAAA,EAAM;AAEjB,QAAA,MAAM,CAAA,GAAI,YAAY,KAAK,CAAA;AAC3B,QAAA,IAAI,IAAI,CAAA,IAAK,OAAA,CAAQ,QAAQ,GAAA,CAAI,EAAE,MAAM,CAAA,EAAG;AAC1C,UAAA,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,EAAA,EAAI,CAAC,CAAA;AACzB,UAAA,OAAA,GAAU,IAAA;AAAA,QACZ;AAAA,MACF;AAEA,MAAA,IAAI,OAAA,EAAS,WAAA,CAAY,CAAC,CAAA,KAAM,IAAI,CAAC,CAAA;AAAA,IACvC,CAAC,CAAA;AACD,IAAA,QAAA,CAAS,OAAA,GAAU,EAAA;AAEnB,IAAA,MAAM,KAAK,WAAA,CAAY,OAAA;AACvB,IAAA,IAAI,EAAA,EAAI,EAAA,CAAG,OAAA,CAAQ,EAAE,CAAA;AAErB,IAAA,MAAM,UAAU,QAAA,CAAS,OAAA;AAEzB,IAAA,OAAO,MAAM;AACX,MAAA,EAAA,CAAG,UAAA,EAAW;AACd,MAAA,QAAA,CAAS,OAAA,GAAU,IAAA;AACnB,MAAA,OAAA,CAAQ,KAAA,EAAM;AAAA,IAChB,CAAA;AAAA,EACF,CAAA,EAAG,EAAE,CAAA;AAOL,EAAA,eAAA,CAAgB,MAAM;AACpB,IAAA,MAAM,MAAM,SAAA,CAAU,OAAA;AACtB,IAAA,IAAI,CAAC,GAAA,EAAK;AAEV,IAAA,MAAM,KAAK,QAAA,CAAS,OAAA;AACpB,IAAA,MAAM,UAAU,QAAA,CAAS,OAAA;AACzB,IAAA,MAAM,OAAA,GAAU,EAAA,mBAAK,IAAI,GAAA,EAAa,GAAI,IAAA;AAE1C,IAAA,IAAI,OAAA,GAAU,KAAA;AACd,IAAA,IAAI,WAAA,GAAc,KAAA;AAClB,IAAA,KAAA,MAAW,EAAA,IAAM,KAAA,CAAM,IAAA,CAAK,GAAA,CAAI,QAAQ,CAAA,EAAoB;AAC1D,MAAA,MAAM,EAAA,GAAK,EAAA,CAAG,YAAA,CAAa,WAAW,CAAA;AACtC,MAAA,IAAI,OAAO,IAAA,EAAM;AAEjB,MAAA,IAAI,MAAM,OAAA,EAAS;AACjB,QAAA,OAAA,CAAQ,IAAI,EAAE,CAAA;AAEd,QAAA,IAAI,OAAA,CAAQ,GAAA,CAAI,EAAE,CAAA,EAAG;AAErB,QAAA,EAAA,CAAG,QAAQ,EAAE,CAAA;AACb,QAAA,OAAA,CAAQ,IAAI,EAAE,CAAA;AAGd,QAAA,IAAI,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,EAAE,CAAA,EAAG;AAAA,MAC/B;AAEA,MAAA,MAAM,CAAA,GAAI,EAAA,CAAG,qBAAA,EAAsB,CAAE,MAAA;AACrC,MAAA,WAAA,GAAc,IAAA;AAGd,MAAA,IAAI,IAAI,CAAA,IAAK,OAAA,CAAQ,QAAQ,GAAA,CAAI,EAAE,MAAM,CAAA,EAAG;AAC1C,QAAA,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,EAAA,EAAI,CAAC,CAAA;AACzB,QAAA,OAAA,GAAU,IAAA;AAAA,MACZ;AAAA,IACF;AAEA,IAAA,IAAI,MAAM,OAAA,EAAS;AACjB,MAAA,KAAA,MAAW,MAAM,OAAA,EAAS;AACxB,QAAA,IAAI,CAAC,OAAA,CAAQ,GAAA,CAAI,EAAE,CAAA,EAAG;AACpB,UAAA,EAAA,CAAG,UAAU,EAAE,CAAA;AACf,UAAA,OAAA,CAAQ,OAAO,EAAE,CAAA;AAAA,QACnB;AAAA,MACF;AAAA,IACF;AAIA,IAAA,MAAM,KAAK,WAAA,CAAY,OAAA;AACvB,IAAA,IAAI,EAAA,KAAO,CAAC,EAAA,IAAM,WAAA,CAAA,IAAgB,GAAG,YAAA,GAAe,CAAA,IAAK,EAAA,CAAG,YAAA,KAAiB,YAAA,EAAc;AACzF,MAAA,eAAA,CAAgB,GAAG,YAAY,CAAA;AAAA,IACjC;AAEA,IAAA,IAAI,OAAA,EAAS,WAAA,CAAY,CAAC,CAAA,KAAM,IAAI,CAAC,CAAA;AAAA,EACvC,CAAC,CAAA;AAKD,EAAA,MAAM,QAAA,GAAW,OAAsB,IAAI,CAAA;AAC3C,EAAA,MAAM,QAAA,GAAW,OAAsB,IAAI,CAAA;AAE3C,EAAA,SAAA,CAAU,MAAM;AAEd,IAAA,QAAA,CAAS,OAAA,GAAU,IAAA;AACnB,IAAA,QAAA,CAAS,OAAA,GAAU,IAAA;AAAA,EACrB,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAEf,EAAA,eAAA,CAAgB,MAAM;AACpB,IAAA,IAAI,UAAA,KAAe,IAAA,IAAQ,QAAA,CAAS,OAAA,KAAY,UAAA,EAAY;AAE5D,IAAA,MAAM,QAAQ,KAAA,CAAM,SAAA,CAAU,CAAC,IAAA,KAAS,IAAA,CAAK,OAAO,UAAU,CAAA;AAC9D,IAAA,IAAI,QAAQ,CAAA,EAAG;AAIb,MAAA,QAAA,CAAS,OAAA,GAAU,UAAA;AACnB,MAAA,oBAAA,GAAuB,YAAY,KAAK,CAAA;AAExC,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,KAAK,WAAA,CAAY,OAAA;AACvB,IAAA,IAAI,CAAC,EAAA,EAAI;AAKT,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,KAAK,GAAA,CAAI,OAAA,CAAQ,KAAK,CAAA,EAAG,KAAK,GAAA,CAAI,CAAA,EAAG,KAAA,GAAQ,QAAQ,CAAC,CAAC,CAAA;AAElF,IAAA,IAAI,QAAA,CAAS,YAAY,MAAA,EAAQ;AAC/B,MAAA,QAAA,CAAS,OAAA,GAAU,MAAA;AACnB,MAAA,EAAA,CAAG,SAAA,GAAY,MAAA;AACf,MAAA,QAAA,CAAS,OAAA,GAAU,MAAA;AACnB,MAAA,YAAA,CAAa,MAAM,CAAA;AAEnB,MAAA;AAAA,IACF;AAIA,IAAA,IAAI,KAAK,GAAA,CAAI,EAAA,CAAG,SAAA,GAAY,MAAM,KAAK,CAAA,EAAG;AACxC,MAAA,QAAA,CAAS,OAAA,GAAU,UAAA;AACnB,MAAA,oBAAA,GAAuB,YAAY,IAAI,CAAA;AAAA,IACzC;AAAA,EACF,CAAC,CAAA;AAID,EAAA,MAAM,cAAA,GAAiB,OAAO,UAAU,CAAA;AACxC,EAAA,MAAM,YAAA,GAAe,OAAO,KAAK,CAAA;AAEjC,EAAA,SAAA,CAAU,MAAM;AAId,IAAA,cAAA,CAAe,OAAA,GAAU,UAAA;AAAA,EAC3B,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAEf,EAAA,MAAM,SAAA,GAAY,WAAA;AAAA,IAChB,CAAC,IAAA,KAAkB;AACjB,MAAA,IAAI,cAAA,CAAe,YAAY,IAAA,EAAM;AACrC,MAAA,cAAA,CAAe,OAAA,GAAU,IAAA;AACzB,MAAA,cAAA,GAAiB,IAAI,CAAA;AAAA,IACvB,CAAA;AAAA,IACA,CAAC,cAAc;AAAA,GACjB;AAEA,EAAA,eAAA,CAAgB,MAAM;AACpB,IAAA,IAAI,CAAC,UAAA,EAAY;AAGjB,IAAA,IAAI,UAAA,KAAe,IAAA,IAAQ,QAAA,CAAS,OAAA,KAAY,UAAA,EAAY;AAE5D,IAAA,MAAM,KAAK,WAAA,CAAY,OAAA;AACvB,IAAA,IAAI,CAAC,EAAA,EAAI;AAWT,IAAA,MAAM,OAAA,GAAU,QAAA,CAAS,OAAA,GAAU,QAAA,CAAS,UAAU,EAAA,CAAG,SAAA;AACzD,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,QAAQ,QAAQ,CAAA;AAC3C,IAAA,IAAI,IAAA,CAAK,GAAA,CAAI,OAAA,GAAU,MAAM,IAAI,GAAA,EAAK;AACpC,MAAA,YAAA,CAAa,OAAA,GAAU,IAAA;AACvB,MAAA,EAAA,CAAG,SAAA,GAAY,MAAA;AACf,MAAA,QAAA,CAAS,OAAA,GAAU,MAAA;AACnB,MAAA,YAAA,CAAa,MAAM,CAAA;AAAA,IACrB;AAAA,EACF,CAAC,CAAA;AAED,EAAA,MAAM,QAAA,GAAW,YAAY,MAAM;AACjC,IAAA,MAAM,KAAK,WAAA,CAAY,OAAA;AACvB,IAAA,IAAI,CAAC,EAAA,EAAI;AAET,IAAA,MAAM,OAAO,EAAA,CAAG,SAAA;AAChB,IAAA,QAAA,CAAS,OAAA,GAAU,IAAA;AACnB,IAAA,MAAM,QAAA,GAAW,SAAA;AACjB,IAAA,YAAA,CAAa,IAAI,CAAA;AAEjB,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,QAAQ,QAAQ,CAAA;AAC3C,IAAA,MAAM,kBAAkB,YAAA,CAAa,OAAA;AACrC,IAAA,YAAA,CAAa,OAAA,GAAU,KAAA;AAEvB,IAAA,IAAI,IAAA,IAAQ,SAAS,YAAA,EAAc;AAEjC,MAAA,IAAI,CAAC,eAAA,IAAmB,IAAA,GAAO,QAAA,YAAoB,KAAK,CAAA;AAAA,IAC1D,CAAA,MAAO;AACL,MAAA,SAAA,CAAU,IAAI,CAAA;AAAA,IAChB;AAAA,EACF,GAAG,CAAC,SAAA,EAAW,KAAA,EAAO,QAAA,EAAU,SAAS,CAAC,CAAA;AAG1C,EAAA,MAAM,OAAA,GAAU,WAAA;AAAA,IACd,CAAC,CAAA,KAAkC;AACjC,MAAA,IAAI,CAAA,CAAE,MAAA,GAAS,CAAA,EAAG,SAAA,CAAU,KAAK,CAAA;AAAA,IACnC,CAAA;AAAA,IACA,CAAC,SAAS;AAAA,GACZ;AAEA,EAAA,MAAM,SAAA,GAAY,WAAA;AAAA,IAChB,CAAC,CAAA,KAAqC;AACpC,MAAA,IAAI,YAAY,GAAA,CAAI,CAAA,CAAE,GAAG,CAAA,YAAa,KAAK,CAAA;AAAA,IAC7C,CAAA;AAAA,IACA,CAAC,SAAS;AAAA,GACZ;AAEA,EAAA,MAAM,UAAA,GAAa,OAAsB,IAAI,CAAA;AAE7C,EAAA,MAAM,YAAA,GAAe,WAAA,CAAY,CAAC,CAAA,KAAkC;AAClE,IAAA,UAAA,CAAW,OAAA,GAAU,CAAA,CAAE,OAAA,CAAQ,CAAC,GAAG,OAAA,IAAW,IAAA;AAAA,EAChD,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,WAAA,GAAc,WAAA;AAAA,IAClB,CAAC,CAAA,KAAkC;AACjC,MAAA,MAAM,OAAO,UAAA,CAAW,OAAA;AACxB,MAAA,MAAM,CAAA,GAAI,CAAA,CAAE,OAAA,CAAQ,CAAC,CAAA,EAAG,OAAA;AAExB,MAAA,IAAI,IAAA,KAAS,QAAQ,CAAA,KAAM,MAAA,IAAa,IAAI,IAAA,GAAO,UAAA,YAAsB,KAAK,CAAA;AAAA,IAChF,CAAA;AAAA,IACA,CAAC,SAAS;AAAA,GACZ;AAIA,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,KAAA,CAAM,MAAM,CAAA;AAChC,EAAA,MAAM,cAAA,GAAiB,OAAsB,IAAI,CAAA;AAEjD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,MAAA,GAAS,aAAa,CAAA,GAAI,IAAA,CAAK,IAAI,CAAA,EAAG,KAAA,CAAM,MAAA,GAAS,IAAA,CAAK,OAAO,CAAA;AACvE,IAAA,IAAI,UAAA,EAAY,IAAA,CAAK,OAAA,GAAU,KAAA,CAAM,MAAA;AAErC,IAAA,IAAI,cAAA,CAAe,YAAY,MAAA,EAAQ;AACvC,IAAA,cAAA,CAAe,OAAA,GAAU,MAAA;AACzB,IAAA,cAAA,GAAiB,MAAM,CAAA;AAAA,EACzB,GAAG,CAAC,KAAA,CAAM,MAAA,EAAQ,UAAA,EAAY,cAAc,CAAC,CAAA;AAI7C,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,GAAA,EAAK,cAAA;AAAA,MACL,+BAAA,EAA8B,EAAA;AAAA,MAC9B,yCAAA,EAAyC,aAAa,MAAA,GAAS,OAAA;AAAA,MAC/D,sCAAoC,KAAA,CAAM,MAAA;AAAA,MAC1C,IAAA,EAAK,KAAA;AAAA,MACL,QAAA,EAAU,CAAA;AAAA,MACV,SAAA,EAAW,EAAA,CAAG,yDAAA,EAA2D,SAAS,CAAA;AAAA,MAClF,KAAA,EAAO,EAAE,MAAA,EAAO;AAAA,MAChB,QAAA;AAAA,MACA,OAAA;AAAA,MACA,SAAA;AAAA,MACA,YAAA;AAAA,MACA,WAAA;AAAA,MACC,GAAG,KAAA;AAAA,MAIJ,QAAA,kBAAA,GAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UACC,qCAAA,EAAoC,EAAA;AAAA,UACpC,OAAO,EAAE,MAAA,EAAQ,GAAG,KAAK,CAAA,EAAA,CAAA,EAAM,UAAU,UAAA,EAAW;AAAA,UAEpD,QAAA,kBAAA,GAAA;AAAA,YAAC,KAAA;AAAA,YAAA;AAAA,cACC,GAAA,EAAK,SAAA;AAAA,cACL,sCAAA,EAAqC,EAAA;AAAA,cACrC,qCAAA,EAAqC,KAAA;AAAA,cACrC,KAAA,EAAO,EAAE,SAAA,EAAW,CAAA,WAAA,EAAc,QAAQ,KAAK,CAAA,IAAK,CAAC,CAAA,GAAA,CAAA,EAAM;AAAA,cAE1D,kBAAQ,GAAA,CAAI,CAAC,IAAA,EAAM,CAAA,yBACjB,KAAA,EAAA,EAAkB,iCAAA,EAAiC,IAAA,CAAK,EAAA,EACtD,oBAAU,IAAA,EAAM,KAAA,GAAQ,CAAC,CAAA,EAAA,EADlB,IAAA,CAAK,EAEf,CACD;AAAA;AAAA;AACH;AAAA;AACF;AAAA,GACF;AAEJ;AAEA,IAAM,WAAA,GAAc,iCAAA;AAQpB,SAAS,YAAY,KAAA,EAAoC;AACvD,EAAA,MAAM,MAAM,KAAA,CAAM,aAAA;AAClB,EAAA,MAAM,QAAQ,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA,GAAI,GAAA,CAAI,CAAC,CAAA,GAAK,GAAA;AAC7C,EAAA,IAAI,SAAS,OAAO,KAAA,CAAM,SAAA,KAAc,QAAA,SAAiB,KAAA,CAAM,SAAA;AAE/D,EAAA,OAAO,KAAA,CAAM,aAAa,MAAA,IAAU,CAAA;AACtC;AAEA,IAAM,YAAA,GAAe,CAAA;AAErB,IAAM,UAAA,GAAa,CAAA;AACnB,IAAM,8BAAc,IAAI,GAAA,CAAI,CAAC,SAAA,EAAW,QAAA,EAAU,MAAM,CAAC,CAAA","file":"chunk-ARNFT23K.js","sourcesContent":["import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport type { KeyboardEvent, TouchEvent, WheelEvent } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport type { VirtualListProps, VirtualListRow } from \"./VirtualList.types\";\n\n/**\n * A windowed list for ledgers that run to tens of thousands of rows — only the\n * visible slice is in the DOM, with **measured** variable row heights, deep\n * links by row id, and sticky-bottom tail-following.\n *\n * Built for an agent event stream, where every one of those three is load\n * bearing at once: message rows wrap and event rows do not, a link carries\n * `?at=<rowId>` to the exact row under discussion, and new rows arrive while\n * the human is reading older ones.\n *\n * ## Heights are measured, never predicted\n *\n * `estimateRowHeight` only seeds rows that have never been on screen. The\n * moment a row renders, its real height is measured and replaces the estimate,\n * and every offset after it moves. The alternative — a `rowHeight(item)`\n * callback — is a prediction about text wrapping, font loading and container\n * width, and when it is wrong the error accumulates down the list: rows draw in\n * the wrong place and `scrollToId` lands on a neighbour. Measuring is slower to\n * settle and correct when it does.\n *\n * ## Tail-follow breaks on INTENT, not on position\n *\n * Any upward gesture — wheel, drag, PageUp, Home — stops following\n * immediately, even when the list is still pinned to the bottom and\n * `scrollTop` has not changed yet. Position cannot tell \"the human scrolled\n * up\" from \"content grew downward\", so a position-only implementation keeps\n * following through a wheel-up and yanks the reader back on the next append.\n *\n * ## It is controlled\n *\n * `followTail` is the consumer's state. This component only ever *asks* via\n * `onFollowChange`, so the follow state is readable and writable by an agent\n * over a bridge, as the Human+ component contract requires. The `↓ n new` pill\n * is the consumer's to render, from `onUnseenChange`.\n *\n * @example\n * ```tsx\n * const [following, setFollowing] = useState(true);\n * const [unseen, setUnseen] = useState(0);\n *\n * <VirtualList\n * items={events}\n * renderRow={(e) => <StreamRow event={e} />}\n * height=\"100%\"\n * estimateRowHeight={28}\n * followTail={following}\n * onFollowChange={setFollowing}\n * onUnseenChange={setUnseen}\n * scrollToId={params.at}\n * />\n * {!following && unseen > 0 && (\n * <Button onClick={() => setFollowing(true)}>↓ {unseen} new</Button>\n * )}\n * ```\n */\nexport function VirtualList<T extends VirtualListRow>({\n items,\n renderRow,\n height = \"100%\",\n estimateRowHeight = 32,\n overscan = 6,\n followTail = false,\n onFollowChange,\n onUnseenChange,\n scrollToId = null,\n onScrollToIdResolved,\n className,\n ref,\n ...props\n}: VirtualListProps<T>) {\n const viewportRef = useRef<HTMLDivElement | null>(null);\n const windowRef = useRef<HTMLDivElement | null>(null);\n\n /** Measured heights by row id, so a height follows its row across a reindex. */\n const heights = useRef(new Map<string, number>());\n /** Bumped when a measurement actually changed, to recompute offsets. */\n const [measured, setMeasured] = useState(0);\n\n const [scrollTop, setScrollTop] = useState(0);\n const [clientHeight, setClientHeight] = useState(0);\n\n /**\n * Where the viewport is scrolled, as far as this list knows: the value from\n * the last `scroll` event, or the last position it set itself. Every change\n * to scrollTop raises a scroll event, so this is never wrong for longer than\n * one frame — and reading it, unlike reading `vp.scrollTop` inside a commit,\n * never forces a synchronous layout.\n */\n const knownTop = useRef(0);\n\n const attachViewport = useCallback(\n (el: HTMLDivElement | null) => {\n viewportRef.current = el;\n if (typeof ref === \"function\") ref(el);\n else if (ref) (ref as { current: HTMLDivElement | null }).current = el;\n },\n [ref],\n );\n\n // A number `height` IS the viewport height, and is usable before the element\n // has been measured. Without one, render a conservative first window so there\n // is something on screen for the measurement pass to work from.\n const viewport =\n clientHeight > 0 ? clientHeight : typeof height === \"number\" ? height : estimateRowHeight * 20;\n\n /**\n * Prefix sums of row heights: `offsets[i]` is where row `i` starts, and the\n * last entry is the total. Rebuilt whenever rows or measurements change —\n * O(n) once per change, against O(log n) per scroll from the binary search,\n * rather than O(n) on every scroll frame.\n */\n const { offsets, total } = useMemo(() => {\n const offs = new Float64Array(items.length + 1);\n for (let i = 0; i < items.length; i += 1) {\n offs[i + 1] = offs[i] + (heights.current.get(items[i].id) ?? estimateRowHeight);\n }\n\n return { offsets: offs, total: items.length === 0 ? 0 : offs[items.length] };\n }, [items, measured, estimateRowHeight]);\n\n /** First row whose span contains `y`. */\n const indexAt = useCallback(\n (y: number): number => {\n let lo = 0;\n let hi = items.length - 1;\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1;\n if (offsets[mid] <= y) lo = mid;\n else hi = mid - 1;\n }\n\n return Math.max(0, lo);\n },\n [items.length, offsets],\n );\n\n const start = Math.max(0, indexAt(scrollTop) - overscan);\n const end = Math.min(items.length, indexAt(scrollTop + viewport) + 1 + overscan);\n const visible = useMemo(() => items.slice(start, end), [items, start, end]);\n\n // ---------------------------------------------------------------- measuring\n\n //\n // Every getBoundingClientRect() / clientHeight read after a commit forces a\n // synchronous layout, and this list re-renders on every appended row. So a\n // row is measured ONCE, when its element first appears, and from then on the\n // ResizeObserver reports when it changes, with the size in the entry: no read\n // that forces layout. An append costs one measurement; a render where nothing\n // changed costs none. (Measured in genie#1340: re-measuring every visible row\n // on every render was 472 forced row measurements/s with 11 agents streaming.)\n\n const observer = useRef<ResizeObserver | null>(null);\n /** Row elements currently observed, so each is observed once and released. */\n const observed = useRef(new Set<Element>());\n\n // A layout effect, declared before the measuring one, so the observer exists\n // by the time the first window is measured and the first rows are observed.\n useLayoutEffect(() => {\n // Absent in jsdom and in older runtimes. Without it the list measures every\n // visible row on every render instead, which is the only way it can notice\n // a row reflowing in place.\n if (typeof ResizeObserver === \"undefined\") return;\n\n const ro = new ResizeObserver((entries) => {\n let changed = false;\n for (const entry of entries) {\n const el = entry.target as HTMLElement;\n\n if (el === viewportRef.current) {\n // The viewport's own resize changes how many rows fit. Reading\n // clientHeight here is free: the browser has just done layout.\n const ch = el.clientHeight;\n if (ch > 0) setClientHeight(ch);\n continue;\n }\n\n const id = el.getAttribute(ROW_ID_ATTR);\n if (id === null) continue;\n\n const h = entryHeight(entry);\n if (h > 0 && heights.current.get(id) !== h) {\n heights.current.set(id, h);\n changed = true;\n }\n }\n\n if (changed) setMeasured((v) => v + 1);\n });\n observer.current = ro;\n\n const vp = viewportRef.current;\n if (vp) ro.observe(vp);\n\n const tracked = observed.current;\n\n return () => {\n ro.disconnect();\n observer.current = null;\n tracked.clear();\n };\n }, []);\n\n // No dependency array on purpose: every render looks at what is on screen.\n // That LOOK is cheap — walking the window's children reads no layout. Only a\n // row seen for the first time is measured (or, with no ResizeObserver, every\n // row, as nothing else would notice a reflow). A render that learns nothing\n // new sets no state, which is what stops it looping.\n useLayoutEffect(() => {\n const win = windowRef.current;\n if (!win) return;\n\n const ro = observer.current;\n const tracked = observed.current;\n const present = ro ? new Set<Element>() : null;\n\n let changed = false;\n let measuredAny = false;\n for (const el of Array.from(win.children) as HTMLElement[]) {\n const id = el.getAttribute(ROW_ID_ATTR);\n if (id === null) continue;\n\n if (ro && present) {\n present.add(el);\n // Already observed: the observer reports its resizes.\n if (tracked.has(el)) continue;\n\n ro.observe(el);\n tracked.add(el);\n // A row scrolling back into view: its height is known, and the\n // observer's first report for the new element corrects it if it moved.\n if (heights.current.has(id)) continue;\n }\n\n const h = el.getBoundingClientRect().height;\n measuredAny = true;\n // A zero height is \"not laid out yet\", not a 0px row. Recording it would\n // collapse the list and make every offset after it wrong.\n if (h > 0 && heights.current.get(id) !== h) {\n heights.current.set(id, h);\n changed = true;\n }\n }\n\n if (ro && present) {\n for (const el of tracked) {\n if (!present.has(el)) {\n ro.unobserve(el);\n tracked.delete(el);\n }\n }\n }\n\n // With an observer, the viewport's resizes arrive through it; read it here\n // only when this pass has already paid for a layout, so it is free.\n const vp = viewportRef.current;\n if (vp && (!ro || measuredAny) && vp.clientHeight > 0 && vp.clientHeight !== clientHeight) {\n setClientHeight(vp.clientHeight);\n }\n\n if (changed) setMeasured((v) => v + 1);\n });\n\n // ------------------------------------------------------------- deep linking\n\n /** The target this component last moved to, to detect that it has settled. */\n const anchored = useRef<number | null>(null);\n const resolved = useRef<string | null>(null);\n\n useEffect(() => {\n // A new id is a new request, even if the old one had resolved.\n anchored.current = null;\n resolved.current = null;\n }, [scrollToId]);\n\n useLayoutEffect(() => {\n if (scrollToId === null || resolved.current === scrollToId) return;\n\n const index = items.findIndex((item) => item.id === scrollToId);\n if (index < 0) {\n // A link to a row that is no longer in the list. Leaving the viewport\n // where it is and SAYING SO is the only honest outcome — scrolling to the\n // top would be indistinguishable from a successful landing.\n resolved.current = scrollToId;\n onScrollToIdResolved?.(scrollToId, false);\n\n return;\n }\n\n const vp = viewportRef.current;\n if (!vp) return;\n\n // Clamped the way the browser clamps: a row inside the final viewport-height\n // of the list cannot be brought to the top, and pretending otherwise would\n // leave this waiting for a position that can never be reached.\n const target = Math.max(0, Math.min(offsets[index], Math.max(0, total - viewport)));\n\n if (anchored.current !== target) {\n anchored.current = target;\n vp.scrollTop = target;\n knownTop.current = target;\n setScrollTop(target);\n\n return;\n }\n\n // The target stopped moving as rows above it were measured, and we are on\n // it. Only now is the deep link actually honoured.\n if (Math.abs(vp.scrollTop - target) <= 1) {\n resolved.current = scrollToId;\n onScrollToIdResolved?.(scrollToId, true);\n }\n });\n\n // ------------------------------------------------------------- tail-follow\n\n const reportedFollow = useRef(followTail);\n const programmatic = useRef(false);\n\n useEffect(() => {\n // The prop is the truth, so adopt it rather than remembering what we asked\n // for. Otherwise a consumer that declines an `onFollowChange(false)` would\n // never be asked again.\n reportedFollow.current = followTail;\n }, [followTail]);\n\n const askFollow = useCallback(\n (next: boolean) => {\n if (reportedFollow.current === next) return;\n reportedFollow.current = next;\n onFollowChange?.(next);\n },\n [onFollowChange],\n );\n\n useLayoutEffect(() => {\n if (!followTail) return;\n // A pending deep link wins while it resolves; otherwise the two would fight\n // over the same scrollTop and the link would lose.\n if (scrollToId !== null && resolved.current !== scrollToId) return;\n\n const vp = viewportRef.current;\n if (!vp) return;\n\n // This is the first layout read after most commits, so reading\n // `vp.scrollTop` here paid the forced synchronous layout on every append\n // (genie#1340 follow-up: 1.5 to 1.8 s per 120 s with 11 agents streaming).\n // Compare against the position the list already tracks instead, which only\n // differs from `bottom` when the bottom moved (rows, measurements or the\n // viewport changed) or someone scrolled — so a commit that moved nothing\n // neither reads nor writes. Without a ResizeObserver the measuring pass has\n // already paid for a layout this commit, so reading is free and keeps the\n // fallback exactly as it was.\n const current = observer.current ? knownTop.current : vp.scrollTop;\n const bottom = Math.max(0, total - viewport);\n if (Math.abs(current - bottom) > 0.5) {\n programmatic.current = true;\n vp.scrollTop = bottom;\n knownTop.current = bottom;\n setScrollTop(bottom);\n }\n });\n\n const onScroll = useCallback(() => {\n const vp = viewportRef.current;\n if (!vp) return;\n\n const next = vp.scrollTop;\n knownTop.current = next;\n const previous = scrollTop;\n setScrollTop(next);\n\n const bottom = Math.max(0, total - viewport);\n const wasProgrammatic = programmatic.current;\n programmatic.current = false;\n\n if (next <= bottom - BOTTOM_SLACK) {\n // Moved up, and not because we moved it.\n if (!wasProgrammatic && next < previous) askFollow(false);\n } else {\n askFollow(true);\n }\n }, [scrollTop, total, viewport, askFollow]);\n\n /** Any upward gesture is a statement of intent, whatever scrollTop says. */\n const onWheel = useCallback(\n (e: WheelEvent<HTMLDivElement>) => {\n if (e.deltaY < 0) askFollow(false);\n },\n [askFollow],\n );\n\n const onKeyDown = useCallback(\n (e: KeyboardEvent<HTMLDivElement>) => {\n if (UPWARD_KEYS.has(e.key)) askFollow(false);\n },\n [askFollow],\n );\n\n const touchStart = useRef<number | null>(null);\n\n const onTouchStart = useCallback((e: TouchEvent<HTMLDivElement>) => {\n touchStart.current = e.touches[0]?.clientY ?? null;\n }, []);\n\n const onTouchMove = useCallback(\n (e: TouchEvent<HTMLDivElement>) => {\n const from = touchStart.current;\n const y = e.touches[0]?.clientY;\n // Finger travelling DOWN drags the content down, which scrolls up.\n if (from !== null && y !== undefined && y - from > TOUCH_SLOP) askFollow(false);\n },\n [askFollow],\n );\n\n // ------------------------------------------------------------ unseen count\n\n const seen = useRef(items.length);\n const reportedUnseen = useRef<number | null>(null);\n\n useEffect(() => {\n const unseen = followTail ? 0 : Math.max(0, items.length - seen.current);\n if (followTail) seen.current = items.length;\n\n if (reportedUnseen.current === unseen) return;\n reportedUnseen.current = unseen;\n onUnseenChange?.(unseen);\n }, [items.length, followTail, onUnseenChange]);\n\n // ------------------------------------------------------------------ render\n\n return (\n <div\n ref={attachViewport}\n data-react-fancy-virtual-list=\"\"\n data-react-fancy-virtual-list-following={followTail ? \"true\" : \"false\"}\n data-react-fancy-virtual-list-rows={items.length}\n role=\"log\"\n tabIndex={0}\n className={cn(\"relative overflow-y-auto overflow-x-hidden outline-none\", className)}\n style={{ height }}\n onScroll={onScroll}\n onWheel={onWheel}\n onKeyDown={onKeyDown}\n onTouchStart={onTouchStart}\n onTouchMove={onTouchMove}\n {...props}\n >\n {/* Holds the full scroll range so the scrollbar is honest from the first\n paint, instead of growing as rows are discovered. */}\n <div\n data-react-fancy-virtual-list-sizer=\"\"\n style={{ height: `${total}px`, position: \"relative\" }}\n >\n <div\n ref={windowRef}\n data-react-fancy-virtual-list-window=\"\"\n data-react-fancy-virtual-list-start={start}\n style={{ transform: `translateY(${offsets[start] ?? 0}px)` }}\n >\n {visible.map((item, i) => (\n <div key={item.id} data-react-fancy-virtual-row-id={item.id}>\n {renderRow(item, start + i)}\n </div>\n ))}\n </div>\n </div>\n </div>\n );\n}\n\nconst ROW_ID_ATTR = \"data-react-fancy-virtual-row-id\";\n\n/**\n * A row's border-box height from a ResizeObserver entry. `borderBoxSize` is\n * what getBoundingClientRect() would report; older engines lack it (and early\n * Firefox gave a single object, not an array), so fall back to `contentRect`,\n * which equals it for the row wrapper, as that carries no padding or border.\n */\nfunction entryHeight(entry: ResizeObserverEntry): number {\n const box = entry.borderBoxSize as ReadonlyArray<ResizeObserverSize> | ResizeObserverSize | undefined;\n const first = Array.isArray(box) ? box[0] : (box as ResizeObserverSize | undefined);\n if (first && typeof first.blockSize === \"number\") return first.blockSize;\n\n return entry.contentRect?.height ?? 0;\n}\n/** Within this many px of the bottom counts as \"at the bottom\". */\nconst BOTTOM_SLACK = 4;\n/** Ignore a touch jitter smaller than this before calling it an upward drag. */\nconst TOUCH_SLOP = 4;\nconst UPWARD_KEYS = new Set([\"ArrowUp\", \"PageUp\", \"Home\"]);\n"]}
@@ -1,31 +0,0 @@
1
- 'use strict';
2
-
3
- var react = require('react');
4
-
5
- // src/components/CodeView/use-is-dark.ts
6
- function darkQuery() {
7
- return typeof window.matchMedia === "function" ? window.matchMedia("(prefers-color-scheme: dark)") : null;
8
- }
9
- function subscribeDark(callback) {
10
- const mq = darkQuery();
11
- mq?.addEventListener?.("change", callback);
12
- const observer = new MutationObserver(callback);
13
- observer.observe(document.documentElement, {
14
- attributes: true,
15
- attributeFilter: ["class"]
16
- });
17
- return () => {
18
- mq?.removeEventListener?.("change", callback);
19
- observer.disconnect();
20
- };
21
- }
22
- function darkSnapshot() {
23
- return document.documentElement.classList.contains("dark") || (darkQuery()?.matches ?? false);
24
- }
25
- function useIsDark() {
26
- return react.useSyncExternalStore(subscribeDark, darkSnapshot, () => false);
27
- }
28
-
29
- exports.useIsDark = useIsDark;
30
- //# sourceMappingURL=chunk-HZUKPYR3.cjs.map
31
- //# sourceMappingURL=chunk-HZUKPYR3.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/CodeView/use-is-dark.ts"],"names":["useSyncExternalStore"],"mappings":";;;;;AAWA,SAAS,SAAA,GAAmC;AAC1C,EAAA,OAAO,OAAO,MAAA,CAAO,UAAA,KAAe,aAChC,MAAA,CAAO,UAAA,CAAW,8BAA8B,CAAA,GAChD,IAAA;AACN;AAEA,SAAS,cAAc,QAAA,EAAkC;AACvD,EAAA,MAAM,KAAK,SAAA,EAAU;AACrB,EAAA,EAAA,EAAI,gBAAA,GAAmB,UAAU,QAAQ,CAAA;AACzC,EAAA,MAAM,QAAA,GAAW,IAAI,gBAAA,CAAiB,QAAQ,CAAA;AAC9C,EAAA,QAAA,CAAS,OAAA,CAAQ,SAAS,eAAA,EAAiB;AAAA,IACzC,UAAA,EAAY,IAAA;AAAA,IACZ,eAAA,EAAiB,CAAC,OAAO;AAAA,GAC1B,CAAA;AACD,EAAA,OAAO,MAAM;AACX,IAAA,EAAA,EAAI,mBAAA,GAAsB,UAAU,QAAQ,CAAA;AAC5C,IAAA,QAAA,CAAS,UAAA,EAAW;AAAA,EACtB,CAAA;AACF;AAEA,SAAS,YAAA,GAAwB;AAC/B,EAAA,OAAO,QAAA,CAAS,gBAAgB,SAAA,CAAU,QAAA,CAAS,MAAM,CAAA,KAAM,SAAA,IAAa,OAAA,IAAW,KAAA,CAAA;AACzF;AAGO,SAAS,SAAA,GAAqB;AACnC,EAAA,OAAOA,0BAAA,CAAqB,aAAA,EAAe,YAAA,EAAc,MAAM,KAAK,CAAA;AACtE","file":"chunk-HZUKPYR3.cjs","sourcesContent":["import { useSyncExternalStore } from \"react\";\n\n// ── Dark-mode detection ────────────────────────────────────────────────────\n// The highlight palette is baked in as inline colors, so we resolve light/dark\n// ourselves. Follow BOTH signals so we match whatever drives the surrounding\n// `dark:` Tailwind variants: the `.dark` class (class strategy) OR the\n// prefers-color-scheme media query (media strategy). Shared by CodeView and\n// CodeBlock so the two always agree on the palette.\n\n// `matchMedia` is optional: jsdom (and so a consumer's component tests) has\n// none, and a missing media API must cost the media signal, not the render.\nfunction darkQuery(): MediaQueryList | null {\n return typeof window.matchMedia === \"function\"\n ? window.matchMedia(\"(prefers-color-scheme: dark)\")\n : null;\n}\n\nfunction subscribeDark(callback: () => void): () => void {\n const mq = darkQuery();\n mq?.addEventListener?.(\"change\", callback);\n const observer = new MutationObserver(callback);\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: [\"class\"],\n });\n return () => {\n mq?.removeEventListener?.(\"change\", callback);\n observer.disconnect();\n };\n}\n\nfunction darkSnapshot(): boolean {\n return document.documentElement.classList.contains(\"dark\") || (darkQuery()?.matches ?? false);\n}\n\n/** Light on the server (and during hydration), then the real theme. */\nexport function useIsDark(): boolean {\n return useSyncExternalStore(subscribeDark, darkSnapshot, () => false);\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/VirtualList/VirtualList.tsx"],"names":["useRef","useState","useCallback","useMemo","useLayoutEffect","useEffect","jsx","cn"],"mappings":"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","file":"chunk-MF2DCMOA.cjs","sourcesContent":["import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport type { KeyboardEvent, TouchEvent, WheelEvent } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport type { VirtualListProps, VirtualListRow } from \"./VirtualList.types\";\n\n/**\n * A windowed list for ledgers that run to tens of thousands of rows — only the\n * visible slice is in the DOM, with **measured** variable row heights, deep\n * links by row id, and sticky-bottom tail-following.\n *\n * Built for an agent event stream, where every one of those three is load\n * bearing at once: message rows wrap and event rows do not, a link carries\n * `?at=<rowId>` to the exact row under discussion, and new rows arrive while\n * the human is reading older ones.\n *\n * ## Heights are measured, never predicted\n *\n * `estimateRowHeight` only seeds rows that have never been on screen. The\n * moment a row renders, its real height is measured and replaces the estimate,\n * and every offset after it moves. The alternative — a `rowHeight(item)`\n * callback — is a prediction about text wrapping, font loading and container\n * width, and when it is wrong the error accumulates down the list: rows draw in\n * the wrong place and `scrollToId` lands on a neighbour. Measuring is slower to\n * settle and correct when it does.\n *\n * ## Tail-follow breaks on INTENT, not on position\n *\n * Any upward gesture — wheel, drag, PageUp, Home — stops following\n * immediately, even when the list is still pinned to the bottom and\n * `scrollTop` has not changed yet. Position cannot tell \"the human scrolled\n * up\" from \"content grew downward\", so a position-only implementation keeps\n * following through a wheel-up and yanks the reader back on the next append.\n *\n * ## It is controlled\n *\n * `followTail` is the consumer's state. This component only ever *asks* via\n * `onFollowChange`, so the follow state is readable and writable by an agent\n * over a bridge, as the Human+ component contract requires. The `↓ n new` pill\n * is the consumer's to render, from `onUnseenChange`.\n *\n * @example\n * ```tsx\n * const [following, setFollowing] = useState(true);\n * const [unseen, setUnseen] = useState(0);\n *\n * <VirtualList\n * items={events}\n * renderRow={(e) => <StreamRow event={e} />}\n * height=\"100%\"\n * estimateRowHeight={28}\n * followTail={following}\n * onFollowChange={setFollowing}\n * onUnseenChange={setUnseen}\n * scrollToId={params.at}\n * />\n * {!following && unseen > 0 && (\n * <Button onClick={() => setFollowing(true)}>↓ {unseen} new</Button>\n * )}\n * ```\n */\nexport function VirtualList<T extends VirtualListRow>({\n items,\n renderRow,\n height = \"100%\",\n estimateRowHeight = 32,\n overscan = 6,\n followTail = false,\n onFollowChange,\n onUnseenChange,\n scrollToId = null,\n onScrollToIdResolved,\n className,\n ref,\n ...props\n}: VirtualListProps<T>) {\n const viewportRef = useRef<HTMLDivElement | null>(null);\n const windowRef = useRef<HTMLDivElement | null>(null);\n\n /** Measured heights by row id, so a height follows its row across a reindex. */\n const heights = useRef(new Map<string, number>());\n /** Bumped when a measurement actually changed, to recompute offsets. */\n const [measured, setMeasured] = useState(0);\n\n const [scrollTop, setScrollTop] = useState(0);\n const [clientHeight, setClientHeight] = useState(0);\n\n /**\n * Where the viewport is scrolled, as far as this list knows: the value from\n * the last `scroll` event, or the last position it set itself. Every change\n * to scrollTop raises a scroll event, so this is never wrong for longer than\n * one frame — and reading it, unlike reading `vp.scrollTop` inside a commit,\n * never forces a synchronous layout.\n */\n const knownTop = useRef(0);\n\n const attachViewport = useCallback(\n (el: HTMLDivElement | null) => {\n viewportRef.current = el;\n if (typeof ref === \"function\") ref(el);\n else if (ref) (ref as { current: HTMLDivElement | null }).current = el;\n },\n [ref],\n );\n\n // A number `height` IS the viewport height, and is usable before the element\n // has been measured. Without one, render a conservative first window so there\n // is something on screen for the measurement pass to work from.\n const viewport =\n clientHeight > 0 ? clientHeight : typeof height === \"number\" ? height : estimateRowHeight * 20;\n\n /**\n * Prefix sums of row heights: `offsets[i]` is where row `i` starts, and the\n * last entry is the total. Rebuilt whenever rows or measurements change —\n * O(n) once per change, against O(log n) per scroll from the binary search,\n * rather than O(n) on every scroll frame.\n */\n const { offsets, total } = useMemo(() => {\n const offs = new Float64Array(items.length + 1);\n for (let i = 0; i < items.length; i += 1) {\n offs[i + 1] = offs[i] + (heights.current.get(items[i].id) ?? estimateRowHeight);\n }\n\n return { offsets: offs, total: items.length === 0 ? 0 : offs[items.length] };\n }, [items, measured, estimateRowHeight]);\n\n /** First row whose span contains `y`. */\n const indexAt = useCallback(\n (y: number): number => {\n let lo = 0;\n let hi = items.length - 1;\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1;\n if (offsets[mid] <= y) lo = mid;\n else hi = mid - 1;\n }\n\n return Math.max(0, lo);\n },\n [items.length, offsets],\n );\n\n const start = Math.max(0, indexAt(scrollTop) - overscan);\n const end = Math.min(items.length, indexAt(scrollTop + viewport) + 1 + overscan);\n const visible = useMemo(() => items.slice(start, end), [items, start, end]);\n\n // ---------------------------------------------------------------- measuring\n\n //\n // Every getBoundingClientRect() / clientHeight read after a commit forces a\n // synchronous layout, and this list re-renders on every appended row. So a\n // row is measured ONCE, when its element first appears, and from then on the\n // ResizeObserver reports when it changes, with the size in the entry: no read\n // that forces layout. An append costs one measurement; a render where nothing\n // changed costs none. (Measured in genie#1340: re-measuring every visible row\n // on every render was 472 forced row measurements/s with 11 agents streaming.)\n\n const observer = useRef<ResizeObserver | null>(null);\n /** Row elements currently observed, so each is observed once and released. */\n const observed = useRef(new Set<Element>());\n\n // A layout effect, declared before the measuring one, so the observer exists\n // by the time the first window is measured and the first rows are observed.\n useLayoutEffect(() => {\n // Absent in jsdom and in older runtimes. Without it the list measures every\n // visible row on every render instead, which is the only way it can notice\n // a row reflowing in place.\n if (typeof ResizeObserver === \"undefined\") return;\n\n const ro = new ResizeObserver((entries) => {\n let changed = false;\n for (const entry of entries) {\n const el = entry.target as HTMLElement;\n\n if (el === viewportRef.current) {\n // The viewport's own resize changes how many rows fit. Reading\n // clientHeight here is free: the browser has just done layout.\n const ch = el.clientHeight;\n if (ch > 0) setClientHeight(ch);\n continue;\n }\n\n const id = el.getAttribute(ROW_ID_ATTR);\n if (id === null) continue;\n\n const h = entryHeight(entry);\n if (h > 0 && heights.current.get(id) !== h) {\n heights.current.set(id, h);\n changed = true;\n }\n }\n\n if (changed) setMeasured((v) => v + 1);\n });\n observer.current = ro;\n\n const vp = viewportRef.current;\n if (vp) ro.observe(vp);\n\n const tracked = observed.current;\n\n return () => {\n ro.disconnect();\n observer.current = null;\n tracked.clear();\n };\n }, []);\n\n // No dependency array on purpose: every render looks at what is on screen.\n // That LOOK is cheap — walking the window's children reads no layout. Only a\n // row seen for the first time is measured (or, with no ResizeObserver, every\n // row, as nothing else would notice a reflow). A render that learns nothing\n // new sets no state, which is what stops it looping.\n useLayoutEffect(() => {\n const win = windowRef.current;\n if (!win) return;\n\n const ro = observer.current;\n const tracked = observed.current;\n const present = ro ? new Set<Element>() : null;\n\n let changed = false;\n let measuredAny = false;\n for (const el of Array.from(win.children) as HTMLElement[]) {\n const id = el.getAttribute(ROW_ID_ATTR);\n if (id === null) continue;\n\n if (ro && present) {\n present.add(el);\n // Already observed: the observer reports its resizes.\n if (tracked.has(el)) continue;\n\n ro.observe(el);\n tracked.add(el);\n // A row scrolling back into view: its height is known, and the\n // observer's first report for the new element corrects it if it moved.\n if (heights.current.has(id)) continue;\n }\n\n const h = el.getBoundingClientRect().height;\n measuredAny = true;\n // A zero height is \"not laid out yet\", not a 0px row. Recording it would\n // collapse the list and make every offset after it wrong.\n if (h > 0 && heights.current.get(id) !== h) {\n heights.current.set(id, h);\n changed = true;\n }\n }\n\n if (ro && present) {\n for (const el of tracked) {\n if (!present.has(el)) {\n ro.unobserve(el);\n tracked.delete(el);\n }\n }\n }\n\n // With an observer, the viewport's resizes arrive through it; read it here\n // only when this pass has already paid for a layout, so it is free.\n const vp = viewportRef.current;\n if (vp && (!ro || measuredAny) && vp.clientHeight > 0 && vp.clientHeight !== clientHeight) {\n setClientHeight(vp.clientHeight);\n }\n\n if (changed) setMeasured((v) => v + 1);\n });\n\n // ------------------------------------------------------------- deep linking\n\n /** The target this component last moved to, to detect that it has settled. */\n const anchored = useRef<number | null>(null);\n const resolved = useRef<string | null>(null);\n\n useEffect(() => {\n // A new id is a new request, even if the old one had resolved.\n anchored.current = null;\n resolved.current = null;\n }, [scrollToId]);\n\n useLayoutEffect(() => {\n if (scrollToId === null || resolved.current === scrollToId) return;\n\n const index = items.findIndex((item) => item.id === scrollToId);\n if (index < 0) {\n // A link to a row that is no longer in the list. Leaving the viewport\n // where it is and SAYING SO is the only honest outcome — scrolling to the\n // top would be indistinguishable from a successful landing.\n resolved.current = scrollToId;\n onScrollToIdResolved?.(scrollToId, false);\n\n return;\n }\n\n const vp = viewportRef.current;\n if (!vp) return;\n\n // Clamped the way the browser clamps: a row inside the final viewport-height\n // of the list cannot be brought to the top, and pretending otherwise would\n // leave this waiting for a position that can never be reached.\n const target = Math.max(0, Math.min(offsets[index], Math.max(0, total - viewport)));\n\n if (anchored.current !== target) {\n anchored.current = target;\n vp.scrollTop = target;\n knownTop.current = target;\n setScrollTop(target);\n\n return;\n }\n\n // The target stopped moving as rows above it were measured, and we are on\n // it. Only now is the deep link actually honoured.\n if (Math.abs(vp.scrollTop - target) <= 1) {\n resolved.current = scrollToId;\n onScrollToIdResolved?.(scrollToId, true);\n }\n });\n\n // ------------------------------------------------------------- tail-follow\n\n const reportedFollow = useRef(followTail);\n const programmatic = useRef(false);\n\n useEffect(() => {\n // The prop is the truth, so adopt it rather than remembering what we asked\n // for. Otherwise a consumer that declines an `onFollowChange(false)` would\n // never be asked again.\n reportedFollow.current = followTail;\n }, [followTail]);\n\n const askFollow = useCallback(\n (next: boolean) => {\n if (reportedFollow.current === next) return;\n reportedFollow.current = next;\n onFollowChange?.(next);\n },\n [onFollowChange],\n );\n\n useLayoutEffect(() => {\n if (!followTail) return;\n // A pending deep link wins while it resolves; otherwise the two would fight\n // over the same scrollTop and the link would lose.\n if (scrollToId !== null && resolved.current !== scrollToId) return;\n\n const vp = viewportRef.current;\n if (!vp) return;\n\n // This is the first layout read after most commits, so reading\n // `vp.scrollTop` here paid the forced synchronous layout on every append\n // (genie#1340 follow-up: 1.5 to 1.8 s per 120 s with 11 agents streaming).\n // Compare against the position the list already tracks instead, which only\n // differs from `bottom` when the bottom moved (rows, measurements or the\n // viewport changed) or someone scrolled — so a commit that moved nothing\n // neither reads nor writes. Without a ResizeObserver the measuring pass has\n // already paid for a layout this commit, so reading is free and keeps the\n // fallback exactly as it was.\n const current = observer.current ? knownTop.current : vp.scrollTop;\n const bottom = Math.max(0, total - viewport);\n if (Math.abs(current - bottom) > 0.5) {\n programmatic.current = true;\n vp.scrollTop = bottom;\n knownTop.current = bottom;\n setScrollTop(bottom);\n }\n });\n\n const onScroll = useCallback(() => {\n const vp = viewportRef.current;\n if (!vp) return;\n\n const next = vp.scrollTop;\n knownTop.current = next;\n const previous = scrollTop;\n setScrollTop(next);\n\n const bottom = Math.max(0, total - viewport);\n const wasProgrammatic = programmatic.current;\n programmatic.current = false;\n\n if (next <= bottom - BOTTOM_SLACK) {\n // Moved up, and not because we moved it.\n if (!wasProgrammatic && next < previous) askFollow(false);\n } else {\n askFollow(true);\n }\n }, [scrollTop, total, viewport, askFollow]);\n\n /** Any upward gesture is a statement of intent, whatever scrollTop says. */\n const onWheel = useCallback(\n (e: WheelEvent<HTMLDivElement>) => {\n if (e.deltaY < 0) askFollow(false);\n },\n [askFollow],\n );\n\n const onKeyDown = useCallback(\n (e: KeyboardEvent<HTMLDivElement>) => {\n if (UPWARD_KEYS.has(e.key)) askFollow(false);\n },\n [askFollow],\n );\n\n const touchStart = useRef<number | null>(null);\n\n const onTouchStart = useCallback((e: TouchEvent<HTMLDivElement>) => {\n touchStart.current = e.touches[0]?.clientY ?? null;\n }, []);\n\n const onTouchMove = useCallback(\n (e: TouchEvent<HTMLDivElement>) => {\n const from = touchStart.current;\n const y = e.touches[0]?.clientY;\n // Finger travelling DOWN drags the content down, which scrolls up.\n if (from !== null && y !== undefined && y - from > TOUCH_SLOP) askFollow(false);\n },\n [askFollow],\n );\n\n // ------------------------------------------------------------ unseen count\n\n const seen = useRef(items.length);\n const reportedUnseen = useRef<number | null>(null);\n\n useEffect(() => {\n const unseen = followTail ? 0 : Math.max(0, items.length - seen.current);\n if (followTail) seen.current = items.length;\n\n if (reportedUnseen.current === unseen) return;\n reportedUnseen.current = unseen;\n onUnseenChange?.(unseen);\n }, [items.length, followTail, onUnseenChange]);\n\n // ------------------------------------------------------------------ render\n\n return (\n <div\n ref={attachViewport}\n data-react-fancy-virtual-list=\"\"\n data-react-fancy-virtual-list-following={followTail ? \"true\" : \"false\"}\n data-react-fancy-virtual-list-rows={items.length}\n role=\"log\"\n tabIndex={0}\n className={cn(\"relative overflow-y-auto overflow-x-hidden outline-none\", className)}\n style={{ height }}\n onScroll={onScroll}\n onWheel={onWheel}\n onKeyDown={onKeyDown}\n onTouchStart={onTouchStart}\n onTouchMove={onTouchMove}\n {...props}\n >\n {/* Holds the full scroll range so the scrollbar is honest from the first\n paint, instead of growing as rows are discovered. */}\n <div\n data-react-fancy-virtual-list-sizer=\"\"\n style={{ height: `${total}px`, position: \"relative\" }}\n >\n <div\n ref={windowRef}\n data-react-fancy-virtual-list-window=\"\"\n data-react-fancy-virtual-list-start={start}\n style={{ transform: `translateY(${offsets[start] ?? 0}px)` }}\n >\n {visible.map((item, i) => (\n <div key={item.id} data-react-fancy-virtual-row-id={item.id}>\n {renderRow(item, start + i)}\n </div>\n ))}\n </div>\n </div>\n </div>\n );\n}\n\nconst ROW_ID_ATTR = \"data-react-fancy-virtual-row-id\";\n\n/**\n * A row's border-box height from a ResizeObserver entry. `borderBoxSize` is\n * what getBoundingClientRect() would report; older engines lack it (and early\n * Firefox gave a single object, not an array), so fall back to `contentRect`,\n * which equals it for the row wrapper, as that carries no padding or border.\n */\nfunction entryHeight(entry: ResizeObserverEntry): number {\n const box = entry.borderBoxSize as ReadonlyArray<ResizeObserverSize> | ResizeObserverSize | undefined;\n const first = Array.isArray(box) ? box[0] : (box as ResizeObserverSize | undefined);\n if (first && typeof first.blockSize === \"number\") return first.blockSize;\n\n return entry.contentRect?.height ?? 0;\n}\n/** Within this many px of the bottom counts as \"at the bottom\". */\nconst BOTTOM_SLACK = 4;\n/** Ignore a touch jitter smaller than this before calling it an upward drag. */\nconst TOUCH_SLOP = 4;\nconst UPWARD_KEYS = new Set([\"ArrowUp\", \"PageUp\", \"Home\"]);\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/CodeView/CodeView.tsx"],"names":[],"mappings":"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","file":"chunk-PVHZO4T3.js","sourcesContent":["import { useEffect, useMemo, useRef } from \"react\";\nimport {\n highlightCode,\n tokenize,\n LIGHT_COLORS,\n DARK_COLORS,\n type ThemeColors,\n} from \"@particle-academy/fancy-file-commons\";\nimport { cn } from \"../../utils/cn\";\nimport { useIsDark } from \"./use-is-dark\";\nimport type { CodeViewProps } from \"./CodeView.types\";\n\n// Shared text metrics — the overlay and the textarea MUST match exactly (font,\n// size, line-height, padding, wrapping) so the highlighted glyphs sit under the\n// caret. Keep this string identical on both layers.\nconst TEXT_METRICS = \"m-0 p-3 font-mono text-xs leading-relaxed\";\n\n/**\n * A lightweight, syntax-highlighted source view built on the pure highlight\n * primitives in `@particle-academy/fancy-file-commons` — a highlight overlay\n * with a transparent, auto-growing textarea on top; the container scrolls both\n * together. Fills its parent's height (`h-full`) and grows with content down to\n * `minHeight`. Highlights every language fancy-file-commons ships (see\n * `CodeViewProps.language`); anything else renders as plain text.\n */\nexport function CodeView({\n value,\n onChange,\n language = \"plaintext\",\n readOnly = false,\n placeholder,\n minHeight = 120,\n maxHeight,\n className,\n}: CodeViewProps) {\n const textareaRef = useRef<HTMLTextAreaElement | null>(null);\n const isDark = useIsDark();\n const colors: ThemeColors = isDark ? DARK_COLORS : LIGHT_COLORS;\n\n const highlighted = useMemo(() => {\n const tokens = tokenize(value, language) ?? [];\n return highlightCode(value, tokens, colors);\n }, [value, language, colors]);\n\n // Grow the textarea to its content height so it never scrolls internally —\n // the outer container owns scrolling and moves overlay + textarea in lockstep.\n useEffect(() => {\n const el = textareaRef.current;\n if (!el) return;\n el.style.height = \"auto\";\n el.style.height = `${el.scrollHeight}px`;\n }, [value]);\n\n const editable = !readOnly && !!onChange;\n\n return (\n <div\n data-react-fancy-code-view=\"\"\n // No baked-in height: the consumer picks the fill mechanism — `flex-1`\n // inside a flex column (the Editor source body), or `h-full` when the\n // parent has a definite height. A hard-coded `h-full` here collapses to\n // content height under a flex-computed (indefinite) parent.\n className={cn(\"relative overflow-auto\", className)}\n style={{ backgroundColor: colors.background, color: colors.foreground, minHeight, maxHeight }}\n >\n <div className=\"relative min-h-full\">\n {/* Highlight overlay */}\n <pre\n aria-hidden=\"true\"\n className={cn(\"pointer-events-none absolute inset-0 overflow-hidden border-none\", TEXT_METRICS)}\n style={{ whiteSpace: \"pre-wrap\", overflowWrap: \"break-word\" }}\n >\n <code dangerouslySetInnerHTML={{ __html: highlighted + \"\\n\" }} />\n </pre>\n\n {/* Placeholder */}\n {placeholder && value.length === 0 && (\n <div className={cn(\"pointer-events-none absolute left-0 top-0 opacity-40\", TEXT_METRICS)}>\n {placeholder}\n </div>\n )}\n\n {/* Transparent input layer (caret + selection visible, glyphs from overlay) */}\n <textarea\n ref={textareaRef}\n data-react-fancy-code-view-input=\"\"\n value={value}\n onChange={editable ? (e) => onChange!(e.target.value) : undefined}\n readOnly={!editable}\n spellCheck={false}\n autoComplete=\"off\"\n autoCorrect=\"off\"\n autoCapitalize=\"off\"\n aria-label={placeholder}\n className={cn(\n \"relative block w-full resize-none border-none bg-transparent text-transparent outline-none\",\n TEXT_METRICS,\n )}\n style={{\n caretColor: colors.cursorColor,\n overflow: \"hidden\",\n whiteSpace: \"pre-wrap\",\n overflowWrap: \"break-word\",\n minHeight,\n }}\n />\n </div>\n </div>\n );\n}\n\nCodeView.displayName = \"CodeView\";\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/CodeBlock/CodeBlock.tsx"],"names":["tokenize","highlightCode","forwardRef","CodeBlock","useIsDark","DARK_COLORS","LIGHT_COLORS","useMemo","useState","useRef","useEffect","useCallback","runWriter","defaultClipboardWriter","jsx","Button","cn","jsxs","visuallyHidden"],"mappings":"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","file":"chunk-QH4YSG7C.cjs","sourcesContent":["import { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport {\n highlightCode,\n tokenize,\n LIGHT_COLORS,\n DARK_COLORS,\n type ThemeColors,\n type Token,\n} from \"@particle-academy/fancy-file-commons\";\nimport { cn } from \"../../utils/cn\";\nimport { visuallyHidden } from \"../../utils/visually-hidden\";\nimport { Button } from \"../Button/Button\";\nimport { defaultClipboardWriter, runWriter } from \"../ContentRenderer/copy\";\nimport { useIsDark } from \"../CodeView/use-is-dark\";\nimport type { CodeBlockProps } from \"./CodeBlock.types\";\n\n/** How long \"Copied\" / \"Copy failed\" stays before the button resets. */\nconst FEEDBACK_MS = 2000;\n\n/**\n * Tokens for any language fancy-file-commons can highlight (aliases included,\n * case-insensitive); none for anything else, which renders as plain text.\n * `tokenize` returns sorted, non-overlapping, in-bounds tokens, which\n * `highlightLines` relies on.\n */\nfunction tokensFor(code: string, language: string): Token[] {\n return tokenize(code, language) ?? [];\n}\n\n/**\n * Highlight one line at a time, clipping each token to the line it falls on, so\n * a token that spans lines (an HTML comment) keeps its colour on every line.\n * `highlightCode` escapes every slice it emits, highlighted or not.\n */\nfunction highlightLines(code: string, tokens: Token[], colors: ThemeColors): string[] {\n const out: string[] = [];\n let start = 0;\n let ti = 0;\n for (const line of code.split(\"\\n\")) {\n const end = start + line.length;\n while (ti < tokens.length && tokens[ti].end <= start) ti++;\n const clipped: Token[] = [];\n for (let j = ti; j < tokens.length && tokens[j].start < end; j++) {\n const s = Math.max(tokens[j].start, start) - start;\n const e = Math.min(tokens[j].end, end) - start;\n if (e > s) clipped.push({ type: tokens[j].type, start: s, end: e });\n }\n out.push(highlightCode(line, clipped, colors));\n start = end + 1;\n }\n return out;\n}\n\ntype CopyState = \"idle\" | \"copied\" | \"failed\";\n\n// Matches CodeView's text metrics so the two sit together.\nconst TEXT_METRICS = \"m-0 p-3 font-mono text-xs leading-relaxed\";\n\n/**\n * A display-only code block: `<pre><code>`, syntax-highlighted where a grammar\n * exists, with an optional copy button. It contains no textarea, input, select\n * or contenteditable element, so it is safe on pages that must not accept\n * input. For an editable or selectable source surface, use `CodeView`.\n */\nexport const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(function CodeBlock(\n {\n code,\n language = \"plaintext\",\n copyable = true,\n title,\n showLineNumbers = false,\n wrap = false,\n copyLabels,\n onCopyCode,\n className,\n style,\n ...rest\n },\n ref,\n) {\n const isDark = useIsDark();\n const colors: ThemeColors = isDark ? DARK_COLORS : LIGHT_COLORS;\n\n const tokens = useMemo(() => tokensFor(code, language), [code, language]);\n const html = useMemo(\n () => (showLineNumbers ? null : highlightCode(code, tokens, colors)),\n [code, tokens, colors, showLineNumbers],\n );\n const lines = useMemo(() => {\n if (!showLineNumbers) return null;\n const all = highlightLines(code, tokens, colors);\n // A trailing newline ends the last line; it does not start a new one.\n if (all.length > 1 && code.endsWith(\"\\n\")) all.pop();\n return all;\n }, [code, tokens, colors, showLineNumbers]);\n\n const [copyState, setCopyState] = useState<CopyState>(\"idle\");\n const timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n useEffect(() => () => clearTimeout(timer.current), []);\n\n // Latest props at click time, without re-creating the handler.\n const latest = useRef({ code, onCopyCode });\n latest.current = { code, onCopyCode };\n\n const onCopyClick = useCallback(async () => {\n const { code: text, onCopyCode: report } = latest.current;\n // Raw `code`, never the highlighted HTML. `defaultClipboardWriter` throws\n // when `navigator.clipboard` is absent (SSR, insecure context), and\n // `runWriter` turns a throw, rejection or hang into `false`.\n const ok = await runWriter(defaultClipboardWriter, { kind: \"code\", text, html: \"\" });\n setCopyState(ok ? \"copied\" : \"failed\");\n clearTimeout(timer.current);\n timer.current = setTimeout(() => setCopyState(\"idle\"), FEEDBACK_MS);\n report?.({ text, ok });\n }, []);\n\n const labels = {\n copy: copyLabels?.copy ?? \"Copy code\",\n copied: copyLabels?.copied ?? \"Copied\",\n failed: copyLabels?.failed ?? \"Copy failed\",\n };\n const label = copyState === \"copied\" ? labels.copied : copyState === \"failed\" ? labels.failed : labels.copy;\n const hasHeader = title != null && title !== false;\n const floating = copyable && !hasHeader;\n\n const copyButton = copyable ? (\n <Button\n type=\"button\"\n size=\"xs\"\n variant=\"ghost\"\n icon={copyState === \"copied\" ? \"check\" : copyState === \"failed\" ? \"x\" : \"copy\"}\n aria-label={label}\n title={label}\n data-react-fancy-code-block-copy=\"\"\n data-copy-state={copyState}\n onClick={onCopyClick}\n className={cn(\n floating && \"opacity-80 group-hover:opacity-100 focus-visible:opacity-100\",\n )}\n style={floating ? { backgroundColor: colors.background } : undefined}\n >\n {/* Visible feedback; idle is icon-only when floating over the code. */}\n {copyState !== \"idle\" ? label : hasHeader ? \"Copy\" : undefined}\n </Button>\n ) : null;\n\n const gutterWidth = lines ? `${String(lines.length).length}ch` : undefined;\n\n return (\n <div\n ref={ref}\n data-react-fancy-code-block=\"\"\n data-language={language}\n data-wrap={wrap ? \"true\" : \"false\"}\n className={cn(\n \"group relative overflow-hidden rounded-lg border\",\n className,\n )}\n style={{\n backgroundColor: colors.background,\n color: colors.foreground,\n borderColor: colors.gutterBorder,\n // `relative` inline too: the floating button and the live region are\n // absolute, and must not escape a host whose Tailwind lacks the class.\n position: \"relative\",\n ...style,\n }}\n {...rest}\n >\n {hasHeader && (\n <div\n data-react-fancy-code-block-header=\"\"\n className=\"flex items-center justify-between gap-2 border-b py-1 pl-3 pr-1 text-xs\"\n style={{\n backgroundColor: colors.gutterBackground,\n color: colors.gutterForeground,\n borderColor: colors.gutterBorder,\n }}\n >\n <span data-react-fancy-code-block-title=\"\" className=\"min-w-0 truncate font-mono\">\n {title}\n </span>\n {copyButton}\n </div>\n )}\n\n <pre\n data-react-fancy-code-block-pre=\"\"\n className={cn(TEXT_METRICS, \"border-none\", wrap ? \"overflow-x-hidden\" : \"overflow-x-auto\", floating && \"pr-12\")}\n style={{\n whiteSpace: wrap ? \"pre-wrap\" : \"pre\",\n overflowWrap: wrap ? \"break-word\" : \"normal\",\n backgroundColor: \"transparent\",\n color: \"inherit\",\n }}\n >\n {lines ? (\n <code\n data-react-fancy-code-block-code=\"\"\n style={{ display: wrap ? \"block\" : \"inline-block\", minWidth: \"100%\" }}\n >\n {lines.map((lineHtml, i) => (\n <span key={i} data-react-fancy-code-block-line={i + 1} style={{ display: \"flex\" }}>\n <span\n data-react-fancy-code-block-line-number=\"\"\n aria-hidden=\"true\"\n className=\"select-none\"\n style={{\n flex: \"none\",\n width: gutterWidth,\n marginRight: \"1.5ch\",\n textAlign: \"right\",\n color: colors.gutterForeground,\n userSelect: \"none\",\n }}\n >\n {i + 1}\n </span>\n <span\n data-react-fancy-code-block-line-content=\"\"\n style={{ flex: \"1 1 auto\", minWidth: 0 }}\n dangerouslySetInnerHTML={{ __html: lineHtml }}\n />\n </span>\n ))}\n </code>\n ) : (\n <code data-react-fancy-code-block-code=\"\" dangerouslySetInnerHTML={{ __html: html ?? \"\" }} />\n )}\n </pre>\n\n {floating && (\n <div\n data-react-fancy-code-block-actions=\"\"\n className=\"absolute right-2 top-2\"\n style={{ position: \"absolute\", right: \"0.5rem\", top: \"0.5rem\" }}\n >\n {copyButton}\n </div>\n )}\n\n {copyable && (\n <span role=\"status\" aria-live=\"polite\" className=\"sr-only\" style={visuallyHidden}>\n {copyState === \"idle\" ? \"\" : label}\n </span>\n )}\n </div>\n );\n});\n\nCodeBlock.displayName = \"CodeBlock\";\n"]}