@twick/studio 0.15.27 → 0.15.28

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
@@ -3,12 +3,13 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
4
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
5
5
  import { forwardRef, createElement, useState, useEffect, useRef, useCallback, createContext, useContext, useMemo } from "react";
6
- import { useTimelineContext, TrackElement, Track, ImageElement, AudioElement, VideoElement, TextElement, TIMELINE_ELEMENT_TYPE, EffectElement, computeCaptionGeometry, CAPTION_STYLE, CaptionElement, TRACK_TYPES, LineElement, ArrowElement, RectElement, CircleElement, ElementTextEffect, ElementAnimation, CAPTION_STYLE_OPTIONS, exportChaptersAsYouTube, exportChaptersAsJSON, getCaptionLanguages, exportCaptionsAsSRT, exportCaptionsAsVTT, PLAYER_STATE } from "@twick/timeline";
7
- import { AudioElement as AudioElement2, CAPTION_COLOR, CAPTION_FONT, CAPTION_STYLE as CAPTION_STYLE2, CAPTION_STYLE_OPTIONS as CAPTION_STYLE_OPTIONS2, CaptionElement as CaptionElement2, CircleElement as CircleElement2, ElementAdder, ElementAnimation as ElementAnimation2, ElementCloner, ElementDeserializer, ElementFrameEffect, ElementRemover, ElementSerializer, ElementSplitter, ElementTextEffect as ElementTextEffect2, ElementUpdater, ElementValidator, INITIAL_TIMELINE_DATA, IconElement, ImageElement as ImageElement2, PROCESS_STATE, RectElement as RectElement2, TIMELINE_ACTION, TIMELINE_ELEMENT_TYPE as TIMELINE_ELEMENT_TYPE2, TextElement as TextElement2, TimelineEditor, TimelineProvider, Track as Track2, TrackElement as TrackElement2, VideoElement as VideoElement2, WORDS_PER_PHRASE, generateShortUuid, getCurrentElements, getTotalDuration, isElementId, isTrackId, useTimelineContext as useTimelineContext2 } from "@twick/timeline";
6
+ import { useTimelineContext, TrackElement, Track, ImageElement, AudioElement, VideoElement, TextElement, EmojiElement, TIMELINE_ELEMENT_TYPE, EffectElement, computeCaptionGeometry, CAPTION_STYLE, CaptionElement, TRACK_TYPES, LineElement, ArrowElement, RectElement, CircleElement, ElementTextEffect, ElementAnimation, CAPTION_STYLE_OPTIONS, exportChaptersAsYouTube, exportChaptersAsJSON, getCaptionLanguages, exportCaptionsAsSRT, exportCaptionsAsVTT, PLAYER_STATE } from "@twick/timeline";
7
+ import { AudioElement as AudioElement2, CAPTION_COLOR, CAPTION_FONT, CAPTION_STYLE as CAPTION_STYLE2, CAPTION_STYLE_OPTIONS as CAPTION_STYLE_OPTIONS2, CaptionElement as CaptionElement2, CircleElement as CircleElement2, ElementAdder, ElementAnimation as ElementAnimation2, ElementCloner, ElementDeserializer, ElementFrameEffect, ElementRemover, ElementSerializer, ElementSplitter, ElementTextEffect as ElementTextEffect2, ElementUpdater, ElementValidator, EmojiElement as EmojiElement2, INITIAL_TIMELINE_DATA, IconElement, ImageElement as ImageElement2, PROCESS_STATE, RectElement as RectElement2, TIMELINE_ACTION, TIMELINE_ELEMENT_TYPE as TIMELINE_ELEMENT_TYPE2, TextElement as TextElement2, TimelineEditor, TimelineProvider, Track as Track2, TrackElement as TrackElement2, VideoElement as VideoElement2, WORDS_PER_PHRASE, generateShortUuid, getCurrentElements, getTotalDuration, isElementId, isTrackId, useTimelineContext as useTimelineContext2 } from "@twick/timeline";
8
8
  import VideoEditor, { useEditorManager, BrowserMediaManager, TIMELINE_DROP_MEDIA_TYPE, throttle, AVAILABLE_TEXT_FONTS, TEXT_EFFECTS, ANIMATIONS } from "@twick/video-editor";
9
9
  import { ANIMATIONS as ANIMATIONS2, BaseMediaManager, BrowserMediaManager as BrowserMediaManager2, PlayerControls, TEXT_EFFECTS as TEXT_EFFECTS2, TimelineManager, default as default2, animationGifs, getAnimationGif, setElementColors, useEditorManager as useEditorManager2, usePlayerControl, useTimelineControl } from "@twick/video-editor";
10
10
  import { useLivePlayerContext } from "@twick/live-player";
11
11
  import { LivePlayer, LivePlayerProvider, PLAYER_STATE as PLAYER_STATE2, generateId, getBaseProject, useLivePlayerContext as useLivePlayerContext2 } from "@twick/live-player";
12
+ import { COLOR_FILTERS, hasAudio, saveAsFile, loadFile } from "@twick/media-utils";
12
13
  /**
13
14
  * @license lucide-react v0.511.0 - ISC
14
15
  *
@@ -114,103 +115,103 @@ const createLucideIcon = (iconName, iconNode) => {
114
115
  * This source code is licensed under the ISC license.
115
116
  * See the LICENSE file in the root directory of this source tree.
116
117
  */
