@twick/timeline 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +175 -0
  2. package/dist/context/timeline-context.d.ts +33 -0
  3. package/dist/context/undo-redo-context.d.ts +21 -0
  4. package/dist/core/addOns/animation.d.ts +24 -0
  5. package/dist/core/addOns/frame-effect.d.ts +14 -0
  6. package/dist/core/addOns/text-effect.d.ts +19 -0
  7. package/dist/core/editor/timeline.editor.d.ts +94 -0
  8. package/dist/core/elements/audio.element.d.ts +20 -0
  9. package/dist/core/elements/base.element.d.ts +35 -0
  10. package/dist/core/elements/caption.element.d.ts +10 -0
  11. package/dist/core/elements/circle.element.d.ts +13 -0
  12. package/dist/core/elements/icon.element.d.ts +9 -0
  13. package/dist/core/elements/image.element.d.ts +32 -0
  14. package/dist/core/elements/rect.element.d.ts +11 -0
  15. package/dist/core/elements/text.element.d.ts +26 -0
  16. package/dist/core/elements/video.element.d.ts +41 -0
  17. package/dist/core/track/track.d.ts +77 -0
  18. package/dist/core/track/track.friend.d.ts +34 -0
  19. package/dist/core/visitor/element-adder.d.ts +29 -0
  20. package/dist/core/visitor/element-cloner.d.ts +22 -0
  21. package/dist/core/visitor/element-deserializer.d.ts +23 -0
  22. package/dist/core/visitor/element-remover.d.ts +28 -0
  23. package/dist/core/visitor/element-serializer.d.ts +23 -0
  24. package/dist/core/visitor/element-splitter.d.ts +28 -0
  25. package/dist/core/visitor/element-updater.d.ts +28 -0
  26. package/dist/core/visitor/element-validator.d.ts +34 -0
  27. package/dist/core/visitor/element-visitor.d.ts +19 -0
  28. package/dist/index.d.ts +36 -0
  29. package/dist/index.js +2630 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/index.mjs +2628 -0
  32. package/dist/index.mjs.map +1 -0
  33. package/dist/services/data.service.d.ts +25 -0
  34. package/dist/types/index.d.ts +169 -0
  35. package/dist/utils/constants.d.ts +55 -0
  36. package/dist/utils/register-editor.d.ts +8 -0
  37. package/dist/utils/timeline.utils.d.ts +11 -0
  38. package/package.json +40 -0
