@tangle-network/agent-app 0.11.1 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/DesignCanvas-3JEEIT6Y.js +10 -0
  2. package/dist/DesignCanvas-3JEEIT6Y.js.map +1 -0
  3. package/dist/DesignCanvasEditor-37LPJIIR.js +9 -0
  4. package/dist/DesignCanvasEditor-37LPJIIR.js.map +1 -0
  5. package/dist/TimelineEditor-OXPJZDP2.js +12 -0
  6. package/dist/TimelineEditor-OXPJZDP2.js.map +1 -0
  7. package/dist/apply-Cp8c3K9D.d.ts +249 -0
  8. package/dist/chunk-2Q73HGDI.js +1743 -0
  9. package/dist/chunk-2Q73HGDI.js.map +1 -0
  10. package/dist/chunk-6UOE5CTA.js +1647 -0
  11. package/dist/chunk-6UOE5CTA.js.map +1 -0
  12. package/dist/chunk-7QCIYDGC.js +1119 -0
  13. package/dist/chunk-7QCIYDGC.js.map +1 -0
  14. package/dist/chunk-A76ZHWNF.js +194 -0
  15. package/dist/chunk-A76ZHWNF.js.map +1 -0
  16. package/dist/chunk-ABGSFUJQ.js +111 -0
  17. package/dist/chunk-ABGSFUJQ.js.map +1 -0
  18. package/dist/{chunk-4YTWB5MG.js → chunk-ETX4O4BB.js} +98 -1
  19. package/dist/chunk-ETX4O4BB.js.map +1 -0
  20. package/dist/chunk-F5KTWRO7.js +2276 -0
  21. package/dist/chunk-F5KTWRO7.js.map +1 -0
  22. package/dist/chunk-IHR6K3GF.js +2367 -0
  23. package/dist/chunk-IHR6K3GF.js.map +1 -0
  24. package/dist/chunk-JZAJE3JL.js +990 -0
  25. package/dist/chunk-JZAJE3JL.js.map +1 -0
  26. package/dist/chunk-ZYBWGSAZ.js +130 -0
  27. package/dist/chunk-ZYBWGSAZ.js.map +1 -0
  28. package/dist/design-canvas/drizzle.d.ts +569 -0
  29. package/dist/design-canvas/drizzle.js +183 -0
  30. package/dist/design-canvas/drizzle.js.map +1 -0
  31. package/dist/design-canvas/index.d.ts +261 -0
  32. package/dist/design-canvas/index.js +96 -0
  33. package/dist/design-canvas/index.js.map +1 -0
  34. package/dist/design-canvas-react/index.d.ts +916 -0
  35. package/dist/design-canvas-react/index.js +423 -0
  36. package/dist/design-canvas-react/index.js.map +1 -0
  37. package/dist/export-presets-Dl5Aa5xj.d.ts +284 -0
  38. package/dist/index.d.ts +11 -2
  39. package/dist/index.js +224 -6
  40. package/dist/mcp-CIupfjxV.d.ts +112 -0
  41. package/dist/mcp-rpc-DLw_r9PQ.d.ts +55 -0
  42. package/dist/model-BHLN208Z.d.ts +183 -0
  43. package/dist/runtime/index.d.ts +108 -1
  44. package/dist/runtime/index.js +7 -1
  45. package/dist/sequences/drizzle.d.ts +1244 -0
  46. package/dist/sequences/drizzle.js +368 -0
  47. package/dist/sequences/drizzle.js.map +1 -0
  48. package/dist/sequences/index.d.ts +331 -0
  49. package/dist/sequences/index.js +114 -0
  50. package/dist/sequences/index.js.map +1 -0
  51. package/dist/sequences-react/index.d.ts +752 -0
  52. package/dist/sequences-react/index.js +241 -0
  53. package/dist/sequences-react/index.js.map +1 -0
  54. package/dist/store-CUStmtdH.d.ts +64 -0
  55. package/dist/store-gckrNq-g.d.ts +242 -0
  56. package/dist/tools/index.d.ts +25 -108
  57. package/dist/tools/index.js +16 -6
  58. package/package.json +62 -2
  59. package/dist/chunk-4YTWB5MG.js.map +0 -1
  60. package/dist/chunk-OLCVUGGI.js +0 -137
  61. package/dist/chunk-OLCVUGGI.js.map +0 -1