117
- const __iconNode$A = [
118
+ const __iconNode$C = [
118
119
  ["path", { d: "M17 12H7", key: "16if0g" }],
119
120
  ["path", { d: "M19 18H5", key: "18s9l3" }],
120
121
  ["path", { d: "M21 6H3", key: "1jwq7v" }]
121
122
  ];
122
- const AlignCenter = createLucideIcon("align-center", __iconNode$A);
123
+ const AlignCenter = createLucideIcon("align-center", __iconNode$C);
123
124
  /**
124
125
  * @license lucide-react v0.511.0 - ISC
125
126
  *
126
127
  * This source code is licensed under the ISC license.
127
128
  * See the LICENSE file in the root directory of this source tree.
128
129
  */
129
- const __iconNode$z = [
130
+ const __iconNode$B = [
130
131
  ["path", { d: "M15 12H3", key: "6jk70r" }],
131
132
  ["path", { d: "M17 18H3", key: "1amg6g" }],
132
133
  ["path", { d: "M21 6H3", key: "1jwq7v" }]
133
134
  ];
134
- const AlignLeft = createLucideIcon("align-left", __iconNode$z);
135
+ const AlignLeft = createLucideIcon("align-left", __iconNode$B);
135
136
  /**
136
137
  * @license lucide-react v0.511.0 - ISC
137
138
  *
138
139
  * This source code is licensed under the ISC license.
139
140
  * See the LICENSE file in the root directory of this source tree.
140
141
  */
141
- const __iconNode$y = [
142
+ const __iconNode$A = [
142
143
  ["path", { d: "M21 12H9", key: "dn1m92" }],
143
144
  ["path", { d: "M21 18H7", key: "1ygte8" }],
144
145
  ["path", { d: "M21 6H3", key: "1jwq7v" }]
145
146
  ];
146
- const AlignRight = createLucideIcon("align-right", __iconNode$y);
147
+ const AlignRight = createLucideIcon("align-right", __iconNode$A);
147
148
  /**
148
149
  * @license lucide-react v0.511.0 - ISC
149
150
  *
150
151
  * This source code is licensed under the ISC license.
151
152
  * See the LICENSE file in the root directory of this source tree.
152
153
  */
153
- const __iconNode$x = [
154
+ const __iconNode$z = [
154
155
  [
155
156
  "path",
156
157
  { d: "M6 12h9a4 4 0 0 1 0 8H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h7a4 4 0 0 1 0 8", key: "mg9rjx" }
157
158
  ]
158
159
  ];
159
- const Bold = createLucideIcon("bold", __iconNode$x);
160
+ const Bold = createLucideIcon("bold", __iconNode$z);
160
161
  /**
161
162
  * @license lucide-react v0.511.0 - ISC
162
163
  *
163
164
  * This source code is licensed under the ISC license.
164
165
  * See the LICENSE file in the root directory of this source tree.
165
166
  */
166
- const __iconNode$w = [["path", { d: "m6 9 6 6 6-6", key: "qrunsl" }]];
167
- const ChevronDown = createLucideIcon("chevron-down", __iconNode$w);
167
+ const __iconNode$y = [["path", { d: "m6 9 6 6 6-6", key: "qrunsl" }]];
168
+ const ChevronDown = createLucideIcon("chevron-down", __iconNode$y);
168
169
  /**
169
170
  * @license lucide-react v0.511.0 - ISC
170
171
  *
171
172
  * This source code is licensed under the ISC license.
172
173
  * See the LICENSE file in the root directory of this source tree.
173
174
  */
174
- const __iconNode$v = [["path", { d: "m9 18 6-6-6-6", key: "mthhwq" }]];
175
- const ChevronRight = createLucideIcon("chevron-right", __iconNode$v);
175
+ const __iconNode$x = [["path", { d: "m9 18 6-6-6-6", key: "mthhwq" }]];
176
+ const ChevronRight = createLucideIcon("chevron-right", __iconNode$x);
176
177
  /**
177
178
  * @license lucide-react v0.511.0 - ISC
178
179
  *
179
180
  * This source code is licensed under the ISC license.
180
181
  * See the LICENSE file in the root directory of this source tree.
181
182
  */
182
- const __iconNode$u = [
183
+ const __iconNode$w = [
183
184
  ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
184
185
  ["path", { d: "m9 12 2 2 4-4", key: "dzmm74" }]
185
186
  ];
186
- const CircleCheck = createLucideIcon("circle-check", __iconNode$u);
187
+ const CircleCheck = createLucideIcon("circle-check", __iconNode$w);
187
188
  /**
188
189
  * @license lucide-react v0.511.0 - ISC
189
190
  *
190
191
  * This source code is licensed under the ISC license.
191
192
  * See the LICENSE file in the root directory of this source tree.
192
193
  */
193
- const __iconNode$t = [
194
+ const __iconNode$v = [
194
195
  ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
195
196
  ["path", { d: "m15 9-6 6", key: "1uzhvr" }],
196
197
  ["path", { d: "m9 9 6 6", key: "z0biqf" }]
197
198
  ];
198
- const CircleX = createLucideIcon("circle-x", __iconNode$t);
199
+ const CircleX = createLucideIcon("circle-x", __iconNode$v);
199
200
  /**
200
201
  * @license lucide-react v0.511.0 - ISC
201
202
  *
202
203
  * This source code is licensed under the ISC license.
203
204
  * See the LICENSE file in the root directory of this source tree.
204
205
  */
205
- const __iconNode$s = [["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }]];
206
- const Circle = createLucideIcon("circle", __iconNode$s);
206
+ const __iconNode$u = [["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }]];
207
+ const Circle = createLucideIcon("circle", __iconNode$u);
207
208
  /**
208
209
  * @license lucide-react v0.511.0 - ISC
209
210
  *
210
211
  * This source code is licensed under the ISC license.
211
212
  * See the LICENSE file in the root directory of this source tree.
212
213
  */
213
- const __iconNode$r = [
214
+ const __iconNode$t = [
214
215
  [
215
216
  "path",
216
217
  { d: "M20.2 6 3 11l-.9-2.4c-.3-1.1.3-2.2 1.3-2.5l13.5-4c1.1-.3 2.2.3 2.5 1.3Z", key: "1tn4o7" }
@@ -219,102 +220,118 @@ const __iconNode$r = [
219
220
  ["path", { d: "m12.4 3.4 3.1 4", key: "6hsd6n" }],
220
221
  ["path", { d: "M3 11h18v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2Z", key: "ltgou9" }]
221
222
  ];
222
- const Clapperboard = createLucideIcon("clapperboard", __iconNode$r);
223
+ const Clapperboard = createLucideIcon("clapperboard", __iconNode$t);
223
224
  /**
224
225
  * @license lucide-react v0.511.0 - ISC
225
226
  *
226
227
  * This source code is licensed under the ISC license.
227
228
  * See the LICENSE file in the root directory of this source tree.
228
229
  */
229
- const __iconNode$q = [
230
+ const __iconNode$s = [
230
231
  ["path", { d: "M12 15V3", key: "m9g1x1" }],
231
232
  ["path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4", key: "ih7n3h" }],
232
233
  ["path", { d: "m7 10 5 5 5-5", key: "brsn70" }]
233
234
  ];
234
- const Download = createLucideIcon("download", __iconNode$q);
235
+ const Download = createLucideIcon("download", __iconNode$s);
235
236
  /**
236
237
  * @license lucide-react v0.511.0 - ISC
237
238
  *
238
239
  * This source code is licensed under the ISC license.
239
240
  * See the LICENSE file in the root directory of this source tree.
240
241
  */
241
- const __iconNode$p = [
242
+ const __iconNode$r = [
242
243
  ["path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z", key: "1rqfz7" }],
243
244
  ["path", { d: "M14 2v4a2 2 0 0 0 2 2h4", key: "tnqrlb" }]
244
245
  ];
245
- const File = createLucideIcon("file", __iconNode$p);
246
+ const File = createLucideIcon("file", __iconNode$r);
246
247
  /**
247
248
  * @license lucide-react v0.511.0 - ISC
248
249
  *
249
250
  * This source code is licensed under the ISC license.
250
251
  * See the LICENSE file in the root directory of this source tree.
251
252
  */
252
- const __iconNode$o = [
253
+ const __iconNode$q = [
254
+ [
255
+ "path",
256
+ {
257
+ d: "M10 20a1 1 0 0 0 .553.895l2 1A1 1 0 0 0 14 21v-7a2 2 0 0 1 .517-1.341L21.74 4.67A1 1 0 0 0 21 3H3a1 1 0 0 0-.742 1.67l7.225 7.989A2 2 0 0 1 10 14z",
258
+ key: "sc7q7i"
259
+ }
260
+ ]
261
+ ];
262
+ const Funnel = createLucideIcon("funnel", __iconNode$q);
263
+ /**
264
+ * @license lucide-react v0.511.0 - ISC
265
+ *
266
+ * This source code is licensed under the ISC license.
267
+ * See the LICENSE file in the root directory of this source tree.
268
+ */
269
+ const __iconNode$p = [
253
270
  ["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2", key: "1m3agn" }],
254
271
  ["circle", { cx: "9", cy: "9", r: "2", key: "af1f0g" }],
255
272
  ["path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21", key: "1xmnt7" }]
256
273
  ];
257
- const Image = createLucideIcon("image", __iconNode$o);
274
+ const Image$1 = createLucideIcon("image", __iconNode$p);
258
275
  /**
259
276
  * @license lucide-react v0.511.0 - ISC
260
277
  *
261
278
  * This source code is licensed under the ISC license.
262
279
  * See the LICENSE file in the root directory of this source tree.
263
280
  */
264
- const __iconNode$n = [
281
+ const __iconNode$o = [
265
282
  ["line", { x1: "19", x2: "10", y1: "4", y2: "4", key: "15jd3p" }],
266
283
  ["line", { x1: "14", x2: "5", y1: "20", y2: "20", key: "bu0au3" }],
267
284
  ["line", { x1: "15", x2: "9", y1: "4", y2: "20", key: "uljnxc" }]
268
285
  ];
269
- const Italic = createLucideIcon("italic", __iconNode$n);
286
+ const Italic = createLucideIcon("italic", __iconNode$o);
270
287
  /**
271
288
  * @license lucide-react v0.511.0 - ISC
272
289
  *
273
290
  * This source code is licensed under the ISC license.
274
291
  * See the LICENSE file in the root directory of this source tree.
275
292
  */
276
- const __iconNode$m = [["path", { d: "M21 12a9 9 0 1 1-6.219-8.56", key: "13zald" }]];
277
- const LoaderCircle = createLucideIcon("loader-circle", __iconNode$m);
293
+ const __iconNode$n = [["path", { d: "M21 12a9 9 0 1 1-6.219-8.56", key: "13zald" }]];
294
+ const LoaderCircle = createLucideIcon("loader-circle", __iconNode$n);
278
295
  /**
279
296
  * @license lucide-react v0.511.0 - ISC
280
297
  *
281
298
  * This source code is licensed under the ISC license.
282
299
  * See the LICENSE file in the root directory of this source tree.
283
300
  */
284
- const __iconNode$l = [
301
+ const __iconNode$m = [
285
302
  ["path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z", key: "1lielz" }]
286
303
  ];
287
- const MessageSquare = createLucideIcon("message-square", __iconNode$l);
304
+ const MessageSquare = createLucideIcon("message-square", __iconNode$m);
288
305
  /**
289
306
  * @license lucide-react v0.511.0 - ISC
290
307
  *
291
308
  * This source code is licensed under the ISC license.
292
309
  * See the LICENSE file in the root directory of this source tree.
293
310
  */
294
- const __iconNode$k = [
311
+ const __iconNode$l = [
295
312
  ["circle", { cx: "8", cy: "18", r: "4", key: "1fc0mg" }],
296
313
  ["path", { d: "M12 18V2l7 4", key: "g04rme" }]
297
314
  ];
298
- const Music2 = createLucideIcon("music-2", __iconNode$k);
315
+ const Music2 = createLucideIcon("music-2", __iconNode$l);
299
316
  /**
300
317
  * @license lucide-react v0.511.0 - ISC
301
318
  *
302
319
  * This source code is licensed under the ISC license.
303
320
  * See the LICENSE file in the root directory of this source tree.
304
321
  */
305
- const __iconNode$j = [
322
+ const __iconNode$k = [
306
323
  ["path", { d: "M9 18V5l12-2v13", key: "1jmyc2" }],
307
324
  ["circle", { cx: "6", cy: "18", r: "3", key: "fqmcym" }],
308
325
  ["circle", { cx: "18", cy: "16", r: "3", key: "1hluhg" }]
309
326
  ];
310
- const Music = createLucideIcon("music", __iconNode$j);
327
+ const Music = createLucideIcon("music", __iconNode$k);
311
328
  /**
312
329
  * @license lucide-react v0.511.0 - ISC
313
330
  *
314
331
  * This source code is licensed under the ISC license.
315
332
  * See the LICENSE file in the root directory of this source tree.
316
333
  */
317
- const __iconNode$i = [
334
+ const __iconNode$j = [
318
335
  [
319
336
  "path",
320
337
  {
@@ -327,64 +344,64 @@ const __iconNode$i = [
327
344
  ["circle", { cx: "6.5", cy: "12.5", r: ".5", fill: "currentColor", key: "qy21gx" }],
328
345
  ["circle", { cx: "8.5", cy: "7.5", r: ".5", fill: "currentColor", key: "fotxhn" }]
329
346
  ];
330
- const Palette = createLucideIcon("palette", __iconNode$i);
347
+ const Palette = createLucideIcon("palette", __iconNode$j);
331
348
  /**
332
349
  * @license lucide-react v0.511.0 - ISC
333
350
  *
334
351
  * This source code is licensed under the ISC license.
335
352
  * See the LICENSE file in the root directory of this source tree.
336
353
  */
337
- const __iconNode$h = [
354
+ const __iconNode$i = [
338
355
  ["rect", { x: "14", y: "4", width: "4", height: "16", rx: "1", key: "zuxfzm" }],
339
356
  ["rect", { x: "6", y: "4", width: "4", height: "16", rx: "1", key: "1okwgv" }]
340
357
  ];
341
- const Pause = createLucideIcon("pause", __iconNode$h);
358
+ const Pause = createLucideIcon("pause", __iconNode$i);
342
359
  /**
343
360
  * @license lucide-react v0.511.0 - ISC
344
361
  *
345
362
  * This source code is licensed under the ISC license.
346
363
  * See the LICENSE file in the root directory of this source tree.
347
364
  */
348
- const __iconNode$g = [["polygon", { points: "6 3 20 12 6 21 6 3", key: "1oa8hb" }]];
349
- const Play = createLucideIcon("play", __iconNode$g);
365
+ const __iconNode$h = [["polygon", { points: "6 3 20 12 6 21 6 3", key: "1oa8hb" }]];
366
+ const Play = createLucideIcon("play", __iconNode$h);
350
367
  /**
351
368
  * @license lucide-react v0.511.0 - ISC
352
369
  *
353
370
  * This source code is licensed under the ISC license.
354
371
  * See the LICENSE file in the root directory of this source tree.
355
372
  */
356
- const __iconNode$f = [
373
+ const __iconNode$g = [
357
374
  ["path", { d: "M5 12h14", key: "1ays0h" }],
358
375
  ["path", { d: "M12 5v14", key: "s699le" }]
359
376
  ];
360
- const Plus = createLucideIcon("plus", __iconNode$f);
377
+ const Plus = createLucideIcon("plus", __iconNode$g);
361
378
  /**
362
379
  * @license lucide-react v0.511.0 - ISC
363
380
  *
364
381
  * This source code is licensed under the ISC license.
365
382
  * See the LICENSE file in the root directory of this source tree.
366
383
  */
367
- const __iconNode$e = [
384
+ const __iconNode$f = [
368
385
  ["rect", { width: "20", height: "12", x: "2", y: "6", rx: "2", key: "9lu3g6" }]
369
386
  ];
370
- const RectangleHorizontal = createLucideIcon("rectangle-horizontal", __iconNode$e);
387
+ const RectangleHorizontal = createLucideIcon("rectangle-horizontal", __iconNode$f);
371
388
  /**
372
389
  * @license lucide-react v0.511.0 - ISC
373
390
  *
374
391
  * This source code is licensed under the ISC license.
375
392
  * See the LICENSE file in the root directory of this source tree.
376
393
  */
377
- const __iconNode$d = [
394
+ const __iconNode$e = [
378
395
  ["rect", { width: "12", height: "20", x: "6", y: "2", rx: "2", key: "1oxtiu" }]
379
396
  ];
380
- const RectangleVertical = createLucideIcon("rectangle-vertical", __iconNode$d);
397
+ const RectangleVertical = createLucideIcon("rectangle-vertical", __iconNode$e);
381
398
  /**
382
399
  * @license lucide-react v0.511.0 - ISC
383
400
  *
384
401
  * This source code is licensed under the ISC license.
385
402
  * See the LICENSE file in the root directory of this source tree.
386
403
  */
387
- const __iconNode$c = [
404
+ const __iconNode$d = [
388
405
  [
389
406
  "path",
390
407
  {
@@ -397,14 +414,14 @@ const __iconNode$c = [
397
414
  ["path", { d: "m8.5 6.5 2-2", key: "vc6u1g" }],
398
415
  ["path", { d: "m17.5 15.5 2-2", key: "wo5hmg" }]
399
416
  ];
400
- const Ruler = createLucideIcon("ruler", __iconNode$c);
417
+ const Ruler = createLucideIcon("ruler", __iconNode$d);
401
418
  /**
402
419
  * @license lucide-react v0.511.0 - ISC
403
420
  *
404
421
  * This source code is licensed under the ISC license.
405
422
  * See the LICENSE file in the root directory of this source tree.
406
423
  */
407
- const __iconNode$b = [
424
+ const __iconNode$c = [
408
425
  [
409
426
  "path",
410
427
  {
@@ -415,32 +432,45 @@ const __iconNode$b = [
415
432
  ["path", { d: "M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7", key: "1ydtos" }],
416
433
  ["path", { d: "M7 3v4a1 1 0 0 0 1 1h7", key: "t51u73" }]
417
434
  ];
418
- const Save = createLucideIcon("save", __iconNode$b);
435
+ const Save = createLucideIcon("save", __iconNode$c);
419
436
  /**
420
437
  * @license lucide-react v0.511.0 - ISC
421
438
  *
422
439
  * This source code is licensed under the ISC license.
423
440
  * See the LICENSE file in the root directory of this source tree.
424
441
  */
425
- const __iconNode$a = [
442
+ const __iconNode$b = [
426
443
  ["circle", { cx: "6", cy: "6", r: "3", key: "1lh9wr" }],
427
444
  ["path", { d: "M8.12 8.12 12 12", key: "1alkpv" }],
428
445
  ["path", { d: "M20 4 8.12 15.88", key: "xgtan2" }],
429
446
  ["circle", { cx: "6", cy: "18", r: "3", key: "fqmcym" }],
430
447
  ["path", { d: "M14.8 14.8 20 20", key: "ptml3r" }]
431
448
  ];
432
- const Scissors = createLucideIcon("scissors", __iconNode$a);
449
+ const Scissors = createLucideIcon("scissors", __iconNode$b);
433
450
  /**
434
451
  * @license lucide-react v0.511.0 - ISC
435
452
  *
436
453
  * This source code is licensed under the ISC license.
437
454
  * See the LICENSE file in the root directory of this source tree.
438
455
  */
439
- const __iconNode$9 = [
456
+ const __iconNode$a = [
440
457
  ["path", { d: "m21 21-4.34-4.34", key: "14j7rj" }],
441
458
  ["circle", { cx: "11", cy: "11", r: "8", key: "4ej97u" }]
442
459
  ];
443
- const Search = createLucideIcon("search", __iconNode$9);
460
+ const Search = createLucideIcon("search", __iconNode$a);
461
+ /**
462
+ * @license lucide-react v0.511.0 - ISC
463
+ *
464
+ * This source code is licensed under the ISC license.
465
+ * See the LICENSE file in the root directory of this source tree.
466
+ */
467
+ const __iconNode$9 = [
468
+ ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
469
+ ["path", { d: "M8 14s1.5 2 4 2 4-2 4-2", key: "1y1vjs" }],
470
+ ["line", { x1: "9", x2: "9.01", y1: "9", y2: "9", key: "yxxnd0" }],
471
+ ["line", { x1: "15", x2: "15.01", y1: "9", y2: "9", key: "1p4y9e" }]
472
+ ];
473
+ const Smile = createLucideIcon("smile", __iconNode$9);
444
474
  /**
445
475
  * @license lucide-react v0.511.0 - ISC
446
476
  *
@@ -592,6 +622,7 @@ const defaultToolCategories = [
592
622
  { id: "image", name: "Image", icon: "Image", description: "Add an image element" },
593
623
  { id: "audio", name: "Audio", icon: "Audio", description: "Add an audio element" },
594
624
  { id: "text", name: "Text", icon: "Type", description: "Add text elements" },
625
+ { id: "emoji", name: "Emoji", icon: "Smile", description: "Add emoji stickers" },
595
626
  { id: "text-style", name: "Text Style", icon: "Type", description: "Apply text style presets" },
596
627
  { id: "effect", name: "Effect", icon: "Wand2", description: "Apply GL video effects" },
597
628
  { id: "shape", name: "Shape", icon: "Square", description: "Add lines, arrows, boxes, and circles" },
@@ -611,7 +642,7 @@ const getIcon = (iconName) => {
611
642
  case "Square":
612
643
  return Square;
613
644
  case "Image":
614
- return Image;
645
+ return Image$1;
615
646
  case "Video":
616
647
  return Video;
617
648
  case "Audio":
@@ -626,6 +657,8 @@ const getIcon = (iconName) => {
626
657
  return WandSparkles;
627
658
  case "File":
628
659
  return File;
660
+ case "Smile":
661
+ return Smile;
629
662
  default:
630
663
  return Plus;
631
664
  }
@@ -3000,6 +3033,155 @@ function TextPanelContainer(props) {
3000
3033
  const textPanelProps = useTextPanel(props);
3001
3034
  return /* @__PURE__ */ jsx(TextPanel, { ...textPanelProps });
3002
3035
  }
3036
+ const SUPPORTED_EMOJIS = [
3037
+ { emoji: "😀", category: "Smileys" },
3038
+ { emoji: "😁", category: "Smileys" },
3039
+ { emoji: "😂", category: "Smileys" },
3040
+ { emoji: "🤣", category: "Smileys" },
3041
+ { emoji: "😍", category: "Smileys" },
3042
+ { emoji: "🤩", category: "Smileys" },
3043
+ { emoji: "😎", category: "Smileys" },
3044
+ { emoji: "🥳", category: "Smileys" },
3045
+ { emoji: "🤯", category: "Smileys" },
3046
+ { emoji: "🤔", category: "Smileys" },
3047
+ { emoji: "😅", category: "Smileys" },
3048
+ { emoji: "😮", category: "Smileys" },
3049
+ { emoji: "🥹", category: "Smileys" },
3050
+ { emoji: "🔥", category: "Symbols" },
3051
+ { emoji: "✨", category: "Symbols" },
3052
+ { emoji: "⭐", category: "Symbols" },
3053
+ { emoji: "💥", category: "Symbols" },
3054
+ { emoji: "💯", category: "Symbols" },
3055
+ { emoji: "✅", category: "Symbols" },
3056
+ { emoji: "❌", category: "Symbols" },
3057
+ { emoji: "⚡", category: "Symbols" },
3058
+ { emoji: "❤️", category: "Symbols" },
3059
+ { emoji: "🏆", category: "Objects" },
3060
+ { emoji: "💡", category: "Objects" },
3061
+ { emoji: "📈", category: "Objects" },
3062
+ { emoji: "📣", category: "Objects" },
3063
+ { emoji: "🧠", category: "Objects" },
3064
+ { emoji: "👀", category: "Objects" },
3065
+ { emoji: "🤖", category: "Objects" },
3066
+ { emoji: "👍", category: "Hands" },
3067
+ { emoji: "👏", category: "Hands" },
3068
+ { emoji: "🙌", category: "Hands" },
3069
+ { emoji: "🤝", category: "Hands" },
3070
+ { emoji: "🙏", category: "Hands" },
3071
+ { emoji: "🚀", category: "Media" },
3072
+ { emoji: "🎯", category: "Media" },
3073
+ { emoji: "🎉", category: "Media" },
3074
+ { emoji: "🎬", category: "Media" },
3075
+ { emoji: "🎥", category: "Media" },
3076
+ { emoji: "🎧", category: "Media" }
3077
+ ];
3078
+ const CDN_BASE = "https://cdnjs.cloudflare.com/ajax/libs/twemoji/14.0.2/72x72";
3079
+ function toCodePointString(emoji) {
3080
+ return Array.from(emoji).map((char) => {
3081
+ var _a;
3082
+ return (_a = char.codePointAt(0)) == null ? void 0 : _a.toString(16);
3083
+ }).filter(Boolean).join("-");
3084
+ }
3085
+ const EMOJI_CATEGORIES = [
3086
+ "All",
3087
+ "Smileys",
3088
+ "Symbols",
3089
+ "Objects",
3090
+ "Hands",
3091
+ "Media"
3092
+ ];
3093
+ const EMOJI_CATALOG = SUPPORTED_EMOJIS.map(
3094
+ ({ emoji, category }) => ({
3095
+ emoji,
3096
+ category,
3097
+ label: emoji,
3098
+ imageUrl: `${CDN_BASE}/${toCodePointString(emoji)}.png`
3099
+ })
3100
+ );
3101
+ function EmojiPanel({ items, onItemSelect }) {
3102
+ const [searchQuery, setSearchQuery] = useState("");
3103
+ const [activeCategory, setActiveCategory] = useState("All");
3104
+ const filteredItems = useMemo(() => {
3105
+ const query = searchQuery.trim().toLowerCase();
3106
+ return items.filter((item) => {
3107
+ const categoryMatch = activeCategory === "All" ? true : item.category === activeCategory;
3108
+ if (!categoryMatch) return false;
3109
+ if (!query) return true;
3110
+ return item.emoji.includes(query) || item.label.toLowerCase().includes(query) || item.category.toLowerCase().includes(query);
3111
+ });
3112
+ }, [items, searchQuery, activeCategory]);
3113
+ return /* @__PURE__ */ jsxs("div", { className: "panel-container", children: [
3114
+ /* @__PURE__ */ jsx("div", { className: "panel-title", children: "Emoji Stickers" }),
3115
+ /* @__PURE__ */ jsx("div", { className: "panel-section", children: /* @__PURE__ */ jsx(
3116
+ "input",
3117
+ {
3118
+ type: "text",
3119
+ placeholder: "Search emoji...",
3120
+ value: searchQuery,
3121
+ onChange: (e) => setSearchQuery(e.target.value),
3122
+ className: "input-dark"
3123
+ }
3124
+ ) }),
3125
+ /* @__PURE__ */ jsx("div", { className: "emoji-categories", children: EMOJI_CATEGORIES.map((category) => /* @__PURE__ */ jsx(
3126
+ "button",
3127
+ {
3128
+ type: "button",
3129
+ className: `emoji-category-chip ${activeCategory === category ? "emoji-category-chip-active" : ""}`,
3130
+ onClick: () => setActiveCategory(category),
3131
+ children: category
3132
+ },
3133
+ category
3134
+ )) }),
3135
+ /* @__PURE__ */ jsx("div", { className: "emoji-grid", children: filteredItems.map((item) => /* @__PURE__ */ jsx(
3136
+ "button",
3137
+ {
3138
+ type: "button",
3139
+ className: "emoji-item",
3140
+ title: item.label,
3141
+ onClick: () => onItemSelect(item),
3142
+ children: /* @__PURE__ */ jsx(
3143
+ "img",
3144
+ {
3145
+ src: item.imageUrl,
3146
+ alt: item.label,
3147
+ className: "emoji-item-image",
3148
+ loading: "lazy"
3149
+ }
3150
+ )
3151
+ },
3152
+ `${item.emoji}-${item.imageUrl}`
3153
+ )) }),
3154
+ filteredItems.length === 0 && /* @__PURE__ */ jsx("div", { className: "empty-state", children: /* @__PURE__ */ jsx("div", { className: "empty-state-content", children: /* @__PURE__ */ jsx("p", { className: "empty-state-text", children: "No emoji found" }) }) })
3155
+ ] });
3156
+ }
3157
+ function EmojiPanelContainer({ addElement, videoResolution }) {
3158
+ const emojiFallbackFont = "Poppins, Noto Color Emoji, Apple Color Emoji, Segoe UI Emoji, sans-serif";
3159
+ const canLoadEmojiImage = async (url) => new Promise((resolve) => {
3160
+ let settled = false;
3161
+ const done = (value) => {
3162
+ if (settled) return;
3163
+ settled = true;
3164
+ resolve(value);
3165
+ };
3166
+ const img = new Image();
3167
+ img.onload = () => done(true);
3168
+ img.onerror = () => done(false);
3169
+ img.src = url;
3170
+ window.setTimeout(() => done(false), 3e3);
3171
+ });
3172
+ const handleSelection = async (item) => {
3173
+ if (!addElement) return;
3174
+ const canUseImage = await canLoadEmojiImage(item.imageUrl);
3175
+ if (canUseImage) {
3176
+ const element = new EmojiElement(item.emoji, item.imageUrl, videoResolution).setName(`Emoji ${item.emoji}`).setEnd(5);
3177
+ await addElement(element);
3178
+ return;
3179
+ }
3180
+ const fallbackText = new TextElement(item.emoji).setName(`Emoji ${item.emoji} (fallback)`).setFontFamily(emojiFallbackFont).setFontSize(Math.round(videoResolution.height * 0.12)).setFill("#FFFFFF").setLineWidth(0).setEnd(5);
3181
+ await addElement(fallbackText);
3182
+ };
3183
+ return /* @__PURE__ */ jsx(EmojiPanel, { items: EMOJI_CATALOG, onItemSelect: handleSelection });
3184
+ }
3003
3185
  const TEXT_STYLE_PRESETS = [
3004
3186
  // Utility / captions
3005
3187
  {
@@ -4725,7 +4907,15 @@ function CaptionsPanel({
4725
4907
  children: [
4726
4908
  /* @__PURE__ */ jsxs("div", { className: "captions-panel-item-header", children: [
4727
4909
  /* @__PURE__ */ jsx("span", { className: "captions-panel-time captions-panel-time-start", children: formatTime(caption.s) }),
4728
- /* @__PURE__ */ jsx("span", { className: "captions-panel-time captions-panel-time-end", children: formatTime(caption.e) })
4910
+ /* @__PURE__ */ jsx("span", { className: "captions-panel-time captions-panel-time-end", children: formatTime(caption.e) }),
4911
+ caption.isCustom ? /* @__PURE__ */ jsx(
4912
+ "span",
4913
+ {
4914
+ className: "captions-panel-custom",
4915
+ title: "This caption overrides track defaults",
4916
+ children: "Custom"
4917
+ }
4918
+ ) : null
4729
4919
  ] }),
4730
4920
  /* @__PURE__ */ jsxs("div", { className: "captions-panel-item-body", children: [
4731
4921
  /* @__PURE__ */ jsx(
@@ -4894,11 +5084,15 @@ const useCaptionsPanel = () => {
4894
5084
  }
4895
5085
  captionsTrack.current = editorCaptionsTrack;
4896
5086
  setCaptions(
4897
- editorCaptionsTrack.getElements().map((element) => ({
4898
- s: element.getStart(),
4899
- e: element.getEnd(),
4900
- t: element.getText()
4901
- }))
5087
+ editorCaptionsTrack.getElements().map((element) => {
5088
+ var _a;
5089
+ return {
5090
+ s: element.getStart(),
5091
+ e: element.getEnd(),
5092
+ t: element.getText(),
5093
+ isCustom: ((_a = element.getProps()) == null ? void 0 : _a.useTrackDefaults) === false
5094
+ };
5095
+ })
4902
5096
  );
4903
5097
  };
4904
5098
  useEffect(() => {
@@ -4912,14 +5106,13 @@ const useCaptionsPanel = () => {
4912
5106
  capStyle: CAPTION_STYLE.WORD_BG_HIGHLIGHT,
4913
5107
  ...CAPTION_PROPS[CAPTION_STYLE.WORD_BG_HIGHLIGHT],
4914
5108
  x: 0,
4915
- y: 200,
4916
- applyToAll: true
5109
+ y: 200
4917
5110
  };
4918
5111
  (_a = captionsTrack.current) == null ? void 0 : _a.setProps(props);
4919
5112
  }
4920
5113
  };
4921
5114
  const addCaption = () => {
4922
- const newCaption = { s: 0, e: 0, t: "New Caption" };
5115
+ const newCaption = { s: 0, e: 0, t: "New Caption", isCustom: false };
4923
5116
  if (captions.length > 0) {
4924
5117
  newCaption.s = captions[captions.length - 1].e;
4925
5118
  }
@@ -5812,6 +6005,16 @@ const ElementPanelContainer = ({
5812
6005
  updateElement
5813
6006
  }
5814
6007
  );
6008
+ case "emoji":
6009
+ return /* @__PURE__ */ jsx(
6010
+ EmojiPanelContainer,
6011
+ {
6012
+ selectedElement,
6013
+ videoResolution,
6014
+ addElement: addNewElement,
6015
+ updateElement
6016
+ }
6017
+ );
5815
6018
  case "text-style":
5816
6019
  return /* @__PURE__ */ jsx(
5817
6020
  TextStylePanelContainer,
@@ -6497,8 +6700,10 @@ const DEFAULT_COLOR_META = {
6497
6700
  const CAPTION_FONTS = Object.values(AVAILABLE_TEXT_FONTS);
6498
6701
  function CaptionPropPanel({
6499
6702
  selectedElement,
6500
- updateElement
6703
+ updateElement,
6704
+ setApplyPropsToAllCaption
6501
6705
  }) {
6706
+ var _a;
6502
6707
  const { editor, changeLog } = useTimelineContext();
6503
6708
  const captionRef = useRef(null);
6504
6709
  const [capStyle, setCapStyle] = useState(
@@ -6516,16 +6721,14 @@ function CaptionPropPanel({
6516
6721
  const [useOutline, setUseOutline] = useState(true);
6517
6722
  const track = selectedElement instanceof CaptionElement ? editor.getTrackById(selectedElement.getTrackId()) : null;
6518
6723
  const trackProps = (track == null ? void 0 : track.getProps()) ?? {};
6519
- const applyToAll = (trackProps == null ? void 0 : trackProps.applyToAll) ?? false;
6520
- const handleUpdateCaption = (updates) => {
6521
- const captionElement = selectedElement;
6522
- if (!captionElement) return;
6523
- const nextFontSize = updates.fontSize ?? fontSize;
6524
- const geometry = computeCaptionGeometry(nextFontSize, updates.style ?? (capStyle == null ? void 0 : capStyle.value) ?? "");
6525
- const highlightEnabled = updates.useHighlightOverride ?? useHighlight;
6526
- const outlineEnabled = updates.useOutlineOverride ?? useOutline;
6527
- const rawNextColors = updates.colors ?? colors;
6528
- let effectiveColors = { ...rawNextColors };
6724
+ const elementProps = ((_a = selectedElement == null ? void 0 : selectedElement.getProps) == null ? void 0 : _a.call(selectedElement)) ?? {};
6725
+ const useTrackDefaults = (elementProps == null ? void 0 : elementProps.useTrackDefaults) ?? true;
6726
+ const getEffectiveColors = ({
6727
+ nextColors,
6728
+ highlightEnabled,
6729
+ outlineEnabled
6730
+ }) => {
6731
+ let effectiveColors = { ...nextColors };
6529
6732
  if (!highlightEnabled) {
6530
6733
  const { highlight, ...rest } = effectiveColors;
6531
6734
  effectiveColors = rest;
@@ -6534,26 +6737,73 @@ function CaptionPropPanel({
6534
6737
  const { outlineColor, ...rest } = effectiveColors;
6535
6738
  effectiveColors = rest;
6536
6739
  }
6537
- if (applyToAll && track) {
6740
+ return effectiveColors;
6741
+ };
6742
+ const handleUseTrackDefaultsChange = (enabled) => {
6743
+ const captionElement = selectedElement;
6744
+ if (!captionElement) return;
6745
+ const prev = captionElement.getProps() ?? {};
6746
+ const next = { ...prev, useTrackDefaults: enabled };
6747
+ if (enabled) {
6748
+ const keysToClear = [
6749
+ "capStyle",
6750
+ "x",
6751
+ "y",
6752
+ "width",
6753
+ "maxWidth",
6754
+ "textAlign",
6755
+ "rotation",
6756
+ "opacity",
6757
+ "colors",
6758
+ "font",
6759
+ "lineWidth",
6760
+ "rectProps",
6761
+ "shadowColor",
6762
+ "shadowBlur",
6763
+ "shadowOffset",
6764
+ "fill",
6765
+ "stroke"
6766
+ ];
6767
+ for (const k of keysToClear) {
6768
+ delete next[k];
6769
+ }
6770
+ }
6771
+ captionElement.setProps(next);
6772
+ updateElement == null ? void 0 : updateElement(captionElement);
6773
+ setApplyPropsToAllCaption == null ? void 0 : setApplyPropsToAllCaption(enabled);
6774
+ };
6775
+ const handleUpdateCaption = (updates) => {
6776
+ const captionElement = selectedElement;
6777
+ if (!captionElement) return;
6778
+ const nextFontSize = updates.fontSize ?? fontSize;
6779
+ const geometry = computeCaptionGeometry(nextFontSize, updates.style ?? (capStyle == null ? void 0 : capStyle.value) ?? "");
6780
+ const highlightEnabled = updates.useHighlightOverride ?? useHighlight;
6781
+ const outlineEnabled = updates.useOutlineOverride ?? useOutline;
6782
+ const rawNextColors = updates.colors ?? colors;
6783
+ const effectiveColors = getEffectiveColors({
6784
+ nextColors: rawNextColors,
6785
+ highlightEnabled,
6786
+ outlineEnabled
6787
+ });
6788
+ if (useTrackDefaults && track) {
6538
6789
  const nextFont = {
6539
6790
  size: nextFontSize,
6540
6791
  family: updates.fontFamily ?? fontFamily
6541
6792
  };
6542
6793
  const nextColors = effectiveColors;
6543
6794
  const nextCapStyle = updates.style ?? (capStyle == null ? void 0 : capStyle.value);
6544
- track.setProps({
6545
- ...trackProps,
6795
+ editor.updateTrackProps(track.getId(), {
6546
6796
  capStyle: nextCapStyle,
6547
6797
  font: { ...(trackProps == null ? void 0 : trackProps.font) ?? {}, ...nextFont },
6548
6798
  colors: nextColors,
6549
6799
  lineWidth: geometry.lineWidth,
6550
6800
  rectProps: geometry.rectProps
6551
6801
  });
6552
- editor.refresh();
6553
6802
  } else {
6554
- const elementProps = captionElement.getProps() ?? {};
6803
+ const elementProps2 = captionElement.getProps() ?? {};
6555
6804
  captionElement.setProps({
6556
- ...elementProps,
6805
+ ...elementProps2,
6806
+ useTrackDefaults: false,
6557
6807
  capStyle: updates.style ?? (capStyle == null ? void 0 : capStyle.value),
6558
6808
  font: {
6559
6809
  size: nextFontSize,
@@ -6566,20 +6816,29 @@ function CaptionPropPanel({
6566
6816
  }
6567
6817
  };
6568
6818
  useEffect(() => {
6569
- var _a, _b;
6570
6819
  const captionElement = selectedElement;
6571
6820
  if (captionElement) {
6572
6821
  if (captionRef.current) {
6573
6822
  captionRef.current.value = captionElement == null ? void 0 : captionElement.getText();
6574
6823
  }
6575
- const props = applyToAll ? trackProps : captionElement.getProps() ?? {};
6576
- const _capStyle = props == null ? void 0 : props.capStyle;
6824
+ const elementProps2 = captionElement.getProps() ?? {};
6825
+ const elementUseTrackDefaults = (elementProps2 == null ? void 0 : elementProps2.useTrackDefaults) ?? true;
6826
+ const resolvedCapStyle = elementUseTrackDefaults ? trackProps == null ? void 0 : trackProps.capStyle : (elementProps2 == null ? void 0 : elementProps2.capStyle) ?? (trackProps == null ? void 0 : trackProps.capStyle);
6827
+ const resolvedFont = elementUseTrackDefaults ? trackProps == null ? void 0 : trackProps.font : {
6828
+ ...(trackProps == null ? void 0 : trackProps.font) ?? {},
6829
+ ...(elementProps2 == null ? void 0 : elementProps2.font) ?? {}
6830
+ };
6831
+ const resolvedColors = elementUseTrackDefaults ? trackProps == null ? void 0 : trackProps.colors : {
6832
+ ...(trackProps == null ? void 0 : trackProps.colors) ?? {},
6833
+ ...(elementProps2 == null ? void 0 : elementProps2.colors) ?? {}
6834
+ };
6835
+ const _capStyle = resolvedCapStyle;
6577
6836
  if (_capStyle && _capStyle in CAPTION_STYLE_OPTIONS) {
6578
6837
  setCapStyle(CAPTION_STYLE_OPTIONS[_capStyle]);
6579
6838
  }
6580
- setFontSize(((_a = props == null ? void 0 : props.font) == null ? void 0 : _a.size) ?? CAPTION_FONT2.size);
6581
- setFontFamily(((_b = props == null ? void 0 : props.font) == null ? void 0 : _b.family) ?? CAPTION_FONT2.family);
6582
- const c = props == null ? void 0 : props.colors;
6839
+ setFontSize((resolvedFont == null ? void 0 : resolvedFont.size) ?? CAPTION_FONT2.size);
6840
+ setFontFamily((resolvedFont == null ? void 0 : resolvedFont.family) ?? CAPTION_FONT2.family);
6841
+ const c = resolvedColors;
6583
6842
  setColors({
6584
6843
  text: (c == null ? void 0 : c.text) ?? CAPTION_COLOR2.text,
6585
6844
  highlight: (c == null ? void 0 : c.highlight) ?? CAPTION_COLOR2.highlight,
@@ -6589,7 +6848,7 @@ function CaptionPropPanel({
6589
6848
  setUseHighlight((c == null ? void 0 : c.highlight) != null);
6590
6849
  setUseOutline((c == null ? void 0 : c.outlineColor) != null);
6591
6850
  }
6592
- }, [selectedElement, applyToAll, changeLog]);
6851
+ }, [selectedElement, track, changeLog]);
6593
6852
  if (!(selectedElement instanceof CaptionElement)) {
6594
6853
  return null;
6595
6854
  }
@@ -6643,6 +6902,18 @@ function CaptionPropPanel({
6643
6902
  ] }, key);
6644
6903
  };
6645
6904
  return /* @__PURE__ */ jsxs("div", { className: "panel-container", children: [
6905
+ /* @__PURE__ */ jsx("div", { className: "panel-section", children: /* @__PURE__ */ jsx("div", { className: "checkbox-control", children: /* @__PURE__ */ jsxs("label", { className: "checkbox-label", children: [
6906
+ /* @__PURE__ */ jsx(
6907
+ "input",
6908
+ {
6909
+ type: "checkbox",
6910
+ checked: useTrackDefaults,
6911
+ onChange: (e) => handleUseTrackDefaultsChange(e.target.checked),
6912
+ className: "checkbox-purple"
6913
+ }
6914
+ ),
6915
+ "Use track defaults"
6916
+ ] }) }) }),
6646
6917
  /* @__PURE__ */ jsxs("div", { className: "panel-section", children: [
6647
6918
  /* @__PURE__ */ jsx("label", { className: "label-dark", children: "Caption Style" }),
6648
6919
  /* @__PURE__ */ jsx(
@@ -6846,92 +7117,82 @@ function PlaybackPropsPanel({
6846
7117
  )
6847
7118
  ] });
6848
7119
  }
6849
- const hasAudio = async (src) => {
6850
- if (!src) return false;
6851
- const isSafeUrl = /^(https?:|blob:|data:)/i.test(src);
6852
- if (!isSafeUrl) return false;
6853
- try {
6854
- const audioBuffer = await fetchAndDecodeAudio(src);
6855
- if (audioBuffer.duration === 0 || audioBuffer.length === 0) {
6856
- return false;
6857
- }
6858
- if (isAudioSilent(audioBuffer)) {
6859
- return false;
6860
- }
6861
- return true;
6862
- } catch (error) {
6863
- return false;
6864
- }
7120
+ const NONE_VALUE = "none";
7121
+ const FILTER_LABELS = {
7122
+ [NONE_VALUE]: "None",
7123
+ [COLOR_FILTERS.SATURATED]: "Saturated",
7124
+ [COLOR_FILTERS.BRIGHT]: "Bright",
7125
+ [COLOR_FILTERS.VIBRANT]: "Vibrant",
7126
+ [COLOR_FILTERS.RETRO]: "Retro",
7127
+ [COLOR_FILTERS.BLACK_WHITE]: "Black & white",
7128
+ [COLOR_FILTERS.SEPIA]: "Sepia",
7129
+ [COLOR_FILTERS.COOL]: "Cool",
7130
+ [COLOR_FILTERS.WARM]: "Warm",
7131
+ [COLOR_FILTERS.CINEMATIC]: "Cinematic",
7132
+ [COLOR_FILTERS.SOFT_GLOW]: "Soft glow",
7133
+ [COLOR_FILTERS.MOODY]: "Moody",
7134
+ [COLOR_FILTERS.DREAMY]: "Dreamy",
7135
+ [COLOR_FILTERS.INVERTED]: "Inverted",
7136
+ [COLOR_FILTERS.VINTAGE]: "Vintage",
7137
+ [COLOR_FILTERS.DRAMATIC]: "Dramatic",
7138
+ [COLOR_FILTERS.FADED]: "Faded"
6865
7139
  };
6866
- const fetchAndDecodeAudio = async (src) => {
6867
- const response = await fetch(src);
6868
- if (!response.ok) throw new Error(`Failed to fetch source: ${response.status}`);
6869
- const arrayBuffer = await response.arrayBuffer();
6870
- return decodeAudioData(arrayBuffer);
6871
- };
6872
- const decodeAudioData = async (arrayBuffer) => {
6873
- const AudioContextCtor = window.AudioContext || window.webkitAudioContext;
6874
- if (!AudioContextCtor) throw new Error("Web Audio API not supported");
6875
- const audioContext = new AudioContextCtor();
6876
- try {
6877
- return await new Promise((resolve, reject) => {
6878
- audioContext.decodeAudioData(
6879
- arrayBuffer.slice(0),
6880
- (buf) => resolve(buf),
6881
- (err) => reject(err || new Error("Failed to decode audio: no audio track found or unsupported format"))
6882
- );
6883
- });
6884
- } finally {
6885
- audioContext.close();
7140
+ function isMediaFilterElement(el) {
7141
+ return el instanceof VideoElement || el instanceof ImageElement;
7142
+ }
7143
+ function ColorFilterPropsPanel({
7144
+ selectedElement,
7145
+ updateElement
7146
+ }) {
7147
+ const mediaEl = isMediaFilterElement(selectedElement) ? selectedElement : null;
7148
+ const options = useMemo(() => {
7149
+ const entries = Object.values(COLOR_FILTERS).map(
7150
+ (value) => ({
7151
+ value,
7152
+ label: FILTER_LABELS[value] ?? value
7153
+ })
7154
+ );
7155
+ return [{ value: NONE_VALUE, label: FILTER_LABELS[NONE_VALUE] }, ...entries];
7156
+ }, []);
7157
+ const elementProps = (mediaEl == null ? void 0 : mediaEl.getProps()) ?? {};
7158
+ const mediaFilter = elementProps.mediaFilter ?? NONE_VALUE;
7159
+ const handleFilterChange = (value) => {
7160
+ if (!mediaEl || !updateElement) return;
7161
+ const allowed = Object.values(COLOR_FILTERS);
7162
+ const next = value === NONE_VALUE ? NONE_VALUE : allowed.includes(value) ? value : NONE_VALUE;
7163
+ updateElement(
7164
+ mediaEl.setProps({
7165
+ ...elementProps,
7166
+ mediaFilter: next
7167
+ })
7168
+ );
7169
+ };
7170
+ const [isOpen, setIsOpen] = useState(false);
7171
+ if (!mediaEl) {
7172
+ return null;
6886
7173
  }
6887
- };
6888
- const isAudioSilent = (buffer, threshold = 1e-3) => {
6889
- for (let channel = 0; channel < buffer.numberOfChannels; channel++) {
6890
- const channelData = buffer.getChannelData(channel);
6891
- for (let i = 0; i < channelData.length; i += 100) {
6892
- if (Math.abs(channelData[i]) > threshold) {
6893
- return false;
7174
+ return /* @__PURE__ */ jsxs("div", { className: "panel-container", children: [
7175
+ /* @__PURE__ */ jsx("div", { className: "panel-title", children: "Look" }),
7176
+ /* @__PURE__ */ jsx(
7177
+ AccordionItem,
7178
+ {
7179
+ title: "Color filter",
7180
+ icon: /* @__PURE__ */ jsx(Funnel, { className: "icon-sm" }),
7181
+ isOpen,
7182
+ onToggle: () => setIsOpen((open) => !open),
7183
+ children: /* @__PURE__ */ jsx("div", { className: "properties-group", children: /* @__PURE__ */ jsx("div", { className: "property-section", children: /* @__PURE__ */ jsx(PropertyRow, { label: "Preset", children: /* @__PURE__ */ jsx(
7184
+ "select",
7185
+ {
7186
+ value: options.some((o) => o.value === mediaFilter) ? mediaFilter : NONE_VALUE,
7187
+ onChange: (e) => handleFilterChange(e.target.value),
7188
+ className: "select-dark w-full",
7189
+ children: options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
7190
+ }
7191
+ ) }) }) })
6894
7192
  }
6895
- }
6896
- }
6897
- return true;
6898
- };
6899
- const loadFile = (accept) => {
6900
- return new Promise((resolve, reject) => {
6901
- try {
6902
- const input = document.createElement("input");
6903
- input.type = "file";
6904
- input.accept = accept;
6905
- input.style.display = "none";
6906
- document.body.appendChild(input);
6907
- const cleanup = () => {
6908
- input.value = "";
6909
- document.body.removeChild(input);
6910
- };
6911
- input.onchange = () => {
6912
- const file = input.files && input.files[0];
6913
- cleanup();
6914
- if (!file) {
6915
- reject(new Error("No file selected"));
6916
- return;
6917
- }
6918
- resolve(file);
6919
- };
6920
- input.click();
6921
- } catch (error) {
6922
- reject(error);
6923
- }
6924
- });
6925
- };
6926
- const saveAsFile = (content, type, name) => {
6927
- const blob = typeof content === "string" ? new Blob([content], { type }) : content;
6928
- const url = URL.createObjectURL(blob);
6929
- const a = document.createElement("a");
6930
- a.href = url;
6931
- a.download = name;
6932
- a.click();
6933
- URL.revokeObjectURL(url);
6934
- };
7193
+ )
7194
+ ] });
7195
+ }
6935
7196
  function GenerateCaptionsPanel({
6936
7197
  selectedElement,
6937
7198
  addCaptionsToTimeline,
@@ -7504,6 +7765,7 @@ function PropertiesPanelContainer({
7504
7765
  selectedElement && !(selectedElement instanceof CaptionElement) && /* @__PURE__ */ jsx(Fragment, { children: (() => {
7505
7766
  const isText = selectedElement instanceof TextElement;
7506
7767
  const isVideo = selectedElement instanceof VideoElement;
7768
+ const isImage = selectedElement instanceof ImageElement;
7507
7769
  const isAudio = selectedElement instanceof AudioElement;
7508
7770
  const isAnnotation = selectedElement instanceof ArrowElement || selectedElement instanceof LineElement || selectedElement instanceof RectElement || selectedElement instanceof CircleElement;
7509
7771
  return /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -7535,6 +7797,13 @@ function PropertiesPanelContainer({
7535
7797
  updateElement
7536
7798
  }
7537
7799
  ),
7800
+ (isVideo || isImage) && /* @__PURE__ */ jsx(
7801
+ ColorFilterPropsPanel,
7802
+ {
7803
+ selectedElement,
7804
+ updateElement
7805
+ }
7806
+ ),
7538
7807
  isText && /* @__PURE__ */ jsx(
7539
7808
  TextEffects,
7540
7809
  {
@@ -8110,6 +8379,8 @@ export {
8110
8379
  ElementTextEffect2 as ElementTextEffect,
8111
8380
  ElementUpdater,
8112
8381
  ElementValidator,
8382
+ EmojiElement2 as EmojiElement,
8383
+ EmojiPanel,
8113
8384
  INITIAL_TIMELINE_DATA,
8114
8385
  IconElement,
8115
8386
  ImageElement2 as ImageElement,