package/dist/index.js ADDED
@@ -0,0 +1,2630 @@
1
+ (function(global, factory) {
2
+ typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("react/jsx-runtime"), require("react")) : typeof define === "function" && define.amd ? define(["exports", "react/jsx-runtime", "react"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.TwickTimeline = {}, global.jsxRuntime, global.React));
3
+ })(this, function(exports2, jsxRuntime, react) {
4
+ "use strict";var __defProp = Object.defineProperty;
5
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
6
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
7
+
8
+ const PLAYER_STATE = {
9
+ REFRESH: "Refresh",
10
+ PLAYING: "Playing",
11
+ PAUSED: "Paused"
12
+ };
13
+ const CAPTION_STYLE = {
14
+ WORD_BG_HIGHLIGHT: "highlight_bg",
15
+ WORD_BY_WORD: "word_by_word",
16
+ WORD_BY_WORD_WITH_BG: "word_by_word_with_bg"
17
+ };
18
+ const CAPTION_STYLE_OPTIONS = {
19
+ [CAPTION_STYLE.WORD_BG_HIGHLIGHT]: {
20
+ label: "Highlight Background",
21
+ value: CAPTION_STYLE.WORD_BG_HIGHLIGHT
22
+ },
23
+ [CAPTION_STYLE.WORD_BY_WORD]: {
24
+ label: "Word by Word",
25
+ value: CAPTION_STYLE.WORD_BY_WORD
26
+ },
27
+ [CAPTION_STYLE.WORD_BY_WORD_WITH_BG]: {
28
+ label: "Word with Background",
29
+ value: CAPTION_STYLE.WORD_BY_WORD_WITH_BG
30
+ }
31
+ };
32
+ const CAPTION_FONT = {
33
+ size: 40
34
+ };
35
+ const CAPTION_COLOR = {
36
+ text: "#ffffff",
37
+ highlight: "#ff4081",
38
+ bgColor: "#8C52FF"
39
+ };
40
+ const WORDS_PER_PHRASE = 4;
41
+ const TIMELINE_ACTION = {
42
+ NONE: "none",
43
+ SET_PLAYER_STATE: "setPlayerState",
44
+ UPDATE_PLAYER_DATA: "updatePlayerData",
45
+ ON_PLAYER_UPDATED: "onPlayerUpdated"
46
+ };
47
+ const TIMELINE_ELEMENT_TYPE = {
48
+ VIDEO: "video",
49
+ CAPTION: "caption",
50
+ IMAGE: "image",
51
+ AUDIO: "audio",
52
+ TEXT: "text",
53
+ RECT: "rect",
54
+ CIRCLE: "circle",
55
+ ICON: "icon"
56
+ };
57
+ const PROCESS_STATE = {
58
+ IDLE: "Idle",
59
+ PROCESSING: "Processing",
60
+ COMPLETED: "Completed",
61
+ FAILED: "Failed"
62
+ };
63
+ const getDecimalNumber = (num, precision = 3) => {
64
+ return Number(num.toFixed(precision));
65
+ };
66
+ const getTotalDuration = (tracks) => {
67
+ return (tracks || []).reduce(
68
+ (maxDuration, timeline) => Math.max(
69
+ maxDuration,
70
+ ((timeline == null ? void 0 : timeline.elements) || []).reduce(
71
+ (timelineDuration, element) => Math.max(timelineDuration, element.e),
72
+ 0
73
+ )
74
+ ),
75
+ 0
76
+ );
77
+ };
78
+ const generateShortUuid = () => {
79
+ return "xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
80
+ const r = Math.random() * 16 | 0, v = c === "x" ? r : r & 3 | 8;
81
+ return v.toString(16);
82
+ });
83
+ };
84
+ const getCurrentElements = (currentTime, tracks) => {
85
+ const currentElements = [];
86
+ if (tracks == null ? void 0 : tracks.length) {
87
+ for (let i = 0; i < tracks.length; i++) {
88
+ if (tracks[i]) {
89
+ const elements = tracks[i].getElements();
90
+ for (let j = 0; j < elements.length; j++) {
91
+ const element = elements[j];
92
+ if (element.getStart() <= currentTime && element.getEnd() >= currentTime) {
93
+ currentElements.push(element);
94
+ }
95
+ }
96
+ }
97
+ }
98
+ }
99
+ return currentElements;
100
+ };
101
+ const canSplitElement = (element, currentTime) => {
102
+ return element.getStart() <= currentTime && element.getEnd() >= currentTime;
103
+ };
104
+ const isElementId = (id) => id.startsWith("e-");
105
+ const isTrackId = (id) => id.startsWith("t-");
106
+ const imageDimensionsCache = {};
107
+ const videoMetaCache = {};
108
+ const audioDurationCache = {};
109
+ const getAudioDuration = (audioSrc) => {
110
+ if (audioDurationCache[audioSrc]) {
111
+ return Promise.resolve(audioDurationCache[audioSrc]);
112
+ }
113
+ return new Promise((resolve, reject) => {
114
+ const audio = document.createElement("audio");
115
+ audio.preload = "metadata";
116
+ const isSafeUrl = /^(https?:|blob:|data:audio\/)/i.test(audioSrc);
117
+ if (!isSafeUrl) {
118
+ throw new Error("Unsafe audio source URL");
119
+ }
120
+ audio.src = audioSrc;
121
+ audio.onloadedmetadata = () => {
122
+ const duration = audio.duration;
123
+ audioDurationCache[audioSrc] = duration;
124
+ resolve(duration);
125
+ };
126
+ audio.onerror = () => {
127
+ reject(new Error("Failed to load audio metadata"));
128
+ };
129
+ });
130
+ };
131
+ const concurrencyLimit = 5;
132
+ let activeCount = 0;
133
+ const queue = [];
134
+ function runNext() {
135
+ if (queue.length === 0 || activeCount >= concurrencyLimit) return;
136
+ const next = queue.shift();
137
+ if (next) {
138
+ activeCount++;
139
+ next();
140
+ }
141
+ }
142
+ function limit(fn) {
143
+ return new Promise((resolve, reject) => {
144
+ const task = () => {
145
+ fn().then(resolve).catch(reject).finally(() => {
146
+ activeCount--;
147
+ runNext();
148
+ });
149
+ };
150
+ if (activeCount < concurrencyLimit) {
151
+ activeCount++;
152
+ task();
153
+ } else {
154
+ queue.push(task);
155
+ }
156
+ });
157
+ }
158
+ const loadImageDimensions = (url) => {
159
+ return new Promise((resolve, reject) => {
160
+ if (typeof document === "undefined") {
161
+ reject(new Error("getImageDimensions() is only available in the browser."));
162
+ return;
163
+ }
164
+ const img = new Image();
165
+ img.onload = () => {
166
+ resolve({ width: img.naturalWidth, height: img.naturalHeight });
167
+ };
168
+ img.onerror = reject;
169
+ img.src = url;
170
+ });
171
+ };
172
+ const getImageDimensions = (url) => {
173
+ if (imageDimensionsCache[url]) {
174
+ return Promise.resolve(imageDimensionsCache[url]);
175
+ }
176
+ return limit(() => loadImageDimensions(url)).then((dimensions) => {
177
+ imageDimensionsCache[url] = dimensions;
178
+ return dimensions;
179
+ });
180
+ };
181
+ const getVideoMeta = (videoSrc) => {
182
+ if (videoMetaCache[videoSrc]) {
183
+ return Promise.resolve(videoMetaCache[videoSrc]);
184
+ }
185
+ return new Promise((resolve, reject) => {
186
+ const video = document.createElement("video");
187
+ video.preload = "metadata";
188
+ const isSafeUrl = /^(https?:|blob:|data:video\/)/i.test(videoSrc);
189
+ if (!isSafeUrl) {
190
+ reject(new Error("Unsafe video source URL"));
191
+ return;
192
+ }
193
+ video.src = videoSrc;
194
+ video.onloadedmetadata = () => {
195
+ const meta = {
196
+ width: video.videoWidth,
197
+ height: video.videoHeight,
198
+ duration: video.duration
199
+ };
200
+ videoMetaCache[videoSrc] = meta;
201
+ resolve(meta);
202
+ };
203
+ video.onerror = () => reject(new Error("Failed to load video metadata"));
204
+ });
205
+ };
206
+ const getObjectFitSize = (objectFit, elementSize, containerSize) => {
207
+ const elementAspectRatio = elementSize.width / elementSize.height;
208
+ const containerAspectRatio = containerSize.width / containerSize.height;
209
+ switch (objectFit) {
210
+ case "contain":
211
+ if (elementAspectRatio > containerAspectRatio) {
212
+ return {
213
+ width: containerSize.width,
214
+ height: containerSize.width / elementAspectRatio
215
+ };
216
+ } else {
217
+ return {
218
+ width: containerSize.height * elementAspectRatio,
219
+ height: containerSize.height
220
+ };
221
+ }
222
+ case "cover":
223
+ if (elementAspectRatio > containerAspectRatio) {
224
+ return {
225
+ width: containerSize.height * elementAspectRatio,
226
+ height: containerSize.height
227
+ };
228
+ } else {
229
+ return {
230
+ width: containerSize.width,
231
+ height: containerSize.width / elementAspectRatio
232
+ };
233
+ }
234
+ case "fill":
235
+ return {
236
+ width: containerSize.width,
237
+ height: containerSize.height
238
+ };
239
+ default:
240
+ return {
241
+ width: elementSize.width,
242
+ height: elementSize.height
243
+ };
244
+ }
245
+ };
246
+ class TrackElement {
247
+ constructor(type, id) {
248
+ __publicField(this, "id");
249
+ __publicField(this, "type");
250
+ __publicField(this, "s");
251
+ __publicField(this, "e");
252
+ __publicField(this, "trackId");
253
+ __publicField(this, "name");
254
+ __publicField(this, "animation");
255
+ __publicField(this, "props");
256
+ this.id = id ?? `e-${generateShortUuid()}`;
257
+ this.type = type;
258
+ this.props = {
259
+ x: 0,
260
+ y: 0
261
+ };
262
+ }
263
+ getId() {
264
+ return this.id;
265
+ }
266
+ getType() {
267
+ return this.type;
268
+ }
269
+ getStart() {
270
+ return this.s;
271
+ }
272
+ getEnd() {
273
+ return this.e;
274
+ }
275
+ getDuration() {
276
+ return this.e - this.s;
277
+ }
278
+ getTrackId() {
279
+ return this.trackId;
280
+ }
281
+ getProps() {
282
+ return this.props;
283
+ }
284
+ getName() {
285
+ return this.name;
286
+ }
287
+ getAnimation() {
288
+ return this.animation;
289
+ }
290
+ getPosition() {
291
+ var _a, _b;
292
+ return {
293
+ x: ((_a = this.props) == null ? void 0 : _a.x) ?? 0,
294
+ y: ((_b = this.props) == null ? void 0 : _b.y) ?? 0
295
+ };
296
+ }
297
+ setId(id) {
298
+ this.id = id;
299
+ return this;
300
+ }
301
+ setType(type) {
302
+ this.type = type;
303
+ return this;
304
+ }
305
+ setStart(s) {
306
+ this.s = Math.max(0, s);
307
+ return this;
308
+ }
309
+ setEnd(e) {
310
+ this.e = Math.max(this.s ?? 0, e);
311
+ return this;
312
+ }
313
+ setTrackId(trackId) {
314
+ this.trackId = trackId;
315
+ return this;
316
+ }
317
+ setName(name) {
318
+ this.name = name;
319
+ return this;
320
+ }
321
+ setAnimation(animation) {
322
+ this.animation = animation;
323
+ return this;
324
+ }
325
+ setPosition(position) {
326
+ this.props.x = position.x;
327
+ this.props.y = position.y;
328
+ return this;
329
+ }
330
+ setProps(props) {
331
+ this.props = structuredClone(props);
332
+ return this;
333
+ }
334
+ }
335
+ class VideoElement extends TrackElement {
336
+ constructor(src, parentSize) {
337
+ super(TIMELINE_ELEMENT_TYPE.VIDEO);
338
+ __publicField(this, "baseSize");
339
+ __publicField(this, "mediaDuration");
340
+ __publicField(this, "parentSize");
341
+ __publicField(this, "backgroundColor");
342
+ __publicField(this, "objectFit");
343
+ __publicField(this, "frameEffects");
344
+ __publicField(this, "frame");
345
+ this.objectFit = "cover";
346
+ this.frameEffects = [];
347
+ this.parentSize = parentSize;
348
+ this.props = {
349
+ src,
350
+ play: true,
351
+ playbackRate: 1,
352
+ time: 0,
353
+ mediaFilter: "none",
354
+ volume: 1
355
+ };
356
+ }
357
+ getParentSize() {
358
+ return this.parentSize;
359
+ }
360
+ getFrame() {
361
+ return this.frame;
362
+ }
363
+ getFrameEffects() {
364
+ return this.frameEffects;
365
+ }
366
+ getBackgroundColor() {
367
+ return this.backgroundColor;
368
+ }
369
+ getObjectFit() {
370
+ return this.objectFit;
371
+ }
372
+ getMediaDuration() {
373
+ return this.mediaDuration;
374
+ }
375
+ getStartAt() {
376
+ return this.props.time || 0;
377
+ }
378
+ getPosition() {
379
+ return {
380
+ x: this.frame.x ?? 0,
381
+ y: this.frame.y ?? 0
382
+ };
383
+ }
384
+ async updateVideoMeta(updateFrame = true) {
385
+ const meta = await getVideoMeta(this.props.src);
386
+ if (updateFrame) {
387
+ const baseSize = getObjectFitSize(
388
+ "contain",
389
+ { width: meta.width, height: meta.height },
390
+ this.parentSize
391
+ );
392
+ this.frame = {
393
+ ...this.frame,
394
+ size: [baseSize.width, baseSize.height]
395
+ };
396
+ }
397
+ this.mediaDuration = meta.duration;
398
+ }
399
+ setPosition(position) {
400
+ this.frame.x = position.x;
401
+ this.frame.y = position.y;
402
+ return this;
403
+ }
404
+ async setSrc(src) {
405
+ this.props.src = src;
406
+ await this.updateVideoMeta();
407
+ return this;
408
+ }
409
+ setMediaDuration(mediaDuration) {
410
+ this.mediaDuration = mediaDuration;
411
+ return this;
412
+ }
413
+ setParentSize(parentSize) {
414
+ this.parentSize = structuredClone(parentSize);
415
+ return this;
416
+ }
417
+ setObjectFit(objectFit) {
418
+ this.objectFit = objectFit;
419
+ return this;
420
+ }
421
+ setFrame(frame) {
422
+ this.frame = structuredClone(frame);
423
+ return this;
424
+ }
425
+ setPlay(play) {
426
+ this.props.play = play;
427
+ return this;
428
+ }
429
+ setPlaybackRate(playbackRate) {
430
+ this.props.playbackRate = playbackRate;
431
+ return this;
432
+ }
433
+ setStartAt(time) {
434
+ this.props.time = Math.max(0, time);
435
+ return this;
436
+ }
437
+ setMediaFilter(mediaFilter) {
438
+ this.props.mediaFilter = mediaFilter;
439
+ return this;
440
+ }
441
+ setVolume(volume) {
442
+ this.props.volume = volume;
443
+ return this;
444
+ }
445
+ setBackgroundColor(backgroundColor) {
446
+ this.backgroundColor = backgroundColor;
447
+ return this;
448
+ }
449
+ setProps(props) {
450
+ this.props = {
451
+ play: this.props.play,
452
+ ...structuredClone(props),
453
+ src: this.props.src
454
+ };
455
+ return this;
456
+ }
457
+ setFrameEffects(frameEffects) {
458
+ this.frameEffects = frameEffects;
459
+ return this;
460
+ }
461
+ addFrameEffect(frameEffect) {
462
+ var _a;
463
+ (_a = this.frameEffects) == null ? void 0 : _a.push(frameEffect);
464
+ return this;
465
+ }
466
+ accept(visitor) {
467
+ return visitor.visitVideoElement(this);
468
+ }
469
+ }
470
+ class AudioElement extends TrackElement {
471
+ constructor(src) {
472
+ super(TIMELINE_ELEMENT_TYPE.AUDIO);
473
+ __publicField(this, "mediaDuration");
474
+ this.props = {
475
+ src,
476
+ time: 0,
477
+ play: true,
478
+ playbackRate: 1,
479
+ volume: 1,
480
+ loop: false
481
+ };
482
+ }
483
+ getMediaDuration() {
484
+ return this.mediaDuration;
485
+ }
486
+ getStartAt() {
487
+ return this.props.time || 0;
488
+ }
489
+ async updateAudioMeta() {
490
+ this.mediaDuration = await getAudioDuration(this.props.src);
491
+ }
492
+ async setSrc(src) {
493
+ this.props.src = src;
494
+ await this.updateAudioMeta();
495
+ return this;
496
+ }
497
+ setMediaDuration(mediaDuration) {
498
+ this.mediaDuration = mediaDuration;
499
+ return this;
500
+ }
501
+ setVolume(volume) {
502
+ this.props.volume = volume;
503
+ return this;
504
+ }
505
+ setLoop(loop) {
506
+ this.props.loop = loop;
507
+ return this;
508
+ }
509
+ setStartAt(time) {
510
+ this.props.time = Math.max(0, time);
511
+ return this;
512
+ }
513
+ setPlaybackRate(playbackRate) {
514
+ this.props.playbackRate = playbackRate;
515
+ return this;
516
+ }
517
+ setProps(props) {
518
+ this.props = {
519
+ play: this.props.play,
520
+ ...structuredClone(props),
521
+ src: this.props.src
522
+ };
523
+ return this;
524
+ }
525
+ accept(visitor) {
526
+ return visitor.visitAudioElement(this);
527
+ }
528
+ }
529
+ class ImageElement extends TrackElement {
530
+ constructor(src, parentSize) {
531
+ super(TIMELINE_ELEMENT_TYPE.IMAGE);
532
+ __publicField(this, "backgroundColor");
533
+ __publicField(this, "parentSize");
534
+ __publicField(this, "objectFit");
535
+ __publicField(this, "frameEffects");
536
+ __publicField(this, "frame");
537
+ this.parentSize = parentSize;
538
+ this.objectFit = "cover";
539
+ this.frameEffects = [];
540
+ this.props = {
541
+ src,
542
+ mediaFilter: "none"
543
+ };
544
+ this.frame = {
545
+ x: 0,
546
+ y: 0
547
+ };
548
+ }
549
+ getParentSize() {
550
+ return this.parentSize;
551
+ }
552
+ getFrame() {
553
+ return this.frame;
554
+ }
555
+ getFrameEffects() {
556
+ return this.frameEffects;
557
+ }
558
+ getBackgroundColor() {
559
+ return this.backgroundColor;
560
+ }
561
+ getObjectFit() {
562
+ return this.objectFit;
563
+ }
564
+ getPosition() {
565
+ return {
566
+ x: this.frame.x ?? 0,
567
+ y: this.frame.y ?? 0
568
+ };
569
+ }
570
+ async updateImageMeta(updateFrame = true) {
571
+ const meta = await getImageDimensions(this.props.src);
572
+ if (updateFrame) {
573
+ const baseSize = getObjectFitSize(
574
+ "contain",
575
+ { width: meta.width, height: meta.height },
576
+ this.parentSize
577
+ );
578
+ this.frame = {
579
+ size: [baseSize.width, baseSize.height],
580
+ ...this.frame
581
+ };
582
+ }
583
+ }
584
+ setPosition(position) {
585
+ this.frame.x = position.x;
586
+ this.frame.y = position.y;
587
+ return this;
588
+ }
589
+ async setSrc(src) {
590
+ this.props.src = src;
591
+ await this.updateImageMeta();
592
+ return this;
593
+ }
594
+ setObjectFit(objectFit) {
595
+ this.objectFit = objectFit;
596
+ return this;
597
+ }
598
+ setFrame(frame) {
599
+ this.frame = structuredClone(frame);
600
+ return this;
601
+ }
602
+ setParentSize(parentSize) {
603
+ this.parentSize = structuredClone(parentSize);
604
+ return this;
605
+ }
606
+ setMediaFilter(mediaFilter) {
607
+ this.props.mediaFilter = mediaFilter;
608
+ return this;
609
+ }
610
+ setBackgroundColor(backgroundColor) {
611
+ this.backgroundColor = backgroundColor;
612
+ return this;
613
+ }
614
+ setProps(props) {
615
+ this.props = { ...structuredClone(props), src: this.props.src };
616
+ return this;
617
+ }
618
+ setFrameEffects(frameEffects) {
619
+ this.frameEffects = frameEffects;
620
+ return this;
621
+ }
622
+ addFrameEffect(frameEffect) {
623
+ var _a;
624
+ (_a = this.frameEffects) == null ? void 0 : _a.push(frameEffect);
625
+ return this;
626
+ }
627
+ accept(visitor) {
628
+ return visitor.visitImageElement(this);
629
+ }
630
+ }
631
+ class TextElement extends TrackElement {
632
+ constructor(text) {
633
+ super(TIMELINE_ELEMENT_TYPE.TEXT);
634
+ __publicField(this, "textEffect");
635
+ this.props = {
636
+ text,
637
+ fill: "#888888"
638
+ //default-grey
639
+ };
640
+ }
641
+ getTextEffect() {
642
+ return this.textEffect;
643
+ }
644
+ getText() {
645
+ return this.props.text;
646
+ }
647
+ getStrokeColor() {
648
+ return this.props.stroke;
649
+ }
650
+ getLineWidth() {
651
+ return this.props.lineWidth;
652
+ }
653
+ setText(text) {
654
+ this.props.text = text;
655
+ return this;
656
+ }
657
+ setFill(fill) {
658
+ this.props.fill = fill;
659
+ return this;
660
+ }
661
+ setRotation(rotation) {
662
+ this.props.rotation = rotation;
663
+ return this;
664
+ }
665
+ setFontSize(fontSize) {
666
+ this.props.fontSize = fontSize;
667
+ return this;
668
+ }
669
+ setFontFamily(fontFamily) {
670
+ this.props.fontFamily = fontFamily;
671
+ return this;
672
+ }
673
+ setFontWeight(fontWeight) {
674
+ this.props.fontWeight = fontWeight;
675
+ return this;
676
+ }
677
+ setFontStyle(fontStyle) {
678
+ this.props.fontStyle = fontStyle;
679
+ return this;
680
+ }
681
+ setTextEffect(textEffect) {
682
+ this.textEffect = textEffect;
683
+ return this;
684
+ }
685
+ setTextAlign(textAlign) {
686
+ this.props.textAlign = textAlign;
687
+ return this;
688
+ }
689
+ setStrokeColor(stroke) {
690
+ this.props.stroke = stroke;
691
+ return this;
692
+ }
693
+ setLineWidth(lineWidth) {
694
+ this.props.lineWidth = lineWidth;
695
+ return this;
696
+ }
697
+ accept(visitor) {
698
+ return visitor.visitTextElement(this);
699
+ }
700
+ }
701
+ class CaptionElement extends TrackElement {
702
+ constructor(t, start, end) {
703
+ super(TIMELINE_ELEMENT_TYPE.CAPTION);
704
+ __publicField(this, "t");
705
+ this.t = t;
706
+ this.s = start;
707
+ this.e = end;
708
+ }
709
+ getText() {
710
+ return this.t;
711
+ }
712
+ setText(t) {
713
+ this.t = t;
714
+ return this;
715
+ }
716
+ accept(visitor) {
717
+ return visitor.visitCaptionElement(this);
718
+ }
719
+ }
720
+ class IconElement extends TrackElement {
721
+ constructor(src, size) {
722
+ super(TIMELINE_ELEMENT_TYPE.ICON);
723
+ this.props = {
724
+ src,
725
+ size
726
+ };
727
+ }
728
+ accept(visitor) {
729
+ return visitor.visitIconElement(this);
730
+ }
731
+ }
732
+ class CircleElement extends TrackElement {
733
+ constructor(fill, radius) {
734
+ super(TIMELINE_ELEMENT_TYPE.CIRCLE);
735
+ this.props = {
736
+ radius,
737
+ fill
738
+ };
739
+ }
740
+ getFill() {
741
+ return this.props.fill;
742
+ }
743
+ getRadius() {
744
+ return this.props.radius;
745
+ }
746
+ setFill(fill) {
747
+ this.props.fill = fill;
748
+ return this;
749
+ }
750
+ setRadius(radius) {
751
+ this.props.radius = radius;
752
+ return this;
753
+ }
754
+ accept(visitor) {
755
+ return visitor.visitCircleElement(this);
756
+ }
757
+ }
758
+ class RectElement extends TrackElement {
759
+ constructor(fill, size) {
760
+ super(TIMELINE_ELEMENT_TYPE.RECT);
761
+ this.props = {
762
+ width: size.width,
763
+ height: size.height,
764
+ fill
765
+ };
766
+ }
767
+ setFill(fill) {
768
+ this.props.fill = fill;
769
+ return this;
770
+ }
771
+ setSize(size) {
772
+ this.props.width = size.width;
773
+ this.props.height = size.height;
774
+ return this;
775
+ }
776
+ accept(visitor) {
777
+ return visitor.visitRectElement(this);
778
+ }
779
+ }
780
+ class ElementAnimation {
781
+ constructor(name) {
782
+ __publicField(this, "name");
783
+ __publicField(this, "interval");
784
+ __publicField(this, "intensity");
785
+ __publicField(this, "animate");
786
+ __publicField(this, "mode");
787
+ __publicField(this, "direction");
788
+ this.name = name;
789
+ }
790
+ getName() {
791
+ return this.name;
792
+ }
793
+ getInterval() {
794
+ return this.interval;
795
+ }
796
+ getIntensity() {
797
+ return this.intensity;
798
+ }
799
+ getAnimate() {
800
+ return this.animate;
801
+ }
802
+ getMode() {
803
+ return this.mode;
804
+ }
805
+ getDirection() {
806
+ return this.direction;
807
+ }
808
+ setInterval(interval) {
809
+ this.interval = interval;
810
+ }
811
+ setIntensity(intensity) {
812
+ this.intensity = intensity;
813
+ }
814
+ setAnimate(animate) {
815
+ this.animate = animate;
816
+ }
817
+ setMode(mode) {
818
+ this.mode = mode;
819
+ }
820
+ setDirection(direction) {
821
+ this.direction = direction;
822
+ }
823
+ toJSON() {
824
+ return {
825
+ name: this.name,
826
+ interval: this.interval,
827
+ intensity: this.intensity,
828
+ animate: this.animate,
829
+ mode: this.mode,
830
+ direction: this.direction
831
+ };
832
+ }
833
+ static fromJSON(json) {
834
+ const animation = new ElementAnimation(json.name);
835
+ animation.setInterval(json.interval);
836
+ animation.setIntensity(json.intensity);
837
+ animation.setAnimate(json.animate);
838
+ animation.setMode(json.mode);
839
+ animation.setDirection(json.direction);
840
+ return animation;
841
+ }
842
+ }
843
+ class ElementFrameEffect {
844
+ constructor(start, end) {
845
+ __publicField(this, "s");
846
+ __publicField(this, "e");
847
+ __publicField(this, "props");
848
+ this.s = start;
849
+ this.e = end;
850
+ }
851
+ setProps(props) {
852
+ this.props = props;
853
+ }
854
+ getProps() {
855
+ return this.props;
856
+ }
857
+ getStart() {
858
+ return this.s;
859
+ }
860
+ getEnd() {
861
+ return this.e;
862
+ }
863
+ toJSON() {
864
+ return {
865
+ s: this.s,
866
+ e: this.e,
867
+ props: this.props
868
+ };
869
+ }
870
+ static fromJSON(json) {
871
+ const effect = new ElementFrameEffect(json.s, json.e);
872
+ effect.setProps(json.props);
873
+ return effect;
874
+ }
875
+ }
876
+ class ElementTextEffect {
877
+ constructor(name) {
878
+ __publicField(this, "name");
879
+ __publicField(this, "duration");
880
+ __publicField(this, "delay");
881
+ __publicField(this, "bufferTime");
882
+ this.name = name;
883
+ }
884
+ getName() {
885
+ return this.name;
886
+ }
887
+ getDuration() {
888
+ return this.duration;
889
+ }
890
+ getDelay() {
891
+ return this.delay;
892
+ }
893
+ getBufferTime() {
894
+ return this.bufferTime;
895
+ }
896
+ setName(name) {
897
+ this.name = name;
898
+ }
899
+ setDuration(duration) {
900
+ this.duration = duration;
901
+ }
902
+ setDelay(delay) {
903
+ this.delay = delay;
904
+ }
905
+ setBufferTime(bufferTime) {
906
+ this.bufferTime = bufferTime;
907
+ }
908
+ toJSON() {
909
+ return {
910
+ name: this.name,
911
+ delay: this.delay,
912
+ duration: this.duration,
913
+ bufferTime: this.bufferTime
914
+ };
915
+ }
916
+ static fromJSON(json) {
917
+ const effect = new ElementTextEffect(json.name);
918
+ effect.setDelay(json.delay);
919
+ effect.setDuration(json.duration);
920
+ effect.setBufferTime(json.bufferTime);
921
+ return effect;
922
+ }
923
+ }
924
+ class ElementDeserializer {
925
+ static deserializeBaseElement(element, json) {
926
+ if (json.id) element.setId(json.id);
927
+ if (json.trackId) element.setTrackId(json.trackId);
928
+ if (json.s !== void 0) element.setStart(json.s);
929
+ if (json.e !== void 0) element.setEnd(json.e);
930
+ if (json.props) element.setProps(json.props);
931
+ if (json.animation) element.setAnimation(ElementAnimation.fromJSON(json.animation));
932
+ }
933
+ static deserializeVideoElement(json) {
934
+ var _a;
935
+ const parentSize = json.frame && json.frame.size ? { width: json.frame.size[0], height: json.frame.size[1] } : { width: 0, height: 0 };
936
+ const videoElement = new VideoElement(((_a = json.props) == null ? void 0 : _a.src) || "", parentSize);
937
+ ElementDeserializer.deserializeBaseElement(videoElement, json);
938
+ if (json.mediaDuration !== void 0) videoElement.setMediaDuration(json.mediaDuration);
939
+ if (json.objectFit) videoElement.setObjectFit(json.objectFit);
940
+ if (json.frame) videoElement.setFrame(json.frame);
941
+ if (json.frameEffects) videoElement.setFrameEffects(json.frameEffects.map((frameEffect) => ElementFrameEffect.fromJSON(frameEffect)));
942
+ if (json.backgroundColor) videoElement.setBackgroundColor(json.backgroundColor);
943
+ return videoElement;
944
+ }
945
+ static deserializeAudioElement(json) {
946
+ var _a;
947
+ const audioElement = new AudioElement(((_a = json.props) == null ? void 0 : _a.src) || "");
948
+ ElementDeserializer.deserializeBaseElement(audioElement, json);
949
+ if (json.mediaDuration !== void 0) audioElement.setMediaDuration(json.mediaDuration);
950
+ return audioElement;
951
+ }
952
+ static deserializeImageElement(json) {
953
+ var _a;
954
+ const parentSize = json.frame && json.frame.size ? { width: json.frame.size[0], height: json.frame.size[1] } : { width: 0, height: 0 };
955
+ const imageElement = new ImageElement(((_a = json.props) == null ? void 0 : _a.src) || "", parentSize);
956
+ ElementDeserializer.deserializeBaseElement(imageElement, json);
957
+ if (json.objectFit) imageElement.setObjectFit(json.objectFit);
958
+ if (json.frame) imageElement.setFrame(json.frame);
959
+ if (json.frameEffects) imageElement.setFrameEffects(json.frameEffects.map((frameEffect) => ElementFrameEffect.fromJSON(frameEffect)));
960
+ if (json.backgroundColor) imageElement.setBackgroundColor(json.backgroundColor);
961
+ return imageElement;
962
+ }
963
+ static deserializeTextElement(json) {
964
+ var _a;
965
+ const textElement = new TextElement(((_a = json.props) == null ? void 0 : _a.text) || "");
966
+ ElementDeserializer.deserializeBaseElement(textElement, json);
967
+ if (json.textEffect) textElement.setTextEffect(ElementTextEffect.fromJSON(json.textEffect));
968
+ return textElement;
969
+ }
970
+ static deserializeCaptionElement(json) {
971
+ const captionElement = new CaptionElement(
972
+ json.t || "",
973
+ json.s || 0,
974
+ json.e || 0
975
+ );
976
+ ElementDeserializer.deserializeBaseElement(captionElement, json);
977
+ return captionElement;
978
+ }
979
+ static deserializeIconElement(json) {
980
+ var _a, _b;
981
+ const size = ((_a = json.props) == null ? void 0 : _a.size) ? { width: json.props.size[0], height: json.props.size[1] } : { width: 0, height: 0 };
982
+ const iconElement = new IconElement(
983
+ ((_b = json.props) == null ? void 0 : _b.src) || "",
984
+ size
985
+ );
986
+ ElementDeserializer.deserializeBaseElement(iconElement, json);
987
+ return iconElement;
988
+ }
989
+ static deserializeCircleElement(json) {
990
+ var _a, _b;
991
+ const circleElement = new CircleElement(
992
+ ((_a = json.props) == null ? void 0 : _a.fill) || "",
993
+ ((_b = json.props) == null ? void 0 : _b.radius) || 0
994
+ );
995
+ ElementDeserializer.deserializeBaseElement(circleElement, json);
996
+ return circleElement;
997
+ }
998
+ static deserializeRectElement(json) {
999
+ var _a, _b, _c;
1000
+ const rectElement = new RectElement(
1001
+ ((_a = json.props) == null ? void 0 : _a.fill) || "",
1002
+ {
1003
+ width: ((_b = json.props) == null ? void 0 : _b.width) || 0,
1004
+ height: ((_c = json.props) == null ? void 0 : _c.height) || 0
1005
+ }
1006
+ );
1007
+ ElementDeserializer.deserializeBaseElement(rectElement, json);
1008
+ return rectElement;
1009
+ }
1010
+ static fromJSON(json) {
1011
+ try {
1012
+ switch (json.type) {
1013
+ case "video":
1014
+ return ElementDeserializer.deserializeVideoElement(json);
1015
+ case "audio":
1016
+ return ElementDeserializer.deserializeAudioElement(json);
1017
+ case "image":
1018
+ return ElementDeserializer.deserializeImageElement(json);
1019
+ case "text":
1020
+ return ElementDeserializer.deserializeTextElement(json);
1021
+ case "caption":
1022
+ return ElementDeserializer.deserializeCaptionElement(json);
1023
+ case "icon":
1024
+ return ElementDeserializer.deserializeIconElement(json);
1025
+ case "circle":
1026
+ return ElementDeserializer.deserializeCircleElement(json);
1027
+ case "rect":
1028
+ return ElementDeserializer.deserializeRectElement(json);
1029
+ default:
1030
+ throw new Error(`Unknown element type: ${json.type}`);
1031
+ }
1032
+ } catch (error) {
1033
+ console.error("Error deserializing element:", error);
1034
+ return null;
1035
+ }
1036
+ }
1037
+ }
1038
+ class ElementSerializer {
1039
+ serializeElement(element) {
1040
+ var _a;
1041
+ return {
1042
+ id: element.getId(),
1043
+ trackId: element.getTrackId(),
1044
+ type: element.getType(),
1045
+ name: element.getName(),
1046
+ s: element.getStart(),
1047
+ e: element.getEnd(),
1048
+ props: structuredClone(element.getProps()),
1049
+ animation: (_a = element.getAnimation()) == null ? void 0 : _a.toJSON()
1050
+ };
1051
+ }
1052
+ visitVideoElement(element) {
1053
+ var _a;
1054
+ return {
1055
+ ...this.serializeElement(element),
1056
+ frame: structuredClone(element.getFrame()),
1057
+ frameEffects: (_a = element.getFrameEffects()) == null ? void 0 : _a.map((frameEffect) => frameEffect.toJSON()),
1058
+ backgroundColor: element.getBackgroundColor(),
1059
+ objectFit: element.getObjectFit(),
1060
+ mediaDuration: element.getMediaDuration()
1061
+ };
1062
+ }
1063
+ visitAudioElement(element) {
1064
+ return {
1065
+ ...this.serializeElement(element),
1066
+ mediaDuration: element.getMediaDuration()
1067
+ };
1068
+ }
1069
+ visitImageElement(element) {
1070
+ var _a;
1071
+ return {
1072
+ ...this.serializeElement(element),
1073
+ frame: structuredClone(element.getFrame()),
1074
+ frameEffects: (_a = element.getFrameEffects()) == null ? void 0 : _a.map((frameEffect) => frameEffect.toJSON()),
1075
+ backgroundColor: element.getBackgroundColor(),
1076
+ objectFit: element.getObjectFit()
1077
+ };
1078
+ }
1079
+ visitTextElement(element) {
1080
+ var _a;
1081
+ return {
1082
+ ...this.serializeElement(element),
1083
+ textEffect: (_a = element.getTextEffect()) == null ? void 0 : _a.toJSON()
1084
+ };
1085
+ }
1086
+ visitCaptionElement(element) {
1087
+ return {
1088
+ ...this.serializeElement(element),
1089
+ t: structuredClone(element.getText())
1090
+ };
1091
+ }
1092
+ visitIconElement(element) {
1093
+ return {
1094
+ ...this.serializeElement(element)
1095
+ };
1096
+ }
1097
+ visitCircleElement(element) {
1098
+ return {
1099
+ ...this.serializeElement(element)
1100
+ };
1101
+ }
1102
+ visitRectElement(element) {
1103
+ return {
1104
+ ...this.serializeElement(element)
1105
+ };
1106
+ }
1107
+ }
1108
+ class ValidationError extends Error {
1109
+ constructor(message, errors, warnings = []) {
1110
+ super(message);
1111
+ this.errors = errors;
1112
+ this.warnings = warnings;
1113
+ this.name = "ValidationError";
1114
+ }
1115
+ }
1116
+ class ElementValidator {
1117
+ validateBasicProperties(element) {
1118
+ const errors = [];
1119
+ const warnings = [];
1120
+ if (!element.getId()) {
1121
+ errors.push("Element must have an ID");
1122
+ }
1123
+ if (!element.getType()) {
1124
+ errors.push("Element must have a type");
1125
+ }
1126
+ if (element.getStart() === void 0 || element.getStart() === null) {
1127
+ errors.push("Element must have a start time (s)");
1128
+ }
1129
+ if (element.getEnd() === void 0 || element.getEnd() === null) {
1130
+ errors.push("Element must have an end time (e)");
1131
+ }
1132
+ if (element.getStart() !== void 0 && element.getEnd() !== void 0) {
1133
+ if (element.getStart() < 0) {
1134
+ errors.push("Start time cannot be negative");
1135
+ }
1136
+ if (element.getEnd() <= element.getStart()) {
1137
+ errors.push("End time must be greater than start time");
1138
+ }
1139
+ }
1140
+ if (!element.getName()) {
1141
+ warnings.push("Element should have a name for better identification");
1142
+ }
1143
+ if (!element.getTrackId()) {
1144
+ warnings.push("Element should have a track Id");
1145
+ }
1146
+ return { errors, warnings };
1147
+ }
1148
+ validateTextElement(element) {
1149
+ const basicValidation = this.validateBasicProperties(element);
1150
+ const errors = [...basicValidation.errors];
1151
+ const warnings = [...basicValidation.warnings];
1152
+ const props = element.getProps();
1153
+ if (!(props == null ? void 0 : props.text)) {
1154
+ errors.push("Text element must have text content");
1155
+ }
1156
+ if ((props == null ? void 0 : props.fontSize) !== void 0 && props.fontSize <= 0) {
1157
+ errors.push("Font size must be greater than 0");
1158
+ }
1159
+ if ((props == null ? void 0 : props.fontWeight) !== void 0 && props.fontWeight < 0) {
1160
+ errors.push("Font weight cannot be negative");
1161
+ }
1162
+ return { errors, warnings };
1163
+ }
1164
+ validateVideoElement(element) {
1165
+ const basicValidation = this.validateBasicProperties(element);
1166
+ const errors = [...basicValidation.errors];
1167
+ const warnings = [...basicValidation.warnings];
1168
+ const props = element.getProps();
1169
+ if (!(props == null ? void 0 : props.src)) {
1170
+ errors.push("Video element must have a source URL");
1171
+ }
1172
+ if ((props == null ? void 0 : props.volume) !== void 0 && (props.volume < 0 || props.volume > 1)) {
1173
+ errors.push("Volume must be between 0 and 1");
1174
+ }
1175
+ if ((props == null ? void 0 : props.playbackRate) !== void 0 && props.playbackRate <= 0) {
1176
+ errors.push("Playback rate must be greater than 0");
1177
+ }
1178
+ return { errors, warnings };
1179
+ }
1180
+ validateAudioElement(element) {
1181
+ const basicValidation = this.validateBasicProperties(element);
1182
+ const errors = [...basicValidation.errors];
1183
+ const warnings = [...basicValidation.warnings];
1184
+ const props = element.getProps();
1185
+ if (!(props == null ? void 0 : props.src)) {
1186
+ errors.push("Audio element must have a source URL");
1187
+ }
1188
+ if ((props == null ? void 0 : props.volume) !== void 0 && (props.volume < 0 || props.volume > 1)) {
1189
+ errors.push("Volume must be between 0 and 1");
1190
+ }
1191
+ if ((props == null ? void 0 : props.playbackRate) !== void 0 && props.playbackRate <= 0) {
1192
+ errors.push("Playback rate must be greater than 0");
1193
+ }
1194
+ return { errors, warnings };
1195
+ }
1196
+ validateImageElement(element) {
1197
+ const basicValidation = this.validateBasicProperties(element);
1198
+ const errors = [...basicValidation.errors];
1199
+ const warnings = [...basicValidation.warnings];
1200
+ const props = element.getProps();
1201
+ if (!(props == null ? void 0 : props.src)) {
1202
+ errors.push("Image element must have a source URL");
1203
+ }
1204
+ return { errors, warnings };
1205
+ }
1206
+ validateCaptionElement(element) {
1207
+ const basicValidation = this.validateBasicProperties(element);
1208
+ const errors = [...basicValidation.errors];
1209
+ const warnings = [...basicValidation.warnings];
1210
+ const props = element.getProps();
1211
+ if (!(props == null ? void 0 : props.text)) {
1212
+ errors.push("Caption element must have text content");
1213
+ }
1214
+ return { errors, warnings };
1215
+ }
1216
+ validateIconElement(element) {
1217
+ const basicValidation = this.validateBasicProperties(element);
1218
+ const errors = [...basicValidation.errors];
1219
+ const warnings = [...basicValidation.warnings];
1220
+ const props = element.getProps();
1221
+ if (!(props == null ? void 0 : props.icon)) {
1222
+ errors.push("Icon element must have an icon name");
1223
+ }
1224
+ return { errors, warnings };
1225
+ }
1226
+ validateCircleElement(element) {
1227
+ const basicValidation = this.validateBasicProperties(element);
1228
+ const errors = [...basicValidation.errors];
1229
+ const warnings = [...basicValidation.warnings];
1230
+ const props = element.getProps();
1231
+ if ((props == null ? void 0 : props.radius) !== void 0 && props.radius <= 0) {
1232
+ errors.push("Circle radius must be greater than 0");
1233
+ }
1234
+ return { errors, warnings };
1235
+ }
1236
+ validateRectElement(element) {
1237
+ const basicValidation = this.validateBasicProperties(element);
1238
+ const errors = [...basicValidation.errors];
1239
+ const warnings = [...basicValidation.warnings];
1240
+ const props = element.getProps();
1241
+ if ((props == null ? void 0 : props.width) !== void 0 && props.width <= 0) {
1242
+ errors.push("Rectangle width must be greater than 0");
1243
+ }
1244
+ if ((props == null ? void 0 : props.height) !== void 0 && props.height <= 0) {
1245
+ errors.push("Rectangle height must be greater than 0");
1246
+ }
1247
+ return { errors, warnings };
1248
+ }
1249
+ visitVideoElement(element) {
1250
+ const validation = this.validateVideoElement(element);
1251
+ if (validation.errors.length > 0) {
1252
+ throw new ValidationError(
1253
+ `Video element validation failed: ${validation.errors.join(", ")}`,
1254
+ validation.errors,
1255
+ validation.warnings
1256
+ );
1257
+ }
1258
+ return true;
1259
+ }
1260
+ visitAudioElement(element) {
1261
+ const validation = this.validateAudioElement(element);
1262
+ if (validation.errors.length > 0) {
1263
+ throw new ValidationError(
1264
+ `Audio element validation failed: ${validation.errors.join(", ")}`,
1265
+ validation.errors,
1266
+ validation.warnings
1267
+ );
1268
+ }
1269
+ return true;
1270
+ }
1271
+ visitImageElement(element) {
1272
+ const validation = this.validateImageElement(element);
1273
+ if (validation.errors.length > 0) {
1274
+ throw new ValidationError(
1275
+ `Image element validation failed: ${validation.errors.join(", ")}`,
1276
+ validation.errors,
1277
+ validation.warnings
1278
+ );
1279
+ }
1280
+ return true;
1281
+ }
1282
+ visitTextElement(element) {
1283
+ const validation = this.validateTextElement(element);
1284
+ if (validation.errors.length > 0) {
1285
+ throw new ValidationError(
1286
+ `Text element validation failed: ${validation.errors.join(", ")}`,
1287
+ validation.errors,
1288
+ validation.warnings
1289
+ );
1290
+ }
1291
+ return true;
1292
+ }
1293
+ visitCaptionElement(element) {
1294
+ const validation = this.validateCaptionElement(element);
1295
+ if (validation.errors.length > 0) {
1296
+ throw new ValidationError(
1297
+ `Caption element validation failed: ${validation.errors.join(", ")}`,
1298
+ validation.errors,
1299
+ validation.warnings
1300
+ );
1301
+ }
1302
+ return true;
1303
+ }
1304
+ visitIconElement(element) {
1305
+ const validation = this.validateIconElement(element);
1306
+ if (validation.errors.length > 0) {
1307
+ throw new ValidationError(
1308
+ `Icon element validation failed: ${validation.errors.join(", ")}`,
1309
+ validation.errors,
1310
+ validation.warnings
1311
+ );
1312
+ }
1313
+ return true;
1314
+ }
1315
+ visitCircleElement(element) {
1316
+ const validation = this.validateCircleElement(element);
1317
+ if (validation.errors.length > 0) {
1318
+ throw new ValidationError(
1319
+ `Circle element validation failed: ${validation.errors.join(", ")}`,
1320
+ validation.errors,
1321
+ validation.warnings
1322
+ );
1323
+ }
1324
+ return true;
1325
+ }
1326
+ visitRectElement(element) {
1327
+ const validation = this.validateRectElement(element);
1328
+ if (validation.errors.length > 0) {
1329
+ throw new ValidationError(
1330
+ `Rectangle element validation failed: ${validation.errors.join(", ")}`,
1331
+ validation.errors,
1332
+ validation.warnings
1333
+ );
1334
+ }
1335
+ return true;
1336
+ }
1337
+ }
1338
+ class TrackFriend {
1339
+ constructor(track) {
1340
+ this.track = track;
1341
+ }
1342
+ /**
1343
+ * Add an element to the track with validation
1344
+ * @param element The element to add
1345
+ * @param skipValidation If true, skips validation (use with caution)
1346
+ * @returns true if element was added successfully, throws ValidationError if validation fails
1347
+ */
1348
+ addElement(element, skipValidation = false) {
1349
+ return this.track.addElementViaFriend(element, skipValidation);
1350
+ }
1351
+ /**
1352
+ * Remove an element from the track
1353
+ * @param element The element to remove
1354
+ */
1355
+ removeElement(element) {
1356
+ this.track.removeElementViaFriend(element);
1357
+ }
1358
+ /**
1359
+ * Update an element in the track with validation
1360
+ * @param element The element to update
1361
+ * @returns true if element was updated successfully, throws ValidationError if validation fails
1362
+ */
1363
+ updateElement(element) {
1364
+ return this.track.updateElementViaFriend(element);
1365
+ }
1366
+ /**
1367
+ * Get the track instance (for advanced operations)
1368
+ * @returns The track instance
1369
+ */
1370
+ getTrack() {
1371
+ return this.track;
1372
+ }
1373
+ }
1374
+ class Track {
1375
+ constructor(name, id) {
1376
+ __publicField(this, "id");
1377
+ __publicField(this, "name");
1378
+ __publicField(this, "type");
1379
+ __publicField(this, "elements");
1380
+ __publicField(this, "validator");
1381
+ this.name = name;
1382
+ this.id = id ?? `t-${generateShortUuid}`;
1383
+ this.type = "element";
1384
+ this.elements = [];
1385
+ this.validator = new ElementValidator();
1386
+ }
1387
+ /**
1388
+ * Create a friend instance for explicit access to protected methods
1389
+ * This implements the Friend Class Pattern
1390
+ * @returns TrackFriend instance
1391
+ */
1392
+ createFriend() {
1393
+ return new TrackFriend(this);
1394
+ }
1395
+ /**
1396
+ * Friend method to add element (called by TrackFriend)
1397
+ * @param element The element to add
1398
+ * @param skipValidation If true, skips validation
1399
+ * @returns true if element was added successfully
1400
+ */
1401
+ addElementViaFriend(element, skipValidation = false) {
1402
+ return this.addElement(element, skipValidation);
1403
+ }
1404
+ /**
1405
+ * Friend method to remove element (called by TrackFriend)
1406
+ * @param element The element to remove
1407
+ */
1408
+ removeElementViaFriend(element) {
1409
+ this.removeElement(element);
1410
+ }
1411
+ /**
1412
+ * Friend method to update element (called by TrackFriend)
1413
+ * @param element The element to update
1414
+ * @returns true if element was updated successfully
1415
+ */
1416
+ updateElementViaFriend(element) {
1417
+ return this.updateElement(element);
1418
+ }
1419
+ getId() {
1420
+ return this.id;
1421
+ }
1422
+ getName() {
1423
+ return this.name;
1424
+ }
1425
+ getType() {
1426
+ return this.type;
1427
+ }
1428
+ getElements() {
1429
+ return [...this.elements];
1430
+ }
1431
+ /**
1432
+ * Validates an element
1433
+ * @param element The element to validate
1434
+ * @returns true if valid, throws ValidationError if invalid
1435
+ */
1436
+ validateElement(element) {
1437
+ return element.accept(this.validator);
1438
+ }
1439
+ getTrackDuration() {
1440
+ var _a;
1441
+ return ((_a = this.elements) == null ? void 0 : _a.length) ? this.elements[this.elements.length - 1].getEnd() : 0;
1442
+ }
1443
+ /**
1444
+ * Adds an element to the track with validation
1445
+ * @param element The element to add
1446
+ * @param skipValidation If true, skips validation (use with caution)
1447
+ * @returns true if element was added successfully, throws ValidationError if validation fails
1448
+ */
1449
+ addElement(element, skipValidation = false) {
1450
+ element.setTrackId(this.id);
1451
+ if (skipValidation) {
1452
+ this.elements.push(element);
1453
+ return true;
1454
+ }
1455
+ try {
1456
+ const isValid = this.validateElement(element);
1457
+ if (isValid) {
1458
+ this.elements.push(element);
1459
+ return true;
1460
+ }
1461
+ } catch (error) {
1462
+ if (error instanceof ValidationError) {
1463
+ throw error;
1464
+ }
1465
+ throw error;
1466
+ }
1467
+ return false;
1468
+ }
1469
+ removeElement(element) {
1470
+ const index = this.elements.findIndex((e) => e.getId() === element.getId());
1471
+ if (index !== -1) {
1472
+ this.elements.splice(index, 1);
1473
+ }
1474
+ }
1475
+ /**
1476
+ * Updates an element in the track with validation
1477
+ * @param element The element to update
1478
+ * @returns true if element was updated successfully, throws ValidationError if validation fails
1479
+ */
1480
+ updateElement(element) {
1481
+ try {
1482
+ const isValid = this.validateElement(element);
1483
+ if (isValid) {
1484
+ const index = this.elements.findIndex(
1485
+ (e) => e.getId() === element.getId()
1486
+ );
1487
+ if (index !== -1) {
1488
+ this.elements[index] = element;
1489
+ return true;
1490
+ }
1491
+ }
1492
+ } catch (error) {
1493
+ if (error instanceof ValidationError) {
1494
+ throw error;
1495
+ }
1496
+ throw error;
1497
+ }
1498
+ return false;
1499
+ }
1500
+ getElementById(id) {
1501
+ const element = this.elements.find((e) => e.getId() === id);
1502
+ if (!element) return void 0;
1503
+ return element;
1504
+ }
1505
+ /**
1506
+ * Validates all elements in the track and returns combined result and per-element status
1507
+ * @returns Object with overall isValid and array of per-element validation results
1508
+ */
1509
+ validateAllElements() {
1510
+ let validResult = true;
1511
+ const results = this.elements.map((element) => {
1512
+ try {
1513
+ const isValid = this.validateElement(element);
1514
+ if (!isValid) {
1515
+ validResult = false;
1516
+ }
1517
+ return { element, isValid };
1518
+ } catch (error) {
1519
+ if (error instanceof ValidationError) {
1520
+ validResult = false;
1521
+ return {
1522
+ element,
1523
+ isValid: false,
1524
+ errors: error.errors,
1525
+ warnings: error.warnings
1526
+ };
1527
+ }
1528
+ return {
1529
+ element,
1530
+ isValid: false,
1531
+ errors: [error instanceof Error ? error.message : "Unknown error"]
1532
+ };
1533
+ }
1534
+ });
1535
+ return { isValid: validResult, results };
1536
+ }
1537
+ serialize() {
1538
+ const serializer = new ElementSerializer();
1539
+ return {
1540
+ id: this.id,
1541
+ name: this.name,
1542
+ type: this.type,
1543
+ elements: this.elements.map(
1544
+ (element) => element.accept(serializer)
1545
+ )
1546
+ };
1547
+ }
1548
+ static fromJSON(json) {
1549
+ const track = new Track(json.name, json.id);
1550
+ track.type = json.type;
1551
+ track.elements = (json.elements || []).map(ElementDeserializer.fromJSON);
1552
+ return track;
1553
+ }
1554
+ }
1555
+ const _TimelineContextStore = class _TimelineContextStore {
1556
+ constructor() {
1557
+ __publicField(this, "storeMap");
1558
+ this.storeMap = /* @__PURE__ */ new Map();
1559
+ }
1560
+ static getInstance() {
1561
+ if (!_TimelineContextStore.instance) {
1562
+ _TimelineContextStore.instance = new _TimelineContextStore();
1563
+ }
1564
+ return _TimelineContextStore.instance;
1565
+ }
1566
+ initializeContext(contextId) {
1567
+ if (!this.storeMap.has(contextId)) {
1568
+ this.storeMap.set(contextId, {
1569
+ tracks: [],
1570
+ version: 0,
1571
+ elementMap: {},
1572
+ trackMap: {},
1573
+ captionProps: {}
1574
+ });
1575
+ }
1576
+ }
1577
+ getTimelineData(contextId) {
1578
+ const timelineStore = this.storeMap.get(contextId);
1579
+ return timelineStore ? {
1580
+ tracks: timelineStore.tracks,
1581
+ version: timelineStore.version
1582
+ } : null;
1583
+ }
1584
+ setTimelineData(contextId, timelineData) {
1585
+ this.ensureContext(contextId);
1586
+ this.storeMap.get(contextId).tracks = timelineData.tracks;
1587
+ this.storeMap.get(contextId).version = timelineData.version;
1588
+ return timelineData;
1589
+ }
1590
+ getElementMap(contextId) {
1591
+ this.ensureContext(contextId);
1592
+ return this.storeMap.get(contextId).elementMap;
1593
+ }
1594
+ setElementMap(contextId, elementMap) {
1595
+ this.ensureContext(contextId);
1596
+ this.storeMap.get(contextId).elementMap = elementMap;
1597
+ }
1598
+ getTrackMap(contextId) {
1599
+ this.ensureContext(contextId);
1600
+ return this.storeMap.get(contextId).trackMap;
1601
+ }
1602
+ setTrackMap(contextId, trackMap) {
1603
+ this.ensureContext(contextId);
1604
+ this.storeMap.get(contextId).trackMap = trackMap;
1605
+ }
1606
+ getCaptionProps(contextId) {
1607
+ this.ensureContext(contextId);
1608
+ return this.storeMap.get(contextId).captionProps;
1609
+ }
1610
+ setCaptionProps(contextId, captionProps) {
1611
+ this.ensureContext(contextId);
1612
+ this.storeMap.get(contextId).captionProps = captionProps;
1613
+ }
1614
+ clearContext(contextId) {
1615
+ this.storeMap.delete(contextId);
1616
+ }
1617
+ ensureContext(contextId) {
1618
+ if (!this.storeMap.has(contextId)) {
1619
+ this.initializeContext(contextId);
1620
+ }
1621
+ }
1622
+ };
1623
+ __publicField(_TimelineContextStore, "instance");
1624
+ let TimelineContextStore = _TimelineContextStore;
1625
+ const timelineContextStore = TimelineContextStore.getInstance();
1626
+ class ElementAdder {
1627
+ constructor(track) {
1628
+ __publicField(this, "track");
1629
+ __publicField(this, "trackFriend");
1630
+ this.track = track;
1631
+ this.trackFriend = track.createFriend();
1632
+ }
1633
+ async visitVideoElement(element) {
1634
+ await element.updateVideoMeta();
1635
+ const elements = this.track.getElements();
1636
+ const lastEndtime = (elements == null ? void 0 : elements.length) ? elements[elements.length - 1].getEnd() : 0;
1637
+ if (isNaN(element.getStart())) {
1638
+ element.setStart(lastEndtime);
1639
+ }
1640
+ if (isNaN(element.getEnd())) {
1641
+ element.setEnd(element.getStart() + element.getMediaDuration());
1642
+ }
1643
+ return this.trackFriend.addElement(element);
1644
+ }
1645
+ async visitAudioElement(element) {
1646
+ await element.updateAudioMeta();
1647
+ const elements = this.track.getElements();
1648
+ const lastEndtime = (elements == null ? void 0 : elements.length) ? elements[elements.length - 1].getEnd() : 0;
1649
+ if (isNaN(element.getStart())) {
1650
+ element.setStart(lastEndtime);
1651
+ }
1652
+ if (isNaN(element.getEnd())) {
1653
+ element.setEnd(element.getStart() + element.getMediaDuration());
1654
+ }
1655
+ return this.trackFriend.addElement(element);
1656
+ }
1657
+ async visitImageElement(element) {
1658
+ await element.updateImageMeta();
1659
+ const elements = this.track.getElements();
1660
+ const lastEndtime = (elements == null ? void 0 : elements.length) ? elements[elements.length - 1].getEnd() : 0;
1661
+ if (isNaN(element.getStart())) {
1662
+ element.setStart(lastEndtime);
1663
+ }
1664
+ if (isNaN(element.getEnd())) {
1665
+ element.setEnd(element.getStart() + 1);
1666
+ }
1667
+ return this.trackFriend.addElement(element);
1668
+ }
1669
+ async visitTextElement(element) {
1670
+ const elements = this.track.getElements();
1671
+ const lastEndtime = (elements == null ? void 0 : elements.length) ? elements[elements.length - 1].getEnd() : 0;
1672
+ if (isNaN(element.getStart())) {
1673
+ element.setStart(lastEndtime);
1674
+ }
1675
+ if (isNaN(element.getEnd())) {
1676
+ element.setEnd(element.getStart() + 1);
1677
+ }
1678
+ return this.trackFriend.addElement(element);
1679
+ }
1680
+ async visitCaptionElement(element) {
1681
+ const elements = this.track.getElements();
1682
+ const lastEndtime = (elements == null ? void 0 : elements.length) ? elements[elements.length - 1].getEnd() : 0;
1683
+ if (isNaN(element.getStart())) {
1684
+ element.setStart(lastEndtime);
1685
+ }
1686
+ if (isNaN(element.getEnd())) {
1687
+ element.setEnd(element.getStart() + 1);
1688
+ }
1689
+ return this.trackFriend.addElement(element);
1690
+ }
1691
+ async visitIconElement(element) {
1692
+ const elements = this.track.getElements();
1693
+ const lastEndtime = (elements == null ? void 0 : elements.length) ? elements[elements.length - 1].getEnd() : 0;
1694
+ if (isNaN(element.getStart())) {
1695
+ element.setStart(lastEndtime);
1696
+ }
1697
+ if (isNaN(element.getEnd())) {
1698
+ element.setEnd(element.getStart() + 1);
1699
+ }
1700
+ return this.trackFriend.addElement(element);
1701
+ }
1702
+ async visitCircleElement(element) {
1703
+ const elements = this.track.getElements();
1704
+ const lastEndtime = (elements == null ? void 0 : elements.length) ? elements[elements.length - 1].getEnd() : 0;
1705
+ if (isNaN(element.getStart())) {
1706
+ element.setStart(lastEndtime);
1707
+ }
1708
+ if (isNaN(element.getEnd())) {
1709
+ element.setEnd(element.getStart() + 1);
1710
+ }
1711
+ return this.trackFriend.addElement(element);
1712
+ }
1713
+ async visitRectElement(element) {
1714
+ const elements = this.track.getElements();
1715
+ const lastEndtime = (elements == null ? void 0 : elements.length) ? elements[elements.length - 1].getEnd() : 0;
1716
+ if (isNaN(element.getStart())) {
1717
+ element.setStart(lastEndtime);
1718
+ }
1719
+ if (isNaN(element.getEnd())) {
1720
+ element.setEnd(element.getStart() + 1);
1721
+ }
1722
+ return this.trackFriend.addElement(element);
1723
+ }
1724
+ }
1725
+ class ElementRemover {
1726
+ constructor(track) {
1727
+ __publicField(this, "trackFriend");
1728
+ this.trackFriend = track.createFriend();
1729
+ }
1730
+ visitVideoElement(element) {
1731
+ this.trackFriend.removeElement(element);
1732
+ return true;
1733
+ }
1734
+ visitAudioElement(element) {
1735
+ this.trackFriend.removeElement(element);
1736
+ return true;
1737
+ }
1738
+ visitImageElement(element) {
1739
+ this.trackFriend.removeElement(element);
1740
+ return true;
1741
+ }
1742
+ visitTextElement(element) {
1743
+ this.trackFriend.removeElement(element);
1744
+ return true;
1745
+ }
1746
+ visitCaptionElement(element) {
1747
+ this.trackFriend.removeElement(element);
1748
+ return true;
1749
+ }
1750
+ visitIconElement(element) {
1751
+ this.trackFriend.removeElement(element);
1752
+ return true;
1753
+ }
1754
+ visitCircleElement(element) {
1755
+ this.trackFriend.removeElement(element);
1756
+ return true;
1757
+ }
1758
+ visitRectElement(element) {
1759
+ this.trackFriend.removeElement(element);
1760
+ return true;
1761
+ }
1762
+ }
1763
+ class ElementUpdater {
1764
+ constructor(track) {
1765
+ __publicField(this, "trackFriend");
1766
+ this.trackFriend = track.createFriend();
1767
+ }
1768
+ visitVideoElement(element) {
1769
+ return this.trackFriend.updateElement(element);
1770
+ }
1771
+ visitAudioElement(element) {
1772
+ return this.trackFriend.updateElement(element);
1773
+ }
1774
+ visitImageElement(element) {
1775
+ return this.trackFriend.updateElement(element);
1776
+ }
1777
+ visitTextElement(element) {
1778
+ return this.trackFriend.updateElement(element);
1779
+ }
1780
+ visitCaptionElement(element) {
1781
+ return this.trackFriend.updateElement(element);
1782
+ }
1783
+ visitIconElement(element) {
1784
+ return this.trackFriend.updateElement(element);
1785
+ }
1786
+ visitCircleElement(element) {
1787
+ return this.trackFriend.updateElement(element);
1788
+ }
1789
+ visitRectElement(element) {
1790
+ return this.trackFriend.updateElement(element);
1791
+ }
1792
+ }
1793
+ class ElementCloner {
1794
+ cloneElementProperties(srcElement, destElement) {
1795
+ return destElement.setName(srcElement.getName()).setType(srcElement.getType()).setStart(srcElement.getStart()).setEnd(srcElement.getEnd()).setProps(srcElement.getProps()).setAnimation(srcElement.getAnimation());
1796
+ }
1797
+ visitVideoElement(element) {
1798
+ const props = element.getProps();
1799
+ const clonedElement = new VideoElement(props.src, element.getParentSize());
1800
+ this.cloneElementProperties(element, clonedElement);
1801
+ clonedElement.setParentSize(element.getParentSize()).setMediaDuration(element.getMediaDuration()).setFrame(element.getFrame()).setFrameEffects(element.getFrameEffects() ?? []).setBackgroundColor(element.getBackgroundColor()).setObjectFit(element.getObjectFit());
1802
+ return clonedElement;
1803
+ }
1804
+ visitAudioElement(element) {
1805
+ const clonedElement = new AudioElement(element.getProps().src);
1806
+ this.cloneElementProperties(element, clonedElement);
1807
+ clonedElement.setMediaDuration(element.getMediaDuration());
1808
+ return clonedElement;
1809
+ }
1810
+ visitImageElement(element) {
1811
+ const clonedElement = new ImageElement(
1812
+ element.getProps().src,
1813
+ element.getParentSize()
1814
+ );
1815
+ this.cloneElementProperties(element, clonedElement);
1816
+ clonedElement.setParentSize(element.getParentSize()).setFrame(element.getFrame()).setFrameEffects(element.getFrameEffects()).setBackgroundColor(element.getBackgroundColor()).setObjectFit(element.getObjectFit());
1817
+ return clonedElement;
1818
+ }
1819
+ visitTextElement(element) {
1820
+ const clonedElement = new TextElement(element.getProps().text);
1821
+ this.cloneElementProperties(element, clonedElement);
1822
+ clonedElement.setTextEffect(element.getTextEffect());
1823
+ return clonedElement;
1824
+ }
1825
+ visitCaptionElement(element) {
1826
+ const clonedElement = new CaptionElement(
1827
+ element.getProps().text,
1828
+ element.getStart(),
1829
+ element.getEnd()
1830
+ );
1831
+ this.cloneElementProperties(element, clonedElement);
1832
+ return clonedElement;
1833
+ }
1834
+ visitRectElement(element) {
1835
+ const clonedElement = new RectElement(
1836
+ element.getProps().fill,
1837
+ element.getProps().size
1838
+ );
1839
+ this.cloneElementProperties(element, clonedElement);
1840
+ return clonedElement;
1841
+ }
1842
+ visitCircleElement(element) {
1843
+ const clonedElement = new CircleElement(
1844
+ element.getProps().fill,
1845
+ element.getProps().radius
1846
+ );
1847
+ this.cloneElementProperties(element, clonedElement);
1848
+ return clonedElement;
1849
+ }
1850
+ visitIconElement(element) {
1851
+ const clonedElement = new IconElement(
1852
+ element.getProps().src,
1853
+ element.getProps().size
1854
+ );
1855
+ this.cloneElementProperties(element, clonedElement);
1856
+ return clonedElement;
1857
+ }
1858
+ }
1859
+ class ElementSplitter {
1860
+ constructor(splitTime) {
1861
+ __publicField(this, "splitTime");
1862
+ __publicField(this, "elementCloner");
1863
+ this.splitTime = splitTime;
1864
+ this.elementCloner = new ElementCloner();
1865
+ }
1866
+ visitVideoElement(element) {
1867
+ if (!canSplitElement(element, this.splitTime)) {
1868
+ return { firstElement: null, secondElement: null, success: false };
1869
+ }
1870
+ const firstElement = this.elementCloner.visitVideoElement(
1871
+ element
1872
+ );
1873
+ const secondElement = this.elementCloner.visitVideoElement(
1874
+ element
1875
+ );
1876
+ const props = element.getProps();
1877
+ const secondStartAt = (props.time ?? 0) + (this.splitTime - element.getStart()) * (props.playbackRate ?? 1);
1878
+ firstElement.setEnd(this.splitTime);
1879
+ secondElement.setStart(this.splitTime).setStartAt(secondStartAt);
1880
+ return { firstElement, secondElement, success: true };
1881
+ }
1882
+ visitAudioElement(element) {
1883
+ if (!canSplitElement(element, this.splitTime)) {
1884
+ return { firstElement: null, secondElement: null, success: false };
1885
+ }
1886
+ const firstElement = this.elementCloner.visitAudioElement(
1887
+ element
1888
+ );
1889
+ const secondElement = this.elementCloner.visitAudioElement(
1890
+ element
1891
+ );
1892
+ const props = element.getProps();
1893
+ const secondStartAt = (props.time ?? 0) + (this.splitTime - element.getStart()) * (props.playbackRate ?? 1);
1894
+ firstElement.setEnd(this.splitTime);
1895
+ secondElement.setStart(this.splitTime).setStartAt(secondStartAt);
1896
+ return { firstElement, secondElement, success: true };
1897
+ }
1898
+ visitImageElement(element) {
1899
+ if (!canSplitElement(element, this.splitTime)) {
1900
+ return { firstElement: null, secondElement: null, success: false };
1901
+ }
1902
+ const firstElement = this.elementCloner.visitImageElement(
1903
+ element
1904
+ );
1905
+ const secondElement = this.elementCloner.visitImageElement(
1906
+ element
1907
+ );
1908
+ firstElement.setEnd(this.splitTime);
1909
+ secondElement.setStart(this.splitTime);
1910
+ return { firstElement, secondElement, success: true };
1911
+ }
1912
+ visitTextElement(element) {
1913
+ if (!canSplitElement(element, this.splitTime)) {
1914
+ return { firstElement: null, secondElement: null, success: false };
1915
+ }
1916
+ const originalText = element.getText() || "";
1917
+ const originalTextArray = originalText.split(" ");
1918
+ const percentage = (this.splitTime - element.getStart()) / element.getDuration();
1919
+ const firstElement = this.elementCloner.visitTextElement(
1920
+ element
1921
+ );
1922
+ firstElement.setText(
1923
+ originalTextArray.slice(0, Math.floor(originalTextArray.length * percentage)).join(" ")
1924
+ );
1925
+ firstElement.setEnd(this.splitTime);
1926
+ const secondElement = this.elementCloner.visitTextElement(
1927
+ element
1928
+ );
1929
+ secondElement.setText(
1930
+ originalTextArray.slice(
1931
+ Math.floor(originalTextArray.length * percentage),
1932
+ originalTextArray.length
1933
+ ).join(" ")
1934
+ );
1935
+ secondElement.setStart(this.splitTime);
1936
+ return { firstElement, secondElement, success: true };
1937
+ }
1938
+ visitCaptionElement(element) {
1939
+ if (!canSplitElement(element, this.splitTime)) {
1940
+ return { firstElement: null, secondElement: null, success: false };
1941
+ }
1942
+ const originalText = element.getText() || "";
1943
+ const originalTextArray = originalText.split(" ");
1944
+ const percentage = (this.splitTime - element.getStart()) / element.getDuration();
1945
+ const firstElement = this.elementCloner.visitCaptionElement(
1946
+ element
1947
+ );
1948
+ firstElement.setText(
1949
+ originalTextArray.slice(0, Math.floor(originalTextArray.length * percentage)).join(" ")
1950
+ );
1951
+ firstElement.setEnd(this.splitTime);
1952
+ const secondElement = this.elementCloner.visitCaptionElement(
1953
+ element
1954
+ );
1955
+ secondElement.setText(
1956
+ originalTextArray.slice(
1957
+ Math.floor(originalTextArray.length * percentage),
1958
+ originalTextArray.length
1959
+ ).join(" ")
1960
+ );
1961
+ secondElement.setStart(this.splitTime);
1962
+ return { firstElement, secondElement, success: true };
1963
+ }
1964
+ visitRectElement(element) {
1965
+ if (!canSplitElement(element, this.splitTime)) {
1966
+ return { firstElement: null, secondElement: null, success: false };
1967
+ }
1968
+ const firstElement = this.elementCloner.visitRectElement(
1969
+ element
1970
+ );
1971
+ const secondElement = this.elementCloner.visitRectElement(
1972
+ element
1973
+ );
1974
+ firstElement.setEnd(this.splitTime);
1975
+ secondElement.setStart(this.splitTime);
1976
+ return { firstElement, secondElement, success: true };
1977
+ }
1978
+ visitCircleElement(element) {
1979
+ if (!canSplitElement(element, this.splitTime)) {
1980
+ return { firstElement: null, secondElement: null, success: false };
1981
+ }
1982
+ const firstElement = this.elementCloner.visitCircleElement(element);
1983
+ const secondElement = this.elementCloner.visitCircleElement(element);
1984
+ firstElement.setEnd(this.splitTime);
1985
+ secondElement.setStart(this.splitTime);
1986
+ return { firstElement, secondElement, success: true };
1987
+ }
1988
+ visitIconElement(element) {
1989
+ if (!canSplitElement(element, this.splitTime)) {
1990
+ return { firstElement: null, secondElement: null, success: false };
1991
+ }
1992
+ const firstElement = this.elementCloner.visitIconElement(element);
1993
+ const secondElement = this.elementCloner.visitIconElement(element);
1994
+ firstElement.setEnd(this.splitTime);
1995
+ secondElement.setStart(this.splitTime);
1996
+ return { firstElement, secondElement, success: true };
1997
+ }
1998
+ }
1999
+ class TimelineEditor {
2000
+ constructor(context) {
2001
+ __publicField(this, "context");
2002
+ this.context = context;
2003
+ timelineContextStore.initializeContext(this.context.contextId);
2004
+ }
2005
+ getContext() {
2006
+ return this.context;
2007
+ }
2008
+ pauseVideo() {
2009
+ var _a;
2010
+ if ((_a = this.context) == null ? void 0 : _a.setTimelineAction) {
2011
+ this.context.setTimelineAction(
2012
+ TIMELINE_ACTION.SET_PLAYER_STATE,
2013
+ PLAYER_STATE.PAUSED
2014
+ );
2015
+ }
2016
+ }
2017
+ getTimelineData() {
2018
+ const contextId = this.context.contextId;
2019
+ return timelineContextStore.getTimelineData(contextId);
2020
+ }
2021
+ getLatestVersion() {
2022
+ const contextId = this.context.contextId;
2023
+ const timelineData = timelineContextStore.getTimelineData(contextId);
2024
+ return (timelineData == null ? void 0 : timelineData.version) || 0;
2025
+ }
2026
+ setTimelineData(tracks, version) {
2027
+ const prevTimelineData = this.getTimelineData();
2028
+ const updatedVersion = version ?? ((prevTimelineData == null ? void 0 : prevTimelineData.version) || 0) + 1;
2029
+ const updatedTimelineData = {
2030
+ tracks,
2031
+ version: updatedVersion
2032
+ };
2033
+ timelineContextStore.setTimelineData(
2034
+ this.context.contextId,
2035
+ updatedTimelineData
2036
+ );
2037
+ this.updateHistory(updatedTimelineData);
2038
+ this.context.updateChangeLog();
2039
+ return updatedTimelineData;
2040
+ }
2041
+ addTrack(name) {
2042
+ const prevTimelineData = this.getTimelineData();
2043
+ const id = `t-${generateShortUuid()}`;
2044
+ const track = new Track(name, id);
2045
+ const updatedTimelines = [...(prevTimelineData == null ? void 0 : prevTimelineData.tracks) || [], track];
2046
+ this.setTimelineData(updatedTimelines);
2047
+ return track;
2048
+ }
2049
+ getTrackById(id) {
2050
+ const prevTimelineData = this.getTimelineData();
2051
+ const track = prevTimelineData == null ? void 0 : prevTimelineData.tracks.find((t) => t.getId() === id);
2052
+ return track;
2053
+ }
2054
+ getTrackByName(name) {
2055
+ const prevTimelineData = this.getTimelineData();
2056
+ const track = prevTimelineData == null ? void 0 : prevTimelineData.tracks.find((t) => t.getName() === name);
2057
+ return track;
2058
+ }
2059
+ removeTrackById(id) {
2060
+ var _a;
2061
+ const tracks = ((_a = this.getTimelineData()) == null ? void 0 : _a.tracks) || [];
2062
+ const updatedTracks = tracks.filter((t) => t.getId() !== id);
2063
+ this.setTimelineData(updatedTracks);
2064
+ }
2065
+ removeTrack(track) {
2066
+ var _a;
2067
+ const tracks = ((_a = this.getTimelineData()) == null ? void 0 : _a.tracks) || [];
2068
+ const updatedTracks = tracks.filter((t) => t.getId() !== track.getId());
2069
+ this.setTimelineData(updatedTracks);
2070
+ }
2071
+ /**
2072
+ * Refresh the timeline data
2073
+ */
2074
+ refresh() {
2075
+ const currentData = this.getTimelineData();
2076
+ if (currentData) {
2077
+ this.setTimelineData(currentData.tracks);
2078
+ }
2079
+ }
2080
+ /**
2081
+ * Add an element to a specific track using the visitor pattern
2082
+ * @param track The track to add the element to
2083
+ * @param element The element to add
2084
+ * @returns Promise<boolean> true if element was added successfully
2085
+ */
2086
+ async addElementToTrack(track, element) {
2087
+ if (!track) {
2088
+ return false;
2089
+ }
2090
+ try {
2091
+ const elementAdder = new ElementAdder(track);
2092
+ const result = await element.accept(elementAdder);
2093
+ if (result) {
2094
+ const currentData = this.getTimelineData();
2095
+ if (currentData) {
2096
+ this.setTimelineData(currentData.tracks);
2097
+ }
2098
+ }
2099
+ return result;
2100
+ } catch (error) {
2101
+ return false;
2102
+ }
2103
+ }
2104
+ /**
2105
+ * Remove an element from a specific track using the visitor pattern
2106
+ * @param element The element to remove
2107
+ * @returns boolean true if element was removed successfully
2108
+ */
2109
+ removeElement(element) {
2110
+ const track = this.getTrackById(element.getTrackId());
2111
+ if (!track) {
2112
+ return false;
2113
+ }
2114
+ try {
2115
+ const elementRemover = new ElementRemover(track);
2116
+ const result = element.accept(elementRemover);
2117
+ if (result) {
2118
+ const currentData = this.getTimelineData();
2119
+ if (currentData) {
2120
+ this.setTimelineData(currentData.tracks);
2121
+ }
2122
+ }
2123
+ return result;
2124
+ } catch (error) {
2125
+ return false;
2126
+ }
2127
+ }
2128
+ /**
2129
+ * Update an element in a specific track using the visitor pattern
2130
+ * @param element The updated element
2131
+ * @returns boolean true if element was updated successfully
2132
+ */
2133
+ updateElement(element) {
2134
+ const track = this.getTrackById(element.getTrackId());
2135
+ if (!track) {
2136
+ return false;
2137
+ }
2138
+ try {
2139
+ const elementUpdater = new ElementUpdater(track);
2140
+ const result = element.accept(elementUpdater);
2141
+ if (result) {
2142
+ const currentData = this.getTimelineData();
2143
+ if (currentData) {
2144
+ this.setTimelineData(currentData.tracks);
2145
+ }
2146
+ }
2147
+ return result;
2148
+ } catch (error) {
2149
+ return false;
2150
+ }
2151
+ }
2152
+ /**
2153
+ * Split an element at a specific time point using the visitor pattern
2154
+ * @param element The element to split
2155
+ * @param splitTime The time point to split at
2156
+ * @returns SplitResult with first element, second element, and success status
2157
+ */
2158
+ async splitElement(element, splitTime) {
2159
+ const track = this.getTrackById(element.getTrackId());
2160
+ if (!track) {
2161
+ return { firstElement: element, secondElement: null, success: false };
2162
+ }
2163
+ try {
2164
+ const elementSplitter = new ElementSplitter(splitTime);
2165
+ const result = element.accept(elementSplitter);
2166
+ if (result.success) {
2167
+ const elementRemover = new ElementRemover(track);
2168
+ element.accept(elementRemover);
2169
+ const elementAdder = new ElementAdder(track);
2170
+ result.firstElement.accept(elementAdder);
2171
+ result.secondElement.accept(elementAdder);
2172
+ const currentData = this.getTimelineData();
2173
+ if (currentData) {
2174
+ this.setTimelineData(currentData.tracks);
2175
+ }
2176
+ }
2177
+ return result;
2178
+ } catch (error) {
2179
+ return { firstElement: element, secondElement: null, success: false };
2180
+ }
2181
+ }
2182
+ /**
2183
+ * Clone an element using the visitor pattern
2184
+ * @param element The element to clone
2185
+ * @returns TrackElement | null - the cloned element or null if cloning failed
2186
+ */
2187
+ cloneElement(element) {
2188
+ try {
2189
+ const elementCloner = new ElementCloner();
2190
+ return element.accept(elementCloner);
2191
+ } catch (error) {
2192
+ return null;
2193
+ }
2194
+ }
2195
+ reorderTracks(tracks) {
2196
+ this.setTimelineData(tracks);
2197
+ }
2198
+ updateHistory(timelineTrackData) {
2199
+ const tracks = timelineTrackData.tracks.map((t) => t.serialize());
2200
+ this.context.setTotalDuration(getTotalDuration(tracks));
2201
+ const version = timelineTrackData.version;
2202
+ this.context.setPresent({
2203
+ tracks,
2204
+ version
2205
+ });
2206
+ }
2207
+ /**
2208
+ * Trigger undo operation and update timeline data
2209
+ */
2210
+ undo() {
2211
+ var _a;
2212
+ const result = this.context.handleUndo();
2213
+ if (result && result.tracks) {
2214
+ const tracks = result.tracks.map((t) => Track.fromJSON(t));
2215
+ timelineContextStore.setTimelineData(this.context.contextId, {
2216
+ tracks,
2217
+ version: result.version
2218
+ });
2219
+ this.context.setTotalDuration(getTotalDuration(result.tracks));
2220
+ this.context.updateChangeLog();
2221
+ if ((_a = this.context) == null ? void 0 : _a.setTimelineAction) {
2222
+ this.context.setTimelineAction(TIMELINE_ACTION.UPDATE_PLAYER_DATA, {
2223
+ tracks: result.tracks,
2224
+ version: result.version
2225
+ });
2226
+ }
2227
+ }
2228
+ }
2229
+ /**
2230
+ * Trigger redo operation and update timeline data
2231
+ */
2232
+ redo() {
2233
+ var _a;
2234
+ const result = this.context.handleRedo();
2235
+ if (result && result.tracks) {
2236
+ const tracks = result.tracks.map((t) => Track.fromJSON(t));
2237
+ timelineContextStore.setTimelineData(this.context.contextId, {
2238
+ tracks,
2239
+ version: result.version
2240
+ });
2241
+ this.context.setTotalDuration(getTotalDuration(result.tracks));
2242
+ this.context.updateChangeLog();
2243
+ if ((_a = this.context) == null ? void 0 : _a.setTimelineAction) {
2244
+ this.context.setTimelineAction(TIMELINE_ACTION.UPDATE_PLAYER_DATA, {
2245
+ tracks: result.tracks,
2246
+ version: result.version
2247
+ });
2248
+ }
2249
+ }
2250
+ }
2251
+ /**
2252
+ * Reset history and clear timeline data
2253
+ */
2254
+ resetHistory() {
2255
+ var _a;
2256
+ this.context.handleResetHistory();
2257
+ timelineContextStore.setTimelineData(this.context.contextId, {
2258
+ tracks: [],
2259
+ version: 0
2260
+ });
2261
+ this.context.setTotalDuration(0);
2262
+ this.context.updateChangeLog();
2263
+ if ((_a = this.context) == null ? void 0 : _a.setTimelineAction) {
2264
+ this.context.setTimelineAction(TIMELINE_ACTION.UPDATE_PLAYER_DATA, {
2265
+ tracks: [],
2266
+ version: 0
2267
+ });
2268
+ }
2269
+ }
2270
+ loadProject({
2271
+ tracks,
2272
+ version
2273
+ }) {
2274
+ var _a;
2275
+ this.pauseVideo();
2276
+ this.context.handleResetHistory();
2277
+ const timelineTracks = tracks.map((t) => Track.fromJSON(t));
2278
+ this.setTimelineData(timelineTracks, version);
2279
+ if ((_a = this.context) == null ? void 0 : _a.setTimelineAction) {
2280
+ this.context.setTimelineAction(TIMELINE_ACTION.UPDATE_PLAYER_DATA, {
2281
+ tracks,
2282
+ version,
2283
+ forceUpdate: true
2284
+ });
2285
+ }
2286
+ }
2287
+ }
2288
+ const MAX_HISTORY = 20;
2289
+ const deepClone = (obj) => {
2290
+ return JSON.parse(JSON.stringify(obj));
2291
+ };
2292
+ const UndoRedoContext = react.createContext(
2293
+ void 0
2294
+ );
2295
+ const STORAGE_KEY_PREFIX = "twick_undo_redo_";
2296
+ const saveToStorage = (key, state) => {
2297
+ try {
2298
+ localStorage.setItem(key, JSON.stringify(state));
2299
+ } catch (error) {
2300
+ console.warn("Failed to save undo-redo state to localStorage:", error);
2301
+ }
2302
+ };
2303
+ const loadFromStorage = (key) => {
2304
+ try {
2305
+ const stored = localStorage.getItem(key);
2306
+ if (!stored) return null;
2307
+ return JSON.parse(stored);
2308
+ } catch (error) {
2309
+ console.warn("Failed to load undo-redo state from localStorage:", error);
2310
+ return null;
2311
+ }
2312
+ };
2313
+ const UndoRedoProvider = ({
2314
+ children,
2315
+ persistenceKey,
2316
+ maxHistorySize = MAX_HISTORY
2317
+ }) => {
2318
+ const [state, setState] = react.useState(() => {
2319
+ if (persistenceKey) {
2320
+ const stored = loadFromStorage(STORAGE_KEY_PREFIX + persistenceKey);
2321
+ if (stored) {
2322
+ return {
2323
+ past: stored.past,
2324
+ present: stored.present,
2325
+ future: stored.future
2326
+ };
2327
+ }
2328
+ }
2329
+ return {
2330
+ past: [],
2331
+ present: null,
2332
+ future: []
2333
+ };
2334
+ });
2335
+ const saveState = (newState) => {
2336
+ if (persistenceKey) {
2337
+ saveToStorage(STORAGE_KEY_PREFIX + persistenceKey, newState);
2338
+ }
2339
+ };
2340
+ const setPresent = (data) => {
2341
+ setState((prevState) => {
2342
+ let newPast = [...prevState.past];
2343
+ if (prevState.present) {
2344
+ newPast.push(deepClone(prevState.present));
2345
+ }
2346
+ const newState = {
2347
+ past: newPast,
2348
+ present: deepClone(data),
2349
+ future: []
2350
+ // Clear future because it's a new change
2351
+ };
2352
+ if (newState.past.length > maxHistorySize) {
2353
+ newState.past.shift();
2354
+ }
2355
+ saveState(newState);
2356
+ return newState;
2357
+ });
2358
+ };
2359
+ const undo = () => {
2360
+ let undoResult = null;
2361
+ setState((prevState) => {
2362
+ if (prevState.past.length === 0) return prevState;
2363
+ const previous = prevState.past[prevState.past.length - 1];
2364
+ const newState = {
2365
+ past: prevState.past.slice(0, -1),
2366
+ // Remove last item
2367
+ present: previous,
2368
+ future: prevState.present ? [deepClone(prevState.present), ...prevState.future] : prevState.future
2369
+ };
2370
+ undoResult = previous;
2371
+ saveState(newState);
2372
+ return newState;
2373
+ });
2374
+ return undoResult;
2375
+ };
2376
+ const redo = () => {
2377
+ let redoResult = null;
2378
+ setState((prevState) => {
2379
+ if (prevState.future.length === 0) return prevState;
2380
+ const next = prevState.future[0];
2381
+ const newState = {
2382
+ past: prevState.present ? [...prevState.past, deepClone(prevState.present)] : prevState.past,
2383
+ present: next,
2384
+ future: prevState.future.slice(1)
2385
+ // Remove first item
2386
+ };
2387
+ if (newState.past.length > maxHistorySize) {
2388
+ newState.past.shift();
2389
+ }
2390
+ redoResult = next;
2391
+ saveState(newState);
2392
+ return newState;
2393
+ });
2394
+ return redoResult;
2395
+ };
2396
+ const getLastPersistedState = () => {
2397
+ if (persistenceKey) {
2398
+ const stored = loadFromStorage(STORAGE_KEY_PREFIX + persistenceKey);
2399
+ return (stored == null ? void 0 : stored.present) || null;
2400
+ }
2401
+ return null;
2402
+ };
2403
+ const resetHistory = () => {
2404
+ const newState = {
2405
+ past: [],
2406
+ present: null,
2407
+ future: []
2408
+ };
2409
+ setState(newState);
2410
+ if (persistenceKey) {
2411
+ localStorage.removeItem(STORAGE_KEY_PREFIX + persistenceKey);
2412
+ }
2413
+ };
2414
+ const disablePersistence = () => {
2415
+ if (persistenceKey) {
2416
+ localStorage.removeItem(STORAGE_KEY_PREFIX + persistenceKey);
2417
+ }
2418
+ };
2419
+ const contextValue = {
2420
+ canUndo: state.past.length > 0,
2421
+ canRedo: state.future.length > 0,
2422
+ present: state.present,
2423
+ setPresent,
2424
+ undo,
2425
+ redo,
2426
+ resetHistory,
2427
+ getLastPersistedState,
2428
+ disablePersistence
2429
+ };
2430
+ return /* @__PURE__ */ jsxRuntime.jsx(UndoRedoContext.Provider, { value: contextValue, children });
2431
+ };
2432
+ const useUndoRedo = () => {
2433
+ const context = react.useContext(UndoRedoContext);
2434
+ if (context === void 0) {
2435
+ throw new Error("useUndoRedo must be used within an UndoRedoProvider");
2436
+ }
2437
+ return context;
2438
+ };
2439
+ const editorRegistry = /* @__PURE__ */ new Map();
2440
+ if (typeof window !== "undefined") {
2441
+ window.twickTimelineEditors = editorRegistry;
2442
+ }
2443
+ const TimelineContext = react.createContext(
2444
+ void 0
2445
+ );
2446
+ const TimelineProviderInner = ({
2447
+ contextId,
2448
+ children,
2449
+ initialData
2450
+ }) => {
2451
+ const [timelineAction, setTimelineActionState] = react.useState({
2452
+ type: TIMELINE_ACTION.NONE,
2453
+ payload: null
2454
+ });
2455
+ const [selectedItem, setSelectedItem] = react.useState(
2456
+ null
2457
+ );
2458
+ const [totalDuration, setTotalDuration] = react.useState(0);
2459
+ const [changeLog, setChangeLog] = react.useState(0);
2460
+ const undoRedoContext = useUndoRedo();
2461
+ const dataInitialized = react.useRef(false);
2462
+ const updateChangeLog = () => {
2463
+ setChangeLog((prev) => prev + 1);
2464
+ };
2465
+ const editor = react.useMemo(() => {
2466
+ if (editorRegistry.has(contextId)) {
2467
+ editorRegistry.delete(contextId);
2468
+ }
2469
+ const newEditor = new TimelineEditor({
2470
+ contextId,
2471
+ setTotalDuration,
2472
+ setPresent: undoRedoContext.setPresent,
2473
+ handleUndo: undoRedoContext.undo,
2474
+ handleRedo: undoRedoContext.redo,
2475
+ handleResetHistory: undoRedoContext.resetHistory,
2476
+ updateChangeLog,
2477
+ setTimelineAction: (action, payload) => {
2478
+ setTimelineActionState({ type: action, payload });
2479
+ }
2480
+ });
2481
+ editorRegistry.set(contextId, newEditor);
2482
+ return newEditor;
2483
+ }, [contextId]);
2484
+ const setTimelineAction = (type, payload) => {
2485
+ setTimelineActionState({ type, payload });
2486
+ };
2487
+ const initialize = (data) => {
2488
+ const lastPersistedState = undoRedoContext.getLastPersistedState();
2489
+ if (lastPersistedState) {
2490
+ editor.loadProject(lastPersistedState);
2491
+ return;
2492
+ } else {
2493
+ editor.loadProject(data);
2494
+ }
2495
+ };
2496
+ react.useEffect(() => {
2497
+ if (initialData && !dataInitialized.current) {
2498
+ initialize(initialData);
2499
+ dataInitialized.current = true;
2500
+ }
2501
+ }, [initialData]);
2502
+ const contextValue = {
2503
+ contextId,
2504
+ selectedItem,
2505
+ timelineAction,
2506
+ totalDuration,
2507
+ changeLog,
2508
+ present: undoRedoContext.present,
2509
+ canUndo: undoRedoContext.canUndo,
2510
+ canRedo: undoRedoContext.canRedo,
2511
+ setSelectedItem,
2512
+ setTimelineAction,
2513
+ editor
2514
+ // Include the editor instance
2515
+ };
2516
+ return /* @__PURE__ */ jsxRuntime.jsx(TimelineContext.Provider, { value: contextValue, children });
2517
+ };
2518
+ const TimelineProvider = ({
2519
+ contextId,
2520
+ children,
2521
+ initialData,
2522
+ undoRedoPersistenceKey,
2523
+ maxHistorySize
2524
+ }) => {
2525
+ return /* @__PURE__ */ jsxRuntime.jsx(
2526
+ UndoRedoProvider,
2527
+ {
2528
+ persistenceKey: undoRedoPersistenceKey,
2529
+ maxHistorySize,
2530
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2531
+ TimelineProviderInner,
2532
+ {
2533
+ initialData,
2534
+ contextId,
2535
+ undoRedoPersistenceKey,
2536
+ maxHistorySize,
2537
+ children
2538
+ }
2539
+ )
2540
+ }
2541
+ );
2542
+ };
2543
+ const useTimelineContext = () => {
2544
+ const context = react.useContext(TimelineContext);
2545
+ if (context === void 0) {
2546
+ throw new Error(
2547
+ "useTimelineContext must be used within a TimelineProvider"
2548
+ );
2549
+ }
2550
+ return context;
2551
+ };
2552
+ if (typeof window !== "undefined") {
2553
+ window.Twick = {
2554
+ Track,
2555
+ TrackElement,
2556
+ ElementDeserializer,
2557
+ ElementSerializer,
2558
+ ElementValidator,
2559
+ ElementAdder,
2560
+ ElementRemover,
2561
+ ElementUpdater,
2562
+ ElementSplitter,
2563
+ ElementCloner,
2564
+ TimelineEditor,
2565
+ TimelineProvider,
2566
+ TIMELINE_ELEMENT_TYPE,
2567
+ // Element types
2568
+ CaptionElement,
2569
+ RectElement,
2570
+ TextElement,
2571
+ ImageElement,
2572
+ AudioElement,
2573
+ CircleElement,
2574
+ IconElement,
2575
+ VideoElement,
2576
+ ElementAnimation,
2577
+ ElementFrameEffect,
2578
+ ElementTextEffect,
2579
+ // Utility functions
2580
+ generateShortUuid,
2581
+ getTotalDuration,
2582
+ getCurrentElements,
2583
+ isTrackId,
2584
+ isElementId
2585
+ };
2586
+ }
2587
+ exports2.AudioElement = AudioElement;
2588
+ exports2.CAPTION_COLOR = CAPTION_COLOR;
2589
+ exports2.CAPTION_FONT = CAPTION_FONT;
2590
+ exports2.CAPTION_STYLE = CAPTION_STYLE;
2591
+ exports2.CAPTION_STYLE_OPTIONS = CAPTION_STYLE_OPTIONS;
2592
+ exports2.CaptionElement = CaptionElement;
2593
+ exports2.CircleElement = CircleElement;
2594
+ exports2.ElementAdder = ElementAdder;
2595
+ exports2.ElementAnimation = ElementAnimation;
2596
+ exports2.ElementCloner = ElementCloner;
2597
+ exports2.ElementDeserializer = ElementDeserializer;
2598
+ exports2.ElementFrameEffect = ElementFrameEffect;
2599
+ exports2.ElementRemover = ElementRemover;
2600
+ exports2.ElementSerializer = ElementSerializer;
2601
+ exports2.ElementSplitter = ElementSplitter;
2602
+ exports2.ElementTextEffect = ElementTextEffect;
2603
+ exports2.ElementUpdater = ElementUpdater;
2604
+ exports2.ElementValidator = ElementValidator;
2605
+ exports2.IconElement = IconElement;
2606
+ exports2.ImageElement = ImageElement;
2607
+ exports2.PLAYER_STATE = PLAYER_STATE;
2608
+ exports2.PROCESS_STATE = PROCESS_STATE;
2609
+ exports2.RectElement = RectElement;
2610
+ exports2.TIMELINE_ACTION = TIMELINE_ACTION;
2611
+ exports2.TIMELINE_ELEMENT_TYPE = TIMELINE_ELEMENT_TYPE;
2612
+ exports2.TextElement = TextElement;
2613
+ exports2.TimelineEditor = TimelineEditor;
2614
+ exports2.TimelineProvider = TimelineProvider;
2615
+ exports2.Track = Track;
2616
+ exports2.TrackElement = TrackElement;
2617
+ exports2.ValidationError = ValidationError;
2618
+ exports2.VideoElement = VideoElement;
2619
+ exports2.WORDS_PER_PHRASE = WORDS_PER_PHRASE;
2620
+ exports2.canSplitElement = canSplitElement;
2621
+ exports2.generateShortUuid = generateShortUuid;
2622
+ exports2.getCurrentElements = getCurrentElements;
2623
+ exports2.getDecimalNumber = getDecimalNumber;
2624
+ exports2.getTotalDuration = getTotalDuration;
2625
+ exports2.isElementId = isElementId;
2626
+ exports2.isTrackId = isTrackId;
2627
+ exports2.useTimelineContext = useTimelineContext;
2628
+ Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
2629
+ });
2630
+ //# sourceMappingURL=index.js.map