@shotstack/shotstack-studio 2.0.0-beta.17 → 2.0.0-beta.19

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.
@@ -3,7 +3,6 @@ import { audioAssetSchema as AudioAssetSchema } from '@shotstack/schemas/zod';
3
3
  import { captionAssetSchema as CaptionAssetSchema } from '@shotstack/schemas/zod';
4
4
  import { clipSchema as ClipSchema } from '@shotstack/schemas/zod';
5
5
  import { components } from '@shotstack/schemas';
6
- import { Container } from 'pixi.js';
7
6
  import { cropSchema as CropSchema } from '@shotstack/schemas/zod';
8
7
  import { editSchema as EditSchema } from '@shotstack/schemas/zod';
9
8
  import { htmlAssetSchema as HtmlAssetSchema } from '@shotstack/schemas/zod';
@@ -25,61 +24,6 @@ import { z } from 'zod';
25
24
 
26
25
  export declare type Asset = components["schemas"]["Asset"];
27
26
 
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
27
  export { AssetSchema }
84
28
 
85
29
  export declare class AssetToolbar {
@@ -187,6 +131,9 @@ export declare class Canvas {
187
131
  private readonly edit;
188
132
  /** Container for interactive overlays (handles, guides). Renders above content. */
189
133
  readonly overlayContainer: pixi.Container;
134
+ private viewportContainer?;
135
+ private editBackground?;
136
+ private viewportMask?;
190
137
  private container?;
191
138
  private background?;
192
139
  private timeline?;
@@ -228,6 +175,28 @@ export declare class Canvas {
228
175
  bottom: number;
229
176
  };
230
177
  registerTimeline(timeline: Timeline): void;
178
+ /**
179
+ * Get the viewport container for coordinate transforms.
180
+ * Used by selection handles, export coordinator, and other components
181
+ * that need to convert between viewport and world coordinates.
182
+ */
183
+ getViewportContainer(): pixi.Container;
184
+ /**
185
+ * Update the edit background and viewport mask when size changes.
186
+ * Called from Edit when output size is changed.
187
+ */
188
+ updateViewportForSize(width: number, height: number, backgroundColor: string): void;
189
+ /**
190
+ * Subscribe to Edit events for visual synchronization.
191
+ * Canvas reacts to these events to update PIXI visuals.
192
+ */
193
+ private subscribeToEditEvents;
194
+ private onPlayerAddedToTrack;
195
+ private onPlayerMovedBetweenTracks;
196
+ private onPlayerRemovedFromTrack;
197
+ private onTrackContainerRemoved;
198
+ private onViewportSizeChanged;
199
+ private onViewportNeedsZoomToFit;
231
200
  private registerExtensions;
232
201
  private configureApplication;
233
202
  private onTick;
@@ -355,16 +324,6 @@ declare interface ClipRenderer {
355
324
 
356
325
  export { ClipSchema }
357
326
 
358
- declare interface ClipStats {
359
- videos: number;
360
- images: number;
361
- text: number;
362
- richText: number;
363
- luma: number;
364
- animatedClips: number;
365
- cachedFrames: number;
366
- }
367
-
368
327
  /**
369
328
  * Toolbar for clip-level properties (timing, linking).
370
329
  * Shows compact timing controls for start and length with:
@@ -385,67 +344,6 @@ export declare class ClipToolbar extends BaseToolbar {
385
344
  dispose(): void;
386
345
  }
387
346
 
388
- declare type ClipType = ResolvedClip;
389
-
390
- declare type CommandContext = {
391
- getClips(): Player[];
392
- getTracks(): Player[][];
393
- getTrack(trackIndex: number): Player[] | null;
394
- getContainer(): Container;
395
- addPlayer(trackIdx: number, player: Player): Promise<void>;
396
- addPlayerToContainer(trackIdx: number, player: Player): void;
397
- createPlayerFromAssetType(clipConfiguration: ClipType): Player;
398
- queueDisposeClip(player: Player): void;
399
- disposeClips(): void;
400
- clearClipError(trackIdx: number, clipIdx: number): void;
401
- undeleteClip(trackIdx: number, clip: Player): void;
402
- setUpdatedClip(clip: Player): void;
403
- restoreClipConfiguration(clip: Player, previousConfig: ClipType): void;
404
- updateDuration(): void;
405
- emitEvent<T extends EditEventName>(name: T, ...args: EditEventMap[T] extends void ? [] : [EditEventMap[T]]): void;
406
- findClipIndices(player: Player): {
407
- trackIndex: number;
408
- clipIndex: number;
409
- } | null;
410
- getClipAt(trackIndex: number, clipIndex: number): Player | null;
411
- getSelectedClip(): Player | null;
412
- setSelectedClip(clip: Player | null): void;
413
- movePlayerToTrackContainer(player: Player, fromTrackIdx: number, toTrackIdx: number): void;
414
- getEditState(): EditType;
415
- propagateTimingChanges(trackIndex: number, startFromClipIndex: number): void;
416
- resolveClipAutoLength(clip: Player): Promise<void>;
417
- untrackEndLengthClip(clip: Player): void;
418
- trackEndLengthClip(clip: Player): void;
419
- getMergeFields(): MergeFieldService;
420
- getOutputSize(): {
421
- width: number;
422
- height: number;
423
- };
424
- setOutputSize(width: number, height: number): void;
425
- getOutputFps(): number;
426
- setOutputFps(fps: number): void;
427
- getTimelineBackground(): string;
428
- setTimelineBackground(color: string): void;
429
- getDocument(): EditDocument | null;
430
- /** Update a clip's properties in the document (source of truth) */
431
- documentUpdateClip(trackIdx: number, clipIdx: number, updates: Partial<DocumentClipType>): void;
432
- /** Add a clip to the document, returns the added clip */
433
- documentAddClip(trackIdx: number, clip: DocumentClipType, clipIdx?: number): DocumentClipType;
434
- /** Remove a clip from the document, returns the removed clip or null */
435
- documentRemoveClip(trackIdx: number, clipIdx: number): DocumentClipType | null;
436
- /** Derive runtime Player state from document clip (applies document data to Player) */
437
- derivePlayerFromDocument(trackIdx: number, clipIdx: number): void;
438
- /**
439
- * Build resolution context for a clip at the given position.
440
- * Extracts all dependencies upfront so resolution can be pure.
441
- *
442
- * @param trackIdx - Track index
443
- * @param clipIdx - Clip index on that track
444
- * @returns Context with previousClipEnd, timelineEnd, intrinsicDuration
445
- */
446
- buildResolutionContext(trackIdx: number, clipIdx: number): ResolutionContext;
447
- };
448
-
449
347
  export declare class Controls {
450
348
  private edit;
451
349
  private seekDistance;
@@ -464,15 +362,11 @@ export { CropSchema }
464
362
 
465
363
  export declare type Destination = components["schemas"]["Destinations"];
466
364
 
467
- export declare const DestinationSchema: z.ZodUnion<readonly [z.ZodIntersection<z.ZodObject<{
468
- destinations: z.ZodOptional<z.ZodLiteral<"shotstackDestination_ShotstackDestination">>;
469
- }, z.core.$strip>, z.ZodObject<{
470
- provider: z.ZodDefault<z.ZodString>;
365
+ export declare const DestinationSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
366
+ provider: z.ZodLiteral<"shotstack">;
471
367
  exclude: z.ZodOptional<z.ZodBoolean>;
472
- }, z.core.$strip>>, z.ZodIntersection<z.ZodObject<{
473
- destinations: z.ZodOptional<z.ZodLiteral<"muxDestination_MuxDestination">>;
474
368
  }, z.core.$strip>, z.ZodObject<{
475
- provider: z.ZodDefault<z.ZodString>;
369
+ provider: z.ZodLiteral<"mux">;
476
370
  options: z.ZodOptional<z.ZodObject<{
477
371
  playbackPolicy: z.ZodOptional<z.ZodArray<z.ZodEnum<{
478
372
  public: "public";
@@ -480,10 +374,8 @@ export declare const DestinationSchema: z.ZodUnion<readonly [z.ZodIntersection<z
480
374
  }>>>;
481
375
  passthrough: z.ZodOptional<z.ZodString>;
482
376
  }, z.core.$strip>>;
483
- }, z.core.$strip>>, z.ZodIntersection<z.ZodObject<{
484
- destinations: z.ZodOptional<z.ZodLiteral<"s3Destination_S3Destination">>;
485
377
  }, z.core.$strip>, z.ZodObject<{
486
- provider: z.ZodDefault<z.ZodString>;
378
+ provider: z.ZodLiteral<"s3">;
487
379
  options: z.ZodOptional<z.ZodObject<{
488
380
  region: z.ZodString;
489
381
  bucket: z.ZodString;
@@ -491,27 +383,21 @@ export declare const DestinationSchema: z.ZodUnion<readonly [z.ZodIntersection<z
491
383
  filename: z.ZodOptional<z.ZodString>;
492
384
  acl: z.ZodOptional<z.ZodString>;
493
385
  }, z.core.$strip>>;
494
- }, z.core.$strip>>, z.ZodIntersection<z.ZodObject<{
495
- destinations: z.ZodOptional<z.ZodLiteral<"googleCloudStorageDestination_GoogleCloudStorageDestination">>;
496
386
  }, z.core.$strip>, z.ZodObject<{
497
- provider: z.ZodDefault<z.ZodString>;
387
+ provider: z.ZodLiteral<"google-cloud-storage">;
498
388
  options: z.ZodOptional<z.ZodObject<{
499
389
  bucket: z.ZodString;
500
390
  prefix: z.ZodOptional<z.ZodString>;
501
391
  filename: z.ZodOptional<z.ZodString>;
502
392
  }, z.core.$strip>>;
503
- }, z.core.$strip>>, z.ZodIntersection<z.ZodObject<{
504
- destinations: z.ZodOptional<z.ZodLiteral<"googleDriveDestination_GoogleDriveDestination">>;
505
393
  }, z.core.$strip>, z.ZodObject<{
506
- provider: z.ZodDefault<z.ZodString>;
394
+ provider: z.ZodLiteral<"google-drive">;
507
395
  options: z.ZodObject<{
508
396
  folderId: z.ZodString;
509
397
  filename: z.ZodOptional<z.ZodString>;
510
398
  }, z.core.$strip>;
511
- }, z.core.$strip>>, z.ZodIntersection<z.ZodObject<{
512
- destinations: z.ZodOptional<z.ZodLiteral<"vimeoDestination_VimeoDestination">>;
513
399
  }, z.core.$strip>, z.ZodObject<{
514
- provider: z.ZodDefault<z.ZodString>;
400
+ provider: z.ZodLiteral<"vimeo">;
515
401
  options: z.ZodOptional<z.ZodObject<{
516
402
  name: z.ZodOptional<z.ZodString>;
517
403
  description: z.ZodOptional<z.ZodString>;
@@ -533,13 +419,13 @@ export declare const DestinationSchema: z.ZodUnion<readonly [z.ZodIntersection<z
533
419
  nobody: "nobody";
534
420
  contacts: "contacts";
535
421
  }>>;
422
+ download: z.ZodOptional<z.ZodBoolean>;
423
+ add: z.ZodOptional<z.ZodBoolean>;
536
424
  }, z.core.$strip>>;
537
425
  folderUri: z.ZodOptional<z.ZodString>;
538
426
  }, z.core.$strip>>;
539
- }, z.core.$strip>>, z.ZodIntersection<z.ZodObject<{
540
- destinations: z.ZodOptional<z.ZodLiteral<"tiktokDestination_TiktokDestination">>;
541
427
  }, z.core.$strip>, z.ZodObject<{
542
- provider: z.ZodDefault<z.ZodString>;
428
+ provider: z.ZodLiteral<"tiktok">;
543
429
  options: z.ZodOptional<z.ZodObject<{
544
430
  title: z.ZodOptional<z.ZodString>;
545
431
  privacyLevel: z.ZodOptional<z.ZodEnum<{
@@ -551,88 +437,60 @@ export declare const DestinationSchema: z.ZodUnion<readonly [z.ZodIntersection<z
551
437
  disableStitch: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
552
438
  disableComment: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
553
439
  }, z.core.$strip>>;
554
- }, z.core.$strip>>]>;
440
+ }, z.core.$strip>], "provider">;
555
441
 
556
- declare type DocumentClipType = Clip;
557
-
558
- export declare class Edit extends Entity {
559
- private static readonly ZIndexPadding;
560
- /**
561
- * Maximum number of commands to keep in undo history.
562
- * Prevents unbounded memory growth in long editing sessions.
563
- * Each command may hold Player references and deep-cloned configs.
564
- */
442
+ export declare class Edit {
565
443
  private static readonly MAX_HISTORY_SIZE;
566
- assetLoader: AssetLoader;
567
- events: EventEmitter<EditEventMap & InternalEventMap>;
444
+ private document;
445
+ private backgroundColor;
568
446
  private edit;
569
447
  private tracks;
570
- private clipsToDispose;
571
- private clips;
572
- private commandHistory;
573
- private commandIndex;
574
- playbackTime: number;
575
- totalDuration: number;
448
+ playbackTime: Seconds;
449
+ totalDuration: Seconds;
450
+ isPlaying: boolean;
451
+ private get clips();
452
+ private get endLengthClips();
576
453
  private cachedTimelineEnd;
577
- private endLengthClips;
578
- private isBatchingEvents;
579
- private isLoadingEdit;
580
- private syncCorrectionCount;
581
- private toolbarButtons;
582
454
  private canvas;
583
455
  private lumaMaskController;
456
+ private playerReconciler;
457
+ private outputSettings;
458
+ private selectionManager;
459
+ private alignmentGuides;
460
+ private commandHistory;
461
+ private commandIndex;
462
+ private commandQueue;
463
+ private clipsToDispose;
584
464
  private clipErrors;
465
+ private playerByClipId;
585
466
  private fontMetadata;
467
+ private isBatchingEvents;
468
+ private syncCorrectionCount;
469
+ private isExporting;
586
470
  /**
587
471
  * Create an Edit instance from a template configuration.
588
- *
589
- * @param template - The Edit JSON configuration (aligns with Shotstack API contract)
590
- *
591
- * @example
592
- * ```typescript
593
- * const edit = new Edit(template);
594
- * await edit.load();
595
- *
596
- * const canvas = new Canvas(edit);
597
- * await canvas.load();
598
- * ```
599
472
  */
600
473
  constructor(template: Edit_2);
474
+ /**
475
+ * Load the edit session.
476
+ */
601
477
  load(): Promise<void>;
602
478
  /**
603
- * Initialize players and timing from the document.
604
- * Called during initial load() and can be called again via loadEdit() for hot-reload.
605
- * @param source - The source identifier for events (default: "load")
479
+ * Initialize runtime from the document.
606
480
  */
607
481
  private initializeFromDocument;
608
- private updateViewportMask;
609
- /** Update canvas visuals after size change (viewport mask, background, zoom) */
610
- private updateCanvasForSize;
611
482
  play(): void;
612
483
  pause(): void;
613
- seek(target: number): void;
484
+ seek(target: Seconds): void;
614
485
  stop(): void;
615
486
  /**
616
487
  * Reload the edit with a new configuration (hot-reload).
617
- * Uses smart diffing to only update what changed when possible.
618
- *
619
- * For initial loading, use the constructor + load() pattern instead:
620
- * ```typescript
621
- * const edit = new Edit(template);
622
- * await edit.load();
623
- * ```
624
- *
625
- * @param edit - The new Edit configuration to load
626
488
  */
627
489
  loadEdit(edit: Edit_2): Promise<void>;
628
490
  private loadSoundtrack;
629
491
  getEdit(): Edit_2;
630
492
  /**
631
- * Validates an edit configuration without applying it.
632
- * Use this to pre-validate user input before calling loadEdit().
633
- *
634
- * @param edit - The edit configuration to validate
635
- * @returns Validation result with valid boolean and any errors
493
+ * Validates an edit configuration.
636
494
  */
637
495
  validateEdit(edit: unknown): {
638
496
  valid: boolean;
@@ -642,285 +500,96 @@ export declare class Edit extends Entity {
642
500
  }>;
643
501
  };
644
502
  getResolvedEdit(): ResolvedEdit;
645
- /**
646
- * Get the original parsed edit configuration.
647
- * Unlike getResolvedEdit(), this returns the edit as originally parsed,
648
- * with all clips present regardless of loading state.
649
- */
650
- getOriginalEdit(): ResolvedEdit | null;
651
503
  addClip(trackIdx: number, clip: Clip): void | Promise<void>;
652
504
  getClip(trackIdx: number, clipIdx: number): Clip | null;
653
- /**
654
- * Get the error state for a clip that failed to load.
655
- * Returns null if the clip loaded successfully.
656
- */
657
- getClipError(trackIdx: number, clipIdx: number): {
658
- error: string;
659
- assetType: string;
660
- } | null;
661
505
  /**
662
506
  * Clear the error for a deleted clip and shift indices for remaining errors.
663
- * Called when a clip is deleted to keep error indices in sync.
664
507
  */
665
508
  private clearClipErrorAndShift;
666
- getPlayerClip(trackIdx: number, clipIdx: number): Player | null;
667
- /** Get the exportable asset for a clip, preserving merge field templates */
668
- getOriginalAsset(trackIndex: number, clipIndex: number): unknown | undefined;
669
- deleteClip(trackIdx: number, clipIdx: number): void;
670
- splitClip(trackIndex: number, clipIndex: number, splitTime: number): void;
509
+ deleteClip(trackIdx: number, clipIdx: number): Promise<void>;
510
+ splitClip(trackIndex: number, clipIndex: number, splitTime: number): Promise<void>;
671
511
  addTrack(trackIdx: number, track: Track): Promise<void>;
672
512
  getTrack(trackIdx: number): Track | null;
673
513
  deleteTrack(trackIdx: number): void;
674
- getTotalDuration(): number;
675
- getMemoryStats(): {
676
- clipCounts: Record<string, number>;
677
- totalClips: number;
678
- richTextCacheStats: {
679
- clips: number;
680
- totalFrames: number;
681
- };
682
- textPlayerCount: number;
683
- lumaMaskCount: number;
684
- commandHistorySize: number;
685
- trackCount: number;
686
- };
687
- getComprehensiveMemoryStats(): {
688
- textureStats: {
689
- videos: {
690
- count: number;
691
- totalMB: number;
692
- avgDimensions: string;
693
- };
694
- images: {
695
- count: number;
696
- totalMB: number;
697
- avgDimensions: string;
698
- };
699
- text: {
700
- count: number;
701
- totalMB: number;
702
- };
703
- richText: {
704
- count: number;
705
- totalMB: number;
706
- };
707
- luma: {
708
- count: number;
709
- totalMB: number;
710
- };
711
- animated: {
712
- count: number;
713
- frames: number;
714
- totalMB: number;
715
- };
716
- totalTextures: number;
717
- totalMB: number;
718
- };
719
- assetDetails: Array<{
720
- id: string;
721
- type: "video" | "image" | "text" | "rich-text" | "luma" | "audio" | "html" | "shape" | "caption" | "unknown";
722
- label: string;
723
- width: number;
724
- height: number;
725
- estimatedMB: number;
726
- }>;
727
- systemStats: {
728
- clipCount: number;
729
- trackCount: number;
730
- commandCount: number;
731
- };
732
- };
733
- private estimateTextureMB;
734
- private getAssetLabel;
735
- getPlaybackHealth(): {
736
- activePlayerCount: number;
737
- totalPlayerCount: number;
738
- videoMaxDrift: number;
739
- audioMaxDrift: number;
740
- syncCorrections: number;
741
- };
742
- recordSyncCorrection(): void;
743
- undo(): void;
744
- redo(): void;
745
- updateClip(trackIdx: number, clipIdx: number, updates: Partial<Clip>): void;
514
+ undo(): Promise<void>;
515
+ redo(): Promise<void>;
746
516
  /**
747
- * Update clip timing mode and/or values.
748
- * Supports manual values, "auto", and "end" timing modes.
749
- * @param trackIdx - Track index
750
- * @param clipIdx - Clip index within track
751
- * @param params - Timing update parameters (start and/or length in milliseconds)
517
+ * Add a command to history without executing it.
752
518
  */
753
- updateClipTiming(trackIdx: number, clipIdx: number, params: TimingUpdateParams): void;
754
- executeEditCommand(command: EditCommand): void | Promise<void>;
755
- protected executeCommand(command: EditCommand): void | Promise<void>;
519
+ private addCommandToHistory;
520
+ updateClip(trackIdx: number, clipIdx: number, updates: Partial<Clip>): Promise<void>;
756
521
  /**
757
- * Emits a unified `edit:changed` event after any state mutation.
758
- * Consumers can subscribe to this single event instead of tracking 31+ granular events.
522
+ * Handle command result - only add to history if successful.
759
523
  */
760
- protected emitEditChanged(source: string): void;
524
+ private handleCommandResult;
761
525
  /**
762
526
  * Detects merge field placeholders in the raw edit before substitution.
763
- * Returns a map of clip keys ("trackIdx-clipIdx") to their merge field bindings.
764
- * Each binding maps a property path to its placeholder and resolved value.
765
527
  */
766
528
  private detectMergeFieldBindings;
767
529
  /**
768
530
  * Recursively walks an object to find merge field placeholders.
769
- * Returns a map of property paths to their bindings.
770
531
  */
771
532
  private detectBindingsInObject;
772
533
  /**
773
534
  * Checks if edit has structural changes requiring full reload.
774
- * Structural = track count, clip count, or asset type changed.
775
535
  */
776
536
  private hasStructuralChanges;
777
537
  /**
778
- * Applies granular changes without full reload (preserves undo history, no flash).
779
- * Only called when structure is unchanged (same track/clip counts).
538
+ * Transfers existing clip IDs from the current document to the new edit configuration.
539
+ */
540
+ private preserveClipIdsForGranularUpdate;
541
+ /**
542
+ * Applies granular changes without full reload.
780
543
  */
781
544
  private applyGranularChanges;
782
- protected createCommandContext(): CommandContext;
783
545
  private queueDisposeClip;
784
- protected disposeClips(): void;
785
546
  /**
786
547
  * Remove fonts from timeline.fonts that are no longer referenced by any clip.
787
- * This keeps the document clean and prevents accumulation of unused font URLs.
788
548
  */
789
549
  private cleanupUnusedFonts;
790
550
  /**
791
551
  * Extract the filename (without extension) from a font URL.
792
- * e.g., "https://fonts.gstatic.com/s/inter/v20/UcCO3Fwr...Bg-4.ttf" → "UcCO3Fwr...Bg-4"
793
552
  */
794
553
  private extractFilenameFromUrl;
795
554
  private disposeClip;
796
555
  private unloadClipAssets;
797
- protected clearClips(): void;
798
556
  private updateTotalDuration;
799
557
  private resolveAllTiming;
800
- propagateTimingChanges(trackIndex: number, startFromClipIndex: number): void;
801
- resolveClipAutoLength(clip: Player): Promise<void>;
802
- private addPlayerToContainer;
803
558
  private movePlayerToTrackContainer;
804
- private createPlayerFromAssetType;
805
559
  private addPlayer;
806
- selectClip(trackIndex: number, clipIndex: number): void;
807
- clearSelection(): void;
808
- isClipSelected(trackIndex: number, clipIndex: number): boolean;
809
- getSelectedClipInfo(): {
810
- trackIndex: number;
811
- clipIndex: number;
812
- player: Player;
813
- } | null;
814
- /**
815
- * Copy a clip to the internal clipboard
816
- */
817
- copyClip(trackIdx: number, clipIdx: number): void;
818
- /**
819
- * Paste the copied clip at the current playhead position
820
- */
821
- pasteClip(): void;
822
- /**
823
- * Check if there is a clip in the clipboard
824
- */
825
- hasCopiedClip(): boolean;
826
- findClipIndices(player: Player): {
827
- trackIndex: number;
828
- clipIndex: number;
829
- } | null;
830
- getClipAt(trackIndex: number, clipIndex: number): Player | null;
831
- selectPlayer(player: Player): void;
832
- isPlayerSelected(player: Player): boolean;
833
- /**
834
- * Move the selected clip by a pixel delta.
835
- * Used for keyboard arrow key positioning.
836
- */
837
- moveSelectedClip(deltaX: number, deltaY: number): void;
838
- setExportMode(exporting: boolean): void;
839
- isInExportMode(): boolean;
840
- setCanvas(canvas: Canvas): void;
841
- getCanvasZoom(): number;
842
- setOutputSize(width: number, height: number): void;
843
- setOutputFps(fps: number): void;
560
+ setOutputSize(width: number, height: number): Promise<void>;
561
+ setOutputFps(fps: number): Promise<void>;
844
562
  getOutputFps(): number;
845
- setOutputFormat(format: string): void;
563
+ setOutputFormat(format: string): Promise<void>;
846
564
  getOutputFormat(): string;
847
- setOutputDestinations(destinations: Destination[]): void;
565
+ setOutputDestinations(destinations: Destination[]): Promise<void>;
848
566
  getOutputDestinations(): Destination[];
849
- setOutputResolution(resolution: string): void;
567
+ setOutputResolution(resolution: string): Promise<void>;
850
568
  getOutputResolution(): string | undefined;
851
- setOutputAspectRatio(aspectRatio: string): void;
569
+ setOutputAspectRatio(aspectRatio: string): Promise<void>;
852
570
  getOutputAspectRatio(): string | undefined;
853
571
  getTimelineFonts(): Array<{
854
572
  src: string;
855
573
  }>;
856
- /**
857
- * Look up a font URL by family name and weight.
858
- * Uses normalized metadata extracted from TTF files during font loading.
859
- * This enables rich-text font resolution for template fonts with UUID-based URLs.
860
- *
861
- * @param familyName - The font family name (e.g., "Lato", "Lato Light")
862
- * @param weight - The font weight (e.g., 300 for Light, 900 for Black)
863
- * @returns The font URL if found, null otherwise
864
- */
865
- getFontUrlByFamilyAndWeight(familyName: string, weight: number): string | null;
866
- /**
867
- * Remove any fonts from timeline.fonts that are no longer used by clips.
868
- * Call this after changing a clip's font to clean up the old font.
869
- */
870
- pruneUnusedFonts(): void;
871
- setTimelineBackground(color: string): void;
574
+ setTimelineBackground(color: string): Promise<void>;
575
+ private setTimelineBackgroundInternal;
872
576
  getTimelineBackground(): string;
577
+ /** Map of asset src → original asset type (for reliable luma detachment during undo) */
578
+ private originalAssetTypes;
873
579
  /**
874
- * Resolve merge field placeholders in a string.
875
- * Replaces {{ FIELD_NAME }} patterns with their current values.
876
- *
877
- * @param input - String potentially containing merge field placeholders
878
- * @returns String with all merge fields resolved to their values
879
- */
880
- resolveMergeFields(input: string): string;
881
- /**
882
- * @deprecated Use `ui.registerButton()` instead.
883
- */
884
- registerToolbarButton(config: ToolbarButtonConfig_2): void;
885
- /**
886
- * @deprecated Use `ui.unregisterButton()` instead.
887
- */
888
- unregisterToolbarButton(id: string): void;
889
- /**
890
- * @deprecated Use `ui.getButtons()` instead.
580
+ * Find the luma clip attached to a content clip via timing match.
891
581
  */
892
- getToolbarButtons(): ToolbarButtonConfig_2[];
893
- /** Get the exportable clip (with merge field placeholders restored) */
894
- protected getTemplateClip(trackIndex: number, clipIndex: number): ResolvedClip | null;
895
- /** Get the text content from the template clip (with merge field placeholders) */
896
- getTemplateClipText(trackIdx: number, clipIdx: number): string | null;
897
- /** Map of content player → luma player for attachment tracking */
898
- private lumaAttachments;
899
- /** Map of asset src → original asset type (for reliable luma detachment) */
900
- private originalAssetTypes;
582
+ private findAttachedLumaPlayer;
901
583
  /**
902
584
  * Attach a luma mask to a specific clip.
903
- * Creates a luma clip on the same track with synchronized timing.
904
- *
905
- * @param trackIndex - Track index of the content clip
906
- * @param clipIndex - Clip index of the content clip
907
- * @param lumaSrc - URL of the luma mask asset (video or image)
908
585
  */
909
586
  attachLumaToClip(trackIndex: number, clipIndex: number, lumaSrc: string): Promise<void>;
910
587
  /**
911
588
  * Detach the luma mask from a clip.
912
- * Removes the luma clip from the track.
913
- *
914
- * @param trackIndex - Track index of the content clip
915
- * @param clipIndex - Clip index of the content clip
916
589
  */
917
590
  detachLumaFromClip(trackIndex: number, clipIndex: number): Promise<void>;
918
591
  /**
919
592
  * Get the luma mask attached to a clip, if any.
920
- *
921
- * @param trackIndex - Track index of the content clip
922
- * @param clipIndex - Clip index of the content clip
923
- * @returns Luma info or null if no luma attached
924
593
  */
925
594
  getClipLuma(trackIndex: number, clipIndex: number): {
926
595
  src: string;
@@ -928,231 +597,20 @@ export declare class Edit extends Entity {
928
597
  } | null;
929
598
  /**
930
599
  * Check if a clip has a luma mask attached.
931
- *
932
- * @param trackIndex - Track index of the content clip
933
- * @param clipIndex - Clip index of the content clip
934
600
  */
935
601
  hasLumaMask(trackIndex: number, clipIndex: number): boolean;
936
602
  /**
937
- * Register a luma attachment in the Edit Session's map.
938
- * This is called when a luma is attached to a content clip (e.g., during drag-drop).
939
- * The map is used by syncAttachedLuma() to coordinate timing between attached clips.
603
+ * Find the content clip that best matches a luma (by temporal overlap).
940
604
  */
941
- registerLumaAttachment(contentTrackIndex: number, contentClipIndex: number, lumaTrackIndex: number, lumaClipIndex: number): void;
942
- /**
943
- * Transform a clip to luma type (for attachment).
944
- * Recreates the player with luma asset type while preserving the src.
945
- *
946
- * @param trackIndex - Track index of the clip
947
- * @param clipIndex - Clip index of the clip
948
- */
949
- transformToLuma(trackIndex: number, clipIndex: number): void;
950
- /**
951
- * Transform a luma clip back to its original type (for detachment).
952
- * Uses stored original type for reliability, with URL extension fallback.
953
- *
954
- * @param trackIndex - Track index of the luma clip
955
- * @param clipIndex - Clip index of the luma clip
956
- */
957
- transformFromLuma(trackIndex: number, clipIndex: number): void;
605
+ private findBestContentMatch;
958
606
  private setupIntentListeners;
959
607
  }
960
608
 
961
609
  declare type Edit_2 = components["schemas"]["Edit"];
962
610
 
963
- declare type EditCommand = {
964
- execute(context?: CommandContext): void | Promise<void>;
965
- undo?(context?: CommandContext): void | Promise<void>;
966
- /**
967
- * Optional cleanup when command is pruned from history.
968
- * Called when command is removed to free memory (e.g., Player references, deep-cloned configs).
969
- */
970
- dispose?(): void;
971
- readonly name: string;
972
- };
973
-
974
611
  /** Alias for Edit type to avoid conflicts with SDK's Edit class */
975
612
  export declare type EditConfig = Edit_2;
976
613
 
977
- declare class EditDocument {
978
- private data;
979
- constructor(edit: Edit_2);
980
- /**
981
- * Get the raw timeline configuration
982
- */
983
- getTimeline(): Edit_2["timeline"];
984
- /**
985
- * Get timeline background color
986
- */
987
- getBackground(): string | undefined;
988
- /**
989
- * Get all tracks (raw, unresolved)
990
- */
991
- getTracks(): Track[];
992
- /**
993
- * Get a specific track by index
994
- */
995
- getTrack(index: number): Track | null;
996
- /**
997
- * Get total number of tracks
998
- */
999
- getTrackCount(): number;
1000
- /**
1001
- * Get soundtrack configuration
1002
- */
1003
- getSoundtrack(): Soundtrack | undefined;
1004
- /**
1005
- * Get a specific clip by track and clip index
1006
- */
1007
- getClip(trackIndex: number, clipIndex: number): Clip | null;
1008
- /**
1009
- * Get all clips in a track
1010
- */
1011
- getClipsInTrack(trackIndex: number): Clip[];
1012
- /**
1013
- * Get total number of clips across all tracks
1014
- */
1015
- getClipCount(): number;
1016
- /**
1017
- * Get clip count in a specific track
1018
- */
1019
- getClipCountInTrack(trackIndex: number): number;
1020
- /**
1021
- * Get the raw output configuration
1022
- */
1023
- getOutput(): Edit_2["output"];
1024
- /**
1025
- * Get output size (width/height)
1026
- * @throws Error if size is not defined
1027
- */
1028
- getSize(): Size;
1029
- /**
1030
- * Get output format (mp4, gif, etc.)
1031
- */
1032
- getFormat(): Edit_2["output"]["format"];
1033
- /**
1034
- * Get output FPS
1035
- */
1036
- getFps(): number | undefined;
1037
- /**
1038
- * Get output resolution preset
1039
- */
1040
- getResolution(): Edit_2["output"]["resolution"];
1041
- /**
1042
- * Get output aspect ratio
1043
- */
1044
- getAspectRatio(): Edit_2["output"]["aspectRatio"];
1045
- /**
1046
- * Get merge field definitions
1047
- */
1048
- getMergeFields(): Edit_2["merge"];
1049
- /**
1050
- * Add a new track at the specified index
1051
- * @returns The added track
1052
- */
1053
- addTrack(index: number, track?: Track): Track;
1054
- /**
1055
- * Remove a track at the specified index
1056
- * @returns The removed track, or null if index invalid
1057
- */
1058
- removeTrack(index: number): Track | null;
1059
- /**
1060
- * Add a clip to a track
1061
- * @returns The added clip
1062
- */
1063
- addClip(trackIndex: number, clip: Clip, clipIndex?: number): Clip;
1064
- /**
1065
- * Remove a clip from a track
1066
- * @returns The removed clip, or null if indices invalid
1067
- */
1068
- removeClip(trackIndex: number, clipIndex: number): Clip | null;
1069
- /**
1070
- * Update a clip's properties (partial update)
1071
- */
1072
- updateClip(trackIndex: number, clipIndex: number, updates: Partial<Clip>): void;
1073
- /**
1074
- * Replace a clip entirely
1075
- */
1076
- replaceClip(trackIndex: number, clipIndex: number, newClip: Clip): Clip | null;
1077
- /**
1078
- * Set timeline background color
1079
- */
1080
- setBackground(color: string): void;
1081
- /**
1082
- * Set soundtrack
1083
- */
1084
- setSoundtrack(soundtrack: Soundtrack | undefined): void;
1085
- /**
1086
- * Get timeline fonts
1087
- */
1088
- getFonts(): Array<{
1089
- src: string;
1090
- }>;
1091
- /**
1092
- * Add a font to the timeline (if not already present)
1093
- */
1094
- addFont(src: string): void;
1095
- /**
1096
- * Remove a font from the timeline
1097
- */
1098
- removeFont(src: string): void;
1099
- /**
1100
- * Set all timeline fonts (replaces existing)
1101
- */
1102
- setFonts(fonts: Array<{
1103
- src: string;
1104
- }>): void;
1105
- /**
1106
- * Set output size
1107
- */
1108
- setSize(size: Size): void;
1109
- /**
1110
- * Set output format
1111
- */
1112
- setFormat(format: Edit_2["output"]["format"]): void;
1113
- /**
1114
- * Set output FPS (must be a valid FPS value)
1115
- */
1116
- setFps(fps: Edit_2["output"]["fps"]): void;
1117
- /**
1118
- * Set output resolution preset
1119
- */
1120
- setResolution(resolution: Edit_2["output"]["resolution"]): void;
1121
- /**
1122
- * Clear output resolution preset
1123
- */
1124
- clearResolution(): void;
1125
- /**
1126
- * Set output aspect ratio
1127
- */
1128
- setAspectRatio(aspectRatio: Edit_2["output"]["aspectRatio"]): void;
1129
- /**
1130
- * Clear output aspect ratio
1131
- */
1132
- clearAspectRatio(): void;
1133
- /**
1134
- * Clear output size (for use when setting resolution/aspectRatio)
1135
- */
1136
- clearSize(): void;
1137
- /**
1138
- * Set merge field definitions
1139
- */
1140
- setMergeFields(mergeFields: Edit_2["merge"]): void;
1141
- /**
1142
- * Export the document as raw Edit JSON (preserves "auto", "end", placeholders)
1143
- * This is what gets sent to the backend API.
1144
- */
1145
- toJSON(): Edit_2;
1146
- /**
1147
- * Create an EditDocument from raw Edit JSON
1148
- */
1149
- static fromJSON(json: Edit_2): EditDocument;
1150
- /**
1151
- * Create a deep clone of this document
1152
- */
1153
- clone(): EditDocument;
1154
- }
1155
-
1156
614
  export declare const EditEvent: {
1157
615
  readonly PlaybackPlay: "playback:play";
1158
616
  readonly PlaybackPause: "playback:pause";
@@ -1298,26 +756,12 @@ export declare type EditEventName = (typeof EditEvent)[keyof typeof EditEvent];
1298
756
 
1299
757
  export { EditSchema }
1300
758
 
1301
- declare type EditType = ResolvedEdit;
1302
-
1303
759
  declare abstract class Entity {
1304
760
  private readonly container;
1305
761
  constructor();
1306
762
  abstract load(): Promise<void>;
1307
763
  }
1308
764
 
1309
- declare class EventEmitter<TEventPayloadMap extends EventPayloadMap = EventPayloadMap> {
1310
- private readonly events;
1311
- constructor();
1312
- on<TEventName extends keyof TEventPayloadMap>(name: TEventName, listener: Listener<TEventPayloadMap[TEventName]>): () => void;
1313
- once<TEventName extends keyof TEventPayloadMap>(name: TEventName, listener: Listener<TEventPayloadMap[TEventName]>): () => void;
1314
- off<TEventName extends keyof TEventPayloadMap>(name: TEventName, listener: Listener<TEventPayloadMap[TEventName]>): void;
1315
- clear(name: keyof TEventPayloadMap): void;
1316
- emit<TEventName extends keyof TEventPayloadMap>(name: TEventName, ...args: TEventPayloadMap[TEventName] extends void ? [] : [TEventPayloadMap[TEventName]]): void;
1317
- }
1318
-
1319
- declare type EventPayloadMap<TPayload = any> = Record<string, TPayload>;
1320
-
1321
765
  /** SDK-extended CaptionAsset with stroke, width, height, alignment */
1322
766
  export declare type ExtendedCaptionAsset = CaptionAsset & {
1323
767
  stroke?: {
@@ -1348,22 +792,23 @@ export declare type ImageAsset = components["schemas"]["ImageAsset"];
1348
792
 
1349
793
  export { ImageAssetSchema }
1350
794
 
1351
- export declare class Inspector extends Entity {
1352
- private static readonly Width;
1353
- private static readonly Height;
1354
- fps: number;
1355
- playbackTime: number;
1356
- playbackDuration: number;
1357
- isPlaying: boolean;
1358
- clipStats: ClipStats | null;
1359
- systemStats: SystemStats | null;
1360
- playbackHealth: PlaybackHealth | null;
1361
- clipCounts: Record<string, number>;
1362
- totalClips: number;
1363
- commandHistorySize: number;
1364
- trackCount: number;
1365
- private background;
1366
- private text;
795
+ /**
796
+ * Inspector displays performance stats as an HTML overlay.
797
+ * Shows FPS, memory, playback health, and clip statistics.
798
+ */
799
+ export declare class Inspector {
800
+ private container;
801
+ private animationFrameId;
802
+ private lastFrameTime;
803
+ private edit;
804
+ private fpsEl;
805
+ private playbackEl;
806
+ private frameStatsEl;
807
+ private frameSparklineEl;
808
+ private jankEl;
809
+ private heapEl;
810
+ private heapSparklineEl;
811
+ private clipsEl;
1367
812
  private historySamples;
1368
813
  private readonly maxSamples;
1369
814
  private lastSampleTime;
@@ -1371,41 +816,28 @@ export declare class Inspector extends Entity {
1371
816
  private frameTimes;
1372
817
  private readonly frameTimeWindow;
1373
818
  private readonly jankThreshold;
1374
- constructor();
1375
- load(): Promise<void>;
1376
- update(_: number, deltaMS: number): void;
819
+ constructor(edit: Edit);
820
+ /**
821
+ * Mount the inspector to a parent element.
822
+ * @param parent - The parent element to append the inspector to
823
+ */
824
+ mount(parent: HTMLElement): void;
825
+ private startUpdateLoop;
826
+ private update;
1377
827
  private trackFrameTime;
1378
828
  private getFrameStats;
1379
829
  private getFrameTimeSparkline;
1380
830
  private addHistorySample;
1381
831
  private getJsHeapSparkline;
1382
- private renderStats;
1383
- private getSyncStatusIcon;
1384
- draw(): void;
1385
- dispose(): void;
1386
- private formatClipCounts;
832
+ private render;
1387
833
  private getMemoryInfo;
1388
834
  private bytesToMegabytes;
835
+ dispose(): void;
1389
836
  }
1390
837
 
1391
- declare type InternalEventMap = {
1392
- "canvas:clipClicked": {
1393
- player: Player;
1394
- };
1395
- "canvas:backgroundClicked": void;
1396
- "font:capabilitiesChanged": {
1397
- supportsBold: boolean;
1398
- };
1399
- "toolbar:buttonsChanged": {
1400
- buttons: ToolbarButtonConfig_2[];
1401
- };
1402
- };
1403
-
1404
838
  declare type Keyframe_2 = Tween;
1405
839
  export { Keyframe_2 as Keyframe }
1406
840
 
1407
- declare type Listener<TPayload = any> = (payload: TPayload) => void;
1408
-
1409
841
  export declare type LumaAsset = components["schemas"]["LumaAsset"];
1410
842
 
1411
843
  export { LumaAssetSchema }
@@ -1452,6 +884,7 @@ export declare class MediaToolbar extends BaseToolbar {
1452
884
  private isDynamicSource;
1453
885
  private dynamicFieldName;
1454
886
  private originalSrc;
887
+ private abortController;
1455
888
  mount(parent: HTMLElement): void;
1456
889
  /**
1457
890
  * Mount composite UI components into their placeholder elements.
@@ -1527,70 +960,6 @@ declare interface MergeField_2 {
1527
960
  description?: string;
1528
961
  }
1529
962
 
1530
- /**
1531
- * Tracks a merge field binding for a specific property path.
1532
- * Used to restore placeholders on export for properties that haven't changed.
1533
- */
1534
- declare interface MergeFieldBinding {
1535
- /** The original placeholder string, e.g., "{{ HERO_IMAGE }}" */
1536
- placeholder: string;
1537
- /** The resolved value at binding time, used for change detection */
1538
- resolvedValue: string;
1539
- }
1540
-
1541
- declare class MergeFieldService {
1542
- private fields;
1543
- private events;
1544
- constructor(events: EventEmitter);
1545
- /**
1546
- * Register or update a merge field.
1547
- * @param field The merge field to register
1548
- * @param options.silent If true, suppresses event emission (for command-based operations)
1549
- */
1550
- register(field: MergeField_2, options?: {
1551
- silent?: boolean;
1552
- }): void;
1553
- /**
1554
- * Remove a merge field by name.
1555
- * @param name The field name to remove
1556
- * @param options.silent If true, suppresses event emission (for command-based operations)
1557
- */
1558
- remove(name: string, options?: {
1559
- silent?: boolean;
1560
- }): boolean;
1561
- /** Get a merge field by name */
1562
- get(name: string): MergeField_2 | undefined;
1563
- /** Get all registered merge fields */
1564
- getAll(): MergeField_2[];
1565
- /** Clear all merge fields */
1566
- clear(): void;
1567
- /**
1568
- * Apply merge field substitutions to a string.
1569
- * Replaces {{ FIELD_NAME }} patterns with their default values.
1570
- */
1571
- resolve(input: string): string;
1572
- /**
1573
- * Check if a string contains unresolved merge fields.
1574
- * Returns true if any {{ FIELD_NAME }} patterns remain after resolution.
1575
- */
1576
- hasUnresolved(input: string): boolean;
1577
- /**
1578
- * Extract the first merge field name from a string.
1579
- * Returns null if no merge field pattern is found.
1580
- */
1581
- extractFieldName(input: string): string | null;
1582
- /** Check if a string is a merge field template (contains {{ FIELD }}) */
1583
- isMergeFieldTemplate(input: string): boolean;
1584
- /** Create a merge field template string from a field name */
1585
- createTemplate(fieldName: string): string;
1586
- /** Export fields in Shotstack API format ({ find, replace }) */
1587
- toSerializedArray(): SerializedMergeField[];
1588
- /** Import fields from Shotstack API format (does not emit event - called during loadEdit) */
1589
- loadFromSerialized(fields: SerializedMergeField[]): void;
1590
- /** Generate a unique field name with a given prefix (e.g., MEDIA_1, MEDIA_2) */
1591
- generateUniqueName(prefix: string): string;
1592
- }
1593
-
1594
963
  export declare interface NumericKeyframe {
1595
964
  start: number;
1596
965
  length: number;
@@ -1641,159 +1010,8 @@ export declare const OutputSizeSchema: z.ZodObject<{
1641
1010
  height: z.ZodOptional<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNumber>>;
1642
1011
  }, z.core.$strip>;
1643
1012
 
1644
- declare interface PlaybackHealth {
1645
- activePlayerCount: number;
1646
- totalPlayerCount: number;
1647
- videoMaxDrift: number;
1648
- audioMaxDrift: number;
1649
- syncCorrections: number;
1650
- }
1651
-
1652
- /**
1653
- * Base class for all visual content players in the canvas.
1654
- *
1655
- * Player is responsible for rendering clip content (video, image, text, etc.)
1656
- * and applying keyframe animations.
1657
- *
1658
- */
1659
- declare abstract class Player extends Entity {
1660
- private static readonly DiscardedFrameCount;
1661
- layer: number;
1662
- shouldDispose: boolean;
1663
- readonly playerType: PlayerType;
1664
- protected edit: Edit;
1665
- clipConfiguration: ResolvedClip;
1666
- private timingIntent;
1667
- private resolvedTiming;
1668
- private positionBuilder;
1669
- private offsetXKeyframeBuilder?;
1670
- private offsetYKeyframeBuilder?;
1671
- private scaleKeyframeBuilder?;
1672
- private opacityKeyframeBuilder?;
1673
- private rotationKeyframeBuilder?;
1674
- private maskXKeyframeBuilder?;
1675
- private wipeMask;
1676
- protected contentContainer: pixi.Container;
1677
- /**
1678
- * Tracks which properties came from merge field templates.
1679
- */
1680
- private mergeFieldBindings;
1681
- constructor(edit: Edit, clipConfiguration: ResolvedClip, playerType: PlayerType);
1682
- reconfigureAfterRestore(): void;
1683
- /**
1684
- * Reload the asset for this player (e.g., when asset.src changes).
1685
- * Override in subclasses that have loadable assets (image, video).
1686
- * Default implementation is a no-op.
1687
- */
1688
- reloadAsset(): Promise<void>;
1689
- protected configureKeyframes(): void;
1690
- load(): Promise<void>;
1691
- update(_: number, __: number): void;
1692
- private updateWipeMask;
1693
- draw(): void;
1694
- dispose(): void;
1695
- getStart(): Seconds;
1696
- getLength(): Seconds;
1697
- getEnd(): Seconds;
1698
- getTimingIntent(): TimingIntent;
1699
- setTimingIntent(intent: Partial<TimingIntent>): void;
1700
- getResolvedTiming(): ResolvedTiming;
1701
- setResolvedTiming(timing: ResolvedTiming): void;
1702
- /**
1703
- * Set a merge field binding for a property path.
1704
- */
1705
- setMergeFieldBinding(path: string, binding: MergeFieldBinding): void;
1706
- /**
1707
- * Get the merge field binding for a property path, if any.
1708
- */
1709
- getMergeFieldBinding(path: string): MergeFieldBinding | undefined;
1710
- /**
1711
- * Remove a merge field binding (e.g., when user changes the value).
1712
- */
1713
- removeMergeFieldBinding(path: string): void;
1714
- /**
1715
- * Get all merge field bindings for this player.
1716
- */
1717
- getMergeFieldBindings(): Map<string, MergeFieldBinding>;
1718
- /**
1719
- * Bulk set bindings during player initialization.
1720
- */
1721
- setInitialBindings(bindings: Map<string, MergeFieldBinding>): void;
1722
- /**
1723
- * Get the exportable clip configuration with merge field placeholders restored.
1724
- */
1725
- getExportableClip(): Clip;
1726
- /**
1727
- * Get the playback time relative to clip start, in seconds.
1728
- */
1729
- getPlaybackTime(): number;
1730
- abstract getSize(): Size;
1731
- /**
1732
- * Returns the source content dimensions (before fit scaling).
1733
- */
1734
- getContentSize(): Size;
1735
- getOpacity(): number;
1736
- getPosition(): Vector;
1737
- getPivot(): Vector;
1738
- protected getFitScale(): number;
1739
- getScale(): number;
1740
- protected getContainerScale(): Vector;
1741
- getRotation(): number;
1742
- isActive(): boolean;
1743
- shouldDiscardFrame(): boolean;
1744
- /**
1745
- * Handle pointer down - emit click event for selection handling.
1746
- * All drag/resize/rotate interaction is handled by SelectionHandles.
1747
- */
1748
- private onPointerDown;
1749
- private clipHasKeyframes;
1750
- protected applyFixedDimensions(): void;
1751
- protected applyAnchorPositioning(anchor: string, clipWidth: number, clipHeight: number, sprite: pixi.Sprite): void;
1752
- /**
1753
- * Override in subclasses to enable edge resize handles for dimension changes.
1754
- * When true, edge handles will be shown instead of corner scale handles.
1755
- */
1756
- supportsEdgeResize(): boolean;
1757
- /**
1758
- * Called when dimensions change via edge resize. Override in subclasses to handle re-rendering.
1759
- */
1760
- protected onDimensionsChanged(): void;
1761
- /**
1762
- * Public wrapper for notifying dimension changes.
1763
- * Called by SelectionHandles after edge resize operations.
1764
- */
1765
- notifyDimensionsChanged(): void;
1766
- }
1767
-
1768
- declare enum PlayerType {
1769
- Video = "video",
1770
- Image = "image",
1771
- Audio = "audio",
1772
- Text = "text",
1773
- RichText = "rich-text",
1774
- Luma = "luma",
1775
- Html = "html",
1776
- Shape = "shape",
1777
- Caption = "caption",
1778
- Svg = "svg"
1779
- }
1780
-
1781
1013
  declare type ResolutionChangeCallback = (width: number, height: number) => void;
1782
1014
 
1783
- /**
1784
- * Context required to resolve timing intent to concrete values.
1785
- *
1786
- * @see resolveTimingIntent - the pure function that uses this context
1787
- */
1788
- declare interface ResolutionContext {
1789
- /** End time of previous clip on same track (for start: "auto") */
1790
- readonly previousClipEnd: Seconds;
1791
- /** Total duration of timeline excluding "end" clips (for length: "end") */
1792
- readonly timelineEnd: Seconds;
1793
- /** Intrinsic duration from asset metadata, null if not yet loaded (for length: "auto") */
1794
- readonly intrinsicDuration: Seconds | null;
1795
- }
1796
-
1797
1015
  declare type ResolvedClip = Omit<Clip, "start" | "length"> & {
1798
1016
  start: Seconds;
1799
1017
  length: Seconds;
@@ -1805,16 +1023,6 @@ declare type ResolvedEdit = Omit<Edit_2, "timeline"> & {
1805
1023
  };
1806
1024
  };
1807
1025
 
1808
- /**
1809
- * Resolved timing values in seconds.
1810
- * Matches the external @shotstack/schemas unit convention.
1811
- * Conversion to milliseconds happens only in the Player layer for pixi rendering.
1812
- */
1813
- declare interface ResolvedTiming {
1814
- start: Seconds;
1815
- length: Seconds;
1816
- }
1817
-
1818
1026
  declare type ResolvedTrack = {
1819
1027
  clips: ResolvedClip[];
1820
1028
  };
@@ -1878,6 +1086,8 @@ export declare class RichTextToolbar extends BaseToolbar {
1878
1086
  private animationDurationValue;
1879
1087
  private animationStyleSection;
1880
1088
  private animationDirectionSection;
1089
+ private pendingAnimationDuration;
1090
+ private animationDurationThrottle;
1881
1091
  private transitionPopup;
1882
1092
  private transitionPanel;
1883
1093
  private effectPopup;
@@ -1903,7 +1113,10 @@ export declare class RichTextToolbar extends BaseToolbar {
1903
1113
  private updateWeightPopupState;
1904
1114
  private setFontWeight;
1905
1115
  private toggleSizePopup;
1116
+ /** Build popup once at mount - uses event delegation (no per-item listeners) */
1906
1117
  private buildSizePopup;
1118
+ /** Update active state without rebuilding DOM */
1119
+ private updateSizePopupState;
1907
1120
  private applyManualSize;
1908
1121
  private toggleSpacingPopup;
1909
1122
  private toggleAnimationPopup;
@@ -1965,6 +1178,7 @@ export declare class SelectionHandles implements CanvasOverlayRegistration {
1965
1178
  private app;
1966
1179
  private positionBuilder;
1967
1180
  private selectedPlayer;
1181
+ private selectedClipId;
1968
1182
  private selectedTrackIndex;
1969
1183
  private selectedClipIndex;
1970
1184
  private isHovering;
@@ -2016,32 +1230,12 @@ export declare class SelectionHandles implements CanvasOverlayRegistration {
2016
1230
  private hasStateChanged;
2017
1231
  }
2018
1232
 
2019
- /**
2020
- * Serialized format for JSON export (matches Shotstack API).
2021
- * The replace value can be any type - strings, numbers, booleans, objects.
2022
- */
2023
- declare interface SerializedMergeField {
2024
- find: string;
2025
- replace: unknown;
2026
- }
2027
-
2028
1233
  export declare type ShapeAsset = components["schemas"]["ShapeAsset"];
2029
1234
 
2030
1235
  export { ShapeAssetSchema }
2031
1236
 
2032
- declare type Size = {
2033
- width: number;
2034
- height: number;
2035
- };
2036
-
2037
1237
  export declare type Soundtrack = components["schemas"]["Soundtrack"];
2038
1238
 
2039
- declare interface SystemStats {
2040
- clipCount: number;
2041
- trackCount: number;
2042
- commandCount: number;
2043
- }
2044
-
2045
1239
  export declare type TextAsset = components["schemas"]["TextAsset"];
2046
1240
 
2047
1241
  export { TextAssetSchema }
@@ -2116,9 +1310,9 @@ export declare class TextToolbar extends BaseToolbar {
2116
1310
  dispose(): void;
2117
1311
  }
2118
1312
 
2119
- /** HTML/CSS-based Timeline component extending TimelineEntity for SDK consistency */
2120
- export declare class Timeline extends TimelineEntity {
1313
+ export declare class Timeline {
2121
1314
  private readonly edit;
1315
+ readonly element: HTMLElement;
2122
1316
  private readonly container;
2123
1317
  private readonly stateManager;
2124
1318
  private features;
@@ -2138,17 +1332,17 @@ export declare class Timeline extends TimelineEntity {
2138
1332
  private lastFrameTime;
2139
1333
  private isInteracting;
2140
1334
  private isLoaded;
1335
+ private thumbnailRenderPending;
2141
1336
  private readonly handleTimelineUpdated;
2142
1337
  private readonly handlePlaybackPlay;
2143
1338
  private readonly handlePlaybackPause;
2144
1339
  private readonly handleClipSelected;
2145
1340
  private readonly handleClipLoadFailed;
1341
+ private readonly handleClipUpdated;
2146
1342
  private readonly handleRulerMouseMove;
2147
1343
  constructor(edit: Edit, container: HTMLElement, options?: TimelineOptions);
2148
1344
  /** Initialize and mount the timeline */
2149
1345
  load(): Promise<void>;
2150
- /** Update component state (called each frame during active rendering) */
2151
- update(_deltaTime: number, _elapsed: number): void;
2152
1346
  /** Render/draw component to DOM (called each frame after update) */
2153
1347
  draw(): void;
2154
1348
  /** Clean up and unmount the timeline */
@@ -2172,7 +1366,7 @@ export declare class Timeline extends TimelineEntity {
2172
1366
  setZoom(pixelsPerSecond: number): void;
2173
1367
  zoomIn(): void;
2174
1368
  zoomOut(): void;
2175
- scrollTo(time: number): void;
1369
+ scrollTo(time: Seconds): void;
2176
1370
  /** Recalculate size from container and re-render */
2177
1371
  resize(): void;
2178
1372
  selectClip(trackIndex: number, clipIndex: number): void;
@@ -2184,38 +1378,6 @@ export declare class Timeline extends TimelineEntity {
2184
1378
  findClipAtPosition(x: number, y: number): ClipInfo | null;
2185
1379
  }
2186
1380
 
2187
- /**
2188
- * Base class for HTML-based timeline components.
2189
- * Mirrors the Entity pattern used by PixiJS components (load/update/draw/dispose lifecycle).
2190
- */
2191
- declare abstract class TimelineEntity {
2192
- readonly element: HTMLElement;
2193
- protected children: TimelineEntity[];
2194
- constructor(tagName?: keyof HTMLElementTagNameMap, className?: string);
2195
- /** Initialize the component and its children */
2196
- abstract load(): Promise<void>;
2197
- /** Update component state (called each frame during active rendering) */
2198
- abstract update(deltaTime: number, elapsed: number): void;
2199
- /** Render/draw component to DOM (called each frame after update) */
2200
- abstract draw(): void;
2201
- /** Clean up resources and remove from DOM */
2202
- abstract dispose(): void;
2203
- /** Add a child entity */
2204
- protected addChild(child: TimelineEntity): void;
2205
- /** Remove a child entity */
2206
- protected removeChild(child: TimelineEntity): void;
2207
- /** Remove all children */
2208
- protected removeAllChildren(): void;
2209
- /** Load all children */
2210
- protected loadChildren(): Promise<void>;
2211
- /** Update all children */
2212
- protected updateChildren(deltaTime: number, elapsed: number): void;
2213
- /** Draw all children */
2214
- protected drawChildren(): void;
2215
- /** Dispose all children */
2216
- protected disposeChildren(): void;
2217
- }
2218
-
2219
1381
  /** Feature toggles for Timeline */
2220
1382
  export declare interface TimelineFeatures {
2221
1383
  /** Show toolbar with playback controls */
@@ -2349,33 +1511,6 @@ export declare interface TimelineThemeInput {
2349
1511
  };
2350
1512
  }
2351
1513
 
2352
- /**
2353
- * Stores the original timing intent as specified by the user.
2354
- * This is preserved even after resolution to numeric values.
2355
- * All numeric values are in seconds.
2356
- */
2357
- declare interface TimingIntent {
2358
- start: Seconds | "auto";
2359
- length: TimingValue;
2360
- }
2361
-
2362
- /**
2363
- * Command parameters for timing updates.
2364
- * Values in milliseconds (for UI convenience).
2365
- */
2366
- declare interface TimingUpdateParams {
2367
- start?: number | "auto";
2368
- length?: number | "auto" | "end";
2369
- }
2370
-
2371
- /**
2372
- * A timing value can be a numeric value (in seconds) or a special string.
2373
- * - "auto" for start: position after previous clip on track
2374
- * - "auto" for length: asset's intrinsic duration
2375
- * - "end" for length: extend to timeline end
2376
- */
2377
- declare type TimingValue = Seconds | "auto" | "end";
2378
-
2379
1514
  export declare type TitleAsset = components["schemas"]["TitleAsset"];
2380
1515
 
2381
1516
  /**
@@ -2392,14 +1527,6 @@ export declare interface ToolbarButtonConfig {
2392
1527
  dividerBefore?: boolean;
2393
1528
  }
2394
1529
 
2395
- declare interface ToolbarButtonConfig_2 {
2396
- id: string;
2397
- icon: string;
2398
- tooltip: string;
2399
- event: string;
2400
- dividerBefore?: boolean;
2401
- }
2402
-
2403
1530
  export declare type Track = components["schemas"]["Track"];
2404
1531
 
2405
1532
  export { TrackSchema }
@@ -2473,6 +1600,7 @@ export declare class UIController {
2473
1600
  private currentTrackIndex;
2474
1601
  private currentClipIndex;
2475
1602
  private onKeyDownBound;
1603
+ private modeButtonHandlers;
2476
1604
  private buttonRegistry;
2477
1605
  private buttonEvents;
2478
1606
  private assetToolbar;
@@ -2685,11 +1813,6 @@ export declare interface UIRegistration {
2685
1813
  dispose(): void;
2686
1814
  }
2687
1815
 
2688
- declare type Vector = {
2689
- x: number;
2690
- y: number;
2691
- };
2692
-
2693
1816
  export declare const VERSION: string;
2694
1817
 
2695
1818
  export declare type VideoAsset = components["schemas"]["VideoAsset"];