@shotstack/shotstack-studio 2.0.0-beta.9 → 2.0.0-rc.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.
package/dist/index.d.ts CHANGED
@@ -1,177 +1,9 @@
1
- import { assetSchema as AssetSchema } from '@shotstack/schemas/zod';
2
- import { audioAssetSchema as AudioAssetSchema } from '@shotstack/schemas/zod';
3
- import { captionAssetSchema as CaptionAssetSchema } from '@shotstack/schemas/zod';
4
- import { clipSchema as ClipSchema } from '@shotstack/schemas/zod';
5
1
  import { components } from '@shotstack/schemas';
6
- import { Container } from 'pixi.js';
7
- import { cropSchema as CropSchema } from '@shotstack/schemas/zod';
8
- import { editSchema as EditSchema } from '@shotstack/schemas/zod';
9
- import { htmlAssetSchema as HtmlAssetSchema } from '@shotstack/schemas/zod';
10
- import { imageAssetSchema as ImageAssetSchema } from '@shotstack/schemas/zod';
11
- import { lumaAssetSchema as LumaAssetSchema } from '@shotstack/schemas/zod';
12
- import { offsetSchema as OffsetSchema } from '@shotstack/schemas/zod';
13
- import { outputSchema as OutputSchema } from '@shotstack/schemas/zod';
14
- import * as pixi from 'pixi.js';
15
- import { richTextAssetSchema as RichTextAssetSchema } from '@shotstack/schemas/zod';
16
- import { shapeAssetSchema as ShapeAssetSchema } from '@shotstack/schemas/zod';
17
- import { textAssetSchema as TextAssetSchema } from '@shotstack/schemas/zod';
18
- import { timelineSchema as TimelineSchema } from '@shotstack/schemas/zod';
19
- import { trackSchema as TrackSchema } from '@shotstack/schemas/zod';
20
- import { transformationSchema as TransformationSchema } from '@shotstack/schemas/zod';
21
- import { transitionSchema as TransitionSchema } from '@shotstack/schemas/zod';
22
- import { tweenSchema } from '@shotstack/schemas/zod';
23
- import { videoAssetSchema as VideoAssetSchema } from '@shotstack/schemas/zod';
24
- import { z } from 'zod';
25
-
26
- export declare type Asset = components["schemas"]["Asset"];
27
-
28
- declare type AssetEventMap = {
29
- onAssetLoadInfoUpdated: AssetLoadInfoUpdatedPayload;
30
- };
31
-
32
- declare class AssetLoader {
33
- private static readonly VIDEO_EXTENSIONS;
34
- private static readonly VIDEO_MIME;
35
- readonly loadTracker: AssetLoadTracker;
36
- /** Reference counts for loaded assets - prevents premature unloading during transforms */
37
- private refCounts;
38
- /**
39
- * Increment reference count for an asset.
40
- * Called when a player starts loading an asset.
41
- */
42
- incrementRef(src: string): void;
43
- /**
44
- * Decrement reference count for an asset.
45
- * @returns true if asset can be safely unloaded (count reached zero)
46
- */
47
- decrementRef(src: string): boolean;
48
- constructor();
49
- load<TResolvedAsset>(identifier: string, loadOptions: pixi.UnresolvedAsset): Promise<TResolvedAsset | null>;
50
- /**
51
- * Load a video with a unique HTMLVideoElement (not cached).
52
- * Each call creates an independent video element, allowing multiple VideoPlayers
53
- * to control playback independently even when using the same video URL.
54
- */
55
- loadVideoUnique(identifier: string, loadOptions: pixi.UnresolvedAsset): Promise<pixi.Texture<pixi.VideoSource> | null>;
56
- getProgress(): number;
57
- private extractUrl;
58
- private hasVideoExtension;
59
- private getContentType;
60
- private canPlayVideo;
61
- private isPlayableVideo;
62
- private shouldUseSafariVideoLoader;
63
- private loadVideoForSafari;
64
- private updateAssetLoadMetadata;
65
- }
66
-
67
- declare type AssetLoadInfo = {
68
- progress: number;
69
- status: AssetLoadInfoStatus;
70
- };
71
-
72
- declare type AssetLoadInfoStatus = "pending" | "loading" | "success" | "failed";
73
-
74
- declare type AssetLoadInfoUpdatedPayload = {
75
- registry: Record<string, AssetLoadInfo>;
76
- };
77
-
78
- declare class AssetLoadTracker extends EventEmitter<AssetEventMap> {
79
- registry: Record<string, AssetLoadInfo>;
80
- constructor();
81
- }
82
-
83
- export { AssetSchema }
84
-
85
- export declare class AssetToolbar {
86
- private container;
87
- private ui;
88
- private unsubscribe;
89
- constructor(ui: UIController);
90
- setPosition(screenX: number, screenY: number): void;
91
- mount(parent: HTMLElement): void;
92
- private render;
93
- private setupEventListeners;
94
- dispose(): void;
95
- }
96
-
97
- export declare type AudioAsset = components["schemas"]["AudioAsset"];
98
-
99
- export { AudioAssetSchema }
100
-
101
- declare type BackgroundChangeCallback = (color: string) => void;
102
-
103
- /**
104
- * Abstract base class for toolbars providing shared lifecycle, popup management,
105
- * and UI helper methods.
106
- */
107
- declare abstract class BaseToolbar {
108
- protected container: HTMLDivElement | null;
109
- protected edit: Edit;
110
- protected selectedTrackIdx: number;
111
- protected selectedClipIdx: number;
112
- protected clickOutsideHandler: ((e: MouseEvent) => void) | null;
113
- constructor(edit: Edit);
114
- /**
115
- * Mount the toolbar to a parent element.
116
- * Subclasses must implement to build their specific HTML structure.
117
- */
118
- abstract mount(parent: HTMLElement): void;
119
- /**
120
- * Show the toolbar for a specific clip.
121
- * Subclasses can override to add custom behavior.
122
- */
123
- show(trackIndex: number, clipIndex: number): void;
124
- /**
125
- * Hide the toolbar.
126
- */
127
- hide(): void;
128
- /**
129
- * Dispose the toolbar and clean up resources.
130
- * Subclasses should call super.dispose() and then null their own references.
131
- */
132
- dispose(): void;
133
- /**
134
- * Check if a popup is currently visible (handles both CSS class and inline style patterns).
135
- */
136
- protected isPopupOpen(popup: HTMLElement | null): boolean;
137
- /**
138
- * Toggle a popup's visibility, closing all others first.
139
- * Uses CSS class-based visibility. Optional callback fires when popup opens.
140
- */
141
- protected togglePopup(popup: HTMLElement | null, onOpen?: () => void): void;
142
- /**
143
- * Close all popups.
144
- * Uses CSS class-based visibility.
145
- */
146
- protected closeAllPopups(): void;
147
- /**
148
- * Set up a document click handler to close popups when clicking outside.
149
- */
150
- protected setupOutsideClickHandler(): void;
151
- /**
152
- * Create a slider input handler with value display update.
153
- */
154
- protected createSliderHandler(slider: HTMLInputElement | null, valueDisplay: HTMLSpanElement | null, callback: (value: number) => void, formatter?: (value: number) => string): void;
155
- /**
156
- * Set active state on a button element.
157
- */
158
- protected setButtonActive(btn: HTMLElement | null, active: boolean): void;
159
- /**
160
- * Sync UI state with current clip configuration.
161
- * Subclasses must implement.
162
- */
163
- protected abstract syncState(): void;
164
- /**
165
- * Get the list of popup elements for popup management.
166
- * Subclasses must implement.
167
- */
168
- protected abstract getPopupList(): (HTMLElement | null)[];
169
- }
170
2
 
171
3
  /**
172
4
  * Payload passed to button click handlers.
173
5
  */
