captions.js 0.4.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,10 +1,1758 @@
1
+ // src/fonts/googleFonts.config.ts
2
+ var googleFontsList = [
3
+ "Roboto",
4
+ "Open Sans",
5
+ "Lato",
6
+ "Montserrat",
7
+ "Nunito",
8
+ "Poppins",
9
+ "Ubuntu",
10
+ "PT Sans",
11
+ "Merriweather Sans",
12
+ "Lobster",
13
+ "Amatic SC",
14
+ "Pacifico",
15
+ "Raleway",
16
+ "Cinzel",
17
+ "Quicksand",
18
+ "Zilla Slab",
19
+ "Caveat",
20
+ "Crimson Pro",
21
+ "Bebas Neue",
22
+ "Comfortaa",
23
+ "Satisfy",
24
+ "Permanent Marker",
25
+ "Oswald",
26
+ "Onset",
27
+ "Bangers",
28
+ "Kanit",
29
+ "Work Sans",
30
+ "Fira Sans",
31
+ "Anton",
32
+ "Playfair Display",
33
+ "Rubik",
34
+ "Alumni Sans",
35
+ "Righteous",
36
+ "Comico",
37
+ "Excon",
38
+ "Kalam",
39
+ "Tanker",
40
+ "Arsenal",
41
+ "Balsamiq Sans",
42
+ "Bona Nova SC"
43
+ ];
44
+
45
+ // src/stylePresets/stylePresets.config.ts
46
+ var stylePresets = [
47
+ {
48
+ id: 1,
49
+ captionsSettings: {
50
+ style: {
51
+ font: {
52
+ italic: false,
53
+ fontSize: 20,
54
+ fontColor: "#ffffffFF",
55
+ underline: false,
56
+ fontFamily: "Montserrat",
57
+ fontWeight: "black",
58
+ fontCapitalize: false,
59
+ fontStrokeColor: "#000000FF",
60
+ fontStrokeWidth: 0
61
+ },
62
+ name: "Karaoke",
63
+ backgroundColor: "#E4E4E4FF",
64
+ verticalCoverImg: "https://storage.googleapis.com/loomz-front-static/video-editor/img/karaoke_preview.png",
65
+ aplifiedWordColor: "#04f827FF"
66
+ },
67
+ position: "bottom",
68
+ animation: "bounce",
69
+ linesPerPage: 3
70
+ },
71
+ layoutSettings: {
72
+ aspectRatio: "9:16",
73
+ aIAutoLayout: ["fill", "fit", "split", "three", "four", "screenShare"],
74
+ fitLayoutAspectRatio: "original"
75
+ }
76
+ },
77
+ {
78
+ id: 2,
79
+ captionsSettings: {
80
+ style: {
81
+ font: {
82
+ italic: false,
83
+ fontSize: 25,
84
+ fontColor: "#ffffffFF",
85
+ underline: false,
86
+ fontFamily: "Permanent Marker",
87
+ fontWeight: "black",
88
+ fontCapitalize: true,
89
+ fontStrokeColor: "#000000FF",
90
+ fontStrokeWidth: 0
91
+ },
92
+ name: "Beasty",
93
+ backgroundColor: "#E4E4E4FF",
94
+ verticalCoverImg: "https://storage.googleapis.com/loomz-front-static/video-editor/img/beasty_preview.png",
95
+ aplifiedWordColor: "#ff0000ff"
96
+ },
97
+ position: "bottom",
98
+ animation: "pop",
99
+ linesPerPage: 1
100
+ },
101
+ layoutSettings: {
102
+ aspectRatio: "9:16",
103
+ aIAutoLayout: ["fill", "fit", "split", "three", "four", "screenShare"],
104
+ fitLayoutAspectRatio: "original"
105
+ }
106
+ },
107
+ {
108
+ id: 3,
109
+ captionsSettings: {
110
+ style: {
111
+ font: {
112
+ italic: false,
113
+ fontSize: 23,
114
+ fontColor: "#C6C6C6FF",
115
+ underline: false,
116
+ fontFamily: "Poppins",
117
+ fontWeight: "bold",
118
+ fontCapitalize: false,
119
+ fontStrokeColor: "#000000FF",
120
+ fontStrokeWidth: 0
121
+ },
122
+ name: "Safari",
123
+ backgroundColor: "#E4E4E4FF",
124
+ verticalCoverImg: "https://storage.googleapis.com/loomz-front-static/video-editor/img/safari_preview.png",
125
+ aplifiedWordColor: "#000000FF"
126
+ },
127
+ position: "bottom",
128
+ animation: "box",
129
+ linesPerPage: 1
130
+ },
131
+ layoutSettings: {
132
+ aspectRatio: "9:16",
133
+ aIAutoLayout: ["fill", "fit", "split", "three", "four", "screenShare"],
134
+ fitLayoutAspectRatio: "original"
135
+ }
136
+ },
137
+ {
138
+ id: 4,
139
+ captionsSettings: {
140
+ style: {
141
+ font: {
142
+ italic: false,
143
+ fontSize: 24,
144
+ fontColor: "#817B81FF",
145
+ underline: false,
146
+ fontFamily: "PT Sans",
147
+ fontWeight: "black",
148
+ fontCapitalize: false,
149
+ fontStrokeColor: "#000000FF",
150
+ fontStrokeWidth: 0
151
+ },
152
+ name: "Acid",
153
+ backgroundColor: "#00000000",
154
+ verticalCoverImg: "https://storage.googleapis.com/loomz-front-static/video-editor/img/aciv_preview.png",
155
+ aplifiedWordColor: "#6BF5C7FF"
156
+ },
157
+ position: "bottom",
158
+ animation: "none",
159
+ linesPerPage: 1,
160
+ positionTopOffset: 42
161
+ },
162
+ layoutSettings: {
163
+ aspectRatio: "9:16",
164
+ aIAutoLayout: ["fill", "fit", "split", "three", "four", "screenShare"],
165
+ fitLayoutAspectRatio: "original"
166
+ }
167
+ },
168
+ {
169
+ id: 5,
170
+ captionsSettings: {
171
+ style: {
172
+ font: {
173
+ italic: false,
174
+ fontSize: 26,
175
+ fontColor: "#F43FE0FF",
176
+ underline: false,
177
+ fontFamily: "Raleway",
178
+ fontWeight: "bold",
179
+ fontCapitalize: false,
180
+ fontStrokeColor: "#000000FF",
181
+ fontStrokeWidth: 8
182
+ },
183
+ name: "Popline",
184
+ backgroundColor: "#E4E4E4FF",
185
+ verticalCoverImg: "https://storage.googleapis.com/loomz-front-static/video-editor/img/popline_preview.png",
186
+ aplifiedWordColor: "#F43FE0FF"
187
+ },
188
+ position: "bottom",
189
+ animation: "none",
190
+ linesPerPage: 1
191
+ },
192
+ layoutSettings: {
193
+ aspectRatio: "9:16",
194
+ aIAutoLayout: ["fill", "fit", "split", "three", "four", "screenShare"],
195
+ fitLayoutAspectRatio: "original"
196
+ }
197
+ },
198
+ {
199
+ id: 6,
200
+ captionsSettings: {
201
+ style: {
202
+ font: {
203
+ italic: false,
204
+ fontSize: 20,
205
+ fontColor: "#FFFFFFFF",
206
+ underline: false,
207
+ fontFamily: "Montserrat",
208
+ fontWeight: "black",
209
+ fontCapitalize: true,
210
+ fontStrokeColor: "#000000FF",
211
+ fontStrokeWidth: 0
212
+ },
213
+ name: "Desert",
214
+ backgroundColor: "#E4E4E4FF",
215
+ verticalCoverImg: "https://storage.googleapis.com/loomz-front-static/video-editor/img/desert_preview.png",
216
+ aplifiedWordColor: "#8F2EEDFF"
217
+ },
218
+ position: "bottom",
219
+ animation: "bounce",
220
+ linesPerPage: 2
221
+ },
222
+ layoutSettings: {
223
+ aspectRatio: "9:16",
224
+ aIAutoLayout: ["fill", "fit", "split", "three", "four", "screenShare"],
225
+ fitLayoutAspectRatio: "original"
226
+ }
227
+ },
228
+ {
229
+ id: 7,
230
+ captionsSettings: {
231
+ style: {
232
+ font: {
233
+ italic: false,
234
+ fontSize: 26,
235
+ fontColor: "#FFFFFFFF",
236
+ underline: false,
237
+ fontFamily: "Lato",
238
+ fontWeight: "black",
239
+ fontCapitalize: true,
240
+ fontStrokeColor: "#000000FF",
241
+ fontStrokeWidth: 0
242
+ },
243
+ name: "Hook",
244
+ backgroundColor: "#E4E4E4FF",
245
+ verticalCoverImg: "https://storage.googleapis.com/loomz-front-static/video-editor/img/hook_preview.png",
246
+ aplifiedWordColor: "#8F2EEDFF"
247
+ },
248
+ position: "bottom",
249
+ animation: "underline",
250
+ linesPerPage: 2
251
+ },
252
+ layoutSettings: {
253
+ aspectRatio: "9:16",
254
+ aIAutoLayout: ["fill", "fit", "split", "three", "four", "screenShare"],
255
+ fitLayoutAspectRatio: "original"
256
+ }
257
+ },
258
+ {
259
+ id: 8,
260
+ captionsSettings: {
261
+ style: {
262
+ font: {
263
+ italic: false,
264
+ fontSize: 20,
265
+ fontColor: "#FFFFFFFF",
266
+ underline: false,
267
+ fontFamily: "Montserrat",
268
+ fontWeight: "black",
269
+ fontCapitalize: true,
270
+ fontStrokeColor: "#000000FF",
271
+ fontStrokeWidth: 0
272
+ },
273
+ name: "Sky",
274
+ backgroundColor: "#E4E4E4FF",
275
+ verticalCoverImg: "https://storage.googleapis.com/loomz-front-static/video-editor/img/sky_preview.png",
276
+ aplifiedWordColor: "#FFFFFFFF"
277
+ },
278
+ position: "bottom",
279
+ animation: "slide-left",
280
+ linesPerPage: 2
281
+ },
282
+ layoutSettings: {
283
+ aspectRatio: "9:16",
284
+ aIAutoLayout: ["fill", "fit", "split", "three", "four", "screenShare"],
285
+ fitLayoutAspectRatio: "original"
286
+ }
287
+ },
288
+ {
289
+ id: 9,
290
+ captionsSettings: {
291
+ style: {
292
+ font: {
293
+ italic: false,
294
+ fontSize: 20,
295
+ fontColor: "#FFFFFFFF",
296
+ underline: false,
297
+ fontFamily: "Montserrat",
298
+ fontWeight: "black",
299
+ fontCapitalize: true,
300
+ fontStrokeColor: "#000000FF",
301
+ fontStrokeWidth: 0
302
+ },
303
+ name: "Flamingo",
304
+ backgroundColor: "#E4E4E4FF",
305
+ verticalCoverImg: "https://storage.googleapis.com/loomz-front-static/video-editor/img/flamingo_preview.png",
306
+ aplifiedWordColor: "#ff0000ff"
307
+ },
308
+ position: "bottom",
309
+ animation: "scale",
310
+ linesPerPage: 2
311
+ },
312
+ layoutSettings: {
313
+ aspectRatio: "9:16",
314
+ aIAutoLayout: ["fill", "fit", "split", "three", "four", "screenShare"],
315
+ fitLayoutAspectRatio: "original"
316
+ }
317
+ },
318
+ {
319
+ id: 10,
320
+ captionsSettings: {
321
+ style: {
322
+ font: {
323
+ italic: false,
324
+ fontSize: 26,
325
+ fontColor: "#8a8a8aff",
326
+ underline: false,
327
+ fontFamily: "Raleway",
328
+ fontWeight: "bold",
329
+ fontCapitalize: false,
330
+ fontStrokeColor: "#000000FF",
331
+ fontStrokeWidth: 0
332
+ },
333
+ name: "Deep Diver B&W",
334
+ backgroundColor: "#000000ff",
335
+ verticalCoverImg: "https://storage.googleapis.com/loomz-front-static/video-editor/img/captions-deep-diver-b&w.png",
336
+ aplifiedWordColor: "#ffffffff"
337
+ },
338
+ position: "bottom",
339
+ animation: "box",
340
+ linesPerPage: 1
341
+ },
342
+ layoutSettings: {
343
+ aspectRatio: "9:16",
344
+ aIAutoLayout: ["fill", "fit", "split", "three", "four", "screenShare"],
345
+ fitLayoutAspectRatio: "original"
346
+ }
347
+ },
348
+ {
349
+ id: 11,
350
+ captionsSettings: {
351
+ style: {
352
+ font: {
353
+ italic: false,
354
+ fontSize: 20,
355
+ fontColor: "#b0b0b0ff",
356
+ underline: false,
357
+ fontFamily: "Montserrat",
358
+ fontWeight: "bold",
359
+ fontCapitalize: true,
360
+ fontStrokeColor: "#000000FF",
361
+ fontStrokeWidth: 0
362
+ },
363
+ name: "New",
364
+ backgroundColor: "#E4E4E4FF",
365
+ verticalCoverImg: "https://storage.googleapis.com/loomz-front-static/video-editor/img/flamingo_preview.png",
366
+ aplifiedWordColor: "#dbff00ff"
367
+ },
368
+ position: "middle",
369
+ animation: "pop",
370
+ linesPerPage: 3
371
+ },
372
+ layoutSettings: {
373
+ aspectRatio: "9:16",
374
+ aIAutoLayout: ["fill", "fit", "split", "three", "four", "screenShare"],
375
+ fitLayoutAspectRatio: "original"
376
+ }
377
+ },
378
+ {
379
+ id: 12,
380
+ captionsSettings: {
381
+ style: {
382
+ font: {
383
+ italic: false,
384
+ fontSize: 30,
385
+ fontColor: "#ffffffFF",
386
+ underline: false,
387
+ fontFamily: "Bangers",
388
+ fontWeight: "bold",
389
+ fontCapitalize: true,
390
+ fontStrokeColor: "#000000ff",
391
+ fontStrokeWidth: 60
392
+ },
393
+ name: "Banger",
394
+ backgroundColor: "#E4E4E4FF",
395
+ verticalCoverImg: "https://storage.googleapis.com/loomz-front-static/video-editor/img/beasty_preview.png",
396
+ aplifiedWordColor: "#fdfa14ff"
397
+ },
398
+ position: "bottom",
399
+ animation: "pop",
400
+ linesPerPage: 1
401
+ },
402
+ layoutSettings: {
403
+ aspectRatio: "9:16",
404
+ aIAutoLayout: ["fill", "fit", "split", "three", "four", "screenShare"],
405
+ fitLayoutAspectRatio: "original"
406
+ }
407
+ },
408
+ {
409
+ id: 13,
410
+ captionsSettings: {
411
+ style: {
412
+ font: {
413
+ italic: false,
414
+ fontSize: 20,
415
+ fontColor: "#FFFFFFFF",
416
+ underline: false,
417
+ fontFamily: "Montserrat",
418
+ fontWeight: "black",
419
+ fontCapitalize: true,
420
+ fontStrokeColor: "#000000FF",
421
+ fontStrokeWidth: 30
422
+ },
423
+ name: "Catchy",
424
+ backgroundColor: "#E4E4E4FF",
425
+ verticalCoverImg: "https://storage.googleapis.com/loomz-front-static/video-editor/img/desert_preview.png",
426
+ aplifiedWordColor: "#ff5700ff"
427
+ },
428
+ position: "bottom",
429
+ animation: "bounce",
430
+ linesPerPage: 2
431
+ },
432
+ layoutSettings: {
433
+ aspectRatio: "9:16",
434
+ aIAutoLayout: ["fill", "fit", "split", "three", "four", "screenShare"],
435
+ fitLayoutAspectRatio: "original"
436
+ }
437
+ },
438
+ {
439
+ id: 14,
440
+ captionsSettings: {
441
+ style: {
442
+ font: {
443
+ italic: false,
444
+ fontSize: 25,
445
+ fontColor: "#ffffffFF",
446
+ underline: false,
447
+ fontFamily: "Rubik",
448
+ fontWeight: "black",
449
+ fontCapitalize: true,
450
+ fontStrokeColor: "#000000FF",
451
+ fontStrokeWidth: 40
452
+ },
453
+ name: "Karaoke 2",
454
+ backgroundColor: "#E4E4E4FF",
455
+ verticalCoverImg: "https://storage.googleapis.com/loomz-front-static/video-editor/img/karaoke_preview.png",
456
+ aplifiedWordColor: "#2bf82aff"
457
+ },
458
+ position: "bottom",
459
+ animation: "bounce",
460
+ linesPerPage: 2
461
+ },
462
+ layoutSettings: {
463
+ aspectRatio: "9:16",
464
+ aIAutoLayout: ["fill", "fit", "split", "three", "four", "screenShare"],
465
+ fitLayoutAspectRatio: "original"
466
+ }
467
+ },
468
+ {
469
+ id: 15,
470
+ captionsSettings: {
471
+ style: {
472
+ font: {
473
+ italic: false,
474
+ fontSize: 25,
475
+ fontColor: "#ffffffFF",
476
+ underline: false,
477
+ fontFamily: "Rubik",
478
+ fontWeight: "black",
479
+ fontCapitalize: true,
480
+ fontStrokeColor: "#000000FF",
481
+ fontStrokeWidth: 60
482
+ },
483
+ name: "Karaoke 3",
484
+ backgroundColor: "#E4E4E4FF",
485
+ verticalCoverImg: "https://storage.googleapis.com/loomz-front-static/video-editor/img/karaoke_preview.png",
486
+ aplifiedWordColor: "#2bf82aff"
487
+ },
488
+ position: "bottom",
489
+ animation: "bounce",
490
+ linesPerPage: 2
491
+ },
492
+ layoutSettings: {
493
+ aspectRatio: "9:16",
494
+ aIAutoLayout: ["fill", "fit", "split", "three", "four", "screenShare"],
495
+ fitLayoutAspectRatio: "original"
496
+ }
497
+ },
498
+ {
499
+ id: 16,
500
+ captionsSettings: {
501
+ style: {
502
+ font: {
503
+ italic: false,
504
+ shadow: {
505
+ fontShadowBlur: 0,
506
+ fontShadowColor: "#000000ff",
507
+ fontShadowOffsetX: 2,
508
+ fontShadowOffsetY: 2
509
+ },
510
+ fontSize: 25,
511
+ fontColor: "#ffffffFF",
512
+ underline: false,
513
+ fontFamily: "Kanit",
514
+ fontWeight: "black",
515
+ fontCapitalize: true,
516
+ fontStrokeColor: "#000000FF",
517
+ fontStrokeWidth: 40
518
+ },
519
+ name: "From",
520
+ backgroundColor: "#E4E4E4FF",
521
+ verticalCoverImg: "https://storage.googleapis.com/loomz-front-static/video-editor/img/karaoke_preview.png",
522
+ aplifiedWordColor: "#ffdd03ff"
523
+ },
524
+ position: "bottom",
525
+ animation: "pop",
526
+ linesPerPage: 1
527
+ },
528
+ layoutSettings: {
529
+ aspectRatio: "9:16",
530
+ aIAutoLayout: ["fill", "fit", "split", "three", "four", "screenShare"],
531
+ fitLayoutAspectRatio: "original"
532
+ }
533
+ },
534
+ {
535
+ id: 17,
536
+ captionsSettings: {
537
+ style: {
538
+ font: {
539
+ italic: false,
540
+ fontSize: 15,
541
+ fontColor: "#ffffffff",
542
+ underline: false,
543
+ fontFamily: "Roboto",
544
+ fontWeight: "bold",
545
+ fontCapitalize: false,
546
+ fontStrokeColor: "#000000FF",
547
+ fontStrokeWidth: 20
548
+ },
549
+ name: "Classic",
550
+ backgroundColor: "#000000ff",
551
+ verticalCoverImg: "https://storage.googleapis.com/loomz-front-static/video-editor/img/safari_preview.png",
552
+ aplifiedWordColor: "#ffffffff"
553
+ },
554
+ position: "bottom",
555
+ animation: "none",
556
+ linesPerPage: 2
557
+ },
558
+ layoutSettings: {
559
+ aspectRatio: "9:16",
560
+ aIAutoLayout: ["fill", "fit", "split", "three", "four", "screenShare"],
561
+ fitLayoutAspectRatio: "original"
562
+ }
563
+ },
564
+ {
565
+ id: 18,
566
+ captionsSettings: {
567
+ style: {
568
+ font: {
569
+ italic: false,
570
+ fontSize: 30,
571
+ fontColor: "#ffffffff",
572
+ underline: false,
573
+ fontFamily: "Roboto",
574
+ fontWeight: "medium",
575
+ fontCapitalize: false,
576
+ fontStrokeColor: "#000000FF",
577
+ fontStrokeWidth: 40
578
+ },
579
+ name: "Classic Big",
580
+ backgroundColor: "#000000ff",
581
+ verticalCoverImg: "https://storage.googleapis.com/loomz-front-static/video-editor/img/safari_preview.png",
582
+ aplifiedWordColor: "#ffffffff"
583
+ },
584
+ position: "bottom",
585
+ animation: "none",
586
+ linesPerPage: 2
587
+ },
588
+ layoutSettings: {
589
+ aspectRatio: "9:16",
590
+ aIAutoLayout: ["fill", "fit", "split", "three", "four", "screenShare"],
591
+ fitLayoutAspectRatio: "original"
592
+ }
593
+ },
594
+ {
595
+ id: 19,
596
+ captionsSettings: {
597
+ style: {
598
+ font: {
599
+ italic: false,
600
+ fontSize: 25,
601
+ fontColor: "#FFFFFFFF",
602
+ underline: false,
603
+ fontFamily: "Montserrat",
604
+ fontWeight: "black",
605
+ fontCapitalize: true,
606
+ fontStrokeColor: "#000000FF",
607
+ fontStrokeWidth: 80
608
+ },
609
+ name: "Crazy",
610
+ backgroundColor: "#E4E4E4FF",
611
+ verticalCoverImg: "https://storage.googleapis.com/loomz-front-static/video-editor/img/desert_preview.png",
612
+ aplifiedWordColor: "#ebf901ff"
613
+ },
614
+ position: "bottom",
615
+ animation: "none",
616
+ linesPerPage: 2,
617
+ positionTopOffset: 0
618
+ },
619
+ layoutSettings: {
620
+ aspectRatio: "9:16",
621
+ aIAutoLayout: ["fill", "fit", "split", "three", "four", "screenShare"],
622
+ fitLayoutAspectRatio: "original"
623
+ }
624
+ },
625
+ {
626
+ id: 20,
627
+ captionsSettings: {
628
+ style: {
629
+ font: {
630
+ italic: false,
631
+ fontSize: 30,
632
+ fontColor: "#ffffffff",
633
+ underline: false,
634
+ fontFamily: "PT Sans",
635
+ fontWeight: "black",
636
+ fontCapitalize: false,
637
+ fontStrokeColor: "#000000FF",
638
+ fontStrokeWidth: 40
639
+ },
640
+ name: "Acid 2",
641
+ backgroundColor: "#00000000",
642
+ verticalCoverImg: "https://storage.googleapis.com/loomz-front-static/video-editor/img/aciv_preview.png",
643
+ aplifiedWordColor: "#6BF5C7FF"
644
+ },
645
+ position: "bottom",
646
+ animation: "none",
647
+ linesPerPage: 1,
648
+ positionTopOffset: 42
649
+ },
650
+ layoutSettings: {
651
+ aspectRatio: "9:16",
652
+ aIAutoLayout: ["fill", "fit", "split", "three", "four", "screenShare"],
653
+ fitLayoutAspectRatio: "original"
654
+ }
655
+ },
656
+ {
657
+ id: 21,
658
+ captionsSettings: {
659
+ style: {
660
+ font: {
661
+ italic: false,
662
+ fontSize: 20,
663
+ fontColor: "#ffffffff",
664
+ underline: false,
665
+ fontFamily: "Oswald",
666
+ fontWeight: "medium",
667
+ fontCapitalize: false,
668
+ fontStrokeColor: "#000000FF",
669
+ fontStrokeWidth: 40
670
+ },
671
+ name: "Marvel",
672
+ backgroundColor: "#00000000",
673
+ verticalCoverImg: "https://storage.googleapis.com/loomz-front-static/video-editor/img/aciv_preview.png",
674
+ aplifiedWordColor: "#a76dffff"
675
+ },
676
+ position: "bottom",
677
+ animation: "scale",
678
+ linesPerPage: 2,
679
+ positionTopOffset: 0
680
+ },
681
+ layoutSettings: {
682
+ aspectRatio: "9:16",
683
+ aIAutoLayout: ["fill", "fit", "split", "three", "four", "screenShare"],
684
+ fitLayoutAspectRatio: "original"
685
+ }
686
+ },
687
+ {
688
+ id: 22,
689
+ captionsSettings: {
690
+ style: {
691
+ font: {
692
+ italic: false,
693
+ shadow: {
694
+ fontShadowBlur: 1,
695
+ fontShadowColor: "0",
696
+ fontShadowOffsetX: 2,
697
+ fontShadowOffsetY: 2
698
+ },
699
+ fontSize: 25,
700
+ fontColor: "#ffffffff",
701
+ underline: false,
702
+ fontFamily: "Pacifico",
703
+ fontWeight: "medium",
704
+ fontCapitalize: false,
705
+ fontStrokeColor: "#000000FF",
706
+ fontStrokeWidth: 0
707
+ },
708
+ name: "Lovly",
709
+ backgroundColor: "#00000000",
710
+ verticalCoverImg: "https://storage.googleapis.com/loomz-front-static/video-editor/img/aciv_preview.png",
711
+ aplifiedWordColor: "#f866cfff"
712
+ },
713
+ position: "bottom",
714
+ animation: "scale",
715
+ linesPerPage: 2,
716
+ positionTopOffset: 0
717
+ },
718
+ layoutSettings: {
719
+ aspectRatio: "9:16",
720
+ aIAutoLayout: ["fill", "fit", "split", "three", "four", "screenShare"],
721
+ fitLayoutAspectRatio: "original"
722
+ }
723
+ },
724
+ {
725
+ id: 23,
726
+ captionsSettings: {
727
+ style: {
728
+ font: {
729
+ italic: false,
730
+ shadow: {
731
+ fontShadowBlur: 0,
732
+ fontShadowColor: "#023210ff",
733
+ fontShadowOffsetX: 2,
734
+ fontShadowOffsetY: 2
735
+ },
736
+ fontSize: 25,
737
+ fontColor: "#ffffffff",
738
+ underline: false,
739
+ fontFamily: "Playfair Display",
740
+ fontWeight: "regular",
741
+ fontCapitalize: false,
742
+ fontStrokeColor: "#000000FF",
743
+ fontStrokeWidth: 0
744
+ },
745
+ name: "Old Money",
746
+ backgroundColor: "#00000000",
747
+ verticalCoverImg: "https://storage.googleapis.com/loomz-front-static/video-editor/img/aciv_preview.png",
748
+ aplifiedWordColor: "#ffffffff"
749
+ },
750
+ position: "bottom",
751
+ animation: "slide-up",
752
+ linesPerPage: 3,
753
+ positionTopOffset: 0
754
+ },
755
+ layoutSettings: {
756
+ aspectRatio: "9:16",
757
+ aIAutoLayout: ["fill", "fit", "split", "three", "four", "screenShare"],
758
+ fitLayoutAspectRatio: "original"
759
+ }
760
+ },
761
+ {
762
+ id: 24,
763
+ captionsSettings: {
764
+ style: {
765
+ font: {
766
+ italic: false,
767
+ fontSize: 14,
768
+ fontColor: "#e4e900ff",
769
+ underline: false,
770
+ fontFamily: "Lato",
771
+ fontWeight: "bold",
772
+ fontCapitalize: false,
773
+ fontStrokeColor: "#000000FF",
774
+ fontStrokeWidth: 20
775
+ },
776
+ name: "Cinema",
777
+ backgroundColor: "#000000ff",
778
+ verticalCoverImg: "https://storage.googleapis.com/loomz-front-static/video-editor/img/safari_preview.png",
779
+ aplifiedWordColor: "#e4e900ff"
780
+ },
781
+ position: "bottom",
782
+ animation: "none",
783
+ linesPerPage: 2
784
+ },
785
+ layoutSettings: {
786
+ aspectRatio: "9:16",
787
+ aIAutoLayout: ["fill", "fit", "split", "three", "four", "screenShare"],
788
+ fitLayoutAspectRatio: "original"
789
+ }
790
+ },
791
+ {
792
+ id: 25,
793
+ captionsSettings: {
794
+ style: {
795
+ font: {
796
+ italic: false,
797
+ shadow: {
798
+ fontShadowBlur: 10,
799
+ fontShadowColor: "#000000ff",
800
+ fontShadowOffsetX: 2,
801
+ fontShadowOffsetY: 2
802
+ },
803
+ fontSize: 20,
804
+ fontColor: "#FFFFFFFF",
805
+ underline: false,
806
+ fontFamily: "Raleway",
807
+ fontWeight: "black",
808
+ fontCapitalize: false,
809
+ fontStrokeColor: "#000000FF",
810
+ fontStrokeWidth: 0
811
+ },
812
+ name: "Marker",
813
+ backgroundColor: "#E4E4E4FF",
814
+ verticalCoverImg: "https://storage.googleapis.com/loomz-front-static/video-editor/img/flamingo_preview.png",
815
+ aplifiedWordColor: "#6f0eecff"
816
+ },
817
+ position: "bottom",
818
+ animation: "underline",
819
+ linesPerPage: 2
820
+ },
821
+ layoutSettings: {
822
+ aspectRatio: "9:16",
823
+ aIAutoLayout: ["fill", "fit", "split", "three", "four", "screenShare"],
824
+ fitLayoutAspectRatio: "original"
825
+ }
826
+ }
827
+ ];
828
+
829
+ // src/fonts/googleFonts.helpers.ts
830
+ import Konva from "konva";
831
+ async function loadGoogleFont2(fontFamily) {
832
+ const fontNameForUrl = fontFamily.replace(/\s+/g, "+");
833
+ const fontUrl = `https://fonts.googleapis.com/css2?family=${fontNameForUrl}:wght@400;700&display=swap`;
834
+ if (!document.querySelector(`link[href="${fontUrl}"]`)) {
835
+ const link = document.createElement("link");
836
+ link.rel = "stylesheet";
837
+ link.href = fontUrl;
838
+ document.head.appendChild(link);
839
+ }
840
+ try {
841
+ await document.fonts.load(`16px "${fontFamily}"`);
842
+ await document.fonts.ready;
843
+ } catch (err) {
844
+ console.warn(`[loadGoogleFont] failed to load font "${fontFamily}":`, err);
845
+ }
846
+ Konva.stages?.forEach((stage) => stage.batchDraw());
847
+ console.log(
848
+ `[loadGoogleFont] Font "${fontFamily}" loaded and ready for Konva.`
849
+ );
850
+ }
851
+
852
+ // src/render/renderString.ts
853
+ import Konva2 from "konva";
854
+ async function renderString(canvas, text, options) {
855
+ const { preset } = options;
856
+ const width = parseInt(canvas.style.width);
857
+ const height = parseInt(canvas.style.height);
858
+ const container = document.createElement("div");
859
+ var stage = new Konva2.Stage({
860
+ container,
861
+ width,
862
+ height
863
+ });
864
+ console.log("width, height", width, height);
865
+ var layer = new Konva2.Layer();
866
+ stage.add(layer);
867
+ await loadGoogleFont2(
868
+ preset.captionsSettings.style.font.fontFamily
869
+ );
870
+ await new Promise((resolve) => requestAnimationFrame(resolve));
871
+ const background = new Konva2.Rect({
872
+ x: 0,
873
+ y: 0,
874
+ width,
875
+ height,
876
+ fill: "black"
877
+ });
878
+ layer.add(background);
879
+ const textOptions = {
880
+ x: width / 2,
881
+ y: height / 2,
882
+ text,
883
+ fontSize: preset.captionsSettings.style.font.fontSize,
884
+ fontFamily: preset.captionsSettings.style.font.fontFamily,
885
+ fill: preset.captionsSettings.style.font.fontColor,
886
+ align: "center",
887
+ offsetX: 0,
888
+ offsetY: 0
889
+ };
890
+ console.log("textOptions", textOptions);
891
+ const konvaText = new Konva2.Text(textOptions);
892
+ konvaText.offsetX(konvaText.width() / 2);
893
+ konvaText.offsetY(konvaText.height() / 2);
894
+ layer.add(konvaText);
895
+ layer.draw();
896
+ const konvaCanvas = layer.getCanvas()._canvas;
897
+ const konvaContext = konvaCanvas.getContext("2d");
898
+ if (konvaContext) {
899
+ const targetContext = canvas.getContext("2d");
900
+ if (targetContext) {
901
+ targetContext.setTransform(
902
+ Konva2.pixelRatio,
903
+ 0,
904
+ 0,
905
+ Konva2.pixelRatio,
906
+ 0,
907
+ 0
908
+ );
909
+ targetContext.drawImage(konvaCanvas, 0, 0, width, height);
910
+ }
911
+ }
912
+ return true;
913
+ }
914
+
915
+ // src/captions/Captions.ts
916
+ import Konva5 from "konva";
917
+
918
+ // src/canvas-captions/index.ts
919
+ import Konva4 from "konva";
920
+
921
+ // src/canvas-captions/animate.ts
922
+ import Konva3 from "konva";
923
+
924
+ // src/canvas-captions/easing.ts
925
+ var mapEaseToFn = {
926
+ ["linear" /* linear */]: linear,
927
+ ["easeInSine" /* inSine */]: easeInSine,
928
+ ["easeOutSine" /* outSine */]: easeOutSine,
929
+ ["easeInOutSine" /* inOutSine */]: easeInOutSine,
930
+ ["easeInQuad" /* inQuad */]: easeInQuad,
931
+ ["easeOutQuad" /* outQuad */]: easeOutQuad,
932
+ ["easeInOutQuad" /* inOutQuad */]: easeInOutQuad,
933
+ ["easeInCubic" /* inCubic */]: easeInCubic,
934
+ ["easeOutCubic" /* outCubic */]: easeOutCubic,
935
+ ["easeInOutCubic" /* inOutCubic */]: easeInOutCubic,
936
+ ["easeInQuart" /* inQuart */]: easeInQuart,
937
+ ["easeOutQuart" /* outQuart */]: easeOutQuart,
938
+ ["easeInOutQuart" /* inOutQuart */]: easeInOutQuart,
939
+ ["easeInQuint" /* inQuint */]: easeInQuint,
940
+ ["easeOutQuint" /* outQuint */]: easeOutQuint,
941
+ ["easeInOutQuint" /* inOutQuint */]: easeInOutQuint,
942
+ ["easeInExpo" /* inExpo */]: easeInExpo,
943
+ ["easeOutExpo" /* outExpo */]: easeOutExpo,
944
+ ["easeInOutExpo" /* inOutExpo */]: easeInOutExpo,
945
+ ["easeInCirc" /* inCirc */]: easeInCirc,
946
+ ["easeOutCirc" /* outCirc */]: easeOutCirc,
947
+ ["easeInOutCirc" /* inOutCirc */]: easeInOutCirc,
948
+ ["easeInBack" /* inBack */]: easeInBack,
949
+ ["easeOutBack" /* outBack */]: easeOutBack,
950
+ ["easeInOutBack" /* inOutBack */]: easeInOutBack,
951
+ ["easeInElastic" /* inElastic */]: easeInElastic,
952
+ ["easeOutElastic" /* outElastic */]: easeOutElastic,
953
+ ["easeInOutElastic" /* inOutElastic */]: easeInOutElastic,
954
+ ["easeInBounce" /* inBounce */]: easeInBounce,
955
+ ["easeOutBounce" /* outBounce */]: easeOutBounce,
956
+ ["easeInOutBounce" /* inOutBounce */]: easeInOutBounce
957
+ };
958
+ function linear(x) {
959
+ return x;
960
+ }
961
+ function easeInSine(x) {
962
+ return 1 - Math.cos(x * Math.PI / 2);
963
+ }
964
+ function easeOutSine(x) {
965
+ return Math.sin(x * Math.PI / 2);
966
+ }
967
+ function easeInOutSine(x) {
968
+ return -(Math.cos(Math.PI * x) - 1) / 2;
969
+ }
970
+ function easeInQuad(x) {
971
+ return x * x;
972
+ }
973
+ function easeOutQuad(x) {
974
+ return 1 - (1 - x) * (1 - x);
975
+ }
976
+ function easeInOutQuad(x) {
977
+ return x < 0.5 ? 2 * x * x : 1 - Math.pow(-2 * x + 2, 2) / 2;
978
+ }
979
+ function easeInCubic(x) {
980
+ return x * x * x;
981
+ }
982
+ function easeOutCubic(x) {
983
+ return 1 - Math.pow(1 - x, 3);
984
+ }
985
+ function easeInOutCubic(x) {
986
+ return x < 0.5 ? 4 * x * x * x : 1 - Math.pow(-2 * x + 2, 3) / 2;
987
+ }
988
+ function easeInQuart(x) {
989
+ return x * x * x * x;
990
+ }
991
+ function easeOutQuart(x) {
992
+ return 1 - Math.pow(1 - x, 4);
993
+ }
994
+ function easeInOutQuart(x) {
995
+ return x < 0.5 ? 8 * x * x * x * x : 1 - Math.pow(-2 * x + 2, 4) / 2;
996
+ }
997
+ function easeInQuint(x) {
998
+ return x * x * x * x * x;
999
+ }
1000
+ function easeOutQuint(x) {
1001
+ return 1 - Math.pow(1 - x, 5);
1002
+ }
1003
+ function easeInOutQuint(x) {
1004
+ return x < 0.5 ? 16 * x * x * x * x * x : 1 - Math.pow(-2 * x + 2, 5) / 2;
1005
+ }
1006
+ function easeInExpo(x) {
1007
+ return x === 0 ? 0 : Math.pow(2, 10 * x - 10);
1008
+ }
1009
+ function easeOutExpo(x) {
1010
+ return x === 1 ? 1 : 1 - Math.pow(2, -10 * x);
1011
+ }
1012
+ function easeInOutExpo(x) {
1013
+ return x === 0 ? 0 : x === 1 ? 1 : x < 0.5 ? Math.pow(2, 20 * x - 10) / 2 : (2 - Math.pow(2, -20 * x + 10)) / 2;
1014
+ }
1015
+ function easeInCirc(x) {
1016
+ return 1 - Math.sqrt(1 - Math.pow(x, 2));
1017
+ }
1018
+ function easeOutCirc(x) {
1019
+ return Math.sqrt(1 - Math.pow(x - 1, 2));
1020
+ }
1021
+ function easeInOutCirc(x) {
1022
+ return x < 0.5 ? (1 - Math.sqrt(1 - Math.pow(2 * x, 2))) / 2 : (Math.sqrt(1 - Math.pow(-2 * x + 2, 2)) + 1) / 2;
1023
+ }
1024
+ function easeInBack(x) {
1025
+ const c1 = 1.70158;
1026
+ const c3 = c1 + 1;
1027
+ return c3 * x * x * x - c1 * x * x;
1028
+ }
1029
+ function easeOutBack(x) {
1030
+ const c1 = 1.70158;
1031
+ const c3 = c1 + 1;
1032
+ return 1 + c3 * Math.pow(x - 1, 3) + c1 * Math.pow(x - 1, 2);
1033
+ }
1034
+ function easeInOutBack(x) {
1035
+ const c1 = 1.70158;
1036
+ const c2 = c1 * 1.525;
1037
+ return x < 0.5 ? Math.pow(2 * x, 2) * ((c2 + 1) * 2 * x - c2) / 2 : (Math.pow(2 * x - 2, 2) * ((c2 + 1) * (x * 2 - 2) + c2) + 2) / 2;
1038
+ }
1039
+ function easeInElastic(x) {
1040
+ const c4 = 2 * Math.PI / 3;
1041
+ return x === 0 ? 0 : x === 1 ? 1 : -Math.pow(2, 10 * x - 10) * Math.sin((x * 10 - 10.75) * c4);
1042
+ }
1043
+ function easeOutElastic(x) {
1044
+ const c4 = 2 * Math.PI / 3;
1045
+ return x === 0 ? 0 : x === 1 ? 1 : Math.pow(2, -10 * x) * Math.sin((x * 10 - 0.75) * c4) + 1;
1046
+ }
1047
+ function easeInOutElastic(x) {
1048
+ const c5 = 2 * Math.PI / 4.5;
1049
+ return x === 0 ? 0 : x === 1 ? 1 : x < 0.5 ? -(Math.pow(2, 20 * x - 10) * Math.sin((20 * x - 11.125) * c5)) / 2 : Math.pow(2, -20 * x + 10) * Math.sin((20 * x - 11.125) * c5) / 2 + 1;
1050
+ }
1051
+ function easeInBounce(x) {
1052
+ return 1 - easeOutBounce(1 - x);
1053
+ }
1054
+ function easeOutBounce(x) {
1055
+ const n1 = 7.5625;
1056
+ const d1 = 2.75;
1057
+ if (x < 1 / d1) {
1058
+ return n1 * x * x;
1059
+ } else if (x < 2 / d1) {
1060
+ return n1 * (x -= 1.5 / d1) * x + 0.75;
1061
+ } else if (x < 2.5 / d1) {
1062
+ return n1 * (x -= 2.25 / d1) * x + 0.9375;
1063
+ } else {
1064
+ return n1 * (x -= 2.625 / d1) * x + 0.984375;
1065
+ }
1066
+ }
1067
+ function easeInOutBounce(x) {
1068
+ return x < 0.5 ? (1 - easeOutBounce(1 - 2 * x)) / 2 : (1 + easeOutBounce(2 * x - 1)) / 2;
1069
+ }
1070
+
1071
+ // src/canvas-captions/animate.ts
1072
+ var animate = (captionsSettings, progress, chunk, current) => {
1073
+ const easeFn = mapEaseToFn["easeInQuint" /* inQuint */];
1074
+ const slideOffset = 7;
1075
+ switch (captionsSettings.animation) {
1076
+ case "bounce":
1077
+ if (current) {
1078
+ current.text.offsetX(current.text.width() / 2);
1079
+ current.text.offsetY(current.text.height() / 2);
1080
+ current.text.x(current.text.x() + current.text.width() / 2);
1081
+ current.text.y(current.text.y() + current.text.height() / 2);
1082
+ current.text.scale({
1083
+ x: 1 + 0.3 * easeFn(1),
1084
+ y: 1 + 0.3 * easeFn(1)
1085
+ });
1086
+ const offsetModule = captionsSettings.style.font.fontSize * 0.3;
1087
+ const curreentChildIndex = current.text.parent?.children.indexOf(current.text) || 0;
1088
+ current.text.parent?.children.forEach((child, index) => {
1089
+ if (index !== curreentChildIndex) {
1090
+ const offset = index < curreentChildIndex ? -offsetModule : offsetModule;
1091
+ child.x(child.x() + offset * easeFn(1));
1092
+ }
1093
+ });
1094
+ }
1095
+ break;
1096
+ case "underline":
1097
+ if (current) {
1098
+ const underline = new Konva3.Line({
1099
+ points: [
1100
+ current.text.x(),
1101
+ current.text.height(),
1102
+ current.text.x() + current.text.width() * easeFn(1),
1103
+ current.text.height()
1104
+ ],
1105
+ lineCap: "round",
1106
+ stroke: current.caption.highlightColor ? current.caption.highlightColor : captionsSettings.style.aplifiedWordColor,
1107
+ strokeWidth: 5
1108
+ //opacity: easeFn(current.progress),
1109
+ });
1110
+ current.text.parent?.add(underline);
1111
+ }
1112
+ break;
1113
+ case "box":
1114
+ const xOffset = 12;
1115
+ const yOffset = 5;
1116
+ const cornerRadius = 6;
1117
+ const box = new Konva3.Rect({
1118
+ x: -xOffset,
1119
+ y: -yOffset - 2,
1120
+ width: chunk.width + 2 * xOffset,
1121
+ height: chunk.height + 2 * yOffset + 2,
1122
+ fill: captionsSettings.style.backgroundColor,
1123
+ cornerRadius
1124
+ });
1125
+ chunk.group.add(box);
1126
+ box.moveToBottom();
1127
+ break;
1128
+ case "box-word":
1129
+ if (current) {
1130
+ const underline = new Konva3.Line({
1131
+ id: "box-word",
1132
+ points: [
1133
+ current.text.x() + 6,
1134
+ current.text.height() - current.text.height() / 2,
1135
+ current.text.x() + (current.textTrim.width() - 6) * easeFn(1),
1136
+ current.text.height() - current.text.height() / 2
1137
+ ],
1138
+ lineCap: "round",
1139
+ stroke: captionsSettings.style.backgroundColor,
1140
+ strokeWidth: current.textTrim.height() + 2
1141
+ });
1142
+ underline.zIndex(4);
1143
+ current.text?.parent?.add(underline);
1144
+ }
1145
+ break;
1146
+ case "pop":
1147
+ const scaleFactor = 0.5 + 0.5 * mapEaseToFn["easeOutBack" /* outBack */](progress);
1148
+ chunk.group.scale({
1149
+ x: scaleFactor,
1150
+ y: scaleFactor
1151
+ });
1152
+ break;
1153
+ case "scale":
1154
+ const scaleFactor2 = 0.4 + 0.6 * mapEaseToFn["linear" /* linear */](progress);
1155
+ chunk.group.scale({
1156
+ x: scaleFactor2,
1157
+ y: scaleFactor2
1158
+ });
1159
+ break;
1160
+ case "slide-down":
1161
+ chunk.group.y(
1162
+ chunk.group.y() - slideOffset + slideOffset * mapEaseToFn["linear" /* linear */](progress)
1163
+ );
1164
+ break;
1165
+ case "slide-up":
1166
+ chunk.group.y(
1167
+ chunk.group.y() + slideOffset - slideOffset * mapEaseToFn["linear" /* linear */](progress)
1168
+ );
1169
+ break;
1170
+ case "slide-left":
1171
+ chunk.group.x(
1172
+ chunk.group.x() + slideOffset - slideOffset * mapEaseToFn["linear" /* linear */](progress)
1173
+ );
1174
+ break;
1175
+ default:
1176
+ break;
1177
+ }
1178
+ };
1179
+
1180
+ // src/canvas-captions/utils.ts
1181
+ var SOCIAL_NETWORK_OFFSET = 70;
1182
+ var splitCaptionsBytotalWordsToDisplay = (captions, totalWordsToDisplay, linesPerPage) => {
1183
+ const result = [];
1184
+ let currentCaptionIndex = 0;
1185
+ let currentSymbolsLength = 0;
1186
+ let currentBlock = [];
1187
+ let currentLine = 1;
1188
+ const maxWordGapSec = 1;
1189
+ const isValidBlockGap = (currentBlock2, caption) => {
1190
+ return true;
1191
+ return currentBlock2.length > 0 && caption.startTime - currentBlock2[currentBlock2.length - 1].endTime < maxWordGapSec || currentBlock2.length === 0;
1192
+ };
1193
+ while (currentCaptionIndex < captions.length) {
1194
+ const caption = captions[currentCaptionIndex];
1195
+ if (currentSymbolsLength + caption.word.length + 1 <= totalWordsToDisplay && isValidBlockGap(currentBlock, caption)) {
1196
+ currentBlock.push(caption);
1197
+ currentSymbolsLength += caption.word.length + 1;
1198
+ currentCaptionIndex++;
1199
+ } else if (linesPerPage > currentLine && isValidBlockGap(currentBlock, caption)) {
1200
+ currentBlock.push(caption);
1201
+ currentCaptionIndex++;
1202
+ currentSymbolsLength = caption.word.length;
1203
+ currentLine++;
1204
+ } else {
1205
+ if (currentBlock.length > 0) {
1206
+ result.push(currentBlock);
1207
+ }
1208
+ currentBlock = [];
1209
+ currentBlock.push(caption);
1210
+ currentSymbolsLength = caption.word.length;
1211
+ currentCaptionIndex++;
1212
+ currentLine = 1;
1213
+ }
1214
+ }
1215
+ if (currentBlock.length > 0) {
1216
+ result.push(currentBlock);
1217
+ }
1218
+ return result;
1219
+ };
1220
+ var fontWeightToFontStyle = (fontWeight, isItalic) => {
1221
+ return (isItalic ? "italic " : "") + fontWeightToNumber(fontWeight);
1222
+ };
1223
+ var fontWeightToNumber = (fontWeight) => {
1224
+ switch (fontWeight) {
1225
+ case "thin":
1226
+ return 100;
1227
+ case "light":
1228
+ return 300;
1229
+ case "regular":
1230
+ return 400;
1231
+ case "medium":
1232
+ return 500;
1233
+ case "bold":
1234
+ return 700;
1235
+ case "black":
1236
+ return 900;
1237
+ default:
1238
+ return 400;
1239
+ }
1240
+ };
1241
+ var getFillColor = (caption, isCurrentCaption, isPastCaption, captionsSettings) => {
1242
+ if (captionsSettings.animation === "underline") {
1243
+ return captionsSettings.style.font.fontColor;
1244
+ }
1245
+ if (captionsSettings.animation === "box" && (isCurrentCaption || isPastCaption)) {
1246
+ return caption.highlightColor || captionsSettings.style.aplifiedWordColor;
1247
+ }
1248
+ if (isCurrentCaption) {
1249
+ return caption.highlightColor || captionsSettings.style.aplifiedWordColor;
1250
+ }
1251
+ return captionsSettings.style.font.fontColor;
1252
+ };
1253
+ var getCaptionsGroupY = (groupHeight, canvasWidth, canvasHeight, position, offset, toCoef) => {
1254
+ const aspectRatio = canvasWidth / canvasHeight;
1255
+ const socialOffset = aspectRatio <= 1 ? SOCIAL_NETWORK_OFFSET * toCoef : 0;
1256
+ switch (position) {
1257
+ case "top":
1258
+ return (offset + 20) * toCoef + groupHeight / 2;
1259
+ case "middle":
1260
+ return canvasHeight / 2 + offset * toCoef;
1261
+ default:
1262
+ return canvasHeight - groupHeight / 2 - (20 + offset) * toCoef - socialOffset;
1263
+ }
1264
+ };
1265
+ var alignLinesInGroup = (group, maxGroupWidth, xOffset) => {
1266
+ return group.children.map((line) => {
1267
+ const lineWords = line.children;
1268
+ const lineWordsWidth = lineWords.reduce(
1269
+ (acc, word, currentIndex) => acc + word.width() + (currentIndex > 0 ? xOffset : 0),
1270
+ 0
1271
+ );
1272
+ const lineHeight = lineWords.reduce(
1273
+ (acc, word) => Math.max(acc, word.height()),
1274
+ 0
1275
+ );
1276
+ const lineX = (maxGroupWidth - lineWordsWidth) / 2;
1277
+ line.x(lineX);
1278
+ return {
1279
+ line,
1280
+ width: lineWordsWidth,
1281
+ height: lineHeight
1282
+ };
1283
+ });
1284
+ };
1285
+ var alphabetEN = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1286
+ var alphabetRU = "\u0410\u0411\u0412\u0413\u0414\u0415\u0401\u0416\u0417\u0418\u0419\u041A\u041B\u041C\u041D\u041E\u041F\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042A\u042B\u042C\u042D\u042E\u042F\u0430\u0431\u0432\u0433\u0434\u0435\u0451\u0436\u0437\u0438\u0439\u043A\u043B\u043C\u043D\u043E\u043F\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044A\u044B\u044C\u044D\u044E\u044F";
1287
+ var alphabetArabic = "\u0627 \u0628 \u062A \u062B \u062C \u062D \u062E \u062F \u0630 \u0631 \u0632 \u0633 \u0634 \u0635 \u0636 \u0637 \u0638 \u0639 \u063A \u0641 \u0642 \u0643 \u0644 \u0645 \u0646 \u0647 \u0648 \u064A";
1288
+ var getAverageSymbolsInLine = (targetWidth, textWidth, alphabetLength, captionsSettings) => {
1289
+ const offset = captionsSettings.animation === "box" || captionsSettings.animation === "bounce" ? 4 : 2;
1290
+ const avg = Math.round(targetWidth / (textWidth / alphabetLength)) - offset;
1291
+ return avg > 0 ? avg : 0;
1292
+ };
1293
+ var getAlphabet = (kind) => {
1294
+ switch (kind) {
1295
+ case "cyrillic" /* Cyrillic */:
1296
+ return alphabetRU;
1297
+ case "latin" /* Latin */:
1298
+ return alphabetEN;
1299
+ case "arabic" /* Arabic */:
1300
+ return alphabetArabic;
1301
+ default:
1302
+ return "latin" /* Latin */;
1303
+ }
1304
+ };
1305
+ var detectAlphabetKind = (text) => {
1306
+ if (isCyrillic(text)) {
1307
+ return "cyrillic" /* Cyrillic */;
1308
+ }
1309
+ if (isLatin(text)) {
1310
+ return "latin" /* Latin */;
1311
+ }
1312
+ if (isArabic(text)) {
1313
+ return "arabic" /* Arabic */;
1314
+ }
1315
+ return "latin" /* Latin */;
1316
+ };
1317
+ function isCyrillic(text) {
1318
+ return /[\u0400-\u04FF]/.test(text);
1319
+ }
1320
+ function isLatin(text) {
1321
+ return /[A-Za-z]/.test(text);
1322
+ }
1323
+ function isArabic(text) {
1324
+ return /[\u0600-\u06FF]/.test(text);
1325
+ }
1326
+
1327
+ // src/canvas-captions/index.ts
1328
+ import objectHash from "object-hash";
1329
+ var memo = {};
1330
+ var drawDebugBoundingBox = (parent, width, height, stroke, dash = [6, 4]) => {
1331
+ const rect = new Konva4.Rect({
1332
+ x: 0,
1333
+ y: 0,
1334
+ width,
1335
+ height,
1336
+ stroke,
1337
+ strokeWidth: 4,
1338
+ dash,
1339
+ listening: false
1340
+ });
1341
+ parent.add(rect);
1342
+ rect.moveToTop();
1343
+ };
1344
+ var renderFrame = (captionsSettings, layoutSettings, captions, currentTime, targetSize, layer, toCoef, debug) => {
1345
+ const [width, height] = targetSize;
1346
+ const targetFontSize = captionsSettings.style.font.fontSize * (toCoef ?? 1);
1347
+ const showDebugBoundingBoxes = Boolean(debug);
1348
+ let totalSymbolInLine = 0;
1349
+ const hash = objectHash({ captionsSettings });
1350
+ if (memo[hash]) {
1351
+ totalSymbolInLine = memo[hash];
1352
+ } else {
1353
+ const textForDetectAlphabet = captions.slice(0, 10).map((x) => x.word).join("");
1354
+ const alphabetKind = detectAlphabetKind(textForDetectAlphabet);
1355
+ const alphabet = getAlphabet(alphabetKind);
1356
+ const alphabetText = new Konva4.Text({
1357
+ text: captionsSettings.style.font.fontCapitalize ? alphabet.toUpperCase() : alphabet,
1358
+ x: 0,
1359
+ y: 0,
1360
+ fontSize: targetFontSize,
1361
+ fontFamily: captionsSettings.style.font.fontFamily,
1362
+ fontStyle: fontWeightToFontStyle(
1363
+ captionsSettings.style.font.fontWeight,
1364
+ captionsSettings.style.font.italic
1365
+ ),
1366
+ stroke: captionsSettings.style.font.fontStrokeColor,
1367
+ strokeWidth: captionsSettings.style.font.fontStrokeWidth ? captionsSettings.style.font.fontStrokeWidth / 10 : 0,
1368
+ textDecoration: captionsSettings.style.font.underline ? "underline" : "",
1369
+ fill: "black"
1370
+ });
1371
+ alphabetText.fillAfterStrokeEnabled(true);
1372
+ totalSymbolInLine = getAverageSymbolsInLine(
1373
+ layer.getWidth(),
1374
+ alphabetText.getWidth(),
1375
+ alphabet.length,
1376
+ captionsSettings
1377
+ );
1378
+ memo[hash] = totalSymbolInLine;
1379
+ }
1380
+ const chunks = splitCaptionsBytotalWordsToDisplay(
1381
+ captions,
1382
+ totalSymbolInLine,
1383
+ captionsSettings.linesPerPage
1384
+ );
1385
+ const currentChunk = chunks.find(
1386
+ (chunk) => chunk[0].startTime <= currentTime && chunk[chunk.length - 1].endTime >= currentTime
1387
+ );
1388
+ const xOffset = targetFontSize * 0.1;
1389
+ const wordSpacing = xOffset + (captionsSettings.animation === "bounce" ? 8 : 0) + (captionsSettings.style.font.fontCapitalize ? 2 : 0);
1390
+ const yOffset = captionsSettings.lineSpacing ?? 4;
1391
+ if (currentChunk) {
1392
+ const group = new Konva4.Group({
1393
+ drawBorder: true
1394
+ });
1395
+ let x = 0;
1396
+ let y = 0;
1397
+ let maxGroupWidth = 0;
1398
+ let groupWidth = 0;
1399
+ let groupHeight = 0;
1400
+ let line = new Konva4.Group({
1401
+ drawBorder: true
1402
+ });
1403
+ let currentSymbolsLength = 0;
1404
+ let current = void 0;
1405
+ currentChunk.forEach((caption, index) => {
1406
+ const isCurrentCaption = caption.startTime <= currentTime && caption.endTime >= currentTime;
1407
+ const isPastCaption = caption.endTime < currentTime;
1408
+ const fillColor = getFillColor(
1409
+ caption,
1410
+ isCurrentCaption,
1411
+ isPastCaption,
1412
+ captionsSettings
1413
+ );
1414
+ const isLastWordInLine = currentSymbolsLength + caption.word.length + (index < currentChunk.length - 1 ? currentChunk[index + 1].word.length : 0) > totalSymbolInLine || currentChunk.length - 1 === index;
1415
+ let word = captionsSettings.style.font.fontCapitalize ? caption.word.toUpperCase() : caption.word;
1416
+ word = isLastWordInLine ? word.trim() : word;
1417
+ const createKonvaText = (word2, x2, targetFontSize2, captionsSettings2, fillColor2, trim) => {
1418
+ return new Konva4.Text({
1419
+ text: trim ? word2?.trim() : word2,
1420
+ x: x2,
1421
+ y: 0,
1422
+ drawBorder: true,
1423
+ fontSize: targetFontSize2,
1424
+ fontFamily: captionsSettings2.style.font.fontFamily,
1425
+ fontStyle: fontWeightToFontStyle(
1426
+ captionsSettings2.style.font.fontWeight,
1427
+ captionsSettings2.style.font.italic
1428
+ ),
1429
+ stroke: captionsSettings2.style.font.fontStrokeColor,
1430
+ strokeWidth: captionsSettings2.style.font.fontStrokeWidth ? captionsSettings2.style.font.fontStrokeWidth / 10 : 0,
1431
+ textDecoration: captionsSettings2.style.font.underline ? "underline" : "",
1432
+ fill: fillColor2,
1433
+ shadowColor: captionsSettings2.style.font.shadow?.fontShadowColor,
1434
+ shadowBlur: captionsSettings2.style.font.shadow?.fontShadowBlur ?? 0,
1435
+ shadowOffset: captionsSettings2.style.font.shadow ? {
1436
+ x: captionsSettings2.style.font.shadow?.fontShadowOffsetX ?? 0,
1437
+ y: captionsSettings2.style.font.shadow?.fontShadowOffsetY ?? 0
1438
+ } : void 0,
1439
+ listening: false,
1440
+ perfectDrawEnabled: false
1441
+ });
1442
+ };
1443
+ const text = createKonvaText(
1444
+ word,
1445
+ x,
1446
+ targetFontSize,
1447
+ captionsSettings,
1448
+ fillColor,
1449
+ false
1450
+ );
1451
+ text.fillAfterStrokeEnabled(true);
1452
+ const textTrim = captionsSettings.animation === "box-word" ? createKonvaText(
1453
+ word,
1454
+ x,
1455
+ targetFontSize,
1456
+ captionsSettings,
1457
+ fillColor,
1458
+ true
1459
+ ) : null;
1460
+ if (isCurrentCaption) {
1461
+ current = {
1462
+ caption,
1463
+ text,
1464
+ progress: (currentTime - caption.startTime) / (caption.endTime - caption.startTime),
1465
+ textTrim
1466
+ //progress: Math.min(1, (currentTime - caption.start_time) / 0.5),
1467
+ };
1468
+ }
1469
+ x += text.width() + wordSpacing;
1470
+ groupWidth += text.width() + (isLastWordInLine ? 0 : wordSpacing);
1471
+ line.y(y);
1472
+ line.add(text);
1473
+ if (isLastWordInLine) {
1474
+ group.add(line);
1475
+ x = 0;
1476
+ const isLastLine = index + 1 === currentChunk.length;
1477
+ y += text.height() + yOffset;
1478
+ groupHeight += text.height() + (isLastLine ? 0 : yOffset);
1479
+ if (groupWidth > maxGroupWidth) {
1480
+ maxGroupWidth = groupWidth;
1481
+ }
1482
+ groupWidth = 0;
1483
+ line = new Konva4.Group({
1484
+ drawBorder: true
1485
+ });
1486
+ currentSymbolsLength = 0;
1487
+ } else {
1488
+ currentSymbolsLength += caption.word.length + 1;
1489
+ }
1490
+ });
1491
+ const lineMetrics = alignLinesInGroup(group, maxGroupWidth, wordSpacing);
1492
+ const progress = Math.min(
1493
+ 1,
1494
+ (currentTime - currentChunk[0].startTime) / 0.2
1495
+ );
1496
+ group.offsetX(maxGroupWidth / 2);
1497
+ group.offsetY(groupHeight / 2);
1498
+ group.x(width / 2);
1499
+ group.y(
1500
+ getCaptionsGroupY(
1501
+ groupHeight,
1502
+ width,
1503
+ height,
1504
+ captionsSettings.position,
1505
+ captionsSettings.positionTopOffset ?? 0,
1506
+ toCoef ?? 1
1507
+ )
1508
+ );
1509
+ animate(
1510
+ captionsSettings,
1511
+ progress,
1512
+ {
1513
+ group,
1514
+ width: maxGroupWidth,
1515
+ height: groupHeight
1516
+ },
1517
+ current
1518
+ );
1519
+ if (showDebugBoundingBoxes) {
1520
+ drawDebugBoundingBox(group, maxGroupWidth, groupHeight, "#ff4081");
1521
+ lineMetrics.forEach(({ line: line2, width: width2, height: height2 }) => {
1522
+ drawDebugBoundingBox(line2, width2, height2, "#2196f3", [4, 3]);
1523
+ });
1524
+ }
1525
+ if (current?.text && captionsSettings.animation === "box-word") {
1526
+ current.text?.zIndex(10);
1527
+ }
1528
+ layer.add(group);
1529
+ }
1530
+ layer.draw();
1531
+ };
1532
+
1533
+ // src/captions/Captions.ts
1534
+ var Captions = class {
1535
+ /**
1536
+ * Create a controller bound to the provided video element and preset.
1537
+ *
1538
+ * @param options - Complete configuration for the controller.
1539
+ */
1540
+ constructor(options) {
1541
+ this.enabled = false;
1542
+ this.ownsContainer = false;
1543
+ this.stage = null;
1544
+ this.layer = null;
1545
+ this.animationFrameId = null;
1546
+ this.videoWidth = 0;
1547
+ this.videoHeight = 0;
1548
+ this.handleResize = () => {
1549
+ this.syncStageDimensions();
1550
+ };
1551
+ this.handleMetadata = () => {
1552
+ this.syncStageDimensions();
1553
+ };
1554
+ this.animationLoop = () => {
1555
+ this.updateFrame();
1556
+ this.animationFrameId = requestAnimationFrame(this.animationLoop);
1557
+ };
1558
+ if (!options.video) {
1559
+ throw new Error("captionsjs requires a video element");
1560
+ }
1561
+ this.video = options.video;
1562
+ this.providedContainer = options.container;
1563
+ this.presetState = options.preset;
1564
+ this.captionsState = options.captions ?? null;
1565
+ this.debug = options.debug ?? false;
1566
+ if (options.autoEnable ?? true) {
1567
+ this.enable();
1568
+ }
1569
+ }
1570
+ /**
1571
+ * Mount caption overlays onto the configured video if they are not active yet.
1572
+ */
1573
+ enable() {
1574
+ if (this.enabled) {
1575
+ return;
1576
+ }
1577
+ this.containerElement = this.providedContainer ?? this.createOverlay();
1578
+ this.ownsContainer = !this.providedContainer;
1579
+ this.stage = new Konva5.Stage({
1580
+ container: this.containerElement
1581
+ });
1582
+ this.layer = new Konva5.Layer({ listening: false });
1583
+ this.stage.add(this.layer);
1584
+ this.videoWidth = this.video.videoWidth;
1585
+ this.videoHeight = this.video.videoHeight;
1586
+ window.addEventListener("resize", this.handleResize);
1587
+ if (typeof ResizeObserver !== "undefined") {
1588
+ this.resizeObserver = new ResizeObserver(this.handleResize);
1589
+ this.resizeObserver.observe(this.video);
1590
+ }
1591
+ this.video.addEventListener("loadedmetadata", this.handleMetadata);
1592
+ this.syncStageDimensions();
1593
+ this.animationFrameId = requestAnimationFrame(this.animationLoop);
1594
+ this.enabled = true;
1595
+ void this.refreshFrame();
1596
+ }
1597
+ /**
1598
+ * Tear down overlays, observers and animation loops to free resources.
1599
+ */
1600
+ disable() {
1601
+ if (!this.enabled) {
1602
+ return;
1603
+ }
1604
+ if (this.animationFrameId !== null) {
1605
+ cancelAnimationFrame(this.animationFrameId);
1606
+ this.animationFrameId = null;
1607
+ }
1608
+ window.removeEventListener("resize", this.handleResize);
1609
+ this.video.removeEventListener("loadedmetadata", this.handleMetadata);
1610
+ this.resizeObserver?.disconnect();
1611
+ this.resizeObserver = void 0;
1612
+ if (this.ownsContainer && this.containerElement?.parentElement) {
1613
+ this.containerElement.parentElement.removeChild(this.containerElement);
1614
+ }
1615
+ this.stage?.destroy();
1616
+ this.stage = null;
1617
+ this.layer = null;
1618
+ this.containerElement = void 0;
1619
+ this.ownsContainer = false;
1620
+ this.enabled = false;
1621
+ }
1622
+ /**
1623
+ * Alias for {@link Captions.disable | disable()} to match typical imperative controller APIs.
1624
+ */
1625
+ destroy() {
1626
+ this.disable();
1627
+ }
1628
+ /**
1629
+ * Swap the active preset and re-render with updated typography/colors.
1630
+ *
1631
+ * @param nextPreset - Preset that becomes the new render baseline.
1632
+ */
1633
+ preset(nextPreset) {
1634
+ this.presetState = nextPreset;
1635
+ if (!this.enabled) {
1636
+ return;
1637
+ }
1638
+ void this.refreshFrame();
1639
+ }
1640
+ /**
1641
+ * Replace the current caption track and repaint without reloading fonts.
1642
+ *
1643
+ * @param nextCaptions - Timed words that should drive the overlay.
1644
+ */
1645
+ captions(nextCaptions) {
1646
+ this.captionsState = nextCaptions;
1647
+ if (!this.enabled) {
1648
+ return;
1649
+ }
1650
+ void this.refreshFrame(false);
1651
+ }
1652
+ /**
1653
+ * Whether the Konva overlay is currently attached to the video element.
1654
+ *
1655
+ * @returns `true` when the overlay is mounted on top of the video.
1656
+ */
1657
+ isEnabled() {
1658
+ return this.enabled;
1659
+ }
1660
+ async refreshFrame(loadFont = true) {
1661
+ if (!this.layer || !this.stage) {
1662
+ return;
1663
+ }
1664
+ if (loadFont) {
1665
+ await this.loadFontForCurrentPreset();
1666
+ }
1667
+ this.updateFrame();
1668
+ }
1669
+ async loadFontForCurrentPreset() {
1670
+ const fontFamily = this.presetState.captionsSettings.style.font.fontFamily;
1671
+ await loadGoogleFont2(fontFamily);
1672
+ }
1673
+ updateFrame() {
1674
+ if (!this.layer || !this.stage) {
1675
+ return;
1676
+ }
1677
+ if (!this.videoWidth || !this.videoHeight) {
1678
+ return;
1679
+ }
1680
+ this.layer.destroyChildren();
1681
+ renderFrame(
1682
+ this.presetState.captionsSettings,
1683
+ void 0,
1684
+ this.captionsState || [],
1685
+ this.video.currentTime,
1686
+ [this.videoWidth, this.videoHeight],
1687
+ this.layer,
1688
+ 2,
1689
+ this.debug
1690
+ );
1691
+ }
1692
+ syncStageDimensions() {
1693
+ if (!this.stage) {
1694
+ return;
1695
+ }
1696
+ const currentVideoWidth = this.video.videoWidth || this.videoWidth;
1697
+ const currentVideoHeight = this.video.videoHeight || this.videoHeight;
1698
+ if (!currentVideoWidth || !currentVideoHeight) {
1699
+ return;
1700
+ }
1701
+ this.videoWidth = currentVideoWidth;
1702
+ this.videoHeight = currentVideoHeight;
1703
+ this.stage.width(this.videoWidth);
1704
+ this.stage.height(this.videoHeight);
1705
+ const rect = this.video.getBoundingClientRect();
1706
+ const stageContainer = this.stage.container();
1707
+ const displayWidth = rect.width || stageContainer.offsetWidth || this.videoWidth;
1708
+ const displayHeight = rect.height || stageContainer.offsetHeight || this.videoHeight;
1709
+ if (displayWidth && displayHeight) {
1710
+ stageContainer.style.width = `${displayWidth}px`;
1711
+ stageContainer.style.height = `${displayHeight}px`;
1712
+ const scaleX = displayWidth / this.videoWidth;
1713
+ const scaleY = displayHeight / this.videoHeight;
1714
+ this.stage.scale({ x: scaleX, y: scaleY });
1715
+ } else {
1716
+ this.stage.scale({ x: 1, y: 1 });
1717
+ stageContainer.style.width = `${this.videoWidth}px`;
1718
+ stageContainer.style.height = `${this.videoHeight}px`;
1719
+ }
1720
+ this.stage.batchDraw();
1721
+ }
1722
+ createOverlay() {
1723
+ const container = document.createElement("div");
1724
+ container.style.position = "absolute";
1725
+ container.style.top = "0";
1726
+ container.style.left = "0";
1727
+ container.style.width = "100%";
1728
+ container.style.height = "100%";
1729
+ container.style.pointerEvents = "none";
1730
+ const parent = this.video.parentElement;
1731
+ parent?.appendChild(container);
1732
+ if (parent) {
1733
+ parent.style.position = "relative";
1734
+ }
1735
+ return container;
1736
+ }
1737
+ };
1738
+ function captionsjs(options) {
1739
+ return new Captions(options);
1740
+ }
1741
+
1
1742
  // src/index.ts
2
1743
  function renderCaptions(ctx, text) {
3
- ctx.font = "24px sans-serif";
4
- ctx.fillStyle = "#17c499ff";
1744
+ ctx.font = "48px sans-serif";
1745
+ ctx.fillStyle = "red";
5
1746
  ctx.fillText(text, 150, 50);
6
1747
  return true;
7
1748
  }
1749
+ var index_default = captionsjs;
8
1750
  export {
9
- renderCaptions
1751
+ Captions,
1752
+ captionsjs,
1753
+ index_default as default,
1754
+ googleFontsList,
1755
+ renderCaptions,
1756
+ renderString,
1757
+ stylePresets
10
1758
  };