@@ -0,0 +1,752 @@
1
+ import { c as SequenceOperation, b as SequenceApplyResult } from '../apply-Cp8c3K9D.js';
2
+ import { o as SequenceTimeline, S as SequenceClip, p as SequenceTrack } from '../store-gckrNq-g.js';
3
+ import * as react from 'react';
4
+ import react__default from 'react';
5
+
6
+ /**
7
+ * Seams between the timeline editor's engine, media pipeline, and components.
8
+ * Everything here is interface-only so the three layers build independently
9
+ * and products can substitute any layer (e.g. a different frame provider)
10
+ * without forking the editor.
11
+ *
12
+ * Persistence model: the command stack executes optimistically against local
13
+ * timeline state and emits the equivalent `SequenceOperation[]` through
14
+ * `TimelineEditorProps.onApplyOperations`. A rejected promise rolls the
15
+ * command back — the server stays the source of truth, the editor stays
16
+ * responsive.
17
+ */
18
+
19
+ /** One undoable edit. `execute`/`undo` mutate ONLY local editor state; the
20
+ * emitted operations are the durable form sent to the product. */
21
+ interface TimelineCommand {
22
+ label: string;
23
+ execute(state: EditorTimelineState): EditorTimelineState;
24
+ undo(state: EditorTimelineState): EditorTimelineState;
25
+ /** Durable operations equivalent to `execute` (sent on commit). */
26
+ operations(): SequenceOperation[];
27
+ /** Durable operations equivalent to `undo` (sent when the user undoes a
28
+ * committed command). */
29
+ inverseOperations(): SequenceOperation[];
30
+ }
31
+ /** Local editor state — the timeline plus volatile view state the server
32
+ * never sees. */
33
+ interface EditorTimelineState {
34
+ timeline: SequenceTimeline;
35
+ playheadFrame: number;
36
+ selectedClipIds: string[];
37
+ zoom: number;
38
+ scrollLeft: number;
39
+ }
40
+ interface CommandStack {
41
+ execute(command: TimelineCommand): void;
42
+ undo(): void;
43
+ redo(): void;
44
+ canUndo(): boolean;
45
+ canRedo(): boolean;
46
+ /** Subscribe to state changes; returns an unsubscribe. */
47
+ subscribe(listener: () => void): () => void;
48
+ getState(): EditorTimelineState;
49
+ /** Replace timeline state from a server refresh WITHOUT clearing history. */
50
+ reset(timeline: SequenceTimeline): void;
51
+ }
52
+ /** Exponential zoom mapping so the slider feels linear across a 10x+ range.
53
+ * zoom = pixels per frame. */
54
+ interface ZoomMath {
55
+ sliderToZoom(slider: number): number;
56
+ zoomToSlider(zoom: number): number;
57
+ minZoom: number;
58
+ maxZoom: number;
59
+ }
60
+ interface SnapPoint {
61
+ frame: number;
62
+ kind: 'clip-start' | 'clip-end' | 'playhead' | 'sequence-end';
63
+ }
64
+ interface SnapResult {
65
+ frame: number;
66
+ snapped: boolean;
67
+ point: SnapPoint | null;
68
+ }
69
+ /** rAF-driven playback clock. `performance.now()` deltas drive the playhead;
70
+ * emits one callback per animation frame while playing. */
71
+ interface PlaybackClock {
72
+ play(): void;
73
+ pause(): void;
74
+ seek(frame: number): void;
75
+ isPlaying(): boolean;
76
+ getFrame(): number;
77
+ subscribe(listener: (frame: number) => void): () => void;
78
+ dispose(): void;
79
+ }
80
+ /** Supplies decoded frames for preview rendering. The baseline implementation
81
+ * seeks an off-DOM HTMLVideoElement (works everywhere); a WebCodecs
82
+ * implementation can replace it behind the same seam. */
83
+ interface VideoFrameProvider {
84
+ /** Draw the frame at `sourceSeconds` into `ctx` at the given rect. Resolves
85
+ * when the frame is painted; rejects when the media cannot be decoded. */
86
+ drawFrame(mediaUrl: string, sourceSeconds: number, ctx: CanvasRenderingContext2D, rect: {
87
+ x: number;
88
+ y: number;
89
+ width: number;
90
+ height: number;
91
+ }): Promise<void>;
92
+ /** Pre-warm a media URL so the first `drawFrame` doesn't stall. */
93
+ prefetch(mediaUrl: string): void;
94
+ dispose(): void;
95
+ }
96
+ /** min/max sample peaks per pixel bucket for waveform rendering. */
97
+ interface WaveformData {
98
+ peaks: Float32Array;
99
+ /** Samples represented per peak pair. */
100
+ samplesPerBucket: number;
101
+ durationSeconds: number;
102
+ }
103
+ interface TranscriptionSegment {
104
+ text: string;
105
+ startSeconds: number;
106
+ endSeconds: number;
107
+ }
108
+ /** Whisper-in-a-worker contract. Implementations dynamically import the model
109
+ * runtime; `available` is false when the optional peer is not installed. */
110
+ interface TranscriptionProvider {
111
+ available: boolean;
112
+ transcribe(mediaUrl: string, opts?: {
113
+ language?: string;
114
+ onProgress?: (fraction: number) => void;
115
+ }): Promise<TranscriptionSegment[]>;
116
+ }
117
+ interface TimelineEditorProps {
118
+ timeline: SequenceTimeline;
119
+ canWrite: boolean;
120
+ /** Persist operations the user produced. Reject to roll the edit back.
121
+ * Resolve with the per-operation `SequenceApplyResult[]` (what
122
+ * `applySequenceOperations` returns, index-aligned with `operations`) so
123
+ * the editor can reconcile its optimistic `local-…` clip ids to the
124
+ * server-minted ids — without it, undoing a committed place/split/caption
125
+ * after a timeline refresh emits operations the server cannot resolve.
126
+ * Resolving void skips reconciliation. */
127
+ onApplyOperations(operations: SequenceOperation[]): Promise<SequenceApplyResult[] | void>;
128
+ /** Selection + playhead surface to the host (e.g. to attach agent context). */
129
+ onSelectionChange?(clips: SequenceClip[]): void;
130
+ onPlayheadChange?(frame: number): void;
131
+ /** Host-rendered side panel (agent chat) and shelf (draggable assets). */
132
+ renderSidePanel?(ctx: {
133
+ selectedClips: SequenceClip[];
134
+ playheadFrame: number;
135
+ }): React.ReactNode;
136
+ renderAssetShelf?(): React.ReactNode;
137
+ /** Frame provider override; omitted → the baseline HTMLVideoElement provider. */
138
+ frameProvider?: VideoFrameProvider;
139
+ className?: string;
140
+ }
141
+
142
+ /**
143
+ * Undo/redo command stack over immutable `EditorTimelineState`.
144
+ *
145
+ * Invariant that makes `reset()` safe: history entries hold COMMANDS — each a
146
+ * pair of state transforms plus the durable operations captured at
147
+ * construction — never state snapshots. Rebasing the timeline from a server
148
+ * refresh therefore cannot stale the history: a later undo re-applies the
149
+ * command's inverse transform to whatever timeline is current. If a rebase
150
+ * removed a clip a historical command targets, that command's transform throws
151
+ * (fail loud) rather than silently editing the wrong clip — the host decides
152
+ * whether to drop history at that point.
153
+ */
154
+
155
+ /** Oldest entries are dropped past this bound; redo is cleared on execute. */
156
+ declare const COMMAND_HISTORY_LIMIT = 200;
157
+ declare function createCommandStack(initial: SequenceTimeline): CommandStack;
158
+
159
+ /**
160
+ * Concrete `TimelineCommand` factories. Every factory captures the inverse
161
+ * from PRE-state at construction — undo is a value computed once, never a
162
+ * re-derivation from whatever state exists later. Local execute/undo update
163
+ * `EditorTimelineState` immutably; `operations()`/`inverseOperations()` return
164
+ * the durable `SequenceOperation[]` equivalent, built per call from captured
165
+ * primitives so callers can never alias command internals.
166
+ *
167
+ * Id boundary: `place_clip`, `add_caption`, and `split_clip` mint clip ids
168
+ * server-side, so factories that create local clips take a caller-minted id.
169
+ * Every factory also takes an optional `resolveClipId` — a LIVE local→server
170
+ * alias lookup the host feeds from `onApplyOperations` results. Resolution
171
+ * runs at execute/undo/emission time (never at construction), so a command
172
+ * captured against an optimistic `local-…` id keeps working after a server
173
+ * refresh replaced it with the minted id. Residual boundary: clips a durable
174
+ * undo recreates (e.g. the `place_clip` inverse of a delete) mint FRESH
175
+ * server ids that only the next refresh reconciles.
176
+ *
177
+ * Transforms re-resolve their target clip at execute/undo time and throw when
178
+ * it no longer exists (e.g. removed by a `reset()` rebase) — editing a wrong
179
+ * or absent clip silently would corrupt the durable op stream.
180
+ */
181
+
182
+ /** Live local→server clip-id lookup (see module header). Must return its
183
+ * argument when no alias exists. */
184
+ type ClipIdResolver = (clipId: string) => string;
185
+ interface MoveClipInput {
186
+ timeline: SequenceTimeline;
187
+ clipId: string;
188
+ startFrame: number;
189
+ /** Omitted → stays on its current track. */
190
+ trackId?: string;
191
+ resolveClipId?: ClipIdResolver;
192
+ }
193
+ /** Drag-move. The target start clamps through the model's `clampClipStart`
194
+ * so drags past either edge land at the boundary instead of throwing
195
+ * mid-gesture; the emitted operation carries the clamped value. */
196
+ declare function moveClipCommand(input: MoveClipInput): TimelineCommand;
197
+ interface TrimClipInput {
198
+ timeline: SequenceTimeline;
199
+ clipId: string;
200
+ startFrame: number;
201
+ durationFrames: number;
202
+ /** New source in-point when trimming the head; omitted → unchanged. */
203
+ sourceInFrame?: number;
204
+ resolveClipId?: ClipIdResolver;
205
+ }
206
+ /** Trim is strict where move is forgiving: the caller (a trim handle) already
207
+ * knows both edges, so out-of-bounds input is a bug, not a gesture. */
208
+ declare function trimClipCommand(input: TrimClipInput): TimelineCommand;
209
+ interface PlaceClipInput {
210
+ timeline: SequenceTimeline;
211
+ /** Caller-minted optimistic id for the local clip (see module header). */
212
+ clipId: string;
213
+ trackId: string;
214
+ label: string;
215
+ startFrame: number;
216
+ durationFrames: number;
217
+ /** Omitted → 0 (start of the source). */
218
+ sourceInFrame?: number;
219
+ media?: {
220
+ url: string;
221
+ kind: 'video' | 'image' | 'audio';
222
+ };
223
+ generationId?: string;
224
+ assetId?: string;
225
+ metadata?: Record<string, unknown>;
226
+ resolveClipId?: ClipIdResolver;
227
+ }
228
+ declare function placeClipCommand(input: PlaceClipInput): TimelineCommand;
229
+ interface DeleteClipInput {
230
+ timeline: SequenceTimeline;
231
+ clipId: string;
232
+ resolveClipId?: ClipIdResolver;
233
+ }
234
+ /** Snapshots the full clip at construction so undo restores it exactly. The
235
+ * durable inverse rebuilds within the closed operation union: caption clips
236
+ * (caption track + text) invert through `add_caption` — `place_clip` cannot
237
+ * carry text/language — everything else through `place_clip` with media,
238
+ * product references, source window, and disabled state intact. Boundary:
239
+ * the caption path cannot carry `disabled` or `metadata`, so a deleted
240
+ * disabled caption resurrects ENABLED server-side and caption metadata is
241
+ * lost — local undo stays exact (same class of loss as the `set_clip_text`
242
+ * language-clear boundary). */
243
+ declare function deleteClipCommand(input: DeleteClipInput): TimelineCommand;
244
+ interface SplitClipInput {
245
+ timeline: SequenceTimeline;
246
+ clipId: string;
247
+ /** Sequence-frame to cut at; must fall strictly inside the clip. */
248
+ atFrame: number;
249
+ /** Caller-minted id for the second (tail) clip. */
250
+ newClipId: string;
251
+ resolveClipId?: ClipIdResolver;
252
+ }
253
+ /** Source mapping is 1:1 frames (no rate ramps in the model), so the tail's
254
+ * source in-point is the head's in-point advanced by the head duration. */
255
+ declare function splitClipCommand(input: SplitClipInput): TimelineCommand;
256
+ interface AddCaptionInput {
257
+ timeline: SequenceTimeline;
258
+ /** Caller-minted optimistic id for the local caption clip. */
259
+ clipId: string;
260
+ /** Editor commands are concrete: the caller resolves placement (e.g. via
261
+ * `chooseCaptionPlacement`) and the target track before constructing. */
262
+ trackId: string;
263
+ text: string;
264
+ language?: string;
265
+ startFrame: number;
266
+ durationFrames: number;
267
+ resolveClipId?: ClipIdResolver;
268
+ }
269
+ declare function addCaptionCommand(input: AddCaptionInput): TimelineCommand;
270
+ interface SetClipTextInput {
271
+ timeline: SequenceTimeline;
272
+ clipId: string;
273
+ text: string;
274
+ /** Omitted → language unchanged. */
275
+ language?: string;
276
+ resolveClipId?: ClipIdResolver;
277
+ }
278
+ /** Requires the clip to already carry text: `set_clip_text` has no "create"
279
+ * semantics in the union, and an inverse for a text-less clip would have to
280
+ * invent an empty string. Boundary: when the clip had NO language and this
281
+ * command sets one, local undo restores `undefined` exactly but the durable
282
+ * inverse cannot clear language (the op has no clear form) — flagged to the
283
+ * apply layer. */
284
+ declare function setClipTextCommand(input: SetClipTextInput): TimelineCommand;
285
+ interface ToggleClipDisabledInput {
286
+ timeline: SequenceTimeline;
287
+ clipId: string;
288
+ resolveClipId?: ClipIdResolver;
289
+ }
290
+ /** The target value is captured at construction (not flipped at execute time)
291
+ * so redo after a rebase applies the same durable op the stack already
292
+ * emitted. */
293
+ declare function toggleClipDisabledCommand(input: ToggleClipDisabledInput): TimelineCommand;
294
+
295
+ /**
296
+ * Zoom + viewport coordinate math. Zoom is PIXELS PER FRAME; the slider is a
297
+ * normalized [0, 1] control mapped exponentially (zoom = min·(max/min)^slider)
298
+ * so each slider step multiplies the scale by a constant factor — linear
299
+ * slider feel across a 10x+ range.
300
+ */
301
+
302
+ interface ZoomMathConfig {
303
+ minZoom: number;
304
+ maxZoom: number;
305
+ }
306
+ declare function createZoomMath(config: ZoomMathConfig): ZoomMath;
307
+ /** Horizontal viewport: zoom in pixels per frame, scrollLeft in pixels. */
308
+ interface ViewportTransform {
309
+ zoom: number;
310
+ scrollLeft: number;
311
+ }
312
+ /** Frame → viewport-relative pixel x. Output is fractional; round through
313
+ * `snapPixel` before drawing. */
314
+ declare function frameToPixel(frame: number, view: ViewportTransform): number;
315
+ /** Viewport-relative pixel x → integer frame. Frames are integer positions,
316
+ * so the result rounds to the nearest frame and floors at 0 (a pointer left
317
+ * of frame 0 resolves to 0). */
318
+ declare function pixelToFrame(pixel: number, view: ViewportTransform): number;
319
+ /** Snap a CSS-pixel value to the device pixel grid so 1px timeline rules
320
+ * render crisp on fractional-DPR displays. */
321
+ declare function snapPixel(value: number, devicePixelRatio: number): number;
322
+
323
+ /**
324
+ * Drag snapping. Snap points come from the timeline's structure (clip edges,
325
+ * playhead, sequence end); the snap THRESHOLD is measured in screen pixels at
326
+ * the current zoom — what feels "close" is a screen distance, not a frame
327
+ * count — and converts to frames as thresholdPx / zoom.
328
+ */
329
+
330
+ /** Snap point with its owning clip when it came from one, so a drag can
331
+ * exclude the dragged clip's own edges. Structurally a `SnapPoint`. */
332
+ interface TimelineSnapPoint extends SnapPoint {
333
+ clipId?: string;
334
+ }
335
+ /** Disabled clips still occupy timeline space visually, so their edges remain
336
+ * snap targets. */
337
+ declare function collectSnapPoints(timeline: SequenceTimeline, playheadFrame: number): TimelineSnapPoint[];
338
+ interface ApplySnapOptions {
339
+ /** Pixels per frame — converts the pixel threshold into frames. */
340
+ zoom: number;
341
+ /** Screen-distance threshold; 10px matches the editor's hit-slop. */
342
+ thresholdPx?: number;
343
+ /** Return true to remove a point from consideration (e.g. the dragged
344
+ * clip's own edges via `TimelineSnapPoint.clipId`). */
345
+ exclude?: (point: SnapPoint) => boolean;
346
+ }
347
+ /** Nearest candidate wins; ties keep the first candidate in `points` order
348
+ * (sorted by frame from `collectSnapPoints`, so the lower frame). */
349
+ declare function applySnap(frame: number, points: SnapPoint[], opts: ApplySnapOptions): SnapResult;
350
+
351
+ /**
352
+ * rAF playback clock. The playhead is derived from a (start time, start
353
+ * frame) anchor and `performance.now()` deltas — never from per-tick
354
+ * increments — so dropped animation frames cannot accumulate drift.
355
+ *
356
+ * Browser-only at PLAY time, import-safe everywhere: `requestAnimationFrame`
357
+ * and `performance` are resolved off `globalThis` when playback starts, never
358
+ * at module load or construction, so server bundles that import the editor
359
+ * engine do not crash.
360
+ */
361
+
362
+ interface PlaybackClockConfig {
363
+ fps: number;
364
+ durationFrames: number;
365
+ }
366
+ declare function createPlaybackClock(config: PlaybackClockConfig): PlaybackClock;
367
+
368
+ /**
369
+ * Baseline frame pipeline behind the `VideoFrameProvider` seam: an off-DOM
370
+ * HTMLVideoElement pool for video URLs and an HTMLImageElement pool for
371
+ * stills, merged by a per-URL kind dispatcher so the editor canvas never
372
+ * cares which kind a clip's media is. A WebCodecs implementation can replace
373
+ * this wholesale behind the same seam.
374
+ *
375
+ * Server-safe at import time, browser-only at call time: nothing touches
376
+ * `document` or `fetch` until a provider method runs.
377
+ */
378
+
379
+ declare const DEFAULT_MAX_MEDIA_ELEMENTS = 4;
380
+ /** Half a frame at 30fps. Seeks closer than this repaint the decoder's
381
+ * current frame instead of forcing a redundant seek. */
382
+ declare const SEEK_TOLERANCE_SECONDS: number;
383
+ /** A seek that hasn't fired `seeked` after this long is a decode failure —
384
+ * the draw REJECTS rather than painting whatever frame happens to be up. */
385
+ declare const SEEK_TIMEOUT_MS = 5000;
386
+ interface FrameRect {
387
+ x: number;
388
+ y: number;
389
+ width: number;
390
+ height: number;
391
+ }
392
+ /** Object-fit 'contain' placement: preserve aspect ratio, fit entirely inside
393
+ * `dest`, center the residual space. Callers own clearing the letterbox
394
+ * margins — this paints only the fitted region. */
395
+ declare function containFitRect(source: {
396
+ width: number;
397
+ height: number;
398
+ }, dest: FrameRect): FrameRect;
399
+ declare function needsSeek(currentTimeSeconds: number, targetSeconds: number): boolean;
400
+ interface PooledElementLease<T> {
401
+ element: T;
402
+ /** Unpins the element; idle elements become LRU-evictable. Idempotent. */
403
+ release(): void;
404
+ }
405
+ interface MediaElementPool<T> {
406
+ acquire(url: string): PooledElementLease<T>;
407
+ has(url: string): boolean;
408
+ size(): number;
409
+ dispose(): void;
410
+ }
411
+ /** LRU pool of media elements keyed by URL. Entries pinned by an outstanding
412
+ * lease are never evicted — a draw in flight must keep its element — so the
413
+ * pool can temporarily exceed `maxElements` under concurrent draws and
414
+ * shrinks back as leases release. */
415
+ declare function createMediaElementPool<T>(opts: {
416
+ maxElements: number;
417
+ create(url: string): T;
418
+ destroy(element: T, url: string): void;
419
+ }): MediaElementPool<T>;
420
+ /** Extension-based kind classification; 'unknown' defers to a HEAD
421
+ * content-type probe at draw time. */
422
+ declare function classifyMediaUrl(url: string): 'video' | 'image' | 'unknown';
423
+ /** Stills behind the same `VideoFrameProvider` seam — `sourceSeconds` is
424
+ * validated for contract parity but does not affect the painted pixels. */
425
+ declare function createImageFrameProvider(opts?: {
426
+ maxElements?: number;
427
+ }): VideoFrameProvider;
428
+ /** The baseline provider `TimelineEditorProps.frameProvider` defaults to.
429
+ * Video and image pools each hold up to `maxElements` entries. */
430
+ declare function createVideoElementFrameProvider(opts?: {
431
+ maxElements?: number;
432
+ }): VideoFrameProvider;
433
+
434
+ /**
435
+ * Waveform rendering for audio clips: bucketed max-abs peaks computed once
436
+ * per (media, zoom bucket count) and painted as mirrored bars around the
437
+ * track lane's midline. `computeWaveform` is pure so peak math is testable
438
+ * without Web Audio; `loadWaveform` is the browser edge that decodes real
439
+ * media into it.
440
+ */
441
+
442
+ /** Structural slice of Web Audio's AudioBuffer so peak math runs on synthetic
443
+ * fixtures in tests and on real decoded buffers in the browser. */
444
+ interface AudioBufferLike {
445
+ numberOfChannels: number;
446
+ length: number;
447
+ sampleRate: number;
448
+ duration: number;
449
+ getChannelData(channel: number): Float32Array;
450
+ }
451
+ /** One max-abs peak per bucket, taken across all channels. `peaks[b]` is the
452
+ * loudest absolute sample in bucket `b`; rendering mirrors it around the
453
+ * midline, which is what the contract's "peak pair" denotes. Buckets past
454
+ * the end of short audio hold 0. */
455
+ declare function computeWaveform(buffer: AudioBufferLike, bucketCount: number): WaveformData;
456
+ /** Fetch + decode `mediaUrl` and bucket it. Pass `ctx` to reuse a shared
457
+ * AudioContext; otherwise one is created and closed around the decode. */
458
+ declare function loadWaveform(mediaUrl: string, bucketCount: number, ctx?: AudioContext): Promise<WaveformData>;
459
+ /** Paint mirrored peak bars centered on the rect's midline. Silent buckets
460
+ * still paint a 1px hairline so the lane reads as audio, not as empty;
461
+ * peaks beyond ±1.0 (hot masters) clip to the full lane height. */
462
+ declare function drawWaveform(ctx: CanvasRenderingContext2D, data: WaveformData, rect: {
463
+ x: number;
464
+ y: number;
465
+ width: number;
466
+ height: number;
467
+ }, color: string): void;
468
+
469
+ /**
470
+ * Whisper transcription behind the `TranscriptionProvider` seam, powered by
471
+ * the OPTIONAL peer `@huggingface/transformers`. The peer is loaded with a
472
+ * dynamic variable-specifier import inside `transcribe()` so bundlers leave
473
+ * it external and apps that never transcribe never pay for it. `available`
474
+ * is the sync UI affordance signal; `transcribe()` re-verifies with the real
475
+ * import and is the source of truth.
476
+ */
477
+
478
+ declare const DEFAULT_WHISPER_MODEL = "onnx-community/whisper-large-v3-turbo";
479
+ interface WhisperChunk {
480
+ text: string;
481
+ timestamp: [number, number | null];
482
+ }
483
+ interface WhisperOutput {
484
+ text: string;
485
+ chunks?: WhisperChunk[];
486
+ }
487
+ /** Mean-mixdown to mono. Single-channel buffers return the live channel data
488
+ * without copying — callers must treat the result as read-only. */
489
+ declare function mixdownToMono(buffer: AudioBufferLike): Float32Array;
490
+ /** Map whisper chunk output to contract segments. A `null` end timestamp is
491
+ * whisper's "ran past the end of the audio" sentinel on the final chunk and
492
+ * resolves to the audio duration. */
493
+ declare function mapWhisperOutput(output: WhisperOutput | WhisperOutput[], durationSeconds: number, mediaUrl: string): TranscriptionSegment[];
494
+ declare function createWhisperTranscriptionProvider(opts?: {
495
+ model?: string;
496
+ }): TranscriptionProvider;
497
+
498
+ declare const SEQUENCE_MEDIA_DRAG_TYPE = "application/x-sequence-media";
499
+ declare function TimelineEditor(props: TimelineEditorProps): react.JSX.Element;
500
+
501
+ interface PreviewCanvasProps {
502
+ timeline: SequenceTimeline;
503
+ clock: PlaybackClock;
504
+ frameProvider: VideoFrameProvider;
505
+ className?: string;
506
+ }
507
+ declare function PreviewCanvas({ timeline, clock, frameProvider, className }: PreviewCanvasProps): react.JSX.Element;
508
+
509
+ /**
510
+ * Adaptive timecode ruler. Tick density follows zoom through
511
+ * `selectTickStepSeconds` (major ticks never closer than ~80px, minor ticks
512
+ * at a fifth of the major step when they'd sit at least 8px apart). Click or
513
+ * drag scrubs: the pointer is captured, every move quantizes to a whole frame,
514
+ * and the frame is committed through `onScrub` (the editor routes it to
515
+ * `PlaybackClock.seek`).
516
+ */
517
+ interface TimelineRulerProps {
518
+ fps: number;
519
+ durationFrames: number;
520
+ /** Pixels per frame. */
521
+ zoom: number;
522
+ onScrub(frame: number): void;
523
+ }
524
+ declare function TimelineRuler({ fps, durationFrames, zoom, onScrub }: TimelineRulerProps): react.JSX.Element;
525
+
526
+ interface ClipMoveCommit {
527
+ clipId: string;
528
+ startFrame: number;
529
+ trackId: string;
530
+ }
531
+ interface ClipTrimCommit {
532
+ clipId: string;
533
+ startFrame: number;
534
+ durationFrames: number;
535
+ sourceInFrame: number;
536
+ }
537
+ interface TimelineClipChipProps {
538
+ clip: SequenceClip;
539
+ track: SequenceTrack;
540
+ fps: number;
541
+ /** Pixels per frame. */
542
+ zoom: number;
543
+ sequenceDurationFrames: number;
544
+ selected: boolean;
545
+ canWrite: boolean;
546
+ frameProvider: VideoFrameProvider;
547
+ /** Snap a candidate move (both clip edges considered); editor closes over
548
+ * the engine's snap points. */
549
+ snapMove(candidate: {
550
+ startFrame: number;
551
+ durationFrames: number;
552
+ clipId: string;
553
+ }): {
554
+ startFrame: number;
555
+ point: SnapPoint | null;
556
+ };
557
+ /** Snap a single trim edge. */
558
+ snapEdge(candidate: {
559
+ frame: number;
560
+ clipId: string;
561
+ }): {
562
+ frame: number;
563
+ point: SnapPoint | null;
564
+ };
565
+ onSnapPointChange(point: SnapPoint | null): void;
566
+ onSelect(clipId: string, additive: boolean): void;
567
+ onCommitMove(input: ClipMoveCommit): void;
568
+ onCommitTrim(input: ClipTrimCommit): void;
569
+ onCommitText(input: {
570
+ clipId: string;
571
+ text: string;
572
+ }): void;
573
+ }
574
+ declare function TimelineClipChip(props: TimelineClipChipProps): react.JSX.Element;
575
+
576
+ interface TimelineTrackRowProps {
577
+ track: SequenceTrack;
578
+ clips: SequenceClip[];
579
+ fps: number;
580
+ zoom: number;
581
+ sequenceDurationFrames: number;
582
+ selectedClipIds: ReadonlySet<string>;
583
+ canWrite: boolean;
584
+ frameProvider: VideoFrameProvider;
585
+ snapMove(candidate: {
586
+ startFrame: number;
587
+ durationFrames: number;
588
+ clipId: string;
589
+ }): {
590
+ startFrame: number;
591
+ point: SnapPoint | null;
592
+ };
593
+ snapEdge(candidate: {
594
+ frame: number;
595
+ clipId: string;
596
+ }): {
597
+ frame: number;
598
+ point: SnapPoint | null;
599
+ };
600
+ onSnapPointChange(point: SnapPoint | null): void;
601
+ onSelectClip(clipId: string, additive: boolean): void;
602
+ onCommitMove(input: ClipMoveCommit): void;
603
+ onCommitTrim(input: ClipTrimCommit): void;
604
+ onCommitText(input: {
605
+ clipId: string;
606
+ text: string;
607
+ }): void;
608
+ onLaneSeek(frame: number): void;
609
+ }
610
+ declare function TimelineTrackRow(props: TimelineTrackRowProps): react.JSX.Element;
611
+
612
+ /**
613
+ * Playhead overlay for the track area: a full-height line with a triangular
614
+ * cap. Positioned in timeline pixels (frame * zoom) inside the scrolled
615
+ * content, so it moves with horizontal scroll for free. Pointer-transparent —
616
+ * scrubbing belongs to the ruler.
617
+ */
618
+ interface TimelinePlayheadProps {
619
+ frame: number;
620
+ zoom: number;
621
+ }
622
+ declare function TimelinePlayhead({ frame, zoom }: TimelinePlayheadProps): react.JSX.Element;
623
+
624
+ interface SnapIndicatorLineProps {
625
+ point: SnapPoint | null;
626
+ zoom: number;
627
+ }
628
+ declare function SnapIndicatorLine({ point, zoom }: SnapIndicatorLineProps): react.JSX.Element | null;
629
+
630
+ interface ZoomControlProps {
631
+ zoomMath: ZoomMath;
632
+ zoom: number;
633
+ onZoomChange(zoom: number): void;
634
+ }
635
+ declare function ZoomControl({ zoomMath, zoom, onZoomChange }: ZoomControlProps): react.JSX.Element;
636
+
637
+ /**
638
+ * Fuses several `TimelineCommand`s into one undo step so a single gesture
639
+ * (multi-select delete, drop that creates a track and places a clip) never
640
+ * fragments the history. Execute runs in order; undo runs in strict reverse so
641
+ * intermediate states reconstruct exactly.
642
+ */
643
+
644
+ declare function compositeCommand(label: string, commands: TimelineCommand[]): TimelineCommand;
645
+
646
+ /**
647
+ * Pure pointer-gesture math for the timeline editor. Every drag/trim/scrub
648
+ * gesture quantizes through these functions so the interactive behavior is
649
+ * unit-testable without a DOM. All inputs/outputs are integer frames except
650
+ * pixel deltas and zoom (px per frame), which are the only float-valued edge.
651
+ */
652
+
653
+ /** Quantize a horizontal pointer delta to whole frames at the current zoom. */
654
+ declare function framesFromPixelDelta(deltaX: number, zoom: number): number;
655
+ interface MoveDragInput {
656
+ originStartFrame: number;
657
+ durationFrames: number;
658
+ deltaFrames: number;
659
+ sequenceDurationFrames: number;
660
+ }
661
+ /** New start frame for a move drag, clamped so the clip stays fully inside
662
+ * the sequence. */
663
+ declare function moveDragStartFrame(input: MoveDragInput): number;
664
+ interface TrimStartDragInput {
665
+ originStartFrame: number;
666
+ originDurationFrames: number;
667
+ originSourceInFrame: number;
668
+ deltaFrames: number;
669
+ }
670
+ interface TrimStartDragResult {
671
+ startFrame: number;
672
+ durationFrames: number;
673
+ sourceInFrame: number;
674
+ }
675
+ /**
676
+ * Head trim: the clip END is invariant; start slides between two hard walls —
677
+ * it cannot reveal media before source frame 0 (sourceInFrame >= 0) and cannot
678
+ * pass within MIN_SEQUENCE_CLIP_FRAMES of the end. sourceInFrame shifts by
679
+ * exactly the start delta so the visible content stays anchored.
680
+ */
681
+ declare function trimStartDrag(input: TrimStartDragInput): TrimStartDragResult;
682
+ interface TrimEndDragInput {
683
+ originStartFrame: number;
684
+ originDurationFrames: number;
685
+ sourceInFrame: number;
686
+ deltaFrames: number;
687
+ sequenceDurationFrames: number;
688
+ /** Natural source length in frames when known; bounds how far the tail can
689
+ * extend. Omit for stills and media of unknown length. */
690
+ sourceDurationFrames?: number;
691
+ }
692
+ /** Tail trim: start is invariant; duration is bounded below by the minimum
693
+ * clip length and above by both the sequence end and the remaining source
694
+ * material past the in-point. */
695
+ declare function trimEndDrag(input: TrimEndDragInput): {
696
+ durationFrames: number;
697
+ };
698
+ /**
699
+ * Smallest ruler step whose major ticks sit at least `minSpacingPx` apart at
700
+ * the current zoom; past the table it grows in whole minutes so labels never
701
+ * collide at extreme zoom-out.
702
+ */
703
+ declare function selectTickStepSeconds(input: {
704
+ zoom: number;
705
+ fps: number;
706
+ minSpacingPx?: number;
707
+ }): number;
708
+ interface LetterboxRect {
709
+ x: number;
710
+ y: number;
711
+ width: number;
712
+ height: number;
713
+ }
714
+ /** Contain-fit a media aspect inside a container, centered with letterbox or
715
+ * pillarbox bars. */
716
+ declare function letterboxRect(input: {
717
+ containerWidth: number;
718
+ containerHeight: number;
719
+ mediaWidth: number;
720
+ mediaHeight: number;
721
+ }): LetterboxRect;
722
+ /** Caption type scales with the rendered frame, floored so captions stay
723
+ * legible on small previews. */
724
+ declare function captionFontPx(canvasCssHeight: number): number;
725
+ /** Pixel geometry for a clip chip; width floors at 2px so 1-frame clips stay
726
+ * grabbable. */
727
+ declare function clipChipGeometry(input: {
728
+ startFrame: number;
729
+ durationFrames: number;
730
+ zoom: number;
731
+ }): {
732
+ left: number;
733
+ width: number;
734
+ };
735
+ /**
736
+ * A move drag snaps whichever clip edge lands closest to a snap point: the
737
+ * start edge directly, or the end edge re-expressed as a start. An unsnapped
738
+ * candidate passes through unchanged.
739
+ */
740
+ declare function chooseMoveSnap(input: {
741
+ candidateStartFrame: number;
742
+ durationFrames: number;
743
+ startSnap: SnapResult;
744
+ endSnap: SnapResult;
745
+ }): {
746
+ startFrame: number;
747
+ point: SnapPoint | null;
748
+ };
749
+
750
+ declare const SequenceTimelineEditorLazy: react__default.LazyExoticComponent<typeof TimelineEditor>;
751
+
752
+ export { type AddCaptionInput, type ApplySnapOptions, type AudioBufferLike, COMMAND_HISTORY_LIMIT, type ClipIdResolver, type ClipMoveCommit, type ClipTrimCommit, type CommandStack, DEFAULT_MAX_MEDIA_ELEMENTS, DEFAULT_WHISPER_MODEL, type DeleteClipInput, type EditorTimelineState, type FrameRect, type LetterboxRect, type MediaElementPool, type MoveClipInput, type MoveDragInput, type PlaceClipInput, type PlaybackClock, type PlaybackClockConfig, type PooledElementLease, PreviewCanvas, type PreviewCanvasProps, SEEK_TIMEOUT_MS, SEEK_TOLERANCE_SECONDS, SEQUENCE_MEDIA_DRAG_TYPE, SequenceTimelineEditorLazy, type SetClipTextInput, SnapIndicatorLine, type SnapIndicatorLineProps, type SnapPoint, type SnapResult, type SplitClipInput, TimelineClipChip, type TimelineClipChipProps, type TimelineCommand, TimelineEditor, type TimelineEditorProps, TimelinePlayhead, type TimelinePlayheadProps, TimelineRuler, type TimelineRulerProps, type TimelineSnapPoint, TimelineTrackRow, type TimelineTrackRowProps, type ToggleClipDisabledInput, type TranscriptionProvider, type TranscriptionSegment, type TrimClipInput, type TrimEndDragInput, type TrimStartDragInput, type TrimStartDragResult, type VideoFrameProvider, type ViewportTransform, type WaveformData, type WhisperOutput, ZoomControl, type ZoomControlProps, type ZoomMath, type ZoomMathConfig, addCaptionCommand, applySnap, captionFontPx, chooseMoveSnap, classifyMediaUrl, clipChipGeometry, collectSnapPoints, compositeCommand, computeWaveform, containFitRect, createCommandStack, createImageFrameProvider, createMediaElementPool, createPlaybackClock, createVideoElementFrameProvider, createWhisperTranscriptionProvider, createZoomMath, deleteClipCommand, drawWaveform, frameToPixel, framesFromPixelDelta, letterboxRect, loadWaveform, mapWhisperOutput, mixdownToMono, moveClipCommand, moveDragStartFrame, needsSeek, pixelToFrame, placeClipCommand, selectTickStepSeconds, setClipTextCommand, snapPixel, splitClipCommand, toggleClipDisabledCommand, trimClipCommand, trimEndDrag, trimStartDrag };