174
- export declare interface ButtonClickPayload {
6
+ declare interface ButtonClickPayload {
175
7
  /** Current playback position in seconds */
176
8
  position: number;
177
9
  /** Currently selected clip, if any */
@@ -185,25 +17,25 @@ export declare class Canvas {
185
17
  private static extensionsRegistered;
186
18
  private viewportSize;
187
19
  private readonly edit;
188
- /** Container for interactive overlays (handles, guides). Renders above content. */
189
- readonly overlayContainer: pixi.Container;
20
+ private viewportContainer?;
21
+ private editBackground?;
22
+ private viewportMask?;
190
23
  private container?;
191
24
  private background?;
192
25
  private timeline?;
193
26
  private uiController;
27
+ private alignmentGuides;
194
28
  private minZoom;
195
29
  private maxZoom;
196
30
  private currentZoom;
197
31
  private onTickBound;
198
32
  private onBackgroundClickBound;
33
+ private onWheelBound;
34
+ private canvasRoot;
199
35
  constructor(edit: Edit);
200
- /**
201
- * Register a UIController to receive tick updates for canvas overlays.
202
- * @deprecated Use `new UIController(edit, canvas)` instead - auto-registers.
203
- */
204
- registerUIController(controller: UIController): void;
205
36
  load(): Promise<void>;
206
37
  private setupTouchHandling;
38
+ private onWheel;
207
39
  centerEdit(): void;
208
40
  zoomToFit(padding?: number): void;
209
41
  resize(): void;
@@ -215,192 +47,38 @@ export declare class Canvas {
215
47
  */
216
48
  private syncContentTransforms;
217
49
  /**
218
- * Get the pixel bounds of the canvas content (edit area) within the viewport.
219
- * Used for positioning toolbars adjacent to the canvas content.
50
+ * Subscribe to Edit events for visual synchronization.
51
+ * Canvas reacts to these events to update PIXI visuals.
220
52
  */
221
- getContentBounds(): {
222
- left: number;
223
- right: number;
224
- top: number;
225
- bottom: number;
226
- };
227
- registerTimeline(timeline: Timeline): void;
53
+ private subscribeToEditEvents;
54
+ private onPlayerAddedToTrack;
55
+ private onPlayerMovedBetweenTracks;
56
+ private onPlayerRemovedFromTrack;
57
+ private onTrackContainerRemoved;
58
+ private onViewportSizeChanged;
59
+ private onViewportNeedsZoomToFit;
228
60
  private registerExtensions;
229
61
  private configureApplication;
230
62
  private onTick;
231
63
  private configureStage;
232
64
  private onBackgroundClick;
233
- pauseTicker(): void;
234
- resumeTicker(): void;
235
- dispose(): void;
236
- }
237
-
238
- /**
239
- * Interface for PixiJS-based overlays that render on the canvas.
240
- * Used for interactive elements like selection handles, alignment guides, etc.
241
- */
242
- export declare interface CanvasOverlayRegistration {
243
- /** Mount to PixiJS container */
244
- mount(container: pixi.Container, app: pixi.Application): void;
245
- /** Called each frame to update state */
246
- update(deltaTime: number, elapsed: number): void;
247
- /** Called each frame to render */
248
- draw(): void;
249
- /** Clean up resources */
250
- dispose(): void;
251
- }
252
-
253
- export declare class CanvasToolbar {
254
- private container;
255
- private edit;
256
- private currentWidth;
257
- private currentHeight;
258
- private currentFps;
259
- private currentBgColor;
260
- private resolutionPopup;
261
- private backgroundPopup;
262
- private fpsPopup;
263
- private variablesPopup;
264
- private resolutionBtn;
265
- private backgroundBtn;
266
- private fpsBtn;
267
- private variablesBtn;
268
- private variablesList;
269
- private variablesEmpty;
270
- private resolutionLabel;
271
- private fpsLabel;
272
- private bgColorDot;
273
- private customWidthInput;
274
- private customHeightInput;
275
- private colorInput;
276
- private resolutionChangeCallback;
277
- private fpsChangeCallback;
278
- private backgroundChangeCallback;
279
- private clickOutsideHandler;
280
- private showMergeFields;
281
- constructor(edit?: Edit, options?: {
282
- mergeFields?: boolean;
283
- });
284
- setPosition(screenX: number, screenY: number): void;
285
- mount(parent: HTMLElement): void;
286
- private setupEventListeners;
287
- private togglePopup;
288
- private closeAllPopups;
289
- private handleResolutionSelect;
290
- private handleCustomSizeChange;
291
- private handleFpsSelect;
292
- private handleColorChange;
293
- private updateResolutionLabel;
294
- private updateFpsLabel;
295
- private updateColorPreview;
296
- private updateActiveStates;
297
- private renderVariablesList;
298
- private addVariable;
299
- setResolution(width: number, height: number): void;
300
- setFps(fps: number): void;
301
- setBackground(color: string): void;
302
- onResolutionChange(callback: ResolutionChangeCallback): void;
303
- onFpsChange(callback: FpsChangeCallback): void;
304
- onBackgroundChange(callback: BackgroundChangeCallback): void;
305
65
  dispose(): void;
306
66
  }
307
67
 
308
- export declare type CaptionAsset = components["schemas"]["CaptionAsset"];
68
+ declare type Clip = components["schemas"]["Clip"];
309
69
 
310
- export { CaptionAssetSchema }
311
-
312
- export declare type ChromaKey = components["schemas"]["ChromaKey"];
313
-
314
- export declare type Clip = components["schemas"]["Clip"];
315
-
316
- export declare type ClipAnchor = Clip["position"];
317
-
318
- /** Clip info for interactions */
319
- declare interface ClipInfo {
320
- trackIndex: number;
321
- clipIndex: number;
322
- config: ResolvedClip;
323
- }
324
-
325
- export declare type ClipLocation = {
70
+ declare type ClipLocation = {
326
71
  trackIndex: number;
327
72
  clipIndex: number;
328
73
  };
329
74
 
330
- export declare type ClipReference = ClipLocation & {
331
- clip: ResolvedClip;
332
- };
333
-
334
- /** Custom clip renderer interface */
335
- declare interface ClipRenderer {
336
- /** Render custom content inside clip element */
337
- render(clip: ResolvedClip, element: HTMLElement): void;
338
- /** Optional cleanup when clip is removed */
339
- dispose?(element: HTMLElement): void;
340
- }
341
-
342
- export { ClipSchema }
343
-
344
- declare interface ClipStats {
345
- videos: number;
346
- images: number;
347
- text: number;
348
- richText: number;
349
- luma: number;
350
- animatedClips: number;
351
- cachedFrames: number;
352
- }
353
-
354
75
  /**
355
- * Toolbar for clip-level properties (timing, linking).
356
- * Shows compact timing controls for start and length with:
357
- * - Click-to-cycle mode badges
358
- * - Scrubbable time values (drag to adjust)
359
- * - Keyboard increment/decrement (arrow keys)
76
+ * Reference to a clip from the document (source of truth).
77
+ * Contains original timing values like "auto", "end", and alias references.
78
+ * Used in public SDK events so consumers see the document state.
360
79
  */
361
- export declare class ClipToolbar extends BaseToolbar {
362
- private startControl;
363
- private lengthControl;
364
- mount(parent: HTMLElement): void;
365
- private mountComponents;
366
- private applyTimingUpdate;
367
- protected syncState(): void;
368
- protected getPopupList(): (HTMLElement | null)[];
369
- dispose(): void;
370
- }
371
-
372
- declare type ClipType = ResolvedClip;
373
-
374
- declare type CommandContext = {
375
- getClips(): Player[];
376
- getTracks(): Player[][];
377
- getTrack(trackIndex: number): Player[] | null;
378
- getContainer(): Container;
379
- addPlayer(trackIdx: number, player: Player): Promise<void>;
380
- addPlayerToContainer(trackIdx: number, player: Player): void;
381
- createPlayerFromAssetType(clipConfiguration: ClipType): Player;
382
- queueDisposeClip(player: Player): void;
383
- disposeClips(): void;
384
- clearClipError(trackIdx: number, clipIdx: number): void;
385
- undeleteClip(trackIdx: number, clip: Player): void;
386
- setUpdatedClip(clip: Player): void;
387
- restoreClipConfiguration(clip: Player, previousConfig: ClipType): void;
388
- updateDuration(): void;
389
- emitEvent<T extends EditEventName>(name: T, ...args: EditEventMap[T] extends void ? [] : [EditEventMap[T]]): void;
390
- findClipIndices(player: Player): {
391
- trackIndex: number;
392
- clipIndex: number;
393
- } | null;
394
- getClipAt(trackIndex: number, clipIndex: number): Player | null;
395
- getSelectedClip(): Player | null;
396
- setSelectedClip(clip: Player | null): void;
397
- movePlayerToTrackContainer(player: Player, fromTrackIdx: number, toTrackIdx: number): void;
398
- getEditState(): EditType;
399
- propagateTimingChanges(trackIndex: number, startFromClipIndex: number): void;
400
- resolveClipAutoLength(clip: Player): Promise<void>;
401
- untrackEndLengthClip(clip: Player): void;
402
- trackEndLengthClip(clip: Player): void;
403
- getMergeFields(): MergeFieldService;
80
+ declare type ClipReference = ClipLocation & {
81
+ clip: Clip;
404
82
  };
405
83
 
406
84
  export declare class Controls {
@@ -415,547 +93,150 @@ export declare class Controls {
415
93
  private handleKeyUp;
416
94
  }
417
95
 
418
- export declare type Crop = components["schemas"]["Crop"];
419
-
420
- export { CropSchema }
421
-
422
- export declare type Destination = components["schemas"]["Destinations"];
423
-
424
- export declare const DestinationSchema: z.ZodUnion<readonly [z.ZodIntersection<z.ZodObject<{
425
- destinations: z.ZodOptional<z.ZodLiteral<"shotstackDestination_ShotstackDestination">>;
426
- }, z.core.$strip>, z.ZodObject<{
427
- provider: z.ZodDefault<z.ZodString>;
428
- exclude: z.ZodOptional<z.ZodBoolean>;
429
- }, z.core.$strip>>, z.ZodIntersection<z.ZodObject<{
430
- destinations: z.ZodOptional<z.ZodLiteral<"muxDestination_MuxDestination">>;
431
- }, z.core.$strip>, z.ZodObject<{
432
- provider: z.ZodDefault<z.ZodString>;
433
- options: z.ZodOptional<z.ZodObject<{
434
- playbackPolicy: z.ZodOptional<z.ZodArray<z.ZodEnum<{
435
- public: "public";
436
- signed: "signed";
437
- }>>>;
438
- passthrough: z.ZodOptional<z.ZodString>;
439
- }, z.core.$strip>>;
440
- }, z.core.$strip>>, z.ZodIntersection<z.ZodObject<{
441
- destinations: z.ZodOptional<z.ZodLiteral<"s3Destination_S3Destination">>;
442
- }, z.core.$strip>, z.ZodObject<{
443
- provider: z.ZodDefault<z.ZodString>;
444
- options: z.ZodOptional<z.ZodObject<{
445
- region: z.ZodString;
446
- bucket: z.ZodString;
447
- prefix: z.ZodOptional<z.ZodString>;
448
- filename: z.ZodOptional<z.ZodString>;
449
- acl: z.ZodOptional<z.ZodString>;
450
- }, z.core.$strip>>;
451
- }, z.core.$strip>>, z.ZodIntersection<z.ZodObject<{
452
- destinations: z.ZodOptional<z.ZodLiteral<"googleCloudStorageDestination_GoogleCloudStorageDestination">>;
453
- }, z.core.$strip>, z.ZodObject<{
454
- provider: z.ZodDefault<z.ZodString>;
455
- options: z.ZodOptional<z.ZodObject<{
456
- bucket: z.ZodString;
457
- prefix: z.ZodOptional<z.ZodString>;
458
- filename: z.ZodOptional<z.ZodString>;
459
- }, z.core.$strip>>;
460
- }, z.core.$strip>>, z.ZodIntersection<z.ZodObject<{
461
- destinations: z.ZodOptional<z.ZodLiteral<"googleDriveDestination_GoogleDriveDestination">>;
462
- }, z.core.$strip>, z.ZodObject<{
463
- provider: z.ZodDefault<z.ZodString>;
464
- options: z.ZodObject<{
465
- folderId: z.ZodString;
466
- filename: z.ZodOptional<z.ZodString>;
467
- }, z.core.$strip>;
468
- }, z.core.$strip>>, z.ZodIntersection<z.ZodObject<{
469
- destinations: z.ZodOptional<z.ZodLiteral<"vimeoDestination_VimeoDestination">>;
470
- }, z.core.$strip>, z.ZodObject<{
471
- provider: z.ZodDefault<z.ZodString>;
472
- options: z.ZodOptional<z.ZodObject<{
473
- name: z.ZodOptional<z.ZodString>;
474
- description: z.ZodOptional<z.ZodString>;
475
- privacy: z.ZodOptional<z.ZodObject<{
476
- view: z.ZodOptional<z.ZodEnum<{
477
- anybody: "anybody";
478
- nobody: "nobody";
479
- contacts: "contacts";
480
- password: "password";
481
- unlisted: "unlisted";
482
- }>>;
483
- embed: z.ZodOptional<z.ZodEnum<{
484
- public: "public";
485
- private: "private";
486
- whitelist: "whitelist";
487
- }>>;
488
- comments: z.ZodOptional<z.ZodEnum<{
489
- anybody: "anybody";
490
- nobody: "nobody";
491
- contacts: "contacts";
492
- }>>;
493
- }, z.core.$strip>>;
494
- folderUri: z.ZodOptional<z.ZodString>;
495
- }, z.core.$strip>>;
496
- }, z.core.$strip>>, z.ZodIntersection<z.ZodObject<{
497
- destinations: z.ZodOptional<z.ZodLiteral<"tiktokDestination_TiktokDestination">>;
498
- }, z.core.$strip>, z.ZodObject<{
499
- provider: z.ZodDefault<z.ZodString>;
500
- options: z.ZodOptional<z.ZodObject<{
501
- title: z.ZodOptional<z.ZodString>;
502
- privacyLevel: z.ZodOptional<z.ZodEnum<{
503
- public: "public";
504
- friends: "friends";
505
- private: "private";
506
- }>>;
507
- disableDuet: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
508
- disableStitch: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
509
- disableComment: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
510
- }, z.core.$strip>>;
511
- }, z.core.$strip>>]>;
96
+ declare type Destination = components["schemas"]["Destinations"];
512
97
 
513
- export declare class Edit extends Entity {
514
- private static readonly ZIndexPadding;
515
- assetLoader: AssetLoader;
516
- events: EventEmitter<EditEventMap & InternalEventMap>;
517
- private edit;
98
+ export declare class Edit {
99
+ private static readonly MAX_HISTORY_SIZE;
100
+ private document;
101
+ private backgroundColor;
518
102
  private tracks;
519
- private clipsToDispose;
520
- private clips;
103
+ playbackTime: Seconds;
104
+ totalDuration: Seconds;
105
+ isPlaying: boolean;
106
+ private get clips();
107
+ private internalEvents;
108
+ events: ReadonlyEventEmitter<EditEventMap>;
109
+ private canvas;
110
+ private timingManager;
111
+ private lumaMaskController;
112
+ private playerReconciler;
113
+ private outputSettings;
114
+ private selectionManager;
521
115
  private commandHistory;
522
116
  private commandIndex;
523
- playbackTime: number;
524
- totalDuration: number;
525
- private cachedTimelineEnd;
526
- private endLengthClips;
117
+ private commandQueue;
118
+ private clipsToDispose;
119
+ private clipErrors;
120
+ private playerByClipId;
121
+ private lumaContentRelations;
122
+ private fontMetadata;
527
123
  private isBatchingEvents;
528
- private isLoadingEdit;
529
124
  private syncCorrectionCount;
530
- private toolbarButtons;
531
- /** Merge field service for managing dynamic content placeholders */
532
- mergeFields: MergeFieldService;
533
- private canvas;
534
- private lumaMaskController;
535
- private clipErrors;
125
+ private isExporting;
126
+ private lastResolved;
536
127
  /**
537
128
  * Create an Edit instance from a template configuration.
538
- *
539
- * @param template - The Edit JSON configuration (aligns with Shotstack API contract)
540
- *
541
- * @example
542
- * ```typescript
543
- * const edit = new Edit(template);
544
- * await edit.load();
545
- *
546
- * const canvas = new Canvas(edit);
547
- * await canvas.load();
548
- * ```
549
129
  */
550
130
  constructor(template: Edit_2);
131
+ /**
132
+ * Load the edit session.
133
+ */
551
134
  load(): Promise<void>;
552
135
  /**
553
- * Initialize players and timing from the document.
554
- * Called during initial load() and can be called again via loadEdit() for hot-reload.
555
- * @param source - The source identifier for events (default: "load")
136
+ * Initialize runtime from the document.
556
137
  */
557
138
  private initializeFromDocument;
558
- private updateViewportMask;
559
139
  play(): void;
560
140
  pause(): void;
561
- seek(target: number): void;
141
+ seek(target: Seconds): void;
562
142
  stop(): void;
563
143
  /**
564
144
  * Reload the edit with a new configuration (hot-reload).
565
- * Uses smart diffing to only update what changed when possible.
566
- *
567
- * For initial loading, use the constructor + load() pattern instead:
568
- * ```typescript
569
- * const edit = new Edit(template);
570
- * await edit.load();
571
- * ```
572
- *
573
- * @param edit - The new Edit configuration to load
574
145
  */
575
146
  loadEdit(edit: Edit_2): Promise<void>;
576
147
  private loadSoundtrack;
577
148
  getEdit(): Edit_2;
578
- /**
579
- * Validates an edit configuration without applying it.
580
- * Use this to pre-validate user input before calling loadEdit().
581
- *
582
- * @param edit - The edit configuration to validate
583
- * @returns Validation result with valid boolean and any errors
584
- */
585
- validateEdit(edit: unknown): {
586
- valid: boolean;
587
- errors: Array<{
588
- path: string;
589
- message: string;
590
- }>;
591
- };
592
- getResolvedEdit(): ResolvedEdit;
593
- /**
594
- * Get the original parsed edit configuration.
595
- * Unlike getResolvedEdit(), this returns the edit as originally parsed,
596
- * with all clips present regardless of loading state.
597
- */
598
- getOriginalEdit(): ResolvedEdit | null;
599
149
  addClip(trackIdx: number, clip: Clip): void | Promise<void>;
600
150
  getClip(trackIdx: number, clipIdx: number): Clip | null;
601
- /**
602
- * Get the error state for a clip that failed to load.
603
- * Returns null if the clip loaded successfully.
604
- */
605
- getClipError(trackIdx: number, clipIdx: number): {
606
- error: string;
607
- assetType: string;
608
- } | null;
609
151
  /**
610
152
  * Clear the error for a deleted clip and shift indices for remaining errors.
611
- * Called when a clip is deleted to keep error indices in sync.
612
153
  */
613
154
  private clearClipErrorAndShift;
614
- getPlayerClip(trackIdx: number, clipIdx: number): Player | null;
615
- /** Get the exportable asset for a clip, preserving merge field templates */
616
- getOriginalAsset(trackIndex: number, clipIndex: number): unknown | undefined;
617
- deleteClip(trackIdx: number, clipIdx: number): void;
618
- splitClip(trackIndex: number, clipIndex: number, splitTime: number): void;
155
+ deleteClip(trackIdx: number, clipIdx: number): Promise<void>;
156
+ splitClip(trackIndex: number, clipIndex: number, splitTime: number): Promise<void>;
619
157
  addTrack(trackIdx: number, track: Track): Promise<void>;
620
158
  getTrack(trackIdx: number): Track | null;
621
159
  deleteTrack(trackIdx: number): void;
622
- getTotalDuration(): number;
623
- getMemoryStats(): {
624
- clipCounts: Record<string, number>;
625
- totalClips: number;
626
- richTextCacheStats: {
627
- clips: number;
628
- totalFrames: number;
629
- };
630
- textPlayerCount: number;
631
- lumaMaskCount: number;
632
- commandHistorySize: number;
633
- trackCount: number;
634
- };
635
- getComprehensiveMemoryStats(): {
636
- textureStats: {
637
- videos: {
638
- count: number;
639
- totalMB: number;
640
- avgDimensions: string;
641
- };
642
- images: {
643
- count: number;
644
- totalMB: number;
645
- avgDimensions: string;
646
- };
647
- text: {
648
- count: number;
649
- totalMB: number;
650
- };
651
- richText: {
652
- count: number;
653
- totalMB: number;
654
- };
655
- luma: {
656
- count: number;
657
- totalMB: number;
658
- };
659
- animated: {
660
- count: number;
661
- frames: number;
662
- totalMB: number;
663
- };
664
- totalTextures: number;
665
- totalMB: number;
666
- };
667
- assetDetails: Array<{
668
- id: string;
669
- type: "video" | "image" | "text" | "rich-text" | "luma" | "audio" | "html" | "shape" | "caption" | "unknown";
670
- label: string;
671
- width: number;
672
- height: number;
673
- estimatedMB: number;
674
- }>;
675
- systemStats: {
676
- clipCount: number;
677
- trackCount: number;
678
- commandCount: number;
679
- };
680
- };
681
- private estimateTextureMB;
682
- private getAssetLabel;
683
- getPlaybackHealth(): {
684
- activePlayerCount: number;
685
- totalPlayerCount: number;
686
- videoMaxDrift: number;
687
- audioMaxDrift: number;
688
- syncCorrections: number;
689
- };
690
- recordSyncCorrection(): void;
691
- undo(): void;
692
- redo(): void;
693
- updateClip(trackIdx: number, clipIdx: number, updates: Partial<Clip>): void;
160
+ undo(): Promise<void>;
161
+ redo(): Promise<void>;
694
162
  /**
695
- * Update clip timing mode and/or values.
696
- * Supports manual values, "auto", and "end" timing modes.
697
- * @param trackIdx - Track index
698
- * @param clipIdx - Clip index within track
699
- * @param params - Timing update parameters (start and/or length in milliseconds)
163
+ * Add a command to history without executing it.
700
164
  */
701
- updateClipTiming(trackIdx: number, clipIdx: number, params: TimingUpdateParams): void;
702
- executeEditCommand(command: EditCommand): void | Promise<void>;
703
- private executeCommand;
165
+ private addCommandToHistory;
166
+ updateClip(trackIdx: number, clipIdx: number, updates: Partial<Clip>): Promise<void>;
704
167
  /**
705
- * Emits a unified `edit:changed` event after any state mutation.
706
- * Consumers can subscribe to this single event instead of tracking 31+ granular events.
168
+ * Handle command result - only add to history if successful.
707
169
  */
708
- private emitEditChanged;
170
+ private handleCommandResult;
709
171
  /**
710
172
  * Detects merge field placeholders in the raw edit before substitution.
711
- * Returns a map of clip keys ("trackIdx-clipIdx") to their merge field bindings.
712
- * Each binding maps a property path to its placeholder and resolved value.
713
173
  */
714
174
  private detectMergeFieldBindings;
715
175
  /**
716
176
  * Recursively walks an object to find merge field placeholders.
717
- * Returns a map of property paths to their bindings.
718
177
  */
719
178
  private detectBindingsInObject;
720
179
  /**
721
180
  * Checks if edit has structural changes requiring full reload.
722
- * Structural = track count, clip count, or asset type changed.
181
+ *
182
+ * TODO: Expand granular path to handle more cases:
183
+ * - Clip add/remove: Use existing addClip()/deleteClip() commands
184
+ * - Soundtrack changes: Add/remove AudioPlayer via commands
185
+ * - Font changes: Load new fonts incrementally
186
+ * - Merge field changes: Re-resolve affected clips
723
187
  */
724
188
  private hasStructuralChanges;
725
189
  /**
726
- * Applies granular changes without full reload (preserves undo history, no flash).
727
- * Only called when structure is unchanged (same track/clip counts).
190
+ * Transfers existing clip IDs from the current document to the new edit configuration.
191
+ */
192
+ private preserveClipIdsForGranularUpdate;
193
+ /**
194
+ * Applies granular changes without full reload.
195
+ * @param newEdit - The new edit configuration
196
+ * @param oldTracks - The old tracks (captured before document update)
197
+ * @param oldOutput - The old output settings (captured before document update)
728
198
  */
729
199
  private applyGranularChanges;
730
- private createCommandContext;
731
200
  private queueDisposeClip;
732
- protected disposeClips(): void;
733
201
  /**
734
202
  * Remove fonts from timeline.fonts that are no longer referenced by any clip.
735
- * This keeps the document clean and prevents accumulation of unused font URLs.
736
203
  */
737
204
  private cleanupUnusedFonts;
738
205
  /**
739
206
  * Extract the filename (without extension) from a font URL.
740
- * e.g., "https://fonts.gstatic.com/s/inter/v20/UcCO3Fwr...Bg-4.ttf" → "UcCO3Fwr...Bg-4"
741
207
  */
742
208
  private extractFilenameFromUrl;
743
209
  private disposeClip;
744
210
  private unloadClipAssets;
745
- protected clearClips(): void;
746
- private updateTotalDuration;
747
- private resolveAllTiming;
748
- propagateTimingChanges(trackIndex: number, startFromClipIndex: number): void;
749
- resolveClipAutoLength(clip: Player): Promise<void>;
750
- private addPlayerToContainer;
751
211
  private movePlayerToTrackContainer;
752
- private createPlayerFromAssetType;
753
212
  private addPlayer;
754
- selectClip(trackIndex: number, clipIndex: number): void;
755
- clearSelection(): void;
756
- isClipSelected(trackIndex: number, clipIndex: number): boolean;
757
- getSelectedClipInfo(): {
758
- trackIndex: number;
759
- clipIndex: number;
760
- player: Player;
761
- } | null;
762
- /**
763
- * Copy a clip to the internal clipboard
764
- */
765
- copyClip(trackIdx: number, clipIdx: number): void;
766
- /**
767
- * Paste the copied clip at the current playhead position
768
- */
769
- pasteClip(): void;
770
- /**
771
- * Check if there is a clip in the clipboard
772
- */
773
- hasCopiedClip(): boolean;
774
- findClipIndices(player: Player): {
775
- trackIndex: number;
776
- clipIndex: number;
777
- } | null;
778
- getClipAt(trackIndex: number, clipIndex: number): Player | null;
779
- selectPlayer(player: Player): void;
780
- isPlayerSelected(player: Player): boolean;
781
- /**
782
- * Move the selected clip by a pixel delta.
783
- * Used for keyboard arrow key positioning.
784
- */
785
- moveSelectedClip(deltaX: number, deltaY: number): void;
786
- setExportMode(exporting: boolean): void;
787
- isInExportMode(): boolean;
788
- setCanvas(canvas: Canvas): void;
789
- getCanvasZoom(): number;
790
- setOutputSize(width: number, height: number): void;
791
- setOutputFps(fps: number): void;
213
+ setOutputSize(width: number, height: number): Promise<void>;
214
+ setOutputFps(fps: number): Promise<void>;
792
215
  getOutputFps(): number;
793
- setOutputFormat(format: string): void;
216
+ setOutputFormat(format: string): Promise<void>;
794
217
  getOutputFormat(): string;
795
- setOutputDestinations(destinations: Destination[]): void;
218
+ setOutputDestinations(destinations: Destination[]): Promise<void>;
796
219
  getOutputDestinations(): Destination[];
797
- getTimelineFonts(): Array<{
798
- src: string;
799
- }>;
800
- /**
801
- * Remove any fonts from timeline.fonts that are no longer used by clips.
802
- * Call this after changing a clip's font to clean up the old font.
803
- */
804
- pruneUnusedFonts(): void;
805
- setTimelineBackground(color: string): void;
220
+ setOutputResolution(resolution: string): Promise<void>;
221
+ getOutputResolution(): string | undefined;
222
+ setOutputAspectRatio(aspectRatio: string): Promise<void>;
223
+ getOutputAspectRatio(): string | undefined;
224
+ setTimelineBackground(color: string): Promise<void>;
225
+ private setTimelineBackgroundInternal;
806
226
  getTimelineBackground(): string;
807
227
  /**
808
- * @deprecated Use `ui.registerButton()` instead.
809
- */
810
- registerToolbarButton(config: ToolbarButtonConfig_2): void;
811
- /**
812
- * @deprecated Use `ui.unregisterButton()` instead.
813
- */
814
- unregisterToolbarButton(id: string): void;
815
- /**
816
- * @deprecated Use `ui.getButtons()` instead.
228
+ * Find the content clip that best matches a luma (by temporal overlap).
817
229
  */
818
- getToolbarButtons(): ToolbarButtonConfig_2[];
819
- /** Get the exportable clip (with merge field placeholders restored) */
820
- private getTemplateClip;
821
- /** Get the text content from the template clip (with merge field placeholders) */
822
- getTemplateClipText(trackIdx: number, clipIdx: number): string | null;
823
- /**
824
- * Apply a merge field to a clip property.
825
- * Creates a command for undo/redo support.
826
- *
827
- * @param trackIndex - Track index
828
- * @param clipIndex - Clip index within the track
829
- * @param propertyPath - Dot-notation path to property (e.g., "asset.src", "asset.color")
830
- * @param fieldName - Name of the merge field (e.g., "MEDIA_URL")
831
- * @param value - The resolved value to apply
832
- * @param originalValue - Optional: the original value before merge field (for undo)
833
- */
834
- applyMergeField(trackIndex: number, clipIndex: number, propertyPath: string, fieldName: string, value: string, originalValue?: string): void;
835
- /**
836
- * Remove a merge field from a clip property, restoring the original value.
837
- *
838
- * @param trackIndex - Track index
839
- * @param clipIndex - Clip index within the track
840
- * @param propertyPath - Dot-notation path to property (e.g., "asset.src")
841
- * @param restoreValue - The value to restore (original pre-merge-field value)
842
- */
843
- removeMergeField(trackIndex: number, clipIndex: number, propertyPath: string, restoreValue: string): void;
844
- /**
845
- * Get the merge field name for a clip property, if any.
846
- *
847
- * @returns The field name if a merge field is applied, null otherwise
848
- */
849
- getMergeFieldForProperty(trackIndex: number, clipIndex: number, propertyPath: string): string | null;
850
- /**
851
- * Update the value of a merge field. Updates all clips using this field in-place.
852
- * This does NOT use the command pattern (no undo) - it's for live preview updates.
853
- */
854
- updateMergeFieldValueLive(fieldName: string, newValue: string): void;
855
- /** Helper: Update merge field binding resolvedValues for a player */
856
- private updateMergeFieldBindings;
857
- /** Helper: Update merge field occurrences in an object */
858
- private updateMergeFieldInObject;
859
- /**
860
- * Redraw all clips that use a specific merge field.
861
- * Call this after updateMergeFieldValueLive() to refresh the canvas.
862
- * Handles both text redraws and asset reloads for URL changes.
863
- */
864
- redrawMergeFieldClips(fieldName: string): void;
865
- /** Helper: Check if and how a clip uses a specific merge field */
866
- private getMergeFieldUsage;
867
- /**
868
- * Check if a merge field is used for asset.src in any clip.
869
- * Used by UI to determine if URL validation should be applied.
870
- */
871
- isSrcMergeField(fieldName: string): boolean;
872
- /**
873
- * Remove a merge field globally from all clips and the registry.
874
- * Restores all affected clip properties to the merge field's default value.
875
- *
876
- * @param fieldName - The merge field name to remove
877
- */
878
- deleteMergeFieldGlobally(fieldName: string): void;
879
- /**
880
- * Helper: Find and restore merge field occurrences in a clip
881
- */
882
- private restoreMergeFieldInClip;
883
- /** Map of content player → luma player for attachment tracking */
884
- private lumaAttachments;
885
- /** Map of asset src → original asset type (for reliable luma detachment) */
886
- private originalAssetTypes;
887
- /**
888
- * Attach a luma mask to a specific clip.
889
- * Creates a luma clip on the same track with synchronized timing.
890
- *
891
- * @param trackIndex - Track index of the content clip
892
- * @param clipIndex - Clip index of the content clip
893
- * @param lumaSrc - URL of the luma mask asset (video or image)
894
- */
895
- attachLumaToClip(trackIndex: number, clipIndex: number, lumaSrc: string): Promise<void>;
896
- /**
897
- * Detach the luma mask from a clip.
898
- * Removes the luma clip from the track.
899
- *
900
- * @param trackIndex - Track index of the content clip
901
- * @param clipIndex - Clip index of the content clip
902
- */
903
- detachLumaFromClip(trackIndex: number, clipIndex: number): Promise<void>;
904
- /**
905
- * Get the luma mask attached to a clip, if any.
906
- *
907
- * @param trackIndex - Track index of the content clip
908
- * @param clipIndex - Clip index of the content clip
909
- * @returns Luma info or null if no luma attached
910
- */
911
- getClipLuma(trackIndex: number, clipIndex: number): {
912
- src: string;
913
- clipIndex: number;
914
- } | null;
915
- /**
916
- * Check if a clip has a luma mask attached.
917
- *
918
- * @param trackIndex - Track index of the content clip
919
- * @param clipIndex - Clip index of the content clip
920
- */
921
- hasLumaMask(trackIndex: number, clipIndex: number): boolean;
922
- /**
923
- * Register a luma attachment in the Edit Session's map.
924
- * This is called when a luma is attached to a content clip (e.g., during drag-drop).
925
- * The map is used by syncAttachedLuma() to coordinate timing between attached clips.
926
- */
927
- registerLumaAttachment(contentTrackIndex: number, contentClipIndex: number, lumaTrackIndex: number, lumaClipIndex: number): void;
928
- /**
929
- * Transform a clip to luma type (for attachment).
930
- * Recreates the player with luma asset type while preserving the src.
931
- *
932
- * @param trackIndex - Track index of the clip
933
- * @param clipIndex - Clip index of the clip
934
- */
935
- transformToLuma(trackIndex: number, clipIndex: number): void;
936
- /**
937
- * Transform a luma clip back to its original type (for detachment).
938
- * Uses stored original type for reliability, with URL extension fallback.
939
- *
940
- * @param trackIndex - Track index of the luma clip
941
- * @param clipIndex - Clip index of the luma clip
942
- */
943
- transformFromLuma(trackIndex: number, clipIndex: number): void;
230
+ private findBestContentMatch;
944
231
  private setupIntentListeners;
945
232
  }
946
233
 
947
234
  declare type Edit_2 = components["schemas"]["Edit"];
948
235
 
949
- declare type EditCommand = {
950
- execute(context?: CommandContext): void | Promise<void>;
951
- undo?(context?: CommandContext): void | Promise<void>;
952
- readonly name: string;
953
- };
954
-
955
- /** Alias for Edit type to avoid conflicts with SDK's Edit class */
236
+ /** Configuration for defining an edit - the structure passed to EditSession */
956
237
  export declare type EditConfig = Edit_2;
957
238
 
958
- export declare const EditEvent: {
239
+ declare const EditEvent: {
959
240
  readonly PlaybackPlay: "playback:play";
960
241
  readonly PlaybackPause: "playback:pause";
961
242
  readonly TimelineUpdated: "timeline:updated";
@@ -968,6 +249,7 @@ export declare const EditEvent: {
968
249
  readonly ClipRestored: "clip:restored";
969
250
  readonly ClipCopied: "clip:copied";
970
251
  readonly ClipLoadFailed: "clip:loadFailed";
252
+ readonly ClipUnresolved: "clip:unresolved";
971
253
  readonly SelectionCleared: "selection:cleared";
972
254
  readonly EditChanged: "edit:changed";
973
255
  readonly EditUndo: "edit:undo";
@@ -976,26 +258,22 @@ export declare const EditEvent: {
976
258
  readonly TrackRemoved: "track:removed";
977
259
  readonly DurationChanged: "duration:changed";
978
260
  readonly OutputResized: "output:resized";
261
+ readonly OutputResolutionChanged: "output:resolutionChanged";
262
+ readonly OutputAspectRatioChanged: "output:aspectRatioChanged";
979
263
  readonly OutputFpsChanged: "output:fpsChanged";
980
264
  readonly OutputFormatChanged: "output:formatChanged";
981
265
  readonly OutputDestinationsChanged: "output:destinationsChanged";
982
- readonly MergeFieldRegistered: "mergefield:registered";
983
- readonly MergeFieldUpdated: "mergefield:updated";
984
- readonly MergeFieldRemoved: "mergefield:removed";
985
266
  readonly MergeFieldChanged: "mergefield:changed";
986
- readonly MergeFieldApplied: "mergefield:applied";
987
- readonly TranscriptionProgress: "transcription:progress";
988
- readonly TranscriptionCompleted: "transcription:completed";
989
- readonly TranscriptionFailed: "transcription:failed";
990
267
  readonly LumaAttached: "luma:attached";
991
268
  readonly LumaDetached: "luma:detached";
992
269
  };
993
270
 
994
- export declare type EditEventMap = {
271
+ declare type EditEventMap = {
995
272
  [EditEvent.PlaybackPlay]: void;
996
273
  [EditEvent.PlaybackPause]: void;
274
+ /** Contains the document (source of truth) with original timing values like "auto", "end" */
997
275
  [EditEvent.TimelineUpdated]: {
998
- current: ResolvedEdit;
276
+ current: Edit_2;
999
277
  };
1000
278
  [EditEvent.TimelineBackgroundChanged]: {
1001
279
  color: string;
@@ -1018,6 +296,10 @@ export declare type EditEventMap = {
1018
296
  error: string;
1019
297
  assetType: string;
1020
298
  };
299
+ [EditEvent.ClipUnresolved]: ClipLocation & {
300
+ assetType: string;
301
+ clipId: string;
302
+ };
1021
303
  [EditEvent.SelectionCleared]: void;
1022
304
  [EditEvent.EditChanged]: {
1023
305
  source: string;
@@ -1043,43 +325,23 @@ export declare type EditEventMap = {
1043
325
  width: number;
1044
326
  height: number;
1045
327
  };
328
+ [EditEvent.OutputResolutionChanged]: {
329
+ resolution: Output["resolution"];
330
+ };
331
+ [EditEvent.OutputAspectRatioChanged]: {
332
+ aspectRatio: Output["aspectRatio"];
333
+ };
1046
334
  [EditEvent.OutputFpsChanged]: {
1047
335
  fps: number;
1048
336
  };
1049
337
  [EditEvent.OutputFormatChanged]: {
1050
- format: string;
338
+ format: Output["format"];
1051
339
  };
1052
340
  [EditEvent.OutputDestinationsChanged]: {
1053
- destinations: unknown[];
1054
- };
1055
- [EditEvent.MergeFieldRegistered]: {
1056
- field: MergeField_2;
1057
- };
1058
- [EditEvent.MergeFieldUpdated]: {
1059
- field: MergeField_2;
1060
- };
1061
- [EditEvent.MergeFieldRemoved]: ClipLocation & {
1062
- propertyPath: string;
1063
- fieldName: string | null;
341
+ destinations: Destination[];
1064
342
  };
1065
343
  [EditEvent.MergeFieldChanged]: {
1066
- fields: MergeField_2[];
1067
- };
1068
- [EditEvent.MergeFieldApplied]: ClipLocation & {
1069
- propertyPath: string;
1070
- fieldName: string;
1071
- };
1072
- [EditEvent.TranscriptionProgress]: {
1073
- clipAlias: string;
1074
- message?: string;
1075
- };
1076
- [EditEvent.TranscriptionCompleted]: {
1077
- clipAlias: string;
1078
- cueCount: number;
1079
- };
1080
- [EditEvent.TranscriptionFailed]: {
1081
- clipAlias: string;
1082
- error: string;
344
+ fields: MergeField[];
1083
345
  };
1084
346
  [EditEvent.LumaAttached]: ClipLocation & {
1085
347
  lumaSrc: string;
@@ -1088,206 +350,10 @@ export declare type EditEventMap = {
1088
350
  [EditEvent.LumaDetached]: ClipLocation;
1089
351
  };
1090
352
 
1091
- export declare type EditEventName = (typeof EditEvent)[keyof typeof EditEvent];
1092
-
1093
- export { EditSchema }
1094
-
1095
- declare type EditType = ResolvedEdit;
1096
-
1097
- declare abstract class Entity {
1098
- private readonly container;
1099
- constructor();
1100
- abstract load(): Promise<void>;
1101
- }
1102
-
1103
- declare class EventEmitter<TEventPayloadMap extends EventPayloadMap = EventPayloadMap> {
1104
- private readonly events;
1105
- constructor();
1106
- on<TEventName extends keyof TEventPayloadMap>(name: TEventName, listener: Listener<TEventPayloadMap[TEventName]>): () => void;
1107
- once<TEventName extends keyof TEventPayloadMap>(name: TEventName, listener: Listener<TEventPayloadMap[TEventName]>): () => void;
1108
- off<TEventName extends keyof TEventPayloadMap>(name: TEventName, listener: Listener<TEventPayloadMap[TEventName]>): void;
1109
- clear(name: keyof TEventPayloadMap): void;
1110
- emit<TEventName extends keyof TEventPayloadMap>(name: TEventName, ...args: TEventPayloadMap[TEventName] extends void ? [] : [TEventPayloadMap[TEventName]]): void;
1111
- }
1112
-
1113
353
  declare type EventPayloadMap<TPayload = any> = Record<string, TPayload>;
1114
354
 
1115
- /** SDK-extended CaptionAsset with stroke, width, height, alignment */
1116
- export declare type ExtendedCaptionAsset = CaptionAsset & {
1117
- stroke?: {
1118
- width: number;
1119
- color: string;
1120
- };
1121
- width?: number;
1122
- height?: number;
1123
- alignment?: {
1124
- horizontal?: "left" | "center" | "right";
1125
- vertical?: "top" | "center" | "bottom";
1126
- };
1127
- };
1128
-
1129
- export declare type Font = components["schemas"]["Font"];
1130
-
1131
- declare type FpsChangeCallback = (fps: number) => void;
1132
-
1133
- export declare const HexColorSchema: z.ZodString;
1134
-
1135
- export declare type HtmlAsset = components["schemas"]["HtmlAsset"];
1136
-
1137
- export declare type HtmlAssetPosition = NonNullable<HtmlAsset["position"]>;
1138
-
1139
- export { HtmlAssetSchema }
1140
-
1141
- export declare type ImageAsset = components["schemas"]["ImageAsset"];
1142
-
1143
- export { ImageAssetSchema }
1144
-
1145
- export declare class Inspector extends Entity {
1146
- private static readonly Width;
1147
- private static readonly Height;
1148
- fps: number;
1149
- playbackTime: number;
1150
- playbackDuration: number;
1151
- isPlaying: boolean;
1152
- clipStats: ClipStats | null;
1153
- systemStats: SystemStats | null;
1154
- playbackHealth: PlaybackHealth | null;
1155
- clipCounts: Record<string, number>;
1156
- totalClips: number;
1157
- commandHistorySize: number;
1158
- trackCount: number;
1159
- private background;
1160
- private text;
1161
- private historySamples;
1162
- private readonly maxSamples;
1163
- private lastSampleTime;
1164
- private readonly sampleInterval;
1165
- private frameTimes;
1166
- private readonly frameTimeWindow;
1167
- private readonly jankThreshold;
1168
- constructor();
1169
- load(): Promise<void>;
1170
- update(_: number, deltaMS: number): void;
1171
- private trackFrameTime;
1172
- private getFrameStats;
1173
- private getFrameTimeSparkline;
1174
- private addHistorySample;
1175
- private getJsHeapSparkline;
1176
- private renderStats;
1177
- private getSyncStatusIcon;
1178
- draw(): void;
1179
- dispose(): void;
1180
- private formatClipCounts;
1181
- private getMemoryInfo;
1182
- private bytesToMegabytes;
1183
- }
1184
-
1185
- declare type InternalEventMap = {
1186
- "canvas:clipClicked": {
1187
- player: Player;
1188
- };
1189
- "canvas:backgroundClicked": void;
1190
- "font:capabilitiesChanged": {
1191
- supportsBold: boolean;
1192
- };
1193
- "toolbar:buttonsChanged": {
1194
- buttons: ToolbarButtonConfig_2[];
1195
- };
1196
- };
1197
-
1198
- declare type Keyframe_2 = Tween;
1199
- export { Keyframe_2 as Keyframe }
1200
-
1201
355
  declare type Listener<TPayload = any> = (payload: TPayload) => void;
1202
356
 
1203
- export declare type LumaAsset = components["schemas"]["LumaAsset"];
1204
-
1205
- export { LumaAssetSchema }
1206
-
1207
- export declare class MediaToolbar extends BaseToolbar {
1208
- private showMergeFields;
1209
- private assetType;
1210
- constructor(edit: Edit, options?: MediaToolbarOptions);
1211
- private currentFit;
1212
- private currentVolume;
1213
- private transitionPanel;
1214
- private effectPanel;
1215
- private opacitySlider;
1216
- private scaleSlider;
1217
- private fitBtn;
1218
- private opacityBtn;
1219
- private scaleBtn;
1220
- private volumeBtn;
1221
- private transitionBtn;
1222
- private effectBtn;
1223
- private advancedBtn;
1224
- private audioFadeBtn;
1225
- private fitPopup;
1226
- private opacityPopup;
1227
- private scalePopup;
1228
- private volumePopup;
1229
- private transitionPopup;
1230
- private effectPopup;
1231
- private advancedPopup;
1232
- private audioFadePopup;
1233
- private fitLabel;
1234
- private volumeSlider;
1235
- private volumeValue;
1236
- private volumeSection;
1237
- private visualSection;
1238
- private audioSection;
1239
- private dynamicToggle;
1240
- private dynamicPanel;
1241
- private dynamicInput;
1242
- private audioFadeEffect;
1243
- private isDynamicSource;
1244
- private dynamicFieldName;
1245
- private originalSrc;
1246
- mount(parent: HTMLElement): void;
1247
- /**
1248
- * Mount composite UI components into their placeholder elements.
1249
- */
1250
- private mountCompositeComponents;
1251
- private setupEventListeners;
1252
- private togglePopupByName;
1253
- protected closeAllPopups(): void;
1254
- protected getPopupList(): (HTMLElement | null)[];
1255
- protected syncState(): void;
1256
- private handleFitChange;
1257
- private handleOpacityChange;
1258
- private handleScaleChange;
1259
- private handleVolumeChange;
1260
- private applyTransitionUpdate;
1261
- private applyEffect;
1262
- private handleAudioFadeSelect;
1263
- private applyAudioFade;
1264
- private updateAudioFadeUI;
1265
- private applyClipUpdate;
1266
- private setupDynamicSourceHandlers;
1267
- private applyDynamicUrl;
1268
- private showUrlError;
1269
- private clearUrlError;
1270
- private clearDynamicSource;
1271
- private updateDynamicSourceUI;
1272
- private updateFitDisplay;
1273
- private updateOpacityDisplay;
1274
- private updateScaleDisplay;
1275
- private updateVolumeDisplay;
1276
- private updateFitActiveState;
1277
- /**
1278
- * Show the toolbar for a specific clip.
1279
- * Derives assetType from the clip's asset configuration.
1280
- */
1281
- show(trackIndex: number, clipIndex: number): void;
1282
- dispose(): void;
1283
- }
1284
-
1285
- declare interface MediaToolbarOptions {
1286
- mergeFields?: boolean;
1287
- }
1288
-
1289
- export declare type MergeField = components["schemas"]["MergeField"];
1290
-
1291
357
  /**
1292
358
  * Merge field types for the Shotstack Studio SDK.
1293
359
  *
@@ -1297,7 +363,7 @@ export declare type MergeField = components["schemas"]["MergeField"];
1297
363
  /**
1298
364
  * A merge field definition used throughout the SDK.
1299
365
  */
1300
- declare interface MergeField_2 {
366
+ declare interface MergeField {
1301
367
  /** Field identifier (uppercase convention: MY_FIELD) */
1302
368
  name: string;
1303
369
  /** Default value used for preview when no runtime value is provided */
@@ -1306,401 +372,16 @@ declare interface MergeField_2 {
1306
372
  description?: string;
1307
373
  }
1308
374
 
1309
- /**
1310
- * Tracks a merge field binding for a specific property path.
1311
- * Used to restore placeholders on export for properties that haven't changed.
1312
- */
1313
- declare interface MergeFieldBinding {
1314
- /** The original placeholder string, e.g., "{{ HERO_IMAGE }}" */
1315
- placeholder: string;
1316
- /** The resolved value at binding time, used for change detection */
1317
- resolvedValue: string;
1318
- }
1319
-
1320
- declare class MergeFieldService {
1321
- private fields;
1322
- private events;
1323
- constructor(events: EventEmitter);
1324
- /**
1325
- * Register or update a merge field.
1326
- * @param field The merge field to register
1327
- * @param options.silent If true, suppresses event emission (for command-based operations)
1328
- */
1329
- register(field: MergeField_2, options?: {
1330
- silent?: boolean;
1331
- }): void;
1332
- /**
1333
- * Remove a merge field by name.
1334
- * @param name The field name to remove
1335
- * @param options.silent If true, suppresses event emission (for command-based operations)
1336
- */
1337
- remove(name: string, options?: {
1338
- silent?: boolean;
1339
- }): boolean;
1340
- /** Get a merge field by name */
1341
- get(name: string): MergeField_2 | undefined;
1342
- /** Get all registered merge fields */
1343
- getAll(): MergeField_2[];
1344
- /** Clear all merge fields */
1345
- clear(): void;
1346
- /**
1347
- * Apply merge field substitutions to a string.
1348
- * Replaces {{ FIELD_NAME }} patterns with their default values.
1349
- */
1350
- resolve(input: string): string;
1351
- /**
1352
- * Check if a string contains unresolved merge fields.
1353
- * Returns true if any {{ FIELD_NAME }} patterns remain after resolution.
1354
- */
1355
- hasUnresolved(input: string): boolean;
1356
- /**
1357
- * Extract the first merge field name from a string.
1358
- * Returns null if no merge field pattern is found.
1359
- */
1360
- extractFieldName(input: string): string | null;
1361
- /** Check if a string is a merge field template (contains {{ FIELD }}) */
1362
- isMergeFieldTemplate(input: string): boolean;
1363
- /** Create a merge field template string from a field name */
1364
- createTemplate(fieldName: string): string;
1365
- /** Export fields in Shotstack API format ({ find, replace }) */
1366
- toSerializedArray(): SerializedMergeField[];
1367
- /** Import fields from Shotstack API format (does not emit event - called during loadEdit) */
1368
- loadFromSerialized(fields: SerializedMergeField[]): void;
1369
- /** Generate a unique field name with a given prefix (e.g., MEDIA_1, MEDIA_2) */
1370
- generateUniqueName(prefix: string): string;
1371
- }
1372
-
1373
- export declare interface NumericKeyframe {
1374
- start: number;
1375
- length: number;
1376
- from: number;
1377
- to: number;
1378
- interpolation?: Tween["interpolation"];
1379
- easing?: Tween["easing"];
1380
- }
1381
-
1382
- export declare type Offset = components["schemas"]["Offset"];
1383
-
1384
- export { OffsetSchema }
1385
-
1386
- export declare type Output = components["schemas"]["Output"];
1387
-
1388
- export declare const OutputFormatSchema: z.ZodEnum<{
1389
- mp4: "mp4";
1390
- gif: "gif";
1391
- mp3: "mp3";
1392
- jpg: "jpg";
1393
- png: "png";
1394
- bmp: "bmp";
1395
- }>;
1396
-
1397
- export declare const OutputFpsSchema: z.ZodUnion<readonly [z.ZodLiteral<12>, z.ZodLiteral<15>, z.ZodLiteral<23.976>, z.ZodLiteral<24>, z.ZodLiteral<25>, z.ZodLiteral<29.97>, z.ZodLiteral<30>, z.ZodLiteral<48>, z.ZodLiteral<50>, z.ZodLiteral<59.94>, z.ZodLiteral<60>]>;
1398
-
1399
- export { OutputSchema }
1400
-
1401
- export declare const OutputSizeSchema: z.ZodObject<{
1402
- width: z.ZodOptional<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodCoercedNumber<unknown>>>;
1403
- height: z.ZodOptional<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodCoercedNumber<unknown>>>;
1404
- }, z.core.$strip>;
1405
-
1406
- declare interface PlaybackHealth {
1407
- activePlayerCount: number;
1408
- totalPlayerCount: number;
1409
- videoMaxDrift: number;
1410
- audioMaxDrift: number;
1411
- syncCorrections: number;
1412
- }
1413
-
1414
- /**
1415
- * Base class for all visual content players in the canvas.
1416
- *
1417
- * Player is responsible for rendering clip content (video, image, text, etc.)
1418
- * and applying keyframe animations. It does NOT handle selection UI or
1419
- * drag/resize/rotate interactions - those are handled by SelectionHandles
1420
- * when registered via UIController.
1421
- *
1422
- * This separation allows Canvas to be used as a pure preview renderer
1423
- * without any interactive overlays.
1424
- */
1425
- declare abstract class Player extends Entity {
1426
- private static readonly DiscardedFrameCount;
1427
- layer: number;
1428
- shouldDispose: boolean;
1429
- readonly playerType: PlayerType;
1430
- protected edit: Edit;
1431
- clipConfiguration: ResolvedClip;
1432
- private timingIntent;
1433
- private resolvedTiming;
1434
- private positionBuilder;
1435
- private offsetXKeyframeBuilder?;
1436
- private offsetYKeyframeBuilder?;
1437
- private scaleKeyframeBuilder?;
1438
- private opacityKeyframeBuilder?;
1439
- private rotationKeyframeBuilder?;
1440
- private maskXKeyframeBuilder?;
1441
- private wipeMask;
1442
- protected contentContainer: pixi.Container;
1443
- /**
1444
- * Tracks which properties came from merge field templates.
1445
- * Key: property path (e.g., "asset.src"), Value: binding info
1446
- */
1447
- private mergeFieldBindings;
1448
- constructor(edit: Edit, clipConfiguration: ResolvedClip, playerType: PlayerType);
1449
- reconfigureAfterRestore(): void;
1450
- /**
1451
- * Reload the asset for this player (e.g., when asset.src changes).
1452
- * Override in subclasses that have loadable assets (image, video).
1453
- * Default implementation is a no-op.
1454
- */
1455
- reloadAsset(): Promise<void>;
1456
- protected configureKeyframes(): void;
1457
- load(): Promise<void>;
1458
- update(_: number, __: number): void;
1459
- private updateWipeMask;
1460
- draw(): void;
1461
- dispose(): void;
1462
- getStart(): Seconds;
1463
- getLength(): Seconds;
1464
- getEnd(): Seconds;
1465
- getTimingIntent(): TimingIntent;
1466
- setTimingIntent(intent: Partial<TimingIntent>): void;
1467
- getResolvedTiming(): ResolvedTiming;
1468
- setResolvedTiming(timing: ResolvedTiming): void;
1469
- convertToFixedTiming(): void;
1470
- /**
1471
- * Set a merge field binding for a property path.
1472
- * Called when a property is resolved from a merge field template.
1473
- */
1474
- setMergeFieldBinding(path: string, binding: MergeFieldBinding): void;
1475
- /**
1476
- * Get the merge field binding for a property path, if any.
1477
- */
1478
- getMergeFieldBinding(path: string): MergeFieldBinding | undefined;
1479
- /**
1480
- * Remove a merge field binding (e.g., when user changes the value).
1481
- */
1482
- removeMergeFieldBinding(path: string): void;
1483
- /**
1484
- * Get all merge field bindings for this player.
1485
- */
1486
- getMergeFieldBindings(): Map<string, MergeFieldBinding>;
1487
- /**
1488
- * Bulk set bindings during player initialization.
1489
- */
1490
- setInitialBindings(bindings: Map<string, MergeFieldBinding>): void;
1491
- /**
1492
- * Get the exportable clip configuration with merge field placeholders restored.
1493
- * For properties that haven't changed from their resolved value, the original
1494
- * placeholder (e.g., "{{ HERO_IMAGE }}") is restored for export.
1495
- */
1496
- getExportableClip(): Clip;
1497
- /**
1498
- * Get the playback time relative to clip start, in seconds.
1499
- * Used for keyframe animation calculations.
1500
- */
1501
- getPlaybackTime(): number;
1502
- abstract getSize(): Size;
1503
- /**
1504
- * Returns the source content dimensions (before fit scaling).
1505
- * Override in subclasses that have different source vs output sizes.
1506
- * Default implementation returns getSize().
1507
- */
1508
- getContentSize(): Size;
1509
- getOpacity(): number;
1510
- getPosition(): Vector;
1511
- getPivot(): Vector;
1512
- protected getFitScale(): number;
1513
- getScale(): number;
1514
- protected getContainerScale(): Vector;
1515
- getRotation(): number;
1516
- isActive(): boolean;
1517
- shouldDiscardFrame(): boolean;
1518
- /**
1519
- * Handle pointer down - emit click event for selection handling.
1520
- * All drag/resize/rotate interaction is handled by SelectionHandles.
1521
- */
1522
- private onPointerDown;
1523
- private clipHasKeyframes;
1524
- protected applyFixedDimensions(): void;
1525
- protected applyAnchorPositioning(anchor: string, clipWidth: number, clipHeight: number, sprite: pixi.Sprite): void;
1526
- /**
1527
- * Override in subclasses to enable edge resize handles for dimension changes.
1528
- * When true, edge handles will be shown instead of corner scale handles.
1529
- */
1530
- supportsEdgeResize(): boolean;
1531
- /**
1532
- * Called when dimensions change via edge resize. Override in subclasses to handle re-rendering.
1533
- */
1534
- protected onDimensionsChanged(): void;
1535
- /**
1536
- * Public wrapper for notifying dimension changes.
1537
- * Called by SelectionHandles after edge resize operations.
1538
- */
1539
- notifyDimensionsChanged(): void;
1540
- }
1541
-
1542
- declare enum PlayerType {
1543
- Video = "video",
1544
- Image = "image",
1545
- Audio = "audio",
1546
- Text = "text",
1547
- RichText = "rich-text",
1548
- Luma = "luma",
1549
- Html = "html",
1550
- Shape = "shape",
1551
- Caption = "caption"
1552
- }
1553
-
1554
- declare type ResolutionChangeCallback = (width: number, height: number) => void;
1555
-
1556
- declare type ResolvedClip = Omit<Clip, "start" | "length"> & {
1557
- start: Seconds;
1558
- length: Seconds;
1559
- };
1560
-
1561
- declare type ResolvedEdit = Omit<Edit_2, "timeline"> & {
1562
- timeline: Omit<Edit_2["timeline"], "tracks"> & {
1563
- tracks: ResolvedTrack[];
1564
- };
1565
- };
375
+ declare type Output = components["schemas"]["Output"];
1566
376
 
1567
377
  /**
1568
- * Resolved timing values in seconds.
1569
- * Matches the external @shotstack/schemas unit convention.
1570
- * Conversion to milliseconds happens only in the Player layer for pixi rendering.
378
+ * Read-only view of an EventEmitter that only exposes subscription methods.
379
+ * Used as the public `events` type on Edit to prevent consumers from emitting events.
1571
380
  */
1572
- declare interface ResolvedTiming {
1573
- start: Seconds;
1574
- length: Seconds;
1575
- }
1576
-
1577
- declare type ResolvedTrack = {
1578
- clips: ResolvedClip[];
1579
- };
1580
-
1581
- export declare type RichTextAsset = components["schemas"]["RichTextAsset"];
1582
-
1583
- export { RichTextAssetSchema }
1584
-
1585
- export declare class RichTextToolbar extends BaseToolbar {
1586
- private showMergeFields;
1587
- private fontPopup;
1588
- private fontPreview;
1589
- private fontPicker;
1590
- private sizeInput;
1591
- private sizePopup;
1592
- private weightPopup;
1593
- private weightPreview;
1594
- private spacingPopup;
1595
- private spacingPanel;
1596
- private anchorTopBtn;
1597
- private anchorMiddleBtn;
1598
- private anchorBottomBtn;
1599
- private alignIcon;
1600
- private transformBtn;
1601
- private underlineBtn;
1602
- private linethroughBtn;
1603
- private textEditPopup;
1604
- private textEditArea;
1605
- private textEditDebounceTimer;
1606
- private autocompletePopup;
1607
- private autocompleteItems;
1608
- private autocompleteVisible;
1609
- private autocompleteFilter;
1610
- private autocompleteStartPos;
1611
- private selectedAutocompleteIndex;
1612
- private borderPopup;
1613
- private borderWidthSlider;
1614
- private borderWidthValue;
1615
- private borderColorInput;
1616
- private borderOpacitySlider;
1617
- private borderOpacityValue;
1618
- private borderRadiusSlider;
1619
- private borderRadiusValue;
1620
- private backgroundPopup;
1621
- private backgroundColorPicker;
1622
- private paddingPopup;
1623
- private paddingTopSlider;
1624
- private paddingTopValue;
1625
- private paddingRightSlider;
1626
- private paddingRightValue;
1627
- private paddingBottomSlider;
1628
- private paddingBottomValue;
1629
- private paddingLeftSlider;
1630
- private paddingLeftValue;
1631
- private fontColorPopup;
1632
- private fontColorPicker;
1633
- private colorDisplay;
1634
- private animationPopup;
1635
- private animationDurationSlider;
1636
- private animationDurationValue;
1637
- private animationStyleSection;
1638
- private animationDirectionSection;
1639
- private transitionPopup;
1640
- private transitionPanel;
1641
- private effectPopup;
1642
- private effectPanel;
1643
- private stylePopup;
1644
- private stylePanel;
1645
- private boundHandleClick;
1646
- constructor(edit: Edit, options?: RichTextToolbarOptions);
1647
- mount(parent: HTMLElement): void;
1648
- private handleClick;
1649
- private getCurrentAsset;
1650
- private updateSize;
1651
- private static readonly FONT_WEIGHTS;
1652
- private static readonly CHECKMARK_SVG;
1653
- /** Single source of truth for weight normalization - handles string, number, or object */
1654
- private normalizeWeight;
1655
- private getWeightName;
1656
- private toggleWeightPopup;
1657
- /** Build popup once at mount - uses event delegation (no per-item listeners) */
1658
- private buildWeightPopup;
1659
- /** Update active state without rebuilding DOM */
1660
- private updateWeightPopupState;
1661
- private setFontWeight;
1662
- private toggleSizePopup;
1663
- private buildSizePopup;
1664
- private applyManualSize;
1665
- private toggleSpacingPopup;
1666
- private toggleAnimationPopup;
1667
- private toggleFontColorPopup;
1668
- private toggleFontPopup;
1669
- private toggleTextEditPopup;
1670
- private debouncedApplyTextEdit;
1671
- private applyTextEdit;
1672
- private checkAutocomplete;
1673
- private showAutocomplete;
1674
- private hideAutocomplete;
1675
- private insertVariable;
1676
- private insertSelectedVariable;
1677
- private getFilteredFieldCount;
1678
- private buildFontPicker;
1679
- private getDisplayName;
1680
- private selectGoogleFont;
1681
- private updateVerticalAlign;
1682
- private cycleAlignment;
1683
- private updateAlignment;
1684
- private updateAlignIcon;
1685
- private cycleTransform;
1686
- private toggleUnderline;
1687
- private toggleLinethrough;
1688
- private updateBorderProperty;
1689
- private updateShadowProperty;
1690
- private updateAnimationProperty;
1691
- private updateAnimationSections;
1692
- private updateBackgroundProperty;
1693
- private updatePaddingProperty;
1694
- private updateFontColorProperty;
1695
- private updateClipProperty;
1696
- private applyClipUpdate;
1697
- protected getPopupList(): (HTMLElement | null)[];
1698
- protected syncState(): void;
1699
- dispose(): void;
1700
- }
1701
-
1702
- declare interface RichTextToolbarOptions {
1703
- mergeFields?: boolean;
381
+ declare interface ReadonlyEventEmitter<TEventPayloadMap extends EventPayloadMap> {
382
+ on<K extends keyof TEventPayloadMap>(name: K, listener: Listener<TEventPayloadMap[K]>): () => void;
383
+ once<K extends keyof TEventPayloadMap>(name: K, listener: Listener<TEventPayloadMap[K]>): () => void;
384
+ off<K extends keyof TEventPayloadMap>(name: K, listener: Listener<TEventPayloadMap[K]>): void;
1704
385
  }
1705
386
 
1706
387
  /**
@@ -1713,175 +394,11 @@ declare type Seconds = number & {
1713
394
 
1714
395
  declare const SecondsSymbol: unique symbol;
1715
396
 
1716
- /**
1717
- * SelectionHandles renders selection UI (outline + resize handles) on selected players
1718
- * and handles all drag/resize/rotate interactions.
1719
- *
1720
- * This class decouples interaction logic from Player, allowing Canvas to work as a pure renderer.
1721
- */
1722
- export declare class SelectionHandles implements CanvasOverlayRegistration {
1723
- private edit;
1724
- private container;
1725
- private outline;
1726
- private handles;
1727
- private app;
1728
- private positionBuilder;
1729
- private selectedPlayer;
1730
- private selectedTrackIndex;
1731
- private selectedClipIndex;
1732
- private isHovering;
1733
- private isDragging;
1734
- private dragOffset;
1735
- private scaleDirection;
1736
- private edgeDragDirection;
1737
- private edgeDragStart;
1738
- private originalDimensions;
1739
- private isRotating;
1740
- private rotationStart;
1741
- private initialRotation;
1742
- private rotationCorner;
1743
- private initialClipConfiguration;
1744
- private onClipSelectedBound;
1745
- private onSelectionClearedBound;
1746
- private onPointerDownBound;
1747
- private onPointerMoveBound;
1748
- private onPointerUpBound;
1749
- constructor(edit: Edit);
1750
- mount(parent: pixi.Container, app: pixi.Application): void;
1751
- update(_deltaTime: number, _elapsed: number): void;
1752
- draw(): void;
1753
- dispose(): void;
1754
- private onClipSelected;
1755
- private onSelectionCleared;
1756
- private syncToPlayer;
1757
- private drawOutline;
1758
- private drawHandles;
1759
- private getUIScale;
1760
- private onPointerDown;
1761
- private onPointerMove;
1762
- private onPointerUp;
1763
- private startDrag;
1764
- private handleDrag;
1765
- private startCornerResize;
1766
- private handleCornerResize;
1767
- private startEdgeResize;
1768
- private handleEdgeResize;
1769
- private startRotation;
1770
- private handleRotation;
1771
- private captureOriginalDimensions;
1772
- private getContentCenter;
1773
- private getRotationCorner;
1774
- private getCornerResizeCursor;
1775
- private updateHoverState;
1776
- private resetDragState;
1777
- private hasStateChanged;
1778
- }
1779
-
1780
- /**
1781
- * Serialized format for JSON export (matches Shotstack API).
1782
- * The replace value can be any type - strings, numbers, booleans, objects.
1783
- */
1784
- declare interface SerializedMergeField {
1785
- find: string;
1786
- replace: unknown;
1787
- }
1788
-
1789
- export declare type ShapeAsset = components["schemas"]["ShapeAsset"];
1790
-
1791
- export { ShapeAssetSchema }
1792
-
1793
- declare type Size = {
1794
- width: number;
1795
- height: number;
1796
- };
1797
-
1798
- export declare type Soundtrack = components["schemas"]["Soundtrack"];
1799
-
1800
- declare interface SystemStats {
1801
- clipCount: number;
1802
- trackCount: number;
1803
- commandCount: number;
1804
- }
1805
-
1806
- export declare type TextAsset = components["schemas"]["TextAsset"];
1807
-
1808
- export { TextAssetSchema }
1809
-
1810
- export declare class TextToolbar extends BaseToolbar {
1811
- private textEditBtn;
1812
- private textEditPopup;
1813
- private textEditArea;
1814
- private textEditDebounceTimer;
1815
- private sizeInput;
1816
- private sizePopup;
1817
- private fontBtn;
1818
- private fontPopup;
1819
- private fontPreview;
1820
- private boldBtn;
1821
- private fontColorBtn;
1822
- private fontColorPopup;
1823
- private fontColorInput;
1824
- private colorDisplay;
1825
- private spacingBtn;
1826
- private spacingPopup;
1827
- private spacingPanel;
1828
- private anchorTopBtn;
1829
- private anchorMiddleBtn;
1830
- private anchorBottomBtn;
1831
- private alignBtn;
1832
- private alignIcon;
1833
- private backgroundBtn;
1834
- private backgroundPopup;
1835
- private bgColorInput;
1836
- private bgOpacitySlider;
1837
- private bgOpacityValue;
1838
- private strokeBtn;
1839
- private strokePopup;
1840
- private strokeWidthSlider;
1841
- private strokeWidthValue;
1842
- private strokeColorInput;
1843
- private transitionBtn;
1844
- private transitionPopup;
1845
- private transitionPanel;
1846
- private effectBtn;
1847
- private effectPopup;
1848
- private effectPanel;
1849
- private boundHandleClick;
1850
- mount(parent: HTMLElement): void;
1851
- private bindElements;
1852
- private setupEventListeners;
1853
- private mountCompositePanels;
1854
- private handleClick;
1855
- protected getPopupList(): (HTMLElement | null)[];
1856
- private buildFontPopup;
1857
- private buildSizePopup;
1858
- private getCurrentAsset;
1859
- private updateAssetProperty;
1860
- private debouncedApplyTextEdit;
1861
- private adjustSize;
1862
- private setSize;
1863
- private applyManualSize;
1864
- private setFont;
1865
- private updateFontActiveState;
1866
- private toggleBold;
1867
- private handleFontColorChange;
1868
- private setVerticalAnchor;
1869
- private updateAnchorActiveState;
1870
- private cycleAlignment;
1871
- private updateAlignmentIcon;
1872
- private handleBackgroundChange;
1873
- private handleStrokeChange;
1874
- private applyClipUpdate;
1875
- protected syncState(): void;
1876
- dispose(): void;
1877
- }
1878
-
1879
- /** HTML/CSS-based Timeline component extending TimelineEntity for SDK consistency */
1880
- export declare class Timeline extends TimelineEntity {
397
+ export declare class Timeline {
1881
398
  private readonly edit;
399
+ readonly element: HTMLElement;
1882
400
  private readonly container;
1883
401
  private readonly stateManager;
1884
- private features;
1885
402
  private clipRenderers;
1886
403
  private thumbnailGenerator;
1887
404
  private mediaThumbnailRenderer;
@@ -1898,20 +415,23 @@ export declare class Timeline extends TimelineEntity {
1898
415
  private lastFrameTime;
1899
416
  private isInteracting;
1900
417
  private isLoaded;
418
+ private thumbnailRenderPending;
1901
419
  private readonly handleTimelineUpdated;
1902
420
  private readonly handlePlaybackPlay;
1903
421
  private readonly handlePlaybackPause;
1904
422
  private readonly handleClipSelected;
1905
423
  private readonly handleClipLoadFailed;
1906
- constructor(edit: Edit, container: HTMLElement, options?: TimelineOptions);
424
+ private readonly handleClipUpdated;
425
+ private readonly handleRulerMouseMove;
426
+ constructor(edit: Edit, container: HTMLElement);
1907
427
  /** Initialize and mount the timeline */
1908
428
  load(): Promise<void>;
1909
- /** Update component state (called each frame during active rendering) */
1910
- update(_deltaTime: number, _elapsed: number): void;
1911
- /** Render/draw component to DOM (called each frame after update) */
1912
- draw(): void;
1913
429
  /** Clean up and unmount the timeline */
1914
430
  dispose(): void;
431
+ /**
432
+ * Pre-compute AI asset numbers for all clips.
433
+ */
434
+ private computeAiAssetNumbers;
1915
435
  private setupEventListeners;
1916
436
  private removeEventListeners;
1917
437
  /** Start continuous render loop (during playback or interaction) */
@@ -1922,221 +442,12 @@ export declare class Timeline extends TimelineEntity {
1922
442
  private tick;
1923
443
  /** Request a single render (used when idle and data changes) */
1924
444
  private requestRender;
1925
- /** Mark interaction as started (enables render loop) */
1926
- beginInteraction(): void;
1927
- /** Mark interaction as ended (may stop render loop) */
1928
- endInteraction(): void;
1929
445
  private buildComponents;
1930
446
  private disposeComponents;
1931
- setZoom(pixelsPerSecond: number): void;
1932
447
  zoomIn(): void;
1933
448
  zoomOut(): void;
1934
- scrollTo(time: number): void;
1935
- /** Recalculate size from container and re-render */
1936
- resize(): void;
1937
- selectClip(trackIndex: number, clipIndex: number): void;
1938
- clearSelection(): void;
1939
- enableFeature(feature: keyof TimelineFeatures): void;
1940
- disableFeature(feature: keyof TimelineFeatures): void;
1941
- registerClipRenderer(type: string, renderer: ClipRenderer): void;
1942
- getEdit(): Edit;
1943
- findClipAtPosition(x: number, y: number): ClipInfo | null;
1944
- }
1945
-
1946
- /**
1947
- * Base class for HTML-based timeline components.
1948
- * Mirrors the Entity pattern used by PixiJS components (load/update/draw/dispose lifecycle).
1949
- */
1950
- declare abstract class TimelineEntity {
1951
- readonly element: HTMLElement;
1952
- protected children: TimelineEntity[];
1953
- constructor(tagName?: keyof HTMLElementTagNameMap, className?: string);
1954
- /** Initialize the component and its children */
1955
- abstract load(): Promise<void>;
1956
- /** Update component state (called each frame during active rendering) */
1957
- abstract update(deltaTime: number, elapsed: number): void;
1958
- /** Render/draw component to DOM (called each frame after update) */
1959
- abstract draw(): void;
1960
- /** Clean up resources and remove from DOM */
1961
- abstract dispose(): void;
1962
- /** Add a child entity */
1963
- protected addChild(child: TimelineEntity): void;
1964
- /** Remove a child entity */
1965
- protected removeChild(child: TimelineEntity): void;
1966
- /** Remove all children */
1967
- protected removeAllChildren(): void;
1968
- /** Load all children */
1969
- protected loadChildren(): Promise<void>;
1970
- /** Update all children */
1971
- protected updateChildren(deltaTime: number, elapsed: number): void;
1972
- /** Draw all children */
1973
- protected drawChildren(): void;
1974
- /** Dispose all children */
1975
- protected disposeChildren(): void;
1976
- }
1977
-
1978
- /** Feature toggles for Timeline */
1979
- export declare interface TimelineFeatures {
1980
- /** Show toolbar with playback controls */
1981
- toolbar?: boolean;
1982
- /** Show time ruler */
1983
- ruler?: boolean;
1984
- /** Show playhead indicator */
1985
- playhead?: boolean;
1986
- /** Enable snap-to-grid/clips */
1987
- snap?: boolean;
1988
- /** Show timing intent badges on clips */
1989
- badges?: boolean;
1990
- /** Enable multi-select with shift/ctrl+click */
1991
- multiSelect?: boolean;
1992
- }
1993
-
1994
- /** Interaction configuration */
1995
- declare interface TimelineInteractionConfig {
1996
- /** Minimum pixels to move before starting drag */
1997
- dragThreshold?: number;
1998
- /** Snap distance in pixels */
1999
- snapThreshold?: number;
2000
- /** Width of resize zone at clip edges */
2001
- resizeZone?: number;
2002
- /** Callback to request timeline re-render */
2003
- onRequestRender?: () => void;
2004
- }
2005
-
2006
- /** Configuration options for Timeline */
2007
- export declare interface TimelineOptions {
2008
- /** Feature toggles */
2009
- features?: TimelineFeatures;
2010
- /** Interaction configuration */
2011
- interaction?: TimelineInteractionConfig;
2012
- /** Initial pixels per second (zoom level) */
2013
- pixelsPerSecond?: number;
2014
- /** Track height in pixels */
2015
- trackHeight?: number;
2016
- }
2017
-
2018
- export { TimelineSchema }
2019
-
2020
- export declare interface TimelineTheme {
2021
- timeline: {
2022
- background: number;
2023
- divider: number;
2024
- toolbar: {
2025
- background: number;
2026
- surface: number;
2027
- hover: number;
2028
- active: number;
2029
- divider: number;
2030
- icon: number;
2031
- text: number;
2032
- height: number;
2033
- };
2034
- ruler: {
2035
- background: number;
2036
- text: number;
2037
- markers: number;
2038
- height: number;
2039
- };
2040
- tracks: {
2041
- surface: number;
2042
- surfaceAlt: number;
2043
- border: number;
2044
- height: number;
2045
- };
2046
- clips: {
2047
- video: number;
2048
- audio: number;
2049
- image: number;
2050
- text: number;
2051
- "rich-text": number;
2052
- shape: number;
2053
- html: number;
2054
- luma: number;
2055
- default: number;
2056
- selected: number;
2057
- radius: number;
2058
- };
2059
- playhead: number;
2060
- snapGuide: number;
2061
- dropZone: number;
2062
- trackInsertion: number;
2063
- };
2064
- }
2065
-
2066
- export declare interface TimelineThemeInput {
2067
- timeline: {
2068
- background: string;
2069
- divider: string;
2070
- toolbar: {
2071
- background: string;
2072
- surface: string;
2073
- hover: string;
2074
- active: string;
2075
- divider: string;
2076
- icon: string;
2077
- text: string;
2078
- height?: number;
2079
- };
2080
- ruler: {
2081
- background: string;
2082
- text: string;
2083
- markers: string;
2084
- height?: number;
2085
- };
2086
- tracks: {
2087
- surface: string;
2088
- surfaceAlt: string;
2089
- border: string;
2090
- height?: number;
2091
- };
2092
- clips: {
2093
- video: string;
2094
- audio: string;
2095
- image: string;
2096
- text: string;
2097
- shape: string;
2098
- html: string;
2099
- luma: string;
2100
- default: string;
2101
- selected: string;
2102
- radius?: number;
2103
- };
2104
- playhead: string;
2105
- snapGuide: string;
2106
- dropZone: string;
2107
- trackInsertion: string;
2108
- };
2109
449
  }
2110
450
 
2111
- /**
2112
- * Stores the original timing intent as specified by the user.
2113
- * This is preserved even after resolution to numeric values.
2114
- * All numeric values are in seconds.
2115
- */
2116
- declare interface TimingIntent {
2117
- start: Seconds | "auto";
2118
- length: TimingValue;
2119
- }
2120
-
2121
- /**
2122
- * Command parameters for timing updates.
2123
- * Values in milliseconds (for UI convenience).
2124
- */
2125
- declare interface TimingUpdateParams {
2126
- start?: number | "auto";
2127
- length?: number | "auto" | "end";
2128
- }
2129
-
2130
- /**
2131
- * A timing value can be a numeric value (in seconds) or a special string.
2132
- * - "auto" for start: position after previous clip on track
2133
- * - "auto" for length: asset's intrinsic duration
2134
- * - "end" for length: extend to timeline end
2135
- */
2136
- declare type TimingValue = Seconds | "auto" | "end";
2137
-
2138
- export declare type TitleAsset = components["schemas"]["TitleAsset"];
2139
-
2140
451
  /**
2141
452
  * Configuration for a toolbar button.
2142
453
  */
@@ -2151,66 +462,19 @@ export declare interface ToolbarButtonConfig {
2151
462
  dividerBefore?: boolean;
2152
463
  }
2153
464
 
2154
- declare interface ToolbarButtonConfig_2 {
2155
- id: string;
2156
- icon: string;
2157
- tooltip: string;
2158
- event: string;
2159
- dividerBefore?: boolean;
2160
- }
2161
-
2162
- export declare type Track = components["schemas"]["Track"];
2163
-
2164
- export { TrackSchema }
2165
-
2166
- export declare class TranscriptionIndicator extends Entity {
2167
- private background;
2168
- private spinner;
2169
- private statusText;
2170
- private spinnerAngle;
2171
- private isVisible;
2172
- private currentMessage;
2173
- load(): Promise<void>;
2174
- show(message: string): void;
2175
- hide(): void;
2176
- getIsVisible(): boolean;
2177
- update(deltaTime: number, _elapsed: number): void;
2178
- draw(): void;
2179
- private redraw;
2180
- setPosition(x: number, y: number): void;
2181
- getWidth(): number;
2182
- dispose(): void;
2183
- }
2184
-
2185
- export declare type Transformation = components["schemas"]["Transformation"];
2186
-
2187
- export { TransformationSchema }
2188
-
2189
- export declare type Transition = components["schemas"]["Transition"];
2190
-
2191
- export { TransitionSchema }
2192
-
2193
- export declare type Tween = components["schemas"]["Tween"];
2194
-
2195
- export { tweenSchema as KeyframeSchema }
2196
- export { tweenSchema as TweenSchema }
465
+ declare type Track = components["schemas"]["Track"];
2197
466
 
2198
467
  /**
2199
468
  * Controller for managing UI elements (toolbars, utilities) separately from Canvas.
2200
469
  *
2201
470
  * This enables:
2202
471
  * - Pure preview mode (Canvas without UI)
2203
- * - Custom toolbar registration
2204
472
  * - Optional UI element loading
2205
473
  *
2206
474
  * @example
2207
475
  * ```typescript
2208
- * // Standard setup with all toolbars
2209
476
  * const ui = UIController.create(edit, canvas, { mergeFields: true });
2210
- *
2211
- * // Minimal setup for custom toolbars
2212
- * const ui = UIController.minimal(edit, canvas);
2213
- * ui.registerToolbar('text', new CustomTextToolbar(edit));
477
+ * ui.registerButton({ id: "text", icon: "...", tooltip: "Add Text" });
2214
478
  * ```
2215
479
  */
2216
480
  export declare class UIController {
@@ -2224,16 +488,23 @@ export declare class UIController {
2224
488
  readonly mergeFieldsEnabled: boolean;
2225
489
  /** Whether selection handles are enabled for drag/resize/rotate */
2226
490
  private readonly selectionHandlesEnabled;
491
+ /** Maximum total pixels for resolution picker (undefined = unlimited) */
492
+ private readonly maxPixels?;
2227
493
  private clipToolbar;
2228
494
  private toolbarMode;
2229
495
  private currentAssetType;
2230
496
  private currentTrackIndex;
2231
497
  private currentClipIndex;
2232
498
  private onKeyDownBound;
499
+ private modeButtonHandlers;
2233
500
  private buttonRegistry;
2234
501
  private buttonEvents;
2235
502
  private assetToolbar;
2236
503
  private canvasToolbar;
504
+ private lastAutoAssetX;
505
+ private lastAutoAssetY;
506
+ private lastAutoCanvasX;
507
+ private lastAutoCanvasY;
2237
508
  /**
2238
509
  * Create a UIController with all standard toolbars pre-registered.
2239
510
  * This is the recommended way to create a UIController for most use cases.
@@ -2253,7 +524,7 @@ export declare class UIController {
2253
524
  static create(edit: Edit, canvas: Canvas, options?: UIControllerOptions): UIController;
2254
525
  /**
2255
526
  * Create a minimal UIController without pre-registered toolbars.
2256
- * Use this when you want full control over which toolbars are registered.
527
+ * Use this when you only need custom buttons without default toolbars.
2257
528
  *
2258
529
  * @param edit - The Edit instance
2259
530
  * @param canvas - Optional Canvas instance
@@ -2262,8 +533,7 @@ export declare class UIController {
2262
533
  * @example
2263
534
  * ```typescript
2264
535
  * const ui = UIController.minimal(edit, canvas);
2265
- * ui.registerToolbar('text', new CustomTextToolbar(edit));
2266
- * ui.registerToolbar('video', new CustomVideoToolbar(edit));
536
+ * ui.registerButton({ id: "text", icon: "...", tooltip: "Add Text" });
2267
537
  * ```
2268
538
  */
2269
539
  static minimal(edit: Edit, canvas?: Canvas): UIController;
@@ -2280,31 +550,6 @@ export declare class UIController {
2280
550
  * Register all standard toolbars. Called by create() factory.
2281
551
  */
2282
552
  private registerStandardToolbars;
2283
- /**
2284
- * Register a toolbar for one or more asset types.
2285
- * When a clip of that type is selected, the toolbar will be shown.
2286
- *
2287
- * @param assetTypes - Single type or array of types (e.g., 'text', ['video', 'image'])
2288
- * @param toolbar - The toolbar component implementing UIRegistration
2289
- * @returns this (for chaining)
2290
- */
2291
- registerToolbar(assetTypes: string | string[], toolbar: UIRegistration): this;
2292
- /**
2293
- * Register a utility component (Inspector, TranscriptionIndicator, etc.).
2294
- * Utilities are mounted but not tied to clip selection.
2295
- *
2296
- * @param component - The utility component implementing UIRegistration
2297
- * @returns this (for chaining)
2298
- */
2299
- registerUtility(component: UIRegistration): this;
2300
- /**
2301
- * Register a PixiJS-based canvas overlay (SelectionHandles, AlignmentGuides, etc.).
2302
- * Overlays render on the canvas and receive update/draw calls each frame.
2303
- *
2304
- * @param overlay - The overlay component implementing CanvasOverlayRegistration
2305
- * @returns this (for chaining)
2306
- */
2307
- registerCanvasOverlay(overlay: CanvasOverlayRegistration): this;
2308
553
  /**
2309
554
  * Mount all registered UI components to a container.
2310
555
  * Should be called after all registrations are complete.
@@ -2313,27 +558,13 @@ export declare class UIController {
2313
558
  */
2314
559
  mount(container: HTMLElement): void;
2315
560
  /**
2316
- * Update all canvas overlays. Called by Canvas each tick.
2317
- */
2318
- updateOverlays(deltaTime: number, elapsed: number): void;
2319
- /**
2320
- * Update toolbar positions to be adjacent to the canvas content.
2321
- * Uses position: fixed with screen coordinates for complete independence from parent CSS.
2322
- * Called by Canvas after zoom, pan, or resize operations.
561
+ * Position a sidebar toolbar at autoX/autoY, applying the user's drag offset if present.
2323
562
  */
2324
- updateToolbarPositions(): void;
563
+ private applySidebarPosition;
2325
564
  /**
2326
565
  * Dispose all registered UI components and clean up event listeners.
2327
566
  */
2328
567
  dispose(): void;
2329
- /**
2330
- * Get the toolbar registered for a specific asset type.
2331
- */
2332
- getToolbar(assetType: string): UIRegistration | undefined;
2333
- /**
2334
- * Check if a toolbar is registered for an asset type.
2335
- */
2336
- hasToolbar(assetType: string): boolean;
2337
568
  /**
2338
569
  * Register a toolbar button.
2339
570
  * Buttons appear in the left toolbar and trigger events when clicked.
@@ -2362,10 +593,6 @@ export declare class UIController {
2362
593
  * @returns this (for chaining)
2363
594
  */
2364
595
  unregisterButton(id: string): this;
2365
- /**
2366
- * Get all registered toolbar buttons.
2367
- */
2368
- getButtons(): ToolbarButtonConfig[];
2369
596
  /**
2370
597
  * Subscribe to a button click event.
2371
598
  *
@@ -2381,10 +608,6 @@ export declare class UIController {
2381
608
  * @returns Unsubscribe function
2382
609
  */
2383
610
  on<K extends `button:${string}`>(event: K, handler: (payload: ButtonClickPayload) => void): () => void;
2384
- /**
2385
- * Unsubscribe from a button click event.
2386
- */
2387
- off<K extends `button:${string}`>(event: K, handler: (payload: ButtonClickPayload) => void): void;
2388
611
  /**
2389
612
  * Set the toolbar mode and update visibility accordingly.
2390
613
  * @param mode - "asset" shows asset-specific toolbar, "clip" shows ClipToolbar
@@ -2423,34 +646,12 @@ export declare interface UIControllerOptions {
2423
646
  selectionHandles?: boolean;
2424
647
  /** Enable merge fields UI (Variables panel, autocomplete). Default: false (vanilla video editor) */
2425
648
  mergeFields?: boolean;
649
+ /** Maximum total pixels allowed for resolution picker input. Omit for unlimited. */
650
+ maxPixels?: number;
2426
651
  }
2427
652
 
2428
- /**
2429
- * Interface for HTML/DOM UI components that can be registered with UIController.
2430
- * Toolbars, inspectors, and other UI elements should implement this interface.
2431
- */
2432
- export declare interface UIRegistration {
2433
- /** Mount the component to a parent container */
2434
- mount(container: HTMLElement): void;
2435
- /** Show the component for a specific clip (optional - utilities may not need this) */
2436
- show?(trackIndex: number, clipIndex: number): void;
2437
- /** Hide the component */
2438
- hide?(): void;
2439
- /** Clean up resources */
2440
- dispose(): void;
2441
- }
2442
-
2443
- declare type Vector = {
2444
- x: number;
2445
- y: number;
2446
- };
2447
-
2448
653
  export declare const VERSION: string;
2449
654
 
2450
- export declare type VideoAsset = components["schemas"]["VideoAsset"];
2451
-
2452
- export { VideoAssetSchema }
2453
-
2454
655
  export declare class VideoExporter {
2455
656
  private readonly edit;
2456
657
  private readonly canvas;
@@ -2469,7 +670,6 @@ export declare class VideoExporter {
2469
670
  private saveEditState;
2470
671
  private restoreEditState;
2471
672
  private isVideoPlayer;
2472
- private waitForPendingTranscriptions;
2473
673
  }
2474
674
 
2475
675
  export { }