@tangle-network/agent-app 0.16.0 → 0.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,11 +2,11 @@ import {
2
2
  SEQUENCE_MEDIA_DRAG_TYPE,
3
3
  TimelineEditor,
4
4
  TimelineEditor_default
5
- } from "./chunk-IHR6K3GF.js";
5
+ } from "./chunk-RJ2ZHK5D.js";
6
6
  import "./chunk-ZYBWGSAZ.js";
7
7
  export {
8
8
  SEQUENCE_MEDIA_DRAG_TYPE,
9
9
  TimelineEditor,
10
10
  TimelineEditor_default as default
11
11
  };
12
- //# sourceMappingURL=TimelineEditor-OXPJZDP2.js.map
12
+ //# sourceMappingURL=TimelineEditor-JDG54OBP.js.map
@@ -1876,36 +1876,47 @@ function TimelineEditor(props) {
1876
1876
  appliedTimelineRef.current = props.timeline;
1877
1877
  stack.reset(props.timeline);
1878
1878
  }, [props.timeline, stack]);
1879
- const clock = useMemo3(
1880
- () => createPlaybackClock({ fps, durationFrames: timeline.sequence.durationFrames }),
1881
- [fps, timeline.sequence.durationFrames]
1879
+ const [clock, setClock] = useState3(
1880
+ () => createPlaybackClock({ fps, durationFrames: timeline.sequence.durationFrames })
1882
1881
  );
1883
- useEffect3(() => () => clock.dispose(), [clock]);
1882
+ const clockRef = useRef3(clock);
1884
1883
  const [playheadFrame, setPlayheadFrame] = useState3(0);
1884
+ const playheadFrameRef = useRef3(0);
1885
1885
  const [isPlaying, setIsPlaying] = useState3(false);
1886
1886
  const onPlayheadChangeRef = useRef3(props.onPlayheadChange);
1887
1887
  onPlayheadChangeRef.current = props.onPlayheadChange;
1888
1888
  useEffect3(() => {
1889
- clock.seek(Math.min(playheadFrame, timeline.sequence.durationFrames - 1));
1890
- return clock.subscribe((frame) => {
1889
+ const next = createPlaybackClock({ fps, durationFrames: timeline.sequence.durationFrames });
1890
+ clockRef.current.dispose();
1891
+ clockRef.current = next;
1892
+ setClock(next);
1893
+ next.seek(Math.min(playheadFrameRef.current, timeline.sequence.durationFrames - 1));
1894
+ const unsubscribe = next.subscribe((frame) => {
1895
+ playheadFrameRef.current = frame;
1891
1896
  setPlayheadFrame(frame);
1892
- setIsPlaying(clock.isPlaying());
1897
+ setIsPlaying(next.isPlaying());
1893
1898
  onPlayheadChangeRef.current?.(frame);
1894
1899
  });
1895
- }, [clock]);
1896
- const ownedProviderRef = useRef3(null);
1897
- const frameProvider = useMemo3(() => {
1898
- if (props.frameProvider) return props.frameProvider;
1899
- if (!ownedProviderRef.current) ownedProviderRef.current = createVideoElementFrameProvider();
1900
- return ownedProviderRef.current;
1901
- }, [props.frameProvider]);
1902
- useEffect3(
1903
- () => () => {
1904
- ownedProviderRef.current?.dispose();
1905
- ownedProviderRef.current = null;
1906
- },
1907
- []
1900
+ return () => {
1901
+ unsubscribe();
1902
+ next.dispose();
1903
+ };
1904
+ }, [fps, timeline.sequence.durationFrames]);
1905
+ const [frameProvider, setFrameProvider] = useState3(
1906
+ () => props.frameProvider ?? createVideoElementFrameProvider()
1908
1907
  );
1908
+ const frameProviderRef = useRef3(frameProvider);
1909
+ const ownsFrameProviderRef = useRef3(!props.frameProvider);
1910
+ useEffect3(() => {
1911
+ const next = props.frameProvider ?? createVideoElementFrameProvider();
1912
+ if (ownsFrameProviderRef.current) frameProviderRef.current.dispose();
1913
+ ownsFrameProviderRef.current = !props.frameProvider;
1914
+ frameProviderRef.current = next;
1915
+ setFrameProvider(next);
1916
+ return () => {
1917
+ if (!props.frameProvider) next.dispose();
1918
+ };
1919
+ }, [props.frameProvider]);
1909
1920
  const zoomMath = useMemo3(() => createZoomMath({ minZoom: MIN_ZOOM, maxZoom: MAX_ZOOM }), []);
1910
1921
  const [zoom, setZoom] = useState3(1);
1911
1922
  const trackViewportRef = useRef3(null);
@@ -2364,4 +2375,4 @@ export {
2364
2375
  TimelineEditor,
2365
2376
  TimelineEditor_default
2366
2377
  };
2367
- //# sourceMappingURL=chunk-IHR6K3GF.js.map
2378
+ //# sourceMappingURL=chunk-RJ2ZHK5D.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/sequences-react/components/TimelineEditor.tsx","../src/sequences-react/engine/command-stack.ts","../src/sequences-react/engine/commands.ts","../src/sequences-react/engine/playback.ts","../src/sequences-react/engine/snap.ts","../src/sequences-react/engine/zoom.ts","../src/sequences-react/media/frame-provider.ts","../src/sequences-react/components/composite-command.ts","../src/sequences-react/components/interaction-math.ts","../src/sequences-react/components/PreviewCanvas.tsx","../src/sequences-react/components/SnapIndicatorLine.tsx","../src/sequences-react/components/TimelinePlayhead.tsx","../src/sequences-react/components/TimelineRuler.tsx","../src/sequences-react/components/TimelineClipChip.tsx","../src/sequences-react/media/waveform.ts","../src/sequences-react/components/glyphs.tsx","../src/sequences-react/components/TimelineTrackRow.tsx","../src/sequences-react/components/ZoomControl.tsx"],"sourcesContent":["/**\n * The timeline editor surface: program monitor, transport row, and the\n * scrollable track area, composed over the engine (command stack, zoom math,\n * snapping, playback clock) and media (frame provider, waveforms) layers.\n *\n * State ownership:\n * - The COMMAND STACK owns the timeline. Every edit is a `TimelineCommand`\n * executed optimistically, then persisted via `onApplyOperations`; a\n * rejected persist rolls the command back locally WITHOUT emitting its\n * inverse (the server never saw it). A user-initiated undo/redo DOES emit\n * (the server applied the original), so the editor mirrors the executed\n * commands to know which operations an undo corresponds to — the engine\n * stack exposes no command identity from `undo()`.\n * - Clip-creating commands mint optimistic `local-…` ids. When\n * `onApplyOperations` resolves with `SequenceApplyResult[]`, the editor\n * records local→server aliases that every command resolves through at\n * execute/undo/emission time, so undoing a committed place/split/caption\n * works after a server refresh. Hosts resolving void skip reconciliation;\n * undo of creates then fails loud (error bar) once a refresh replaced the\n * local ids.\n *\n * Captions require an unlocked caption track: the editor creates clips, never\n * tracks. Products seed a caption track at sequence creation (or let the\n * agent's create_track tool add one); the caption button errors otherwise.\n * - The PLAYBACK CLOCK owns the playhead. Volatile view state (selection,\n * zoom, snap toggle) lives in React state; the contract's\n * `EditorTimelineState` view fields are initials, not a live channel.\n *\n * Compositing/stacking rule: `sortOrder` is paint order — later tracks paint\n * over earlier ones in the preview; rows render top→bottom in the same order.\n *\n * Asset drops: lanes accept `application/x-sequence-media` payloads\n * (JSON `{ url, kind, label?, durationSeconds?, generationId?, assetId? }`),\n * which is how a host's `renderAssetShelf` content places media — video/image\n * onto video tracks, audio onto audio tracks.\n *\n * Keyboard: space play/pause · delete/backspace removes the selection (one\n * undo step) · mod+z undo · shift+mod+z / mod+y redo · escape cancels an\n * in-flight drag (handled by the chips).\n */\n\nimport { useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react'\nimport type { DragEvent as ReactDragEvent } from 'react'\nimport {\n chooseCaptionPlacement,\n formatTimecode,\n secondsToFrames,\n trackIntervals,\n} from '../../sequences/model'\nimport type { SequenceApplyResult } from '../../sequences/apply'\nimport type { SequenceClip, SequenceMediaKind } from '../../sequences/model'\nimport type { SequenceOperation } from '../../sequences/operations'\nimport type { PlaybackClock, SnapPoint, TimelineCommand, TimelineEditorProps, VideoFrameProvider } from '../contracts'\nimport { createCommandStack } from '../engine/command-stack'\nimport {\n addCaptionCommand,\n deleteClipCommand,\n moveClipCommand,\n placeClipCommand,\n setClipTextCommand,\n splitClipCommand,\n trimClipCommand,\n} from '../engine/commands'\nimport { createPlaybackClock } from '../engine/playback'\nimport { applySnap, collectSnapPoints } from '../engine/snap'\nimport type { TimelineSnapPoint } from '../engine/snap'\nimport { createZoomMath } from '../engine/zoom'\nimport { createVideoElementFrameProvider } from '../media/frame-provider'\nimport { compositeCommand } from './composite-command'\nimport { chooseMoveSnap } from './interaction-math'\nimport { PreviewCanvas } from './PreviewCanvas'\nimport { SnapIndicatorLine } from './SnapIndicatorLine'\nimport type { ClipMoveCommit, ClipTrimCommit } from './TimelineClipChip'\nimport { TimelinePlayhead } from './TimelinePlayhead'\nimport { TimelineRuler } from './TimelineRuler'\nimport { TimelineTrackRow } from './TimelineTrackRow'\nimport { ZoomControl } from './ZoomControl'\nimport {\n CaptionPlusGlyph,\n MagnetGlyph,\n PauseGlyph,\n PlayGlyph,\n RedoGlyph,\n ScissorsGlyph,\n UndoGlyph,\n} from './glyphs'\n\nexport const SEQUENCE_MEDIA_DRAG_TYPE = 'application/x-sequence-media'\n\n/** Matches the engine's COMMAND_HISTORY_LIMIT so the operation mirror and the\n * stack's history can never disagree about what an undo refers to. */\nconst HISTORY_MIRROR_LIMIT = 200\n\nconst MIN_ZOOM = 0.005\nconst MAX_ZOOM = 24\n/** Tailwind w-36 on the track header column. */\nconst TRACK_HEADER_PX = 144\n\nconst TRANSPORT_BUTTON =\n 'flex h-7 w-7 items-center justify-center rounded border border-[var(--border-default)] text-[var(--text-secondary)] transition hover:text-[var(--text-primary)] disabled:cursor-default disabled:opacity-40 disabled:hover:text-[var(--text-secondary)]'\n\nfunction mintClipId(): string {\n const uuid = globalThis.crypto && 'randomUUID' in globalThis.crypto ? globalThis.crypto.randomUUID() : null\n return `local-${uuid ?? `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}`\n}\n\nfunction isTypingTarget(target: EventTarget | null): boolean {\n return target instanceof Element && target.closest('input, textarea, select, button, [contenteditable=\"true\"]') !== null\n}\n\ninterface MediaDragPayload {\n url: string\n kind: SequenceMediaKind\n label?: string\n durationSeconds?: number\n generationId?: string\n assetId?: string\n}\n\nfunction parseMediaDragPayload(raw: string): MediaDragPayload {\n const parsed = JSON.parse(raw) as Partial<MediaDragPayload>\n if (typeof parsed.url !== 'string' || parsed.url.length === 0) {\n throw new Error(`${SEQUENCE_MEDIA_DRAG_TYPE} payload requires a non-empty url`)\n }\n if (parsed.kind !== 'video' && parsed.kind !== 'image' && parsed.kind !== 'audio') {\n throw new Error(`${SEQUENCE_MEDIA_DRAG_TYPE} payload kind must be video | image | audio, got ${String(parsed.kind)}`)\n }\n return parsed as MediaDragPayload\n}\n\nexport function TimelineEditor(props: TimelineEditorProps) {\n const { canWrite, onApplyOperations } = props\n const fps = props.timeline.sequence.fps\n const durationFrames = props.timeline.sequence.durationFrames\n\n // --- engine lifecycles ----------------------------------------------------\n\n const stack = useMemo(() => createCommandStack(props.timeline), [])\n const editorState = useSyncExternalStore(stack.subscribe, stack.getState, stack.getState)\n const timeline = editorState.timeline\n\n const appliedTimelineRef = useRef(props.timeline)\n useEffect(() => {\n if (appliedTimelineRef.current === props.timeline) return\n appliedTimelineRef.current = props.timeline\n stack.reset(props.timeline)\n }, [props.timeline, stack])\n\n // The playback clock lives in state so render-time consumers (preview canvas,\n // transport, scrub handlers) share the exact instance the driving effect owns.\n // Creation and disposal are co-located inside that effect, keyed on the engine\n // config, so every (re)mount builds a fresh clock. Tying a render-created\n // clock's disposal to an effect cleanup instead reuses the disposed instance\n // under React StrictMode's mount/unmount/remount probe — the remount then\n // seeks a disposed clock (\"PlaybackClock is disposed\"). The initial state value\n // is a placeholder the effect disposes and replaces on mount; a recreated clock\n // (duration change) resumes from the prior playhead, tracked in a ref.\n const [clock, setClock] = useState<PlaybackClock>(() =>\n createPlaybackClock({ fps, durationFrames: timeline.sequence.durationFrames }),\n )\n const clockRef = useRef(clock)\n const [playheadFrame, setPlayheadFrame] = useState(0)\n const playheadFrameRef = useRef(0)\n const [isPlaying, setIsPlaying] = useState(false)\n const onPlayheadChangeRef = useRef(props.onPlayheadChange)\n onPlayheadChangeRef.current = props.onPlayheadChange\n\n useEffect(() => {\n const next = createPlaybackClock({ fps, durationFrames: timeline.sequence.durationFrames })\n // Dispose the clock this run replaces — the initial placeholder on the first\n // run, or an already-disposed prior clock on a config change (dispose is\n // idempotent) — so no instance is left undisposed across recreations.\n clockRef.current.dispose()\n clockRef.current = next\n setClock(next)\n next.seek(Math.min(playheadFrameRef.current, timeline.sequence.durationFrames - 1))\n const unsubscribe = next.subscribe((frame) => {\n playheadFrameRef.current = frame\n setPlayheadFrame(frame)\n setIsPlaying(next.isPlaying())\n onPlayheadChangeRef.current?.(frame)\n })\n return () => {\n unsubscribe()\n next.dispose()\n }\n }, [fps, timeline.sequence.durationFrames])\n\n // The frame provider follows the same effect-owned lifecycle as the playback\n // clock: created and disposed inside the effect so a remount — including React\n // StrictMode's mount/unmount/remount probe — rebuilds it rather than leaving a\n // disposed provider in render. A caller-supplied provider is used as-is and is\n // never disposed here; only one we create is. The initial state value is a\n // placeholder the effect disposes and replaces on mount (or the caller's\n // provider, which the effect leaves untouched).\n const [frameProvider, setFrameProvider] = useState<VideoFrameProvider>(\n () => props.frameProvider ?? createVideoElementFrameProvider(),\n )\n const frameProviderRef = useRef(frameProvider)\n const ownsFrameProviderRef = useRef(!props.frameProvider)\n useEffect(() => {\n const next = props.frameProvider ?? createVideoElementFrameProvider()\n if (ownsFrameProviderRef.current) frameProviderRef.current.dispose()\n ownsFrameProviderRef.current = !props.frameProvider\n frameProviderRef.current = next\n setFrameProvider(next)\n return () => {\n if (!props.frameProvider) next.dispose()\n }\n }, [props.frameProvider])\n\n // --- view state -------------------------------------------------------------\n\n const zoomMath = useMemo(() => createZoomMath({ minZoom: MIN_ZOOM, maxZoom: MAX_ZOOM }), [])\n const [zoom, setZoom] = useState(1)\n const trackViewportRef = useRef<HTMLDivElement | null>(null)\n useEffect(() => {\n // Initial zoom fits the whole sequence into the visible track viewport.\n const viewport = trackViewportRef.current\n if (!viewport) return\n const laneWidth = viewport.clientWidth - TRACK_HEADER_PX\n if (laneWidth <= 0) return\n setZoom(Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, laneWidth / durationFrames)))\n // Fit once on mount; afterwards zoom belongs to the user.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n const [snapEnabled, setSnapEnabled] = useState(true)\n const [activeSnapPoint, setActiveSnapPoint] = useState<SnapPoint | null>(null)\n const [selectedClipIds, setSelectedClipIds] = useState<string[]>([])\n const [commitError, setCommitError] = useState<string | null>(null)\n\n const selectedClips = useMemo(\n () => timeline.clips.filter((clip) => selectedClipIds.includes(clip.id)),\n [timeline, selectedClipIds],\n )\n const onSelectionChangeRef = useRef(props.onSelectionChange)\n onSelectionChangeRef.current = props.onSelectionChange\n useEffect(() => {\n onSelectionChangeRef.current?.(selectedClips)\n }, [selectedClips])\n\n const sortedTracks = useMemo(() => [...timeline.tracks].sort((a, b) => a.sortOrder - b.sortOrder), [timeline.tracks])\n const clipsByTrack = useMemo(() => {\n const byTrack = new Map<string, SequenceClip[]>()\n for (const clip of timeline.clips) {\n const bucket = byTrack.get(clip.trackId)\n if (bucket) bucket.push(clip)\n else byTrack.set(clip.trackId, [clip])\n }\n return byTrack\n }, [timeline.clips])\n\n // --- command commit + history mirror ---------------------------------------\n\n interface HistoryEntry {\n command: TimelineCommand\n /** Optimistic ids this command's clip-creating operations minted, in\n * operation order — the pairing key for id reconciliation. */\n createdLocalIds: string[]\n }\n\n const historyRef = useRef<{ done: HistoryEntry[]; undone: HistoryEntry[] }>({ done: [], undone: [] })\n\n /** Local→server clip-id aliases, fed by `onApplyOperations` results and\n * consulted live by every command (see engine/commands resolveClipId). */\n const clipIdAliasesRef = useRef(new Map<string, string>())\n\n function resolveClipId(clipId: string): string {\n return clipIdAliasesRef.current.get(clipId) ?? clipId\n }\n\n /** Pair each clip-creating operation with its apply result and record the\n * local→server alias. Results are index-aligned with operations (the\n * `applySequenceOperations` contract); a host returning a mismatched array\n * is a wiring bug surfaced loud, not silently skipped. */\n function reconcileCreatedClipIds(\n operations: SequenceOperation[],\n createdLocalIds: string[],\n results: SequenceApplyResult[] | void,\n ) {\n if (createdLocalIds.length === 0 || !Array.isArray(results)) return\n if (results.length !== operations.length) {\n setCommitError(\n `onApplyOperations returned ${results.length} results for ${operations.length} operations — clip-id reconciliation skipped`,\n )\n return\n }\n let cursor = 0\n operations.forEach((operation, index) => {\n if (operation.type !== 'place_clip' && operation.type !== 'add_caption' && operation.type !== 'split_clip') return\n const localId = createdLocalIds[cursor]\n cursor += 1\n const result = results[index]\n if (localId !== undefined && result !== undefined && result.kind === 'clip') {\n clipIdAliasesRef.current.set(localId, result.clip.id)\n }\n })\n }\n\n function commitCommand(command: TimelineCommand, createdLocalIds: string[] = []) {\n if (!canWrite) return\n try {\n stack.execute(command)\n } catch (error) {\n setCommitError(error instanceof Error ? error.message : String(error))\n return\n }\n const history = historyRef.current\n const entry: HistoryEntry = { command, createdLocalIds }\n history.done.push(entry)\n if (history.done.length > HISTORY_MIRROR_LIMIT) history.done.splice(0, history.done.length - HISTORY_MIRROR_LIMIT)\n history.undone = []\n setCommitError(null)\n const operations = command.operations()\n void onApplyOperations(operations)\n .then((results) => reconcileCreatedClipIds(operations, createdLocalIds, results))\n .catch((error: unknown) => {\n // Roll back ONLY when this command is still the newest local edit; if\n // the user already undid or stacked more edits, local rollback would\n // corrupt history — surface the error and let the next server refresh\n // (stack.reset) reconcile.\n const mirror = historyRef.current\n if (mirror.done[mirror.done.length - 1] === entry && stack.canUndo()) {\n stack.undo()\n mirror.done.pop()\n }\n setCommitError(error instanceof Error ? error.message : String(error))\n })\n }\n\n function undoLast() {\n const history = historyRef.current\n const entry = history.done[history.done.length - 1]\n if (!entry || !stack.canUndo()) return\n try {\n stack.undo()\n } catch (error) {\n // A reset() removed the command's target clip; the stack kept the entry\n // (it may succeed after the next refresh), so the mirror stays too.\n setCommitError(`Undo failed: ${error instanceof Error ? error.message : String(error)}`)\n return\n }\n history.done.pop()\n history.undone.push(entry)\n void onApplyOperations(entry.command.inverseOperations()).catch((error: unknown) => {\n setCommitError(error instanceof Error ? error.message : String(error))\n })\n }\n\n function redoLast() {\n const history = historyRef.current\n const entry = history.undone[history.undone.length - 1]\n if (!entry || !stack.canRedo()) return\n try {\n stack.redo()\n } catch (error) {\n setCommitError(`Redo failed: ${error instanceof Error ? error.message : String(error)}`)\n return\n }\n history.undone.pop()\n history.done.push(entry)\n const operations = entry.command.operations()\n // A redo of a clip-creating command mints fresh server ids — re-pair them\n // so a later undo deletes the recreated clips, not the stale ones.\n void onApplyOperations(operations)\n .then((results) => reconcileCreatedClipIds(operations, entry.createdLocalIds, results))\n .catch((error: unknown) => {\n setCommitError(error instanceof Error ? error.message : String(error))\n })\n }\n\n // --- edit handlers ----------------------------------------------------------\n\n function handleCommitMove(input: ClipMoveCommit) {\n const current = stack.getState().timeline\n const clip = current.clips.find((candidate) => candidate.id === input.clipId)\n if (!clip) return\n commitCommand(\n moveClipCommand({\n timeline: current,\n clipId: input.clipId,\n startFrame: input.startFrame,\n ...(input.trackId !== clip.trackId ? { trackId: input.trackId } : {}),\n resolveClipId,\n }),\n )\n }\n\n function handleCommitTrim(input: ClipTrimCommit) {\n commitCommand(\n trimClipCommand({\n timeline: stack.getState().timeline,\n clipId: input.clipId,\n startFrame: input.startFrame,\n durationFrames: input.durationFrames,\n sourceInFrame: input.sourceInFrame,\n resolveClipId,\n }),\n )\n }\n\n function handleCommitText(input: { clipId: string; text: string }) {\n commitCommand(\n setClipTextCommand({ timeline: stack.getState().timeline, clipId: input.clipId, text: input.text, resolveClipId }),\n )\n }\n\n function deleteSelection() {\n const current = stack.getState().timeline\n const lockedTrackIds = new Set(current.tracks.filter((track) => track.locked).map((track) => track.id))\n const targets = selectedClips.filter((clip) => !lockedTrackIds.has(clip.trackId))\n if (targets.length === 0) return\n const commands = targets.map((clip) => deleteClipCommand({ timeline: current, clipId: clip.id, resolveClipId }))\n commitCommand(commands.length === 1 ? (commands[0] as TimelineCommand) : compositeCommand(`Delete ${commands.length} clips`, commands))\n setSelectedClipIds([])\n }\n\n const splittableClip =\n selectedClips.length === 1 &&\n selectedClips[0] &&\n playheadFrame > selectedClips[0].startFrame &&\n playheadFrame < selectedClips[0].startFrame + selectedClips[0].durationFrames\n ? selectedClips[0]\n : null\n\n function splitAtPlayhead() {\n if (!splittableClip) return\n const newClipId = mintClipId()\n commitCommand(\n splitClipCommand({\n timeline: stack.getState().timeline,\n clipId: splittableClip.id,\n atFrame: playheadFrame,\n newClipId,\n resolveClipId,\n }),\n [newClipId],\n )\n }\n\n function addCaptionAtPlayhead() {\n const current = stack.getState().timeline\n const captionTrack = [...current.tracks]\n .sort((a, b) => a.sortOrder - b.sortOrder)\n .find((track) => track.kind === 'caption' && !track.locked)\n if (!captionTrack) {\n setCommitError('No unlocked caption track in this sequence — add one before inserting captions.')\n return\n }\n // Throws when the caption track has no free gap left near the playhead.\n let placement: { startFrame: number; durationFrames: number }\n try {\n placement = chooseCaptionPlacement({\n playheadFrame,\n fps,\n sequenceDurationFrames: current.sequence.durationFrames,\n occupiedIntervals: trackIntervals(current, captionTrack.id),\n })\n } catch (error) {\n setCommitError(error instanceof Error ? error.message : String(error))\n return\n }\n const clipId = mintClipId()\n commitCommand(\n addCaptionCommand({\n timeline: current,\n clipId,\n trackId: captionTrack.id,\n text: 'New caption',\n startFrame: placement.startFrame,\n durationFrames: placement.durationFrames,\n resolveClipId,\n }),\n [clipId],\n )\n }\n\n function selectClip(clipId: string, additive: boolean) {\n setSelectedClipIds((current) => {\n if (!additive) return [clipId]\n return current.includes(clipId) ? current.filter((id) => id !== clipId) : [...current, clipId]\n })\n }\n\n // --- snapping ---------------------------------------------------------------\n\n function snapMove(candidate: { startFrame: number; durationFrames: number; clipId: string }) {\n if (!snapEnabled) return { startFrame: candidate.startFrame, point: null }\n const points = collectSnapPoints(timeline, playheadFrame)\n const exclude = (point: SnapPoint) => (point as TimelineSnapPoint).clipId === candidate.clipId\n return chooseMoveSnap({\n candidateStartFrame: candidate.startFrame,\n durationFrames: candidate.durationFrames,\n startSnap: applySnap(candidate.startFrame, points, { zoom, exclude }),\n endSnap: applySnap(candidate.startFrame + candidate.durationFrames, points, { zoom, exclude }),\n })\n }\n\n function snapEdge(candidate: { frame: number; clipId: string }) {\n if (!snapEnabled) return { frame: candidate.frame, point: null }\n const points = collectSnapPoints(timeline, playheadFrame)\n const result = applySnap(candidate.frame, points, {\n zoom,\n exclude: (point: SnapPoint) => (point as TimelineSnapPoint).clipId === candidate.clipId,\n })\n return { frame: result.frame, point: result.point }\n }\n\n // --- transport --------------------------------------------------------------\n\n function togglePlayback() {\n if (clock.isPlaying()) clock.pause()\n else clock.play()\n setIsPlaying(clock.isPlaying())\n }\n\n useEffect(() => {\n function onKeyDown(event: KeyboardEvent) {\n if (event.code === 'Space' && !isTypingTarget(event.target)) {\n event.preventDefault()\n togglePlayback()\n return\n }\n if ((event.key === 'Delete' || event.key === 'Backspace') && !isTypingTarget(event.target)) {\n if (!canWrite) return\n event.preventDefault()\n deleteSelection()\n return\n }\n const mod = event.metaKey || event.ctrlKey\n if (!mod || isTypingTarget(event.target)) return\n if (event.key.toLowerCase() === 'z') {\n event.preventDefault()\n if (event.shiftKey) redoLast()\n else undoLast()\n } else if (event.key.toLowerCase() === 'y') {\n event.preventDefault()\n redoLast()\n }\n }\n window.addEventListener('keydown', onKeyDown)\n return () => window.removeEventListener('keydown', onKeyDown)\n })\n\n // --- asset drops ------------------------------------------------------------\n\n function handleTrackAreaDragOver(event: ReactDragEvent<HTMLDivElement>) {\n if (!canWrite || !event.dataTransfer.types.includes(SEQUENCE_MEDIA_DRAG_TYPE)) return\n event.preventDefault()\n event.dataTransfer.dropEffect = 'copy'\n }\n\n function handleTrackAreaDrop(event: ReactDragEvent<HTMLDivElement>) {\n if (!canWrite || !event.dataTransfer.types.includes(SEQUENCE_MEDIA_DRAG_TYPE)) return\n event.preventDefault()\n const lane = event.target instanceof Element ? event.target.closest<HTMLElement>('[data-lane-track]') : null\n if (!lane || !lane.dataset.laneTrack) {\n setCommitError('Drop media on a track lane to place it.')\n return\n }\n let payload: MediaDragPayload\n try {\n payload = parseMediaDragPayload(event.dataTransfer.getData(SEQUENCE_MEDIA_DRAG_TYPE))\n } catch (error) {\n setCommitError(error instanceof Error ? error.message : String(error))\n return\n }\n const laneKind = lane.dataset.laneKind\n const accepts = laneKind === 'video' ? payload.kind === 'video' || payload.kind === 'image' : laneKind === 'audio' ? payload.kind === 'audio' : false\n if (!accepts || lane.dataset.laneLocked === 'true') {\n setCommitError(`A ${laneKind ?? 'unknown'} track cannot take ${payload.kind} media${lane.dataset.laneLocked === 'true' ? ' (track is locked)' : ''}.`)\n return\n }\n const current = stack.getState().timeline\n const rect = lane.getBoundingClientRect()\n const dropFrame = Math.max(0, Math.round((event.clientX - rect.left) / zoom))\n const naturalDuration = payload.durationSeconds !== undefined\n ? secondsToFrames(payload.durationSeconds, fps)\n : payload.kind === 'image'\n ? fps * 3\n : fps * 5\n const startFrame = Math.min(dropFrame, current.sequence.durationFrames - 1)\n const placedDuration = Math.max(1, Math.min(naturalDuration, current.sequence.durationFrames - startFrame))\n const clipId = mintClipId()\n commitCommand(\n placeClipCommand({\n timeline: current,\n clipId,\n trackId: lane.dataset.laneTrack,\n label: payload.label ?? payload.url.split('/').pop() ?? payload.url,\n startFrame,\n durationFrames: placedDuration,\n media: { url: payload.url, kind: payload.kind },\n ...(payload.generationId !== undefined ? { generationId: payload.generationId } : {}),\n ...(payload.assetId !== undefined ? { assetId: payload.assetId } : {}),\n resolveClipId,\n }),\n [clipId],\n )\n }\n\n // --- render -------------------------------------------------------------------\n\n const timelineWidth = timeline.sequence.durationFrames * zoom\n\n return (\n <div className={`flex h-full min-h-0 flex-col bg-[var(--bg-input)] text-[var(--text-primary)] ${props.className ?? ''}`}>\n <div className=\"flex min-h-0 flex-1\">\n <div className=\"flex min-w-0 flex-1 flex-col\">\n <PreviewCanvas timeline={timeline} clock={clock} frameProvider={frameProvider} />\n\n <div className=\"flex h-10 shrink-0 items-center gap-2 border-y border-[var(--border-default)] px-2\">\n <button\n type=\"button\"\n aria-label={isPlaying ? 'Pause' : 'Play'}\n onClick={togglePlayback}\n className={TRANSPORT_BUTTON}\n >\n {isPlaying ? <PauseGlyph className=\"h-3.5 w-3.5\" /> : <PlayGlyph className=\"h-3.5 w-3.5\" />}\n </button>\n <span className=\"font-mono text-xs tabular-nums text-[var(--text-secondary)]\">\n {formatTimecode(playheadFrame, fps)}\n <span className=\"text-[var(--text-muted)]\"> / {formatTimecode(timeline.sequence.durationFrames, fps)}</span>\n </span>\n\n <div className=\"mx-1 h-4 w-px bg-[var(--border-default)]\" />\n\n <button type=\"button\" aria-label=\"Undo\" disabled={!stack.canUndo() || !canWrite} onClick={undoLast} className={TRANSPORT_BUTTON}>\n <UndoGlyph className=\"h-3.5 w-3.5\" />\n </button>\n <button type=\"button\" aria-label=\"Redo\" disabled={!stack.canRedo() || !canWrite} onClick={redoLast} className={TRANSPORT_BUTTON}>\n <RedoGlyph className=\"h-3.5 w-3.5\" />\n </button>\n <button\n type=\"button\"\n aria-label=\"Toggle snapping\"\n aria-pressed={snapEnabled}\n onClick={() => setSnapEnabled((current) => !current)}\n className={`${TRANSPORT_BUTTON} ${snapEnabled ? 'border-[var(--brand-primary)] text-[var(--brand-primary)] hover:text-[var(--brand-primary)]' : ''}`}\n >\n <MagnetGlyph className=\"h-3.5 w-3.5\" />\n </button>\n\n {canWrite ? (\n <>\n <button type=\"button\" aria-label=\"Split clip at playhead\" disabled={!splittableClip} onClick={splitAtPlayhead} className={TRANSPORT_BUTTON}>\n <ScissorsGlyph className=\"h-3.5 w-3.5\" />\n </button>\n <button type=\"button\" aria-label=\"Add caption at playhead\" onClick={addCaptionAtPlayhead} className={TRANSPORT_BUTTON}>\n <CaptionPlusGlyph className=\"h-3.5 w-3.5\" />\n </button>\n </>\n ) : null}\n\n <div className=\"flex-1\" />\n <ZoomControl zoomMath={zoomMath} zoom={zoom} onZoomChange={setZoom} />\n </div>\n\n {commitError ? (\n <div className=\"flex shrink-0 items-center justify-between gap-3 border-b border-rose-500/30 bg-rose-500/10 px-3 py-1.5 text-xs text-rose-300\" role=\"alert\">\n <span className=\"min-w-0 truncate\">{commitError}</span>\n <button type=\"button\" onClick={() => setCommitError(null)} className=\"shrink-0 underline-offset-2 hover:underline\">\n Dismiss\n </button>\n </div>\n ) : null}\n\n {props.renderAssetShelf ? (\n <div className=\"shrink-0 border-b border-[var(--border-default)]\">{props.renderAssetShelf()}</div>\n ) : null}\n\n <div\n ref={trackViewportRef}\n data-timeline-tracks\n className=\"relative max-h-60 min-h-[6rem] shrink-0 overflow-auto overscroll-x-contain\"\n onDragOver={handleTrackAreaDragOver}\n onDrop={handleTrackAreaDrop}\n >\n <div className=\"relative\" style={{ width: `${TRACK_HEADER_PX + timelineWidth}px`, minWidth: '100%' }}>\n <div className=\"sticky top-0 z-20 flex\">\n <div className=\"sticky left-0 z-30 w-36 shrink-0 border-b border-r border-[var(--border-default)] bg-[var(--bg-input)]\" />\n <TimelineRuler fps={fps} durationFrames={timeline.sequence.durationFrames} zoom={zoom} onScrub={(frame) => clock.seek(frame)} />\n </div>\n\n <div className=\"relative\">\n {sortedTracks.map((track) => (\n <TimelineTrackRow\n key={track.id}\n track={track}\n clips={clipsByTrack.get(track.id) ?? []}\n fps={fps}\n zoom={zoom}\n sequenceDurationFrames={timeline.sequence.durationFrames}\n selectedClipIds={new Set(selectedClipIds)}\n canWrite={canWrite}\n frameProvider={frameProvider}\n snapMove={snapMove}\n snapEdge={snapEdge}\n onSnapPointChange={setActiveSnapPoint}\n onSelectClip={selectClip}\n onCommitMove={handleCommitMove}\n onCommitTrim={handleCommitTrim}\n onCommitText={handleCommitText}\n onLaneSeek={(frame) => clock.seek(frame)}\n />\n ))}\n <div className=\"pointer-events-none absolute inset-y-0\" style={{ left: `${TRACK_HEADER_PX}px`, width: `${timelineWidth}px` }}>\n <TimelinePlayhead frame={playheadFrame} zoom={zoom} />\n <SnapIndicatorLine point={activeSnapPoint} zoom={zoom} />\n </div>\n </div>\n </div>\n </div>\n </div>\n\n {props.renderSidePanel ? (\n <aside className=\"flex w-80 shrink-0 flex-col overflow-hidden border-l border-[var(--border-default)]\">\n {props.renderSidePanel({ selectedClips, playheadFrame })}\n </aside>\n ) : null}\n </div>\n </div>\n )\n}\n\nexport default TimelineEditor\n","/**\n * Undo/redo command stack over immutable `EditorTimelineState`.\n *\n * Invariant that makes `reset()` safe: history entries hold COMMANDS — each a\n * pair of state transforms plus the durable operations captured at\n * construction — never state snapshots. Rebasing the timeline from a server\n * refresh therefore cannot stale the history: a later undo re-applies the\n * command's inverse transform to whatever timeline is current. If a rebase\n * removed a clip a historical command targets, that command's transform throws\n * (fail loud) rather than silently editing the wrong clip — the host decides\n * whether to drop history at that point.\n */\n\nimport type { SequenceTimeline } from '../../sequences/model'\nimport type { CommandStack, EditorTimelineState, TimelineCommand } from '../contracts'\n\n/** Oldest entries are dropped past this bound; redo is cleared on execute. */\nexport const COMMAND_HISTORY_LIMIT = 200\n\nexport function createCommandStack(initial: SequenceTimeline): CommandStack {\n /** View fields start at their neutral values; the host layer owns volatile\n * view state and treats these as initials, not a live channel. */\n let state: EditorTimelineState = {\n timeline: initial,\n playheadFrame: 0,\n selectedClipIds: [],\n zoom: 1,\n scrollLeft: 0,\n }\n const undoStack: TimelineCommand[] = []\n const redoStack: TimelineCommand[] = []\n const listeners = new Set<() => void>()\n\n const notify = (): void => {\n for (const listener of [...listeners]) listener()\n }\n\n return {\n execute(command: TimelineCommand): void {\n state = command.execute(state)\n undoStack.push(command)\n if (undoStack.length > COMMAND_HISTORY_LIMIT) {\n undoStack.splice(0, undoStack.length - COMMAND_HISTORY_LIMIT)\n }\n redoStack.length = 0\n notify()\n },\n\n // Both transforms run BEFORE the stacks move: a throwing transform (the\n // documented missing-clip path after reset()) leaves history and state\n // exactly as they were, so the entry is never silently destroyed and the\n // caller can retry after the next refresh restores the target.\n undo(): void {\n const command = undoStack[undoStack.length - 1]\n if (!command) throw new Error('nothing to undo — guard with canUndo() before calling undo()')\n state = command.undo(state)\n undoStack.pop()\n redoStack.push(command)\n notify()\n },\n\n redo(): void {\n const command = redoStack[redoStack.length - 1]\n if (!command) throw new Error('nothing to redo — guard with canRedo() before calling redo()')\n state = command.execute(state)\n redoStack.pop()\n undoStack.push(command)\n notify()\n },\n\n canUndo(): boolean {\n return undoStack.length > 0\n },\n\n canRedo(): boolean {\n return redoStack.length > 0\n },\n\n subscribe(listener: () => void): () => void {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n },\n\n getState(): EditorTimelineState {\n return state\n },\n\n /** Rebase onto a server-refreshed timeline. History survives (see module\n * header); selection drops ids the refresh removed and the playhead\n * clamps into the new duration so view state never dangles. */\n reset(timeline: SequenceTimeline): void {\n const liveClipIds = new Set(timeline.clips.map((clip) => clip.id))\n state = {\n ...state,\n timeline,\n playheadFrame: Math.max(0, Math.min(state.playheadFrame, timeline.sequence.durationFrames - 1)),\n selectedClipIds: state.selectedClipIds.filter((id) => liveClipIds.has(id)),\n }\n notify()\n },\n }\n}\n","/**\n * Concrete `TimelineCommand` factories. Every factory captures the inverse\n * from PRE-state at construction — undo is a value computed once, never a\n * re-derivation from whatever state exists later. Local execute/undo update\n * `EditorTimelineState` immutably; `operations()`/`inverseOperations()` return\n * the durable `SequenceOperation[]` equivalent, built per call from captured\n * primitives so callers can never alias command internals.\n *\n * Id boundary: `place_clip`, `add_caption`, and `split_clip` mint clip ids\n * server-side, so factories that create local clips take a caller-minted id.\n * Every factory also takes an optional `resolveClipId` — a LIVE local→server\n * alias lookup the host feeds from `onApplyOperations` results. Resolution\n * runs at execute/undo/emission time (never at construction), so a command\n * captured against an optimistic `local-…` id keeps working after a server\n * refresh replaced it with the minted id. Residual boundary: clips a durable\n * undo recreates (e.g. the `place_clip` inverse of a delete) mint FRESH\n * server ids that only the next refresh reconciles.\n *\n * Transforms re-resolve their target clip at execute/undo time and throw when\n * it no longer exists (e.g. removed by a `reset()` rebase) — editing a wrong\n * or absent clip silently would corrupt the durable op stream.\n */\n\nimport {\n assertClipFitsSequence,\n clampClipStart,\n type SequenceClip,\n type SequenceTimeline,\n type SequenceTrack,\n} from '../../sequences/model'\nimport type { EditorTimelineState, TimelineCommand } from '../contracts'\n\n// ---------------------------------------------------------------------------\n// Shared lookup + immutable-update helpers\n// ---------------------------------------------------------------------------\n\n/** Live local→server clip-id lookup (see module header). Must return its\n * argument when no alias exists. */\nexport type ClipIdResolver = (clipId: string) => string\n\nconst identityClipId: ClipIdResolver = (clipId) => clipId\n\nfunction requireClip(timeline: SequenceTimeline, clipId: string, context: string): SequenceClip {\n const clip = timeline.clips.find((candidate) => candidate.id === clipId)\n if (!clip) throw new Error(`${context}: clip ${clipId} does not exist in sequence ${timeline.sequence.id}`)\n return clip\n}\n\nfunction requireUnlockedTrack(timeline: SequenceTimeline, trackId: string, context: string): SequenceTrack {\n const track = timeline.tracks.find((candidate) => candidate.id === trackId)\n if (!track) throw new Error(`${context}: track ${trackId} does not exist in sequence ${timeline.sequence.id}`)\n if (track.locked) throw new Error(`${context}: track ${track.name} (${trackId}) is locked`)\n return track\n}\n\nfunction assertNewClipId(timeline: SequenceTimeline, clipId: string, context: string): void {\n if (timeline.clips.some((candidate) => candidate.id === clipId)) {\n throw new Error(`${context}: clip id ${clipId} already exists in sequence ${timeline.sequence.id}`)\n }\n}\n\nfunction patchClip(\n state: EditorTimelineState,\n clipId: string,\n context: string,\n patch: Partial<SequenceClip>,\n): EditorTimelineState {\n requireClip(state.timeline, clipId, context)\n return {\n ...state,\n timeline: {\n ...state.timeline,\n clips: state.timeline.clips.map((clip) => (clip.id === clipId ? { ...clip, ...patch } : clip)),\n },\n }\n}\n\nfunction insertClip(state: EditorTimelineState, clip: SequenceClip, context: string): EditorTimelineState {\n assertNewClipId(state.timeline, clip.id, context)\n return {\n ...state,\n timeline: { ...state.timeline, clips: [...state.timeline.clips, clip] },\n }\n}\n\nfunction removeClip(state: EditorTimelineState, clipId: string, context: string): EditorTimelineState {\n requireClip(state.timeline, clipId, context)\n return {\n ...state,\n timeline: {\n ...state.timeline,\n clips: state.timeline.clips.filter((clip) => clip.id !== clipId),\n },\n selectedClipIds: state.selectedClipIds.filter((id) => id !== clipId),\n }\n}\n\n// ---------------------------------------------------------------------------\n// move_clip\n// ---------------------------------------------------------------------------\n\nexport interface MoveClipInput {\n timeline: SequenceTimeline\n clipId: string\n startFrame: number\n /** Omitted → stays on its current track. */\n trackId?: string\n resolveClipId?: ClipIdResolver\n}\n\n/** Drag-move. The target start clamps through the model's `clampClipStart`\n * so drags past either edge land at the boundary instead of throwing\n * mid-gesture; the emitted operation carries the clamped value. */\nexport function moveClipCommand(input: MoveClipInput): TimelineCommand {\n const context = 'move_clip'\n const clip = requireClip(input.timeline, input.clipId, context)\n const targetTrackId = input.trackId ?? clip.trackId\n requireUnlockedTrack(input.timeline, targetTrackId, context)\n if (!Number.isInteger(input.startFrame)) throw new Error(`${context}: startFrame must be an integer frame`)\n const targetStart = clampClipStart({\n startFrame: input.startFrame,\n durationFrames: clip.durationFrames,\n sequenceDurationFrames: input.timeline.sequence.durationFrames,\n })\n const originalStart = clip.startFrame\n const originalTrackId = clip.trackId\n const trackChanged = targetTrackId !== originalTrackId\n const clipId = input.clipId\n const resolve = input.resolveClipId ?? identityClipId\n\n return {\n label: `Move ${clip.label}`,\n execute: (state) => patchClip(state, resolve(clipId), context, { startFrame: targetStart, trackId: targetTrackId }),\n undo: (state) => patchClip(state, resolve(clipId), context, { startFrame: originalStart, trackId: originalTrackId }),\n operations: () => [\n { type: 'move_clip', clipId: resolve(clipId), startFrame: targetStart, ...(trackChanged ? { trackId: targetTrackId } : {}) },\n ],\n inverseOperations: () => [\n { type: 'move_clip', clipId: resolve(clipId), startFrame: originalStart, ...(trackChanged ? { trackId: originalTrackId } : {}) },\n ],\n }\n}\n\n// ---------------------------------------------------------------------------\n// trim_clip\n// ---------------------------------------------------------------------------\n\nexport interface TrimClipInput {\n timeline: SequenceTimeline\n clipId: string\n startFrame: number\n durationFrames: number\n /** New source in-point when trimming the head; omitted → unchanged. */\n sourceInFrame?: number\n resolveClipId?: ClipIdResolver\n}\n\n/** Trim is strict where move is forgiving: the caller (a trim handle) already\n * knows both edges, so out-of-bounds input is a bug, not a gesture. */\nexport function trimClipCommand(input: TrimClipInput): TimelineCommand {\n const context = 'trim_clip'\n const clip = requireClip(input.timeline, input.clipId, context)\n assertClipFitsSequence({\n startFrame: input.startFrame,\n durationFrames: input.durationFrames,\n sequenceDurationFrames: input.timeline.sequence.durationFrames,\n label: `${context} ${clip.label}`,\n })\n if (input.sourceInFrame !== undefined && (!Number.isInteger(input.sourceInFrame) || input.sourceInFrame < 0)) {\n throw new Error(`${context}: sourceInFrame must be a non-negative integer`)\n }\n const targetSourceIn = input.sourceInFrame ?? clip.sourceInFrame\n // Mirror of the server-side source-window invariant: a clip with an explicit\n // out-point (a split half) cannot claim more source frames than the window\n // holds — failing here keeps optimistic state from diverging into a commit\n // the validator rejects.\n if (clip.sourceOutFrame !== null && targetSourceIn + input.durationFrames > clip.sourceOutFrame) {\n throw new Error(\n `${context}: needs ${input.durationFrames} source frames from ${targetSourceIn} but ${clip.label} has source window [${clip.sourceInFrame}, ${clip.sourceOutFrame})`,\n )\n }\n const target = { startFrame: input.startFrame, durationFrames: input.durationFrames, sourceInFrame: targetSourceIn }\n const original = { startFrame: clip.startFrame, durationFrames: clip.durationFrames, sourceInFrame: clip.sourceInFrame }\n const clipId = input.clipId\n const resolve = input.resolveClipId ?? identityClipId\n\n return {\n label: `Trim ${clip.label}`,\n execute: (state) => patchClip(state, resolve(clipId), context, target),\n undo: (state) => patchClip(state, resolve(clipId), context, original),\n operations: () => [{ type: 'trim_clip', clipId: resolve(clipId), ...target }],\n inverseOperations: () => [{ type: 'trim_clip', clipId: resolve(clipId), ...original }],\n }\n}\n\n// ---------------------------------------------------------------------------\n// place_clip\n// ---------------------------------------------------------------------------\n\nexport interface PlaceClipInput {\n timeline: SequenceTimeline\n /** Caller-minted optimistic id for the local clip (see module header). */\n clipId: string\n trackId: string\n label: string\n startFrame: number\n durationFrames: number\n /** Omitted → 0 (start of the source). */\n sourceInFrame?: number\n media?: { url: string; kind: 'video' | 'image' | 'audio' }\n generationId?: string\n assetId?: string\n metadata?: Record<string, unknown>\n resolveClipId?: ClipIdResolver\n}\n\nexport function placeClipCommand(input: PlaceClipInput): TimelineCommand {\n const context = 'place_clip'\n assertNewClipId(input.timeline, input.clipId, context)\n requireUnlockedTrack(input.timeline, input.trackId, context)\n assertClipFitsSequence({\n startFrame: input.startFrame,\n durationFrames: input.durationFrames,\n sequenceDurationFrames: input.timeline.sequence.durationFrames,\n label: `${context} ${input.label}`,\n })\n const sourceInFrame = input.sourceInFrame ?? 0\n if (!Number.isInteger(sourceInFrame) || sourceInFrame < 0) {\n throw new Error(`${context}: sourceInFrame must be a non-negative integer`)\n }\n const clip: SequenceClip = {\n id: input.clipId,\n trackId: input.trackId,\n label: input.label,\n startFrame: input.startFrame,\n durationFrames: input.durationFrames,\n sourceInFrame,\n sourceOutFrame: null,\n disabled: false,\n ...(input.media ? { media: { url: input.media.url, kind: input.media.kind } } : {}),\n ...(input.generationId !== undefined ? { generationId: input.generationId } : {}),\n ...(input.assetId !== undefined ? { assetId: input.assetId } : {}),\n metadata: input.metadata ?? {},\n }\n const clipId = input.clipId\n const resolve = input.resolveClipId ?? identityClipId\n\n return {\n label: `Place ${input.label}`,\n execute: (state) => insertClip(state, { ...structuredClone(clip), id: resolve(clipId) }, context),\n undo: (state) => removeClip(state, resolve(clipId), context),\n operations: () => [\n {\n type: 'place_clip',\n trackId: clip.trackId,\n label: clip.label,\n startFrame: clip.startFrame,\n durationFrames: clip.durationFrames,\n sourceInFrame,\n ...(input.media ? { media: { url: input.media.url, kind: input.media.kind } } : {}),\n ...(input.generationId !== undefined ? { generationId: input.generationId } : {}),\n ...(input.assetId !== undefined ? { assetId: input.assetId } : {}),\n ...(input.metadata !== undefined ? { metadata: input.metadata } : {}),\n },\n ],\n inverseOperations: () => [{ type: 'delete_clip', clipId: resolve(clipId) }],\n }\n}\n\n// ---------------------------------------------------------------------------\n// delete_clip\n// ---------------------------------------------------------------------------\n\nexport interface DeleteClipInput {\n timeline: SequenceTimeline\n clipId: string\n resolveClipId?: ClipIdResolver\n}\n\n/** Snapshots the full clip at construction so undo restores it exactly. The\n * durable inverse rebuilds within the closed operation union: caption clips\n * (caption track + text) invert through `add_caption` — `place_clip` cannot\n * carry text/language — everything else through `place_clip` with media,\n * product references, source window, and disabled state intact. Boundary:\n * the caption path cannot carry `disabled` or `metadata`, so a deleted\n * disabled caption resurrects ENABLED server-side and caption metadata is\n * lost — local undo stays exact (same class of loss as the `set_clip_text`\n * language-clear boundary). */\nexport function deleteClipCommand(input: DeleteClipInput): TimelineCommand {\n const context = 'delete_clip'\n const snapshot = structuredClone(requireClip(input.timeline, input.clipId, context))\n const track = input.timeline.tracks.find((candidate) => candidate.id === snapshot.trackId)\n if (!track) throw new Error(`${context}: clip ${snapshot.id} references unknown track ${snapshot.trackId}`)\n const captionText =\n track.kind === 'caption' && typeof snapshot.text === 'string' && snapshot.text.length > 0 ? snapshot.text : null\n const clipId = input.clipId\n const resolve = input.resolveClipId ?? identityClipId\n\n return {\n label: `Delete ${snapshot.label}`,\n execute: (state) => removeClip(state, resolve(clipId), context),\n undo: (state) => insertClip(state, { ...structuredClone(snapshot), id: resolve(clipId) }, context),\n operations: () => [{ type: 'delete_clip', clipId: resolve(clipId) }],\n inverseOperations: () =>\n captionText !== null\n ? [\n {\n type: 'add_caption',\n text: captionText,\n ...(snapshot.language !== undefined ? { language: snapshot.language } : {}),\n startFrame: snapshot.startFrame,\n durationFrames: snapshot.durationFrames,\n trackId: snapshot.trackId,\n },\n ]\n : [\n {\n type: 'place_clip',\n trackId: snapshot.trackId,\n label: snapshot.label,\n startFrame: snapshot.startFrame,\n durationFrames: snapshot.durationFrames,\n sourceInFrame: snapshot.sourceInFrame,\n ...(snapshot.sourceOutFrame !== null ? { sourceOutFrame: snapshot.sourceOutFrame } : {}),\n ...(snapshot.disabled ? { disabled: true } : {}),\n ...(snapshot.media ? { media: { url: snapshot.media.url, kind: snapshot.media.kind } } : {}),\n ...(snapshot.generationId !== undefined ? { generationId: snapshot.generationId } : {}),\n ...(snapshot.assetId !== undefined ? { assetId: snapshot.assetId } : {}),\n metadata: structuredClone(snapshot.metadata),\n },\n ],\n }\n}\n\n// ---------------------------------------------------------------------------\n// split_clip\n// ---------------------------------------------------------------------------\n\nexport interface SplitClipInput {\n timeline: SequenceTimeline\n clipId: string\n /** Sequence-frame to cut at; must fall strictly inside the clip. */\n atFrame: number\n /** Caller-minted id for the second (tail) clip. */\n newClipId: string\n resolveClipId?: ClipIdResolver\n}\n\n/** Source mapping is 1:1 frames (no rate ramps in the model), so the tail's\n * source in-point is the head's in-point advanced by the head duration. */\nexport function splitClipCommand(input: SplitClipInput): TimelineCommand {\n const context = 'split_clip'\n const original = structuredClone(requireClip(input.timeline, input.clipId, context))\n assertNewClipId(input.timeline, input.newClipId, context)\n if (!Number.isInteger(input.atFrame)) throw new Error(`${context}: atFrame must be an integer frame`)\n const clipEnd = original.startFrame + original.durationFrames\n if (input.atFrame <= original.startFrame || input.atFrame >= clipEnd) {\n throw new Error(\n `${context}: atFrame ${input.atFrame} must fall strictly inside clip ${original.id} [${original.startFrame}, ${clipEnd})`,\n )\n }\n const headDurationFrames = input.atFrame - original.startFrame\n const tailDurationFrames = clipEnd - input.atFrame\n const tail: SequenceClip = {\n ...structuredClone(original),\n id: input.newClipId,\n startFrame: input.atFrame,\n durationFrames: tailDurationFrames,\n sourceInFrame: original.sourceInFrame + headDurationFrames,\n }\n const clipId = input.clipId\n const newClipId = input.newClipId\n const resolve = input.resolveClipId ?? identityClipId\n // Mirrors applySplitClip: the head's out-point becomes explicit at the cut,\n // so optimistic state matches what the server persists.\n const headSourceOutFrame = original.sourceInFrame + headDurationFrames\n\n return {\n label: `Split ${original.label}`,\n execute: (state) =>\n insertClip(\n patchClip(state, resolve(clipId), context, { durationFrames: headDurationFrames, sourceOutFrame: headSourceOutFrame }),\n { ...structuredClone(tail), id: resolve(newClipId) },\n context,\n ),\n undo: (state) =>\n patchClip(removeClip(state, resolve(newClipId), context), resolve(clipId), context, {\n ...structuredClone(original),\n id: resolve(clipId),\n }),\n operations: () => [{ type: 'split_clip', clipId: resolve(clipId), atFrame: input.atFrame }],\n inverseOperations: () => [\n { type: 'delete_clip', clipId: resolve(newClipId) },\n {\n type: 'trim_clip',\n clipId: resolve(clipId),\n startFrame: original.startFrame,\n durationFrames: original.durationFrames,\n sourceInFrame: original.sourceInFrame,\n // Restores the pre-split window; without it the head keeps its\n // out-point at the cut while regaining the full duration.\n sourceOutFrame: original.sourceOutFrame,\n },\n ],\n }\n}\n\n// ---------------------------------------------------------------------------\n// add_caption\n// ---------------------------------------------------------------------------\n\nexport interface AddCaptionInput {\n timeline: SequenceTimeline\n /** Caller-minted optimistic id for the local caption clip. */\n clipId: string\n /** Editor commands are concrete: the caller resolves placement (e.g. via\n * `chooseCaptionPlacement`) and the target track before constructing. */\n trackId: string\n text: string\n language?: string\n startFrame: number\n durationFrames: number\n resolveClipId?: ClipIdResolver\n}\n\nexport function addCaptionCommand(input: AddCaptionInput): TimelineCommand {\n const context = 'add_caption'\n assertNewClipId(input.timeline, input.clipId, context)\n const track = requireUnlockedTrack(input.timeline, input.trackId, context)\n if (track.kind !== 'caption') {\n throw new Error(`${context}: track ${track.name} (${track.id}) is kind ${track.kind}; captions require a caption track`)\n }\n if (typeof input.text !== 'string' || input.text.length === 0) {\n throw new Error(`${context}: text must be a non-empty string`)\n }\n assertClipFitsSequence({\n startFrame: input.startFrame,\n durationFrames: input.durationFrames,\n sequenceDurationFrames: input.timeline.sequence.durationFrames,\n label: context,\n })\n const clip: SequenceClip = {\n id: input.clipId,\n trackId: input.trackId,\n label: input.text,\n startFrame: input.startFrame,\n durationFrames: input.durationFrames,\n sourceInFrame: 0,\n sourceOutFrame: null,\n disabled: false,\n text: input.text,\n ...(input.language !== undefined ? { language: input.language } : {}),\n metadata: {},\n }\n const clipId = input.clipId\n const resolve = input.resolveClipId ?? identityClipId\n\n return {\n label: `Add caption`,\n execute: (state) => insertClip(state, { ...structuredClone(clip), id: resolve(clipId) }, context),\n undo: (state) => removeClip(state, resolve(clipId), context),\n operations: () => [\n {\n type: 'add_caption',\n text: input.text,\n ...(input.language !== undefined ? { language: input.language } : {}),\n startFrame: input.startFrame,\n durationFrames: input.durationFrames,\n trackId: input.trackId,\n },\n ],\n inverseOperations: () => [{ type: 'delete_clip', clipId: resolve(clipId) }],\n }\n}\n\n// ---------------------------------------------------------------------------\n// set_clip_text\n// ---------------------------------------------------------------------------\n\nexport interface SetClipTextInput {\n timeline: SequenceTimeline\n clipId: string\n text: string\n /** Omitted → language unchanged. */\n language?: string\n resolveClipId?: ClipIdResolver\n}\n\n/** Requires the clip to already carry text: `set_clip_text` has no \"create\"\n * semantics in the union, and an inverse for a text-less clip would have to\n * invent an empty string. Boundary: when the clip had NO language and this\n * command sets one, local undo restores `undefined` exactly but the durable\n * inverse cannot clear language (the op has no clear form) — flagged to the\n * apply layer. */\nexport function setClipTextCommand(input: SetClipTextInput): TimelineCommand {\n const context = 'set_clip_text'\n const clip = requireClip(input.timeline, input.clipId, context)\n if (typeof clip.text !== 'string') {\n throw new Error(`${context}: clip ${clip.id} has no text body; create caption text through add_caption`)\n }\n if (typeof input.text !== 'string' || input.text.length === 0) {\n throw new Error(`${context}: text must be a non-empty string`)\n }\n const originalText = clip.text\n const originalLanguage = clip.language\n const targetLanguage = input.language ?? clip.language\n /** Caption labels mirror their text (see addCaptionCommand); keep the\n * mirror in sync only when it was in sync to begin with. */\n const mirrorsLabel = clip.label === clip.text\n const clipId = input.clipId\n const resolve = input.resolveClipId ?? identityClipId\n\n return {\n label: `Edit caption text`,\n execute: (state) =>\n patchClip(state, resolve(clipId), context, {\n text: input.text,\n language: targetLanguage,\n ...(mirrorsLabel ? { label: input.text } : {}),\n }),\n undo: (state) =>\n patchClip(state, resolve(clipId), context, {\n text: originalText,\n language: originalLanguage,\n ...(mirrorsLabel ? { label: originalText } : {}),\n }),\n operations: () => [\n { type: 'set_clip_text', clipId: resolve(clipId), text: input.text, ...(input.language !== undefined ? { language: input.language } : {}) },\n ],\n inverseOperations: () => [\n {\n type: 'set_clip_text',\n clipId: resolve(clipId),\n text: originalText,\n ...(originalLanguage !== undefined ? { language: originalLanguage } : {}),\n },\n ],\n }\n}\n\n// ---------------------------------------------------------------------------\n// set_clip_disabled (toggle)\n// ---------------------------------------------------------------------------\n\nexport interface ToggleClipDisabledInput {\n timeline: SequenceTimeline\n clipId: string\n resolveClipId?: ClipIdResolver\n}\n\n/** The target value is captured at construction (not flipped at execute time)\n * so redo after a rebase applies the same durable op the stack already\n * emitted. */\nexport function toggleClipDisabledCommand(input: ToggleClipDisabledInput): TimelineCommand {\n const context = 'set_clip_disabled'\n const clip = requireClip(input.timeline, input.clipId, context)\n const original = clip.disabled\n const target = !original\n const clipId = input.clipId\n const resolve = input.resolveClipId ?? identityClipId\n\n return {\n label: target ? `Disable ${clip.label}` : `Enable ${clip.label}`,\n execute: (state) => patchClip(state, resolve(clipId), context, { disabled: target }),\n undo: (state) => patchClip(state, resolve(clipId), context, { disabled: original }),\n operations: () => [{ type: 'set_clip_disabled', clipId: resolve(clipId), disabled: target }],\n inverseOperations: () => [{ type: 'set_clip_disabled', clipId: resolve(clipId), disabled: original }],\n }\n}\n","/**\n * rAF playback clock. The playhead is derived from a (start time, start\n * frame) anchor and `performance.now()` deltas — never from per-tick\n * increments — so dropped animation frames cannot accumulate drift.\n *\n * Browser-only at PLAY time, import-safe everywhere: `requestAnimationFrame`\n * and `performance` are resolved off `globalThis` when playback starts, never\n * at module load or construction, so server bundles that import the editor\n * engine do not crash.\n */\n\nimport type { PlaybackClock } from '../contracts'\n\nexport interface PlaybackClockConfig {\n fps: number\n durationFrames: number\n}\n\ninterface RafGlobals {\n requestAnimationFrame?: (callback: (time: number) => void) => number\n cancelAnimationFrame?: (id: number) => void\n performance?: { now(): number }\n}\n\nfunction resolveRaf(): {\n request: (callback: (time: number) => void) => number\n cancel: (id: number) => void\n} {\n const g = globalThis as RafGlobals\n if (typeof g.requestAnimationFrame !== 'function' || typeof g.cancelAnimationFrame !== 'function') {\n throw new Error(\n 'PlaybackClock requires requestAnimationFrame/cancelAnimationFrame — playback runs only in a browser (or a test that stubs both globals)',\n )\n }\n return { request: g.requestAnimationFrame.bind(globalThis), cancel: g.cancelAnimationFrame.bind(globalThis) }\n}\n\nfunction now(): number {\n const perf = (globalThis as RafGlobals).performance\n if (!perf || typeof perf.now !== 'function') {\n throw new Error('PlaybackClock requires performance.now() — playback runs only in a browser (or a test that stubs it)')\n }\n return perf.now()\n}\n\nexport function createPlaybackClock(config: PlaybackClockConfig): PlaybackClock {\n if (!Number.isInteger(config.fps) || config.fps <= 0) {\n throw new Error(`fps must be a positive integer, got ${config.fps}`)\n }\n if (!Number.isInteger(config.durationFrames) || config.durationFrames < 1) {\n throw new Error(`durationFrames must be a positive integer, got ${config.durationFrames}`)\n }\n const lastFrame = config.durationFrames - 1\n\n let frame = 0\n let playing = false\n let disposed = false\n let rafId: number | null = null\n let cancelRaf: ((id: number) => void) | null = null\n let anchorTime = 0\n let anchorFrame = 0\n const listeners = new Set<(frame: number) => void>()\n\n const notify = (): void => {\n for (const listener of [...listeners]) listener(frame)\n }\n\n const stopLoop = (): void => {\n if (rafId !== null && cancelRaf) cancelRaf(rafId)\n rafId = null\n }\n\n /** One callback per animation frame while playing; pauses on reaching the\n * final frame. */\n const tick = (): void => {\n rafId = null\n if (!playing) return\n const elapsedMs = now() - anchorTime\n const advanced = anchorFrame + Math.floor((elapsedMs / 1000) * config.fps)\n if (advanced >= lastFrame) {\n frame = lastFrame\n playing = false\n notify()\n return\n }\n frame = advanced\n notify()\n rafId = resolveRaf().request(tick)\n }\n\n return {\n /** Idempotent while playing. Playing from the final frame restarts at 0 —\n * a play button at the end means \"watch again\", not a dead control. */\n play(): void {\n if (disposed) throw new Error('PlaybackClock is disposed')\n if (playing) return\n const raf = resolveRaf()\n cancelRaf = raf.cancel\n if (frame >= lastFrame) frame = 0\n anchorTime = now()\n anchorFrame = frame\n playing = true\n rafId = raf.request(tick)\n },\n\n pause(): void {\n if (!playing) return\n playing = false\n stopLoop()\n },\n\n /** Clamps into [0, durationFrames - 1]; fractional input rounds to the\n * nearest frame. Re-anchors mid-play so playback continues from the\n * seek target, and notifies so scrubbing drives the playhead. */\n seek(target: number): void {\n if (disposed) throw new Error('PlaybackClock is disposed')\n if (!Number.isFinite(target)) throw new Error(`seek target must be a finite number, got ${target}`)\n frame = Math.max(0, Math.min(lastFrame, Math.round(target)))\n if (playing) {\n anchorTime = now()\n anchorFrame = frame\n }\n notify()\n },\n\n isPlaying(): boolean {\n return playing\n },\n\n getFrame(): number {\n return frame\n },\n\n subscribe(listener: (frame: number) => void): () => void {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n },\n\n dispose(): void {\n playing = false\n stopLoop()\n listeners.clear()\n disposed = true\n },\n }\n}\n","/**\n * Drag snapping. Snap points come from the timeline's structure (clip edges,\n * playhead, sequence end); the snap THRESHOLD is measured in screen pixels at\n * the current zoom — what feels \"close\" is a screen distance, not a frame\n * count — and converts to frames as thresholdPx / zoom.\n */\n\nimport type { SequenceTimeline } from '../../sequences/model'\nimport type { SnapPoint, SnapResult } from '../contracts'\n\n/** Snap point with its owning clip when it came from one, so a drag can\n * exclude the dragged clip's own edges. Structurally a `SnapPoint`. */\nexport interface TimelineSnapPoint extends SnapPoint {\n clipId?: string\n}\n\n/** Disabled clips still occupy timeline space visually, so their edges remain\n * snap targets. */\nexport function collectSnapPoints(timeline: SequenceTimeline, playheadFrame: number): TimelineSnapPoint[] {\n if (!Number.isInteger(playheadFrame) || playheadFrame < 0) {\n throw new Error(`playheadFrame must be a non-negative integer, got ${playheadFrame}`)\n }\n const points: TimelineSnapPoint[] = []\n for (const clip of timeline.clips) {\n points.push({ frame: clip.startFrame, kind: 'clip-start', clipId: clip.id })\n points.push({ frame: clip.startFrame + clip.durationFrames, kind: 'clip-end', clipId: clip.id })\n }\n points.push({ frame: playheadFrame, kind: 'playhead' })\n points.push({ frame: timeline.sequence.durationFrames, kind: 'sequence-end' })\n return points.sort((a, b) => a.frame - b.frame || a.kind.localeCompare(b.kind))\n}\n\nexport interface ApplySnapOptions {\n /** Pixels per frame — converts the pixel threshold into frames. */\n zoom: number\n /** Screen-distance threshold; 10px matches the editor's hit-slop. */\n thresholdPx?: number\n /** Return true to remove a point from consideration (e.g. the dragged\n * clip's own edges via `TimelineSnapPoint.clipId`). */\n exclude?: (point: SnapPoint) => boolean\n}\n\n/** Nearest candidate wins; ties keep the first candidate in `points` order\n * (sorted by frame from `collectSnapPoints`, so the lower frame). */\nexport function applySnap(frame: number, points: SnapPoint[], opts: ApplySnapOptions): SnapResult {\n if (!Number.isFinite(frame)) throw new Error(`frame must be a finite number, got ${frame}`)\n if (!Number.isFinite(opts.zoom) || opts.zoom <= 0) {\n throw new Error(`zoom must be a positive finite number (pixels per frame), got ${opts.zoom}`)\n }\n const thresholdPx = opts.thresholdPx ?? 10\n if (!Number.isFinite(thresholdPx) || thresholdPx < 0) {\n throw new Error(`thresholdPx must be a non-negative finite number, got ${thresholdPx}`)\n }\n const thresholdFrames = thresholdPx / opts.zoom\n\n let best: SnapPoint | null = null\n let bestDistance = Infinity\n for (const point of points) {\n if (opts.exclude && opts.exclude(point)) continue\n const distance = Math.abs(point.frame - frame)\n if (distance < bestDistance) {\n best = point\n bestDistance = distance\n }\n }\n\n if (best !== null && bestDistance <= thresholdFrames) {\n return { frame: best.frame, snapped: true, point: best }\n }\n return { frame, snapped: false, point: null }\n}\n","/**\n * Zoom + viewport coordinate math. Zoom is PIXELS PER FRAME; the slider is a\n * normalized [0, 1] control mapped exponentially (zoom = min·(max/min)^slider)\n * so each slider step multiplies the scale by a constant factor — linear\n * slider feel across a 10x+ range.\n */\n\nimport type { ZoomMath } from '../contracts'\n\nexport interface ZoomMathConfig {\n minZoom: number\n maxZoom: number\n}\n\nexport function createZoomMath(config: ZoomMathConfig): ZoomMath {\n const { minZoom, maxZoom } = config\n if (!Number.isFinite(minZoom) || minZoom <= 0) {\n throw new Error(`minZoom must be a positive finite number, got ${minZoom}`)\n }\n if (!Number.isFinite(maxZoom) || maxZoom <= minZoom) {\n throw new Error(`maxZoom must be finite and greater than minZoom ${minZoom}, got ${maxZoom}`)\n }\n const ratio = maxZoom / minZoom\n\n return {\n minZoom,\n maxZoom,\n /** Slider clamps into [0, 1]: range inputs can overshoot during fast\n * drags and the boundary value is always the right answer. */\n sliderToZoom(slider: number): number {\n if (!Number.isFinite(slider)) throw new Error(`slider must be a finite number, got ${slider}`)\n const t = Math.min(1, Math.max(0, slider))\n return minZoom * Math.pow(ratio, t)\n },\n zoomToSlider(zoom: number): number {\n if (!Number.isFinite(zoom) || zoom <= 0) throw new Error(`zoom must be a positive finite number, got ${zoom}`)\n const clamped = Math.min(maxZoom, Math.max(minZoom, zoom))\n return Math.log(clamped / minZoom) / Math.log(ratio)\n },\n }\n}\n\n/** Horizontal viewport: zoom in pixels per frame, scrollLeft in pixels. */\nexport interface ViewportTransform {\n zoom: number\n scrollLeft: number\n}\n\nfunction assertViewport(view: ViewportTransform): void {\n if (!Number.isFinite(view.zoom) || view.zoom <= 0) {\n throw new Error(`viewport zoom must be a positive finite number, got ${view.zoom}`)\n }\n if (!Number.isFinite(view.scrollLeft)) {\n throw new Error(`viewport scrollLeft must be a finite number, got ${view.scrollLeft}`)\n }\n}\n\n/** Frame → viewport-relative pixel x. Output is fractional; round through\n * `snapPixel` before drawing. */\nexport function frameToPixel(frame: number, view: ViewportTransform): number {\n assertViewport(view)\n if (!Number.isFinite(frame)) throw new Error(`frame must be a finite number, got ${frame}`)\n return frame * view.zoom - view.scrollLeft\n}\n\n/** Viewport-relative pixel x → integer frame. Frames are integer positions,\n * so the result rounds to the nearest frame and floors at 0 (a pointer left\n * of frame 0 resolves to 0). */\nexport function pixelToFrame(pixel: number, view: ViewportTransform): number {\n assertViewport(view)\n if (!Number.isFinite(pixel)) throw new Error(`pixel must be a finite number, got ${pixel}`)\n return Math.max(0, Math.round((pixel + view.scrollLeft) / view.zoom))\n}\n\n/** Snap a CSS-pixel value to the device pixel grid so 1px timeline rules\n * render crisp on fractional-DPR displays. */\nexport function snapPixel(value: number, devicePixelRatio: number): number {\n if (!Number.isFinite(value)) throw new Error(`value must be a finite number, got ${value}`)\n if (!Number.isFinite(devicePixelRatio) || devicePixelRatio <= 0) {\n throw new Error(`devicePixelRatio must be a positive finite number, got ${devicePixelRatio}`)\n }\n return Math.round(value * devicePixelRatio) / devicePixelRatio\n}\n","/**\n * Baseline frame pipeline behind the `VideoFrameProvider` seam: an off-DOM\n * HTMLVideoElement pool for video URLs and an HTMLImageElement pool for\n * stills, merged by a per-URL kind dispatcher so the editor canvas never\n * cares which kind a clip's media is. A WebCodecs implementation can replace\n * this wholesale behind the same seam.\n *\n * Server-safe at import time, browser-only at call time: nothing touches\n * `document` or `fetch` until a provider method runs.\n */\n\nimport type { VideoFrameProvider } from '../contracts'\n\nexport const DEFAULT_MAX_MEDIA_ELEMENTS = 4\n\n/** Half a frame at 30fps. Seeks closer than this repaint the decoder's\n * current frame instead of forcing a redundant seek. */\nexport const SEEK_TOLERANCE_SECONDS = 1 / 60\n\n/** A seek that hasn't fired `seeked` after this long is a decode failure —\n * the draw REJECTS rather than painting whatever frame happens to be up. */\nexport const SEEK_TIMEOUT_MS = 5_000\n\nexport interface FrameRect {\n x: number\n y: number\n width: number\n height: number\n}\n\n/** Object-fit 'contain' placement: preserve aspect ratio, fit entirely inside\n * `dest`, center the residual space. Callers own clearing the letterbox\n * margins — this paints only the fitted region. */\nexport function containFitRect(\n source: { width: number; height: number },\n dest: FrameRect,\n): FrameRect {\n if (!(source.width > 0) || !(source.height > 0)) {\n throw new Error(`containFitRect requires positive source dimensions, got ${source.width}x${source.height}`)\n }\n if (!(dest.width > 0) || !(dest.height > 0)) {\n throw new Error(`containFitRect requires positive destination dimensions, got ${dest.width}x${dest.height}`)\n }\n const scale = Math.min(dest.width / source.width, dest.height / source.height)\n const width = source.width * scale\n const height = source.height * scale\n return {\n x: dest.x + (dest.width - width) / 2,\n y: dest.y + (dest.height - height) / 2,\n width,\n height,\n }\n}\n\nexport function needsSeek(currentTimeSeconds: number, targetSeconds: number): boolean {\n return Math.abs(currentTimeSeconds - targetSeconds) >= SEEK_TOLERANCE_SECONDS\n}\n\n// ---------------------------------------------------------------------------\n// LRU element pool\n// ---------------------------------------------------------------------------\n\nexport interface PooledElementLease<T> {\n element: T\n /** Unpins the element; idle elements become LRU-evictable. Idempotent. */\n release(): void\n}\n\nexport interface MediaElementPool<T> {\n acquire(url: string): PooledElementLease<T>\n has(url: string): boolean\n size(): number\n dispose(): void\n}\n\n/** LRU pool of media elements keyed by URL. Entries pinned by an outstanding\n * lease are never evicted — a draw in flight must keep its element — so the\n * pool can temporarily exceed `maxElements` under concurrent draws and\n * shrinks back as leases release. */\nexport function createMediaElementPool<T>(opts: {\n maxElements: number\n create(url: string): T\n destroy(element: T, url: string): void\n}): MediaElementPool<T> {\n if (!Number.isInteger(opts.maxElements) || opts.maxElements < 1) {\n throw new Error(`maxElements must be a positive integer, got ${opts.maxElements}`)\n }\n // Map insertion order doubles as recency: acquire re-inserts at the back,\n // so eviction scans from the front (least recently used).\n const entries = new Map<string, { element: T; pinned: number }>()\n let disposed = false\n\n const evictOverBudget = (): void => {\n if (entries.size <= opts.maxElements) return\n for (const [url, entry] of entries) {\n if (entries.size <= opts.maxElements) return\n if (entry.pinned > 0) continue\n entries.delete(url)\n opts.destroy(entry.element, url)\n }\n }\n\n return {\n acquire(url) {\n if (disposed) throw new Error(`media element pool is disposed — cannot acquire ${url}`)\n let entry = entries.get(url)\n if (entry) {\n entries.delete(url)\n } else {\n entry = { element: opts.create(url), pinned: 0 }\n }\n entries.set(url, entry)\n entry.pinned += 1\n evictOverBudget()\n let released = false\n return {\n element: entry.element,\n release: () => {\n if (released) return\n released = true\n entry.pinned -= 1\n evictOverBudget()\n },\n }\n },\n has: (url) => entries.has(url),\n size: () => entries.size,\n dispose() {\n disposed = true\n for (const [url, entry] of entries) opts.destroy(entry.element, url)\n entries.clear()\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// Media kind dispatch\n// ---------------------------------------------------------------------------\n\nconst IMAGE_EXTENSIONS = new Set(['apng', 'avif', 'bmp', 'gif', 'jpeg', 'jpg', 'png', 'svg', 'webp'])\nconst VIDEO_EXTENSIONS = new Set(['m4v', 'mkv', 'mov', 'mp4', 'mpeg', 'mpg', 'ogv', 'webm'])\n\n/** Extension-based kind classification; 'unknown' defers to a HEAD\n * content-type probe at draw time. */\nexport function classifyMediaUrl(url: string): 'video' | 'image' | 'unknown' {\n const match = /\\.([a-z0-9]+)(?:[?#].*)?$/i.exec(url)\n const extension = match?.[1]?.toLowerCase()\n if (extension === undefined) return 'unknown'\n if (VIDEO_EXTENSIONS.has(extension)) return 'video'\n if (IMAGE_EXTENSIONS.has(extension)) return 'image'\n return 'unknown'\n}\n\nasync function probeMediaKind(url: string): Promise<'video' | 'image'> {\n const known = classifyMediaUrl(url)\n if (known !== 'unknown') return known\n let contentType: string | null = null\n try {\n const response = await fetch(url, { method: 'HEAD' })\n contentType = response.headers.get('content-type')\n } catch {\n // Servers that reject HEAD usually still serve the bytes; HTMLImageElement\n // is the cheapest probe-by-decoding, so unknowns route to the image path\n // and a genuine failure surfaces from image decode with the URL attached.\n return 'image'\n }\n if (contentType !== null) {\n if (contentType.startsWith('video/')) return 'video'\n if (contentType.startsWith('audio/')) {\n throw new Error(`cannot draw frames from audio media ${url} (content-type ${contentType})`)\n }\n }\n return 'image'\n}\n\n// ---------------------------------------------------------------------------\n// Shared guards\n// ---------------------------------------------------------------------------\n\nfunction requireDocument(caller: string): Document {\n if (typeof document === 'undefined') {\n throw new Error(`${caller} requires a browser document — frame providers are client-side only`)\n }\n return document\n}\n\nfunction assertSourceSeconds(sourceSeconds: number): void {\n if (!Number.isFinite(sourceSeconds) || sourceSeconds < 0) {\n throw new Error(`sourceSeconds must be a non-negative finite number, got ${sourceSeconds}`)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Video path\n// ---------------------------------------------------------------------------\n\nfunction createPooledVideo(url: string): HTMLVideoElement {\n const video = requireDocument('createVideoElementFrameProvider').createElement('video')\n video.crossOrigin = 'anonymous'\n video.muted = true\n video.preload = 'auto'\n video.playsInline = true\n video.src = url\n return video\n}\n\nfunction destroyPooledVideo(video: HTMLVideoElement): void {\n video.pause()\n // Clearing src alone leaves the media resource attached; the empty load()\n // is what actually releases the decoder.\n video.removeAttribute('src')\n video.load()\n}\n\nfunction awaitMediaEvent(video: HTMLVideoElement, eventName: string, url: string): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const cleanup = (): void => {\n clearTimeout(timer)\n video.removeEventListener(eventName, onSuccess)\n video.removeEventListener('error', onError)\n }\n const onSuccess = (): void => {\n cleanup()\n resolve()\n }\n const onError = (): void => {\n cleanup()\n const detail = video.error ? `code ${video.error.code}: ${video.error.message}` : 'no MediaError attached'\n reject(new Error(`media error while waiting for '${eventName}' on ${url} (${detail})`))\n }\n const timer = setTimeout(() => {\n cleanup()\n reject(new Error(`timed out after ${SEEK_TIMEOUT_MS}ms waiting for '${eventName}' on ${url}`))\n }, SEEK_TIMEOUT_MS)\n video.addEventListener(eventName, onSuccess)\n video.addEventListener('error', onError)\n })\n}\n\nasync function seekVideo(video: HTMLVideoElement, targetSeconds: number, url: string): Promise<void> {\n const seeked = awaitMediaEvent(video, 'seeked', url)\n // Out-of-range targets clamp to the media's end so timeline frames past the\n // source hold the last frame (standard NLE behavior) instead of erroring.\n video.currentTime = Number.isFinite(video.duration) && video.duration > 0\n ? Math.min(targetSeconds, video.duration)\n : targetSeconds\n await seeked\n}\n\nasync function drawVideoFrame(\n video: HTMLVideoElement,\n url: string,\n sourceSeconds: number,\n ctx: CanvasRenderingContext2D,\n rect: FrameRect,\n): Promise<void> {\n // HAVE_METADATA (readyState 1): dimensions + duration known, safe to seek.\n if (video.readyState < 1) await awaitMediaEvent(video, 'loadedmetadata', url)\n if (needsSeek(video.currentTime, sourceSeconds)) await seekVideo(video, sourceSeconds, url)\n if (video.videoWidth === 0 || video.videoHeight === 0) {\n throw new Error(`media at ${url} decoded with no video frames (audio-only or corrupt) — cannot draw`)\n }\n const fit = containFitRect({ width: video.videoWidth, height: video.videoHeight }, rect)\n ctx.drawImage(video, fit.x, fit.y, fit.width, fit.height)\n}\n\n// ---------------------------------------------------------------------------\n// Image path\n// ---------------------------------------------------------------------------\n\ninterface PooledImage {\n element: HTMLImageElement\n ready: Promise<void>\n}\n\nfunction createPooledImage(url: string): PooledImage {\n const element = requireDocument('createImageFrameProvider').createElement('img')\n element.crossOrigin = 'anonymous'\n element.src = url\n const ready = element.decode().then(\n () => undefined,\n (error: unknown) => {\n throw new Error(`failed to decode image ${url}`, { cause: error })\n },\n )\n // prefetch never awaits `ready`; pre-attach a handler so a broken image\n // can't fire unhandledrejection. drawFrame's await still observes the error.\n void ready.catch(() => undefined)\n return { element, ready }\n}\n\n/** Stills behind the same `VideoFrameProvider` seam — `sourceSeconds` is\n * validated for contract parity but does not affect the painted pixels. */\nexport function createImageFrameProvider(opts?: { maxElements?: number }): VideoFrameProvider {\n const pool = createMediaElementPool<PooledImage>({\n maxElements: opts?.maxElements ?? DEFAULT_MAX_MEDIA_ELEMENTS,\n create: createPooledImage,\n destroy: (pooled) => {\n pooled.element.src = ''\n },\n })\n return {\n async drawFrame(mediaUrl, sourceSeconds, ctx, rect) {\n assertSourceSeconds(sourceSeconds)\n const lease = pool.acquire(mediaUrl)\n try {\n await lease.element.ready\n const image = lease.element.element\n const fit = containFitRect({ width: image.naturalWidth, height: image.naturalHeight }, rect)\n ctx.drawImage(image, fit.x, fit.y, fit.width, fit.height)\n } finally {\n lease.release()\n }\n },\n prefetch(mediaUrl) {\n pool.acquire(mediaUrl).release()\n },\n dispose() {\n pool.dispose()\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// Merged provider\n// ---------------------------------------------------------------------------\n\n/** The baseline provider `TimelineEditorProps.frameProvider` defaults to.\n * Video and image pools each hold up to `maxElements` entries. */\nexport function createVideoElementFrameProvider(opts?: { maxElements?: number }): VideoFrameProvider {\n const maxElements = opts?.maxElements ?? DEFAULT_MAX_MEDIA_ELEMENTS\n const videoPool = createMediaElementPool<HTMLVideoElement>({\n maxElements,\n create: createPooledVideo,\n destroy: destroyPooledVideo,\n })\n const imageProvider = createImageFrameProvider({ maxElements })\n const kindByUrl = new Map<string, Promise<'video' | 'image'>>()\n // One draw at a time per URL: interleaved currentTime writes on a shared\n // element make the browser coalesce seeks, leaving earlier waiters watching\n // for a `seeked` event that never fires.\n const drawQueue = new Map<string, Promise<void>>()\n\n const resolveKind = (url: string): Promise<'video' | 'image'> => {\n let pending = kindByUrl.get(url)\n if (pending === undefined) {\n pending = probeMediaKind(url)\n // A rejected probe must not poison the cache — drop it so the next\n // draw retries instead of failing forever on a transient.\n pending.catch(() => kindByUrl.delete(url))\n kindByUrl.set(url, pending)\n }\n return pending\n }\n\n const enqueueVideoDraw = (url: string, work: () => Promise<void>): Promise<void> => {\n const previous = drawQueue.get(url) ?? Promise.resolve()\n const run = previous.then(work, work)\n const tail = run.then(\n () => undefined,\n () => undefined,\n ).then(() => {\n if (drawQueue.get(url) === tail) drawQueue.delete(url)\n })\n drawQueue.set(url, tail)\n return run\n }\n\n return {\n async drawFrame(mediaUrl, sourceSeconds, ctx, rect) {\n assertSourceSeconds(sourceSeconds)\n const kind = await resolveKind(mediaUrl)\n if (kind === 'image') {\n await imageProvider.drawFrame(mediaUrl, sourceSeconds, ctx, rect)\n return\n }\n await enqueueVideoDraw(mediaUrl, async () => {\n const lease = videoPool.acquire(mediaUrl)\n try {\n await drawVideoFrame(lease.element, mediaUrl, sourceSeconds, ctx, rect)\n } finally {\n lease.release()\n }\n })\n },\n prefetch(mediaUrl) {\n // Best-effort warm: failures vanish here by design because the same\n // failure resurfaces with full detail on the next drawFrame.\n void resolveKind(mediaUrl)\n .then((kind) => {\n if (kind === 'image') {\n imageProvider.prefetch(mediaUrl)\n return\n }\n videoPool.acquire(mediaUrl).release()\n })\n .catch(() => undefined)\n },\n dispose() {\n videoPool.dispose()\n imageProvider.dispose()\n kindByUrl.clear()\n drawQueue.clear()\n },\n }\n}\n","/**\n * Fuses several `TimelineCommand`s into one undo step so a single gesture\n * (multi-select delete, drop that creates a track and places a clip) never\n * fragments the history. Execute runs in order; undo runs in strict reverse so\n * intermediate states reconstruct exactly.\n */\n\nimport type { TimelineCommand } from '../contracts'\n\nexport function compositeCommand(label: string, commands: TimelineCommand[]): TimelineCommand {\n if (commands.length === 0) throw new Error('compositeCommand requires at least one command')\n return {\n label,\n execute: (state) => commands.reduce((acc, command) => command.execute(acc), state),\n undo: (state) => [...commands].reverse().reduce((acc, command) => command.undo(acc), state),\n operations: () => commands.flatMap((command) => command.operations()),\n inverseOperations: () => [...commands].reverse().flatMap((command) => command.inverseOperations()),\n }\n}\n","/**\n * Pure pointer-gesture math for the timeline editor. Every drag/trim/scrub\n * gesture quantizes through these functions so the interactive behavior is\n * unit-testable without a DOM. All inputs/outputs are integer frames except\n * pixel deltas and zoom (px per frame), which are the only float-valued edge.\n */\n\nimport { MIN_SEQUENCE_CLIP_FRAMES, clampClipStart } from '../../sequences/model'\nimport type { SnapPoint, SnapResult } from '../contracts'\n\n/** Quantize a horizontal pointer delta to whole frames at the current zoom. */\nexport function framesFromPixelDelta(deltaX: number, zoom: number): number {\n if (!Number.isFinite(deltaX)) throw new Error('deltaX must be a finite pixel delta')\n if (!Number.isFinite(zoom) || zoom <= 0) throw new Error('zoom (pixels per frame) must be a positive finite number')\n return Math.round(deltaX / zoom)\n}\n\nexport interface MoveDragInput {\n originStartFrame: number\n durationFrames: number\n deltaFrames: number\n sequenceDurationFrames: number\n}\n\n/** New start frame for a move drag, clamped so the clip stays fully inside\n * the sequence. */\nexport function moveDragStartFrame(input: MoveDragInput): number {\n return clampClipStart({\n startFrame: input.originStartFrame + input.deltaFrames,\n durationFrames: input.durationFrames,\n sequenceDurationFrames: input.sequenceDurationFrames,\n })\n}\n\nexport interface TrimStartDragInput {\n originStartFrame: number\n originDurationFrames: number\n originSourceInFrame: number\n deltaFrames: number\n}\n\nexport interface TrimStartDragResult {\n startFrame: number\n durationFrames: number\n sourceInFrame: number\n}\n\n/**\n * Head trim: the clip END is invariant; start slides between two hard walls —\n * it cannot reveal media before source frame 0 (sourceInFrame >= 0) and cannot\n * pass within MIN_SEQUENCE_CLIP_FRAMES of the end. sourceInFrame shifts by\n * exactly the start delta so the visible content stays anchored.\n */\nexport function trimStartDrag(input: TrimStartDragInput): TrimStartDragResult {\n const endFrame = input.originStartFrame + input.originDurationFrames\n const minStart = Math.max(0, input.originStartFrame - input.originSourceInFrame)\n const maxStart = endFrame - MIN_SEQUENCE_CLIP_FRAMES\n const startFrame = Math.max(minStart, Math.min(maxStart, input.originStartFrame + input.deltaFrames))\n return {\n startFrame,\n durationFrames: endFrame - startFrame,\n sourceInFrame: input.originSourceInFrame + (startFrame - input.originStartFrame),\n }\n}\n\nexport interface TrimEndDragInput {\n originStartFrame: number\n originDurationFrames: number\n sourceInFrame: number\n deltaFrames: number\n sequenceDurationFrames: number\n /** Natural source length in frames when known; bounds how far the tail can\n * extend. Omit for stills and media of unknown length. */\n sourceDurationFrames?: number\n}\n\n/** Tail trim: start is invariant; duration is bounded below by the minimum\n * clip length and above by both the sequence end and the remaining source\n * material past the in-point. */\nexport function trimEndDrag(input: TrimEndDragInput): { durationFrames: number } {\n const bySequence = input.sequenceDurationFrames - input.originStartFrame\n const bySource = input.sourceDurationFrames === undefined\n ? Number.POSITIVE_INFINITY\n : input.sourceDurationFrames - input.sourceInFrame\n const maxDuration = Math.min(bySequence, bySource)\n const durationFrames = Math.max(\n MIN_SEQUENCE_CLIP_FRAMES,\n Math.min(maxDuration, input.originDurationFrames + input.deltaFrames),\n )\n return { durationFrames }\n}\n\nconst TICK_STEPS_SECONDS = [1, 5, 10, 30, 60, 300] as const\n\n/**\n * Smallest ruler step whose major ticks sit at least `minSpacingPx` apart at\n * the current zoom; past the table it grows in whole minutes so labels never\n * collide at extreme zoom-out.\n */\nexport function selectTickStepSeconds(input: { zoom: number; fps: number; minSpacingPx?: number }): number {\n if (!Number.isFinite(input.zoom) || input.zoom <= 0) throw new Error('zoom must be a positive finite number')\n if (!Number.isInteger(input.fps) || input.fps <= 0) throw new Error('fps must be a positive integer')\n const minSpacing = input.minSpacingPx ?? 80\n for (const step of TICK_STEPS_SECONDS) {\n if (step * input.fps * input.zoom >= minSpacing) return step\n }\n const pxPerMinute = 60 * input.fps * input.zoom\n return Math.ceil(minSpacing / pxPerMinute) * 60\n}\n\nexport interface LetterboxRect {\n x: number\n y: number\n width: number\n height: number\n}\n\n/** Contain-fit a media aspect inside a container, centered with letterbox or\n * pillarbox bars. */\nexport function letterboxRect(input: {\n containerWidth: number\n containerHeight: number\n mediaWidth: number\n mediaHeight: number\n}): LetterboxRect {\n const { containerWidth, containerHeight, mediaWidth, mediaHeight } = input\n if (containerWidth <= 0 || containerHeight <= 0) throw new Error('container dimensions must be positive')\n if (mediaWidth <= 0 || mediaHeight <= 0) throw new Error('media dimensions must be positive')\n const scale = Math.min(containerWidth / mediaWidth, containerHeight / mediaHeight)\n const width = mediaWidth * scale\n const height = mediaHeight * scale\n return {\n x: (containerWidth - width) / 2,\n y: (containerHeight - height) / 2,\n width,\n height,\n }\n}\n\n/** Caption type scales with the rendered frame, floored so captions stay\n * legible on small previews. */\nexport function captionFontPx(canvasCssHeight: number): number {\n if (!Number.isFinite(canvasCssHeight) || canvasCssHeight <= 0) throw new Error('canvas height must be positive')\n return Math.max(12, Math.round(canvasCssHeight / 18))\n}\n\n/** Pixel geometry for a clip chip; width floors at 2px so 1-frame clips stay\n * grabbable. */\nexport function clipChipGeometry(input: { startFrame: number; durationFrames: number; zoom: number }): { left: number; width: number } {\n return {\n left: input.startFrame * input.zoom,\n width: Math.max(2, input.durationFrames * input.zoom),\n }\n}\n\n/**\n * A move drag snaps whichever clip edge lands closest to a snap point: the\n * start edge directly, or the end edge re-expressed as a start. An unsnapped\n * candidate passes through unchanged.\n */\nexport function chooseMoveSnap(input: {\n candidateStartFrame: number\n durationFrames: number\n startSnap: SnapResult\n endSnap: SnapResult\n}): { startFrame: number; point: SnapPoint | null } {\n const startDelta = input.startSnap.snapped\n ? Math.abs(input.startSnap.frame - input.candidateStartFrame)\n : Number.POSITIVE_INFINITY\n const endStartFrame = input.endSnap.frame - input.durationFrames\n const endDelta = input.endSnap.snapped\n ? Math.abs(endStartFrame - input.candidateStartFrame)\n : Number.POSITIVE_INFINITY\n if (startDelta === Number.POSITIVE_INFINITY && endDelta === Number.POSITIVE_INFINITY) {\n return { startFrame: input.candidateStartFrame, point: null }\n }\n if (startDelta <= endDelta) return { startFrame: input.startSnap.frame, point: input.startSnap.point }\n return { startFrame: endStartFrame, point: input.endSnap.point }\n}\n","/**\n * Program monitor: a canvas letterboxed to the sequence aspect that paints\n * the playhead frame — black base, the topmost enabled video-track clip via\n * `frameProvider.drawFrame`, then caption text bottom-centered on an 80%\n * black backing bar with type scaled to canvas height / 18.\n *\n * Track stacking: tracks composite bottom-up, so among clips active at the\n * frame the one on the HIGHEST sortOrder track covers the rest; muted tracks\n * do not render. Only `video` tracks paint — `reference` tracks are\n * non-rendered guide media (model contract) and are excluded from mp4/EDL/\n * contact-sheet export, so painting them would preview content the program\n * output does not contain. Paints serialize through a latest-wins queue —\n * decode is async, so a slow seek never paints over a newer frame.\n *\n * `sourceSeconds` = (sourceInFrame + playhead offset into the clip) / fps:\n * the model maps source frames 1:1 at sequence fps.\n */\n\nimport { useEffect, useMemo, useRef, useState } from 'react'\nimport { framesToSeconds, snapshotFrame } from '../../sequences/model'\nimport type { SequenceTimeline } from '../../sequences/model'\nimport type { PlaybackClock, VideoFrameProvider } from '../contracts'\nimport { captionFontPx, letterboxRect } from './interaction-math'\n\nexport interface PreviewCanvasProps {\n timeline: SequenceTimeline\n clock: PlaybackClock\n frameProvider: VideoFrameProvider\n className?: string\n}\n\ninterface CanvasSize {\n width: number\n height: number\n}\n\nexport function PreviewCanvas({ timeline, clock, frameProvider, className }: PreviewCanvasProps) {\n const containerRef = useRef<HTMLDivElement | null>(null)\n const canvasRef = useRef<HTMLCanvasElement | null>(null)\n const [size, setSize] = useState<CanvasSize | null>(null)\n const [drawError, setDrawError] = useState<string | null>(null)\n const paintQueueRef = useRef<{ running: boolean; queuedFrame: number | null }>({ running: false, queuedFrame: null })\n /** Latest paint inputs, readable from the async queue without re-binding it. */\n const paintInputsRef = useRef({ timeline, frameProvider, size })\n paintInputsRef.current = { timeline, frameProvider, size }\n\n useEffect(() => {\n const container = containerRef.current\n if (!container) return\n function measure() {\n const node = containerRef.current\n if (!node) return\n const rect = node.getBoundingClientRect()\n // Zero-size during layout/hidden tabs is a transient, not an error.\n if (rect.width <= 0 || rect.height <= 0) return\n const fit = letterboxRect({\n containerWidth: rect.width,\n containerHeight: rect.height,\n mediaWidth: timeline.sequence.width,\n mediaHeight: timeline.sequence.height,\n })\n setSize((current) => {\n const next = { width: Math.round(fit.width), height: Math.round(fit.height) }\n return current && current.width === next.width && current.height === next.height ? current : next\n })\n }\n measure()\n // ResizeObserver is absent in non-browser test environments; the single\n // mount measure above still sizes the canvas there.\n if (typeof ResizeObserver === 'undefined') return\n const observer = new ResizeObserver(measure)\n observer.observe(container)\n return () => observer.disconnect()\n }, [timeline.sequence.width, timeline.sequence.height])\n\n const requestPaint = useMemo(() => {\n async function paint(frame: number) {\n const { timeline: current, frameProvider: provider, size: cssSize } = paintInputsRef.current\n const canvas = canvasRef.current\n if (!canvas || !cssSize) return\n const ctx = canvas.getContext('2d')\n // Canvas 2D is unavailable in non-browser test environments; layout\n // still renders, only pixels are skipped.\n if (!ctx) return\n const dpr = (typeof window !== 'undefined' && window.devicePixelRatio) || 1\n const backingWidth = Math.round(cssSize.width * dpr)\n const backingHeight = Math.round(cssSize.height * dpr)\n if (canvas.width !== backingWidth) canvas.width = backingWidth\n if (canvas.height !== backingHeight) canvas.height = backingHeight\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0)\n ctx.fillStyle = '#000'\n ctx.fillRect(0, 0, cssSize.width, cssSize.height)\n\n // The playhead may rest at durationFrames (sequence end); paint the\n // final addressable frame there.\n const paintFrame = Math.max(0, Math.min(frame, current.sequence.durationFrames - 1))\n const snapshot = snapshotFrame(current, paintFrame)\n\n const mediaEntries = snapshot.active.filter(({ track, clip }) => (\n track.kind === 'video' && !track.muted && clip.media !== undefined && (clip.media.kind === 'video' || clip.media.kind === 'image')\n ))\n const top = mediaEntries[mediaEntries.length - 1]\n if (top && top.clip.media) {\n const sourceSeconds = framesToSeconds(top.clip.sourceInFrame + (paintFrame - top.clip.startFrame), current.sequence.fps)\n await provider.drawFrame(top.clip.media.url, sourceSeconds, ctx, {\n x: 0,\n y: 0,\n width: cssSize.width,\n height: cssSize.height,\n })\n }\n\n if (snapshot.captions.length > 0) {\n const fontPx = captionFontPx(cssSize.height)\n ctx.font = `600 ${fontPx}px system-ui, sans-serif`\n ctx.textAlign = 'center'\n ctx.textBaseline = 'middle'\n const barHeight = fontPx * 1.6\n let centerY = cssSize.height - barHeight\n for (const caption of [...snapshot.captions].reverse()) {\n const textWidth = Math.min(ctx.measureText(caption.text).width, cssSize.width * 0.86)\n const barWidth = textWidth + fontPx * 1.2\n ctx.fillStyle = 'rgba(0, 0, 0, 0.8)'\n ctx.fillRect((cssSize.width - barWidth) / 2, centerY - barHeight / 2, barWidth, barHeight)\n ctx.fillStyle = '#fff'\n ctx.fillText(caption.text, cssSize.width / 2, centerY, cssSize.width * 0.86)\n centerY -= barHeight + fontPx * 0.25\n }\n }\n }\n\n return function requestPaint(frame: number) {\n const queue = paintQueueRef.current\n if (queue.running) {\n queue.queuedFrame = frame\n return\n }\n queue.running = true\n void (async () => {\n let next: number | null = frame\n while (next !== null) {\n const target = next\n queue.queuedFrame = null\n try {\n await paint(target)\n setDrawError(null)\n } catch (error) {\n setDrawError(error instanceof Error ? error.message : String(error))\n }\n next = queue.queuedFrame\n }\n queue.running = false\n })()\n }\n }, [])\n\n useEffect(() => {\n requestPaint(clock.getFrame())\n return clock.subscribe(requestPaint)\n }, [clock, requestPaint])\n\n // Timeline edits and canvas resizes repaint the held frame.\n useEffect(() => {\n requestPaint(clock.getFrame())\n }, [timeline, size, clock, requestPaint])\n\n return (\n <div ref={containerRef} className={`relative flex min-h-0 flex-1 items-center justify-center overflow-hidden bg-black ${className ?? ''}`}>\n <canvas\n ref={canvasRef}\n data-preview-canvas\n className=\"block\"\n style={size ? { width: `${size.width}px`, height: `${size.height}px` } : { width: '100%', height: '100%' }}\n />\n {drawError ? (\n <p className=\"absolute inset-x-3 bottom-2 truncate rounded bg-rose-950/80 px-2 py-1 text-center text-xs text-rose-200\" role=\"alert\">\n {drawError}\n </p>\n ) : null}\n </div>\n )\n}\n","/**\n * Vertical accent line at the frame an in-flight drag is snapped to. Rendered\n * inside the horizontally-scrolled track area so its x position is plain\n * frame * zoom; visibility is owned by the editor (non-null point = visible).\n */\n\nimport type { SnapPoint } from '../contracts'\n\nexport interface SnapIndicatorLineProps {\n point: SnapPoint | null\n zoom: number\n}\n\nexport function SnapIndicatorLine({ point, zoom }: SnapIndicatorLineProps) {\n if (!point) return null\n return (\n <div\n data-snap-kind={point.kind}\n className=\"pointer-events-none absolute bottom-0 top-0 z-30 w-px bg-[var(--brand-primary)] shadow-[0_0_8px_var(--brand-primary)]\"\n style={{ left: `${point.frame * zoom}px` }}\n />\n )\n}\n","/**\n * Playhead overlay for the track area: a full-height line with a triangular\n * cap. Positioned in timeline pixels (frame * zoom) inside the scrolled\n * content, so it moves with horizontal scroll for free. Pointer-transparent —\n * scrubbing belongs to the ruler.\n */\n\nexport interface TimelinePlayheadProps {\n frame: number\n zoom: number\n}\n\nexport function TimelinePlayhead({ frame, zoom }: TimelinePlayheadProps) {\n return (\n <div\n data-timeline-playhead\n className=\"pointer-events-none absolute bottom-0 top-0 z-20\"\n style={{ left: `${frame * zoom}px` }}\n >\n <div className=\"absolute bottom-0 top-0 w-px bg-[var(--brand-primary)] shadow-[0_0_10px_var(--brand-primary)]\" />\n <div\n className=\"absolute -left-[5px] top-0 h-0 w-0 border-x-[5px] border-t-[7px] border-x-transparent\"\n style={{ borderTopColor: 'var(--brand-primary)' }}\n />\n </div>\n )\n}\n","/**\n * Adaptive timecode ruler. Tick density follows zoom through\n * `selectTickStepSeconds` (major ticks never closer than ~80px, minor ticks\n * at a fifth of the major step when they'd sit at least 8px apart). Click or\n * drag scrubs: the pointer is captured, every move quantizes to a whole frame,\n * and the frame is committed through `onScrub` (the editor routes it to\n * `PlaybackClock.seek`).\n */\n\nimport { useMemo } from 'react'\nimport type { PointerEvent } from 'react'\nimport { formatTimecode } from '../../sequences/model'\nimport { selectTickStepSeconds } from './interaction-math'\n\nexport interface TimelineRulerProps {\n fps: number\n durationFrames: number\n /** Pixels per frame. */\n zoom: number\n onScrub(frame: number): void\n}\n\ninterface RulerTick {\n frame: number\n label: string | null\n}\n\nexport function TimelineRuler({ fps, durationFrames, zoom, onScrub }: TimelineRulerProps) {\n const ticks = useMemo<RulerTick[]>(() => {\n const stepSeconds = selectTickStepSeconds({ zoom, fps })\n const majorStepFrames = stepSeconds * fps\n const minorStepFrames = Math.round(majorStepFrames / 5)\n const drawMinor = minorStepFrames * zoom >= 8 && minorStepFrames >= 1\n const result: RulerTick[] = []\n for (let frame = 0; frame <= durationFrames; frame += majorStepFrames) {\n result.push({ frame, label: formatTimecode(frame, fps) })\n if (!drawMinor) continue\n for (let minor = 1; minor < 5; minor += 1) {\n const minorFrame = frame + minor * minorStepFrames\n if (minorFrame >= durationFrames) break\n result.push({ frame: minorFrame, label: null })\n }\n }\n return result\n }, [durationFrames, fps, zoom])\n\n function frameFromPointer(event: PointerEvent<HTMLDivElement>): number {\n const rect = event.currentTarget.getBoundingClientRect()\n const frame = Math.round((event.clientX - rect.left) / zoom)\n return Math.max(0, Math.min(durationFrames, frame))\n }\n\n function handlePointerDown(event: PointerEvent<HTMLDivElement>) {\n if (event.button !== 0) return\n event.preventDefault()\n // Pointer capture is absent in non-browser test environments.\n if (typeof event.currentTarget.setPointerCapture === 'function') {\n event.currentTarget.setPointerCapture(event.pointerId)\n }\n onScrub(frameFromPointer(event))\n }\n\n function handlePointerMove(event: PointerEvent<HTMLDivElement>) {\n if (typeof event.currentTarget.hasPointerCapture !== 'function') return\n if (!event.currentTarget.hasPointerCapture(event.pointerId)) return\n onScrub(frameFromPointer(event))\n }\n\n return (\n <div\n data-timeline-ruler\n className=\"relative h-7 cursor-ew-resize select-none border-b border-[var(--border-default)] bg-[var(--bg-input)]\"\n style={{ width: `${durationFrames * zoom}px` }}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n >\n {ticks.map((tick) => (\n <div\n key={tick.frame}\n className={`absolute bottom-0 w-px bg-[var(--border-default)] ${tick.label !== null ? 'top-2.5' : 'top-[18px]'}`}\n style={{ left: `${tick.frame * zoom}px` }}\n >\n {tick.label !== null ? (\n <span className=\"absolute -top-2 left-1 whitespace-nowrap font-mono text-[10px] leading-none text-[var(--text-muted)]\">\n {tick.label}\n </span>\n ) : null}\n </div>\n ))}\n </div>\n )\n}\n","/**\n * One clip on a track lane. Owns the pointer gestures that edit it:\n *\n * - body drag → move (horizontal frames + vertical retarget onto another\n * unlocked track of the same kind)\n * - edge handles → head/tail trim, clamped to MIN_SEQUENCE_CLIP_FRAMES and\n * the source material bounds\n * - double-click → inline caption text edit (caption clips only)\n *\n * Gesture discipline: pointer capture on gesture start, every move quantizes\n * to whole frames, Escape restores the pre-drag state without emitting, and a\n * completed gesture commits EXACTLY ONCE through the `onCommit*` callbacks —\n * the editor turns that into one command on the stack (one undo step). The\n * chip never writes timeline state itself; until commit it renders a local\n * preview only.\n *\n * Vertical retarget reads lane geometry captured at gesture start (rects of\n * every `[data-lane-track]` under the editor's `[data-timeline-tracks]` root),\n * so the moving chip itself can never occlude the hit test.\n */\n\nimport { useEffect, useRef, useState } from 'react'\nimport type { PointerEvent as ReactPointerEvent } from 'react'\nimport { formatTimecode } from '../../sequences/model'\nimport type { SequenceClip, SequenceTrack, SequenceTrackKind } from '../../sequences/model'\nimport type { SnapPoint, VideoFrameProvider, WaveformData } from '../contracts'\nimport { loadWaveform, drawWaveform } from '../media/waveform'\nimport {\n clipChipGeometry,\n framesFromPixelDelta,\n moveDragStartFrame,\n trimEndDrag,\n trimStartDrag,\n} from './interaction-math'\n\nexport interface ClipMoveCommit {\n clipId: string\n startFrame: number\n trackId: string\n}\n\nexport interface ClipTrimCommit {\n clipId: string\n startFrame: number\n durationFrames: number\n sourceInFrame: number\n}\n\nexport interface TimelineClipChipProps {\n clip: SequenceClip\n track: SequenceTrack\n fps: number\n /** Pixels per frame. */\n zoom: number\n sequenceDurationFrames: number\n selected: boolean\n canWrite: boolean\n frameProvider: VideoFrameProvider\n /** Snap a candidate move (both clip edges considered); editor closes over\n * the engine's snap points. */\n snapMove(candidate: { startFrame: number; durationFrames: number; clipId: string }): { startFrame: number; point: SnapPoint | null }\n /** Snap a single trim edge. */\n snapEdge(candidate: { frame: number; clipId: string }): { frame: number; point: SnapPoint | null }\n onSnapPointChange(point: SnapPoint | null): void\n onSelect(clipId: string, additive: boolean): void\n onCommitMove(input: ClipMoveCommit): void\n onCommitTrim(input: ClipTrimCommit): void\n onCommitText(input: { clipId: string; text: string }): void\n}\n\ntype GestureKind = 'move' | 'trim-start' | 'trim-end'\n\ninterface LaneTarget {\n trackId: string\n top: number\n bottom: number\n offsetY: number\n}\n\ninterface GestureState {\n kind: GestureKind\n pointerId: number\n originClientX: number\n origin: { startFrame: number; durationFrames: number; sourceInFrame: number }\n /** Same-kind unlocked lanes, captured once at gesture start. */\n laneTargets: LaneTarget[]\n originTrackId: string\n}\n\ninterface GesturePreview {\n startFrame: number\n durationFrames: number\n sourceInFrame: number\n trackId: string\n translateY: number\n moved: boolean\n}\n\nconst KIND_TONES: Record<SequenceTrackKind, string> = {\n video: 'border-sky-400/40 bg-sky-500/15',\n audio: 'border-emerald-400/40 bg-emerald-500/15',\n caption: 'border-amber-400/40 bg-amber-500/15',\n reference: 'border-zinc-400/40 bg-zinc-500/15',\n agent: 'border-violet-400/40 bg-violet-500/15',\n}\n\n/** Spec'd cache key is the clip id: a clip's waveform survives re-renders and\n * zoom changes; a different clip with the same media decodes independently. */\nconst waveformCache = new Map<string, Promise<WaveformData>>()\nconst WAVEFORM_BUCKETS = 256\n\nfunction sourceDurationFrames(clip: SequenceClip, fps: number): number | undefined {\n if (clip.sourceOutFrame !== null && clip.sourceOutFrame !== undefined) return clip.sourceOutFrame\n if (clip.media?.durationSeconds !== undefined) return Math.round(clip.media.durationSeconds * fps)\n return undefined\n}\n\nexport function TimelineClipChip(props: TimelineClipChipProps) {\n const { clip, track, fps, zoom, selected, canWrite, frameProvider } = props\n const rootRef = useRef<HTMLDivElement | null>(null)\n const gestureRef = useRef<GestureState | null>(null)\n const [preview, setPreview] = useState<GesturePreview | null>(null)\n /** Mirror of `preview` readable inside pointerup before React flushes the\n * last pointermove's state update. */\n const previewRef = useRef<GesturePreview | null>(null)\n const [editingText, setEditingText] = useState<string | null>(null)\n const posterRef = useRef<HTMLCanvasElement | null>(null)\n const waveformRef = useRef<HTMLCanvasElement | null>(null)\n\n const shown = preview ?? {\n startFrame: clip.startFrame,\n durationFrames: clip.durationFrames,\n sourceInFrame: clip.sourceInFrame,\n trackId: clip.trackId,\n translateY: 0,\n moved: false,\n }\n const geometry = clipChipGeometry({ startFrame: shown.startFrame, durationFrames: shown.durationFrames, zoom })\n const interactive = canWrite && !track.locked\n\n // -------------------------------------------------------------------------\n // Gestures\n // -------------------------------------------------------------------------\n\n function collectLaneTargets(): LaneTarget[] {\n const root = rootRef.current?.closest('[data-timeline-tracks]')\n const originLane = rootRef.current?.closest('[data-lane-track]')\n if (!root || !originLane) return []\n const originTop = originLane.getBoundingClientRect().top\n const targets: LaneTarget[] = []\n for (const lane of Array.from(root.querySelectorAll<HTMLElement>('[data-lane-track]'))) {\n if (lane.dataset.laneKind !== track.kind || lane.dataset.laneLocked === 'true') continue\n const rect = lane.getBoundingClientRect()\n const trackId = lane.dataset.laneTrack\n if (!trackId) continue\n targets.push({ trackId, top: rect.top, bottom: rect.bottom, offsetY: rect.top - originTop })\n }\n return targets\n }\n\n function beginGesture(event: ReactPointerEvent<HTMLElement>, kind: GestureKind) {\n if (!interactive || event.button !== 0 || editingText !== null) return\n event.preventDefault()\n event.stopPropagation()\n // Pointer capture is absent in non-browser test environments; the gesture\n // still works there, it just loses the off-element tracking guarantee.\n if (typeof event.currentTarget.setPointerCapture === 'function') {\n event.currentTarget.setPointerCapture(event.pointerId)\n }\n gestureRef.current = {\n kind,\n pointerId: event.pointerId,\n originClientX: event.clientX,\n origin: { startFrame: clip.startFrame, durationFrames: clip.durationFrames, sourceInFrame: clip.sourceInFrame },\n laneTargets: kind === 'move' ? collectLaneTargets() : [],\n originTrackId: clip.trackId,\n }\n document.body.style.cursor = kind === 'move' ? 'grabbing' : 'ew-resize'\n document.body.style.userSelect = 'none'\n }\n\n function applyPreview(next: GesturePreview) {\n previewRef.current = next\n setPreview(next)\n }\n\n function updateGesture(event: ReactPointerEvent<HTMLElement>) {\n const gesture = gestureRef.current\n if (!gesture || event.pointerId !== gesture.pointerId) return\n const deltaFrames = framesFromPixelDelta(event.clientX - gesture.originClientX, zoom)\n const moved = previewRef.current?.moved || Math.abs(event.clientX - gesture.originClientX) > 3\n\n if (gesture.kind === 'move') {\n const candidate = moveDragStartFrame({\n originStartFrame: gesture.origin.startFrame,\n durationFrames: gesture.origin.durationFrames,\n deltaFrames,\n sequenceDurationFrames: props.sequenceDurationFrames,\n })\n const snapped = props.snapMove({ startFrame: candidate, durationFrames: gesture.origin.durationFrames, clipId: clip.id })\n const startFrame = moveDragStartFrame({\n originStartFrame: snapped.startFrame,\n durationFrames: gesture.origin.durationFrames,\n deltaFrames: 0,\n sequenceDurationFrames: props.sequenceDurationFrames,\n })\n // A clamp that displaced the snapped frame voids the snap indicator.\n props.onSnapPointChange(startFrame === snapped.startFrame ? snapped.point : null)\n const lane = gesture.laneTargets.find((target) => event.clientY >= target.top && event.clientY < target.bottom)\n applyPreview({\n startFrame,\n durationFrames: gesture.origin.durationFrames,\n sourceInFrame: gesture.origin.sourceInFrame,\n trackId: lane?.trackId ?? gesture.originTrackId,\n translateY: lane?.offsetY ?? 0,\n moved,\n })\n return\n }\n\n if (gesture.kind === 'trim-start') {\n const raw = trimStartDrag({\n originStartFrame: gesture.origin.startFrame,\n originDurationFrames: gesture.origin.durationFrames,\n originSourceInFrame: gesture.origin.sourceInFrame,\n deltaFrames,\n })\n const snapped = props.snapEdge({ frame: raw.startFrame, clipId: clip.id })\n const clamped = trimStartDrag({\n originStartFrame: gesture.origin.startFrame,\n originDurationFrames: gesture.origin.durationFrames,\n originSourceInFrame: gesture.origin.sourceInFrame,\n deltaFrames: snapped.frame - gesture.origin.startFrame,\n })\n props.onSnapPointChange(clamped.startFrame === snapped.frame ? snapped.point : null)\n applyPreview({ ...clamped, trackId: gesture.originTrackId, translateY: 0, moved })\n return\n }\n\n const rawEnd = gesture.origin.startFrame + trimEndDrag({\n originStartFrame: gesture.origin.startFrame,\n originDurationFrames: gesture.origin.durationFrames,\n sourceInFrame: gesture.origin.sourceInFrame,\n deltaFrames,\n sequenceDurationFrames: props.sequenceDurationFrames,\n sourceDurationFrames: sourceDurationFrames(clip, fps),\n }).durationFrames\n const snapped = props.snapEdge({ frame: rawEnd, clipId: clip.id })\n const clamped = trimEndDrag({\n originStartFrame: gesture.origin.startFrame,\n originDurationFrames: gesture.origin.durationFrames,\n sourceInFrame: gesture.origin.sourceInFrame,\n deltaFrames: snapped.frame - (gesture.origin.startFrame + gesture.origin.durationFrames),\n sequenceDurationFrames: props.sequenceDurationFrames,\n sourceDurationFrames: sourceDurationFrames(clip, fps),\n })\n props.onSnapPointChange(gesture.origin.startFrame + clamped.durationFrames === snapped.frame ? snapped.point : null)\n applyPreview({\n startFrame: gesture.origin.startFrame,\n durationFrames: clamped.durationFrames,\n sourceInFrame: gesture.origin.sourceInFrame,\n trackId: gesture.originTrackId,\n translateY: 0,\n moved,\n })\n }\n\n function endGestureCleanup() {\n gestureRef.current = null\n previewRef.current = null\n setPreview(null)\n props.onSnapPointChange(null)\n document.body.style.cursor = ''\n document.body.style.userSelect = ''\n }\n\n function finishGesture(event: ReactPointerEvent<HTMLElement>) {\n const gesture = gestureRef.current\n if (!gesture || event.pointerId !== gesture.pointerId) return\n const finalPreview = previewRef.current\n const kind = gesture.kind\n const origin = gesture.origin\n const originTrackId = gesture.originTrackId\n endGestureCleanup()\n\n if (!finalPreview || !finalPreview.moved) {\n props.onSelect(clip.id, event.shiftKey)\n return\n }\n if (kind === 'move') {\n if (finalPreview.startFrame === origin.startFrame && finalPreview.trackId === originTrackId) return\n props.onCommitMove({ clipId: clip.id, startFrame: finalPreview.startFrame, trackId: finalPreview.trackId })\n return\n }\n if (finalPreview.startFrame === origin.startFrame && finalPreview.durationFrames === origin.durationFrames) return\n props.onCommitTrim({\n clipId: clip.id,\n startFrame: finalPreview.startFrame,\n durationFrames: finalPreview.durationFrames,\n sourceInFrame: finalPreview.sourceInFrame,\n })\n }\n\n // Escape abandons the gesture: pre-drag state restores, nothing emits.\n useEffect(() => {\n if (!preview) return\n function onKeyDown(event: KeyboardEvent) {\n if (event.key !== 'Escape') return\n event.preventDefault()\n endGestureCleanup()\n }\n window.addEventListener('keydown', onKeyDown)\n return () => window.removeEventListener('keydown', onKeyDown)\n // endGestureCleanup is stable in behavior; preview presence gates the listener.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [preview !== null])\n\n // -------------------------------------------------------------------------\n // Media previews (poster frame / waveform)\n // -------------------------------------------------------------------------\n\n const mediaUrl = clip.media?.url\n const mediaKind = clip.media?.kind\n const isVisualMedia = mediaKind === 'video' || mediaKind === 'image'\n const isAudioMedia = mediaKind === 'audio'\n\n useEffect(() => {\n const canvas = posterRef.current\n if (!canvas || !mediaUrl || !isVisualMedia) return\n const ctx = canvas.getContext('2d')\n // Canvas 2D is unavailable in non-browser test environments; the chip\n // still lays out, only the poster paint is skipped.\n if (!ctx) return\n const dpr = window.devicePixelRatio || 1\n const cssWidth = canvas.clientWidth || 40\n const cssHeight = canvas.clientHeight || 40\n canvas.width = Math.round(cssWidth * dpr)\n canvas.height = Math.round(cssHeight * dpr)\n ctx.scale(dpr, dpr)\n let cancelled = false\n frameProvider\n .drawFrame(mediaUrl, clip.sourceInFrame / fps, ctx, { x: 0, y: 0, width: cssWidth, height: cssHeight })\n .catch(() => {\n // A failed poster leaves the chip's base tone; the same failure\n // surfaces loudly in the preview canvas where it blocks real work.\n if (!cancelled) canvas.dataset.posterError = 'true'\n })\n return () => {\n cancelled = true\n }\n }, [mediaUrl, isVisualMedia, clip.sourceInFrame, fps, frameProvider])\n\n useEffect(() => {\n const canvas = waveformRef.current\n if (!canvas || !mediaUrl || !isAudioMedia) return\n let pending = waveformCache.get(clip.id)\n if (!pending) {\n pending = loadWaveform(mediaUrl, WAVEFORM_BUCKETS)\n pending.catch(() => waveformCache.delete(clip.id))\n waveformCache.set(clip.id, pending)\n }\n let cancelled = false\n pending\n .then((data) => {\n if (cancelled) return\n const ctx = canvas.getContext('2d')\n if (!ctx) return\n const dpr = window.devicePixelRatio || 1\n const cssWidth = canvas.clientWidth || 1\n const cssHeight = canvas.clientHeight || 1\n canvas.width = Math.round(cssWidth * dpr)\n canvas.height = Math.round(cssHeight * dpr)\n ctx.scale(dpr, dpr)\n // Paint only the source window this clip plays.\n const bucketsPerSecond = data.peaks.length / data.durationSeconds\n const fromBucket = Math.floor((shown.sourceInFrame / fps) * bucketsPerSecond)\n const toBucket = Math.ceil(((shown.sourceInFrame + shown.durationFrames) / fps) * bucketsPerSecond)\n const peaks = data.peaks.subarray(Math.max(0, fromBucket), Math.min(data.peaks.length, Math.max(fromBucket + 1, toBucket)))\n if (peaks.length === 0) return\n drawWaveform(\n ctx,\n { peaks, samplesPerBucket: data.samplesPerBucket, durationSeconds: data.durationSeconds },\n { x: 0, y: 0, width: cssWidth, height: cssHeight },\n 'rgba(52, 211, 153, 0.75)',\n )\n })\n .catch(() => {\n if (!cancelled) canvas.dataset.waveformError = 'true'\n })\n return () => {\n cancelled = true\n }\n }, [mediaUrl, isAudioMedia, clip.id, fps, shown.sourceInFrame, shown.durationFrames, geometry.width])\n\n // -------------------------------------------------------------------------\n // Caption text editing\n // -------------------------------------------------------------------------\n\n function commitText() {\n if (editingText === null) return\n const next = editingText.trim()\n setEditingText(null)\n if (next.length === 0 || next === clip.text) return\n props.onCommitText({ clipId: clip.id, text: next })\n }\n\n const isCaption = track.kind === 'caption'\n const dragging = preview !== null\n\n return (\n <div\n ref={rootRef}\n data-clip-id={clip.id}\n role=\"button\"\n tabIndex={-1}\n title={clip.label}\n className={`group absolute bottom-1 top-1 overflow-hidden rounded border text-left select-none ${KIND_TONES[track.kind]} ${\n selected ? 'ring-2 ring-[var(--brand-primary)]' : 'hover:ring-1 hover:ring-[var(--text-muted)]'\n } ${clip.disabled ? 'opacity-40' : ''} ${dragging ? 'z-30 shadow-lg shadow-black/30' : ''} ${\n interactive ? 'cursor-grab active:cursor-grabbing' : 'cursor-default'\n }`}\n style={{\n left: `${geometry.left}px`,\n width: `${geometry.width}px`,\n transform: shown.translateY !== 0 ? `translateY(${shown.translateY}px)` : undefined,\n }}\n onPointerDown={(event) => beginGesture(event, 'move')}\n onPointerMove={updateGesture}\n onPointerUp={finishGesture}\n onPointerCancel={endGestureCleanup}\n onClick={(event) => event.stopPropagation()}\n onDoubleClick={(event) => {\n event.stopPropagation()\n if (isCaption && interactive && typeof clip.text === 'string') setEditingText(clip.text)\n }}\n >\n {isAudioMedia ? <canvas ref={waveformRef} className=\"absolute inset-0 h-full w-full\" /> : null}\n\n <div className=\"relative flex h-full min-w-0 items-stretch gap-1.5 px-1.5 py-1\">\n {isVisualMedia ? (\n <canvas ref={posterRef} className=\"h-full w-10 shrink-0 rounded-sm bg-black/40 object-cover\" />\n ) : null}\n <div className=\"min-w-0 flex-1\">\n <div className=\"truncate text-[11px] font-medium leading-4 text-[var(--text-primary)]\">\n {isCaption && typeof clip.text === 'string' ? clip.text : clip.label}\n </div>\n <span className=\"mt-0.5 inline-block rounded bg-black/30 px-1 font-mono text-[9px] leading-3 text-[var(--text-secondary)]\">\n {formatTimecode(shown.durationFrames, fps)}\n </span>\n </div>\n </div>\n\n {editingText !== null ? (\n <input\n autoFocus\n value={editingText}\n onChange={(event) => setEditingText(event.target.value)}\n onPointerDown={(event) => event.stopPropagation()}\n onKeyDown={(event) => {\n if (event.key === 'Enter') commitText()\n if (event.key === 'Escape') setEditingText(null)\n event.stopPropagation()\n }}\n onBlur={commitText}\n className=\"absolute inset-0 z-10 w-full bg-black/80 px-1.5 text-[11px] text-[var(--text-primary)] outline-none ring-1 ring-[var(--brand-primary)]\"\n aria-label=\"Caption text\"\n />\n ) : null}\n\n {interactive ? (\n <>\n <span\n data-trim-handle=\"start\"\n className=\"absolute bottom-0 left-0 top-0 z-10 w-1.5 cursor-ew-resize bg-transparent opacity-0 transition group-hover:opacity-100 group-hover:bg-[var(--brand-primary)]/60\"\n onPointerDown={(event) => beginGesture(event, 'trim-start')}\n onPointerMove={updateGesture}\n onPointerUp={finishGesture}\n onPointerCancel={endGestureCleanup}\n aria-hidden\n />\n <span\n data-trim-handle=\"end\"\n className=\"absolute bottom-0 right-0 top-0 z-10 w-1.5 cursor-ew-resize bg-transparent opacity-0 transition group-hover:opacity-100 group-hover:bg-[var(--brand-primary)]/60\"\n onPointerDown={(event) => beginGesture(event, 'trim-end')}\n onPointerMove={updateGesture}\n onPointerUp={finishGesture}\n onPointerCancel={endGestureCleanup}\n aria-hidden\n />\n </>\n ) : null}\n </div>\n )\n}\n","/**\n * Waveform rendering for audio clips: bucketed max-abs peaks computed once\n * per (media, zoom bucket count) and painted as mirrored bars around the\n * track lane's midline. `computeWaveform` is pure so peak math is testable\n * without Web Audio; `loadWaveform` is the browser edge that decodes real\n * media into it.\n */\n\nimport type { WaveformData } from '../contracts'\n\n/** Structural slice of Web Audio's AudioBuffer so peak math runs on synthetic\n * fixtures in tests and on real decoded buffers in the browser. */\nexport interface AudioBufferLike {\n numberOfChannels: number\n length: number\n sampleRate: number\n duration: number\n getChannelData(channel: number): Float32Array\n}\n\n/** One max-abs peak per bucket, taken across all channels. `peaks[b]` is the\n * loudest absolute sample in bucket `b`; rendering mirrors it around the\n * midline, which is what the contract's \"peak pair\" denotes. Buckets past\n * the end of short audio hold 0. */\nexport function computeWaveform(buffer: AudioBufferLike, bucketCount: number): WaveformData {\n if (!Number.isInteger(bucketCount) || bucketCount < 1) {\n throw new Error(`bucketCount must be a positive integer, got ${bucketCount}`)\n }\n if (!Number.isInteger(buffer.length) || buffer.length < 1) {\n throw new Error(`audio buffer is empty (length ${buffer.length}) — cannot compute a waveform`)\n }\n if (!Number.isInteger(buffer.numberOfChannels) || buffer.numberOfChannels < 1) {\n throw new Error(`audio buffer must have at least one channel, got ${buffer.numberOfChannels}`)\n }\n if (!Number.isFinite(buffer.duration) || buffer.duration <= 0) {\n throw new Error(`audio buffer duration must be positive, got ${buffer.duration}`)\n }\n const samplesPerBucket = Math.ceil(buffer.length / bucketCount)\n const peaks = new Float32Array(bucketCount)\n for (let channel = 0; channel < buffer.numberOfChannels; channel++) {\n const data = buffer.getChannelData(channel)\n if (data.length !== buffer.length) {\n throw new Error(`channel ${channel} has ${data.length} samples, expected ${buffer.length}`)\n }\n for (let bucket = 0; bucket < bucketCount; bucket++) {\n const start = bucket * samplesPerBucket\n if (start >= data.length) break\n let peak = peaks[bucket] as number\n for (const sample of data.subarray(start, Math.min(data.length, start + samplesPerBucket))) {\n const magnitude = Math.abs(sample)\n if (magnitude > peak) peak = magnitude\n }\n peaks[bucket] = peak\n }\n }\n return { peaks, samplesPerBucket, durationSeconds: buffer.duration }\n}\n\n/** Fetch + decode `mediaUrl` and bucket it. Pass `ctx` to reuse a shared\n * AudioContext; otherwise one is created and closed around the decode. */\nexport async function loadWaveform(mediaUrl: string, bucketCount: number, ctx?: AudioContext): Promise<WaveformData> {\n const response = await fetch(mediaUrl)\n if (!response.ok) {\n throw new Error(`failed to fetch audio for waveform: ${response.status} ${response.statusText} from ${mediaUrl}`)\n }\n const bytes = await response.arrayBuffer()\n const ownsContext = ctx === undefined\n if (ctx === undefined) {\n if (typeof AudioContext === 'undefined') {\n throw new Error('loadWaveform requires Web Audio (AudioContext) — pass a ctx or call from a browser')\n }\n ctx = new AudioContext()\n }\n try {\n const decoded = await ctx.decodeAudioData(bytes)\n return computeWaveform(decoded, bucketCount)\n } finally {\n if (ownsContext) await ctx.close()\n }\n}\n\n/** Paint mirrored peak bars centered on the rect's midline. Silent buckets\n * still paint a 1px hairline so the lane reads as audio, not as empty;\n * peaks beyond ±1.0 (hot masters) clip to the full lane height. */\nexport function drawWaveform(\n ctx: CanvasRenderingContext2D,\n data: WaveformData,\n rect: { x: number; y: number; width: number; height: number },\n color: string,\n): void {\n if (data.peaks.length === 0) {\n throw new Error('waveform has no peaks — compute it with a positive bucketCount')\n }\n if (!(rect.width > 0) || !(rect.height > 0)) {\n throw new Error(`drawWaveform requires positive rect dimensions, got ${rect.width}x${rect.height}`)\n }\n ctx.fillStyle = color\n const midline = rect.y + rect.height / 2\n const step = rect.width / data.peaks.length\n const barWidth = Math.max(1, step - 1)\n for (const [index, peak] of data.peaks.entries()) {\n const half = Math.min(1, Math.max(0, peak)) * (rect.height / 2)\n const barHeight = Math.max(1, half * 2)\n ctx.fillRect(rect.x + index * step, midline - barHeight / 2, barWidth, barHeight)\n }\n}\n","/**\n * Inline SVG glyphs for the timeline editor. The package stays dependency-free\n * beyond React (the repo convention — see web-react), so the handful of icons\n * the editor needs are inlined with lucide-equivalent path data.\n */\n\ninterface GlyphProps {\n className?: string\n}\n\nfunction glyph(paths: React.ReactNode) {\n return function Glyph({ className }: GlyphProps) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n {paths}\n </svg>\n )\n }\n}\n\nexport const FilmGlyph = glyph(\n <>\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" />\n <path d=\"M7 3v18M17 3v18M3 8h4M3 16h4M17 8h4M17 16h4\" />\n </>,\n)\n\nexport const AudioGlyph = glyph(\n <path d=\"M2 12h2l2-7 3 14 3-9 2 5 2-3h6\" />,\n)\n\nexport const CaptionGlyph = glyph(\n <>\n <rect x=\"2\" y=\"5\" width=\"20\" height=\"14\" rx=\"2\" />\n <path d=\"M6 13h4M6 16h8M14 13h4\" />\n </>,\n)\n\nexport const ReferenceGlyph = glyph(\n <>\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" />\n <circle cx=\"9\" cy=\"9\" r=\"2\" />\n <path d=\"m21 15-3.1-3.1a2 2 0 0 0-2.8 0L6 21\" />\n </>,\n)\n\nexport const AgentGlyph = glyph(\n <>\n <rect x=\"4\" y=\"8\" width=\"16\" height=\"12\" rx=\"2\" />\n <path d=\"M12 8V4M8 4h8M9 13v2M15 13v2\" />\n </>,\n)\n\nexport const LockGlyph = glyph(\n <>\n <rect x=\"5\" y=\"11\" width=\"14\" height=\"10\" rx=\"2\" />\n <path d=\"M8 11V7a4 4 0 0 1 8 0v4\" />\n </>,\n)\n\nexport const MutedGlyph = glyph(\n <>\n <path d=\"M11 5 6 9H2v6h4l5 4z\" />\n <path d=\"m23 9-6 6M17 9l6 6\" />\n </>,\n)\n\nexport const PlayGlyph = glyph(\n <path d=\"m6 4 14 8-14 8z\" fill=\"currentColor\" stroke=\"none\" />,\n)\n\nexport const PauseGlyph = glyph(\n <path d=\"M7 4h3v16H7zM14 4h3v16h-3z\" fill=\"currentColor\" stroke=\"none\" />,\n)\n\nexport const UndoGlyph = glyph(\n <path d=\"M3 7v6h6M3 13a9 9 0 1 0 3-7.7\" />,\n)\n\nexport const RedoGlyph = glyph(\n <path d=\"M21 7v6h-6M21 13a9 9 0 1 1-3-7.7\" />,\n)\n\nexport const MagnetGlyph = glyph(\n <>\n <path d=\"m6 15-4-4 6.75-6.77a7.79 7.79 0 0 1 11 11L13 22l-4-4 6.39-6.36a2.14 2.14 0 0 0-3-3z\" />\n <path d=\"m5 8 4 4M12 15l4 4\" />\n </>,\n)\n\nexport const ScissorsGlyph = glyph(\n <>\n <circle cx=\"6\" cy=\"6\" r=\"3\" />\n <circle cx=\"6\" cy=\"18\" r=\"3\" />\n <path d=\"M20 4 8.12 15.88M14.47 14.48 20 20M8.12 8.12 12 12\" />\n </>,\n)\n\nexport const CaptionPlusGlyph = glyph(\n <>\n <rect x=\"2\" y=\"5\" width=\"20\" height=\"14\" rx=\"2\" />\n <path d=\"M12 9v6M9 12h6\" />\n </>,\n)\n","/**\n * One track: a sticky-left header (name, kind glyph, lock/mute state) and a\n * lane sized in timeline pixels (durationFrames * zoom) carrying a\n * `TimelineClipChip` per clip. The lane advertises itself through\n * `data-lane-track`/`data-lane-kind`/`data-lane-locked` — the geometry chips\n * read for vertical drag retargeting. Clicking empty lane space seeks the\n * playhead.\n */\n\nimport type { PointerEvent as ReactPointerEvent } from 'react'\nimport type { SequenceClip, SequenceTrack, SequenceTrackKind } from '../../sequences/model'\nimport type { SnapPoint, VideoFrameProvider } from '../contracts'\nimport { TimelineClipChip } from './TimelineClipChip'\nimport type { ClipMoveCommit, ClipTrimCommit } from './TimelineClipChip'\nimport { AgentGlyph, AudioGlyph, CaptionGlyph, FilmGlyph, LockGlyph, MutedGlyph, ReferenceGlyph } from './glyphs'\n\nconst LANE_HEIGHTS: Record<SequenceTrackKind, string> = {\n video: 'h-16',\n reference: 'h-16',\n audio: 'h-14',\n caption: 'h-9',\n agent: 'h-9',\n}\n\nconst KIND_GLYPHS: Record<SequenceTrackKind, (props: { className?: string }) => React.ReactNode> = {\n video: FilmGlyph,\n audio: AudioGlyph,\n caption: CaptionGlyph,\n reference: ReferenceGlyph,\n agent: AgentGlyph,\n}\n\nexport interface TimelineTrackRowProps {\n track: SequenceTrack\n clips: SequenceClip[]\n fps: number\n zoom: number\n sequenceDurationFrames: number\n selectedClipIds: ReadonlySet<string>\n canWrite: boolean\n frameProvider: VideoFrameProvider\n snapMove(candidate: { startFrame: number; durationFrames: number; clipId: string }): { startFrame: number; point: SnapPoint | null }\n snapEdge(candidate: { frame: number; clipId: string }): { frame: number; point: SnapPoint | null }\n onSnapPointChange(point: SnapPoint | null): void\n onSelectClip(clipId: string, additive: boolean): void\n onCommitMove(input: ClipMoveCommit): void\n onCommitTrim(input: ClipTrimCommit): void\n onCommitText(input: { clipId: string; text: string }): void\n onLaneSeek(frame: number): void\n}\n\nexport function TimelineTrackRow(props: TimelineTrackRowProps) {\n const { track, clips, fps, zoom, sequenceDurationFrames } = props\n const Glyph = KIND_GLYPHS[track.kind]\n const laneHeight = LANE_HEIGHTS[track.kind]\n\n function handleLanePointerDown(event: ReactPointerEvent<HTMLDivElement>) {\n // Chips stop propagation; a pointerdown reaching the lane is empty space.\n if (event.button !== 0) return\n const rect = event.currentTarget.getBoundingClientRect()\n const frame = Math.max(0, Math.min(sequenceDurationFrames, Math.round((event.clientX - rect.left) / zoom)))\n props.onLaneSeek(frame)\n }\n\n return (\n <div className=\"flex border-b border-[var(--border-default)] last:border-b-0\">\n <div className={`sticky left-0 z-10 flex w-36 shrink-0 items-center gap-2 border-r border-[var(--border-default)] bg-[var(--bg-input)] px-2.5 ${laneHeight}`}>\n <Glyph className=\"h-3.5 w-3.5 shrink-0 text-[var(--text-muted)]\" />\n <span className=\"min-w-0 flex-1 truncate text-xs font-medium text-[var(--text-secondary)]\">{track.name}</span>\n {track.locked ? <LockGlyph className=\"h-3 w-3 shrink-0 text-amber-400\" /> : null}\n {track.muted ? <MutedGlyph className=\"h-3 w-3 shrink-0 text-[var(--text-muted)]\" /> : null}\n </div>\n <div\n data-lane-track={track.id}\n data-lane-kind={track.kind}\n data-lane-locked={track.locked ? 'true' : 'false'}\n className={`relative ${laneHeight} ${track.muted ? 'opacity-60' : ''}`}\n style={{ width: `${sequenceDurationFrames * zoom}px` }}\n onPointerDown={handleLanePointerDown}\n >\n {clips.map((clip) => (\n <TimelineClipChip\n key={clip.id}\n clip={clip}\n track={track}\n fps={fps}\n zoom={zoom}\n sequenceDurationFrames={sequenceDurationFrames}\n selected={props.selectedClipIds.has(clip.id)}\n canWrite={props.canWrite}\n frameProvider={props.frameProvider}\n snapMove={props.snapMove}\n snapEdge={props.snapEdge}\n onSnapPointChange={props.onSnapPointChange}\n onSelect={props.onSelectClip}\n onCommitMove={props.onCommitMove}\n onCommitTrim={props.onCommitTrim}\n onCommitText={props.onCommitText}\n />\n ))}\n </div>\n </div>\n )\n}\n","/**\n * Zoom slider mapped through `ZoomMath` so equal slider travel feels like\n * equal zoom ratio. The slider's numeric domain is derived from the math\n * itself (`zoomToSlider(minZoom)..zoomToSlider(maxZoom)`) — this component\n * never assumes what scale the engine chose.\n */\n\nimport type { ZoomMath } from '../contracts'\n\nexport interface ZoomControlProps {\n zoomMath: ZoomMath\n zoom: number\n onZoomChange(zoom: number): void\n}\n\nexport function ZoomControl({ zoomMath, zoom, onZoomChange }: ZoomControlProps) {\n const sliderMin = zoomMath.zoomToSlider(zoomMath.minZoom)\n const sliderMax = zoomMath.zoomToSlider(zoomMath.maxZoom)\n const sliderStep = (sliderMax - sliderMin) / 100\n const slider = zoomMath.zoomToSlider(zoom)\n\n function setSlider(next: number) {\n const clamped = Math.max(sliderMin, Math.min(sliderMax, next))\n onZoomChange(zoomMath.sliderToZoom(clamped))\n }\n\n return (\n <div className=\"flex items-center gap-1.5\">\n <button\n type=\"button\"\n aria-label=\"Zoom out\"\n onClick={() => setSlider(slider - sliderStep * 10)}\n className=\"flex h-6 w-6 items-center justify-center rounded border border-[var(--border-default)] text-sm leading-none text-[var(--text-secondary)] hover:text-[var(--text-primary)]\"\n >\n −\n </button>\n <input\n type=\"range\"\n aria-label=\"Timeline zoom\"\n min={sliderMin}\n max={sliderMax}\n step={sliderStep}\n value={slider}\n onChange={(event) => setSlider(Number(event.target.value))}\n className=\"h-1 w-24 cursor-pointer accent-[var(--brand-primary)]\"\n />\n <button\n type=\"button\"\n aria-label=\"Zoom in\"\n onClick={() => setSlider(slider + sliderStep * 10)}\n className=\"flex h-6 w-6 items-center justify-center rounded border border-[var(--border-default)] text-sm leading-none text-[var(--text-secondary)] hover:text-[var(--text-primary)]\"\n >\n +\n </button>\n </div>\n )\n}\n"],"mappings":"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","names":["useEffect","useMemo","useRef","useState","requestPaint","jsx","jsx","jsxs","useMemo","jsx","useMemo","useEffect","useRef","useState","jsx","jsxs","useRef","useState","snapped","clamped","useEffect","Fragment","jsx","jsxs","jsx","jsxs","jsx","jsxs","Fragment","jsx","jsxs","useMemo","useRef","useEffect","useState"]}
@@ -47,7 +47,7 @@ import {
47
47
  trimClipCommand,
48
48
  trimEndDrag,
49
49
  trimStartDrag
50
- } from "../chunk-IHR6K3GF.js";
50
+ } from "../chunk-RJ2ZHK5D.js";
51
51
  import "../chunk-ZYBWGSAZ.js";
52
52
 
53
53
  // src/sequences-react/media/transcription.ts
@@ -182,7 +182,7 @@ function createWhisperTranscriptionProvider(opts) {
182
182
 
183
183
  // src/sequences-react/lazy.tsx
184
184
  import React from "react";
185
- var SequenceTimelineEditorLazy = React.lazy(() => import("../TimelineEditor-OXPJZDP2.js"));
185
+ var SequenceTimelineEditorLazy = React.lazy(() => import("../TimelineEditor-JDG54OBP.js"));
186
186
  export {
187
187
  COMMAND_HISTORY_LIMIT,
188
188
  DEFAULT_MAX_MEDIA_ELEMENTS,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-app",
3
- "version": "0.16.0",
3
+ "version": "0.16.1",
4
4
  "packageManager": "pnpm@10.33.4",
5
5
  "description": "Application-shell framework for Tangle agent products: a bounded tool loop, the structured agent\u2192app tool side channel, integration-hub client, per-workspace billing, and crypto \u2014 composed over the Tangle agent substrate through typed seams.",
6
6
  "keywords": [
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/sequences-react/components/TimelineEditor.tsx","../src/sequences-react/engine/command-stack.ts","../src/sequences-react/engine/commands.ts","../src/sequences-react/engine/playback.ts","../src/sequences-react/engine/snap.ts","../src/sequences-react/engine/zoom.ts","../src/sequences-react/media/frame-provider.ts","../src/sequences-react/components/composite-command.ts","../src/sequences-react/components/interaction-math.ts","../src/sequences-react/components/PreviewCanvas.tsx","../src/sequences-react/components/SnapIndicatorLine.tsx","../src/sequences-react/components/TimelinePlayhead.tsx","../src/sequences-react/components/TimelineRuler.tsx","../src/sequences-react/components/TimelineClipChip.tsx","../src/sequences-react/media/waveform.ts","../src/sequences-react/components/glyphs.tsx","../src/sequences-react/components/TimelineTrackRow.tsx","../src/sequences-react/components/ZoomControl.tsx"],"sourcesContent":["/**\n * The timeline editor surface: program monitor, transport row, and the\n * scrollable track area, composed over the engine (command stack, zoom math,\n * snapping, playback clock) and media (frame provider, waveforms) layers.\n *\n * State ownership:\n * - The COMMAND STACK owns the timeline. Every edit is a `TimelineCommand`\n * executed optimistically, then persisted via `onApplyOperations`; a\n * rejected persist rolls the command back locally WITHOUT emitting its\n * inverse (the server never saw it). A user-initiated undo/redo DOES emit\n * (the server applied the original), so the editor mirrors the executed\n * commands to know which operations an undo corresponds to — the engine\n * stack exposes no command identity from `undo()`.\n * - Clip-creating commands mint optimistic `local-…` ids. When\n * `onApplyOperations` resolves with `SequenceApplyResult[]`, the editor\n * records local→server aliases that every command resolves through at\n * execute/undo/emission time, so undoing a committed place/split/caption\n * works after a server refresh. Hosts resolving void skip reconciliation;\n * undo of creates then fails loud (error bar) once a refresh replaced the\n * local ids.\n *\n * Captions require an unlocked caption track: the editor creates clips, never\n * tracks. Products seed a caption track at sequence creation (or let the\n * agent's create_track tool add one); the caption button errors otherwise.\n * - The PLAYBACK CLOCK owns the playhead. Volatile view state (selection,\n * zoom, snap toggle) lives in React state; the contract's\n * `EditorTimelineState` view fields are initials, not a live channel.\n *\n * Compositing/stacking rule: `sortOrder` is paint order — later tracks paint\n * over earlier ones in the preview; rows render top→bottom in the same order.\n *\n * Asset drops: lanes accept `application/x-sequence-media` payloads\n * (JSON `{ url, kind, label?, durationSeconds?, generationId?, assetId? }`),\n * which is how a host's `renderAssetShelf` content places media — video/image\n * onto video tracks, audio onto audio tracks.\n *\n * Keyboard: space play/pause · delete/backspace removes the selection (one\n * undo step) · mod+z undo · shift+mod+z / mod+y redo · escape cancels an\n * in-flight drag (handled by the chips).\n */\n\nimport { useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react'\nimport type { DragEvent as ReactDragEvent } from 'react'\nimport {\n chooseCaptionPlacement,\n formatTimecode,\n secondsToFrames,\n trackIntervals,\n} from '../../sequences/model'\nimport type { SequenceApplyResult } from '../../sequences/apply'\nimport type { SequenceClip, SequenceMediaKind } from '../../sequences/model'\nimport type { SequenceOperation } from '../../sequences/operations'\nimport type { SnapPoint, TimelineCommand, TimelineEditorProps, VideoFrameProvider } from '../contracts'\nimport { createCommandStack } from '../engine/command-stack'\nimport {\n addCaptionCommand,\n deleteClipCommand,\n moveClipCommand,\n placeClipCommand,\n setClipTextCommand,\n splitClipCommand,\n trimClipCommand,\n} from '../engine/commands'\nimport { createPlaybackClock } from '../engine/playback'\nimport { applySnap, collectSnapPoints } from '../engine/snap'\nimport type { TimelineSnapPoint } from '../engine/snap'\nimport { createZoomMath } from '../engine/zoom'\nimport { createVideoElementFrameProvider } from '../media/frame-provider'\nimport { compositeCommand } from './composite-command'\nimport { chooseMoveSnap } from './interaction-math'\nimport { PreviewCanvas } from './PreviewCanvas'\nimport { SnapIndicatorLine } from './SnapIndicatorLine'\nimport type { ClipMoveCommit, ClipTrimCommit } from './TimelineClipChip'\nimport { TimelinePlayhead } from './TimelinePlayhead'\nimport { TimelineRuler } from './TimelineRuler'\nimport { TimelineTrackRow } from './TimelineTrackRow'\nimport { ZoomControl } from './ZoomControl'\nimport {\n CaptionPlusGlyph,\n MagnetGlyph,\n PauseGlyph,\n PlayGlyph,\n RedoGlyph,\n ScissorsGlyph,\n UndoGlyph,\n} from './glyphs'\n\nexport const SEQUENCE_MEDIA_DRAG_TYPE = 'application/x-sequence-media'\n\n/** Matches the engine's COMMAND_HISTORY_LIMIT so the operation mirror and the\n * stack's history can never disagree about what an undo refers to. */\nconst HISTORY_MIRROR_LIMIT = 200\n\nconst MIN_ZOOM = 0.005\nconst MAX_ZOOM = 24\n/** Tailwind w-36 on the track header column. */\nconst TRACK_HEADER_PX = 144\n\nconst TRANSPORT_BUTTON =\n 'flex h-7 w-7 items-center justify-center rounded border border-[var(--border-default)] text-[var(--text-secondary)] transition hover:text-[var(--text-primary)] disabled:cursor-default disabled:opacity-40 disabled:hover:text-[var(--text-secondary)]'\n\nfunction mintClipId(): string {\n const uuid = globalThis.crypto && 'randomUUID' in globalThis.crypto ? globalThis.crypto.randomUUID() : null\n return `local-${uuid ?? `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}`\n}\n\nfunction isTypingTarget(target: EventTarget | null): boolean {\n return target instanceof Element && target.closest('input, textarea, select, button, [contenteditable=\"true\"]') !== null\n}\n\ninterface MediaDragPayload {\n url: string\n kind: SequenceMediaKind\n label?: string\n durationSeconds?: number\n generationId?: string\n assetId?: string\n}\n\nfunction parseMediaDragPayload(raw: string): MediaDragPayload {\n const parsed = JSON.parse(raw) as Partial<MediaDragPayload>\n if (typeof parsed.url !== 'string' || parsed.url.length === 0) {\n throw new Error(`${SEQUENCE_MEDIA_DRAG_TYPE} payload requires a non-empty url`)\n }\n if (parsed.kind !== 'video' && parsed.kind !== 'image' && parsed.kind !== 'audio') {\n throw new Error(`${SEQUENCE_MEDIA_DRAG_TYPE} payload kind must be video | image | audio, got ${String(parsed.kind)}`)\n }\n return parsed as MediaDragPayload\n}\n\nexport function TimelineEditor(props: TimelineEditorProps) {\n const { canWrite, onApplyOperations } = props\n const fps = props.timeline.sequence.fps\n const durationFrames = props.timeline.sequence.durationFrames\n\n // --- engine lifecycles ----------------------------------------------------\n\n const stack = useMemo(() => createCommandStack(props.timeline), [])\n const editorState = useSyncExternalStore(stack.subscribe, stack.getState, stack.getState)\n const timeline = editorState.timeline\n\n const appliedTimelineRef = useRef(props.timeline)\n useEffect(() => {\n if (appliedTimelineRef.current === props.timeline) return\n appliedTimelineRef.current = props.timeline\n stack.reset(props.timeline)\n }, [props.timeline, stack])\n\n const clock = useMemo(\n () => createPlaybackClock({ fps, durationFrames: timeline.sequence.durationFrames }),\n [fps, timeline.sequence.durationFrames],\n )\n useEffect(() => () => clock.dispose(), [clock])\n\n const [playheadFrame, setPlayheadFrame] = useState(0)\n const [isPlaying, setIsPlaying] = useState(false)\n const onPlayheadChangeRef = useRef(props.onPlayheadChange)\n onPlayheadChangeRef.current = props.onPlayheadChange\n useEffect(() => {\n // A recreated clock (duration change) resumes from the prior playhead.\n clock.seek(Math.min(playheadFrame, timeline.sequence.durationFrames - 1))\n return clock.subscribe((frame) => {\n setPlayheadFrame(frame)\n setIsPlaying(clock.isPlaying())\n onPlayheadChangeRef.current?.(frame)\n })\n // playheadFrame is intentionally read once per clock identity.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [clock])\n\n const ownedProviderRef = useRef<VideoFrameProvider | null>(null)\n const frameProvider = useMemo(() => {\n if (props.frameProvider) return props.frameProvider\n if (!ownedProviderRef.current) ownedProviderRef.current = createVideoElementFrameProvider()\n return ownedProviderRef.current\n }, [props.frameProvider])\n useEffect(\n () => () => {\n ownedProviderRef.current?.dispose()\n ownedProviderRef.current = null\n },\n [],\n )\n\n // --- view state -------------------------------------------------------------\n\n const zoomMath = useMemo(() => createZoomMath({ minZoom: MIN_ZOOM, maxZoom: MAX_ZOOM }), [])\n const [zoom, setZoom] = useState(1)\n const trackViewportRef = useRef<HTMLDivElement | null>(null)\n useEffect(() => {\n // Initial zoom fits the whole sequence into the visible track viewport.\n const viewport = trackViewportRef.current\n if (!viewport) return\n const laneWidth = viewport.clientWidth - TRACK_HEADER_PX\n if (laneWidth <= 0) return\n setZoom(Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, laneWidth / durationFrames)))\n // Fit once on mount; afterwards zoom belongs to the user.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n const [snapEnabled, setSnapEnabled] = useState(true)\n const [activeSnapPoint, setActiveSnapPoint] = useState<SnapPoint | null>(null)\n const [selectedClipIds, setSelectedClipIds] = useState<string[]>([])\n const [commitError, setCommitError] = useState<string | null>(null)\n\n const selectedClips = useMemo(\n () => timeline.clips.filter((clip) => selectedClipIds.includes(clip.id)),\n [timeline, selectedClipIds],\n )\n const onSelectionChangeRef = useRef(props.onSelectionChange)\n onSelectionChangeRef.current = props.onSelectionChange\n useEffect(() => {\n onSelectionChangeRef.current?.(selectedClips)\n }, [selectedClips])\n\n const sortedTracks = useMemo(() => [...timeline.tracks].sort((a, b) => a.sortOrder - b.sortOrder), [timeline.tracks])\n const clipsByTrack = useMemo(() => {\n const byTrack = new Map<string, SequenceClip[]>()\n for (const clip of timeline.clips) {\n const bucket = byTrack.get(clip.trackId)\n if (bucket) bucket.push(clip)\n else byTrack.set(clip.trackId, [clip])\n }\n return byTrack\n }, [timeline.clips])\n\n // --- command commit + history mirror ---------------------------------------\n\n interface HistoryEntry {\n command: TimelineCommand\n /** Optimistic ids this command's clip-creating operations minted, in\n * operation order — the pairing key for id reconciliation. */\n createdLocalIds: string[]\n }\n\n const historyRef = useRef<{ done: HistoryEntry[]; undone: HistoryEntry[] }>({ done: [], undone: [] })\n\n /** Local→server clip-id aliases, fed by `onApplyOperations` results and\n * consulted live by every command (see engine/commands resolveClipId). */\n const clipIdAliasesRef = useRef(new Map<string, string>())\n\n function resolveClipId(clipId: string): string {\n return clipIdAliasesRef.current.get(clipId) ?? clipId\n }\n\n /** Pair each clip-creating operation with its apply result and record the\n * local→server alias. Results are index-aligned with operations (the\n * `applySequenceOperations` contract); a host returning a mismatched array\n * is a wiring bug surfaced loud, not silently skipped. */\n function reconcileCreatedClipIds(\n operations: SequenceOperation[],\n createdLocalIds: string[],\n results: SequenceApplyResult[] | void,\n ) {\n if (createdLocalIds.length === 0 || !Array.isArray(results)) return\n if (results.length !== operations.length) {\n setCommitError(\n `onApplyOperations returned ${results.length} results for ${operations.length} operations — clip-id reconciliation skipped`,\n )\n return\n }\n let cursor = 0\n operations.forEach((operation, index) => {\n if (operation.type !== 'place_clip' && operation.type !== 'add_caption' && operation.type !== 'split_clip') return\n const localId = createdLocalIds[cursor]\n cursor += 1\n const result = results[index]\n if (localId !== undefined && result !== undefined && result.kind === 'clip') {\n clipIdAliasesRef.current.set(localId, result.clip.id)\n }\n })\n }\n\n function commitCommand(command: TimelineCommand, createdLocalIds: string[] = []) {\n if (!canWrite) return\n try {\n stack.execute(command)\n } catch (error) {\n setCommitError(error instanceof Error ? error.message : String(error))\n return\n }\n const history = historyRef.current\n const entry: HistoryEntry = { command, createdLocalIds }\n history.done.push(entry)\n if (history.done.length > HISTORY_MIRROR_LIMIT) history.done.splice(0, history.done.length - HISTORY_MIRROR_LIMIT)\n history.undone = []\n setCommitError(null)\n const operations = command.operations()\n void onApplyOperations(operations)\n .then((results) => reconcileCreatedClipIds(operations, createdLocalIds, results))\n .catch((error: unknown) => {\n // Roll back ONLY when this command is still the newest local edit; if\n // the user already undid or stacked more edits, local rollback would\n // corrupt history — surface the error and let the next server refresh\n // (stack.reset) reconcile.\n const mirror = historyRef.current\n if (mirror.done[mirror.done.length - 1] === entry && stack.canUndo()) {\n stack.undo()\n mirror.done.pop()\n }\n setCommitError(error instanceof Error ? error.message : String(error))\n })\n }\n\n function undoLast() {\n const history = historyRef.current\n const entry = history.done[history.done.length - 1]\n if (!entry || !stack.canUndo()) return\n try {\n stack.undo()\n } catch (error) {\n // A reset() removed the command's target clip; the stack kept the entry\n // (it may succeed after the next refresh), so the mirror stays too.\n setCommitError(`Undo failed: ${error instanceof Error ? error.message : String(error)}`)\n return\n }\n history.done.pop()\n history.undone.push(entry)\n void onApplyOperations(entry.command.inverseOperations()).catch((error: unknown) => {\n setCommitError(error instanceof Error ? error.message : String(error))\n })\n }\n\n function redoLast() {\n const history = historyRef.current\n const entry = history.undone[history.undone.length - 1]\n if (!entry || !stack.canRedo()) return\n try {\n stack.redo()\n } catch (error) {\n setCommitError(`Redo failed: ${error instanceof Error ? error.message : String(error)}`)\n return\n }\n history.undone.pop()\n history.done.push(entry)\n const operations = entry.command.operations()\n // A redo of a clip-creating command mints fresh server ids — re-pair them\n // so a later undo deletes the recreated clips, not the stale ones.\n void onApplyOperations(operations)\n .then((results) => reconcileCreatedClipIds(operations, entry.createdLocalIds, results))\n .catch((error: unknown) => {\n setCommitError(error instanceof Error ? error.message : String(error))\n })\n }\n\n // --- edit handlers ----------------------------------------------------------\n\n function handleCommitMove(input: ClipMoveCommit) {\n const current = stack.getState().timeline\n const clip = current.clips.find((candidate) => candidate.id === input.clipId)\n if (!clip) return\n commitCommand(\n moveClipCommand({\n timeline: current,\n clipId: input.clipId,\n startFrame: input.startFrame,\n ...(input.trackId !== clip.trackId ? { trackId: input.trackId } : {}),\n resolveClipId,\n }),\n )\n }\n\n function handleCommitTrim(input: ClipTrimCommit) {\n commitCommand(\n trimClipCommand({\n timeline: stack.getState().timeline,\n clipId: input.clipId,\n startFrame: input.startFrame,\n durationFrames: input.durationFrames,\n sourceInFrame: input.sourceInFrame,\n resolveClipId,\n }),\n )\n }\n\n function handleCommitText(input: { clipId: string; text: string }) {\n commitCommand(\n setClipTextCommand({ timeline: stack.getState().timeline, clipId: input.clipId, text: input.text, resolveClipId }),\n )\n }\n\n function deleteSelection() {\n const current = stack.getState().timeline\n const lockedTrackIds = new Set(current.tracks.filter((track) => track.locked).map((track) => track.id))\n const targets = selectedClips.filter((clip) => !lockedTrackIds.has(clip.trackId))\n if (targets.length === 0) return\n const commands = targets.map((clip) => deleteClipCommand({ timeline: current, clipId: clip.id, resolveClipId }))\n commitCommand(commands.length === 1 ? (commands[0] as TimelineCommand) : compositeCommand(`Delete ${commands.length} clips`, commands))\n setSelectedClipIds([])\n }\n\n const splittableClip =\n selectedClips.length === 1 &&\n selectedClips[0] &&\n playheadFrame > selectedClips[0].startFrame &&\n playheadFrame < selectedClips[0].startFrame + selectedClips[0].durationFrames\n ? selectedClips[0]\n : null\n\n function splitAtPlayhead() {\n if (!splittableClip) return\n const newClipId = mintClipId()\n commitCommand(\n splitClipCommand({\n timeline: stack.getState().timeline,\n clipId: splittableClip.id,\n atFrame: playheadFrame,\n newClipId,\n resolveClipId,\n }),\n [newClipId],\n )\n }\n\n function addCaptionAtPlayhead() {\n const current = stack.getState().timeline\n const captionTrack = [...current.tracks]\n .sort((a, b) => a.sortOrder - b.sortOrder)\n .find((track) => track.kind === 'caption' && !track.locked)\n if (!captionTrack) {\n setCommitError('No unlocked caption track in this sequence — add one before inserting captions.')\n return\n }\n // Throws when the caption track has no free gap left near the playhead.\n let placement: { startFrame: number; durationFrames: number }\n try {\n placement = chooseCaptionPlacement({\n playheadFrame,\n fps,\n sequenceDurationFrames: current.sequence.durationFrames,\n occupiedIntervals: trackIntervals(current, captionTrack.id),\n })\n } catch (error) {\n setCommitError(error instanceof Error ? error.message : String(error))\n return\n }\n const clipId = mintClipId()\n commitCommand(\n addCaptionCommand({\n timeline: current,\n clipId,\n trackId: captionTrack.id,\n text: 'New caption',\n startFrame: placement.startFrame,\n durationFrames: placement.durationFrames,\n resolveClipId,\n }),\n [clipId],\n )\n }\n\n function selectClip(clipId: string, additive: boolean) {\n setSelectedClipIds((current) => {\n if (!additive) return [clipId]\n return current.includes(clipId) ? current.filter((id) => id !== clipId) : [...current, clipId]\n })\n }\n\n // --- snapping ---------------------------------------------------------------\n\n function snapMove(candidate: { startFrame: number; durationFrames: number; clipId: string }) {\n if (!snapEnabled) return { startFrame: candidate.startFrame, point: null }\n const points = collectSnapPoints(timeline, playheadFrame)\n const exclude = (point: SnapPoint) => (point as TimelineSnapPoint).clipId === candidate.clipId\n return chooseMoveSnap({\n candidateStartFrame: candidate.startFrame,\n durationFrames: candidate.durationFrames,\n startSnap: applySnap(candidate.startFrame, points, { zoom, exclude }),\n endSnap: applySnap(candidate.startFrame + candidate.durationFrames, points, { zoom, exclude }),\n })\n }\n\n function snapEdge(candidate: { frame: number; clipId: string }) {\n if (!snapEnabled) return { frame: candidate.frame, point: null }\n const points = collectSnapPoints(timeline, playheadFrame)\n const result = applySnap(candidate.frame, points, {\n zoom,\n exclude: (point: SnapPoint) => (point as TimelineSnapPoint).clipId === candidate.clipId,\n })\n return { frame: result.frame, point: result.point }\n }\n\n // --- transport --------------------------------------------------------------\n\n function togglePlayback() {\n if (clock.isPlaying()) clock.pause()\n else clock.play()\n setIsPlaying(clock.isPlaying())\n }\n\n useEffect(() => {\n function onKeyDown(event: KeyboardEvent) {\n if (event.code === 'Space' && !isTypingTarget(event.target)) {\n event.preventDefault()\n togglePlayback()\n return\n }\n if ((event.key === 'Delete' || event.key === 'Backspace') && !isTypingTarget(event.target)) {\n if (!canWrite) return\n event.preventDefault()\n deleteSelection()\n return\n }\n const mod = event.metaKey || event.ctrlKey\n if (!mod || isTypingTarget(event.target)) return\n if (event.key.toLowerCase() === 'z') {\n event.preventDefault()\n if (event.shiftKey) redoLast()\n else undoLast()\n } else if (event.key.toLowerCase() === 'y') {\n event.preventDefault()\n redoLast()\n }\n }\n window.addEventListener('keydown', onKeyDown)\n return () => window.removeEventListener('keydown', onKeyDown)\n })\n\n // --- asset drops ------------------------------------------------------------\n\n function handleTrackAreaDragOver(event: ReactDragEvent<HTMLDivElement>) {\n if (!canWrite || !event.dataTransfer.types.includes(SEQUENCE_MEDIA_DRAG_TYPE)) return\n event.preventDefault()\n event.dataTransfer.dropEffect = 'copy'\n }\n\n function handleTrackAreaDrop(event: ReactDragEvent<HTMLDivElement>) {\n if (!canWrite || !event.dataTransfer.types.includes(SEQUENCE_MEDIA_DRAG_TYPE)) return\n event.preventDefault()\n const lane = event.target instanceof Element ? event.target.closest<HTMLElement>('[data-lane-track]') : null\n if (!lane || !lane.dataset.laneTrack) {\n setCommitError('Drop media on a track lane to place it.')\n return\n }\n let payload: MediaDragPayload\n try {\n payload = parseMediaDragPayload(event.dataTransfer.getData(SEQUENCE_MEDIA_DRAG_TYPE))\n } catch (error) {\n setCommitError(error instanceof Error ? error.message : String(error))\n return\n }\n const laneKind = lane.dataset.laneKind\n const accepts = laneKind === 'video' ? payload.kind === 'video' || payload.kind === 'image' : laneKind === 'audio' ? payload.kind === 'audio' : false\n if (!accepts || lane.dataset.laneLocked === 'true') {\n setCommitError(`A ${laneKind ?? 'unknown'} track cannot take ${payload.kind} media${lane.dataset.laneLocked === 'true' ? ' (track is locked)' : ''}.`)\n return\n }\n const current = stack.getState().timeline\n const rect = lane.getBoundingClientRect()\n const dropFrame = Math.max(0, Math.round((event.clientX - rect.left) / zoom))\n const naturalDuration = payload.durationSeconds !== undefined\n ? secondsToFrames(payload.durationSeconds, fps)\n : payload.kind === 'image'\n ? fps * 3\n : fps * 5\n const startFrame = Math.min(dropFrame, current.sequence.durationFrames - 1)\n const placedDuration = Math.max(1, Math.min(naturalDuration, current.sequence.durationFrames - startFrame))\n const clipId = mintClipId()\n commitCommand(\n placeClipCommand({\n timeline: current,\n clipId,\n trackId: lane.dataset.laneTrack,\n label: payload.label ?? payload.url.split('/').pop() ?? payload.url,\n startFrame,\n durationFrames: placedDuration,\n media: { url: payload.url, kind: payload.kind },\n ...(payload.generationId !== undefined ? { generationId: payload.generationId } : {}),\n ...(payload.assetId !== undefined ? { assetId: payload.assetId } : {}),\n resolveClipId,\n }),\n [clipId],\n )\n }\n\n // --- render -------------------------------------------------------------------\n\n const timelineWidth = timeline.sequence.durationFrames * zoom\n\n return (\n <div className={`flex h-full min-h-0 flex-col bg-[var(--bg-input)] text-[var(--text-primary)] ${props.className ?? ''}`}>\n <div className=\"flex min-h-0 flex-1\">\n <div className=\"flex min-w-0 flex-1 flex-col\">\n <PreviewCanvas timeline={timeline} clock={clock} frameProvider={frameProvider} />\n\n <div className=\"flex h-10 shrink-0 items-center gap-2 border-y border-[var(--border-default)] px-2\">\n <button\n type=\"button\"\n aria-label={isPlaying ? 'Pause' : 'Play'}\n onClick={togglePlayback}\n className={TRANSPORT_BUTTON}\n >\n {isPlaying ? <PauseGlyph className=\"h-3.5 w-3.5\" /> : <PlayGlyph className=\"h-3.5 w-3.5\" />}\n </button>\n <span className=\"font-mono text-xs tabular-nums text-[var(--text-secondary)]\">\n {formatTimecode(playheadFrame, fps)}\n <span className=\"text-[var(--text-muted)]\"> / {formatTimecode(timeline.sequence.durationFrames, fps)}</span>\n </span>\n\n <div className=\"mx-1 h-4 w-px bg-[var(--border-default)]\" />\n\n <button type=\"button\" aria-label=\"Undo\" disabled={!stack.canUndo() || !canWrite} onClick={undoLast} className={TRANSPORT_BUTTON}>\n <UndoGlyph className=\"h-3.5 w-3.5\" />\n </button>\n <button type=\"button\" aria-label=\"Redo\" disabled={!stack.canRedo() || !canWrite} onClick={redoLast} className={TRANSPORT_BUTTON}>\n <RedoGlyph className=\"h-3.5 w-3.5\" />\n </button>\n <button\n type=\"button\"\n aria-label=\"Toggle snapping\"\n aria-pressed={snapEnabled}\n onClick={() => setSnapEnabled((current) => !current)}\n className={`${TRANSPORT_BUTTON} ${snapEnabled ? 'border-[var(--brand-primary)] text-[var(--brand-primary)] hover:text-[var(--brand-primary)]' : ''}`}\n >\n <MagnetGlyph className=\"h-3.5 w-3.5\" />\n </button>\n\n {canWrite ? (\n <>\n <button type=\"button\" aria-label=\"Split clip at playhead\" disabled={!splittableClip} onClick={splitAtPlayhead} className={TRANSPORT_BUTTON}>\n <ScissorsGlyph className=\"h-3.5 w-3.5\" />\n </button>\n <button type=\"button\" aria-label=\"Add caption at playhead\" onClick={addCaptionAtPlayhead} className={TRANSPORT_BUTTON}>\n <CaptionPlusGlyph className=\"h-3.5 w-3.5\" />\n </button>\n </>\n ) : null}\n\n <div className=\"flex-1\" />\n <ZoomControl zoomMath={zoomMath} zoom={zoom} onZoomChange={setZoom} />\n </div>\n\n {commitError ? (\n <div className=\"flex shrink-0 items-center justify-between gap-3 border-b border-rose-500/30 bg-rose-500/10 px-3 py-1.5 text-xs text-rose-300\" role=\"alert\">\n <span className=\"min-w-0 truncate\">{commitError}</span>\n <button type=\"button\" onClick={() => setCommitError(null)} className=\"shrink-0 underline-offset-2 hover:underline\">\n Dismiss\n </button>\n </div>\n ) : null}\n\n {props.renderAssetShelf ? (\n <div className=\"shrink-0 border-b border-[var(--border-default)]\">{props.renderAssetShelf()}</div>\n ) : null}\n\n <div\n ref={trackViewportRef}\n data-timeline-tracks\n className=\"relative max-h-60 min-h-[6rem] shrink-0 overflow-auto overscroll-x-contain\"\n onDragOver={handleTrackAreaDragOver}\n onDrop={handleTrackAreaDrop}\n >\n <div className=\"relative\" style={{ width: `${TRACK_HEADER_PX + timelineWidth}px`, minWidth: '100%' }}>\n <div className=\"sticky top-0 z-20 flex\">\n <div className=\"sticky left-0 z-30 w-36 shrink-0 border-b border-r border-[var(--border-default)] bg-[var(--bg-input)]\" />\n <TimelineRuler fps={fps} durationFrames={timeline.sequence.durationFrames} zoom={zoom} onScrub={(frame) => clock.seek(frame)} />\n </div>\n\n <div className=\"relative\">\n {sortedTracks.map((track) => (\n <TimelineTrackRow\n key={track.id}\n track={track}\n clips={clipsByTrack.get(track.id) ?? []}\n fps={fps}\n zoom={zoom}\n sequenceDurationFrames={timeline.sequence.durationFrames}\n selectedClipIds={new Set(selectedClipIds)}\n canWrite={canWrite}\n frameProvider={frameProvider}\n snapMove={snapMove}\n snapEdge={snapEdge}\n onSnapPointChange={setActiveSnapPoint}\n onSelectClip={selectClip}\n onCommitMove={handleCommitMove}\n onCommitTrim={handleCommitTrim}\n onCommitText={handleCommitText}\n onLaneSeek={(frame) => clock.seek(frame)}\n />\n ))}\n <div className=\"pointer-events-none absolute inset-y-0\" style={{ left: `${TRACK_HEADER_PX}px`, width: `${timelineWidth}px` }}>\n <TimelinePlayhead frame={playheadFrame} zoom={zoom} />\n <SnapIndicatorLine point={activeSnapPoint} zoom={zoom} />\n </div>\n </div>\n </div>\n </div>\n </div>\n\n {props.renderSidePanel ? (\n <aside className=\"flex w-80 shrink-0 flex-col overflow-hidden border-l border-[var(--border-default)]\">\n {props.renderSidePanel({ selectedClips, playheadFrame })}\n </aside>\n ) : null}\n </div>\n </div>\n )\n}\n\nexport default TimelineEditor\n","/**\n * Undo/redo command stack over immutable `EditorTimelineState`.\n *\n * Invariant that makes `reset()` safe: history entries hold COMMANDS — each a\n * pair of state transforms plus the durable operations captured at\n * construction — never state snapshots. Rebasing the timeline from a server\n * refresh therefore cannot stale the history: a later undo re-applies the\n * command's inverse transform to whatever timeline is current. If a rebase\n * removed a clip a historical command targets, that command's transform throws\n * (fail loud) rather than silently editing the wrong clip — the host decides\n * whether to drop history at that point.\n */\n\nimport type { SequenceTimeline } from '../../sequences/model'\nimport type { CommandStack, EditorTimelineState, TimelineCommand } from '../contracts'\n\n/** Oldest entries are dropped past this bound; redo is cleared on execute. */\nexport const COMMAND_HISTORY_LIMIT = 200\n\nexport function createCommandStack(initial: SequenceTimeline): CommandStack {\n /** View fields start at their neutral values; the host layer owns volatile\n * view state and treats these as initials, not a live channel. */\n let state: EditorTimelineState = {\n timeline: initial,\n playheadFrame: 0,\n selectedClipIds: [],\n zoom: 1,\n scrollLeft: 0,\n }\n const undoStack: TimelineCommand[] = []\n const redoStack: TimelineCommand[] = []\n const listeners = new Set<() => void>()\n\n const notify = (): void => {\n for (const listener of [...listeners]) listener()\n }\n\n return {\n execute(command: TimelineCommand): void {\n state = command.execute(state)\n undoStack.push(command)\n if (undoStack.length > COMMAND_HISTORY_LIMIT) {\n undoStack.splice(0, undoStack.length - COMMAND_HISTORY_LIMIT)\n }\n redoStack.length = 0\n notify()\n },\n\n // Both transforms run BEFORE the stacks move: a throwing transform (the\n // documented missing-clip path after reset()) leaves history and state\n // exactly as they were, so the entry is never silently destroyed and the\n // caller can retry after the next refresh restores the target.\n undo(): void {\n const command = undoStack[undoStack.length - 1]\n if (!command) throw new Error('nothing to undo — guard with canUndo() before calling undo()')\n state = command.undo(state)\n undoStack.pop()\n redoStack.push(command)\n notify()\n },\n\n redo(): void {\n const command = redoStack[redoStack.length - 1]\n if (!command) throw new Error('nothing to redo — guard with canRedo() before calling redo()')\n state = command.execute(state)\n redoStack.pop()\n undoStack.push(command)\n notify()\n },\n\n canUndo(): boolean {\n return undoStack.length > 0\n },\n\n canRedo(): boolean {\n return redoStack.length > 0\n },\n\n subscribe(listener: () => void): () => void {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n },\n\n getState(): EditorTimelineState {\n return state\n },\n\n /** Rebase onto a server-refreshed timeline. History survives (see module\n * header); selection drops ids the refresh removed and the playhead\n * clamps into the new duration so view state never dangles. */\n reset(timeline: SequenceTimeline): void {\n const liveClipIds = new Set(timeline.clips.map((clip) => clip.id))\n state = {\n ...state,\n timeline,\n playheadFrame: Math.max(0, Math.min(state.playheadFrame, timeline.sequence.durationFrames - 1)),\n selectedClipIds: state.selectedClipIds.filter((id) => liveClipIds.has(id)),\n }\n notify()\n },\n }\n}\n","/**\n * Concrete `TimelineCommand` factories. Every factory captures the inverse\n * from PRE-state at construction — undo is a value computed once, never a\n * re-derivation from whatever state exists later. Local execute/undo update\n * `EditorTimelineState` immutably; `operations()`/`inverseOperations()` return\n * the durable `SequenceOperation[]` equivalent, built per call from captured\n * primitives so callers can never alias command internals.\n *\n * Id boundary: `place_clip`, `add_caption`, and `split_clip` mint clip ids\n * server-side, so factories that create local clips take a caller-minted id.\n * Every factory also takes an optional `resolveClipId` — a LIVE local→server\n * alias lookup the host feeds from `onApplyOperations` results. Resolution\n * runs at execute/undo/emission time (never at construction), so a command\n * captured against an optimistic `local-…` id keeps working after a server\n * refresh replaced it with the minted id. Residual boundary: clips a durable\n * undo recreates (e.g. the `place_clip` inverse of a delete) mint FRESH\n * server ids that only the next refresh reconciles.\n *\n * Transforms re-resolve their target clip at execute/undo time and throw when\n * it no longer exists (e.g. removed by a `reset()` rebase) — editing a wrong\n * or absent clip silently would corrupt the durable op stream.\n */\n\nimport {\n assertClipFitsSequence,\n clampClipStart,\n type SequenceClip,\n type SequenceTimeline,\n type SequenceTrack,\n} from '../../sequences/model'\nimport type { EditorTimelineState, TimelineCommand } from '../contracts'\n\n// ---------------------------------------------------------------------------\n// Shared lookup + immutable-update helpers\n// ---------------------------------------------------------------------------\n\n/** Live local→server clip-id lookup (see module header). Must return its\n * argument when no alias exists. */\nexport type ClipIdResolver = (clipId: string) => string\n\nconst identityClipId: ClipIdResolver = (clipId) => clipId\n\nfunction requireClip(timeline: SequenceTimeline, clipId: string, context: string): SequenceClip {\n const clip = timeline.clips.find((candidate) => candidate.id === clipId)\n if (!clip) throw new Error(`${context}: clip ${clipId} does not exist in sequence ${timeline.sequence.id}`)\n return clip\n}\n\nfunction requireUnlockedTrack(timeline: SequenceTimeline, trackId: string, context: string): SequenceTrack {\n const track = timeline.tracks.find((candidate) => candidate.id === trackId)\n if (!track) throw new Error(`${context}: track ${trackId} does not exist in sequence ${timeline.sequence.id}`)\n if (track.locked) throw new Error(`${context}: track ${track.name} (${trackId}) is locked`)\n return track\n}\n\nfunction assertNewClipId(timeline: SequenceTimeline, clipId: string, context: string): void {\n if (timeline.clips.some((candidate) => candidate.id === clipId)) {\n throw new Error(`${context}: clip id ${clipId} already exists in sequence ${timeline.sequence.id}`)\n }\n}\n\nfunction patchClip(\n state: EditorTimelineState,\n clipId: string,\n context: string,\n patch: Partial<SequenceClip>,\n): EditorTimelineState {\n requireClip(state.timeline, clipId, context)\n return {\n ...state,\n timeline: {\n ...state.timeline,\n clips: state.timeline.clips.map((clip) => (clip.id === clipId ? { ...clip, ...patch } : clip)),\n },\n }\n}\n\nfunction insertClip(state: EditorTimelineState, clip: SequenceClip, context: string): EditorTimelineState {\n assertNewClipId(state.timeline, clip.id, context)\n return {\n ...state,\n timeline: { ...state.timeline, clips: [...state.timeline.clips, clip] },\n }\n}\n\nfunction removeClip(state: EditorTimelineState, clipId: string, context: string): EditorTimelineState {\n requireClip(state.timeline, clipId, context)\n return {\n ...state,\n timeline: {\n ...state.timeline,\n clips: state.timeline.clips.filter((clip) => clip.id !== clipId),\n },\n selectedClipIds: state.selectedClipIds.filter((id) => id !== clipId),\n }\n}\n\n// ---------------------------------------------------------------------------\n// move_clip\n// ---------------------------------------------------------------------------\n\nexport interface MoveClipInput {\n timeline: SequenceTimeline\n clipId: string\n startFrame: number\n /** Omitted → stays on its current track. */\n trackId?: string\n resolveClipId?: ClipIdResolver\n}\n\n/** Drag-move. The target start clamps through the model's `clampClipStart`\n * so drags past either edge land at the boundary instead of throwing\n * mid-gesture; the emitted operation carries the clamped value. */\nexport function moveClipCommand(input: MoveClipInput): TimelineCommand {\n const context = 'move_clip'\n const clip = requireClip(input.timeline, input.clipId, context)\n const targetTrackId = input.trackId ?? clip.trackId\n requireUnlockedTrack(input.timeline, targetTrackId, context)\n if (!Number.isInteger(input.startFrame)) throw new Error(`${context}: startFrame must be an integer frame`)\n const targetStart = clampClipStart({\n startFrame: input.startFrame,\n durationFrames: clip.durationFrames,\n sequenceDurationFrames: input.timeline.sequence.durationFrames,\n })\n const originalStart = clip.startFrame\n const originalTrackId = clip.trackId\n const trackChanged = targetTrackId !== originalTrackId\n const clipId = input.clipId\n const resolve = input.resolveClipId ?? identityClipId\n\n return {\n label: `Move ${clip.label}`,\n execute: (state) => patchClip(state, resolve(clipId), context, { startFrame: targetStart, trackId: targetTrackId }),\n undo: (state) => patchClip(state, resolve(clipId), context, { startFrame: originalStart, trackId: originalTrackId }),\n operations: () => [\n { type: 'move_clip', clipId: resolve(clipId), startFrame: targetStart, ...(trackChanged ? { trackId: targetTrackId } : {}) },\n ],\n inverseOperations: () => [\n { type: 'move_clip', clipId: resolve(clipId), startFrame: originalStart, ...(trackChanged ? { trackId: originalTrackId } : {}) },\n ],\n }\n}\n\n// ---------------------------------------------------------------------------\n// trim_clip\n// ---------------------------------------------------------------------------\n\nexport interface TrimClipInput {\n timeline: SequenceTimeline\n clipId: string\n startFrame: number\n durationFrames: number\n /** New source in-point when trimming the head; omitted → unchanged. */\n sourceInFrame?: number\n resolveClipId?: ClipIdResolver\n}\n\n/** Trim is strict where move is forgiving: the caller (a trim handle) already\n * knows both edges, so out-of-bounds input is a bug, not a gesture. */\nexport function trimClipCommand(input: TrimClipInput): TimelineCommand {\n const context = 'trim_clip'\n const clip = requireClip(input.timeline, input.clipId, context)\n assertClipFitsSequence({\n startFrame: input.startFrame,\n durationFrames: input.durationFrames,\n sequenceDurationFrames: input.timeline.sequence.durationFrames,\n label: `${context} ${clip.label}`,\n })\n if (input.sourceInFrame !== undefined && (!Number.isInteger(input.sourceInFrame) || input.sourceInFrame < 0)) {\n throw new Error(`${context}: sourceInFrame must be a non-negative integer`)\n }\n const targetSourceIn = input.sourceInFrame ?? clip.sourceInFrame\n // Mirror of the server-side source-window invariant: a clip with an explicit\n // out-point (a split half) cannot claim more source frames than the window\n // holds — failing here keeps optimistic state from diverging into a commit\n // the validator rejects.\n if (clip.sourceOutFrame !== null && targetSourceIn + input.durationFrames > clip.sourceOutFrame) {\n throw new Error(\n `${context}: needs ${input.durationFrames} source frames from ${targetSourceIn} but ${clip.label} has source window [${clip.sourceInFrame}, ${clip.sourceOutFrame})`,\n )\n }\n const target = { startFrame: input.startFrame, durationFrames: input.durationFrames, sourceInFrame: targetSourceIn }\n const original = { startFrame: clip.startFrame, durationFrames: clip.durationFrames, sourceInFrame: clip.sourceInFrame }\n const clipId = input.clipId\n const resolve = input.resolveClipId ?? identityClipId\n\n return {\n label: `Trim ${clip.label}`,\n execute: (state) => patchClip(state, resolve(clipId), context, target),\n undo: (state) => patchClip(state, resolve(clipId), context, original),\n operations: () => [{ type: 'trim_clip', clipId: resolve(clipId), ...target }],\n inverseOperations: () => [{ type: 'trim_clip', clipId: resolve(clipId), ...original }],\n }\n}\n\n// ---------------------------------------------------------------------------\n// place_clip\n// ---------------------------------------------------------------------------\n\nexport interface PlaceClipInput {\n timeline: SequenceTimeline\n /** Caller-minted optimistic id for the local clip (see module header). */\n clipId: string\n trackId: string\n label: string\n startFrame: number\n durationFrames: number\n /** Omitted → 0 (start of the source). */\n sourceInFrame?: number\n media?: { url: string; kind: 'video' | 'image' | 'audio' }\n generationId?: string\n assetId?: string\n metadata?: Record<string, unknown>\n resolveClipId?: ClipIdResolver\n}\n\nexport function placeClipCommand(input: PlaceClipInput): TimelineCommand {\n const context = 'place_clip'\n assertNewClipId(input.timeline, input.clipId, context)\n requireUnlockedTrack(input.timeline, input.trackId, context)\n assertClipFitsSequence({\n startFrame: input.startFrame,\n durationFrames: input.durationFrames,\n sequenceDurationFrames: input.timeline.sequence.durationFrames,\n label: `${context} ${input.label}`,\n })\n const sourceInFrame = input.sourceInFrame ?? 0\n if (!Number.isInteger(sourceInFrame) || sourceInFrame < 0) {\n throw new Error(`${context}: sourceInFrame must be a non-negative integer`)\n }\n const clip: SequenceClip = {\n id: input.clipId,\n trackId: input.trackId,\n label: input.label,\n startFrame: input.startFrame,\n durationFrames: input.durationFrames,\n sourceInFrame,\n sourceOutFrame: null,\n disabled: false,\n ...(input.media ? { media: { url: input.media.url, kind: input.media.kind } } : {}),\n ...(input.generationId !== undefined ? { generationId: input.generationId } : {}),\n ...(input.assetId !== undefined ? { assetId: input.assetId } : {}),\n metadata: input.metadata ?? {},\n }\n const clipId = input.clipId\n const resolve = input.resolveClipId ?? identityClipId\n\n return {\n label: `Place ${input.label}`,\n execute: (state) => insertClip(state, { ...structuredClone(clip), id: resolve(clipId) }, context),\n undo: (state) => removeClip(state, resolve(clipId), context),\n operations: () => [\n {\n type: 'place_clip',\n trackId: clip.trackId,\n label: clip.label,\n startFrame: clip.startFrame,\n durationFrames: clip.durationFrames,\n sourceInFrame,\n ...(input.media ? { media: { url: input.media.url, kind: input.media.kind } } : {}),\n ...(input.generationId !== undefined ? { generationId: input.generationId } : {}),\n ...(input.assetId !== undefined ? { assetId: input.assetId } : {}),\n ...(input.metadata !== undefined ? { metadata: input.metadata } : {}),\n },\n ],\n inverseOperations: () => [{ type: 'delete_clip', clipId: resolve(clipId) }],\n }\n}\n\n// ---------------------------------------------------------------------------\n// delete_clip\n// ---------------------------------------------------------------------------\n\nexport interface DeleteClipInput {\n timeline: SequenceTimeline\n clipId: string\n resolveClipId?: ClipIdResolver\n}\n\n/** Snapshots the full clip at construction so undo restores it exactly. The\n * durable inverse rebuilds within the closed operation union: caption clips\n * (caption track + text) invert through `add_caption` — `place_clip` cannot\n * carry text/language — everything else through `place_clip` with media,\n * product references, source window, and disabled state intact. Boundary:\n * the caption path cannot carry `disabled` or `metadata`, so a deleted\n * disabled caption resurrects ENABLED server-side and caption metadata is\n * lost — local undo stays exact (same class of loss as the `set_clip_text`\n * language-clear boundary). */\nexport function deleteClipCommand(input: DeleteClipInput): TimelineCommand {\n const context = 'delete_clip'\n const snapshot = structuredClone(requireClip(input.timeline, input.clipId, context))\n const track = input.timeline.tracks.find((candidate) => candidate.id === snapshot.trackId)\n if (!track) throw new Error(`${context}: clip ${snapshot.id} references unknown track ${snapshot.trackId}`)\n const captionText =\n track.kind === 'caption' && typeof snapshot.text === 'string' && snapshot.text.length > 0 ? snapshot.text : null\n const clipId = input.clipId\n const resolve = input.resolveClipId ?? identityClipId\n\n return {\n label: `Delete ${snapshot.label}`,\n execute: (state) => removeClip(state, resolve(clipId), context),\n undo: (state) => insertClip(state, { ...structuredClone(snapshot), id: resolve(clipId) }, context),\n operations: () => [{ type: 'delete_clip', clipId: resolve(clipId) }],\n inverseOperations: () =>\n captionText !== null\n ? [\n {\n type: 'add_caption',\n text: captionText,\n ...(snapshot.language !== undefined ? { language: snapshot.language } : {}),\n startFrame: snapshot.startFrame,\n durationFrames: snapshot.durationFrames,\n trackId: snapshot.trackId,\n },\n ]\n : [\n {\n type: 'place_clip',\n trackId: snapshot.trackId,\n label: snapshot.label,\n startFrame: snapshot.startFrame,\n durationFrames: snapshot.durationFrames,\n sourceInFrame: snapshot.sourceInFrame,\n ...(snapshot.sourceOutFrame !== null ? { sourceOutFrame: snapshot.sourceOutFrame } : {}),\n ...(snapshot.disabled ? { disabled: true } : {}),\n ...(snapshot.media ? { media: { url: snapshot.media.url, kind: snapshot.media.kind } } : {}),\n ...(snapshot.generationId !== undefined ? { generationId: snapshot.generationId } : {}),\n ...(snapshot.assetId !== undefined ? { assetId: snapshot.assetId } : {}),\n metadata: structuredClone(snapshot.metadata),\n },\n ],\n }\n}\n\n// ---------------------------------------------------------------------------\n// split_clip\n// ---------------------------------------------------------------------------\n\nexport interface SplitClipInput {\n timeline: SequenceTimeline\n clipId: string\n /** Sequence-frame to cut at; must fall strictly inside the clip. */\n atFrame: number\n /** Caller-minted id for the second (tail) clip. */\n newClipId: string\n resolveClipId?: ClipIdResolver\n}\n\n/** Source mapping is 1:1 frames (no rate ramps in the model), so the tail's\n * source in-point is the head's in-point advanced by the head duration. */\nexport function splitClipCommand(input: SplitClipInput): TimelineCommand {\n const context = 'split_clip'\n const original = structuredClone(requireClip(input.timeline, input.clipId, context))\n assertNewClipId(input.timeline, input.newClipId, context)\n if (!Number.isInteger(input.atFrame)) throw new Error(`${context}: atFrame must be an integer frame`)\n const clipEnd = original.startFrame + original.durationFrames\n if (input.atFrame <= original.startFrame || input.atFrame >= clipEnd) {\n throw new Error(\n `${context}: atFrame ${input.atFrame} must fall strictly inside clip ${original.id} [${original.startFrame}, ${clipEnd})`,\n )\n }\n const headDurationFrames = input.atFrame - original.startFrame\n const tailDurationFrames = clipEnd - input.atFrame\n const tail: SequenceClip = {\n ...structuredClone(original),\n id: input.newClipId,\n startFrame: input.atFrame,\n durationFrames: tailDurationFrames,\n sourceInFrame: original.sourceInFrame + headDurationFrames,\n }\n const clipId = input.clipId\n const newClipId = input.newClipId\n const resolve = input.resolveClipId ?? identityClipId\n // Mirrors applySplitClip: the head's out-point becomes explicit at the cut,\n // so optimistic state matches what the server persists.\n const headSourceOutFrame = original.sourceInFrame + headDurationFrames\n\n return {\n label: `Split ${original.label}`,\n execute: (state) =>\n insertClip(\n patchClip(state, resolve(clipId), context, { durationFrames: headDurationFrames, sourceOutFrame: headSourceOutFrame }),\n { ...structuredClone(tail), id: resolve(newClipId) },\n context,\n ),\n undo: (state) =>\n patchClip(removeClip(state, resolve(newClipId), context), resolve(clipId), context, {\n ...structuredClone(original),\n id: resolve(clipId),\n }),\n operations: () => [{ type: 'split_clip', clipId: resolve(clipId), atFrame: input.atFrame }],\n inverseOperations: () => [\n { type: 'delete_clip', clipId: resolve(newClipId) },\n {\n type: 'trim_clip',\n clipId: resolve(clipId),\n startFrame: original.startFrame,\n durationFrames: original.durationFrames,\n sourceInFrame: original.sourceInFrame,\n // Restores the pre-split window; without it the head keeps its\n // out-point at the cut while regaining the full duration.\n sourceOutFrame: original.sourceOutFrame,\n },\n ],\n }\n}\n\n// ---------------------------------------------------------------------------\n// add_caption\n// ---------------------------------------------------------------------------\n\nexport interface AddCaptionInput {\n timeline: SequenceTimeline\n /** Caller-minted optimistic id for the local caption clip. */\n clipId: string\n /** Editor commands are concrete: the caller resolves placement (e.g. via\n * `chooseCaptionPlacement`) and the target track before constructing. */\n trackId: string\n text: string\n language?: string\n startFrame: number\n durationFrames: number\n resolveClipId?: ClipIdResolver\n}\n\nexport function addCaptionCommand(input: AddCaptionInput): TimelineCommand {\n const context = 'add_caption'\n assertNewClipId(input.timeline, input.clipId, context)\n const track = requireUnlockedTrack(input.timeline, input.trackId, context)\n if (track.kind !== 'caption') {\n throw new Error(`${context}: track ${track.name} (${track.id}) is kind ${track.kind}; captions require a caption track`)\n }\n if (typeof input.text !== 'string' || input.text.length === 0) {\n throw new Error(`${context}: text must be a non-empty string`)\n }\n assertClipFitsSequence({\n startFrame: input.startFrame,\n durationFrames: input.durationFrames,\n sequenceDurationFrames: input.timeline.sequence.durationFrames,\n label: context,\n })\n const clip: SequenceClip = {\n id: input.clipId,\n trackId: input.trackId,\n label: input.text,\n startFrame: input.startFrame,\n durationFrames: input.durationFrames,\n sourceInFrame: 0,\n sourceOutFrame: null,\n disabled: false,\n text: input.text,\n ...(input.language !== undefined ? { language: input.language } : {}),\n metadata: {},\n }\n const clipId = input.clipId\n const resolve = input.resolveClipId ?? identityClipId\n\n return {\n label: `Add caption`,\n execute: (state) => insertClip(state, { ...structuredClone(clip), id: resolve(clipId) }, context),\n undo: (state) => removeClip(state, resolve(clipId), context),\n operations: () => [\n {\n type: 'add_caption',\n text: input.text,\n ...(input.language !== undefined ? { language: input.language } : {}),\n startFrame: input.startFrame,\n durationFrames: input.durationFrames,\n trackId: input.trackId,\n },\n ],\n inverseOperations: () => [{ type: 'delete_clip', clipId: resolve(clipId) }],\n }\n}\n\n// ---------------------------------------------------------------------------\n// set_clip_text\n// ---------------------------------------------------------------------------\n\nexport interface SetClipTextInput {\n timeline: SequenceTimeline\n clipId: string\n text: string\n /** Omitted → language unchanged. */\n language?: string\n resolveClipId?: ClipIdResolver\n}\n\n/** Requires the clip to already carry text: `set_clip_text` has no \"create\"\n * semantics in the union, and an inverse for a text-less clip would have to\n * invent an empty string. Boundary: when the clip had NO language and this\n * command sets one, local undo restores `undefined` exactly but the durable\n * inverse cannot clear language (the op has no clear form) — flagged to the\n * apply layer. */\nexport function setClipTextCommand(input: SetClipTextInput): TimelineCommand {\n const context = 'set_clip_text'\n const clip = requireClip(input.timeline, input.clipId, context)\n if (typeof clip.text !== 'string') {\n throw new Error(`${context}: clip ${clip.id} has no text body; create caption text through add_caption`)\n }\n if (typeof input.text !== 'string' || input.text.length === 0) {\n throw new Error(`${context}: text must be a non-empty string`)\n }\n const originalText = clip.text\n const originalLanguage = clip.language\n const targetLanguage = input.language ?? clip.language\n /** Caption labels mirror their text (see addCaptionCommand); keep the\n * mirror in sync only when it was in sync to begin with. */\n const mirrorsLabel = clip.label === clip.text\n const clipId = input.clipId\n const resolve = input.resolveClipId ?? identityClipId\n\n return {\n label: `Edit caption text`,\n execute: (state) =>\n patchClip(state, resolve(clipId), context, {\n text: input.text,\n language: targetLanguage,\n ...(mirrorsLabel ? { label: input.text } : {}),\n }),\n undo: (state) =>\n patchClip(state, resolve(clipId), context, {\n text: originalText,\n language: originalLanguage,\n ...(mirrorsLabel ? { label: originalText } : {}),\n }),\n operations: () => [\n { type: 'set_clip_text', clipId: resolve(clipId), text: input.text, ...(input.language !== undefined ? { language: input.language } : {}) },\n ],\n inverseOperations: () => [\n {\n type: 'set_clip_text',\n clipId: resolve(clipId),\n text: originalText,\n ...(originalLanguage !== undefined ? { language: originalLanguage } : {}),\n },\n ],\n }\n}\n\n// ---------------------------------------------------------------------------\n// set_clip_disabled (toggle)\n// ---------------------------------------------------------------------------\n\nexport interface ToggleClipDisabledInput {\n timeline: SequenceTimeline\n clipId: string\n resolveClipId?: ClipIdResolver\n}\n\n/** The target value is captured at construction (not flipped at execute time)\n * so redo after a rebase applies the same durable op the stack already\n * emitted. */\nexport function toggleClipDisabledCommand(input: ToggleClipDisabledInput): TimelineCommand {\n const context = 'set_clip_disabled'\n const clip = requireClip(input.timeline, input.clipId, context)\n const original = clip.disabled\n const target = !original\n const clipId = input.clipId\n const resolve = input.resolveClipId ?? identityClipId\n\n return {\n label: target ? `Disable ${clip.label}` : `Enable ${clip.label}`,\n execute: (state) => patchClip(state, resolve(clipId), context, { disabled: target }),\n undo: (state) => patchClip(state, resolve(clipId), context, { disabled: original }),\n operations: () => [{ type: 'set_clip_disabled', clipId: resolve(clipId), disabled: target }],\n inverseOperations: () => [{ type: 'set_clip_disabled', clipId: resolve(clipId), disabled: original }],\n }\n}\n","/**\n * rAF playback clock. The playhead is derived from a (start time, start\n * frame) anchor and `performance.now()` deltas — never from per-tick\n * increments — so dropped animation frames cannot accumulate drift.\n *\n * Browser-only at PLAY time, import-safe everywhere: `requestAnimationFrame`\n * and `performance` are resolved off `globalThis` when playback starts, never\n * at module load or construction, so server bundles that import the editor\n * engine do not crash.\n */\n\nimport type { PlaybackClock } from '../contracts'\n\nexport interface PlaybackClockConfig {\n fps: number\n durationFrames: number\n}\n\ninterface RafGlobals {\n requestAnimationFrame?: (callback: (time: number) => void) => number\n cancelAnimationFrame?: (id: number) => void\n performance?: { now(): number }\n}\n\nfunction resolveRaf(): {\n request: (callback: (time: number) => void) => number\n cancel: (id: number) => void\n} {\n const g = globalThis as RafGlobals\n if (typeof g.requestAnimationFrame !== 'function' || typeof g.cancelAnimationFrame !== 'function') {\n throw new Error(\n 'PlaybackClock requires requestAnimationFrame/cancelAnimationFrame — playback runs only in a browser (or a test that stubs both globals)',\n )\n }\n return { request: g.requestAnimationFrame.bind(globalThis), cancel: g.cancelAnimationFrame.bind(globalThis) }\n}\n\nfunction now(): number {\n const perf = (globalThis as RafGlobals).performance\n if (!perf || typeof perf.now !== 'function') {\n throw new Error('PlaybackClock requires performance.now() — playback runs only in a browser (or a test that stubs it)')\n }\n return perf.now()\n}\n\nexport function createPlaybackClock(config: PlaybackClockConfig): PlaybackClock {\n if (!Number.isInteger(config.fps) || config.fps <= 0) {\n throw new Error(`fps must be a positive integer, got ${config.fps}`)\n }\n if (!Number.isInteger(config.durationFrames) || config.durationFrames < 1) {\n throw new Error(`durationFrames must be a positive integer, got ${config.durationFrames}`)\n }\n const lastFrame = config.durationFrames - 1\n\n let frame = 0\n let playing = false\n let disposed = false\n let rafId: number | null = null\n let cancelRaf: ((id: number) => void) | null = null\n let anchorTime = 0\n let anchorFrame = 0\n const listeners = new Set<(frame: number) => void>()\n\n const notify = (): void => {\n for (const listener of [...listeners]) listener(frame)\n }\n\n const stopLoop = (): void => {\n if (rafId !== null && cancelRaf) cancelRaf(rafId)\n rafId = null\n }\n\n /** One callback per animation frame while playing; pauses on reaching the\n * final frame. */\n const tick = (): void => {\n rafId = null\n if (!playing) return\n const elapsedMs = now() - anchorTime\n const advanced = anchorFrame + Math.floor((elapsedMs / 1000) * config.fps)\n if (advanced >= lastFrame) {\n frame = lastFrame\n playing = false\n notify()\n return\n }\n frame = advanced\n notify()\n rafId = resolveRaf().request(tick)\n }\n\n return {\n /** Idempotent while playing. Playing from the final frame restarts at 0 —\n * a play button at the end means \"watch again\", not a dead control. */\n play(): void {\n if (disposed) throw new Error('PlaybackClock is disposed')\n if (playing) return\n const raf = resolveRaf()\n cancelRaf = raf.cancel\n if (frame >= lastFrame) frame = 0\n anchorTime = now()\n anchorFrame = frame\n playing = true\n rafId = raf.request(tick)\n },\n\n pause(): void {\n if (!playing) return\n playing = false\n stopLoop()\n },\n\n /** Clamps into [0, durationFrames - 1]; fractional input rounds to the\n * nearest frame. Re-anchors mid-play so playback continues from the\n * seek target, and notifies so scrubbing drives the playhead. */\n seek(target: number): void {\n if (disposed) throw new Error('PlaybackClock is disposed')\n if (!Number.isFinite(target)) throw new Error(`seek target must be a finite number, got ${target}`)\n frame = Math.max(0, Math.min(lastFrame, Math.round(target)))\n if (playing) {\n anchorTime = now()\n anchorFrame = frame\n }\n notify()\n },\n\n isPlaying(): boolean {\n return playing\n },\n\n getFrame(): number {\n return frame\n },\n\n subscribe(listener: (frame: number) => void): () => void {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n },\n\n dispose(): void {\n playing = false\n stopLoop()\n listeners.clear()\n disposed = true\n },\n }\n}\n","/**\n * Drag snapping. Snap points come from the timeline's structure (clip edges,\n * playhead, sequence end); the snap THRESHOLD is measured in screen pixels at\n * the current zoom — what feels \"close\" is a screen distance, not a frame\n * count — and converts to frames as thresholdPx / zoom.\n */\n\nimport type { SequenceTimeline } from '../../sequences/model'\nimport type { SnapPoint, SnapResult } from '../contracts'\n\n/** Snap point with its owning clip when it came from one, so a drag can\n * exclude the dragged clip's own edges. Structurally a `SnapPoint`. */\nexport interface TimelineSnapPoint extends SnapPoint {\n clipId?: string\n}\n\n/** Disabled clips still occupy timeline space visually, so their edges remain\n * snap targets. */\nexport function collectSnapPoints(timeline: SequenceTimeline, playheadFrame: number): TimelineSnapPoint[] {\n if (!Number.isInteger(playheadFrame) || playheadFrame < 0) {\n throw new Error(`playheadFrame must be a non-negative integer, got ${playheadFrame}`)\n }\n const points: TimelineSnapPoint[] = []\n for (const clip of timeline.clips) {\n points.push({ frame: clip.startFrame, kind: 'clip-start', clipId: clip.id })\n points.push({ frame: clip.startFrame + clip.durationFrames, kind: 'clip-end', clipId: clip.id })\n }\n points.push({ frame: playheadFrame, kind: 'playhead' })\n points.push({ frame: timeline.sequence.durationFrames, kind: 'sequence-end' })\n return points.sort((a, b) => a.frame - b.frame || a.kind.localeCompare(b.kind))\n}\n\nexport interface ApplySnapOptions {\n /** Pixels per frame — converts the pixel threshold into frames. */\n zoom: number\n /** Screen-distance threshold; 10px matches the editor's hit-slop. */\n thresholdPx?: number\n /** Return true to remove a point from consideration (e.g. the dragged\n * clip's own edges via `TimelineSnapPoint.clipId`). */\n exclude?: (point: SnapPoint) => boolean\n}\n\n/** Nearest candidate wins; ties keep the first candidate in `points` order\n * (sorted by frame from `collectSnapPoints`, so the lower frame). */\nexport function applySnap(frame: number, points: SnapPoint[], opts: ApplySnapOptions): SnapResult {\n if (!Number.isFinite(frame)) throw new Error(`frame must be a finite number, got ${frame}`)\n if (!Number.isFinite(opts.zoom) || opts.zoom <= 0) {\n throw new Error(`zoom must be a positive finite number (pixels per frame), got ${opts.zoom}`)\n }\n const thresholdPx = opts.thresholdPx ?? 10\n if (!Number.isFinite(thresholdPx) || thresholdPx < 0) {\n throw new Error(`thresholdPx must be a non-negative finite number, got ${thresholdPx}`)\n }\n const thresholdFrames = thresholdPx / opts.zoom\n\n let best: SnapPoint | null = null\n let bestDistance = Infinity\n for (const point of points) {\n if (opts.exclude && opts.exclude(point)) continue\n const distance = Math.abs(point.frame - frame)\n if (distance < bestDistance) {\n best = point\n bestDistance = distance\n }\n }\n\n if (best !== null && bestDistance <= thresholdFrames) {\n return { frame: best.frame, snapped: true, point: best }\n }\n return { frame, snapped: false, point: null }\n}\n","/**\n * Zoom + viewport coordinate math. Zoom is PIXELS PER FRAME; the slider is a\n * normalized [0, 1] control mapped exponentially (zoom = min·(max/min)^slider)\n * so each slider step multiplies the scale by a constant factor — linear\n * slider feel across a 10x+ range.\n */\n\nimport type { ZoomMath } from '../contracts'\n\nexport interface ZoomMathConfig {\n minZoom: number\n maxZoom: number\n}\n\nexport function createZoomMath(config: ZoomMathConfig): ZoomMath {\n const { minZoom, maxZoom } = config\n if (!Number.isFinite(minZoom) || minZoom <= 0) {\n throw new Error(`minZoom must be a positive finite number, got ${minZoom}`)\n }\n if (!Number.isFinite(maxZoom) || maxZoom <= minZoom) {\n throw new Error(`maxZoom must be finite and greater than minZoom ${minZoom}, got ${maxZoom}`)\n }\n const ratio = maxZoom / minZoom\n\n return {\n minZoom,\n maxZoom,\n /** Slider clamps into [0, 1]: range inputs can overshoot during fast\n * drags and the boundary value is always the right answer. */\n sliderToZoom(slider: number): number {\n if (!Number.isFinite(slider)) throw new Error(`slider must be a finite number, got ${slider}`)\n const t = Math.min(1, Math.max(0, slider))\n return minZoom * Math.pow(ratio, t)\n },\n zoomToSlider(zoom: number): number {\n if (!Number.isFinite(zoom) || zoom <= 0) throw new Error(`zoom must be a positive finite number, got ${zoom}`)\n const clamped = Math.min(maxZoom, Math.max(minZoom, zoom))\n return Math.log(clamped / minZoom) / Math.log(ratio)\n },\n }\n}\n\n/** Horizontal viewport: zoom in pixels per frame, scrollLeft in pixels. */\nexport interface ViewportTransform {\n zoom: number\n scrollLeft: number\n}\n\nfunction assertViewport(view: ViewportTransform): void {\n if (!Number.isFinite(view.zoom) || view.zoom <= 0) {\n throw new Error(`viewport zoom must be a positive finite number, got ${view.zoom}`)\n }\n if (!Number.isFinite(view.scrollLeft)) {\n throw new Error(`viewport scrollLeft must be a finite number, got ${view.scrollLeft}`)\n }\n}\n\n/** Frame → viewport-relative pixel x. Output is fractional; round through\n * `snapPixel` before drawing. */\nexport function frameToPixel(frame: number, view: ViewportTransform): number {\n assertViewport(view)\n if (!Number.isFinite(frame)) throw new Error(`frame must be a finite number, got ${frame}`)\n return frame * view.zoom - view.scrollLeft\n}\n\n/** Viewport-relative pixel x → integer frame. Frames are integer positions,\n * so the result rounds to the nearest frame and floors at 0 (a pointer left\n * of frame 0 resolves to 0). */\nexport function pixelToFrame(pixel: number, view: ViewportTransform): number {\n assertViewport(view)\n if (!Number.isFinite(pixel)) throw new Error(`pixel must be a finite number, got ${pixel}`)\n return Math.max(0, Math.round((pixel + view.scrollLeft) / view.zoom))\n}\n\n/** Snap a CSS-pixel value to the device pixel grid so 1px timeline rules\n * render crisp on fractional-DPR displays. */\nexport function snapPixel(value: number, devicePixelRatio: number): number {\n if (!Number.isFinite(value)) throw new Error(`value must be a finite number, got ${value}`)\n if (!Number.isFinite(devicePixelRatio) || devicePixelRatio <= 0) {\n throw new Error(`devicePixelRatio must be a positive finite number, got ${devicePixelRatio}`)\n }\n return Math.round(value * devicePixelRatio) / devicePixelRatio\n}\n","/**\n * Baseline frame pipeline behind the `VideoFrameProvider` seam: an off-DOM\n * HTMLVideoElement pool for video URLs and an HTMLImageElement pool for\n * stills, merged by a per-URL kind dispatcher so the editor canvas never\n * cares which kind a clip's media is. A WebCodecs implementation can replace\n * this wholesale behind the same seam.\n *\n * Server-safe at import time, browser-only at call time: nothing touches\n * `document` or `fetch` until a provider method runs.\n */\n\nimport type { VideoFrameProvider } from '../contracts'\n\nexport const DEFAULT_MAX_MEDIA_ELEMENTS = 4\n\n/** Half a frame at 30fps. Seeks closer than this repaint the decoder's\n * current frame instead of forcing a redundant seek. */\nexport const SEEK_TOLERANCE_SECONDS = 1 / 60\n\n/** A seek that hasn't fired `seeked` after this long is a decode failure —\n * the draw REJECTS rather than painting whatever frame happens to be up. */\nexport const SEEK_TIMEOUT_MS = 5_000\n\nexport interface FrameRect {\n x: number\n y: number\n width: number\n height: number\n}\n\n/** Object-fit 'contain' placement: preserve aspect ratio, fit entirely inside\n * `dest`, center the residual space. Callers own clearing the letterbox\n * margins — this paints only the fitted region. */\nexport function containFitRect(\n source: { width: number; height: number },\n dest: FrameRect,\n): FrameRect {\n if (!(source.width > 0) || !(source.height > 0)) {\n throw new Error(`containFitRect requires positive source dimensions, got ${source.width}x${source.height}`)\n }\n if (!(dest.width > 0) || !(dest.height > 0)) {\n throw new Error(`containFitRect requires positive destination dimensions, got ${dest.width}x${dest.height}`)\n }\n const scale = Math.min(dest.width / source.width, dest.height / source.height)\n const width = source.width * scale\n const height = source.height * scale\n return {\n x: dest.x + (dest.width - width) / 2,\n y: dest.y + (dest.height - height) / 2,\n width,\n height,\n }\n}\n\nexport function needsSeek(currentTimeSeconds: number, targetSeconds: number): boolean {\n return Math.abs(currentTimeSeconds - targetSeconds) >= SEEK_TOLERANCE_SECONDS\n}\n\n// ---------------------------------------------------------------------------\n// LRU element pool\n// ---------------------------------------------------------------------------\n\nexport interface PooledElementLease<T> {\n element: T\n /** Unpins the element; idle elements become LRU-evictable. Idempotent. */\n release(): void\n}\n\nexport interface MediaElementPool<T> {\n acquire(url: string): PooledElementLease<T>\n has(url: string): boolean\n size(): number\n dispose(): void\n}\n\n/** LRU pool of media elements keyed by URL. Entries pinned by an outstanding\n * lease are never evicted — a draw in flight must keep its element — so the\n * pool can temporarily exceed `maxElements` under concurrent draws and\n * shrinks back as leases release. */\nexport function createMediaElementPool<T>(opts: {\n maxElements: number\n create(url: string): T\n destroy(element: T, url: string): void\n}): MediaElementPool<T> {\n if (!Number.isInteger(opts.maxElements) || opts.maxElements < 1) {\n throw new Error(`maxElements must be a positive integer, got ${opts.maxElements}`)\n }\n // Map insertion order doubles as recency: acquire re-inserts at the back,\n // so eviction scans from the front (least recently used).\n const entries = new Map<string, { element: T; pinned: number }>()\n let disposed = false\n\n const evictOverBudget = (): void => {\n if (entries.size <= opts.maxElements) return\n for (const [url, entry] of entries) {\n if (entries.size <= opts.maxElements) return\n if (entry.pinned > 0) continue\n entries.delete(url)\n opts.destroy(entry.element, url)\n }\n }\n\n return {\n acquire(url) {\n if (disposed) throw new Error(`media element pool is disposed — cannot acquire ${url}`)\n let entry = entries.get(url)\n if (entry) {\n entries.delete(url)\n } else {\n entry = { element: opts.create(url), pinned: 0 }\n }\n entries.set(url, entry)\n entry.pinned += 1\n evictOverBudget()\n let released = false\n return {\n element: entry.element,\n release: () => {\n if (released) return\n released = true\n entry.pinned -= 1\n evictOverBudget()\n },\n }\n },\n has: (url) => entries.has(url),\n size: () => entries.size,\n dispose() {\n disposed = true\n for (const [url, entry] of entries) opts.destroy(entry.element, url)\n entries.clear()\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// Media kind dispatch\n// ---------------------------------------------------------------------------\n\nconst IMAGE_EXTENSIONS = new Set(['apng', 'avif', 'bmp', 'gif', 'jpeg', 'jpg', 'png', 'svg', 'webp'])\nconst VIDEO_EXTENSIONS = new Set(['m4v', 'mkv', 'mov', 'mp4', 'mpeg', 'mpg', 'ogv', 'webm'])\n\n/** Extension-based kind classification; 'unknown' defers to a HEAD\n * content-type probe at draw time. */\nexport function classifyMediaUrl(url: string): 'video' | 'image' | 'unknown' {\n const match = /\\.([a-z0-9]+)(?:[?#].*)?$/i.exec(url)\n const extension = match?.[1]?.toLowerCase()\n if (extension === undefined) return 'unknown'\n if (VIDEO_EXTENSIONS.has(extension)) return 'video'\n if (IMAGE_EXTENSIONS.has(extension)) return 'image'\n return 'unknown'\n}\n\nasync function probeMediaKind(url: string): Promise<'video' | 'image'> {\n const known = classifyMediaUrl(url)\n if (known !== 'unknown') return known\n let contentType: string | null = null\n try {\n const response = await fetch(url, { method: 'HEAD' })\n contentType = response.headers.get('content-type')\n } catch {\n // Servers that reject HEAD usually still serve the bytes; HTMLImageElement\n // is the cheapest probe-by-decoding, so unknowns route to the image path\n // and a genuine failure surfaces from image decode with the URL attached.\n return 'image'\n }\n if (contentType !== null) {\n if (contentType.startsWith('video/')) return 'video'\n if (contentType.startsWith('audio/')) {\n throw new Error(`cannot draw frames from audio media ${url} (content-type ${contentType})`)\n }\n }\n return 'image'\n}\n\n// ---------------------------------------------------------------------------\n// Shared guards\n// ---------------------------------------------------------------------------\n\nfunction requireDocument(caller: string): Document {\n if (typeof document === 'undefined') {\n throw new Error(`${caller} requires a browser document — frame providers are client-side only`)\n }\n return document\n}\n\nfunction assertSourceSeconds(sourceSeconds: number): void {\n if (!Number.isFinite(sourceSeconds) || sourceSeconds < 0) {\n throw new Error(`sourceSeconds must be a non-negative finite number, got ${sourceSeconds}`)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Video path\n// ---------------------------------------------------------------------------\n\nfunction createPooledVideo(url: string): HTMLVideoElement {\n const video = requireDocument('createVideoElementFrameProvider').createElement('video')\n video.crossOrigin = 'anonymous'\n video.muted = true\n video.preload = 'auto'\n video.playsInline = true\n video.src = url\n return video\n}\n\nfunction destroyPooledVideo(video: HTMLVideoElement): void {\n video.pause()\n // Clearing src alone leaves the media resource attached; the empty load()\n // is what actually releases the decoder.\n video.removeAttribute('src')\n video.load()\n}\n\nfunction awaitMediaEvent(video: HTMLVideoElement, eventName: string, url: string): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const cleanup = (): void => {\n clearTimeout(timer)\n video.removeEventListener(eventName, onSuccess)\n video.removeEventListener('error', onError)\n }\n const onSuccess = (): void => {\n cleanup()\n resolve()\n }\n const onError = (): void => {\n cleanup()\n const detail = video.error ? `code ${video.error.code}: ${video.error.message}` : 'no MediaError attached'\n reject(new Error(`media error while waiting for '${eventName}' on ${url} (${detail})`))\n }\n const timer = setTimeout(() => {\n cleanup()\n reject(new Error(`timed out after ${SEEK_TIMEOUT_MS}ms waiting for '${eventName}' on ${url}`))\n }, SEEK_TIMEOUT_MS)\n video.addEventListener(eventName, onSuccess)\n video.addEventListener('error', onError)\n })\n}\n\nasync function seekVideo(video: HTMLVideoElement, targetSeconds: number, url: string): Promise<void> {\n const seeked = awaitMediaEvent(video, 'seeked', url)\n // Out-of-range targets clamp to the media's end so timeline frames past the\n // source hold the last frame (standard NLE behavior) instead of erroring.\n video.currentTime = Number.isFinite(video.duration) && video.duration > 0\n ? Math.min(targetSeconds, video.duration)\n : targetSeconds\n await seeked\n}\n\nasync function drawVideoFrame(\n video: HTMLVideoElement,\n url: string,\n sourceSeconds: number,\n ctx: CanvasRenderingContext2D,\n rect: FrameRect,\n): Promise<void> {\n // HAVE_METADATA (readyState 1): dimensions + duration known, safe to seek.\n if (video.readyState < 1) await awaitMediaEvent(video, 'loadedmetadata', url)\n if (needsSeek(video.currentTime, sourceSeconds)) await seekVideo(video, sourceSeconds, url)\n if (video.videoWidth === 0 || video.videoHeight === 0) {\n throw new Error(`media at ${url} decoded with no video frames (audio-only or corrupt) — cannot draw`)\n }\n const fit = containFitRect({ width: video.videoWidth, height: video.videoHeight }, rect)\n ctx.drawImage(video, fit.x, fit.y, fit.width, fit.height)\n}\n\n// ---------------------------------------------------------------------------\n// Image path\n// ---------------------------------------------------------------------------\n\ninterface PooledImage {\n element: HTMLImageElement\n ready: Promise<void>\n}\n\nfunction createPooledImage(url: string): PooledImage {\n const element = requireDocument('createImageFrameProvider').createElement('img')\n element.crossOrigin = 'anonymous'\n element.src = url\n const ready = element.decode().then(\n () => undefined,\n (error: unknown) => {\n throw new Error(`failed to decode image ${url}`, { cause: error })\n },\n )\n // prefetch never awaits `ready`; pre-attach a handler so a broken image\n // can't fire unhandledrejection. drawFrame's await still observes the error.\n void ready.catch(() => undefined)\n return { element, ready }\n}\n\n/** Stills behind the same `VideoFrameProvider` seam — `sourceSeconds` is\n * validated for contract parity but does not affect the painted pixels. */\nexport function createImageFrameProvider(opts?: { maxElements?: number }): VideoFrameProvider {\n const pool = createMediaElementPool<PooledImage>({\n maxElements: opts?.maxElements ?? DEFAULT_MAX_MEDIA_ELEMENTS,\n create: createPooledImage,\n destroy: (pooled) => {\n pooled.element.src = ''\n },\n })\n return {\n async drawFrame(mediaUrl, sourceSeconds, ctx, rect) {\n assertSourceSeconds(sourceSeconds)\n const lease = pool.acquire(mediaUrl)\n try {\n await lease.element.ready\n const image = lease.element.element\n const fit = containFitRect({ width: image.naturalWidth, height: image.naturalHeight }, rect)\n ctx.drawImage(image, fit.x, fit.y, fit.width, fit.height)\n } finally {\n lease.release()\n }\n },\n prefetch(mediaUrl) {\n pool.acquire(mediaUrl).release()\n },\n dispose() {\n pool.dispose()\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// Merged provider\n// ---------------------------------------------------------------------------\n\n/** The baseline provider `TimelineEditorProps.frameProvider` defaults to.\n * Video and image pools each hold up to `maxElements` entries. */\nexport function createVideoElementFrameProvider(opts?: { maxElements?: number }): VideoFrameProvider {\n const maxElements = opts?.maxElements ?? DEFAULT_MAX_MEDIA_ELEMENTS\n const videoPool = createMediaElementPool<HTMLVideoElement>({\n maxElements,\n create: createPooledVideo,\n destroy: destroyPooledVideo,\n })\n const imageProvider = createImageFrameProvider({ maxElements })\n const kindByUrl = new Map<string, Promise<'video' | 'image'>>()\n // One draw at a time per URL: interleaved currentTime writes on a shared\n // element make the browser coalesce seeks, leaving earlier waiters watching\n // for a `seeked` event that never fires.\n const drawQueue = new Map<string, Promise<void>>()\n\n const resolveKind = (url: string): Promise<'video' | 'image'> => {\n let pending = kindByUrl.get(url)\n if (pending === undefined) {\n pending = probeMediaKind(url)\n // A rejected probe must not poison the cache — drop it so the next\n // draw retries instead of failing forever on a transient.\n pending.catch(() => kindByUrl.delete(url))\n kindByUrl.set(url, pending)\n }\n return pending\n }\n\n const enqueueVideoDraw = (url: string, work: () => Promise<void>): Promise<void> => {\n const previous = drawQueue.get(url) ?? Promise.resolve()\n const run = previous.then(work, work)\n const tail = run.then(\n () => undefined,\n () => undefined,\n ).then(() => {\n if (drawQueue.get(url) === tail) drawQueue.delete(url)\n })\n drawQueue.set(url, tail)\n return run\n }\n\n return {\n async drawFrame(mediaUrl, sourceSeconds, ctx, rect) {\n assertSourceSeconds(sourceSeconds)\n const kind = await resolveKind(mediaUrl)\n if (kind === 'image') {\n await imageProvider.drawFrame(mediaUrl, sourceSeconds, ctx, rect)\n return\n }\n await enqueueVideoDraw(mediaUrl, async () => {\n const lease = videoPool.acquire(mediaUrl)\n try {\n await drawVideoFrame(lease.element, mediaUrl, sourceSeconds, ctx, rect)\n } finally {\n lease.release()\n }\n })\n },\n prefetch(mediaUrl) {\n // Best-effort warm: failures vanish here by design because the same\n // failure resurfaces with full detail on the next drawFrame.\n void resolveKind(mediaUrl)\n .then((kind) => {\n if (kind === 'image') {\n imageProvider.prefetch(mediaUrl)\n return\n }\n videoPool.acquire(mediaUrl).release()\n })\n .catch(() => undefined)\n },\n dispose() {\n videoPool.dispose()\n imageProvider.dispose()\n kindByUrl.clear()\n drawQueue.clear()\n },\n }\n}\n","/**\n * Fuses several `TimelineCommand`s into one undo step so a single gesture\n * (multi-select delete, drop that creates a track and places a clip) never\n * fragments the history. Execute runs in order; undo runs in strict reverse so\n * intermediate states reconstruct exactly.\n */\n\nimport type { TimelineCommand } from '../contracts'\n\nexport function compositeCommand(label: string, commands: TimelineCommand[]): TimelineCommand {\n if (commands.length === 0) throw new Error('compositeCommand requires at least one command')\n return {\n label,\n execute: (state) => commands.reduce((acc, command) => command.execute(acc), state),\n undo: (state) => [...commands].reverse().reduce((acc, command) => command.undo(acc), state),\n operations: () => commands.flatMap((command) => command.operations()),\n inverseOperations: () => [...commands].reverse().flatMap((command) => command.inverseOperations()),\n }\n}\n","/**\n * Pure pointer-gesture math for the timeline editor. Every drag/trim/scrub\n * gesture quantizes through these functions so the interactive behavior is\n * unit-testable without a DOM. All inputs/outputs are integer frames except\n * pixel deltas and zoom (px per frame), which are the only float-valued edge.\n */\n\nimport { MIN_SEQUENCE_CLIP_FRAMES, clampClipStart } from '../../sequences/model'\nimport type { SnapPoint, SnapResult } from '../contracts'\n\n/** Quantize a horizontal pointer delta to whole frames at the current zoom. */\nexport function framesFromPixelDelta(deltaX: number, zoom: number): number {\n if (!Number.isFinite(deltaX)) throw new Error('deltaX must be a finite pixel delta')\n if (!Number.isFinite(zoom) || zoom <= 0) throw new Error('zoom (pixels per frame) must be a positive finite number')\n return Math.round(deltaX / zoom)\n}\n\nexport interface MoveDragInput {\n originStartFrame: number\n durationFrames: number\n deltaFrames: number\n sequenceDurationFrames: number\n}\n\n/** New start frame for a move drag, clamped so the clip stays fully inside\n * the sequence. */\nexport function moveDragStartFrame(input: MoveDragInput): number {\n return clampClipStart({\n startFrame: input.originStartFrame + input.deltaFrames,\n durationFrames: input.durationFrames,\n sequenceDurationFrames: input.sequenceDurationFrames,\n })\n}\n\nexport interface TrimStartDragInput {\n originStartFrame: number\n originDurationFrames: number\n originSourceInFrame: number\n deltaFrames: number\n}\n\nexport interface TrimStartDragResult {\n startFrame: number\n durationFrames: number\n sourceInFrame: number\n}\n\n/**\n * Head trim: the clip END is invariant; start slides between two hard walls —\n * it cannot reveal media before source frame 0 (sourceInFrame >= 0) and cannot\n * pass within MIN_SEQUENCE_CLIP_FRAMES of the end. sourceInFrame shifts by\n * exactly the start delta so the visible content stays anchored.\n */\nexport function trimStartDrag(input: TrimStartDragInput): TrimStartDragResult {\n const endFrame = input.originStartFrame + input.originDurationFrames\n const minStart = Math.max(0, input.originStartFrame - input.originSourceInFrame)\n const maxStart = endFrame - MIN_SEQUENCE_CLIP_FRAMES\n const startFrame = Math.max(minStart, Math.min(maxStart, input.originStartFrame + input.deltaFrames))\n return {\n startFrame,\n durationFrames: endFrame - startFrame,\n sourceInFrame: input.originSourceInFrame + (startFrame - input.originStartFrame),\n }\n}\n\nexport interface TrimEndDragInput {\n originStartFrame: number\n originDurationFrames: number\n sourceInFrame: number\n deltaFrames: number\n sequenceDurationFrames: number\n /** Natural source length in frames when known; bounds how far the tail can\n * extend. Omit for stills and media of unknown length. */\n sourceDurationFrames?: number\n}\n\n/** Tail trim: start is invariant; duration is bounded below by the minimum\n * clip length and above by both the sequence end and the remaining source\n * material past the in-point. */\nexport function trimEndDrag(input: TrimEndDragInput): { durationFrames: number } {\n const bySequence = input.sequenceDurationFrames - input.originStartFrame\n const bySource = input.sourceDurationFrames === undefined\n ? Number.POSITIVE_INFINITY\n : input.sourceDurationFrames - input.sourceInFrame\n const maxDuration = Math.min(bySequence, bySource)\n const durationFrames = Math.max(\n MIN_SEQUENCE_CLIP_FRAMES,\n Math.min(maxDuration, input.originDurationFrames + input.deltaFrames),\n )\n return { durationFrames }\n}\n\nconst TICK_STEPS_SECONDS = [1, 5, 10, 30, 60, 300] as const\n\n/**\n * Smallest ruler step whose major ticks sit at least `minSpacingPx` apart at\n * the current zoom; past the table it grows in whole minutes so labels never\n * collide at extreme zoom-out.\n */\nexport function selectTickStepSeconds(input: { zoom: number; fps: number; minSpacingPx?: number }): number {\n if (!Number.isFinite(input.zoom) || input.zoom <= 0) throw new Error('zoom must be a positive finite number')\n if (!Number.isInteger(input.fps) || input.fps <= 0) throw new Error('fps must be a positive integer')\n const minSpacing = input.minSpacingPx ?? 80\n for (const step of TICK_STEPS_SECONDS) {\n if (step * input.fps * input.zoom >= minSpacing) return step\n }\n const pxPerMinute = 60 * input.fps * input.zoom\n return Math.ceil(minSpacing / pxPerMinute) * 60\n}\n\nexport interface LetterboxRect {\n x: number\n y: number\n width: number\n height: number\n}\n\n/** Contain-fit a media aspect inside a container, centered with letterbox or\n * pillarbox bars. */\nexport function letterboxRect(input: {\n containerWidth: number\n containerHeight: number\n mediaWidth: number\n mediaHeight: number\n}): LetterboxRect {\n const { containerWidth, containerHeight, mediaWidth, mediaHeight } = input\n if (containerWidth <= 0 || containerHeight <= 0) throw new Error('container dimensions must be positive')\n if (mediaWidth <= 0 || mediaHeight <= 0) throw new Error('media dimensions must be positive')\n const scale = Math.min(containerWidth / mediaWidth, containerHeight / mediaHeight)\n const width = mediaWidth * scale\n const height = mediaHeight * scale\n return {\n x: (containerWidth - width) / 2,\n y: (containerHeight - height) / 2,\n width,\n height,\n }\n}\n\n/** Caption type scales with the rendered frame, floored so captions stay\n * legible on small previews. */\nexport function captionFontPx(canvasCssHeight: number): number {\n if (!Number.isFinite(canvasCssHeight) || canvasCssHeight <= 0) throw new Error('canvas height must be positive')\n return Math.max(12, Math.round(canvasCssHeight / 18))\n}\n\n/** Pixel geometry for a clip chip; width floors at 2px so 1-frame clips stay\n * grabbable. */\nexport function clipChipGeometry(input: { startFrame: number; durationFrames: number; zoom: number }): { left: number; width: number } {\n return {\n left: input.startFrame * input.zoom,\n width: Math.max(2, input.durationFrames * input.zoom),\n }\n}\n\n/**\n * A move drag snaps whichever clip edge lands closest to a snap point: the\n * start edge directly, or the end edge re-expressed as a start. An unsnapped\n * candidate passes through unchanged.\n */\nexport function chooseMoveSnap(input: {\n candidateStartFrame: number\n durationFrames: number\n startSnap: SnapResult\n endSnap: SnapResult\n}): { startFrame: number; point: SnapPoint | null } {\n const startDelta = input.startSnap.snapped\n ? Math.abs(input.startSnap.frame - input.candidateStartFrame)\n : Number.POSITIVE_INFINITY\n const endStartFrame = input.endSnap.frame - input.durationFrames\n const endDelta = input.endSnap.snapped\n ? Math.abs(endStartFrame - input.candidateStartFrame)\n : Number.POSITIVE_INFINITY\n if (startDelta === Number.POSITIVE_INFINITY && endDelta === Number.POSITIVE_INFINITY) {\n return { startFrame: input.candidateStartFrame, point: null }\n }\n if (startDelta <= endDelta) return { startFrame: input.startSnap.frame, point: input.startSnap.point }\n return { startFrame: endStartFrame, point: input.endSnap.point }\n}\n","/**\n * Program monitor: a canvas letterboxed to the sequence aspect that paints\n * the playhead frame — black base, the topmost enabled video-track clip via\n * `frameProvider.drawFrame`, then caption text bottom-centered on an 80%\n * black backing bar with type scaled to canvas height / 18.\n *\n * Track stacking: tracks composite bottom-up, so among clips active at the\n * frame the one on the HIGHEST sortOrder track covers the rest; muted tracks\n * do not render. Only `video` tracks paint — `reference` tracks are\n * non-rendered guide media (model contract) and are excluded from mp4/EDL/\n * contact-sheet export, so painting them would preview content the program\n * output does not contain. Paints serialize through a latest-wins queue —\n * decode is async, so a slow seek never paints over a newer frame.\n *\n * `sourceSeconds` = (sourceInFrame + playhead offset into the clip) / fps:\n * the model maps source frames 1:1 at sequence fps.\n */\n\nimport { useEffect, useMemo, useRef, useState } from 'react'\nimport { framesToSeconds, snapshotFrame } from '../../sequences/model'\nimport type { SequenceTimeline } from '../../sequences/model'\nimport type { PlaybackClock, VideoFrameProvider } from '../contracts'\nimport { captionFontPx, letterboxRect } from './interaction-math'\n\nexport interface PreviewCanvasProps {\n timeline: SequenceTimeline\n clock: PlaybackClock\n frameProvider: VideoFrameProvider\n className?: string\n}\n\ninterface CanvasSize {\n width: number\n height: number\n}\n\nexport function PreviewCanvas({ timeline, clock, frameProvider, className }: PreviewCanvasProps) {\n const containerRef = useRef<HTMLDivElement | null>(null)\n const canvasRef = useRef<HTMLCanvasElement | null>(null)\n const [size, setSize] = useState<CanvasSize | null>(null)\n const [drawError, setDrawError] = useState<string | null>(null)\n const paintQueueRef = useRef<{ running: boolean; queuedFrame: number | null }>({ running: false, queuedFrame: null })\n /** Latest paint inputs, readable from the async queue without re-binding it. */\n const paintInputsRef = useRef({ timeline, frameProvider, size })\n paintInputsRef.current = { timeline, frameProvider, size }\n\n useEffect(() => {\n const container = containerRef.current\n if (!container) return\n function measure() {\n const node = containerRef.current\n if (!node) return\n const rect = node.getBoundingClientRect()\n // Zero-size during layout/hidden tabs is a transient, not an error.\n if (rect.width <= 0 || rect.height <= 0) return\n const fit = letterboxRect({\n containerWidth: rect.width,\n containerHeight: rect.height,\n mediaWidth: timeline.sequence.width,\n mediaHeight: timeline.sequence.height,\n })\n setSize((current) => {\n const next = { width: Math.round(fit.width), height: Math.round(fit.height) }\n return current && current.width === next.width && current.height === next.height ? current : next\n })\n }\n measure()\n // ResizeObserver is absent in non-browser test environments; the single\n // mount measure above still sizes the canvas there.\n if (typeof ResizeObserver === 'undefined') return\n const observer = new ResizeObserver(measure)\n observer.observe(container)\n return () => observer.disconnect()\n }, [timeline.sequence.width, timeline.sequence.height])\n\n const requestPaint = useMemo(() => {\n async function paint(frame: number) {\n const { timeline: current, frameProvider: provider, size: cssSize } = paintInputsRef.current\n const canvas = canvasRef.current\n if (!canvas || !cssSize) return\n const ctx = canvas.getContext('2d')\n // Canvas 2D is unavailable in non-browser test environments; layout\n // still renders, only pixels are skipped.\n if (!ctx) return\n const dpr = (typeof window !== 'undefined' && window.devicePixelRatio) || 1\n const backingWidth = Math.round(cssSize.width * dpr)\n const backingHeight = Math.round(cssSize.height * dpr)\n if (canvas.width !== backingWidth) canvas.width = backingWidth\n if (canvas.height !== backingHeight) canvas.height = backingHeight\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0)\n ctx.fillStyle = '#000'\n ctx.fillRect(0, 0, cssSize.width, cssSize.height)\n\n // The playhead may rest at durationFrames (sequence end); paint the\n // final addressable frame there.\n const paintFrame = Math.max(0, Math.min(frame, current.sequence.durationFrames - 1))\n const snapshot = snapshotFrame(current, paintFrame)\n\n const mediaEntries = snapshot.active.filter(({ track, clip }) => (\n track.kind === 'video' && !track.muted && clip.media !== undefined && (clip.media.kind === 'video' || clip.media.kind === 'image')\n ))\n const top = mediaEntries[mediaEntries.length - 1]\n if (top && top.clip.media) {\n const sourceSeconds = framesToSeconds(top.clip.sourceInFrame + (paintFrame - top.clip.startFrame), current.sequence.fps)\n await provider.drawFrame(top.clip.media.url, sourceSeconds, ctx, {\n x: 0,\n y: 0,\n width: cssSize.width,\n height: cssSize.height,\n })\n }\n\n if (snapshot.captions.length > 0) {\n const fontPx = captionFontPx(cssSize.height)\n ctx.font = `600 ${fontPx}px system-ui, sans-serif`\n ctx.textAlign = 'center'\n ctx.textBaseline = 'middle'\n const barHeight = fontPx * 1.6\n let centerY = cssSize.height - barHeight\n for (const caption of [...snapshot.captions].reverse()) {\n const textWidth = Math.min(ctx.measureText(caption.text).width, cssSize.width * 0.86)\n const barWidth = textWidth + fontPx * 1.2\n ctx.fillStyle = 'rgba(0, 0, 0, 0.8)'\n ctx.fillRect((cssSize.width - barWidth) / 2, centerY - barHeight / 2, barWidth, barHeight)\n ctx.fillStyle = '#fff'\n ctx.fillText(caption.text, cssSize.width / 2, centerY, cssSize.width * 0.86)\n centerY -= barHeight + fontPx * 0.25\n }\n }\n }\n\n return function requestPaint(frame: number) {\n const queue = paintQueueRef.current\n if (queue.running) {\n queue.queuedFrame = frame\n return\n }\n queue.running = true\n void (async () => {\n let next: number | null = frame\n while (next !== null) {\n const target = next\n queue.queuedFrame = null\n try {\n await paint(target)\n setDrawError(null)\n } catch (error) {\n setDrawError(error instanceof Error ? error.message : String(error))\n }\n next = queue.queuedFrame\n }\n queue.running = false\n })()\n }\n }, [])\n\n useEffect(() => {\n requestPaint(clock.getFrame())\n return clock.subscribe(requestPaint)\n }, [clock, requestPaint])\n\n // Timeline edits and canvas resizes repaint the held frame.\n useEffect(() => {\n requestPaint(clock.getFrame())\n }, [timeline, size, clock, requestPaint])\n\n return (\n <div ref={containerRef} className={`relative flex min-h-0 flex-1 items-center justify-center overflow-hidden bg-black ${className ?? ''}`}>\n <canvas\n ref={canvasRef}\n data-preview-canvas\n className=\"block\"\n style={size ? { width: `${size.width}px`, height: `${size.height}px` } : { width: '100%', height: '100%' }}\n />\n {drawError ? (\n <p className=\"absolute inset-x-3 bottom-2 truncate rounded bg-rose-950/80 px-2 py-1 text-center text-xs text-rose-200\" role=\"alert\">\n {drawError}\n </p>\n ) : null}\n </div>\n )\n}\n","/**\n * Vertical accent line at the frame an in-flight drag is snapped to. Rendered\n * inside the horizontally-scrolled track area so its x position is plain\n * frame * zoom; visibility is owned by the editor (non-null point = visible).\n */\n\nimport type { SnapPoint } from '../contracts'\n\nexport interface SnapIndicatorLineProps {\n point: SnapPoint | null\n zoom: number\n}\n\nexport function SnapIndicatorLine({ point, zoom }: SnapIndicatorLineProps) {\n if (!point) return null\n return (\n <div\n data-snap-kind={point.kind}\n className=\"pointer-events-none absolute bottom-0 top-0 z-30 w-px bg-[var(--brand-primary)] shadow-[0_0_8px_var(--brand-primary)]\"\n style={{ left: `${point.frame * zoom}px` }}\n />\n )\n}\n","/**\n * Playhead overlay for the track area: a full-height line with a triangular\n * cap. Positioned in timeline pixels (frame * zoom) inside the scrolled\n * content, so it moves with horizontal scroll for free. Pointer-transparent —\n * scrubbing belongs to the ruler.\n */\n\nexport interface TimelinePlayheadProps {\n frame: number\n zoom: number\n}\n\nexport function TimelinePlayhead({ frame, zoom }: TimelinePlayheadProps) {\n return (\n <div\n data-timeline-playhead\n className=\"pointer-events-none absolute bottom-0 top-0 z-20\"\n style={{ left: `${frame * zoom}px` }}\n >\n <div className=\"absolute bottom-0 top-0 w-px bg-[var(--brand-primary)] shadow-[0_0_10px_var(--brand-primary)]\" />\n <div\n className=\"absolute -left-[5px] top-0 h-0 w-0 border-x-[5px] border-t-[7px] border-x-transparent\"\n style={{ borderTopColor: 'var(--brand-primary)' }}\n />\n </div>\n )\n}\n","/**\n * Adaptive timecode ruler. Tick density follows zoom through\n * `selectTickStepSeconds` (major ticks never closer than ~80px, minor ticks\n * at a fifth of the major step when they'd sit at least 8px apart). Click or\n * drag scrubs: the pointer is captured, every move quantizes to a whole frame,\n * and the frame is committed through `onScrub` (the editor routes it to\n * `PlaybackClock.seek`).\n */\n\nimport { useMemo } from 'react'\nimport type { PointerEvent } from 'react'\nimport { formatTimecode } from '../../sequences/model'\nimport { selectTickStepSeconds } from './interaction-math'\n\nexport interface TimelineRulerProps {\n fps: number\n durationFrames: number\n /** Pixels per frame. */\n zoom: number\n onScrub(frame: number): void\n}\n\ninterface RulerTick {\n frame: number\n label: string | null\n}\n\nexport function TimelineRuler({ fps, durationFrames, zoom, onScrub }: TimelineRulerProps) {\n const ticks = useMemo<RulerTick[]>(() => {\n const stepSeconds = selectTickStepSeconds({ zoom, fps })\n const majorStepFrames = stepSeconds * fps\n const minorStepFrames = Math.round(majorStepFrames / 5)\n const drawMinor = minorStepFrames * zoom >= 8 && minorStepFrames >= 1\n const result: RulerTick[] = []\n for (let frame = 0; frame <= durationFrames; frame += majorStepFrames) {\n result.push({ frame, label: formatTimecode(frame, fps) })\n if (!drawMinor) continue\n for (let minor = 1; minor < 5; minor += 1) {\n const minorFrame = frame + minor * minorStepFrames\n if (minorFrame >= durationFrames) break\n result.push({ frame: minorFrame, label: null })\n }\n }\n return result\n }, [durationFrames, fps, zoom])\n\n function frameFromPointer(event: PointerEvent<HTMLDivElement>): number {\n const rect = event.currentTarget.getBoundingClientRect()\n const frame = Math.round((event.clientX - rect.left) / zoom)\n return Math.max(0, Math.min(durationFrames, frame))\n }\n\n function handlePointerDown(event: PointerEvent<HTMLDivElement>) {\n if (event.button !== 0) return\n event.preventDefault()\n // Pointer capture is absent in non-browser test environments.\n if (typeof event.currentTarget.setPointerCapture === 'function') {\n event.currentTarget.setPointerCapture(event.pointerId)\n }\n onScrub(frameFromPointer(event))\n }\n\n function handlePointerMove(event: PointerEvent<HTMLDivElement>) {\n if (typeof event.currentTarget.hasPointerCapture !== 'function') return\n if (!event.currentTarget.hasPointerCapture(event.pointerId)) return\n onScrub(frameFromPointer(event))\n }\n\n return (\n <div\n data-timeline-ruler\n className=\"relative h-7 cursor-ew-resize select-none border-b border-[var(--border-default)] bg-[var(--bg-input)]\"\n style={{ width: `${durationFrames * zoom}px` }}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n >\n {ticks.map((tick) => (\n <div\n key={tick.frame}\n className={`absolute bottom-0 w-px bg-[var(--border-default)] ${tick.label !== null ? 'top-2.5' : 'top-[18px]'}`}\n style={{ left: `${tick.frame * zoom}px` }}\n >\n {tick.label !== null ? (\n <span className=\"absolute -top-2 left-1 whitespace-nowrap font-mono text-[10px] leading-none text-[var(--text-muted)]\">\n {tick.label}\n </span>\n ) : null}\n </div>\n ))}\n </div>\n )\n}\n","/**\n * One clip on a track lane. Owns the pointer gestures that edit it:\n *\n * - body drag → move (horizontal frames + vertical retarget onto another\n * unlocked track of the same kind)\n * - edge handles → head/tail trim, clamped to MIN_SEQUENCE_CLIP_FRAMES and\n * the source material bounds\n * - double-click → inline caption text edit (caption clips only)\n *\n * Gesture discipline: pointer capture on gesture start, every move quantizes\n * to whole frames, Escape restores the pre-drag state without emitting, and a\n * completed gesture commits EXACTLY ONCE through the `onCommit*` callbacks —\n * the editor turns that into one command on the stack (one undo step). The\n * chip never writes timeline state itself; until commit it renders a local\n * preview only.\n *\n * Vertical retarget reads lane geometry captured at gesture start (rects of\n * every `[data-lane-track]` under the editor's `[data-timeline-tracks]` root),\n * so the moving chip itself can never occlude the hit test.\n */\n\nimport { useEffect, useRef, useState } from 'react'\nimport type { PointerEvent as ReactPointerEvent } from 'react'\nimport { formatTimecode } from '../../sequences/model'\nimport type { SequenceClip, SequenceTrack, SequenceTrackKind } from '../../sequences/model'\nimport type { SnapPoint, VideoFrameProvider, WaveformData } from '../contracts'\nimport { loadWaveform, drawWaveform } from '../media/waveform'\nimport {\n clipChipGeometry,\n framesFromPixelDelta,\n moveDragStartFrame,\n trimEndDrag,\n trimStartDrag,\n} from './interaction-math'\n\nexport interface ClipMoveCommit {\n clipId: string\n startFrame: number\n trackId: string\n}\n\nexport interface ClipTrimCommit {\n clipId: string\n startFrame: number\n durationFrames: number\n sourceInFrame: number\n}\n\nexport interface TimelineClipChipProps {\n clip: SequenceClip\n track: SequenceTrack\n fps: number\n /** Pixels per frame. */\n zoom: number\n sequenceDurationFrames: number\n selected: boolean\n canWrite: boolean\n frameProvider: VideoFrameProvider\n /** Snap a candidate move (both clip edges considered); editor closes over\n * the engine's snap points. */\n snapMove(candidate: { startFrame: number; durationFrames: number; clipId: string }): { startFrame: number; point: SnapPoint | null }\n /** Snap a single trim edge. */\n snapEdge(candidate: { frame: number; clipId: string }): { frame: number; point: SnapPoint | null }\n onSnapPointChange(point: SnapPoint | null): void\n onSelect(clipId: string, additive: boolean): void\n onCommitMove(input: ClipMoveCommit): void\n onCommitTrim(input: ClipTrimCommit): void\n onCommitText(input: { clipId: string; text: string }): void\n}\n\ntype GestureKind = 'move' | 'trim-start' | 'trim-end'\n\ninterface LaneTarget {\n trackId: string\n top: number\n bottom: number\n offsetY: number\n}\n\ninterface GestureState {\n kind: GestureKind\n pointerId: number\n originClientX: number\n origin: { startFrame: number; durationFrames: number; sourceInFrame: number }\n /** Same-kind unlocked lanes, captured once at gesture start. */\n laneTargets: LaneTarget[]\n originTrackId: string\n}\n\ninterface GesturePreview {\n startFrame: number\n durationFrames: number\n sourceInFrame: number\n trackId: string\n translateY: number\n moved: boolean\n}\n\nconst KIND_TONES: Record<SequenceTrackKind, string> = {\n video: 'border-sky-400/40 bg-sky-500/15',\n audio: 'border-emerald-400/40 bg-emerald-500/15',\n caption: 'border-amber-400/40 bg-amber-500/15',\n reference: 'border-zinc-400/40 bg-zinc-500/15',\n agent: 'border-violet-400/40 bg-violet-500/15',\n}\n\n/** Spec'd cache key is the clip id: a clip's waveform survives re-renders and\n * zoom changes; a different clip with the same media decodes independently. */\nconst waveformCache = new Map<string, Promise<WaveformData>>()\nconst WAVEFORM_BUCKETS = 256\n\nfunction sourceDurationFrames(clip: SequenceClip, fps: number): number | undefined {\n if (clip.sourceOutFrame !== null && clip.sourceOutFrame !== undefined) return clip.sourceOutFrame\n if (clip.media?.durationSeconds !== undefined) return Math.round(clip.media.durationSeconds * fps)\n return undefined\n}\n\nexport function TimelineClipChip(props: TimelineClipChipProps) {\n const { clip, track, fps, zoom, selected, canWrite, frameProvider } = props\n const rootRef = useRef<HTMLDivElement | null>(null)\n const gestureRef = useRef<GestureState | null>(null)\n const [preview, setPreview] = useState<GesturePreview | null>(null)\n /** Mirror of `preview` readable inside pointerup before React flushes the\n * last pointermove's state update. */\n const previewRef = useRef<GesturePreview | null>(null)\n const [editingText, setEditingText] = useState<string | null>(null)\n const posterRef = useRef<HTMLCanvasElement | null>(null)\n const waveformRef = useRef<HTMLCanvasElement | null>(null)\n\n const shown = preview ?? {\n startFrame: clip.startFrame,\n durationFrames: clip.durationFrames,\n sourceInFrame: clip.sourceInFrame,\n trackId: clip.trackId,\n translateY: 0,\n moved: false,\n }\n const geometry = clipChipGeometry({ startFrame: shown.startFrame, durationFrames: shown.durationFrames, zoom })\n const interactive = canWrite && !track.locked\n\n // -------------------------------------------------------------------------\n // Gestures\n // -------------------------------------------------------------------------\n\n function collectLaneTargets(): LaneTarget[] {\n const root = rootRef.current?.closest('[data-timeline-tracks]')\n const originLane = rootRef.current?.closest('[data-lane-track]')\n if (!root || !originLane) return []\n const originTop = originLane.getBoundingClientRect().top\n const targets: LaneTarget[] = []\n for (const lane of Array.from(root.querySelectorAll<HTMLElement>('[data-lane-track]'))) {\n if (lane.dataset.laneKind !== track.kind || lane.dataset.laneLocked === 'true') continue\n const rect = lane.getBoundingClientRect()\n const trackId = lane.dataset.laneTrack\n if (!trackId) continue\n targets.push({ trackId, top: rect.top, bottom: rect.bottom, offsetY: rect.top - originTop })\n }\n return targets\n }\n\n function beginGesture(event: ReactPointerEvent<HTMLElement>, kind: GestureKind) {\n if (!interactive || event.button !== 0 || editingText !== null) return\n event.preventDefault()\n event.stopPropagation()\n // Pointer capture is absent in non-browser test environments; the gesture\n // still works there, it just loses the off-element tracking guarantee.\n if (typeof event.currentTarget.setPointerCapture === 'function') {\n event.currentTarget.setPointerCapture(event.pointerId)\n }\n gestureRef.current = {\n kind,\n pointerId: event.pointerId,\n originClientX: event.clientX,\n origin: { startFrame: clip.startFrame, durationFrames: clip.durationFrames, sourceInFrame: clip.sourceInFrame },\n laneTargets: kind === 'move' ? collectLaneTargets() : [],\n originTrackId: clip.trackId,\n }\n document.body.style.cursor = kind === 'move' ? 'grabbing' : 'ew-resize'\n document.body.style.userSelect = 'none'\n }\n\n function applyPreview(next: GesturePreview) {\n previewRef.current = next\n setPreview(next)\n }\n\n function updateGesture(event: ReactPointerEvent<HTMLElement>) {\n const gesture = gestureRef.current\n if (!gesture || event.pointerId !== gesture.pointerId) return\n const deltaFrames = framesFromPixelDelta(event.clientX - gesture.originClientX, zoom)\n const moved = previewRef.current?.moved || Math.abs(event.clientX - gesture.originClientX) > 3\n\n if (gesture.kind === 'move') {\n const candidate = moveDragStartFrame({\n originStartFrame: gesture.origin.startFrame,\n durationFrames: gesture.origin.durationFrames,\n deltaFrames,\n sequenceDurationFrames: props.sequenceDurationFrames,\n })\n const snapped = props.snapMove({ startFrame: candidate, durationFrames: gesture.origin.durationFrames, clipId: clip.id })\n const startFrame = moveDragStartFrame({\n originStartFrame: snapped.startFrame,\n durationFrames: gesture.origin.durationFrames,\n deltaFrames: 0,\n sequenceDurationFrames: props.sequenceDurationFrames,\n })\n // A clamp that displaced the snapped frame voids the snap indicator.\n props.onSnapPointChange(startFrame === snapped.startFrame ? snapped.point : null)\n const lane = gesture.laneTargets.find((target) => event.clientY >= target.top && event.clientY < target.bottom)\n applyPreview({\n startFrame,\n durationFrames: gesture.origin.durationFrames,\n sourceInFrame: gesture.origin.sourceInFrame,\n trackId: lane?.trackId ?? gesture.originTrackId,\n translateY: lane?.offsetY ?? 0,\n moved,\n })\n return\n }\n\n if (gesture.kind === 'trim-start') {\n const raw = trimStartDrag({\n originStartFrame: gesture.origin.startFrame,\n originDurationFrames: gesture.origin.durationFrames,\n originSourceInFrame: gesture.origin.sourceInFrame,\n deltaFrames,\n })\n const snapped = props.snapEdge({ frame: raw.startFrame, clipId: clip.id })\n const clamped = trimStartDrag({\n originStartFrame: gesture.origin.startFrame,\n originDurationFrames: gesture.origin.durationFrames,\n originSourceInFrame: gesture.origin.sourceInFrame,\n deltaFrames: snapped.frame - gesture.origin.startFrame,\n })\n props.onSnapPointChange(clamped.startFrame === snapped.frame ? snapped.point : null)\n applyPreview({ ...clamped, trackId: gesture.originTrackId, translateY: 0, moved })\n return\n }\n\n const rawEnd = gesture.origin.startFrame + trimEndDrag({\n originStartFrame: gesture.origin.startFrame,\n originDurationFrames: gesture.origin.durationFrames,\n sourceInFrame: gesture.origin.sourceInFrame,\n deltaFrames,\n sequenceDurationFrames: props.sequenceDurationFrames,\n sourceDurationFrames: sourceDurationFrames(clip, fps),\n }).durationFrames\n const snapped = props.snapEdge({ frame: rawEnd, clipId: clip.id })\n const clamped = trimEndDrag({\n originStartFrame: gesture.origin.startFrame,\n originDurationFrames: gesture.origin.durationFrames,\n sourceInFrame: gesture.origin.sourceInFrame,\n deltaFrames: snapped.frame - (gesture.origin.startFrame + gesture.origin.durationFrames),\n sequenceDurationFrames: props.sequenceDurationFrames,\n sourceDurationFrames: sourceDurationFrames(clip, fps),\n })\n props.onSnapPointChange(gesture.origin.startFrame + clamped.durationFrames === snapped.frame ? snapped.point : null)\n applyPreview({\n startFrame: gesture.origin.startFrame,\n durationFrames: clamped.durationFrames,\n sourceInFrame: gesture.origin.sourceInFrame,\n trackId: gesture.originTrackId,\n translateY: 0,\n moved,\n })\n }\n\n function endGestureCleanup() {\n gestureRef.current = null\n previewRef.current = null\n setPreview(null)\n props.onSnapPointChange(null)\n document.body.style.cursor = ''\n document.body.style.userSelect = ''\n }\n\n function finishGesture(event: ReactPointerEvent<HTMLElement>) {\n const gesture = gestureRef.current\n if (!gesture || event.pointerId !== gesture.pointerId) return\n const finalPreview = previewRef.current\n const kind = gesture.kind\n const origin = gesture.origin\n const originTrackId = gesture.originTrackId\n endGestureCleanup()\n\n if (!finalPreview || !finalPreview.moved) {\n props.onSelect(clip.id, event.shiftKey)\n return\n }\n if (kind === 'move') {\n if (finalPreview.startFrame === origin.startFrame && finalPreview.trackId === originTrackId) return\n props.onCommitMove({ clipId: clip.id, startFrame: finalPreview.startFrame, trackId: finalPreview.trackId })\n return\n }\n if (finalPreview.startFrame === origin.startFrame && finalPreview.durationFrames === origin.durationFrames) return\n props.onCommitTrim({\n clipId: clip.id,\n startFrame: finalPreview.startFrame,\n durationFrames: finalPreview.durationFrames,\n sourceInFrame: finalPreview.sourceInFrame,\n })\n }\n\n // Escape abandons the gesture: pre-drag state restores, nothing emits.\n useEffect(() => {\n if (!preview) return\n function onKeyDown(event: KeyboardEvent) {\n if (event.key !== 'Escape') return\n event.preventDefault()\n endGestureCleanup()\n }\n window.addEventListener('keydown', onKeyDown)\n return () => window.removeEventListener('keydown', onKeyDown)\n // endGestureCleanup is stable in behavior; preview presence gates the listener.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [preview !== null])\n\n // -------------------------------------------------------------------------\n // Media previews (poster frame / waveform)\n // -------------------------------------------------------------------------\n\n const mediaUrl = clip.media?.url\n const mediaKind = clip.media?.kind\n const isVisualMedia = mediaKind === 'video' || mediaKind === 'image'\n const isAudioMedia = mediaKind === 'audio'\n\n useEffect(() => {\n const canvas = posterRef.current\n if (!canvas || !mediaUrl || !isVisualMedia) return\n const ctx = canvas.getContext('2d')\n // Canvas 2D is unavailable in non-browser test environments; the chip\n // still lays out, only the poster paint is skipped.\n if (!ctx) return\n const dpr = window.devicePixelRatio || 1\n const cssWidth = canvas.clientWidth || 40\n const cssHeight = canvas.clientHeight || 40\n canvas.width = Math.round(cssWidth * dpr)\n canvas.height = Math.round(cssHeight * dpr)\n ctx.scale(dpr, dpr)\n let cancelled = false\n frameProvider\n .drawFrame(mediaUrl, clip.sourceInFrame / fps, ctx, { x: 0, y: 0, width: cssWidth, height: cssHeight })\n .catch(() => {\n // A failed poster leaves the chip's base tone; the same failure\n // surfaces loudly in the preview canvas where it blocks real work.\n if (!cancelled) canvas.dataset.posterError = 'true'\n })\n return () => {\n cancelled = true\n }\n }, [mediaUrl, isVisualMedia, clip.sourceInFrame, fps, frameProvider])\n\n useEffect(() => {\n const canvas = waveformRef.current\n if (!canvas || !mediaUrl || !isAudioMedia) return\n let pending = waveformCache.get(clip.id)\n if (!pending) {\n pending = loadWaveform(mediaUrl, WAVEFORM_BUCKETS)\n pending.catch(() => waveformCache.delete(clip.id))\n waveformCache.set(clip.id, pending)\n }\n let cancelled = false\n pending\n .then((data) => {\n if (cancelled) return\n const ctx = canvas.getContext('2d')\n if (!ctx) return\n const dpr = window.devicePixelRatio || 1\n const cssWidth = canvas.clientWidth || 1\n const cssHeight = canvas.clientHeight || 1\n canvas.width = Math.round(cssWidth * dpr)\n canvas.height = Math.round(cssHeight * dpr)\n ctx.scale(dpr, dpr)\n // Paint only the source window this clip plays.\n const bucketsPerSecond = data.peaks.length / data.durationSeconds\n const fromBucket = Math.floor((shown.sourceInFrame / fps) * bucketsPerSecond)\n const toBucket = Math.ceil(((shown.sourceInFrame + shown.durationFrames) / fps) * bucketsPerSecond)\n const peaks = data.peaks.subarray(Math.max(0, fromBucket), Math.min(data.peaks.length, Math.max(fromBucket + 1, toBucket)))\n if (peaks.length === 0) return\n drawWaveform(\n ctx,\n { peaks, samplesPerBucket: data.samplesPerBucket, durationSeconds: data.durationSeconds },\n { x: 0, y: 0, width: cssWidth, height: cssHeight },\n 'rgba(52, 211, 153, 0.75)',\n )\n })\n .catch(() => {\n if (!cancelled) canvas.dataset.waveformError = 'true'\n })\n return () => {\n cancelled = true\n }\n }, [mediaUrl, isAudioMedia, clip.id, fps, shown.sourceInFrame, shown.durationFrames, geometry.width])\n\n // -------------------------------------------------------------------------\n // Caption text editing\n // -------------------------------------------------------------------------\n\n function commitText() {\n if (editingText === null) return\n const next = editingText.trim()\n setEditingText(null)\n if (next.length === 0 || next === clip.text) return\n props.onCommitText({ clipId: clip.id, text: next })\n }\n\n const isCaption = track.kind === 'caption'\n const dragging = preview !== null\n\n return (\n <div\n ref={rootRef}\n data-clip-id={clip.id}\n role=\"button\"\n tabIndex={-1}\n title={clip.label}\n className={`group absolute bottom-1 top-1 overflow-hidden rounded border text-left select-none ${KIND_TONES[track.kind]} ${\n selected ? 'ring-2 ring-[var(--brand-primary)]' : 'hover:ring-1 hover:ring-[var(--text-muted)]'\n } ${clip.disabled ? 'opacity-40' : ''} ${dragging ? 'z-30 shadow-lg shadow-black/30' : ''} ${\n interactive ? 'cursor-grab active:cursor-grabbing' : 'cursor-default'\n }`}\n style={{\n left: `${geometry.left}px`,\n width: `${geometry.width}px`,\n transform: shown.translateY !== 0 ? `translateY(${shown.translateY}px)` : undefined,\n }}\n onPointerDown={(event) => beginGesture(event, 'move')}\n onPointerMove={updateGesture}\n onPointerUp={finishGesture}\n onPointerCancel={endGestureCleanup}\n onClick={(event) => event.stopPropagation()}\n onDoubleClick={(event) => {\n event.stopPropagation()\n if (isCaption && interactive && typeof clip.text === 'string') setEditingText(clip.text)\n }}\n >\n {isAudioMedia ? <canvas ref={waveformRef} className=\"absolute inset-0 h-full w-full\" /> : null}\n\n <div className=\"relative flex h-full min-w-0 items-stretch gap-1.5 px-1.5 py-1\">\n {isVisualMedia ? (\n <canvas ref={posterRef} className=\"h-full w-10 shrink-0 rounded-sm bg-black/40 object-cover\" />\n ) : null}\n <div className=\"min-w-0 flex-1\">\n <div className=\"truncate text-[11px] font-medium leading-4 text-[var(--text-primary)]\">\n {isCaption && typeof clip.text === 'string' ? clip.text : clip.label}\n </div>\n <span className=\"mt-0.5 inline-block rounded bg-black/30 px-1 font-mono text-[9px] leading-3 text-[var(--text-secondary)]\">\n {formatTimecode(shown.durationFrames, fps)}\n </span>\n </div>\n </div>\n\n {editingText !== null ? (\n <input\n autoFocus\n value={editingText}\n onChange={(event) => setEditingText(event.target.value)}\n onPointerDown={(event) => event.stopPropagation()}\n onKeyDown={(event) => {\n if (event.key === 'Enter') commitText()\n if (event.key === 'Escape') setEditingText(null)\n event.stopPropagation()\n }}\n onBlur={commitText}\n className=\"absolute inset-0 z-10 w-full bg-black/80 px-1.5 text-[11px] text-[var(--text-primary)] outline-none ring-1 ring-[var(--brand-primary)]\"\n aria-label=\"Caption text\"\n />\n ) : null}\n\n {interactive ? (\n <>\n <span\n data-trim-handle=\"start\"\n className=\"absolute bottom-0 left-0 top-0 z-10 w-1.5 cursor-ew-resize bg-transparent opacity-0 transition group-hover:opacity-100 group-hover:bg-[var(--brand-primary)]/60\"\n onPointerDown={(event) => beginGesture(event, 'trim-start')}\n onPointerMove={updateGesture}\n onPointerUp={finishGesture}\n onPointerCancel={endGestureCleanup}\n aria-hidden\n />\n <span\n data-trim-handle=\"end\"\n className=\"absolute bottom-0 right-0 top-0 z-10 w-1.5 cursor-ew-resize bg-transparent opacity-0 transition group-hover:opacity-100 group-hover:bg-[var(--brand-primary)]/60\"\n onPointerDown={(event) => beginGesture(event, 'trim-end')}\n onPointerMove={updateGesture}\n onPointerUp={finishGesture}\n onPointerCancel={endGestureCleanup}\n aria-hidden\n />\n </>\n ) : null}\n </div>\n )\n}\n","/**\n * Waveform rendering for audio clips: bucketed max-abs peaks computed once\n * per (media, zoom bucket count) and painted as mirrored bars around the\n * track lane's midline. `computeWaveform` is pure so peak math is testable\n * without Web Audio; `loadWaveform` is the browser edge that decodes real\n * media into it.\n */\n\nimport type { WaveformData } from '../contracts'\n\n/** Structural slice of Web Audio's AudioBuffer so peak math runs on synthetic\n * fixtures in tests and on real decoded buffers in the browser. */\nexport interface AudioBufferLike {\n numberOfChannels: number\n length: number\n sampleRate: number\n duration: number\n getChannelData(channel: number): Float32Array\n}\n\n/** One max-abs peak per bucket, taken across all channels. `peaks[b]` is the\n * loudest absolute sample in bucket `b`; rendering mirrors it around the\n * midline, which is what the contract's \"peak pair\" denotes. Buckets past\n * the end of short audio hold 0. */\nexport function computeWaveform(buffer: AudioBufferLike, bucketCount: number): WaveformData {\n if (!Number.isInteger(bucketCount) || bucketCount < 1) {\n throw new Error(`bucketCount must be a positive integer, got ${bucketCount}`)\n }\n if (!Number.isInteger(buffer.length) || buffer.length < 1) {\n throw new Error(`audio buffer is empty (length ${buffer.length}) — cannot compute a waveform`)\n }\n if (!Number.isInteger(buffer.numberOfChannels) || buffer.numberOfChannels < 1) {\n throw new Error(`audio buffer must have at least one channel, got ${buffer.numberOfChannels}`)\n }\n if (!Number.isFinite(buffer.duration) || buffer.duration <= 0) {\n throw new Error(`audio buffer duration must be positive, got ${buffer.duration}`)\n }\n const samplesPerBucket = Math.ceil(buffer.length / bucketCount)\n const peaks = new Float32Array(bucketCount)\n for (let channel = 0; channel < buffer.numberOfChannels; channel++) {\n const data = buffer.getChannelData(channel)\n if (data.length !== buffer.length) {\n throw new Error(`channel ${channel} has ${data.length} samples, expected ${buffer.length}`)\n }\n for (let bucket = 0; bucket < bucketCount; bucket++) {\n const start = bucket * samplesPerBucket\n if (start >= data.length) break\n let peak = peaks[bucket] as number\n for (const sample of data.subarray(start, Math.min(data.length, start + samplesPerBucket))) {\n const magnitude = Math.abs(sample)\n if (magnitude > peak) peak = magnitude\n }\n peaks[bucket] = peak\n }\n }\n return { peaks, samplesPerBucket, durationSeconds: buffer.duration }\n}\n\n/** Fetch + decode `mediaUrl` and bucket it. Pass `ctx` to reuse a shared\n * AudioContext; otherwise one is created and closed around the decode. */\nexport async function loadWaveform(mediaUrl: string, bucketCount: number, ctx?: AudioContext): Promise<WaveformData> {\n const response = await fetch(mediaUrl)\n if (!response.ok) {\n throw new Error(`failed to fetch audio for waveform: ${response.status} ${response.statusText} from ${mediaUrl}`)\n }\n const bytes = await response.arrayBuffer()\n const ownsContext = ctx === undefined\n if (ctx === undefined) {\n if (typeof AudioContext === 'undefined') {\n throw new Error('loadWaveform requires Web Audio (AudioContext) — pass a ctx or call from a browser')\n }\n ctx = new AudioContext()\n }\n try {\n const decoded = await ctx.decodeAudioData(bytes)\n return computeWaveform(decoded, bucketCount)\n } finally {\n if (ownsContext) await ctx.close()\n }\n}\n\n/** Paint mirrored peak bars centered on the rect's midline. Silent buckets\n * still paint a 1px hairline so the lane reads as audio, not as empty;\n * peaks beyond ±1.0 (hot masters) clip to the full lane height. */\nexport function drawWaveform(\n ctx: CanvasRenderingContext2D,\n data: WaveformData,\n rect: { x: number; y: number; width: number; height: number },\n color: string,\n): void {\n if (data.peaks.length === 0) {\n throw new Error('waveform has no peaks — compute it with a positive bucketCount')\n }\n if (!(rect.width > 0) || !(rect.height > 0)) {\n throw new Error(`drawWaveform requires positive rect dimensions, got ${rect.width}x${rect.height}`)\n }\n ctx.fillStyle = color\n const midline = rect.y + rect.height / 2\n const step = rect.width / data.peaks.length\n const barWidth = Math.max(1, step - 1)\n for (const [index, peak] of data.peaks.entries()) {\n const half = Math.min(1, Math.max(0, peak)) * (rect.height / 2)\n const barHeight = Math.max(1, half * 2)\n ctx.fillRect(rect.x + index * step, midline - barHeight / 2, barWidth, barHeight)\n }\n}\n","/**\n * Inline SVG glyphs for the timeline editor. The package stays dependency-free\n * beyond React (the repo convention — see web-react), so the handful of icons\n * the editor needs are inlined with lucide-equivalent path data.\n */\n\ninterface GlyphProps {\n className?: string\n}\n\nfunction glyph(paths: React.ReactNode) {\n return function Glyph({ className }: GlyphProps) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n {paths}\n </svg>\n )\n }\n}\n\nexport const FilmGlyph = glyph(\n <>\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" />\n <path d=\"M7 3v18M17 3v18M3 8h4M3 16h4M17 8h4M17 16h4\" />\n </>,\n)\n\nexport const AudioGlyph = glyph(\n <path d=\"M2 12h2l2-7 3 14 3-9 2 5 2-3h6\" />,\n)\n\nexport const CaptionGlyph = glyph(\n <>\n <rect x=\"2\" y=\"5\" width=\"20\" height=\"14\" rx=\"2\" />\n <path d=\"M6 13h4M6 16h8M14 13h4\" />\n </>,\n)\n\nexport const ReferenceGlyph = glyph(\n <>\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" />\n <circle cx=\"9\" cy=\"9\" r=\"2\" />\n <path d=\"m21 15-3.1-3.1a2 2 0 0 0-2.8 0L6 21\" />\n </>,\n)\n\nexport const AgentGlyph = glyph(\n <>\n <rect x=\"4\" y=\"8\" width=\"16\" height=\"12\" rx=\"2\" />\n <path d=\"M12 8V4M8 4h8M9 13v2M15 13v2\" />\n </>,\n)\n\nexport const LockGlyph = glyph(\n <>\n <rect x=\"5\" y=\"11\" width=\"14\" height=\"10\" rx=\"2\" />\n <path d=\"M8 11V7a4 4 0 0 1 8 0v4\" />\n </>,\n)\n\nexport const MutedGlyph = glyph(\n <>\n <path d=\"M11 5 6 9H2v6h4l5 4z\" />\n <path d=\"m23 9-6 6M17 9l6 6\" />\n </>,\n)\n\nexport const PlayGlyph = glyph(\n <path d=\"m6 4 14 8-14 8z\" fill=\"currentColor\" stroke=\"none\" />,\n)\n\nexport const PauseGlyph = glyph(\n <path d=\"M7 4h3v16H7zM14 4h3v16h-3z\" fill=\"currentColor\" stroke=\"none\" />,\n)\n\nexport const UndoGlyph = glyph(\n <path d=\"M3 7v6h6M3 13a9 9 0 1 0 3-7.7\" />,\n)\n\nexport const RedoGlyph = glyph(\n <path d=\"M21 7v6h-6M21 13a9 9 0 1 1-3-7.7\" />,\n)\n\nexport const MagnetGlyph = glyph(\n <>\n <path d=\"m6 15-4-4 6.75-6.77a7.79 7.79 0 0 1 11 11L13 22l-4-4 6.39-6.36a2.14 2.14 0 0 0-3-3z\" />\n <path d=\"m5 8 4 4M12 15l4 4\" />\n </>,\n)\n\nexport const ScissorsGlyph = glyph(\n <>\n <circle cx=\"6\" cy=\"6\" r=\"3\" />\n <circle cx=\"6\" cy=\"18\" r=\"3\" />\n <path d=\"M20 4 8.12 15.88M14.47 14.48 20 20M8.12 8.12 12 12\" />\n </>,\n)\n\nexport const CaptionPlusGlyph = glyph(\n <>\n <rect x=\"2\" y=\"5\" width=\"20\" height=\"14\" rx=\"2\" />\n <path d=\"M12 9v6M9 12h6\" />\n </>,\n)\n","/**\n * One track: a sticky-left header (name, kind glyph, lock/mute state) and a\n * lane sized in timeline pixels (durationFrames * zoom) carrying a\n * `TimelineClipChip` per clip. The lane advertises itself through\n * `data-lane-track`/`data-lane-kind`/`data-lane-locked` — the geometry chips\n * read for vertical drag retargeting. Clicking empty lane space seeks the\n * playhead.\n */\n\nimport type { PointerEvent as ReactPointerEvent } from 'react'\nimport type { SequenceClip, SequenceTrack, SequenceTrackKind } from '../../sequences/model'\nimport type { SnapPoint, VideoFrameProvider } from '../contracts'\nimport { TimelineClipChip } from './TimelineClipChip'\nimport type { ClipMoveCommit, ClipTrimCommit } from './TimelineClipChip'\nimport { AgentGlyph, AudioGlyph, CaptionGlyph, FilmGlyph, LockGlyph, MutedGlyph, ReferenceGlyph } from './glyphs'\n\nconst LANE_HEIGHTS: Record<SequenceTrackKind, string> = {\n video: 'h-16',\n reference: 'h-16',\n audio: 'h-14',\n caption: 'h-9',\n agent: 'h-9',\n}\n\nconst KIND_GLYPHS: Record<SequenceTrackKind, (props: { className?: string }) => React.ReactNode> = {\n video: FilmGlyph,\n audio: AudioGlyph,\n caption: CaptionGlyph,\n reference: ReferenceGlyph,\n agent: AgentGlyph,\n}\n\nexport interface TimelineTrackRowProps {\n track: SequenceTrack\n clips: SequenceClip[]\n fps: number\n zoom: number\n sequenceDurationFrames: number\n selectedClipIds: ReadonlySet<string>\n canWrite: boolean\n frameProvider: VideoFrameProvider\n snapMove(candidate: { startFrame: number; durationFrames: number; clipId: string }): { startFrame: number; point: SnapPoint | null }\n snapEdge(candidate: { frame: number; clipId: string }): { frame: number; point: SnapPoint | null }\n onSnapPointChange(point: SnapPoint | null): void\n onSelectClip(clipId: string, additive: boolean): void\n onCommitMove(input: ClipMoveCommit): void\n onCommitTrim(input: ClipTrimCommit): void\n onCommitText(input: { clipId: string; text: string }): void\n onLaneSeek(frame: number): void\n}\n\nexport function TimelineTrackRow(props: TimelineTrackRowProps) {\n const { track, clips, fps, zoom, sequenceDurationFrames } = props\n const Glyph = KIND_GLYPHS[track.kind]\n const laneHeight = LANE_HEIGHTS[track.kind]\n\n function handleLanePointerDown(event: ReactPointerEvent<HTMLDivElement>) {\n // Chips stop propagation; a pointerdown reaching the lane is empty space.\n if (event.button !== 0) return\n const rect = event.currentTarget.getBoundingClientRect()\n const frame = Math.max(0, Math.min(sequenceDurationFrames, Math.round((event.clientX - rect.left) / zoom)))\n props.onLaneSeek(frame)\n }\n\n return (\n <div className=\"flex border-b border-[var(--border-default)] last:border-b-0\">\n <div className={`sticky left-0 z-10 flex w-36 shrink-0 items-center gap-2 border-r border-[var(--border-default)] bg-[var(--bg-input)] px-2.5 ${laneHeight}`}>\n <Glyph className=\"h-3.5 w-3.5 shrink-0 text-[var(--text-muted)]\" />\n <span className=\"min-w-0 flex-1 truncate text-xs font-medium text-[var(--text-secondary)]\">{track.name}</span>\n {track.locked ? <LockGlyph className=\"h-3 w-3 shrink-0 text-amber-400\" /> : null}\n {track.muted ? <MutedGlyph className=\"h-3 w-3 shrink-0 text-[var(--text-muted)]\" /> : null}\n </div>\n <div\n data-lane-track={track.id}\n data-lane-kind={track.kind}\n data-lane-locked={track.locked ? 'true' : 'false'}\n className={`relative ${laneHeight} ${track.muted ? 'opacity-60' : ''}`}\n style={{ width: `${sequenceDurationFrames * zoom}px` }}\n onPointerDown={handleLanePointerDown}\n >\n {clips.map((clip) => (\n <TimelineClipChip\n key={clip.id}\n clip={clip}\n track={track}\n fps={fps}\n zoom={zoom}\n sequenceDurationFrames={sequenceDurationFrames}\n selected={props.selectedClipIds.has(clip.id)}\n canWrite={props.canWrite}\n frameProvider={props.frameProvider}\n snapMove={props.snapMove}\n snapEdge={props.snapEdge}\n onSnapPointChange={props.onSnapPointChange}\n onSelect={props.onSelectClip}\n onCommitMove={props.onCommitMove}\n onCommitTrim={props.onCommitTrim}\n onCommitText={props.onCommitText}\n />\n ))}\n </div>\n </div>\n )\n}\n","/**\n * Zoom slider mapped through `ZoomMath` so equal slider travel feels like\n * equal zoom ratio. The slider's numeric domain is derived from the math\n * itself (`zoomToSlider(minZoom)..zoomToSlider(maxZoom)`) — this component\n * never assumes what scale the engine chose.\n */\n\nimport type { ZoomMath } from '../contracts'\n\nexport interface ZoomControlProps {\n zoomMath: ZoomMath\n zoom: number\n onZoomChange(zoom: number): void\n}\n\nexport function ZoomControl({ zoomMath, zoom, onZoomChange }: ZoomControlProps) {\n const sliderMin = zoomMath.zoomToSlider(zoomMath.minZoom)\n const sliderMax = zoomMath.zoomToSlider(zoomMath.maxZoom)\n const sliderStep = (sliderMax - sliderMin) / 100\n const slider = zoomMath.zoomToSlider(zoom)\n\n function setSlider(next: number) {\n const clamped = Math.max(sliderMin, Math.min(sliderMax, next))\n onZoomChange(zoomMath.sliderToZoom(clamped))\n }\n\n return (\n <div className=\"flex items-center gap-1.5\">\n <button\n type=\"button\"\n aria-label=\"Zoom out\"\n onClick={() => setSlider(slider - sliderStep * 10)}\n className=\"flex h-6 w-6 items-center justify-center rounded border border-[var(--border-default)] text-sm leading-none text-[var(--text-secondary)] hover:text-[var(--text-primary)]\"\n >\n −\n </button>\n <input\n type=\"range\"\n aria-label=\"Timeline zoom\"\n min={sliderMin}\n max={sliderMax}\n step={sliderStep}\n value={slider}\n onChange={(event) => setSlider(Number(event.target.value))}\n className=\"h-1 w-24 cursor-pointer accent-[var(--brand-primary)]\"\n />\n <button\n type=\"button\"\n aria-label=\"Zoom in\"\n onClick={() => setSlider(slider + sliderStep * 10)}\n className=\"flex h-6 w-6 items-center justify-center rounded border border-[var(--border-default)] text-sm leading-none text-[var(--text-secondary)] hover:text-[var(--text-primary)]\"\n >\n +\n </button>\n </div>\n )\n}\n"],"mappings":"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","names":["useEffect","useMemo","useRef","useState","requestPaint","jsx","jsx","jsxs","useMemo","jsx","useMemo","useEffect","useRef","useState","jsx","jsxs","useRef","useState","snapped","clamped","useEffect","Fragment","jsx","jsxs","jsx","jsxs","jsx","jsxs","Fragment","jsx","jsxs","useMemo","useRef","useEffect","useState"]}