@hyperframes/studio 0.8.143 → 0.8.145
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{hyperframes-player-BXSuksFh.js → hyperframes-player-DZ5JrMDg.js} +3 -3
- package/dist/assets/{index-FgyO_5Kv.js → index-BOs6C0aZ.js} +1 -1
- package/dist/assets/{index-BgOVCqjo.js → index-BqFRhNZa.js} +1 -1
- package/dist/assets/{index-J7jpL4pj.js → index-D8W0a0Z9.js} +149 -149
- package/dist/{chunk-TFLU2MGI.js → chunk-6JY3MTIX.js} +108 -2
- package/dist/chunk-6JY3MTIX.js.map +1 -0
- package/dist/{chunk-H2DUOXTF.js → chunk-NZPJUZKT.js} +100 -4
- package/dist/chunk-NZPJUZKT.js.map +1 -0
- package/dist/{chunk-UYWON5AX.js → chunk-TP6D3U37.js} +7 -100
- package/dist/chunk-TP6D3U37.js.map +1 -0
- package/dist/{chunk-LOVBLSMR.js → chunk-Y2OHHNM2.js} +35 -19
- package/dist/chunk-Y2OHHNM2.js.map +1 -0
- package/dist/{domEditingLayers-KBTPQKGA.js → domEditingLayers-PTP2ICGM.js} +3 -3
- package/dist/index.d.ts +8 -5
- package/dist/index.html +1 -1
- package/dist/index.js +545 -495
- package/dist/index.js.map +1 -1
- package/dist/player.js +3 -3
- package/dist/ui.d.ts +4 -5
- package/dist/ui.js +1 -1
- package/package.json +8 -8
- package/src/components/StudioLeftPanels.tsx +2 -2
- package/src/components/editor/DomEditSelectionChrome.test.tsx +103 -0
- package/src/components/editor/DomEditSelectionChrome.tsx +42 -16
- package/src/components/editor/LayersPanel.test.ts +4 -4
- package/src/components/editor/LayersPanel.tsx +2 -2
- package/src/components/editor/TopologyLens.test.tsx +5 -4
- package/src/components/editor/anchoredResizeCommitFeedsOffset.test.ts +36 -0
- package/src/components/editor/domEditOverlayGestures.ts +1 -0
- package/src/components/editor/domEditOverlayStartGesture.ts +5 -0
- package/src/components/editor/domEditResizeLocal.test.ts +81 -1
- package/src/components/editor/domEditResizeLocal.ts +27 -3
- package/src/components/editor/domEditingLayers.ts +4 -1
- package/src/components/editor/domEditingTextFields.test.ts +18 -0
- package/src/components/editor/manualEditsDom.ts +12 -12
- package/src/components/editor/previewGestureStarts.ts +14 -5
- package/src/components/editor/resizeDraft.ts +13 -4
- package/src/components/editor/useDomEditOverlayGestures.ts +5 -3
- package/src/components/renders/GpuEncodingToggle.tsx +28 -0
- package/src/components/renders/RenderQueue.test.tsx +20 -2
- package/src/components/renders/RenderQueue.tsx +13 -1
- package/src/components/renders/RenderQueuePanel.tsx +2 -1
- package/src/components/renders/renderSettings.ts +5 -2
- package/src/components/renders/useRenderQueue.ts +3 -0
- package/src/components/renders/useRenderQueueComposition.test.tsx +12 -0
- package/src/components/ui/Menu.test.tsx +67 -5
- package/src/components/ui/Menu.tsx +21 -4
- package/src/components/ui/Popover.tsx +11 -5
- package/src/hooks/gsapResizeIntercept.test.ts +46 -11
- package/src/hooks/gsapResizeIntercept.ts +25 -20
- package/src/hooks/useConsoleErrorCapture.test.tsx +21 -2
- package/src/hooks/useConsoleErrorCapture.ts +3 -2
- package/src/hooks/useThumbnailStripSize.test.tsx +123 -0
- package/src/hooks/useThumbnailStripSize.ts +19 -8
- package/src/index.ts +1 -0
- package/src/player/components/CompositionThumbnail.test.ts +3 -0
- package/src/player/components/ImageThumbnail.test.tsx +4 -1
- package/src/player/components/Player.test.ts +44 -0
- package/src/player/components/Player.tsx +11 -0
- package/src/player/components/VideoThumbnail.test.tsx +13 -2
- package/src/player/hooks/timelineSyncHydration.ts +2 -1
- package/src/player/lib/playbackTypes.ts +1 -0
- package/src/player/sceneSwap.test.ts +27 -1
- package/src/player/sceneSwap.ts +16 -2
- package/src/utils/authoredSource.test.ts +25 -0
- package/src/utils/authoredSource.ts +3 -5
- package/src/utils/rounding.ts +7 -0
- package/dist/chunk-H2DUOXTF.js.map +0 -1
- package/dist/chunk-LOVBLSMR.js.map +0 -1
- package/dist/chunk-TFLU2MGI.js.map +0 -1
- package/dist/chunk-UYWON5AX.js.map +0 -1
- /package/dist/{domEditingLayers-KBTPQKGA.js.map → domEditingLayers-PTP2ICGM.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/ui/cn.ts","../src/components/ui/Button.tsx","../src/components/ui/IconButton.tsx","../src/components/ui/Tabs.tsx","../src/components/ui/HyperframesMark.tsx","../src/components/ui/HyperframesLoader.tsx","../src/components/ui/Menu.tsx","../src/components/ui/Tooltip.tsx","../src/components/ui/Popover.tsx","../src/components/ui/Input.tsx","../src/components/ui/NumberField.tsx","../src/components/ui/Select.tsx","../src/components/ui/Slider.tsx","../src/components/ui/Toggle.tsx"],"sourcesContent":["/**\n * Studio's tailwind-merge config: teaches it the two scales `theme.css`\n * invents (`text-step-*`, `ctl`/`ctl-sm`/`ctl-lg`) so overrides replace, not stack.\n */\n\nimport type { CnFunction } from \"cn\";\nimport { createCn } from \"cn/config\";\n\n/** `text-step-11`, `text-step-9`, … — the type scale from `theme.css`. */\nconst isTypeStep = (value: string) => /^step-\\d+$/.test(value);\n\n/** `ctl`, `ctl-sm`, `ctl-lg` — the control heights from `theme.css`. */\nconst isControlSize = (value: string) => /^ctl(-sm|-lg)?$/.test(value);\n\nexport const cn: CnFunction = createCn({\n extend: {\n classGroups: {\n \"font-size\": [{ text: [isTypeStep] }],\n h: [{ h: [isControlSize] }],\n w: [{ w: [isControlSize] }],\n size: [{ size: [isControlSize] }],\n \"min-h\": [{ \"min-h\": [isControlSize] }],\n \"min-w\": [{ \"min-w\": [isControlSize] }],\n },\n },\n});\n","/**\n * Button — a native `<button>` wearing Studio's tokens.\n *\n * Everything visual comes from `theme.css`: the three control heights, the\n * radius scale, the semantic colours, the motion durations. No value is decided\n * here, so a button cannot drift from the rest of the system.\n *\n * Two conventions: classes merge through `cn`, so a caller's `className`\n * always wins within its group; and every interactive look is written twice,\n * once as the real state and once as `data-[preview-state=…]` (CSS-only, for\n * screenshots). `Button.test.tsx` asserts the two stay in sync.\n */\n\nimport { forwardRef, type ButtonHTMLAttributes, type ReactNode } from \"react\";\nimport { cn } from \"./cn\";\n\nexport type ButtonVariant = \"primary\" | \"secondary\" | \"danger\" | \"ghost\";\nexport type ButtonSize = \"sm\" | \"md\" | \"lg\";\n\n/** Forces one interactive look for a gallery shot. CSS-only; see the header. */\nexport type PreviewState = \"hover\" | \"active\" | \"focus\";\n\nexport interface ButtonBaseProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: ButtonVariant;\n size?: ButtonSize;\n \"data-preview-state\"?: PreviewState;\n}\n\ninterface ButtonProps extends ButtonBaseProps {\n loading?: boolean;\n icon?: ReactNode;\n}\n\n/**\n * Shared by Button and IconButton. `disabled:` keeps pointer events alive so a\n * wrapping Tooltip can still explain why the control is disabled.\n */\nexport const buttonBase = cn(\n \"inline-flex items-center justify-center select-none cursor-pointer whitespace-nowrap\",\n \"transition-[background-color,border-color,color,filter,transform] ease-out-quint duration-press\",\n \"disabled:opacity-40 disabled:cursor-not-allowed\",\n \"outline-hidden\",\n \"focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-accent\",\n \"data-[preview-state=focus]:outline-solid data-[preview-state=focus]:outline-2 data-[preview-state=focus]:outline-offset-1 data-[preview-state=focus]:outline-accent\",\n);\n\n/**\n * One entry per variant. The `data-[preview-state=…]` half of each string\n * repeats the `hover:` and `active:` half exactly; the test pairs them.\n */\nexport const buttonVariants: Record<ButtonVariant, string> = {\n primary: cn(\n \"bg-accent text-on-accent font-semibold\",\n \"enabled:hover:bg-accent-hover data-[preview-state=hover]:bg-accent-hover\",\n \"enabled:active:scale-[0.98] data-[preview-state=active]:scale-[0.98]\",\n ),\n secondary: cn(\n \"bg-transparent text-text-1 font-medium border border-border-strong\",\n \"enabled:hover:bg-hover enabled:hover:text-text-0 data-[preview-state=hover]:bg-hover data-[preview-state=hover]:text-text-0\",\n \"enabled:active:scale-[0.98] data-[preview-state=active]:scale-[0.98]\",\n ),\n danger: cn(\n \"bg-danger text-on-danger font-medium\",\n \"enabled:hover:brightness-95 data-[preview-state=hover]:brightness-95\",\n \"enabled:active:scale-[0.98] data-[preview-state=active]:scale-[0.98]\",\n ),\n ghost: cn(\n \"bg-transparent text-text-2 font-medium\",\n \"enabled:hover:bg-hover enabled:hover:text-text-0 data-[preview-state=hover]:bg-hover data-[preview-state=hover]:text-text-0\",\n \"enabled:active:scale-[0.98] data-[preview-state=active]:scale-[0.98]\",\n ),\n};\n\n/**\n * The three control heights, 24 / 28 / 32 px, from `--spacing-ctl-*`.\n * Exported so the Capture download `<a href>` can wear the same recipe as a Button.\n */\nexport const buttonSizes: Record<ButtonSize, string> = {\n sm: \"h-ctl-sm px-2 gap-1 rounded-sm text-step-11\",\n md: \"h-ctl px-3 gap-1.5 rounded-md text-step-12\",\n lg: \"h-ctl-lg px-4 gap-2 rounded-md text-step-13\",\n};\n\nfunction Spinner() {\n return (\n <svg className=\"animate-spin size-3.5\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <circle className=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" strokeWidth=\"4\" />\n <path\n className=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z\"\n />\n </svg>\n );\n}\n\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(\n (\n { variant = \"secondary\", size = \"md\", loading, icon, children, className, disabled, ...props },\n ref,\n ) => {\n const isDisabled = disabled || loading;\n let leading: ReactNode = null;\n if (loading) leading = <Spinner />;\n else if (icon) leading = <span className=\"shrink-0\">{icon}</span>;\n return (\n <button\n ref={ref}\n disabled={isDisabled}\n // `aria-disabled` as well as `disabled`: assistive tech announces the\n // state even where the native attribute is filtered out of the tree.\n aria-disabled={isDisabled || undefined}\n className={cn(buttonBase, buttonVariants[variant], buttonSizes[size], className)}\n {...props}\n >\n {leading}\n {children}\n </button>\n );\n },\n);\nButton.displayName = \"Button\";\n","/**\n * IconButton — Button's square sibling; shares its base/variant classes so\n * the two never drift. `aria-label` is required for screen readers.\n */\n\nimport { forwardRef } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { buttonBase, buttonVariants, type ButtonBaseProps, type ButtonSize } from \"./Button\";\nimport { cn } from \"./cn\";\n\ninterface IconButtonProps extends ButtonBaseProps {\n icon: ReactNode;\n \"aria-label\": string;\n}\n\n/** Square boxes on the same 24 / 28 / 32 px control heights as Button. */\nconst iconSizeStyles: Record<ButtonSize, string> = {\n sm: \"size-ctl-sm rounded-sm\",\n md: \"size-ctl rounded-md\",\n lg: \"size-ctl-lg rounded-md\",\n};\n\nexport const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(\n ({ icon, size = \"md\", variant = \"ghost\", className, disabled, ...props }, ref) => {\n return (\n <button\n ref={ref}\n disabled={disabled}\n aria-disabled={disabled || undefined}\n className={cn(buttonBase, buttonVariants[variant], iconSizeStyles[size], className)}\n {...props}\n >\n {icon}\n </button>\n );\n },\n);\nIconButton.displayName = \"IconButton\";\n","/**\n * Tabs — Base UI's tabs wearing Studio's tokens; replaces six hand-rolled\n * strips with one implementation (roving tabindex, arrow/Home/End, `aria-controls`).\n */\n\nimport { Tabs as BaseTabs } from \"@base-ui/react/tabs\";\nimport type { ComponentPropsWithoutRef, ElementType } from \"react\";\nimport { cn } from \"./cn\";\nimport type { PreviewState } from \"./Button\";\n\n/**\n * Base UI lets `className` be a function of the part's state. Studio's parts\n * merge a string with `cn`, so the string form is the one they accept.\n */\ntype StyledProps<T extends ElementType> = Omit<ComponentPropsWithoutRef<T>, \"className\"> & {\n className?: string;\n};\n\n/**\n * Groups a tab list with its panels. Uncontrolled by default; pass `value` and\n * `onValueChange` for a strip whose selection lives in a store.\n */\nexport function Tabs(props: ComponentPropsWithoutRef<typeof BaseTabs.Root>) {\n return <BaseTabs.Root {...props} />;\n}\n\n/** The strip. Give it an `aria-label`: a tablist with no name is unlabelled. */\nexport function TabsList({ className, ...props }: StyledProps<typeof BaseTabs.List>) {\n return (\n <BaseTabs.List\n // Arrow keys move selection, matching today's sidebar strip.\n activateOnFocus\n className={cn(\"inline-flex items-center gap-0.5 rounded-lg bg-surface-alt p-1\", className)}\n {...props}\n />\n );\n}\n\ninterface TabProps extends StyledProps<typeof BaseTabs.Tab> {\n value: string;\n \"data-preview-state\"?: PreviewState;\n}\n\n/**\n * aria-selected drives the selected look (Base UI sets it) so assistive tech\n * and sighted users match. data-preview-state is for gallery shots only.\n */\nexport function Tab({ value, className, ...props }: TabProps) {\n return (\n <BaseTabs.Tab\n value={value}\n data-tab-id={value}\n className={cn(\n \"inline-flex h-ctl-sm cursor-pointer select-none items-center justify-center rounded-sm px-2.5\",\n \"text-step-11 font-semibold whitespace-nowrap text-text-3\",\n \"transition-[background-color,color] ease-out-quint duration-hover\",\n \"hover:text-text-1 data-[preview-state=hover]:text-text-1\",\n \"aria-selected:bg-hover aria-selected:text-text-0\",\n \"outline-hidden focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-accent\",\n \"data-[preview-state=focus]:outline-solid data-[preview-state=focus]:outline-2 data-[preview-state=focus]:outline-offset-1 data-[preview-state=focus]:outline-accent\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/** The panel for one tab. Base UI wires `aria-labelledby` back to its tab. */\nexport function TabPanel({ className, ...props }: StyledProps<typeof BaseTabs.Panel>) {\n return <BaseTabs.Panel className={cn(\"outline-hidden\", className)} {...props} />;\n}\n","import { useId, type SVGProps } from \"react\";\n\nexport function HyperframesMark({ viewBox = \"0 0 100 100\", ...props }: SVGProps<SVGSVGElement>) {\n const gradientId = useId();\n const leftGradient = `${gradientId}-left`;\n const rightGradient = `${gradientId}-right`;\n return (\n <svg\n viewBox={viewBox}\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n {...props}\n >\n <g transform=\"translate(50 50)\" opacity=\".92\">\n <g transform=\"translate(-50 -50)\">\n <path\n d=\"M10.1851 57.8021L33.1145 73.8313C36.2202 75.9978 41.5173 73.5433 42.4816 69.4984L51.7611 30.4271C52.7253 26.3822 48.5802 23.9277 44.4602 26.0942L13.917 42.1235C6.96677 45.7676 4.97564 54.1579 10.1851 57.8021Z\"\n fill={`url(#${leftGradient})`}\n />\n <path\n d=\"M87.5129 57.5141L56.9696 73.5433C52.8371 75.7098 48.7046 73.2553 49.6688 69.2104L58.9483 30.1391C59.9125 26.0942 65.2097 23.6397 68.3154 25.8062L91.2447 41.8354C96.4668 45.4796 94.4631 53.8699 87.5129 57.5141Z\"\n fill={`url(#${rightGradient})`}\n />\n </g>\n </g>\n <defs>\n <linearGradient\n id={leftGradient}\n x1=\"48.5676\"\n y1=\"25\"\n x2=\"44.7804\"\n y2=\"71.9384\"\n gradientUnits=\"userSpaceOnUse\"\n >\n <stop stopColor=\"#06E3FA\" />\n <stop offset=\"1\" stopColor=\"#4FDB5E\" />\n </linearGradient>\n <linearGradient\n id={rightGradient}\n x1=\"54.8282\"\n y1=\"73.8392\"\n x2=\"72.0989\"\n y2=\"32.8932\"\n gradientUnits=\"userSpaceOnUse\"\n >\n <stop stopColor=\"#06E3FA\" />\n <stop offset=\"1\" stopColor=\"#4FDB5E\" />\n </linearGradient>\n </defs>\n </svg>\n );\n}\n","import { HyperframesMark } from \"./HyperframesMark\";\n\nexport interface HyperframesLoaderProps {\n /** Status text shown below the mark. */\n title: string;\n /** Optional secondary detail line. */\n detail?: string;\n /** Optional monospace third line for IDs, counts, or percentages. */\n mono?: string;\n /** Pixel size of the mark itself; status text scales independently. */\n size?: number;\n /** Optional normalized progress value from 0 to 1. */\n progress?: number;\n}\n\nexport function HyperframesLoader({\n title,\n detail,\n mono,\n size = 64,\n progress,\n}: HyperframesLoaderProps) {\n const boundedProgress =\n typeof progress === \"number\" && Number.isFinite(progress)\n ? Math.min(1, Math.max(0, progress))\n : undefined;\n const markFrameSize = Math.round(size * 1.16);\n\n return (\n <div className=\"hf-loader\" role=\"status\" draggable={false}>\n <div\n className=\"hf-loader-mark-frame\"\n style={{ width: markFrameSize, height: markFrameSize }}\n draggable={false}\n >\n <HyperframesMark className=\"hf-loader-mark\" width={size} height={size} />\n </div>\n <div className=\"hf-loader-title\">{title}</div>\n {detail && <div className=\"hf-loader-detail\">{detail}</div>}\n {boundedProgress !== undefined && (\n <div\n className=\"hf-loader-progress\"\n role=\"progressbar\"\n aria-valuemin={0}\n aria-valuemax={100}\n aria-valuenow={Math.round(boundedProgress * 100)}\n >\n <div\n className=\"hf-loader-progress__fill\"\n style={{ transform: `scaleX(${boundedProgress})` }}\n />\n </div>\n )}\n {mono && <div className=\"hf-loader-mono\">{mono}</div>}\n </div>\n );\n}\n\n// fallow-ignore-next-line unused-export\nexport function StatusFrame(props: HyperframesLoaderProps) {\n return (\n <div className=\"hf-frame\">\n <HyperframesLoader {...props} />\n </div>\n );\n}\n","/**\n * Menu and ContextMenu on Base UI. Dismissal is plain Base UI (capture-phase listeners survive the overlay's\n * `stopPropagation`); `trigger` is rendered, not wrapped; `container` exists for shadow roots.\n */\n\nimport { ContextMenu as BaseContextMenu } from \"@base-ui/react/context-menu\";\nimport { Menu as BaseMenu } from \"@base-ui/react/menu\";\nimport type { ComponentPropsWithoutRef, ElementType, ReactElement, ReactNode } from \"react\";\nimport { cn } from \"./cn\";\n\n/**\n * Base UI lets `className` be a function of the part's state. Studio's parts\n * merge a string with `cn`, so the string form is the one they accept.\n */\ntype StyledProps<T extends ElementType> = Omit<ComponentPropsWithoutRef<T>, \"className\"> & {\n className?: string;\n};\n\n/** Forces the settled open look for a gallery shot; CSS-only, open state stays controlled. */\nexport type PopupPreviewState = \"open\";\n\n/** Where the portal puts the popup. `null` keeps it inline, next to its trigger. */\ntype PortalContainer = ComponentPropsWithoutRef<typeof BaseMenu.Portal>[\"container\"];\n\n/** Matches Tooltip's gap from its trigger, and its viewport margin. */\nconst SIDE_OFFSET = 6;\nconst VIEWPORT_MARGIN = 8;\n\n/**\n * Enter and exit shared by menus, popovers, dropdowns, and tooltips.\n * The open duration class differs; the shape does not.\n * `data-starting-style`/`data-ending-style` are Base UI's transition attributes.\n */\nexport function floatingMotion(openDuration: \"duration-open\" | \"duration-tooltip\") {\n return cn(\n \"origin-[var(--transform-origin)] outline-hidden\",\n \"transition-[opacity,transform] ease-out-quint\",\n openDuration,\n \"data-[ending-style]:duration-close data-[ending-style]:ease-in\",\n \"data-[starting-style]:[opacity:var(--popup-enter-opacity)]\",\n \"data-[starting-style]:[scale:var(--popup-enter-scale)]\",\n \"data-[ending-style]:opacity-0\",\n );\n}\n\n/**\n * Chrome shared by every floating panel (`Popover` too); callers add their own shadow token.\n */\nexport const popupSurface = cn(\n \"rounded-lg border border-border-input bg-surface\",\n floatingMotion(\"duration-open\"),\n \"data-[preview-state=open]:opacity-100 data-[preview-state=open]:scale-100\",\n);\n\n/** Every floating list, menu, popover and tooltip sits on this layer, above panel chrome and modals. */\nexport const POPUP_LAYER = \"z-200\";\n\nconst menuPopup = cn(popupSurface, \"min-w-36 p-1 shadow-menu\");\n\n/** One row. `data-highlighted` is set by keyboard and pointer alike, so the seen row is the Enter row. */\nconst itemBase = cn(\n \"flex cursor-default select-none items-center justify-between gap-6 rounded-sm px-2 py-1.5\",\n \"text-step-11 whitespace-nowrap text-text-1\",\n \"outline-hidden transition-colors ease-out-quint duration-hover\",\n \"data-[highlighted]:bg-hover data-[highlighted]:text-text-0\",\n \"data-[preview-state=hover]:bg-hover data-[preview-state=hover]:text-text-0\",\n \"data-[disabled]:pointer-events-none data-[disabled]:opacity-40\",\n);\n\nconst itemDanger = cn(\n \"text-danger-ink data-[highlighted]:bg-danger/15 data-[highlighted]:text-danger-ink\",\n);\n\nexport type MenuItemTone = \"default\" | \"danger\";\n\ninterface PositionedProps {\n side?: \"top\" | \"bottom\" | \"left\" | \"right\";\n align?: \"start\" | \"center\" | \"end\";\n sideOffset?: number;\n /** Portal target. Pass the shadow root when the trigger lives in one. */\n container?: PortalContainer;\n /** Names the popup for assistive tech. A menu with no name is unlabelled. */\n \"aria-label\"?: string;\n className?: string;\n \"data-preview-state\"?: PopupPreviewState;\n}\n\ninterface MenuProps\n extends PositionedProps, Omit<ComponentPropsWithoutRef<typeof BaseMenu.Root>, \"children\"> {\n /** A single element. It becomes the trigger; no wrapper is added around it. */\n trigger: ReactElement;\n /** The items: `MenuItem`, `MenuRadioGroup`, `MenuSeparator`. */\n children: ReactNode;\n}\n\n/**\n * A menu opened by its trigger. Uncontrolled by default; pass `open` and\n * `onOpenChange` for a menu whose state lives in a store.\n */\nexport function Menu({\n trigger,\n children,\n side = \"bottom\",\n align = \"start\",\n sideOffset = SIDE_OFFSET,\n container,\n className,\n \"aria-label\": ariaLabel,\n \"data-preview-state\": previewState,\n ...root\n}: MenuProps) {\n return (\n <BaseMenu.Root {...root}>\n <BaseMenu.Trigger render={trigger} />\n <BaseMenu.Portal container={container}>\n <BaseMenu.Positioner\n side={side}\n align={align}\n sideOffset={sideOffset}\n collisionPadding={VIEWPORT_MARGIN}\n className={POPUP_LAYER}\n >\n <BaseMenu.Popup\n aria-label={ariaLabel}\n data-preview-state={previewState}\n className={cn(menuPopup, className)}\n >\n {children}\n </BaseMenu.Popup>\n </BaseMenu.Positioner>\n </BaseMenu.Portal>\n </BaseMenu.Root>\n );\n}\n\ninterface ContextMenuProps\n extends PositionedProps, Omit<ComponentPropsWithoutRef<typeof BaseContextMenu.Root>, \"children\"> {\n /**\n * The right-clickable area, as the caller's own element. Its children are\n * kept: the trigger renders that element rather than wrapping it.\n */\n trigger: ReactElement;\n children: ReactNode;\n}\n\n/** Opened by right click or long press at the pointer, so `side` and `align` are not offered. */\nexport function ContextMenu({\n trigger,\n children,\n container,\n className,\n \"aria-label\": ariaLabel,\n \"data-preview-state\": previewState,\n ...root\n}: Omit<ContextMenuProps, \"side\" | \"align\" | \"sideOffset\">) {\n return (\n <BaseContextMenu.Root {...root}>\n <BaseContextMenu.Trigger render={trigger} />\n <BaseContextMenu.Portal container={container}>\n <BaseContextMenu.Positioner collisionPadding={VIEWPORT_MARGIN} className={POPUP_LAYER}>\n <BaseContextMenu.Popup\n aria-label={ariaLabel}\n data-preview-state={previewState}\n className={cn(menuPopup, className)}\n >\n {children}\n </BaseContextMenu.Popup>\n </BaseContextMenu.Positioner>\n </BaseContextMenu.Portal>\n </BaseContextMenu.Root>\n );\n}\n\n/** Keyboard shortcut hint, mono and tabular; exported for hints that are not a plain string. */\nexport function MenuShortcut({ className, ...props }: StyledProps<\"span\">) {\n return (\n <span\n className={cn(\"shrink-0 font-mono text-step-10 tabular-nums text-text-4\", className)}\n aria-hidden=\"true\"\n {...props}\n />\n );\n}\n\ninterface MenuItemProps extends StyledProps<typeof BaseMenu.Item> {\n /** Rendered as a dim mono hint on the trailing edge. Decorative. */\n shortcut?: string;\n /** `danger` for a destructive action (Delete, Remove). */\n tone?: MenuItemTone;\n \"data-preview-state\"?: \"hover\";\n}\n\n/** One action. `disabled` items are skipped by the arrow keys, not just dimmed. */\nexport function MenuItem({ shortcut, tone, className, children, ...props }: MenuItemProps) {\n return (\n <BaseMenu.Item className={cn(itemBase, tone === \"danger\" && itemDanger, className)} {...props}>\n <span className=\"truncate\">{children}</span>\n {shortcut ? <MenuShortcut>{shortcut}</MenuShortcut> : null}\n </BaseMenu.Item>\n );\n}\n\n/** Single-choice group; selection is `aria-checked` on each item, which the dot indicator draws from. */\nexport function MenuRadioGroup(props: ComponentPropsWithoutRef<typeof BaseMenu.RadioGroup>) {\n return <BaseMenu.RadioGroup {...props} />;\n}\n\ninterface MenuRadioItemProps extends StyledProps<typeof BaseMenu.RadioItem> {\n \"data-preview-state\"?: \"hover\";\n}\n\n/** One choice in a `MenuRadioGroup`. The dot renders only while it is checked. */\nexport function MenuRadioItem({ className, children, ...props }: MenuRadioItemProps) {\n return (\n <BaseMenu.RadioItem className={cn(itemBase, className)} {...props}>\n <span className=\"truncate\">{children}</span>\n {/* Fixed box so a checked and an unchecked row keep the same width. */}\n <span className=\"flex size-3 shrink-0 items-center justify-center\">\n <BaseMenu.RadioItemIndicator className=\"size-1.5 rounded-full bg-accent\" />\n </span>\n </BaseMenu.RadioItem>\n );\n}\n\n/** An on/off row; the tick renders only while `checked`. */\nexport function MenuCheckboxItem({\n className,\n children,\n ...props\n}: StyledProps<typeof BaseMenu.CheckboxItem>) {\n return (\n <BaseMenu.CheckboxItem className={cn(itemBase, className)} {...props}>\n <span className=\"truncate\">{children}</span>\n <span className=\"flex size-3 shrink-0 items-center justify-center text-accent-ink\">\n <BaseMenu.CheckboxItemIndicator>✓</BaseMenu.CheckboxItemIndicator>\n </span>\n </BaseMenu.CheckboxItem>\n );\n}\n\n/** A hairline between two groups of items. */\nexport function MenuSeparator({ className, ...props }: StyledProps<typeof BaseMenu.Separator>) {\n return <BaseMenu.Separator className={cn(\"my-1 h-px bg-hairline\", className)} {...props} />;\n}\n","/**\n * Tooltip: Base UI's tooltip in Studio tokens. The trigger is a box-less\n * `display: contents` wrapper so a disabled control still gets hover.\n */\n\nimport { Tooltip as BaseTooltip } from \"@base-ui/react/tooltip\";\nimport { cloneElement, useId, useRef, useState, type ReactElement } from \"react\";\nimport { cn } from \"./cn\";\nimport { floatingMotion, POPUP_LAYER } from \"./Menu\";\n\ninterface TooltipProps {\n label: string;\n /** A single element, wrapped in a box-less span so a disabled one still gets hover. */\n children: ReactElement<{ \"aria-describedby\"?: string }>;\n /** Hover delay in ms. */\n delay?: number;\n disabled?: boolean;\n side?: \"top\" | \"bottom\" | \"left\" | \"right\";\n}\n\n/** Matches the old bubble's gap from its trigger, and its viewport margin. */\nconst SIDE_OFFSET = 6;\nconst VIEWPORT_MARGIN = 8;\n\nexport function Tooltip({\n label,\n children,\n delay = 400,\n side = \"top\",\n disabled = false,\n}: TooltipProps) {\n const [open, setOpen] = useState(false);\n const boxRef = useRef<HTMLSpanElement>(null);\n const tooltipId = useId();\n\n return (\n <BaseTooltip.Root open={open} onOpenChange={setOpen} disabled={disabled}>\n <BaseTooltip.Trigger delay={delay} render={<span ref={boxRef} className=\"contents\" />}>\n {cloneElement(children, {\n \"aria-describedby\": open && !disabled ? tooltipId : children.props[\"aria-describedby\"],\n })}\n </BaseTooltip.Trigger>\n <BaseTooltip.Portal>\n <BaseTooltip.Positioner\n anchor={() => boxRef.current?.firstElementChild ?? null}\n side={side}\n sideOffset={SIDE_OFFSET}\n collisionPadding={VIEWPORT_MARGIN}\n className={POPUP_LAYER}\n >\n <BaseTooltip.Popup\n id={tooltipId}\n role=\"tooltip\"\n className={cn(\n \"pointer-events-none rounded-md border border-border-input bg-surface px-2 py-1 text-step-10 font-medium text-text-1 whitespace-nowrap shadow-menu\",\n floatingMotion(\"duration-tooltip\"),\n )}\n >\n {label}\n </BaseTooltip.Popup>\n </BaseTooltip.Positioner>\n </BaseTooltip.Portal>\n </BaseTooltip.Root>\n );\n}\n","/**\n * Popover: `Menu`'s floating chrome (`popupSurface`) without item semantics, so arrow keys\n * stay with whatever is focused inside (rename inputs, forms). Only the shadow differs.\n */\n\nimport { Popover as BasePopover } from \"@base-ui/react/popover\";\nimport type { ComponentPropsWithoutRef, ReactElement, ReactNode } from \"react\";\nimport { cn } from \"./cn\";\nimport { POPUP_LAYER, popupSurface, type PopupPreviewState } from \"./Menu\";\n\ntype PortalContainer = ComponentPropsWithoutRef<typeof BasePopover.Portal>[\"container\"];\n\n/** Matches Menu's gap from its trigger, and its viewport margin. */\nconst SIDE_OFFSET = 6;\nconst VIEWPORT_MARGIN = 8;\n\n/** The arrow's 14x8 box overlaps the popup's border by 1px, so its fill hides the border where it meets the popup. */\nconst ARROW_PX = 7;\nconst ARROW_CORNER_CLEARANCE = 10;\nconst arrowPlace = cn(\n \"data-[side=bottom]:-top-[7px] data-[side=top]:-bottom-[7px] data-[side=top]:rotate-180\",\n \"data-[side=left]:-right-[10px] data-[side=left]:rotate-90\",\n \"data-[side=right]:-left-[10px] data-[side=right]:-rotate-90\",\n);\n\ninterface PopoverProps extends Omit<ComponentPropsWithoutRef<typeof BasePopover.Root>, \"children\"> {\n /** A single element. It becomes the trigger; no wrapper is added around it. */\n trigger: ReactElement;\n /** Arbitrary content. The popover owns none of its keys. */\n children: ReactNode;\n side?: \"top\" | \"bottom\" | \"left\" | \"right\";\n align?: \"start\" | \"center\" | \"end\";\n sideOffset?: number;\n /** Points the popup at its trigger. Off by default; the default gap from the trigger grows by the arrow's height. */\n arrow?: boolean;\n /** Portal target. Pass the shadow root when the trigger lives in one. */\n container?: PortalContainer;\n /** Names the popup for assistive tech. */\n \"aria-label\"?: string;\n /**\n * What to focus on open. Defaults to Base UI's behaviour (the first tabbable\n * element), which is what a rename field wants.\n */\n initialFocus?: ComponentPropsWithoutRef<typeof BasePopover.Popup>[\"initialFocus\"];\n className?: string;\n \"data-preview-state\"?: PopupPreviewState;\n}\n\nexport function Popover({\n trigger,\n children,\n side = \"bottom\",\n align = \"center\",\n arrow = false,\n sideOffset = arrow ? SIDE_OFFSET + ARROW_PX : SIDE_OFFSET,\n container,\n className,\n initialFocus,\n \"aria-label\": ariaLabel,\n \"data-preview-state\": previewState,\n ...root\n}: PopoverProps) {\n return (\n <BasePopover.Root {...root}>\n <BasePopover.Trigger render={trigger} />\n <BasePopover.Portal container={container}>\n <BasePopover.Positioner\n side={side}\n align={align}\n sideOffset={sideOffset}\n collisionPadding={VIEWPORT_MARGIN}\n arrowPadding={ARROW_CORNER_CLEARANCE}\n className={POPUP_LAYER}\n >\n <BasePopover.Popup\n aria-label={ariaLabel}\n initialFocus={initialFocus}\n data-preview-state={previewState}\n className={cn(popupSurface, \"p-3 text-step-11 text-text-1 shadow-popover\", className)}\n >\n {arrow && (\n <BasePopover.Arrow className={arrowPlace}>\n <svg width=\"14\" height=\"8\" viewBox=\"0 0 14 8\" aria-hidden=\"true\" className=\"block\">\n <path d=\"M0 8 L7 1 L14 8\" className=\"fill-surface stroke-border-input\" />\n </svg>\n </BasePopover.Arrow>\n )}\n {children}\n </BasePopover.Popup>\n </BasePopover.Positioner>\n </BasePopover.Portal>\n </BasePopover.Root>\n );\n}\n","/**\n * Input: a boxed text field. Typing edits a local draft; Enter and blur commit it,\n * Escape abandons it. `onTrack` fires once per commit, never per keystroke.\n */\n\nimport { Input as BaseInput } from \"@base-ui/react/input\";\nimport { useEffect, useRef, useState, type ComponentPropsWithoutRef } from \"react\";\nimport { cn } from \"./cn\";\nimport type { PreviewState } from \"./Button\";\n\n/**\n * Shared field look (also used by NumberField and Select). Each interactive state is written\n * twice, real and `data-[preview-state=...]`; `valueControls.test.tsx` asserts the lists match.\n */\nexport const fieldBase = cn(\n \"flex min-w-0 items-center gap-1.5 h-ctl px-2 rounded-md\",\n \"bg-input border border-border-input text-step-11 font-medium text-text-1\",\n \"transition-[border-color,background-color] ease-standard duration-focus\",\n \"hover:border-border-strong data-[preview-state=hover]:border-border-strong\",\n \"outline-hidden\",\n \"focus-within:outline-solid focus-within:outline-2 focus-within:outline-offset-1 focus-within:outline-accent\",\n \"data-[preview-state=focus]:outline-solid data-[preview-state=focus]:outline-2 data-[preview-state=focus]:outline-offset-1 data-[preview-state=focus]:outline-accent\",\n // `aria-invalid` is not one of Tailwind's built-in aria variants, so the\n // arbitrary form is the one that compiles. The token gate would have caught\n // the bare `aria-invalid:` spelling as a class that resolves to nothing.\n \"aria-[invalid]:border-danger-ink\",\n \"has-[:disabled]:opacity-40 has-[:disabled]:cursor-not-allowed\",\n);\n\n/** The text itself, inside the box. The box owns the border and the ring. */\nexport const fieldText = cn(\n \"min-w-0 w-full bg-transparent text-inherit outline-hidden\",\n \"placeholder:text-text-5 disabled:cursor-not-allowed\",\n);\n\nexport interface InputProps extends Omit<\n ComponentPropsWithoutRef<\"input\">,\n \"value\" | \"onChange\" | \"className\"\n> {\n /** The committed value. A draft lives inside until Enter or blur. */\n value: string;\n /** Called with the draft on Enter and on blur, only when it really changed. */\n onCommit: (next: string) => void;\n /** Called once per committed change, for design-input telemetry. */\n onTrack?: () => void;\n /** Marks the field invalid: red boundary and `aria-invalid`. */\n invalid?: boolean;\n className?: string;\n \"data-preview-state\"?: PreviewState;\n}\n\nexport function Input({\n value,\n onCommit,\n onTrack,\n invalid,\n disabled,\n className,\n \"data-preview-state\": previewState,\n ...props\n}: InputProps) {\n const [draft, setDraft] = useState(value);\n const valueRef = useRef(value);\n valueRef.current = value;\n const dirtyRef = useRef(false);\n\n // An external write (a seek, an undo, another panel) is authoritative, but it\n // must not eat what the user is halfway through typing.\n useEffect(() => {\n if (!dirtyRef.current) setDraft(value);\n }, [value]);\n\n const commit = (next: string) => {\n dirtyRef.current = false;\n if (next === valueRef.current) return;\n onCommit(next);\n onTrack?.();\n };\n\n return (\n <div\n className={cn(fieldBase, className)}\n aria-invalid={invalid || undefined}\n data-preview-state={previewState}\n >\n <BaseInput\n value={draft}\n disabled={disabled}\n aria-invalid={invalid || undefined}\n className={fieldText}\n onValueChange={(next) => {\n dirtyRef.current = true;\n setDraft(next);\n }}\n onBlur={() => commit(draft)}\n onKeyDown={(event) => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n commit(draft);\n return;\n }\n if (event.key === \"Escape\") {\n // Abandon the draft rather than leave it for the blur commit.\n event.preventDefault();\n event.stopPropagation();\n dirtyRef.current = false;\n setDraft(valueRef.current);\n event.currentTarget.blur();\n }\n }}\n {...props}\n />\n </div>\n );\n}\n","/**\n * NumberField: the inspector's metric control. Commits only on blur, Enter, or a settled step (KTD11),\n * never per keystroke. The unit is a sibling of the input; unparseable text shows a red boundary and commits nothing.\n */\n\nimport { NumberField as BaseNumberField } from \"@base-ui/react/number-field\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { cn } from \"./cn\";\nimport { fieldBase, fieldText } from \"./Input\";\nimport type { PreviewState } from \"./Button\";\n\nexport interface NumberFieldProps {\n /** Accessible name. The inspector's row label is the usual one. */\n label: string;\n /** The committed value. */\n value: number;\n /** Called when a commit boundary produces a value different from `value`. */\n onCommit: (next: number) => void;\n /** Called once per committed change, for design-input telemetry. */\n onTrack?: () => void;\n /** Rendered beside the number: \"px\", \"%\", \"deg\". Never part of the text. */\n unit?: string;\n min?: number;\n max?: number;\n step?: number;\n disabled?: boolean;\n className?: string;\n \"data-preview-state\"?: PreviewState;\n}\n\nexport function NumberField({\n label,\n value,\n onCommit,\n onTrack,\n unit,\n min,\n max,\n step,\n disabled,\n className,\n \"data-preview-state\": previewState,\n}: NumberFieldProps) {\n const [draft, setDraft] = useState<number | null>(value);\n const [invalid, setInvalid] = useState(false);\n const inputRef = useRef<HTMLInputElement>(null);\n // What was last handed to `onCommit`, which is not the same as `value`: a\n // blur can fire both Base UI's commit and ours in one turn, before the parent\n // has re-rendered with the new prop, and the second must be a no-op.\n const committedRef = useRef(value);\n const valueRef = useRef(value);\n valueRef.current = value;\n\n useEffect(() => {\n committedRef.current = value;\n setDraft(value);\n setInvalid(false);\n }, [value]);\n\n const commit = (next: number | null) => {\n if (next === null || !Number.isFinite(next)) {\n setInvalid(true);\n return;\n }\n setInvalid(false);\n if (next === committedRef.current) return;\n committedRef.current = next;\n onCommit(next);\n onTrack?.();\n };\n\n /** Enter commits what is on screen, which Base UI has not parsed for us yet. */\n const commitFromText = () => {\n const text = inputRef.current?.value.trim() ?? \"\";\n commit(text === \"\" ? null : Number(text));\n };\n\n return (\n <BaseNumberField.Root\n value={draft}\n min={min}\n max={max}\n step={step}\n disabled={disabled}\n onValueChange={(next) => {\n setDraft(next);\n if (next !== null) setInvalid(false);\n }}\n onValueCommitted={(next) => commit(next)}\n >\n <BaseNumberField.Group\n className={cn(fieldBase, className)}\n aria-invalid={invalid || undefined}\n data-preview-state={previewState}\n >\n <BaseNumberField.Input\n ref={inputRef}\n aria-label={label}\n aria-invalid={invalid || undefined}\n className={cn(fieldText, \"tabular-nums\")}\n onKeyDown={(event) => {\n if (event.key !== \"Enter\") return;\n event.preventDefault();\n commitFromText();\n }}\n onBlur={() => {\n // Base UI's own blur handler returns without committing when the\n // text does not parse. That is the only case left for us: a valid\n // blur has already arrived through `onValueCommitted`.\n const text = inputRef.current?.value.trim() ?? \"\";\n if (text === \"\" || !Number.isFinite(Number(text))) setInvalid(true);\n }}\n />\n {unit && (\n <span className=\"shrink-0 select-none text-step-10 text-text-4\" aria-hidden=\"true\">\n {unit}\n </span>\n )}\n </BaseNumberField.Group>\n </BaseNumberField.Root>\n );\n}\n","/**\n * Select: Base UI select in Input's box. The trigger keeps `role=\"combobox\"`, which both hotkey selector\n * lists match. Options are data, not children, so typeahead and the value display can read labels.\n */\n\nimport { Select as BaseSelect } from \"@base-ui/react/select\";\nimport { cn } from \"./cn\";\nimport { fieldBase } from \"./Input\";\nimport { floatingMotion, POPUP_LAYER } from \"./Menu\";\nimport type { PreviewState } from \"./Button\";\n\nexport interface SelectOption {\n label: string;\n value: string;\n /** Offered but not choosable; the label stays visible so the reason stays visible too. */\n disabled?: boolean;\n}\n\nexport interface SelectProps {\n /** Accessible name for the trigger. */\n label: string;\n value: string;\n options: SelectOption[];\n /** Called when a different option is chosen. */\n onCommit: (next: string) => void;\n /** Called once per committed change, for design-input telemetry. */\n onTrack?: () => void;\n disabled?: boolean;\n className?: string;\n \"data-preview-state\"?: PreviewState;\n}\n\nexport function Select({\n label,\n value,\n options,\n onCommit,\n onTrack,\n disabled,\n className,\n \"data-preview-state\": previewState,\n}: SelectProps) {\n return (\n <BaseSelect.Root\n value={value}\n disabled={disabled}\n items={options}\n onValueChange={(next) => {\n const chosen = String(next);\n if (chosen === value) return;\n onCommit(chosen);\n onTrack?.();\n }}\n >\n <BaseSelect.Trigger\n aria-label={label}\n className={cn(\n fieldBase,\n \"w-full cursor-pointer justify-between text-left\",\n \"data-[popup-open]:border-border-strong\",\n className,\n )}\n data-preview-state={previewState}\n >\n <BaseSelect.Value className=\"truncate\" />\n <BaseSelect.Icon className=\"shrink-0 text-text-4\" aria-hidden=\"true\">\n <svg width=\"8\" height=\"5\" viewBox=\"0 0 8 5\" fill=\"none\">\n <path\n d=\"M1 1L4 4L7 1\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </BaseSelect.Icon>\n </BaseSelect.Trigger>\n\n <BaseSelect.Portal>\n <BaseSelect.Positioner sideOffset={4} alignItemWithTrigger={false} className={POPUP_LAYER}>\n <BaseSelect.Popup\n className={cn(\n \"min-w-[var(--anchor-width)] rounded-md border border-border bg-surface py-1 shadow-menu\",\n floatingMotion(\"duration-open\"),\n )}\n >\n <BaseSelect.List>\n {options.map((option) => (\n <BaseSelect.Item\n key={option.value}\n value={option.value}\n disabled={option.disabled}\n className={cn(\n \"flex h-ctl-sm cursor-pointer select-none items-center gap-2 px-2.5\",\n \"text-step-11 text-text-2 outline-hidden\",\n \"data-[highlighted]:bg-hover data-[highlighted]:text-text-0\",\n \"data-[selected]:text-text-0\",\n \"data-[disabled]:cursor-not-allowed data-[disabled]:opacity-40\",\n )}\n >\n <BaseSelect.ItemText className=\"truncate\">{option.label}</BaseSelect.ItemText>\n </BaseSelect.Item>\n ))}\n </BaseSelect.List>\n </BaseSelect.Popup>\n </BaseSelect.Positioner>\n </BaseSelect.Portal>\n </BaseSelect.Root>\n );\n}\n","/**\n * Slider: `onPreview` fires while the thumb moves and writes nothing durable; `onCommit` (with `onTrack`, once)\n * fires at a boundary. Right-click or Escape aborts a drag via `eventDetails.cancel()` and restores the start value.\n */\n\nimport { Slider as BaseSlider } from \"@base-ui/react/slider\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { cn } from \"./cn\";\nimport type { PreviewState } from \"./Button\";\n\nexport interface SliderProps {\n /** Accessible name for the thumb. The inspector's row label is the usual one. */\n label: string;\n /** The committed value. */\n value: number;\n min: number;\n max: number;\n step?: number;\n /** Every intermediate value while dragging or stepping. Not a commit. */\n onPreview?: (next: number) => void;\n /** Called at a commit boundary when the value really changed. */\n onCommit: (next: number) => void;\n /** Called once per commit boundary, for design-input telemetry. */\n onTrack?: () => void;\n disabled?: boolean;\n className?: string;\n \"data-preview-state\"?: PreviewState;\n}\n\nconst first = (next: number | number[]): number => (Array.isArray(next) ? next[0] : next);\n\nexport function Slider({\n label,\n value,\n min,\n max,\n step,\n onPreview,\n onCommit,\n onTrack,\n disabled,\n className,\n \"data-preview-state\": previewState,\n}: SliderProps) {\n const [draft, setDraft] = useState(value);\n const draggingRef = useRef(false);\n const abortedRef = useRef(false);\n // The committed value the current gesture started from, so an abort has\n // something to put back.\n const dragStartRef = useRef(value);\n // What was last handed to `onCommit`. A gesture's own commit lands before the\n // parent re-renders, so deduping against the `value` prop would double-fire.\n const committedRef = useRef(value);\n const valueRef = useRef(value);\n valueRef.current = value;\n\n // An external write wins, except mid-drag: the echo is a frame behind, and\n // applying it would snap the thumb backwards under the pointer.\n useEffect(() => {\n if (draggingRef.current) return;\n committedRef.current = value;\n setDraft(value);\n }, [value]);\n\n const commit = (next: number) => {\n if (next === committedRef.current) return;\n committedRef.current = next;\n onCommit(next);\n };\n\n const abort = () => {\n if (!draggingRef.current) return;\n abortedRef.current = true;\n const start = dragStartRef.current;\n setDraft(start);\n onPreview?.(start);\n commit(start);\n };\n\n return (\n <BaseSlider.Root\n value={draft}\n min={min}\n max={max}\n step={step}\n disabled={disabled}\n onValueChange={(next, details) => {\n if (abortedRef.current) {\n // The pointer is still down after a right-click abort. Base UI would\n // otherwise keep applying moves to a gesture the user cancelled.\n details.cancel();\n return;\n }\n const n = first(next);\n setDraft(n);\n onPreview?.(n);\n }}\n onValueCommitted={(next) => {\n draggingRef.current = false;\n if (abortedRef.current) {\n abortedRef.current = false;\n return;\n }\n commit(first(next));\n onTrack?.();\n }}\n className={cn(\"flex min-w-0 items-center\", className)}\n data-preview-state={previewState}\n >\n <BaseSlider.Control\n // A stable hook for the drag tests and for the screenshot script. Base\n // UI's own marker on this part only exists before hydration.\n data-slider-control=\"true\"\n // h-6 is the 24x24 WCAG 2.2 (2.5.8) target: the visible track stays\n // 2px, only the pointer box grows.\n className=\"flex h-6 w-full min-w-0 touch-none select-none items-center\"\n onPointerDown={() => {\n draggingRef.current = true;\n abortedRef.current = false;\n dragStartRef.current = valueRef.current;\n }}\n onContextMenu={(event) => {\n if (!draggingRef.current) return;\n event.preventDefault();\n abort();\n }}\n onKeyDown={(event) => {\n if (event.key !== \"Escape\" || !draggingRef.current) return;\n event.preventDefault();\n abort();\n }}\n >\n <BaseSlider.Track className=\"h-0.5 w-full rounded-full bg-hover\">\n <BaseSlider.Indicator className=\"rounded-full bg-text-5\" />\n <BaseSlider.Thumb\n aria-label={label}\n className={cn(\n // The ring separates the thumb from the track behind it. A drop\n // shadow would need a raw colour literal, which R5 does not allow\n // outside the token file, and no shadow token fits a 10px dot.\n \"size-2.5 rounded-full bg-text-0 ring-2 ring-bg-1\",\n \"cursor-grab active:cursor-grabbing data-[preview-state=active]:cursor-grabbing\",\n \"hover:scale-110 data-[preview-state=hover]:scale-110\",\n \"transition-transform ease-standard duration-hover\",\n \"has-[:focus-visible]:outline-solid has-[:focus-visible]:outline-2 has-[:focus-visible]:outline-offset-2 has-[:focus-visible]:outline-accent\",\n \"data-[preview-state=focus]:outline-solid data-[preview-state=focus]:outline-2 data-[preview-state=focus]:outline-offset-2 data-[preview-state=focus]:outline-accent\",\n )}\n />\n </BaseSlider.Track>\n </BaseSlider.Control>\n </BaseSlider.Root>\n );\n}\n","/**\n * Toggle: a switch, not a checkbox (immediate effect). `role=\"switch\"` needed an entry in\n * `typingTarget.ts` so Space is not leaked to the global playback shortcut.\n */\n\nimport { Switch } from \"@base-ui/react/switch\";\nimport { cn } from \"./cn\";\nimport type { PreviewState } from \"./Button\";\n\nexport interface ToggleProps {\n /** Accessible name. */\n label: string;\n checked: boolean;\n /** Called on every flip. */\n onCommit: (next: boolean) => void;\n /** Called once per flip, for design-input telemetry. */\n onTrack?: () => void;\n disabled?: boolean;\n className?: string;\n \"data-preview-state\"?: PreviewState;\n}\n\nexport function Toggle({\n label,\n checked,\n onCommit,\n onTrack,\n disabled,\n className,\n \"data-preview-state\": previewState,\n}: ToggleProps) {\n return (\n <Switch.Root\n aria-label={label}\n checked={checked}\n disabled={disabled}\n onCheckedChange={(next) => {\n onCommit(next);\n onTrack?.();\n }}\n data-preview-state={previewState}\n className={cn(\n \"relative inline-flex h-4 w-7 shrink-0 cursor-pointer items-center rounded-full p-0.5\",\n \"border border-border-input bg-input\",\n \"transition-[background-color,border-color] ease-standard duration-press\",\n \"hover:border-border-strong data-[preview-state=hover]:border-border-strong\",\n \"data-[checked]:border-accent data-[checked]:bg-accent\",\n \"outline-hidden focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-accent\",\n \"data-[preview-state=focus]:outline-solid data-[preview-state=focus]:outline-2 data-[preview-state=focus]:outline-offset-1 data-[preview-state=focus]:outline-accent\",\n \"disabled:cursor-not-allowed disabled:opacity-40\",\n className,\n )}\n >\n <Switch.Thumb\n className={cn(\n \"size-3 rounded-full bg-text-3\",\n \"transition-[transform,background-color] ease-standard duration-press\",\n \"data-[checked]:translate-x-3 data-[checked]:bg-bg-0\",\n )}\n />\n </Switch.Root>\n );\n}\n"],"mappings":"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","names":["forwardRef","jsx","forwardRef","jsx","jsx","jsxs","jsx","jsxs","jsx","jsxs","useId","jsx","jsxs","SIDE_OFFSET","VIEWPORT_MARGIN","useId","jsx","jsxs","SIDE_OFFSET","VIEWPORT_MARGIN","useRef","useState","jsx","useState","useRef","useEffect","useRef","useState","jsx","jsxs","useState","useRef","useEffect","jsx","jsxs","useEffect","useRef","useState","jsx","jsxs","useState","useRef","useEffect","jsx"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/sourceScopedSelectorIndex.ts","../src/components/editor/domEditingDom.ts","../src/components/editor/domEditingTypes.ts","../src/player/lib/timelineText.ts","../src/utils/rounding.ts","../src/components/editor/colorValue.ts","../src/player/lib/playbackAdapter.ts","../src/player/lib/timelineElementHelpers.ts","../src/player/lib/storedVolume.ts","../src/utils/projectRouting.ts"],"sourcesContent":["/**\n * Occurrence index for a selector within its source document.\n *\n * The preview flattens multiple composition files into one DOM, so a raw\n * `querySelectorAll` index is not a stable source-file identity. Callers supply\n * their existing source resolver; this helper alone owns occurrence scoping.\n */\n\n/** Every element matching one selector, mapped to the source file it belongs to\n * and its occurrence index within that file. One document query fills it. */\ntype SelectorOccurrences = Map<Element, { scope: string; index: number }>;\n\ninterface SelectorIndexPass {\n doc: Document;\n bySelector: Map<string, SelectorOccurrences>;\n}\n\nlet activePass: SelectorIndexPass | null = null;\n\n/**\n * Share one occurrence index per selector across every\n * `getSourceScopedSelectorIndex` call made synchronously inside `run`.\n *\n * Unshared, each call runs its own whole-document `querySelectorAll(selector)`\n * and resolves the source file of every match. A rebuild that walks n elements\n * sharing a selector therefore does n whole-document queries and n² resolver\n * calls — a composition of repeated cards or tiles is exactly that shape. One\n * index per selector makes the same pass linear.\n *\n * Correctness rests on the pass being ONE synchronous walk of a document that\n * does not change under it: nothing invalidates the index, so `run` must not\n * mutate `doc`, and every call inside it must resolve source files the same way\n * (the resolver may be a fresh closure per call, as long as it answers the\n * same). Outside a pass the index is per-call, exactly as before.\n */\nexport function withSelectorIndexPass<T>(doc: Document, run: () => T): T {\n const previous = activePass;\n activePass = { doc, bySelector: new Map() };\n try {\n return run();\n } finally {\n activePass = previous;\n }\n}\n\nfunction buildOccurrences(\n doc: Document,\n selector: string,\n resolveSourceFile: (candidate: Element) => string | undefined,\n): SelectorOccurrences {\n const occurrences: SelectorOccurrences = new Map();\n const counts = new Map<string, number>();\n for (const candidate of doc.querySelectorAll(selector)) {\n const scope = resolveSourceFile(candidate) ?? \"index.html\";\n const index = counts.get(scope) ?? 0;\n counts.set(scope, index + 1);\n occurrences.set(candidate, { scope, index });\n }\n return occurrences;\n}\n\nexport function getSourceScopedSelectorIndex(\n doc: Document,\n el: Element,\n selector: string | undefined,\n sourceFile: string | undefined,\n resolveSourceFile: (candidate: Element) => string | undefined,\n): number | undefined {\n if (!selector || selector.startsWith(\"#\") || selector.startsWith(\"[data-composition-id=\")) {\n return undefined;\n }\n\n try {\n const pass = activePass?.doc === doc ? activePass : null;\n let occurrences = pass?.bySelector.get(selector);\n if (!occurrences) {\n occurrences = buildOccurrences(doc, selector, resolveSourceFile);\n pass?.bySelector.set(selector, occurrences);\n }\n // An element outside the requested scope is not in that scope's occurrence\n // run at all, which is the `indexOf` miss the filtered form returned before.\n const hit = occurrences.get(el);\n return hit && hit.scope === (sourceFile ?? \"index.html\") ? hit.index : undefined;\n } catch {\n return undefined;\n }\n}\n","/**\n * Low-level DOM primitives: type guards, style getters, CSS escaping,\n * selector utilities, and composition source resolution.\n * No imports from other domEditing* modules — safe to import from anywhere.\n */\nimport { COLOR_GRADING_SOURCE_HIDDEN_ATTR } from \"@hyperframes/core/color-grading\";\nimport { getSourceScopedSelectorIndex } from \"../../utils/sourceScopedSelectorIndex\";\nimport { CURATED_STYLE_PROPERTIES } from \"./domEditingTypes\";\n\n// ─── Type guard ───────────────────────────────────────────────────────────────\n\nexport function isHtmlElement(value: unknown): value is HTMLElement {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"nodeType\" in value &&\n typeof (value as { nodeType?: unknown }).nodeType === \"number\" &&\n (value as { nodeType: number }).nodeType === 1\n );\n}\n\n// ─── Style parsing ────────────────────────────────────────────────────────────\n\n// Single source of truth lives in @hyperframes/core/editing so the studio\n// callers and the core resolver can't drift. Re-exported here to keep this\n// module's public surface (6 studio callers import parsePx from it).\nexport { parsePx } from \"@hyperframes/core/editing\";\n\nexport function isTextBearingTag(tagName: string): boolean {\n return [\"div\", \"span\", \"p\", \"strong\", \"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\"].includes(tagName);\n}\n\nexport function isEditableTextLeaf(el: HTMLElement): boolean {\n return isTextBearingTag(el.tagName.toLowerCase()) && el.children.length === 0;\n}\n\n/** Does this node render AT ALL, ignoring what it inherits? Sole owner of the\n * rule; the walk below only decides which nodes to ask it about. */\nfunction elementRendersItself(win: Window, el: HTMLElement): boolean {\n const computed = win.getComputedStyle(el);\n if (computed.display === \"none\" || computed.visibility === \"hidden\") return false;\n const opacity = Number.parseFloat(computed.opacity);\n return !(\n Number.isFinite(opacity) &&\n opacity <= 0.01 &&\n !el.hasAttribute(COLOR_GRADING_SOURCE_HIDDEN_ATTR)\n );\n}\n\n/**\n * Does `el` render, given everything above it?\n *\n * `memo` is for a caller asking this about MANY elements in one synchronous\n * pass. Answers are a function of the node and its ancestors, and siblings\n * share almost all of their chain, so memoizing per node turns a walk per\n * element into one style read per node in the tree. It is only ever valid for\n * the length of one pass — the DOM cannot change under a pass, and every read\n * here is a read — so the caller creates it and drops it, and nothing survives\n * to be invalidated. Omitted, every call walks the chain itself.\n *\n * The ANSWER is what it always was. Which nodes get a style read is not: this\n * resolves top-down and stops at the first node that is out, where the previous\n * version resolved bottom-up and stopped at the first one that is out going the\n * other way. Same boolean for every input, a different (and, for a subtree\n * hidden near the root, smaller) set of reads.\n */\nexport function isElementVisibleThroughAncestors(\n el: HTMLElement,\n memo?: Map<HTMLElement, boolean>,\n): boolean {\n const win = el.ownerDocument.defaultView;\n if (!win) return true;\n // Up to the first node already answered for, then back down: a node's answer\n // needs its ancestors' first, and the topmost unanswered node is where the\n // chain of unknowns starts.\n const pending: HTMLElement[] = [];\n let inherited = true;\n for (let node: HTMLElement | null = el; node; node = node.parentElement) {\n const answered = memo?.get(node);\n if (answered !== undefined) {\n inherited = answered;\n break;\n }\n pending.push(node);\n }\n for (let i = pending.length - 1; i >= 0; i -= 1) {\n const node = pending[i]!;\n // Once an ancestor is out, its descendants are out with it, and asking the\n // platform about them would be a style read for an answer already known.\n inherited = inherited && elementRendersItself(win, node);\n memo?.set(node, inherited);\n }\n return inherited;\n}\n\n// ─── Style accessors ──────────────────────────────────────────────────────────\n\nexport function getCuratedComputedStyles(el: HTMLElement): Record<string, string> {\n const styles: Record<string, string> = {};\n const computed = el.ownerDocument.defaultView?.getComputedStyle(el);\n if (!computed) return styles;\n\n for (const prop of CURATED_STYLE_PROPERTIES) {\n const value = computed.getPropertyValue(prop);\n if (value) styles[prop] = value;\n }\n\n return styles;\n}\n\nexport function getInlineStyles(el: HTMLElement): Record<string, string> {\n const styles: Record<string, string> = {};\n for (const property of CURATED_STYLE_PROPERTIES) {\n const value = el.style.getPropertyValue(property);\n if (value) styles[property] = value;\n }\n return styles;\n}\n\nexport function getDataAttributes(el: HTMLElement): Record<string, string> {\n const attrs: Record<string, string> = {};\n for (const attr of el.attributes) {\n if (attr.name.startsWith(\"data-\")) {\n attrs[attr.name.slice(5)] = attr.value;\n }\n }\n return attrs;\n}\n\n// ─── DOM traversal ────────────────────────────────────────────────────────────\n\nexport function findClosestByAttribute(\n el: HTMLElement,\n attributeNames: string[],\n): HTMLElement | null {\n let current: HTMLElement | null = el;\n while (current) {\n const candidate = current;\n if (attributeNames.some((attribute) => candidate.hasAttribute(attribute))) {\n return candidate;\n }\n current = current.parentElement;\n }\n return null;\n}\n// ─── Composition source resolution ───────────────────────────────────────────\n\n// The runtime INLINES subcompositions and strips the source-file linkage from the\n// mounted root (it keeps `data-composition-id` but drops `data-composition-src`/\n// `-file`), so a subcomp element's DOM ancestors no longer say which file it came\n// from. This project-global map (composition-id → source file, built once from\n// index.html's clips — see NLEContext/EditorShell) recovers it. The studio loads one project at a\n// time, so module scope is the right lifetime; it's empty until set, in which case\n// resolution falls back to the historical attribute-only behavior.\nlet compositionSourceMap: Map<string, string> = new Map();\n// Bumped on every replacement so a consumer that memoizes a resolved source file\n// can tell that the map it resolved against is gone. The map is module state\n// with no DOM footprint, so nothing else can observe the change.\nlet compositionSourceMapRevision = 0;\n\nexport function setCompositionSourceMap(map: Map<string, string>): void {\n compositionSourceMap = map;\n compositionSourceMapRevision += 1;\n}\n\nexport function getCompositionSourceMapRevision(): number {\n return compositionSourceMapRevision;\n}\n\nfunction sourceFromCompositionId(ownerRoot: HTMLElement | null): string | undefined {\n if (!ownerRoot || compositionSourceMap.size === 0) return undefined;\n // The runtime may rename the mounted id to a runtime-unique one, preserving the\n // authored id on `data-hf-original-composition-id` — prefer that, then the current id.\n const authored = ownerRoot.getAttribute(\"data-hf-original-composition-id\");\n const current = ownerRoot.getAttribute(\"data-composition-id\");\n return (\n (authored ? compositionSourceMap.get(authored) : undefined) ??\n (current ? compositionSourceMap.get(current) : undefined)\n );\n}\n\nexport function getSourceFileForElement(\n el: HTMLElement,\n activeCompositionPath: string | null,\n): { sourceFile: string; compositionPath: string } {\n const sourceHost = findClosestByAttribute(el, [\"data-composition-file\", \"data-composition-src\"]);\n const ownerRoot = findClosestByAttribute(el, [\"data-composition-id\"]);\n const sourceFile =\n sourceHost?.getAttribute(\"data-composition-file\") ??\n sourceHost?.getAttribute(\"data-composition-src\") ??\n ownerRoot?.getAttribute(\"data-composition-file\") ??\n ownerRoot?.getAttribute(\"data-composition-src\") ??\n sourceFromCompositionId(ownerRoot) ??\n activeCompositionPath ??\n \"index.html\";\n\n return {\n sourceFile,\n compositionPath: sourceFile,\n };\n}\n\nexport function normalizeTimelineCompositionSource(value: string | undefined): string | undefined {\n const trimmed = value?.trim();\n if (!trimmed) return undefined;\n if (!/^([a-z][a-z0-9+.-]*:|\\/)/i.test(trimmed)) return trimmed;\n\n let pathname = trimmed;\n try {\n pathname = new URL(trimmed, \"http://studio.local\").pathname;\n } catch {\n pathname = trimmed;\n }\n\n for (const marker of [\"/preview/comp/\", \"/preview/\"]) {\n const markerIndex = pathname.indexOf(marker);\n if (markerIndex < 0) continue;\n const sourcePath = pathname.slice(markerIndex + marker.length).replace(/^\\/+/, \"\");\n return sourcePath || trimmed;\n }\n\n return trimmed;\n}\n\n// ─── CSS escaping ─────────────────────────────────────────────────────────────\n\nfunction escapeCssIdentifier(value: string): string {\n const css = globalThis.CSS as { escape?: (input: string) => string } | undefined;\n if (typeof css?.escape === \"function\") return css.escape(value);\n\n if (value === \"-\") return \"\\\\-\";\n\n let escaped = \"\";\n for (let index = 0; index < value.length; index += 1) {\n const char = value[index] ?? \"\";\n const code = char.charCodeAt(0);\n if (code === 0) {\n escaped += \"�\";\n continue;\n }\n\n const isDigit = code >= 48 && code <= 57;\n const isUpperAlpha = code >= 65 && code <= 90;\n const isLowerAlpha = code >= 97 && code <= 122;\n const isControl = (code >= 1 && code <= 31) || code === 127;\n const isLeadingDigit = index === 0 && isDigit;\n const isSecondDigitAfterDash = index === 1 && value.startsWith(\"-\") && isDigit;\n if (isControl || isLeadingDigit || isSecondDigitAfterDash) {\n escaped += `\\\\${code.toString(16)} `;\n continue;\n }\n if (isUpperAlpha || isLowerAlpha || isDigit || char === \"-\" || char === \"_\" || code >= 128) {\n escaped += char;\n continue;\n }\n escaped += `\\\\${char}`;\n }\n return escaped;\n}\n\nexport function escapeCssString(value: string): string {\n return value\n .replace(/\\\\/g, \"\\\\\\\\\")\n .replace(/\"/g, '\\\\\"')\n .replace(/\\n/g, \"\\\\a \")\n .replace(/\\r/g, \"\\\\d \")\n .replace(/\\f/g, \"\\\\c \");\n}\n\nexport function querySelectorAllSafely(doc: Document, selector: string): Element[] {\n try {\n return Array.from(doc.querySelectorAll(selector));\n } catch {\n return [];\n }\n}\n\nfunction humanizeIdentifier(value: string): string {\n return (\n value\n .replace(/\\.html$/i, \"\")\n .replace(/^compositions\\//i, \"\")\n .split(\"/\")\n .at(-1)\n ?.replace(/[-_]+/g, \" \")\n .replace(/\\b\\w/g, (char) => char.toUpperCase()) ?? value\n );\n}\n\n// ─── CSS selector building ────────────────────────────────────────────────────\n\nexport function buildStableSelector(el: HTMLElement): string | undefined {\n if (el.id) return `#${escapeCssIdentifier(el.id)}`;\n\n const compositionId = el.getAttribute(\"data-composition-id\");\n if (compositionId) return `[data-composition-id=\"${escapeCssString(compositionId)}\"]`;\n\n // Group wrappers carry no id/class; their data-hf-group value is the unique,\n // stable handle the source mutations write — use it so the wrapper is\n // selectable, patchable (move/scale), and addressable for ungroup.\n const group = el.getAttribute(\"data-hf-group\");\n if (group) return `[data-hf-group=\"${escapeCssString(group)}\"]`;\n\n return getPreferredClassSelector(el);\n}\n\nfunction getPreferredClassSelector(el: HTMLElement): string | undefined {\n const classes = Array.from(el.classList)\n .map((value) => value.trim())\n .filter(Boolean);\n if (classes.length === 0) return undefined;\n const preferred =\n classes.find((value) => value !== \"clip\" && !value.startsWith(\"__hf-\")) ?? classes[0];\n return preferred ? `.${escapeCssIdentifier(preferred)}` : undefined;\n}\n\n// fallow-ignore-next-line complexity\nexport function buildElementLabel(el: HTMLElement): string {\n const compositionId =\n el.getAttribute(\"data-hf-original-composition-id\") ?? el.getAttribute(\"data-composition-id\");\n if (compositionId && compositionId !== \"main\") {\n return humanizeIdentifier(compositionId);\n }\n\n const compositionSrc =\n el.getAttribute(\"data-composition-src\") ?? el.getAttribute(\"data-composition-file\");\n if (compositionSrc) {\n return humanizeIdentifier(compositionSrc);\n }\n\n const group = el.getAttribute(\"data-hf-group\");\n if (group) return group;\n\n if (el.id) return humanizeIdentifier(el.id);\n\n const preferredClass = getPreferredClassSelector(el);\n if (preferredClass) {\n return humanizeIdentifier(preferredClass.replace(/^\\./, \"\"));\n }\n\n const text = (el.textContent ?? \"\").trim().replace(/\\s+/g, \" \");\n if (text) return text.length > 40 ? `${text.slice(0, 39)}…` : text;\n return el.tagName.toLowerCase();\n}\n\nexport function getSelectorIndex(\n doc: Document,\n el: HTMLElement,\n selector: string | undefined,\n sourceFile: string,\n activeCompositionPath: string | null,\n): number | undefined {\n if (!selector?.startsWith(\".\")) return undefined;\n\n return getSourceScopedSelectorIndex(doc, el, selector, sourceFile, (candidate) =>\n isHtmlElement(candidate)\n ? getSourceFileForElement(candidate, activeCompositionPath).sourceFile\n : undefined,\n );\n}\n","import type { PatchTarget } from \"../../utils/sourcePatcher\";\nimport type { GsapAnimation } from \"@hyperframes/parsers/gsap-parser\";\n\nexport const CURATED_STYLE_PROPERTIES = [\n \"position\",\n \"display\",\n \"top\",\n \"left\",\n \"right\",\n \"bottom\",\n \"inset\",\n \"width\",\n \"height\",\n \"gap\",\n \"justify-content\",\n \"align-items\",\n \"flex-direction\",\n \"font-size\",\n \"font-style\",\n \"font-weight\",\n \"font-family\",\n \"line-height\",\n \"letter-spacing\",\n \"text-align\",\n \"text-transform\",\n \"color\",\n \"background-color\",\n \"background-image\",\n \"opacity\",\n \"mix-blend-mode\",\n \"border-radius\",\n \"border-width\",\n \"border-style\",\n \"border-color\",\n \"border-top-width\",\n \"border-top-style\",\n \"border-top-color\",\n \"outline-color\",\n \"overflow\",\n \"clip-path\",\n \"box-shadow\",\n \"filter\",\n \"backdrop-filter\",\n \"z-index\",\n \"transform\",\n \"object-fit\",\n \"object-position\",\n] as const;\n\nexport interface DomEditCapabilities {\n canSelect: boolean;\n canEditStyles: boolean;\n /** Can take a non-destructive `clip-path: inset()` crop — broader than\n * canEditStyles (a sub-composition host is croppable from the parent view). */\n canCrop: boolean;\n /** Directly editable authored left/top style fields. Canvas drag uses manual edits instead. */\n canMove: boolean;\n /** Directly editable authored width/height style fields. Canvas resize uses manual edits instead. */\n canResize: boolean;\n canApplyManualOffset: boolean;\n canApplyManualSize: boolean;\n canApplyManualRotation: boolean;\n reasonIfDisabled?: string;\n commitCheckPending?: boolean;\n}\n\nexport interface DomEditTextField {\n key: string;\n label: string;\n value: string;\n tagName: string;\n attributes: Array<{ name: string; value: string }>;\n inlineStyles: Record<string, string>;\n computedStyles: Record<string, string>;\n source: \"self\" | \"child\" | \"text-node\";\n sourceChildIndex?: number;\n}\n\nexport interface DomEditSelection extends PatchTarget {\n element: HTMLElement;\n label: string;\n tagName: string;\n sourceFile: string;\n compositionPath: string;\n compositionSrc?: string;\n isCompositionHost: boolean;\n isInsideLockedComposition: boolean;\n boundingBox: { x: number; y: number; width: number; height: number };\n textContent: string | null;\n dataAttributes: Record<string, string>;\n inlineStyles: Record<string, string>;\n computedStyles: Record<string, string>;\n textFields: DomEditTextField[];\n capabilities: DomEditCapabilities;\n existsInSource?: boolean;\n gsapAnimations?: GsapAnimation[];\n}\n\nexport interface DomEditLayerItem {\n key: string;\n element: HTMLElement;\n label: string;\n tagName: string;\n depth: number;\n childCount: number;\n id?: string;\n hfId?: string;\n selector?: string;\n selectorIndex?: number;\n sourceFile: string;\n}\n\nexport interface DomEditContextOptions {\n activeCompositionPath: string | null;\n isMasterView: boolean;\n preferClipAncestor?: boolean;\n /** The group wrapper the user has drilled into (null = top level). Selection\n * resolution treats groups as a unit unless drilled into one. */\n activeGroupElement?: HTMLElement | null;\n}\n\nexport interface DomEditViewport {\n width: number;\n height: number;\n}\n\nexport interface TimelineElementDomTarget {\n id?: string;\n domId?: string;\n selector?: string;\n selectorIndex?: number;\n sourceFile?: string;\n compositionSrc?: string;\n}\n\nexport interface TimelineElementDomTargetOptions {\n activeCompositionPath: string | null;\n compIdToSrc?: ReadonlyMap<string, string>;\n isMasterView: boolean;\n}\n","import { isHtmlElement } from \"@hyperframes/core/runtime/dom-realm\";\nimport { parseCssColor } from \"../../components/editor/colorValue\";\nimport type { TimelineText } from \"../store/timelineElement\";\nimport {\n getCuratedComputedStyles,\n isEditableTextLeaf,\n isTextBearingTag,\n} from \"../../components/editor/domEditingDom\";\n\n/** A layer that paints only text and a flat colour (a text tag whose children are all text leaves,\n * no background image): its words and look. Anything painted beyond that keeps its picture. */\nexport function readTimelineText(el: Element): TimelineText | undefined {\n if (!isHtmlElement(el) || !isTextBearingTag(el.localName)) return undefined;\n const children = Array.from(el.children);\n if (!children.every((child) => isHtmlElement(child) && isEditableTextLeaf(child)))\n return undefined;\n const value = el.textContent?.replace(/\\s+/g, \" \").trim();\n if (!value) return undefined;\n const style = getCuratedComputedStyles(el);\n if ((style[\"background-image\"] ?? \"none\") !== \"none\") return undefined;\n const background = style[\"background-color\"];\n return {\n value,\n fontFamily: style[\"font-family\"],\n fontWeight: style[\"font-weight\"],\n color: style.color,\n background: background && parseCssColor(background)?.alpha === 1 ? background : undefined,\n };\n}\n\nexport function sameTimelineText(a?: TimelineText, b?: TimelineText): boolean {\n if (!a || !b) return a === b;\n return (\n a.value === b.value &&\n a.fontFamily === b.fontFamily &&\n a.fontWeight === b.fontWeight &&\n a.color === b.color &&\n a.background === b.background\n );\n}\n","/** Round to 3 decimal places (millisecond precision for GSAP values). */\nexport function roundTo3(val: number): number {\n return Math.round(val * 1000) / 1000;\n}\n\n/** Whole CSS px: GSAP parses a -50% translate against the integer offsetWidth/Height. */\nexport function roundToLayoutPx(val: number): number {\n return Math.round(val);\n}\n\n/** Round to 2 decimal places (centisecond precision for timeline values). */\nexport function roundToCenti(val: number): number {\n return Math.round(val * 100) / 100;\n}\n","import { roundToCenti } from \"../../utils/rounding\";\n\nexport interface ParsedColor {\n red: number;\n green: number;\n blue: number;\n alpha: number;\n}\n\nexport interface HsvColor {\n hue: number;\n saturation: number;\n value: number;\n}\n\nfunction clampChannel(value: number): number {\n return Math.max(0, Math.min(255, Math.round(value)));\n}\n\nfunction clampAlpha(value: number): number {\n return Math.max(0, Math.min(1, value));\n}\n\nfunction toHex(value: number): string {\n return clampChannel(value).toString(16).padStart(2, \"0\");\n}\n\nfunction formatAlpha(value: number): string {\n return `${roundToCenti(clampAlpha(value))}`;\n}\n\nfunction parseComponent(value: string, scale: number): number {\n return value.endsWith(\"%\") ? (Number(value.slice(0, -1)) * scale) / 100 : Number(value);\n}\n\nconst NUMBER = String.raw`(?:\\d+(?:\\.\\d+)?|\\.\\d+)`;\nconst COMMA_RGB = new RegExp(\n String.raw`^rgba?\\(\\s*(${NUMBER})\\s*,\\s*(${NUMBER})\\s*,\\s*(${NUMBER})(?:\\s*,\\s*(${NUMBER}))?\\s*\\)$`,\n);\nconst SPACE_RGB = new RegExp(\n String.raw`^rgba?\\(\\s*(${NUMBER}%?)\\s+(${NUMBER}%?)\\s+(${NUMBER}%?)(?:\\s*\\/\\s*(${NUMBER}%?))?\\s*\\)$`,\n);\n\nfunction parseSerializedColor(value: string): ParsedColor | null {\n const trimmed = value.trim().toLowerCase();\n if (!trimmed) return null;\n if (trimmed === \"transparent\") {\n return { red: 0, green: 0, blue: 0, alpha: 0 };\n }\n\n const hex = trimmed.match(/^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/);\n if (hex) {\n const digits = hex[1].length <= 4 ? [...hex[1]].map((digit) => digit + digit).join(\"\") : hex[1];\n return {\n red: Number.parseInt(digits.slice(0, 2), 16),\n green: Number.parseInt(digits.slice(2, 4), 16),\n blue: Number.parseInt(digits.slice(4, 6), 16),\n alpha: digits.length === 8 ? Number.parseInt(digits.slice(6, 8), 16) / 255 : 1,\n };\n }\n\n const rgba = trimmed.match(COMMA_RGB) ?? trimmed.match(SPACE_RGB);\n if (rgba) {\n return {\n red: clampChannel(parseComponent(rgba[1], 255)),\n green: clampChannel(parseComponent(rgba[2], 255)),\n blue: clampChannel(parseComponent(rgba[3], 255)),\n alpha: clampAlpha(rgba[4] != null ? parseComponent(rgba[4], 1) : 1),\n };\n }\n\n return null;\n}\n\nlet colorContext: CanvasRenderingContext2D | null = null;\n\nfunction canResolveInBrowser(value: string): boolean {\n return (\n typeof document !== \"undefined\" &&\n typeof CSS !== \"undefined\" &&\n CSS.supports(\"color\", value) &&\n !/\\bcurrentcolor\\b|\\bvar\\s*\\(|\\blight-dark\\s*\\(/i.test(value)\n );\n}\n\nfunction parseBrowserColor(value: string): ParsedColor | null {\n try {\n if (!canResolveInBrowser(value)) return null;\n colorContext ??= document.createElement(\"canvas\").getContext(\"2d\");\n if (!colorContext) return null;\n return (\n parseCanvasColor(\n colorContext,\n `color(from ${value} srgb r g b / alpha)`,\n parseSrgbSerialization,\n ) ?? parseCanvasColor(colorContext, value, parseSerializedColor)\n );\n } catch {\n return null;\n }\n}\n\nfunction parseCanvasColor(\n context: CanvasRenderingContext2D,\n value: string,\n parse: (serialized: string) => ParsedColor | null,\n): ParsedColor | null {\n const serialized = readCanvasColor(context, value);\n return serialized === null ? null : parse(serialized);\n}\n\nfunction readCanvasColor(context: CanvasRenderingContext2D, value: string): string | null {\n context.fillStyle = \"transparent\";\n context.fillStyle = value;\n const first = context.fillStyle;\n context.fillStyle = \"white\";\n context.fillStyle = value;\n return typeof first === \"string\" && first === context.fillStyle ? first : null;\n}\n\nfunction parseSrgbSerialization(serialized: string): ParsedColor | null {\n const match = serialized.match(/^color\\(srgb ([^ ]+) ([^ ]+) ([^ /)]+)(?: \\/ ([^)]+))?\\)$/);\n if (!match) return null;\n const channels = match.slice(1, 4).map(Number);\n const alpha = match[4] === undefined ? 1 : Number(match[4]);\n if (!channels.every(Number.isFinite) || !Number.isFinite(alpha)) return null;\n return {\n red: clampChannel(channels[0] * 255),\n green: clampChannel(channels[1] * 255),\n blue: clampChannel(channels[2] * 255),\n alpha: clampAlpha(alpha),\n };\n}\n\nexport function parseCssColor(value: string): ParsedColor | null {\n return parseSerializedColor(value) ?? parseBrowserColor(value.trim());\n}\n\nexport function resolvePickerColor(\n value: string | undefined,\n element: Element | null,\n fallback: string,\n): string {\n const view = element?.ownerDocument.defaultView;\n const parsed =\n (value ? parseCssColor(value) : null) ??\n (element && view ? parseCssColor(view.getComputedStyle(element).color) : null);\n return parsed ? toHexColor(parsed) : fallback;\n}\n\nexport function toColorPickerValue(value: string): string {\n const parsed = parseCssColor(value);\n if (!parsed) return \"#000000\";\n return toHexColor(parsed);\n}\n\nexport function toHexColor(color: Pick<ParsedColor, \"red\" | \"green\" | \"blue\">): string {\n return `#${toHex(color.red)}${toHex(color.green)}${toHex(color.blue)}`;\n}\n\nexport function formatCssColor(color: ParsedColor): string {\n const red = clampChannel(color.red);\n const green = clampChannel(color.green);\n const blue = clampChannel(color.blue);\n const alpha = clampAlpha(color.alpha);\n\n if (alpha >= 1) {\n return `rgb(${red}, ${green}, ${blue})`;\n }\n\n return `rgba(${red}, ${green}, ${blue}, ${formatAlpha(alpha)})`;\n}\n\nexport function rgbToHsv(color: Pick<ParsedColor, \"red\" | \"green\" | \"blue\">): HsvColor {\n const red = clampChannel(color.red) / 255;\n const green = clampChannel(color.green) / 255;\n const blue = clampChannel(color.blue) / 255;\n const max = Math.max(red, green, blue);\n const min = Math.min(red, green, blue);\n const delta = max - min;\n\n let hue = 0;\n if (delta !== 0) {\n if (max === red) {\n hue = 60 * (((green - blue) / delta) % 6);\n } else if (max === green) {\n hue = 60 * ((blue - red) / delta + 2);\n } else {\n hue = 60 * ((red - green) / delta + 4);\n }\n }\n\n if (hue < 0) hue += 360;\n\n return {\n hue,\n saturation: max === 0 ? 0 : delta / max,\n value: max,\n };\n}\n\nexport function hsvToRgb(color: HsvColor): Pick<ParsedColor, \"red\" | \"green\" | \"blue\"> {\n const hue = (((color.hue % 360) + 360) % 360) / 60;\n const saturation = Math.max(0, Math.min(1, color.saturation));\n const value = Math.max(0, Math.min(1, color.value));\n const chroma = value * saturation;\n const x = chroma * (1 - Math.abs((hue % 2) - 1));\n const m = value - chroma;\n\n let red = 0;\n let green = 0;\n let blue = 0;\n\n if (hue >= 0 && hue < 1) {\n red = chroma;\n green = x;\n } else if (hue >= 1 && hue < 2) {\n red = x;\n green = chroma;\n } else if (hue >= 2 && hue < 3) {\n green = chroma;\n blue = x;\n } else if (hue >= 3 && hue < 4) {\n green = x;\n blue = chroma;\n } else if (hue >= 4 && hue < 5) {\n red = x;\n blue = chroma;\n } else {\n red = chroma;\n blue = x;\n }\n\n return {\n red: clampChannel((red + m) * 255),\n green: clampChannel((green + m) * 255),\n blue: clampChannel((blue + m) * 255),\n };\n}\n\nexport function mergeColorWithExistingAlpha(nextHex: string, previousValue: string): string {\n const hex = nextHex.trim();\n const match = hex.match(/^#([0-9a-f]{6})$/i);\n if (!match) return previousValue;\n\n const previous = parseCssColor(previousValue);\n const red = Number.parseInt(match[1].slice(0, 2), 16);\n const green = Number.parseInt(match[1].slice(2, 4), 16);\n const blue = Number.parseInt(match[1].slice(4, 6), 16);\n const alpha = previous?.alpha ?? 1;\n\n return formatCssColor({ red, green, blue, alpha });\n}\n","/**\n * Playback adapter utilities: factory for the static-seek adapter used when a\n * composition exposes only a `renderSeek` / `seek` API (no native play/pause\n * support), plus a thin wrapper that normalises GSAP-style `TimelineLike`\n * objects to the `PlaybackAdapter` interface.\n */\n\nimport type {\n PlaybackAdapter,\n RuntimePlaybackAdapter,\n StaticSeekPlaybackClock,\n TimelineLike,\n} from \"./playbackTypes\";\n\n// ---------------------------------------------------------------------------\n// Pure numeric helpers\n// ---------------------------------------------------------------------------\n\nexport function isFinitePositive(value: number): boolean {\n return Number.isFinite(value) && value > 0;\n}\n\nfunction clampTime(time: number, duration: number): number {\n const safeDuration = Math.max(0, Number.isFinite(duration) ? duration : 0);\n const safeTime = Math.max(0, Number.isFinite(time) ? time : 0);\n return safeDuration > 0 ? Math.min(safeTime, safeDuration) : safeTime;\n}\n\nexport function getAdapterDuration(adapter: PlaybackAdapter | null | undefined): number {\n if (!adapter) return 0;\n try {\n const duration = Number(adapter.getDuration());\n return isFinitePositive(duration) ? duration : 0;\n } catch {\n return 0;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Clock factory\n// ---------------------------------------------------------------------------\n\nexport function getDefaultStaticSeekPlaybackClock(win: Window): StaticSeekPlaybackClock {\n return {\n now: () => win.performance.now(),\n requestAnimationFrame: (callback) => win.requestAnimationFrame(callback),\n cancelAnimationFrame: (handle) => win.cancelAnimationFrame(handle),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Static-seek adapter\n// ---------------------------------------------------------------------------\n\n/**\n * Wraps a render-only player (exposes `renderSeek`/`seek` but no native\n * play/pause) and drives playback via `requestAnimationFrame`.\n */\nexport function createStaticSeekPlaybackAdapter(\n player: Pick<RuntimePlaybackAdapter, \"getTime\"> &\n Partial<Pick<RuntimePlaybackAdapter, \"renderSeek\" | \"seek\">>,\n duration: number,\n clock: StaticSeekPlaybackClock,\n getPlaybackRate: () => number = () => 1,\n): PlaybackAdapter {\n const safeDuration = Math.max(0, Number.isFinite(duration) ? duration : 0);\n let currentTime = clampTime(Number(player.getTime?.() ?? 0), safeDuration);\n let playing = false;\n let rafId = 0;\n let playStartTime = currentTime;\n let playStartNow = clock.now();\n\n const renderSeek = (time: number) => {\n currentTime = clampTime(time, safeDuration);\n if (typeof player.renderSeek === \"function\") {\n player.renderSeek(currentTime);\n return;\n }\n player.seek?.(currentTime);\n };\n\n const stopTicker = () => {\n if (rafId) {\n clock.cancelAnimationFrame(rafId);\n rafId = 0;\n }\n };\n\n const tick: FrameRequestCallback = (now) => {\n if (!playing) return;\n const playbackRate = Math.max(0.1, Number(getPlaybackRate()) || 1);\n const elapsed = ((now - playStartNow) / 1000) * playbackRate;\n renderSeek(playStartTime + elapsed);\n if (currentTime >= safeDuration) {\n playing = false;\n rafId = 0;\n return;\n }\n rafId = clock.requestAnimationFrame(tick);\n };\n\n return {\n play: () => {\n if (playing || safeDuration <= 0) return;\n if (currentTime >= safeDuration) renderSeek(0);\n playing = true;\n playStartTime = currentTime;\n playStartNow = clock.now();\n stopTicker();\n rafId = clock.requestAnimationFrame(tick);\n },\n pause: () => {\n playing = false;\n stopTicker();\n },\n seek: (time, options) => {\n renderSeek(time);\n if (options?.keepPlaying) {\n if (playing) {\n playStartTime = currentTime;\n playStartNow = clock.now();\n }\n return;\n }\n // Default seek aligns with wrapTimeline: stop the RAF ticker so the\n // adapter's `playing` flag matches the public seek contract instead of\n // silently driving renderSeek in the background.\n playing = false;\n stopTicker();\n },\n getTime: () => currentTime,\n getDuration: () => safeDuration,\n isPlaying: () => playing,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Static-seek fallback cache\n// ---------------------------------------------------------------------------\n\nexport type StaticSeekCacheEntry = {\n player: RuntimePlaybackAdapter | PlaybackAdapter;\n duration: number;\n adapter: PlaybackAdapter;\n};\n\ntype StaticSeekCacheRef = { current: StaticSeekCacheEntry | null };\ntype WarnedRef = { current: boolean };\n\n/**\n * Pause and drop the cached static-seek adapter. Must be called whenever\n * adapter selection switches to a native adapter — a cached static-seek\n * adapter that was mid-play keeps its private rAF loop seeking the player\n * forever otherwise, fighting the native transport. Also re-arms the\n * downgrade warning so a later re-downgrade is surfaced again.\n */\nexport function releaseStaticSeekCache(cache: StaticSeekCacheRef, warned: WarnedRef): void {\n cache.current?.adapter.pause();\n cache.current = null;\n warned.current = false;\n}\n\n/**\n * Resolve (with caching) the seek-driven fallback adapter. Warns once per\n * downgrade streak: seek-driven playback never starts media elements or\n * WebAudio, so without the warning the downgrade silently loses audio.\n */\nexport function resolveStaticSeekFallback(opts: {\n cache: StaticSeekCacheRef;\n warned: WarnedRef;\n bestAdapter: RuntimePlaybackAdapter | PlaybackAdapter;\n effectiveDuration: number;\n docDuration: number;\n clock: StaticSeekPlaybackClock;\n getPlaybackRate: () => number;\n}): PlaybackAdapter {\n const { cache, warned, bestAdapter, effectiveDuration, docDuration } = opts;\n const cached = cache.current;\n if (cached?.player === bestAdapter && cached.duration === effectiveDuration) {\n return cached.adapter;\n }\n cached?.adapter.pause();\n if (!warned.current) {\n warned.current = true;\n console.warn(\n `[useTimelinePlayer] Selected adapter duration (${getAdapterDuration(bestAdapter)}s) does not cover the document duration (${docDuration}s); falling back to seek-driven playback, which never starts media elements or WebAudio. Audio will not play in preview — extend the GSAP timeline to cover the declared data-duration.`,\n );\n }\n const adapter = createStaticSeekPlaybackAdapter(\n bestAdapter,\n effectiveDuration,\n opts.clock,\n opts.getPlaybackRate,\n );\n cache.current = { player: bestAdapter, duration: effectiveDuration, adapter };\n return adapter;\n}\n\n// ---------------------------------------------------------------------------\n// GSAP timeline wrapper\n// ---------------------------------------------------------------------------\n\nexport function wrapTimeline(tl: TimelineLike): PlaybackAdapter {\n return {\n play: () => tl.play(),\n pause: () => tl.pause(),\n seek: (t, options) => {\n const shouldPause = !options?.keepPlaying;\n if (shouldPause) tl.pause();\n tl.seek(t);\n if (shouldPause) tl.pause();\n },\n getTime: () => tl.time(),\n getDuration: () => tl.duration(),\n isPlaying: () => tl.isActive(),\n };\n}\n","/**\n * Low-level helpers for building and identifying TimelineElement objects.\n *\n * Covers: duration reading, media-element metadata extraction, selector/key/\n * identity builders, and DOM node lookup. These are\n * intentionally dependency-free (no store, no hooks) so they can be used in\n * both the React hook and test environments.\n */\n\nimport type { TimelineElement } from \"../store/playerStore\";\nimport { readTimelineText } from \"./timelineText\";\nimport type { ClipManifestClip } from \"./playbackTypes\";\nimport { isFinitePositive } from \"./playbackAdapter\";\nimport { getSourceScopedSelectorIndex } from \"../../utils/sourceScopedSelectorIndex\";\nimport { HF_AUDIO_GROUP_TAG } from \"@hyperframes/core/audio-groups\";\nimport { readElementFades } from \"@hyperframes/core/audio-fade\";\nimport { isHtmlElement, isImageElement, isMediaElement } from \"@hyperframes/core/runtime/dom-realm\";\nimport { elementVolume } from \"./storedVolume\";\nimport {\n type AttrReader,\n clampPlaybackRate,\n readMediaOffsetSeconds,\n} from \"@hyperframes/parsers/media-duration\";\n\n// ---------------------------------------------------------------------------\n// Layer-reveal lift transparency\n// ---------------------------------------------------------------------------\n\n/**\n * Attributes carrying the pre-lift state of a Layers-panel selection reveal\n * (useLayerRevealOverride): while a layer is selected it PAINTS on top via a\n * temporary inline z-index, but the lift is a purely visual, ephemeral studio\n * affordance — every z reader must keep reporting the element's TRUE z\n * (stored here) so menus, badges, the lane mirror, and the panel sort never\n * reason on the lifted value. A z-reorder commit removes the attributes (the\n * commit is the new truth).\n */\nexport const LAYER_REVEAL_PRIOR_Z_ATTR = \"data-hf-reveal-prior-z\";\nexport const LAYER_REVEAL_PRIOR_POSITION_ATTR = \"data-hf-reveal-prior-pos\";\n\n/** The lifted element's true (pre-lift) z, or null when no lift is active. */\nexport function readLayerRevealPriorZ(el: Element): number | null {\n const raw = el.getAttribute(LAYER_REVEAL_PRIOR_Z_ATTR);\n if (raw == null) return null;\n const n = Number.parseInt(raw, 10);\n return Number.isFinite(n) ? n : null;\n}\n\n// ---------------------------------------------------------------------------\n// Duration attribute helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Read a host element's effective CSS stacking order for the timeline's reverse\n * z→lane mapping. Prefers the inline `style.zIndex` (what the canvas context\n * menu and LayersPanel z-edits write via handleDomZIndexReorderCommit), falls\n * back to computed style; \"auto\" / empty / unparseable ⇒ 0. Works with a\n * detached parse Document (no defaultView) as well as a live iframe. Mirrors\n * canvasContextMenuZOrder.parseZIndex semantics so the two directions agree.\n * Reveal-lift transparent: an active lift reports the stored TRUE z.\n */\nexport function readTimelineElementZIndex(el: Element): number {\n const prior = readLayerRevealPriorZ(el);\n if (prior != null) return prior;\n const html = el as HTMLElement;\n const parseZ = (value: string | null | undefined): number | null => {\n if (value == null || value === \"\" || value === \"auto\") return null;\n const n = Number.parseInt(value, 10);\n return Number.isFinite(n) ? n : null;\n };\n const fromInline = parseZ(html.style?.zIndex);\n if (fromInline != null) return fromInline;\n const view = el.ownerDocument?.defaultView;\n if (view?.getComputedStyle) {\n const fromComputed = parseZ(view.getComputedStyle(html).zIndex);\n if (fromComputed != null) return fromComputed;\n }\n return 0;\n}\n\nfunction readDurationAttribute(el: Element | null | undefined): number {\n if (!el) return 0;\n const duration =\n Number.parseFloat(el.getAttribute(\"data-duration\") ?? \"\") ||\n Number.parseFloat(el.getAttribute(\"data-hf-authored-duration\") ?? \"\");\n return isFinitePositive(duration) ? duration : 0;\n}\n\nexport function isTimelineIgnoredElement(el: Element): boolean {\n // An `<hf-audio-group>` is a mixer bus with no timing of its own, drawn as a group row, never a clip.\n if (el.tagName.toLowerCase() === HF_AUDIO_GROUP_TAG) return true;\n return Boolean(\n el.closest(\n [\n \"[data-hyperframes-ignore]\",\n \"[data-hyperframes-picker-ignore]\",\n \"[data-hf-ignore]\",\n \"[data-hf-color-grading-canvas]\",\n ].join(\",\"),\n ),\n );\n}\n\n/**\n * Furthest clip end (start + RAW `data-duration`) over every non-root clip in the\n * document. Reads the authored attribute, NOT any runtime-computed value — so it\n * is immune to the runtime's clamp that truncates a clip's live duration to the\n * composition length. This is the source of truth for content-driven duration:\n * computing it from the store instead would feed the truncated value back in and\n * make the composition length ratchet down (research HANDOFF-3 §6.1 feedback loop).\n */\nexport function furthestClipEndFromDocument(doc: Document | null | undefined): number {\n if (!doc) return 0;\n const root = doc.querySelector(\"[data-composition-id]\");\n let maxEnd = 0;\n for (const node of Array.from(doc.querySelectorAll(\"[data-start]\"))) {\n if (node === root || isTimelineIgnoredElement(node)) continue;\n const start = Number.parseFloat(node.getAttribute(\"data-start\") ?? \"\");\n const duration = readDurationAttribute(node);\n if (!Number.isFinite(start) || start < 0 || duration <= 0) continue;\n maxEnd = Math.max(maxEnd, start + duration);\n }\n return maxEnd;\n}\n\nexport function readTimelineDurationFromDocument(doc: Document | null | undefined): number {\n if (!doc) return 0;\n const rootDuration = readDurationAttribute(doc.querySelector(\"[data-composition-id]\"));\n if (rootDuration > 0) return rootDuration;\n return furthestClipEndFromDocument(doc);\n}\n\n// ---------------------------------------------------------------------------\n// DOM element type guards\n// ---------------------------------------------------------------------------\n\nfunction isCompositionHost(el: Element): boolean {\n return (\n el.hasAttribute(\"data-composition-id\") ||\n el.hasAttribute(\"data-composition-src\") ||\n el.hasAttribute(\"data-composition-file\")\n );\n}\n\nexport function resolveMediaElement(el: Element): HTMLMediaElement | HTMLImageElement | null {\n if (isMediaElement(el) || isImageElement(el)) return el;\n // A composition's media belongs to its own timeline, not the clip's: its length would cap a trim.\n if (isCompositionHost(el)) return null;\n const candidate = el.querySelector(\"video, audio, img\");\n return isMediaElement(candidate) || isImageElement(candidate) ? candidate : null;\n}\n\n/** The in-point as playback reads it, and the attribute holding it; empty when neither is authored. */\nexport function readPlaybackStartAttributes(\n getAttr: AttrReader,\n): Pick<TimelineElement, \"playbackStart\" | \"playbackStartAttr\"> {\n const playbackStartAttr =\n getAttr(\"data-playback-start\") != null\n ? \"playback-start\"\n : getAttr(\"data-media-start\") != null\n ? \"media-start\"\n : undefined;\n return playbackStartAttr\n ? { playbackStart: readMediaOffsetSeconds(getAttr), playbackStartAttr }\n : {};\n}\n\nexport function playbackStartAttributeForElement(\n element: Pick<TimelineElement, \"kind\" | \"playbackStartAttr\">,\n): \"data-media-start\" | \"data-playback-start\" {\n return element.playbackStartAttr === \"playback-start\" || element.kind === \"composition\"\n ? \"data-playback-start\"\n : \"data-media-start\";\n}\n\nfunction applyPlaybackMetadataFromElement(entry: TimelineElement, el: Element): void {\n Object.assign(\n entry,\n readPlaybackStartAttributes((n) => el.getAttribute(n)),\n );\n\n const authoredPlaybackRate = Number.parseFloat(el.getAttribute(\"data-playback-rate\") ?? \"\");\n if (Number.isFinite(authoredPlaybackRate) && authoredPlaybackRate > 0) {\n entry.playbackRate = clampPlaybackRate(authoredPlaybackRate);\n }\n}\n\n/** Sets or clears an optional field, so a re-parse after the attribute is removed drops it. */\nfunction setOptional<K extends keyof TimelineElement>(\n entry: TimelineElement,\n key: K,\n value: TimelineElement[K] | undefined,\n): void {\n if (value === undefined) delete entry[key];\n else entry[key] = value;\n}\n\n/** The compiler's rule (timingCompiler): explicit data-has-audio wins; otherwise an unmuted <video> is audible. */\nexport function isVideoAudible(opts: {\n tag: string;\n hasAudioAttr: string | null | undefined;\n muted: boolean;\n}): boolean {\n if (opts.hasAudioAttr === \"true\") return true;\n if (opts.hasAudioAttr === \"false\" || opts.hasAudioAttr === \"\") return false;\n return opts.tag.toLowerCase() === \"video\" && !opts.muted;\n}\n\nexport function isAudibleVideoNode(el: Element): boolean {\n if (el.tagName.toLowerCase() !== \"video\" || el.hasAttribute(\"muted\")) return false;\n return isVideoAudible({\n tag: \"video\",\n hasAudioAttr: el.getAttribute(\"data-has-audio\"),\n muted: false,\n });\n}\n\n/** What the mixer gets: the compiler's `data-has-audio` rule, muted and volume. */\nfunction applyAudioMetadataFromElement(entry: TimelineElement, el: Element): void {\n const media = resolveMediaElement(el) ?? el;\n const muted = el.hasAttribute(\"muted\") || media.hasAttribute(\"muted\");\n const sound = isVideoAudible({\n tag: el.tagName,\n hasAudioAttr: el.getAttribute(\"data-has-audio\"),\n muted,\n });\n setOptional(entry, \"hasAudio\", sound ? true : undefined);\n setOptional(entry, \"muted\", muted ? true : undefined);\n setOptional(entry, \"volume\", elementVolume(el, media));\n}\n\nfunction applyFadeMetadataFromElement(entry: TimelineElement, el: Element): void {\n const fades = readElementFades(el);\n setOptional(entry, \"fadeIn\", fades.fadeIn > 0 ? fades.fadeIn : undefined);\n setOptional(entry, \"fadeOut\", fades.fadeOut > 0 ? fades.fadeOut : undefined);\n}\n\nexport function applyMediaMetadataFromElement(entry: TimelineElement, el: Element): void {\n setOptional(entry, \"text\", readTimelineText(el));\n applyPlaybackMetadataFromElement(entry, el);\n applyAudioMetadataFromElement(entry, el);\n applyFadeMetadataFromElement(entry, el);\n\n const mediaEl = resolveMediaElement(el);\n if (!mediaEl) return;\n\n entry.tag = mediaEl.tagName.toLowerCase();\n const src = mediaEl.getAttribute(\"src\");\n if (src) entry.src = src;\n\n if (!isMediaElement(mediaEl)) return;\n\n const sourceDurationAttr =\n el.getAttribute(\"data-source-duration\") ?? mediaEl.getAttribute(\"data-source-duration\");\n const sourceDuration = sourceDurationAttr ? parseFloat(sourceDurationAttr) : mediaEl.duration;\n if (Number.isFinite(sourceDuration) && sourceDuration > 0) {\n entry.sourceDuration = sourceDuration;\n }\n\n const playbackRate = mediaEl.defaultPlaybackRate;\n if (entry.playbackRate == null && Number.isFinite(playbackRate) && playbackRate > 0) {\n entry.playbackRate = clampPlaybackRate(playbackRate);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Label helpers\n// ---------------------------------------------------------------------------\n\nexport function getTimelineElementDisplayLabel(input: {\n id?: string | null;\n label?: string | null;\n tag?: string | null;\n}): string {\n const label = input.label?.trim();\n if (label) return label;\n const id = input.id?.trim();\n if (id) return id;\n const tag = input.tag?.trim().toLowerCase();\n return tag ? `${tag} clip` : \"Timeline clip\";\n}\n\n// ---------------------------------------------------------------------------\n// Selector / identity / key builders\n// ---------------------------------------------------------------------------\n\nexport function getTimelineElementSelector(el: Element): string | undefined {\n if (isHtmlElement(el) && el.id) return `#${CSS.escape(el.id)}`;\n const compId = el.getAttribute(\"data-composition-id\");\n if (compId) return `[data-composition-id=\"${CSS.escape(compId)}\"]`;\n if (isHtmlElement(el)) {\n const classes = el.className.split(/\\s+/).filter(Boolean);\n const firstClass = classes.find((className) => className !== \"clip\") ?? classes[0];\n if (firstClass) return `.${CSS.escape(firstClass)}`;\n }\n return undefined;\n}\n\nexport function getTimelineElementSourceFile(el: Element): string | undefined {\n const ownerRoot = el.parentElement?.closest(\"[data-composition-id]\");\n return (\n ownerRoot?.getAttribute(\"data-composition-file\") ??\n ownerRoot?.getAttribute(\"data-composition-src\") ??\n undefined\n );\n}\n\nexport function getTimelineElementSelectorIndex(\n doc: Document,\n el: Element,\n selector: string | undefined,\n): number | undefined {\n return getSourceScopedSelectorIndex(\n doc,\n el,\n selector,\n getTimelineElementSourceFile(el),\n getTimelineElementSourceFile,\n );\n}\n\nexport function buildTimelineElementKey(params: {\n id: string;\n fallbackIndex: number;\n domId?: string;\n selector?: string;\n selectorIndex?: number;\n sourceFile?: string;\n}): string {\n const scope = params.sourceFile ?? \"index.html\";\n if (params.domId) return `${scope}#${params.domId}`;\n if (params.selector) return `${scope}:${params.selector}:${params.selectorIndex ?? 0}`;\n return `${scope}:${params.id}:${params.fallbackIndex}`;\n}\n\n/**\n * Inverse of {@link buildTimelineElementKey} for the `sourceFile#domId` form.\n * A key with no `#` is a bare dom id and carries no source file of its own, so\n * the caller supplies the scope it wants to look that id up in.\n */\nexport function splitTimelineElementKey(key: string): {\n sourceFile: string | null;\n domId: string;\n} {\n const hashIndex = key.lastIndexOf(\"#\");\n if (hashIndex < 0) return { sourceFile: null, domId: key };\n return { sourceFile: key.slice(0, hashIndex), domId: key.slice(hashIndex + 1) };\n}\n\nexport function buildTimelineElementIdentity(params: {\n preferredId?: string | null;\n label: string;\n fallbackIndex: number;\n domId?: string;\n selector?: string;\n selectorIndex?: number;\n sourceFile?: string;\n}): { id: string; key: string } {\n const id =\n params.preferredId?.trim() ||\n buildTimelineElementKey({\n id: params.label,\n fallbackIndex: params.fallbackIndex,\n domId: params.domId,\n selector: params.selector,\n selectorIndex: params.selectorIndex,\n sourceFile: params.sourceFile,\n });\n const key = buildTimelineElementKey({\n id,\n fallbackIndex: params.fallbackIndex,\n domId: params.domId,\n selector: params.selector,\n selectorIndex: params.selectorIndex,\n sourceFile: params.sourceFile,\n });\n return { id, key };\n}\n\nexport function getTimelineElementIdentity(element: { key?: string | null; id: string }): string {\n return element.key ?? element.id;\n}\n\n/**\n * The id space the RUNTIME matches on — a bare DOM id, never a store key.\n *\n * Studio addresses rows by `buildTimelineElementKey`'s composite\n * `<sourceFile>#<domId>`, but everything audio in `@hyperframes/core` keys off\n * the live document: `resolveAudioGroups` collects `member.id`,\n * `resolveCarveSourceIds` goes through `getElementById`. Anything crossing into\n * that space — a group membership list, a carve source — has to be\n * converted here first; a composite key silently matches nothing.\n *\n * `null` for a row with no DOM id at all (selector-addressed elements): such an\n * element cannot be grouped, because `resolveAudioGroups` skips\n * members without an `id` and would build a group that is half there.\n */\nexport function runtimeAudioId(element: { domId?: string | null }): string | null {\n return element.domId || null;\n}\n\n/**\n * Timeline store key for a z-reorder entry built OUTSIDE the timeline\n * expansion (canvas context menu / LayersPanel), so the reorder commit can\n * update the store's zIndex synchronously. Matches buildTimelineElementKey's\n * stable branches (`sourceFile#domId`, else the selector-based key). Undefined\n * when the element has neither a DOM id nor a selector — the timeline's\n * fallback branch needs its own fallbackIndex, which these callers don't have,\n * so such an entry simply skips the synchronous store update.\n */\nexport function deriveTimelineStoreKey(params: {\n domId?: string;\n selector?: string;\n selectorIndex?: number;\n sourceFile?: string;\n}): string | undefined {\n if (!params.domId && !params.selector) return undefined;\n return buildTimelineElementKey({ id: \"\", fallbackIndex: 0, ...params });\n}\n\n/**\n * {@link deriveTimelineStoreKey} for a caller that already has a DOM id in\n * hand (e.g. one it just minted), so the undefined branch never applies.\n */\nexport function deriveTimelineStoreKeyForDomId(domId: string, sourceFile?: string): string {\n return deriveTimelineStoreKey({ domId, sourceFile })!;\n}\n\n// ---------------------------------------------------------------------------\n// DOM node querying\n// ---------------------------------------------------------------------------\n\nfunction getTimelineDomNodes(doc: Document): Element[] {\n const rootComp = doc.querySelector(\"[data-composition-id]\");\n return Array.from(doc.querySelectorAll(\"[data-start]\")).filter(\n (node) => node !== rootComp && !isTimelineIgnoredElement(node),\n );\n}\n\nfunction numbersNearlyEqual(a: number, b: number): boolean {\n return Math.abs(a - b) < 0.001;\n}\n\nconst MANIFEST_CLIP_ATTRS: ReadonlyArray<[string, (clip: ClipManifestClip) => number]> = [\n [\"data-start\", (clip) => clip.start],\n [\"data-duration\", (clip) => clip.duration],\n [\"data-track-index\", (clip) => clip.track],\n];\n\nfunction nodeMatchesClipTag(node: Element, clip: ClipManifestClip): boolean {\n return !clip.tagName || node.tagName.toLowerCase() === clip.tagName.toLowerCase();\n}\n\nfunction nodeMatchesManifestClip(node: Element, clip: ClipManifestClip): boolean {\n if (!nodeMatchesClipTag(node, clip)) return false;\n // An attribute only constrains the match when it parses to a finite number:\n // missing or garbled reads as \"unknown\", not \"mismatch\".\n return MANIFEST_CLIP_ATTRS.every(([attr, expected]) => {\n const actual = Number.parseFloat(node.getAttribute(attr) ?? \"\");\n return !Number.isFinite(actual) || numbersNearlyEqual(actual, expected(clip));\n });\n}\n\n/** Whether `node` sits in the composition the clip was read from, the chain the runtime records outermost first\n * (`resolveNearestCompositionContext` in core's runtime/timeline.ts). A clip without that scope accepts any node. */\nfunction nodeInClipScope(node: Element, clip: ClipManifestClip): boolean {\n const scope = clip.compositionAncestors;\n if (!scope) return true;\n const ids: string[] = [];\n for (let cursor = node.parentElement; cursor; cursor = cursor.parentElement) {\n const id = cursor.getAttribute(\"data-composition-id\");\n if (id) ids.unshift(id);\n }\n return ids.length === scope.length && ids.every((id, index) => id === scope[index]);\n}\n\n/** The first match in the clip's composition across `selectors`; a lone match stands only when none is in scope, as a\n * healed host can stale the clip's chain for a pass. An id can repeat in a sub-composition earlier in the document. */\nfunction findInClipScope(\n doc: Document,\n clip: ClipManifestClip,\n selectors: string[],\n): Element | null {\n let lone: Element | null = null;\n for (const selector of selectors) {\n const nodes = Array.from(doc.querySelectorAll(selector));\n const scoped = nodes.find((node) => nodeInClipScope(node, clip));\n if (scoped) return scoped;\n if (nodes.length === 1) lone ??= nodes[0];\n }\n return lone;\n}\n\nexport type PreviewTarget = Pick<TimelineElement, \"hfId\" | \"domId\" | \"id\" | \"sourceFile\">;\n\n/** Finds a row's preview element by `data-hf-id`, then id, preferring one in the row's own file: both repeat across\n * files. Indexes the document once, so a pass over every row costs one scan. */\nexport function previewElementFinder(\n doc: Document,\n selector = \"[data-hf-id], [id]\",\n): (target: PreviewTarget) => Element | null {\n const byKey = new Map<string, Element[]>();\n const add = (key: string, node: Element) => {\n const nodes = byKey.get(key);\n if (nodes) nodes.push(node);\n else byKey.set(key, [node]);\n };\n for (const node of doc.querySelectorAll(selector)) {\n const hfId = node.getAttribute(\"data-hf-id\");\n const id = node.getAttribute(\"id\");\n if (hfId) add(`hf:${hfId}`, node);\n if (id) add(`id:${id}`, node);\n }\n return (target) => {\n const matches = [\n ...((target.hfId && byKey.get(`hf:${target.hfId}`)) || []),\n ...(byKey.get(`id:${target.domId ?? target.id}`) ?? []),\n ];\n return (\n matches.find((node) => getTimelineElementSourceFile(node) === target.sourceFile) ??\n matches[0] ??\n null\n );\n };\n}\n\nexport function findClipElementById(doc: Document, clip: ClipManifestClip): Element | null {\n if (!clip.id) return null;\n const first = doc.getElementById(clip.id);\n if (!first || nodeInClipScope(first, clip)) return first;\n return findInClipScope(doc, clip, [`[id=\"${CSS.escape(clip.id)}\"]`]);\n}\n\nfunction findTimelineDomNode(doc: Document, clip: ClipManifestClip): Element | null {\n if (!clip.id) return null;\n const first = doc.getElementById(clip.id);\n if (first && nodeInClipScope(first, clip)) return first;\n const id = CSS.escape(clip.id);\n const byOtherKeys = [`[data-hf-id=\"${id}\"]`, `[data-composition-id=\"${id}\"]`, `.${id}`];\n return findInClipScope(doc, clip, first ? [`[id=\"${id}\"]`, ...byOtherKeys] : byOtherKeys);\n}\n\nexport function findTimelineDomNodeForClip(\n doc: Document,\n clip: ClipManifestClip,\n fallbackIndex: number,\n usedNodes = new Set<Element>(),\n getCandidates = () => getTimelineDomNodes(doc),\n): Element | null {\n const byIdentity = findTimelineDomNode(doc, clip);\n if (byIdentity && !usedNodes.has(byIdentity) && nodeMatchesClipTag(byIdentity, clip))\n return byIdentity;\n\n const candidates = getCandidates().filter((node) => !usedNodes.has(node));\n const exact = candidates.find((node) => nodeMatchesManifestClip(node, clip));\n if (exact) return exact;\n\n const positional = candidates[fallbackIndex];\n return positional && nodeMatchesClipTag(positional, clip) ? positional : null;\n}\n\n/** One synchronous hydration pass: snapshot only on a miss, never across reloads. */\nexport function createTimelineDomNodeResolver(doc: Document) {\n let candidates: Element[] | undefined;\n const usedNodes = new Set<Element>();\n const getCandidates = () => (candidates ??= getTimelineDomNodes(doc));\n return (clip: ClipManifestClip, index: number): Element | null => {\n const node = findTimelineDomNodeForClip(doc, clip, index, usedNodes, getCandidates);\n if (node) usedNodes.add(node);\n return node;\n };\n}\n","export function parseStoredVolume(text: string | null): number | undefined {\n const volume = Number.parseFloat(text ?? \"\");\n return Number.isFinite(volume) ? volume : undefined;\n}\n\nexport const elementVolume = (el: Element, media: Element) =>\n parseStoredVolume(el.getAttribute(\"data-volume\") ?? media.getAttribute(\"data-volume\"));\n","const PROJECT_HASH_PREFIX = \"#project/\";\n\nexport interface ProjectHashRoute {\n projectId: string;\n params: URLSearchParams;\n}\n\nconst WINDOWS_DRIVE_PREFIX = /^[a-z]:/i;\n\n/** Project names are single path segments, including when received from a hash or server. */\nexport function isValidProjectId(value: string): boolean {\n return (\n value.length > 0 &&\n value !== \".\" &&\n value !== \"..\" &&\n !WINDOWS_DRIVE_PREFIX.test(value) &&\n !value.includes(\"/\") &&\n !value.includes(\"\\\\\") &&\n !Array.from(value).some((char) => char.charCodeAt(0) < 32)\n );\n}\n\nfunction decodeHashProjectId(value: string): string {\n try {\n return decodeURIComponent(value);\n } catch {\n return value;\n }\n}\n\nfunction normalizeHashParams(\n params?: URLSearchParams | Record<string, string | null | undefined>,\n): URLSearchParams {\n if (!params) return new URLSearchParams();\n if (params instanceof URLSearchParams) return params;\n\n const next = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n if (!key || value == null || value === \"\") continue;\n next.set(key, value);\n }\n return next;\n}\n\nexport function encodeProjectId(projectId: string): string {\n if (!isValidProjectId(projectId)) throw new Error(\"Invalid project ID\");\n return encodeURIComponent(projectId);\n}\n\nexport function buildProjectHash(\n projectId: string,\n params?: URLSearchParams | Record<string, string | null | undefined>,\n): string {\n const search = normalizeHashParams(params).toString();\n return `${PROJECT_HASH_PREFIX}${encodeProjectId(projectId)}${search ? `?${search}` : \"\"}`;\n}\n\nexport function parseProjectHashRoute(hash: string): ProjectHashRoute | null {\n if (!hash.startsWith(PROJECT_HASH_PREFIX)) return null;\n\n const route = hash.slice(PROJECT_HASH_PREFIX.length);\n const queryIndex = route.indexOf(\"?\");\n const encodedProjectId = queryIndex >= 0 ? route.slice(0, queryIndex) : route;\n if (!encodedProjectId || encodedProjectId.includes(\"/\")) return null;\n\n const projectId = decodeHashProjectId(encodedProjectId);\n if (!isValidProjectId(projectId)) return null;\n\n const rawParams = queryIndex >= 0 ? route.slice(queryIndex + 1) : \"\";\n return {\n projectId,\n params: new URLSearchParams(rawParams),\n };\n}\n\nexport function parseProjectIdFromHash(hash: string): string | null {\n return parseProjectHashRoute(hash)?.projectId ?? null;\n}\n\nexport function buildProjectApiPath(projectId: string, suffix = \"\"): string {\n const normalizedSuffix = suffix && !suffix.startsWith(\"/\") ? `/${suffix}` : suffix;\n return `/api/projects/${encodeProjectId(projectId)}${normalizedSuffix}`;\n}\n"],"mappings":"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","names":["isHtmlElement","isHtmlElement","isHtmlElement","isHtmlElement"]}
|