@twick/studio 0.15.13 → 0.15.15
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/README.md +2 -2
- package/dist/components/container/captions-panel-container.d.ts +1 -0
- package/dist/components/container/element-panel-container.d.ts +1 -1
- package/dist/components/container/properties-panel-container.d.ts +7 -7
- package/dist/components/panel/captions-panel.d.ts +46 -0
- package/dist/components/panel/circle-panel.d.ts +1 -1
- package/dist/components/panel/rect-panel.d.ts +1 -1
- package/dist/components/panel/text-panel.d.ts +1 -1
- package/dist/components/properties/{subtitlte-prop.d.ts → caption-prop.d.ts} +4 -4
- package/dist/components/properties/generate-captions.d.ts +13 -0
- package/dist/components/properties/property-row.d.ts +10 -0
- package/dist/components/properties/text-props.d.ts +3 -0
- package/dist/helpers/generate-captions.service.d.ts +21 -0
- package/dist/helpers/volume-db.d.ts +22 -0
- package/dist/hooks/use-captions-panel.d.ts +13 -0
- package/dist/hooks/use-generate-captions.d.ts +9 -0
- package/dist/hooks/use-studio-operation.d.ts +4 -4
- package/dist/hooks/use-text-panel.d.ts +6 -0
- package/dist/index.d.ts +10 -13
- package/dist/index.js +1209 -1046
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1194 -1034
- package/dist/index.mjs.map +1 -1
- package/dist/studio.css +521 -162
- package/dist/types/index.d.ts +18 -16
- package/package.json +14 -12
- package/dist/components/container/icon-panel-container.d.ts +0 -3
- package/dist/components/container/subtitles-panel-container.d.ts +0 -1
- package/dist/components/panel/icon-panel.d.ts +0 -4
- package/dist/components/panel/subtitles-panel.d.ts +0 -46
- package/dist/components/properties/generate-subtitles.d.ts +0 -13
- package/dist/helpers/generate-subtitles.service.d.ts +0 -21
- package/dist/hooks/use-generate-subtitles.d.ts +0 -9
- package/dist/hooks/use-icon-panel.d.ts +0 -24
- package/dist/hooks/use-subtitles-panel.d.ts +0 -13
package/dist/index.mjs
CHANGED
|
@@ -3,9 +3,9 @@ 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, createContext, useContext, useCallback, useMemo } from "react";
|
|
6
|
-
import { useTimelineContext, TrackElement, Track, ImageElement, AudioElement, VideoElement, TextElement,
|
|
7
|
-
import { AudioElement as AudioElement2, CAPTION_COLOR, CAPTION_FONT, CAPTION_STYLE as CAPTION_STYLE2, CAPTION_STYLE_OPTIONS, 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
|
|
8
|
-
import VideoEditor, { useEditorManager, BrowserMediaManager, AVAILABLE_TEXT_FONTS, TEXT_EFFECTS, ANIMATIONS } from "@twick/video-editor";
|
|
6
|
+
import { useTimelineContext, TrackElement, Track, ImageElement, AudioElement, VideoElement, TextElement, RectElement, CircleElement, CAPTION_STYLE, CaptionElement, ElementTextEffect, ElementAnimation, PLAYER_STATE } from "@twick/timeline";
|
|
7
|
+
import { AudioElement as AudioElement2, CAPTION_COLOR, CAPTION_FONT, CAPTION_STYLE as CAPTION_STYLE2, CAPTION_STYLE_OPTIONS, 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, 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
|
+
import VideoEditor, { useEditorManager, BrowserMediaManager, TIMELINE_DROP_MEDIA_TYPE, 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";
|
|
@@ -114,49 +114,103 @@ const createLucideIcon = (iconName, iconNode) => {
|
|
|
114
114
|
* This source code is licensed under the ISC license.
|
|
115
115
|
* See the LICENSE file in the root directory of this source tree.
|
|
116
116
|
*/
|
|
117
|
-
const __iconNode$
|
|
118
|
-
["
|
|
119
|
-
["path", { d: "
|
|
117
|
+
const __iconNode$y = [
|
|
118
|
+
["path", { d: "M17 12H7", key: "16if0g" }],
|
|
119
|
+
["path", { d: "M19 18H5", key: "18s9l3" }],
|
|
120
|
+
["path", { d: "M21 6H3", key: "1jwq7v" }]
|
|
120
121
|
];
|
|
121
|
-
const
|
|
122
|
+
const AlignCenter = createLucideIcon("align-center", __iconNode$y);
|
|
122
123
|
/**
|
|
123
124
|
* @license lucide-react v0.511.0 - ISC
|
|
124
125
|
*
|
|
125
126
|
* This source code is licensed under the ISC license.
|
|
126
127
|
* See the LICENSE file in the root directory of this source tree.
|
|
127
128
|
*/
|
|
128
|
-
const __iconNode$
|
|
129
|
+
const __iconNode$x = [
|
|
130
|
+
["path", { d: "M15 12H3", key: "6jk70r" }],
|
|
131
|
+
["path", { d: "M17 18H3", key: "1amg6g" }],
|
|
132
|
+
["path", { d: "M21 6H3", key: "1jwq7v" }]
|
|
133
|
+
];
|
|
134
|
+
const AlignLeft = createLucideIcon("align-left", __iconNode$x);
|
|
135
|
+
/**
|
|
136
|
+
* @license lucide-react v0.511.0 - ISC
|
|
137
|
+
*
|
|
138
|
+
* This source code is licensed under the ISC license.
|
|
139
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
140
|
+
*/
|
|
141
|
+
const __iconNode$w = [
|
|
142
|
+
["path", { d: "M21 12H9", key: "dn1m92" }],
|
|
143
|
+
["path", { d: "M21 18H7", key: "1ygte8" }],
|
|
144
|
+
["path", { d: "M21 6H3", key: "1jwq7v" }]
|
|
145
|
+
];
|
|
146
|
+
const AlignRight = createLucideIcon("align-right", __iconNode$w);
|
|
147
|
+
/**
|
|
148
|
+
* @license lucide-react v0.511.0 - ISC
|
|
149
|
+
*
|
|
150
|
+
* This source code is licensed under the ISC license.
|
|
151
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
152
|
+
*/
|
|
153
|
+
const __iconNode$v = [
|
|
154
|
+
[
|
|
155
|
+
"path",
|
|
156
|
+
{ 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
|
+
];
|
|
159
|
+
const Bold = createLucideIcon("bold", __iconNode$v);
|
|
160
|
+
/**
|
|
161
|
+
* @license lucide-react v0.511.0 - ISC
|
|
162
|
+
*
|
|
163
|
+
* This source code is licensed under the ISC license.
|
|
164
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
165
|
+
*/
|
|
166
|
+
const __iconNode$u = [["path", { d: "m6 9 6 6 6-6", key: "qrunsl" }]];
|
|
167
|
+
const ChevronDown = createLucideIcon("chevron-down", __iconNode$u);
|
|
168
|
+
/**
|
|
169
|
+
* @license lucide-react v0.511.0 - ISC
|
|
170
|
+
*
|
|
171
|
+
* This source code is licensed under the ISC license.
|
|
172
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
173
|
+
*/
|
|
174
|
+
const __iconNode$t = [["path", { d: "m9 18 6-6-6-6", key: "mthhwq" }]];
|
|
175
|
+
const ChevronRight = createLucideIcon("chevron-right", __iconNode$t);
|
|
176
|
+
/**
|
|
177
|
+
* @license lucide-react v0.511.0 - ISC
|
|
178
|
+
*
|
|
179
|
+
* This source code is licensed under the ISC license.
|
|
180
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
181
|
+
*/
|
|
182
|
+
const __iconNode$s = [
|
|
129
183
|
["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
|
|
130
184
|
["path", { d: "m9 12 2 2 4-4", key: "dzmm74" }]
|
|
131
185
|
];
|
|
132
|
-
const CircleCheck = createLucideIcon("circle-check", __iconNode$
|
|
186
|
+
const CircleCheck = createLucideIcon("circle-check", __iconNode$s);
|
|
133
187
|
/**
|
|
134
188
|
* @license lucide-react v0.511.0 - ISC
|
|
135
189
|
*
|
|
136
190
|
* This source code is licensed under the ISC license.
|
|
137
191
|
* See the LICENSE file in the root directory of this source tree.
|
|
138
192
|
*/
|
|
139
|
-
const __iconNode$
|
|
193
|
+
const __iconNode$r = [
|
|
140
194
|
["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
|
|
141
195
|
["path", { d: "m15 9-6 6", key: "1uzhvr" }],
|
|
142
196
|
["path", { d: "m9 9 6 6", key: "z0biqf" }]
|
|
143
197
|
];
|
|
144
|
-
const CircleX = createLucideIcon("circle-x", __iconNode$
|
|
198
|
+
const CircleX = createLucideIcon("circle-x", __iconNode$r);
|
|
145
199
|
/**
|
|
146
200
|
* @license lucide-react v0.511.0 - ISC
|
|
147
201
|
*
|
|
148
202
|
* This source code is licensed under the ISC license.
|
|
149
203
|
* See the LICENSE file in the root directory of this source tree.
|
|
150
204
|
*/
|
|
151
|
-
const __iconNode$
|
|
152
|
-
const Circle = createLucideIcon("circle", __iconNode$
|
|
205
|
+
const __iconNode$q = [["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }]];
|
|
206
|
+
const Circle = createLucideIcon("circle", __iconNode$q);
|
|
153
207
|
/**
|
|
154
208
|
* @license lucide-react v0.511.0 - ISC
|
|
155
209
|
*
|
|
156
210
|
* This source code is licensed under the ISC license.
|
|
157
211
|
* See the LICENSE file in the root directory of this source tree.
|
|
158
212
|
*/
|
|
159
|
-
const __iconNode$
|
|
213
|
+
const __iconNode$p = [
|
|
160
214
|
[
|
|
161
215
|
"path",
|
|
162
216
|
{ 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" }
|
|
@@ -165,139 +219,172 @@ const __iconNode$q = [
|
|
|
165
219
|
["path", { d: "m12.4 3.4 3.1 4", key: "6hsd6n" }],
|
|
166
220
|
["path", { d: "M3 11h18v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2Z", key: "ltgou9" }]
|
|
167
221
|
];
|
|
168
|
-
const Clapperboard = createLucideIcon("clapperboard", __iconNode$
|
|
222
|
+
const Clapperboard = createLucideIcon("clapperboard", __iconNode$p);
|
|
169
223
|
/**
|
|
170
224
|
* @license lucide-react v0.511.0 - ISC
|
|
171
225
|
*
|
|
172
226
|
* This source code is licensed under the ISC license.
|
|
173
227
|
* See the LICENSE file in the root directory of this source tree.
|
|
174
228
|
*/
|
|
175
|
-
const __iconNode$
|
|
229
|
+
const __iconNode$o = [
|
|
176
230
|
["path", { d: "M12 15V3", key: "m9g1x1" }],
|
|
177
231
|
["path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4", key: "ih7n3h" }],
|
|
178
232
|
["path", { d: "m7 10 5 5 5-5", key: "brsn70" }]
|
|
179
233
|
];
|
|
180
|
-
const Download = createLucideIcon("download", __iconNode$
|
|
234
|
+
const Download = createLucideIcon("download", __iconNode$o);
|
|
181
235
|
/**
|
|
182
236
|
* @license lucide-react v0.511.0 - ISC
|
|
183
237
|
*
|
|
184
238
|
* This source code is licensed under the ISC license.
|
|
185
239
|
* See the LICENSE file in the root directory of this source tree.
|
|
186
240
|
*/
|
|
187
|
-
const __iconNode$
|
|
241
|
+
const __iconNode$n = [
|
|
188
242
|
["path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z", key: "1rqfz7" }],
|
|
189
243
|
["path", { d: "M14 2v4a2 2 0 0 0 2 2h4", key: "tnqrlb" }]
|
|
190
244
|
];
|
|
191
|
-
const File = createLucideIcon("file", __iconNode$
|
|
245
|
+
const File = createLucideIcon("file", __iconNode$n);
|
|
192
246
|
/**
|
|
193
247
|
* @license lucide-react v0.511.0 - ISC
|
|
194
248
|
*
|
|
195
249
|
* This source code is licensed under the ISC license.
|
|
196
250
|
* See the LICENSE file in the root directory of this source tree.
|
|
197
251
|
*/
|
|
198
|
-
const __iconNode$
|
|
252
|
+
const __iconNode$m = [
|
|
199
253
|
["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2", key: "1m3agn" }],
|
|
200
254
|
["circle", { cx: "9", cy: "9", r: "2", key: "af1f0g" }],
|
|
201
255
|
["path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21", key: "1xmnt7" }]
|
|
202
256
|
];
|
|
203
|
-
const Image = createLucideIcon("image", __iconNode$
|
|
257
|
+
const Image = createLucideIcon("image", __iconNode$m);
|
|
204
258
|
/**
|
|
205
259
|
* @license lucide-react v0.511.0 - ISC
|
|
206
260
|
*
|
|
207
261
|
* This source code is licensed under the ISC license.
|
|
208
262
|
* See the LICENSE file in the root directory of this source tree.
|
|
209
263
|
*/
|
|
210
|
-
const __iconNode$
|
|
211
|
-
["
|
|
264
|
+
const __iconNode$l = [
|
|
265
|
+
["line", { x1: "19", x2: "10", y1: "4", y2: "4", key: "15jd3p" }],
|
|
266
|
+
["line", { x1: "14", x2: "5", y1: "20", y2: "20", key: "bu0au3" }],
|
|
267
|
+
["line", { x1: "15", x2: "9", y1: "4", y2: "20", key: "uljnxc" }]
|
|
212
268
|
];
|
|
213
|
-
const
|
|
269
|
+
const Italic = createLucideIcon("italic", __iconNode$l);
|
|
214
270
|
/**
|
|
215
271
|
* @license lucide-react v0.511.0 - ISC
|
|
216
272
|
*
|
|
217
273
|
* This source code is licensed under the ISC license.
|
|
218
274
|
* See the LICENSE file in the root directory of this source tree.
|
|
219
275
|
*/
|
|
220
|
-
const __iconNode$
|
|
221
|
-
const LoaderCircle = createLucideIcon("loader-circle", __iconNode$
|
|
276
|
+
const __iconNode$k = [["path", { d: "M21 12a9 9 0 1 1-6.219-8.56", key: "13zald" }]];
|
|
277
|
+
const LoaderCircle = createLucideIcon("loader-circle", __iconNode$k);
|
|
222
278
|
/**
|
|
223
279
|
* @license lucide-react v0.511.0 - ISC
|
|
224
280
|
*
|
|
225
281
|
* This source code is licensed under the ISC license.
|
|
226
282
|
* See the LICENSE file in the root directory of this source tree.
|
|
227
283
|
*/
|
|
228
|
-
const __iconNode$
|
|
284
|
+
const __iconNode$j = [
|
|
229
285
|
["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" }]
|
|
230
286
|
];
|
|
231
|
-
const MessageSquare = createLucideIcon("message-square", __iconNode$
|
|
287
|
+
const MessageSquare = createLucideIcon("message-square", __iconNode$j);
|
|
232
288
|
/**
|
|
233
289
|
* @license lucide-react v0.511.0 - ISC
|
|
234
290
|
*
|
|
235
291
|
* This source code is licensed under the ISC license.
|
|
236
292
|
* See the LICENSE file in the root directory of this source tree.
|
|
237
293
|
*/
|
|
238
|
-
const __iconNode$
|
|
294
|
+
const __iconNode$i = [
|
|
295
|
+
["circle", { cx: "8", cy: "18", r: "4", key: "1fc0mg" }],
|
|
296
|
+
["path", { d: "M12 18V2l7 4", key: "g04rme" }]
|
|
297
|
+
];
|
|
298
|
+
const Music2 = createLucideIcon("music-2", __iconNode$i);
|
|
299
|
+
/**
|
|
300
|
+
* @license lucide-react v0.511.0 - ISC
|
|
301
|
+
*
|
|
302
|
+
* This source code is licensed under the ISC license.
|
|
303
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
304
|
+
*/
|
|
305
|
+
const __iconNode$h = [
|
|
239
306
|
["path", { d: "M9 18V5l12-2v13", key: "1jmyc2" }],
|
|
240
307
|
["circle", { cx: "6", cy: "18", r: "3", key: "fqmcym" }],
|
|
241
308
|
["circle", { cx: "18", cy: "16", r: "3", key: "1hluhg" }]
|
|
242
309
|
];
|
|
243
|
-
const Music = createLucideIcon("music", __iconNode$
|
|
310
|
+
const Music = createLucideIcon("music", __iconNode$h);
|
|
244
311
|
/**
|
|
245
312
|
* @license lucide-react v0.511.0 - ISC
|
|
246
313
|
*
|
|
247
314
|
* This source code is licensed under the ISC license.
|
|
248
315
|
* See the LICENSE file in the root directory of this source tree.
|
|
249
316
|
*/
|
|
250
|
-
const __iconNode$
|
|
317
|
+
const __iconNode$g = [
|
|
251
318
|
["rect", { x: "14", y: "4", width: "4", height: "16", rx: "1", key: "zuxfzm" }],
|
|
252
319
|
["rect", { x: "6", y: "4", width: "4", height: "16", rx: "1", key: "1okwgv" }]
|
|
253
320
|
];
|
|
254
|
-
const Pause = createLucideIcon("pause", __iconNode$
|
|
321
|
+
const Pause = createLucideIcon("pause", __iconNode$g);
|
|
255
322
|
/**
|
|
256
323
|
* @license lucide-react v0.511.0 - ISC
|
|
257
324
|
*
|
|
258
325
|
* This source code is licensed under the ISC license.
|
|
259
326
|
* See the LICENSE file in the root directory of this source tree.
|
|
260
327
|
*/
|
|
261
|
-
const __iconNode$
|
|
262
|
-
const Play = createLucideIcon("play", __iconNode$
|
|
328
|
+
const __iconNode$f = [["polygon", { points: "6 3 20 12 6 21 6 3", key: "1oa8hb" }]];
|
|
329
|
+
const Play = createLucideIcon("play", __iconNode$f);
|
|
263
330
|
/**
|
|
264
331
|
* @license lucide-react v0.511.0 - ISC
|
|
265
332
|
*
|
|
266
333
|
* This source code is licensed under the ISC license.
|
|
267
334
|
* See the LICENSE file in the root directory of this source tree.
|
|
268
335
|
*/
|
|
269
|
-
const __iconNode$
|
|
336
|
+
const __iconNode$e = [
|
|
270
337
|
["path", { d: "M5 12h14", key: "1ays0h" }],
|
|
271
338
|
["path", { d: "M12 5v14", key: "s699le" }]
|
|
272
339
|
];
|
|
273
|
-
const Plus = createLucideIcon("plus", __iconNode$
|
|
340
|
+
const Plus = createLucideIcon("plus", __iconNode$e);
|
|
274
341
|
/**
|
|
275
342
|
* @license lucide-react v0.511.0 - ISC
|
|
276
343
|
*
|
|
277
344
|
* This source code is licensed under the ISC license.
|
|
278
345
|
* See the LICENSE file in the root directory of this source tree.
|
|
279
346
|
*/
|
|
280
|
-
const __iconNode$
|
|
347
|
+
const __iconNode$d = [
|
|
281
348
|
["rect", { width: "20", height: "12", x: "2", y: "6", rx: "2", key: "9lu3g6" }]
|
|
282
349
|
];
|
|
283
|
-
const RectangleHorizontal = createLucideIcon("rectangle-horizontal", __iconNode$
|
|
350
|
+
const RectangleHorizontal = createLucideIcon("rectangle-horizontal", __iconNode$d);
|
|
284
351
|
/**
|
|
285
352
|
* @license lucide-react v0.511.0 - ISC
|
|
286
353
|
*
|
|
287
354
|
* This source code is licensed under the ISC license.
|
|
288
355
|
* See the LICENSE file in the root directory of this source tree.
|
|
289
356
|
*/
|
|
290
|
-
const __iconNode$
|
|
357
|
+
const __iconNode$c = [
|
|
291
358
|
["rect", { width: "12", height: "20", x: "6", y: "2", rx: "2", key: "1oxtiu" }]
|
|
292
359
|
];
|
|
293
|
-
const RectangleVertical = createLucideIcon("rectangle-vertical", __iconNode$
|
|
360
|
+
const RectangleVertical = createLucideIcon("rectangle-vertical", __iconNode$c);
|
|
294
361
|
/**
|
|
295
362
|
* @license lucide-react v0.511.0 - ISC
|
|
296
363
|
*
|
|
297
364
|
* This source code is licensed under the ISC license.
|
|
298
365
|
* See the LICENSE file in the root directory of this source tree.
|
|
299
366
|
*/
|
|
300
|
-
const __iconNode$
|
|
367
|
+
const __iconNode$b = [
|
|
368
|
+
[
|
|
369
|
+
"path",
|
|
370
|
+
{
|
|
371
|
+
d: "M21.3 15.3a2.4 2.4 0 0 1 0 3.4l-2.6 2.6a2.4 2.4 0 0 1-3.4 0L2.7 8.7a2.41 2.41 0 0 1 0-3.4l2.6-2.6a2.41 2.41 0 0 1 3.4 0Z",
|
|
372
|
+
key: "icamh8"
|
|
373
|
+
}
|
|
374
|
+
],
|
|
375
|
+
["path", { d: "m14.5 12.5 2-2", key: "inckbg" }],
|
|
376
|
+
["path", { d: "m11.5 9.5 2-2", key: "fmmyf7" }],
|
|
377
|
+
["path", { d: "m8.5 6.5 2-2", key: "vc6u1g" }],
|
|
378
|
+
["path", { d: "m17.5 15.5 2-2", key: "wo5hmg" }]
|
|
379
|
+
];
|
|
380
|
+
const Ruler = createLucideIcon("ruler", __iconNode$b);
|
|
381
|
+
/**
|
|
382
|
+
* @license lucide-react v0.511.0 - ISC
|
|
383
|
+
*
|
|
384
|
+
* This source code is licensed under the ISC license.
|
|
385
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
386
|
+
*/
|
|
387
|
+
const __iconNode$a = [
|
|
301
388
|
[
|
|
302
389
|
"path",
|
|
303
390
|
{
|
|
@@ -308,56 +395,28 @@ const __iconNode$d = [
|
|
|
308
395
|
["path", { d: "M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7", key: "1ydtos" }],
|
|
309
396
|
["path", { d: "M7 3v4a1 1 0 0 0 1 1h7", key: "t51u73" }]
|
|
310
397
|
];
|
|
311
|
-
const Save = createLucideIcon("save", __iconNode$
|
|
398
|
+
const Save = createLucideIcon("save", __iconNode$a);
|
|
312
399
|
/**
|
|
313
400
|
* @license lucide-react v0.511.0 - ISC
|
|
314
401
|
*
|
|
315
402
|
* This source code is licensed under the ISC license.
|
|
316
403
|
* See the LICENSE file in the root directory of this source tree.
|
|
317
404
|
*/
|
|
318
|
-
const __iconNode$
|
|
405
|
+
const __iconNode$9 = [
|
|
319
406
|
["circle", { cx: "6", cy: "6", r: "3", key: "1lh9wr" }],
|
|
320
407
|
["path", { d: "M8.12 8.12 12 12", key: "1alkpv" }],
|
|
321
408
|
["path", { d: "M20 4 8.12 15.88", key: "xgtan2" }],
|
|
322
409
|
["circle", { cx: "6", cy: "18", r: "3", key: "fqmcym" }],
|
|
323
410
|
["path", { d: "M14.8 14.8 20 20", key: "ptml3r" }]
|
|
324
411
|
];
|
|
325
|
-
const Scissors = createLucideIcon("scissors", __iconNode$
|
|
326
|
-
/**
|
|
327
|
-
* @license lucide-react v0.511.0 - ISC
|
|
328
|
-
*
|
|
329
|
-
* This source code is licensed under the ISC license.
|
|
330
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
331
|
-
*/
|
|
332
|
-
const __iconNode$b = [
|
|
333
|
-
["path", { d: "m21 21-4.34-4.34", key: "14j7rj" }],
|
|
334
|
-
["circle", { cx: "11", cy: "11", r: "8", key: "4ej97u" }]
|
|
335
|
-
];
|
|
336
|
-
const Search = createLucideIcon("search", __iconNode$b);
|
|
337
|
-
/**
|
|
338
|
-
* @license lucide-react v0.511.0 - ISC
|
|
339
|
-
*
|
|
340
|
-
* This source code is licensed under the ISC license.
|
|
341
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
342
|
-
*/
|
|
343
|
-
const __iconNode$a = [
|
|
344
|
-
[
|
|
345
|
-
"path",
|
|
346
|
-
{
|
|
347
|
-
d: "M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z",
|
|
348
|
-
key: "1qme2f"
|
|
349
|
-
}
|
|
350
|
-
],
|
|
351
|
-
["circle", { cx: "12", cy: "12", r: "3", key: "1v7zrd" }]
|
|
352
|
-
];
|
|
353
|
-
const Settings = createLucideIcon("settings", __iconNode$a);
|
|
412
|
+
const Scissors = createLucideIcon("scissors", __iconNode$9);
|
|
354
413
|
/**
|
|
355
414
|
* @license lucide-react v0.511.0 - ISC
|
|
356
415
|
*
|
|
357
416
|
* This source code is licensed under the ISC license.
|
|
358
417
|
* See the LICENSE file in the root directory of this source tree.
|
|
359
418
|
*/
|
|
360
|
-
const __iconNode$
|
|
419
|
+
const __iconNode$8 = [
|
|
361
420
|
[
|
|
362
421
|
"path",
|
|
363
422
|
{
|
|
@@ -370,62 +429,62 @@ const __iconNode$9 = [
|
|
|
370
429
|
["path", { d: "M4 17v2", key: "vumght" }],
|
|
371
430
|
["path", { d: "M5 18H3", key: "zchphs" }]
|
|
372
431
|
];
|
|
373
|
-
const Sparkles = createLucideIcon("sparkles", __iconNode$
|
|
432
|
+
const Sparkles = createLucideIcon("sparkles", __iconNode$8);
|
|
374
433
|
/**
|
|
375
434
|
* @license lucide-react v0.511.0 - ISC
|
|
376
435
|
*
|
|
377
436
|
* This source code is licensed under the ISC license.
|
|
378
437
|
* See the LICENSE file in the root directory of this source tree.
|
|
379
438
|
*/
|
|
380
|
-
const __iconNode$
|
|
439
|
+
const __iconNode$7 = [
|
|
381
440
|
["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", key: "afitv7" }]
|
|
382
441
|
];
|
|
383
|
-
const Square = createLucideIcon("square", __iconNode$
|
|
442
|
+
const Square = createLucideIcon("square", __iconNode$7);
|
|
384
443
|
/**
|
|
385
444
|
* @license lucide-react v0.511.0 - ISC
|
|
386
445
|
*
|
|
387
446
|
* This source code is licensed under the ISC license.
|
|
388
447
|
* See the LICENSE file in the root directory of this source tree.
|
|
389
448
|
*/
|
|
390
|
-
const __iconNode$
|
|
449
|
+
const __iconNode$6 = [
|
|
391
450
|
["path", { d: "M3 6h18", key: "d0wm0j" }],
|
|
392
451
|
["path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6", key: "4alrt4" }],
|
|
393
452
|
["path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2", key: "v07s0e" }],
|
|
394
453
|
["line", { x1: "10", x2: "10", y1: "11", y2: "17", key: "1uufr5" }],
|
|
395
454
|
["line", { x1: "14", x2: "14", y1: "11", y2: "17", key: "xtxkd" }]
|
|
396
455
|
];
|
|
397
|
-
const Trash2 = createLucideIcon("trash-2", __iconNode$
|
|
456
|
+
const Trash2 = createLucideIcon("trash-2", __iconNode$6);
|
|
398
457
|
/**
|
|
399
458
|
* @license lucide-react v0.511.0 - ISC
|
|
400
459
|
*
|
|
401
460
|
* This source code is licensed under the ISC license.
|
|
402
461
|
* See the LICENSE file in the root directory of this source tree.
|
|
403
462
|
*/
|
|
404
|
-
const __iconNode$
|
|
463
|
+
const __iconNode$5 = [
|
|
405
464
|
["path", { d: "M12 4v16", key: "1654pz" }],
|
|
406
465
|
["path", { d: "M4 7V5a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v2", key: "e0r10z" }],
|
|
407
466
|
["path", { d: "M9 20h6", key: "s66wpe" }]
|
|
408
467
|
];
|
|
409
|
-
const Type = createLucideIcon("type", __iconNode$
|
|
468
|
+
const Type = createLucideIcon("type", __iconNode$5);
|
|
410
469
|
/**
|
|
411
470
|
* @license lucide-react v0.511.0 - ISC
|
|
412
471
|
*
|
|
413
472
|
* This source code is licensed under the ISC license.
|
|
414
473
|
* See the LICENSE file in the root directory of this source tree.
|
|
415
474
|
*/
|
|
416
|
-
const __iconNode$
|
|
475
|
+
const __iconNode$4 = [
|
|
417
476
|
["path", { d: "M12 3v12", key: "1x0j5s" }],
|
|
418
477
|
["path", { d: "m17 8-5-5-5 5", key: "7q97r8" }],
|
|
419
478
|
["path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4", key: "ih7n3h" }]
|
|
420
479
|
];
|
|
421
|
-
const Upload = createLucideIcon("upload", __iconNode$
|
|
480
|
+
const Upload = createLucideIcon("upload", __iconNode$4);
|
|
422
481
|
/**
|
|
423
482
|
* @license lucide-react v0.511.0 - ISC
|
|
424
483
|
*
|
|
425
484
|
* This source code is licensed under the ISC license.
|
|
426
485
|
* See the LICENSE file in the root directory of this source tree.
|
|
427
486
|
*/
|
|
428
|
-
const __iconNode$
|
|
487
|
+
const __iconNode$3 = [
|
|
429
488
|
[
|
|
430
489
|
"path",
|
|
431
490
|
{
|
|
@@ -435,14 +494,14 @@ const __iconNode$4 = [
|
|
|
435
494
|
],
|
|
436
495
|
["rect", { x: "2", y: "6", width: "14", height: "12", rx: "2", key: "158x01" }]
|
|
437
496
|
];
|
|
438
|
-
const Video = createLucideIcon("video", __iconNode$
|
|
497
|
+
const Video = createLucideIcon("video", __iconNode$3);
|
|
439
498
|
/**
|
|
440
499
|
* @license lucide-react v0.511.0 - ISC
|
|
441
500
|
*
|
|
442
501
|
* This source code is licensed under the ISC license.
|
|
443
502
|
* See the LICENSE file in the root directory of this source tree.
|
|
444
503
|
*/
|
|
445
|
-
const __iconNode$
|
|
504
|
+
const __iconNode$2 = [
|
|
446
505
|
[
|
|
447
506
|
"path",
|
|
448
507
|
{
|
|
@@ -453,14 +512,14 @@ const __iconNode$3 = [
|
|
|
453
512
|
["path", { d: "M16 9a5 5 0 0 1 0 6", key: "1q6k2b" }],
|
|
454
513
|
["path", { d: "M19.364 18.364a9 9 0 0 0 0-12.728", key: "ijwkga" }]
|
|
455
514
|
];
|
|
456
|
-
const Volume2 = createLucideIcon("volume-2", __iconNode$
|
|
515
|
+
const Volume2 = createLucideIcon("volume-2", __iconNode$2);
|
|
457
516
|
/**
|
|
458
517
|
* @license lucide-react v0.511.0 - ISC
|
|
459
518
|
*
|
|
460
519
|
* This source code is licensed under the ISC license.
|
|
461
520
|
* See the LICENSE file in the root directory of this source tree.
|
|
462
521
|
*/
|
|
463
|
-
const __iconNode$
|
|
522
|
+
const __iconNode$1 = [
|
|
464
523
|
[
|
|
465
524
|
"path",
|
|
466
525
|
{
|
|
@@ -471,14 +530,14 @@ const __iconNode$2 = [
|
|
|
471
530
|
["line", { x1: "22", x2: "16", y1: "9", y2: "15", key: "1ewh16" }],
|
|
472
531
|
["line", { x1: "16", x2: "22", y1: "9", y2: "15", key: "5ykzw1" }]
|
|
473
532
|
];
|
|
474
|
-
const VolumeX = createLucideIcon("volume-x", __iconNode$
|
|
533
|
+
const VolumeX = createLucideIcon("volume-x", __iconNode$1);
|
|
475
534
|
/**
|
|
476
535
|
* @license lucide-react v0.511.0 - ISC
|
|
477
536
|
*
|
|
478
537
|
* This source code is licensed under the ISC license.
|
|
479
538
|
* See the LICENSE file in the root directory of this source tree.
|
|
480
539
|
*/
|
|
481
|
-
const __iconNode
|
|
540
|
+
const __iconNode = [
|
|
482
541
|
[
|
|
483
542
|
"path",
|
|
484
543
|
{
|
|
@@ -494,41 +553,22 @@ const __iconNode$1 = [
|
|
|
494
553
|
["path", { d: "M21 16h-4", key: "1cnmox" }],
|
|
495
554
|
["path", { d: "M11 3H9", key: "1obp7u" }]
|
|
496
555
|
];
|
|
497
|
-
const WandSparkles = createLucideIcon("wand-sparkles", __iconNode
|
|
498
|
-
/**
|
|
499
|
-
* @license lucide-react v0.511.0 - ISC
|
|
500
|
-
*
|
|
501
|
-
* This source code is licensed under the ISC license.
|
|
502
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
503
|
-
*/
|
|
504
|
-
const __iconNode = [
|
|
505
|
-
[
|
|
506
|
-
"path",
|
|
507
|
-
{
|
|
508
|
-
d: "M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z",
|
|
509
|
-
key: "1xq2db"
|
|
510
|
-
}
|
|
511
|
-
]
|
|
512
|
-
];
|
|
513
|
-
const Zap = createLucideIcon("zap", __iconNode);
|
|
556
|
+
const WandSparkles = createLucideIcon("wand-sparkles", __iconNode);
|
|
514
557
|
const toolCategories = [
|
|
515
|
-
{ id: "video", name: "Video", icon: "Video", description: "
|
|
516
|
-
{ id: "image", name: "Image", icon: "Image", description: "
|
|
517
|
-
{ id: "audio", name: "Audio", icon: "Audio", description: "
|
|
518
|
-
{ id: "text", name: "Text", icon: "Type", description: "Add text elements"
|
|
519
|
-
{ id: "
|
|
520
|
-
{ id: "
|
|
521
|
-
{ id: "
|
|
522
|
-
{ id: "subtitle", name: "Subtitles", icon: "MessageSquare", description: "Manage subtitles", shortcut: "S" }
|
|
558
|
+
{ id: "video", name: "Video", icon: "Video", description: "Add a video element" },
|
|
559
|
+
{ id: "image", name: "Image", icon: "Image", description: "Add an image element" },
|
|
560
|
+
{ id: "audio", name: "Audio", icon: "Audio", description: "Add an audio element" },
|
|
561
|
+
{ id: "text", name: "Text", icon: "Type", description: "Add text elements" },
|
|
562
|
+
{ id: "circle", name: "Circle", icon: "Circle", description: "Add a circle element" },
|
|
563
|
+
{ id: "rect", name: "Rect", icon: "Rect", description: "Add a rectangle element" },
|
|
564
|
+
{ id: "caption", name: "Caption", icon: "MessageSquare", description: "Manage captions" }
|
|
523
565
|
];
|
|
524
|
-
const getIcon
|
|
566
|
+
const getIcon = (iconName) => {
|
|
525
567
|
switch (iconName) {
|
|
526
568
|
case "Plus":
|
|
527
569
|
return Plus;
|
|
528
570
|
case "Type":
|
|
529
571
|
return Type;
|
|
530
|
-
case "Icon":
|
|
531
|
-
return Infinity;
|
|
532
572
|
case "Upload":
|
|
533
573
|
return Upload;
|
|
534
574
|
case "Square":
|
|
@@ -554,14 +594,16 @@ function Toolbar({ selectedTool, setSelectedTool }) {
|
|
|
554
594
|
setSelectedTool(toolId);
|
|
555
595
|
};
|
|
556
596
|
return /* @__PURE__ */ jsx("div", { className: "sidebar", children: toolCategories.map((tool) => {
|
|
557
|
-
const Icon2 = getIcon
|
|
597
|
+
const Icon2 = getIcon(tool.icon);
|
|
558
598
|
const isSelected = selectedTool === tool.id;
|
|
599
|
+
const tooltipText = `${tool.name}${tool.shortcut ? ` (${tool.shortcut})` : ""}`;
|
|
559
600
|
return /* @__PURE__ */ jsxs(
|
|
560
601
|
"div",
|
|
561
602
|
{
|
|
562
603
|
onClick: () => handleToolSelect(tool.id),
|
|
563
604
|
className: `toolbar-btn ${isSelected ? "active" : ""}`,
|
|
564
|
-
title:
|
|
605
|
+
title: tooltipText,
|
|
606
|
+
"data-tooltip": tooltipText,
|
|
565
607
|
children: [
|
|
566
608
|
/* @__PURE__ */ jsx(Icon2, { className: "icon-sm" }),
|
|
567
609
|
/* @__PURE__ */ jsx("span", { className: "toolbar-label", children: tool.name })
|
|
@@ -608,28 +650,29 @@ const StudioHeader = ({
|
|
|
608
650
|
return /* @__PURE__ */ jsxs("header", { className: "header", children: [
|
|
609
651
|
/* @__PURE__ */ jsxs("div", { className: "flex-container", children: [
|
|
610
652
|
/* @__PURE__ */ jsx(Clapperboard, { className: "icon-lg accent-purple" }),
|
|
611
|
-
/* @__PURE__ */ jsx("h1", { className: "text-gradient", children: "Twick Studio" })
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
653
|
+
/* @__PURE__ */ jsx("h1", { className: "text-gradient", children: "Twick Studio" }),
|
|
654
|
+
/* @__PURE__ */ jsx("div", { className: "header-separator" }),
|
|
655
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-container", style: { gap: "0.5rem" }, children: [
|
|
656
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm opacity-80", children: "Orientation" }),
|
|
657
|
+
/* @__PURE__ */ jsx(
|
|
658
|
+
"button",
|
|
659
|
+
{
|
|
660
|
+
className: `btn-ghost ${orientation === "vertical" ? "btn-primary" : ""}`,
|
|
661
|
+
title: "Portrait (720×1280)",
|
|
662
|
+
onClick: () => handleOrientationChange("vertical"),
|
|
663
|
+
children: /* @__PURE__ */ jsx(RectangleVertical, { className: "icon-sm" })
|
|
664
|
+
}
|
|
665
|
+
),
|
|
666
|
+
/* @__PURE__ */ jsx(
|
|
667
|
+
"button",
|
|
668
|
+
{
|
|
669
|
+
className: `btn-ghost ${orientation === "horizontal" ? "btn-primary" : ""}`,
|
|
670
|
+
title: "Landscape (1280×720)",
|
|
671
|
+
onClick: () => handleOrientationChange("horizontal"),
|
|
672
|
+
children: /* @__PURE__ */ jsx(RectangleHorizontal, { className: "icon-sm" })
|
|
673
|
+
}
|
|
674
|
+
)
|
|
675
|
+
] })
|
|
633
676
|
] }),
|
|
634
677
|
/* @__PURE__ */ jsxs("div", { className: "flex-container", children: [
|
|
635
678
|
/* @__PURE__ */ jsxs(
|
|
@@ -1181,8 +1224,16 @@ const AudioPanel = ({
|
|
|
1181
1224
|
return /* @__PURE__ */ jsx(
|
|
1182
1225
|
"div",
|
|
1183
1226
|
{
|
|
1227
|
+
draggable: true,
|
|
1184
1228
|
onDoubleClick: () => onItemSelect(item),
|
|
1185
|
-
|
|
1229
|
+
onDragStart: (e) => {
|
|
1230
|
+
e.dataTransfer.setData(
|
|
1231
|
+
TIMELINE_DROP_MEDIA_TYPE,
|
|
1232
|
+
JSON.stringify({ type: "audio", url: item.url })
|
|
1233
|
+
);
|
|
1234
|
+
e.dataTransfer.effectAllowed = "copy";
|
|
1235
|
+
},
|
|
1236
|
+
className: "media-list-item media-item-draggable",
|
|
1186
1237
|
children: /* @__PURE__ */ jsxs("div", { className: "media-list-content", children: [
|
|
1187
1238
|
/* @__PURE__ */ jsx(
|
|
1188
1239
|
"button",
|
|
@@ -1284,18 +1335,26 @@ function ImagePanel({
|
|
|
1284
1335
|
/* @__PURE__ */ jsx("div", { className: "media-grid", children: (items || []).map((item) => /* @__PURE__ */ jsxs(
|
|
1285
1336
|
"div",
|
|
1286
1337
|
{
|
|
1338
|
+
draggable: true,
|
|
1287
1339
|
onDoubleClick: () => onItemSelect(item),
|
|
1288
|
-
|
|
1340
|
+
onDragStart: (e) => {
|
|
1341
|
+
e.dataTransfer.setData(
|
|
1342
|
+
TIMELINE_DROP_MEDIA_TYPE,
|
|
1343
|
+
JSON.stringify({ type: "image", url: item.url })
|
|
1344
|
+
);
|
|
1345
|
+
e.dataTransfer.effectAllowed = "copy";
|
|
1346
|
+
},
|
|
1347
|
+
className: "media-item media-item-draggable",
|
|
1289
1348
|
children: [
|
|
1290
1349
|
/* @__PURE__ */ jsx("img", { src: item.url, alt: "", className: "media-item-content" }),
|
|
1291
|
-
/* @__PURE__ */ jsx("div", { className: "media-actions", children: /* @__PURE__ */ jsx(
|
|
1350
|
+
/* @__PURE__ */ jsx("div", { className: "media-actions media-actions-corner", children: /* @__PURE__ */ jsx(
|
|
1292
1351
|
"button",
|
|
1293
1352
|
{
|
|
1294
1353
|
onClick: (e) => {
|
|
1295
1354
|
e.stopPropagation();
|
|
1296
1355
|
onItemSelect(item, true);
|
|
1297
1356
|
},
|
|
1298
|
-
className: "media-action-btn
|
|
1357
|
+
className: "media-action-btn",
|
|
1299
1358
|
children: /* @__PURE__ */ jsx(Plus, { className: "icon-sm" })
|
|
1300
1359
|
}
|
|
1301
1360
|
) })
|
|
@@ -1413,8 +1472,16 @@ function VideoPanel({
|
|
|
1413
1472
|
/* @__PURE__ */ jsx("div", { className: "media-grid", children: (items || []).map((item) => /* @__PURE__ */ jsxs(
|
|
1414
1473
|
"div",
|
|
1415
1474
|
{
|
|
1475
|
+
draggable: true,
|
|
1416
1476
|
onDoubleClick: () => onItemSelect(item),
|
|
1417
|
-
|
|
1477
|
+
onDragStart: (e) => {
|
|
1478
|
+
e.dataTransfer.setData(
|
|
1479
|
+
TIMELINE_DROP_MEDIA_TYPE,
|
|
1480
|
+
JSON.stringify({ type: "video", url: item.url })
|
|
1481
|
+
);
|
|
1482
|
+
e.dataTransfer.effectAllowed = "copy";
|
|
1483
|
+
},
|
|
1484
|
+
className: "media-item media-item-draggable",
|
|
1418
1485
|
children: [
|
|
1419
1486
|
/* @__PURE__ */ jsx(
|
|
1420
1487
|
"video",
|
|
@@ -1431,32 +1498,34 @@ function VideoPanel({
|
|
|
1431
1498
|
}
|
|
1432
1499
|
}
|
|
1433
1500
|
),
|
|
1434
|
-
/* @__PURE__ */
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
e
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1501
|
+
/* @__PURE__ */ jsxs("div", { className: "media-actions media-actions-corner", children: [
|
|
1502
|
+
/* @__PURE__ */ jsx(
|
|
1503
|
+
"button",
|
|
1504
|
+
{
|
|
1505
|
+
onClick: (e) => {
|
|
1506
|
+
var _a, _b;
|
|
1507
|
+
e.stopPropagation();
|
|
1508
|
+
const videoEl = (_b = (_a = e.currentTarget.parentElement) == null ? void 0 : _a.parentElement) == null ? void 0 : _b.querySelector("video");
|
|
1509
|
+
if (videoEl) {
|
|
1510
|
+
togglePlayPause(item, videoEl);
|
|
1511
|
+
}
|
|
1512
|
+
},
|
|
1513
|
+
className: "media-action-btn",
|
|
1514
|
+
children: playingVideo === item.id ? /* @__PURE__ */ jsx(Pause, { className: "icon-sm" }) : /* @__PURE__ */ jsx(Play, { className: "icon-sm" })
|
|
1515
|
+
}
|
|
1516
|
+
),
|
|
1517
|
+
/* @__PURE__ */ jsx(
|
|
1518
|
+
"button",
|
|
1519
|
+
{
|
|
1520
|
+
onClick: (e) => {
|
|
1521
|
+
e.stopPropagation();
|
|
1522
|
+
onItemSelect(item, true);
|
|
1523
|
+
},
|
|
1524
|
+
className: "media-action-btn",
|
|
1525
|
+
children: /* @__PURE__ */ jsx(Plus, { className: "icon-sm" })
|
|
1526
|
+
}
|
|
1527
|
+
)
|
|
1528
|
+
] })
|
|
1460
1529
|
]
|
|
1461
1530
|
},
|
|
1462
1531
|
item.id
|
|
@@ -1527,6 +1596,9 @@ function TextPanel({
|
|
|
1527
1596
|
applyShadow,
|
|
1528
1597
|
shadowColor,
|
|
1529
1598
|
strokeWidth,
|
|
1599
|
+
applyBackground,
|
|
1600
|
+
backgroundColor,
|
|
1601
|
+
backgroundOpacity,
|
|
1530
1602
|
fonts,
|
|
1531
1603
|
operation,
|
|
1532
1604
|
setTextContent,
|
|
@@ -1539,6 +1611,9 @@ function TextPanel({
|
|
|
1539
1611
|
setApplyShadow,
|
|
1540
1612
|
setShadowColor,
|
|
1541
1613
|
setStrokeWidth,
|
|
1614
|
+
setApplyBackground,
|
|
1615
|
+
setBackgroundColor,
|
|
1616
|
+
setBackgroundOpacity,
|
|
1542
1617
|
handleApplyChanges
|
|
1543
1618
|
}) {
|
|
1544
1619
|
return /* @__PURE__ */ jsxs("div", { className: "panel-container", children: [
|
|
@@ -1707,7 +1782,70 @@ function TextPanel({
|
|
|
1707
1782
|
/* @__PURE__ */ jsx("span", { className: "slider-value", children: strokeWidth })
|
|
1708
1783
|
] })
|
|
1709
1784
|
] }),
|
|
1710
|
-
/* @__PURE__ */
|
|
1785
|
+
/* @__PURE__ */ jsxs("div", { className: "panel-section", children: [
|
|
1786
|
+
/* @__PURE__ */ jsx("label", { className: "label-dark", children: "Background" }),
|
|
1787
|
+
/* @__PURE__ */ jsxs("div", { className: "color-section", children: [
|
|
1788
|
+
/* @__PURE__ */ jsx("div", { className: "checkbox-control", children: /* @__PURE__ */ jsxs("label", { className: "checkbox-label", children: [
|
|
1789
|
+
/* @__PURE__ */ jsx(
|
|
1790
|
+
"input",
|
|
1791
|
+
{
|
|
1792
|
+
type: "checkbox",
|
|
1793
|
+
checked: applyBackground,
|
|
1794
|
+
onChange: (e) => setApplyBackground(e.target.checked),
|
|
1795
|
+
className: "checkbox-purple"
|
|
1796
|
+
}
|
|
1797
|
+
),
|
|
1798
|
+
"Apply Background"
|
|
1799
|
+
] }) }),
|
|
1800
|
+
applyBackground && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1801
|
+
/* @__PURE__ */ jsxs("div", { className: "color-control", children: [
|
|
1802
|
+
/* @__PURE__ */ jsx("label", { className: "label-small", children: "Background Color" }),
|
|
1803
|
+
/* @__PURE__ */ jsxs("div", { className: "color-inputs", children: [
|
|
1804
|
+
/* @__PURE__ */ jsx(
|
|
1805
|
+
"input",
|
|
1806
|
+
{
|
|
1807
|
+
type: "color",
|
|
1808
|
+
value: backgroundColor,
|
|
1809
|
+
onChange: (e) => setBackgroundColor(e.target.value),
|
|
1810
|
+
className: "color-picker"
|
|
1811
|
+
}
|
|
1812
|
+
),
|
|
1813
|
+
/* @__PURE__ */ jsx(
|
|
1814
|
+
"input",
|
|
1815
|
+
{
|
|
1816
|
+
type: "text",
|
|
1817
|
+
value: backgroundColor,
|
|
1818
|
+
onChange: (e) => setBackgroundColor(e.target.value),
|
|
1819
|
+
className: "color-text"
|
|
1820
|
+
}
|
|
1821
|
+
)
|
|
1822
|
+
] })
|
|
1823
|
+
] }),
|
|
1824
|
+
/* @__PURE__ */ jsxs("div", { className: "panel-section", children: [
|
|
1825
|
+
/* @__PURE__ */ jsx("label", { className: "label-small", children: "Background Opacity" }),
|
|
1826
|
+
/* @__PURE__ */ jsxs("div", { className: "slider-container", children: [
|
|
1827
|
+
/* @__PURE__ */ jsx(
|
|
1828
|
+
"input",
|
|
1829
|
+
{
|
|
1830
|
+
type: "range",
|
|
1831
|
+
min: "0",
|
|
1832
|
+
max: "1",
|
|
1833
|
+
step: 0.1,
|
|
1834
|
+
value: backgroundOpacity,
|
|
1835
|
+
onChange: (e) => setBackgroundOpacity(Number(e.target.value)),
|
|
1836
|
+
className: "slider-purple"
|
|
1837
|
+
}
|
|
1838
|
+
),
|
|
1839
|
+
/* @__PURE__ */ jsxs("span", { className: "slider-value", children: [
|
|
1840
|
+
Math.round(backgroundOpacity * 100),
|
|
1841
|
+
"%"
|
|
1842
|
+
] })
|
|
1843
|
+
] })
|
|
1844
|
+
] })
|
|
1845
|
+
] })
|
|
1846
|
+
] })
|
|
1847
|
+
] }),
|
|
1848
|
+
operation !== "Apply Changes" && /* @__PURE__ */ jsx("div", { className: "flex panel-section", children: /* @__PURE__ */ jsx("button", { onClick: handleApplyChanges, className: "btn-primary w-full", children: operation }) })
|
|
1711
1849
|
] });
|
|
1712
1850
|
}
|
|
1713
1851
|
const DEFAULT_TEXT_PROPS = {
|
|
@@ -1741,53 +1879,134 @@ const useTextPanel = ({
|
|
|
1741
1879
|
const [applyShadow, setApplyShadow] = useState(DEFAULT_TEXT_PROPS.applyShadow);
|
|
1742
1880
|
const [shadowColor, setShadowColor] = useState(DEFAULT_TEXT_PROPS.shadowColor);
|
|
1743
1881
|
const [strokeWidth, setStrokeWidth] = useState(DEFAULT_TEXT_PROPS.strokeWidth);
|
|
1882
|
+
const [applyBackground, setApplyBackground] = useState(false);
|
|
1883
|
+
const [backgroundColor, setBackgroundColor] = useState("#FACC15");
|
|
1884
|
+
const [backgroundOpacity, setBackgroundOpacity] = useState(1);
|
|
1744
1885
|
const fonts = Object.values(AVAILABLE_TEXT_FONTS);
|
|
1745
|
-
const
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1886
|
+
const applyLiveChangesToExistingText = (overrides = {}) => {
|
|
1887
|
+
if (!(selectedElement instanceof TextElement)) {
|
|
1888
|
+
return;
|
|
1889
|
+
}
|
|
1890
|
+
const textElement = selectedElement;
|
|
1891
|
+
const nextState = {
|
|
1892
|
+
textContent,
|
|
1893
|
+
fontSize,
|
|
1894
|
+
selectedFont,
|
|
1895
|
+
isBold,
|
|
1896
|
+
isItalic,
|
|
1897
|
+
textColor,
|
|
1898
|
+
strokeColor,
|
|
1899
|
+
strokeWidth,
|
|
1900
|
+
applyShadow,
|
|
1901
|
+
shadowColor,
|
|
1902
|
+
applyBackground,
|
|
1903
|
+
backgroundColor,
|
|
1904
|
+
backgroundOpacity,
|
|
1905
|
+
...overrides
|
|
1906
|
+
};
|
|
1907
|
+
textElement.setText(nextState.textContent);
|
|
1908
|
+
textElement.setFontSize(nextState.fontSize);
|
|
1909
|
+
textElement.setFontFamily(nextState.selectedFont);
|
|
1910
|
+
textElement.setFontWeight(nextState.isBold ? 700 : 400);
|
|
1911
|
+
textElement.setFontStyle(nextState.isItalic ? "italic" : "normal");
|
|
1912
|
+
textElement.setFill(nextState.textColor);
|
|
1913
|
+
textElement.setStrokeColor(nextState.strokeColor);
|
|
1914
|
+
textElement.setLineWidth(nextState.strokeWidth);
|
|
1915
|
+
textElement.setTextAlign(DEFAULT_TEXT_PROPS.textAlign);
|
|
1916
|
+
const nextProps = { ...textElement.getProps() };
|
|
1917
|
+
if (nextState.applyShadow) {
|
|
1918
|
+
nextProps.shadowColor = nextState.shadowColor;
|
|
1919
|
+
nextProps.shadowOffset = DEFAULT_TEXT_PROPS.shadowOffset;
|
|
1920
|
+
nextProps.shadowBlur = DEFAULT_TEXT_PROPS.shadowBlur;
|
|
1921
|
+
nextProps.shadowOpacity = DEFAULT_TEXT_PROPS.shadowOpacity;
|
|
1776
1922
|
} else {
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1923
|
+
nextProps.shadowColor = void 0;
|
|
1924
|
+
nextProps.shadowOffset = void 0;
|
|
1925
|
+
nextProps.shadowBlur = void 0;
|
|
1926
|
+
nextProps.shadowOpacity = void 0;
|
|
1927
|
+
}
|
|
1928
|
+
if (nextState.applyBackground) {
|
|
1929
|
+
nextProps.backgroundColor = nextState.backgroundColor;
|
|
1930
|
+
nextProps.backgroundOpacity = nextState.backgroundOpacity;
|
|
1931
|
+
} else {
|
|
1932
|
+
nextProps.backgroundColor = void 0;
|
|
1933
|
+
nextProps.backgroundOpacity = void 0;
|
|
1788
1934
|
}
|
|
1935
|
+
textElement.setProps(nextProps);
|
|
1936
|
+
updateElement(textElement);
|
|
1789
1937
|
};
|
|
1790
|
-
|
|
1938
|
+
const handleTextContentChange = (text) => {
|
|
1939
|
+
setTextContent(text);
|
|
1940
|
+
applyLiveChangesToExistingText({ textContent: text });
|
|
1941
|
+
};
|
|
1942
|
+
const handleFontSizeChange = (size) => {
|
|
1943
|
+
setFontSize(size);
|
|
1944
|
+
applyLiveChangesToExistingText({ fontSize: size });
|
|
1945
|
+
};
|
|
1946
|
+
const handleSelectedFontChange = (font) => {
|
|
1947
|
+
setSelectedFont(font);
|
|
1948
|
+
applyLiveChangesToExistingText({ selectedFont: font });
|
|
1949
|
+
};
|
|
1950
|
+
const handleIsBoldChange = (bold) => {
|
|
1951
|
+
setIsBold(bold);
|
|
1952
|
+
applyLiveChangesToExistingText({ isBold: bold });
|
|
1953
|
+
};
|
|
1954
|
+
const handleIsItalicChange = (italic) => {
|
|
1955
|
+
setIsItalic(italic);
|
|
1956
|
+
applyLiveChangesToExistingText({ isItalic: italic });
|
|
1957
|
+
};
|
|
1958
|
+
const handleTextColorChange = (color) => {
|
|
1959
|
+
setTextColor(color);
|
|
1960
|
+
applyLiveChangesToExistingText({ textColor: color });
|
|
1961
|
+
};
|
|
1962
|
+
const handleStrokeColorChange = (color) => {
|
|
1963
|
+
setStrokeColor(color);
|
|
1964
|
+
applyLiveChangesToExistingText({ strokeColor: color });
|
|
1965
|
+
};
|
|
1966
|
+
const handleStrokeWidthChange = (width) => {
|
|
1967
|
+
setStrokeWidth(width);
|
|
1968
|
+
applyLiveChangesToExistingText({ strokeWidth: width });
|
|
1969
|
+
};
|
|
1970
|
+
const handleApplyShadowChange = (shadow) => {
|
|
1971
|
+
setApplyShadow(shadow);
|
|
1972
|
+
applyLiveChangesToExistingText({ applyShadow: shadow });
|
|
1973
|
+
};
|
|
1974
|
+
const handleShadowColorChange = (color) => {
|
|
1975
|
+
setShadowColor(color);
|
|
1976
|
+
applyLiveChangesToExistingText({ shadowColor: color });
|
|
1977
|
+
};
|
|
1978
|
+
const handleApplyBackgroundChange = (apply) => {
|
|
1979
|
+
setApplyBackground(apply);
|
|
1980
|
+
applyLiveChangesToExistingText({ applyBackground: apply });
|
|
1981
|
+
};
|
|
1982
|
+
const handleBackgroundColorChange = (color) => {
|
|
1983
|
+
setBackgroundColor(color);
|
|
1984
|
+
applyLiveChangesToExistingText({ backgroundColor: color });
|
|
1985
|
+
};
|
|
1986
|
+
const handleBackgroundOpacityChange = (opacity) => {
|
|
1987
|
+
setBackgroundOpacity(opacity);
|
|
1988
|
+
applyLiveChangesToExistingText({ backgroundOpacity: opacity });
|
|
1989
|
+
};
|
|
1990
|
+
const handleApplyChanges = async () => {
|
|
1991
|
+
if (selectedElement instanceof TextElement) {
|
|
1992
|
+
return;
|
|
1993
|
+
}
|
|
1994
|
+
const textElement = new TextElement(textContent).setFontSize(fontSize).setFontFamily(selectedFont).setFontWeight(isBold ? 700 : 400).setFontStyle(isItalic ? "italic" : "normal").setFill(textColor).setStrokeColor(strokeColor).setLineWidth(strokeWidth).setTextAlign("center");
|
|
1995
|
+
const nextProps = { ...textElement.getProps() };
|
|
1996
|
+
if (applyShadow) {
|
|
1997
|
+
nextProps.shadowColor = shadowColor;
|
|
1998
|
+
nextProps.shadowOffset = DEFAULT_TEXT_PROPS.shadowOffset;
|
|
1999
|
+
nextProps.shadowBlur = DEFAULT_TEXT_PROPS.shadowBlur;
|
|
2000
|
+
nextProps.shadowOpacity = DEFAULT_TEXT_PROPS.shadowOpacity;
|
|
2001
|
+
}
|
|
2002
|
+
if (applyBackground) {
|
|
2003
|
+
nextProps.backgroundColor = backgroundColor;
|
|
2004
|
+
nextProps.backgroundOpacity = backgroundOpacity;
|
|
2005
|
+
}
|
|
2006
|
+
textElement.setProps(nextProps);
|
|
2007
|
+
await addElement(textElement);
|
|
2008
|
+
};
|
|
2009
|
+
useEffect(() => {
|
|
1791
2010
|
if (selectedElement instanceof TextElement) {
|
|
1792
2011
|
setTextContent(selectedElement.getText());
|
|
1793
2012
|
const textProps = selectedElement.getProps();
|
|
@@ -1803,6 +2022,12 @@ const useTextPanel = ({
|
|
|
1803
2022
|
if (hasShadow) {
|
|
1804
2023
|
setShadowColor(textProps.shadowColor ?? DEFAULT_TEXT_PROPS.shadowColor);
|
|
1805
2024
|
}
|
|
2025
|
+
const hasBackground = textProps.backgroundColor != null && textProps.backgroundColor !== "";
|
|
2026
|
+
setApplyBackground(hasBackground);
|
|
2027
|
+
if (hasBackground) {
|
|
2028
|
+
setBackgroundColor(textProps.backgroundColor ?? "#FACC15");
|
|
2029
|
+
setBackgroundOpacity(textProps.backgroundOpacity ?? 1);
|
|
2030
|
+
}
|
|
1806
2031
|
} else {
|
|
1807
2032
|
setTextContent(DEFAULT_TEXT_PROPS.text);
|
|
1808
2033
|
setFontSize(DEFAULT_TEXT_PROPS.fontSize);
|
|
@@ -1814,6 +2039,9 @@ const useTextPanel = ({
|
|
|
1814
2039
|
setStrokeWidth(DEFAULT_TEXT_PROPS.strokeWidth);
|
|
1815
2040
|
setApplyShadow(DEFAULT_TEXT_PROPS.applyShadow);
|
|
1816
2041
|
setShadowColor(DEFAULT_TEXT_PROPS.shadowColor);
|
|
2042
|
+
setApplyBackground(false);
|
|
2043
|
+
setBackgroundColor("#FACC15");
|
|
2044
|
+
setBackgroundOpacity(1);
|
|
1817
2045
|
}
|
|
1818
2046
|
}, [selectedElement]);
|
|
1819
2047
|
return {
|
|
@@ -1829,16 +2057,22 @@ const useTextPanel = ({
|
|
|
1829
2057
|
strokeWidth,
|
|
1830
2058
|
fonts,
|
|
1831
2059
|
operation: selectedElement instanceof TextElement ? "Apply Changes" : "Add Text",
|
|
1832
|
-
setTextContent,
|
|
1833
|
-
setFontSize,
|
|
1834
|
-
setSelectedFont,
|
|
1835
|
-
setIsBold,
|
|
1836
|
-
setIsItalic,
|
|
1837
|
-
setTextColor,
|
|
1838
|
-
setStrokeColor,
|
|
1839
|
-
setApplyShadow,
|
|
1840
|
-
setShadowColor,
|
|
1841
|
-
setStrokeWidth,
|
|
2060
|
+
setTextContent: handleTextContentChange,
|
|
2061
|
+
setFontSize: handleFontSizeChange,
|
|
2062
|
+
setSelectedFont: handleSelectedFontChange,
|
|
2063
|
+
setIsBold: handleIsBoldChange,
|
|
2064
|
+
setIsItalic: handleIsItalicChange,
|
|
2065
|
+
setTextColor: handleTextColorChange,
|
|
2066
|
+
setStrokeColor: handleStrokeColorChange,
|
|
2067
|
+
setApplyShadow: handleApplyShadowChange,
|
|
2068
|
+
setShadowColor: handleShadowColorChange,
|
|
2069
|
+
setStrokeWidth: handleStrokeWidthChange,
|
|
2070
|
+
applyBackground,
|
|
2071
|
+
backgroundColor,
|
|
2072
|
+
backgroundOpacity,
|
|
2073
|
+
setApplyBackground: handleApplyBackgroundChange,
|
|
2074
|
+
setBackgroundColor: handleBackgroundColorChange,
|
|
2075
|
+
setBackgroundOpacity: handleBackgroundOpacityChange,
|
|
1842
2076
|
handleApplyChanges
|
|
1843
2077
|
};
|
|
1844
2078
|
};
|
|
@@ -1846,227 +2080,14 @@ function TextPanelContainer(props) {
|
|
|
1846
2080
|
const textPanelProps = useTextPanel(props);
|
|
1847
2081
|
return /* @__PURE__ */ jsx(TextPanel, { ...textPanelProps });
|
|
1848
2082
|
}
|
|
1849
|
-
const SearchInput = ({
|
|
1850
|
-
searchQuery,
|
|
1851
|
-
setSearchQuery
|
|
1852
|
-
}) => {
|
|
1853
|
-
return /* @__PURE__ */ jsxs("div", { className: "search-container", children: [
|
|
1854
|
-
/* @__PURE__ */ jsx(
|
|
1855
|
-
"input",
|
|
1856
|
-
{
|
|
1857
|
-
type: "text",
|
|
1858
|
-
placeholder: "Search media...",
|
|
1859
|
-
value: searchQuery,
|
|
1860
|
-
onChange: (e) => setSearchQuery(e.target.value),
|
|
1861
|
-
className: "input search-input w-full"
|
|
1862
|
-
}
|
|
1863
|
-
),
|
|
1864
|
-
/* @__PURE__ */ jsx(Search, { className: "search-icon" })
|
|
1865
|
-
] });
|
|
1866
|
-
};
|
|
1867
|
-
function IconPanel({
|
|
1868
|
-
icons,
|
|
1869
|
-
loading,
|
|
1870
|
-
totalIcons,
|
|
1871
|
-
searchQuery,
|
|
1872
|
-
handleSearch,
|
|
1873
|
-
handleSelection,
|
|
1874
|
-
handleDownloadIcon,
|
|
1875
|
-
handleLoadMore
|
|
1876
|
-
}) {
|
|
1877
|
-
return /* @__PURE__ */ jsxs("div", { className: "panel-container", children: [
|
|
1878
|
-
/* @__PURE__ */ jsx("div", { className: "panel-title", children: "Icon Library" }),
|
|
1879
|
-
/* @__PURE__ */ jsx("div", { className: "flex panel-section", children: /* @__PURE__ */ jsx(SearchInput, { searchQuery, setSearchQuery: handleSearch }) }),
|
|
1880
|
-
/* @__PURE__ */ jsxs("div", { className: "media-content", children: [
|
|
1881
|
-
totalIcons > 0 && /* @__PURE__ */ jsxs("div", { className: "media-count", children: [
|
|
1882
|
-
"Showing ",
|
|
1883
|
-
icons.length,
|
|
1884
|
-
" of ",
|
|
1885
|
-
totalIcons,
|
|
1886
|
-
" icons"
|
|
1887
|
-
] }),
|
|
1888
|
-
loading && icons.length === 0 ? /* @__PURE__ */ jsx("div", { className: "empty-state", children: /* @__PURE__ */ jsxs("div", { className: "empty-state-content", children: [
|
|
1889
|
-
/* @__PURE__ */ jsx(LoaderCircle, { className: "empty-state-icon animate-spin" }),
|
|
1890
|
-
/* @__PURE__ */ jsx("p", { className: "empty-state-text", children: "Loading icons..." })
|
|
1891
|
-
] }) }) : /* @__PURE__ */ jsx("div", { className: "icon-grid", children: (icons || []).map((icon, index) => /* @__PURE__ */ jsxs("div", { className: "icon-item", children: [
|
|
1892
|
-
/* @__PURE__ */ jsx(
|
|
1893
|
-
"div",
|
|
1894
|
-
{
|
|
1895
|
-
onClick: () => handleSelection(icon),
|
|
1896
|
-
className: "icon-content",
|
|
1897
|
-
dangerouslySetInnerHTML: { __html: icon.svg }
|
|
1898
|
-
}
|
|
1899
|
-
),
|
|
1900
|
-
/* @__PURE__ */ jsxs("div", { className: "icon-actions", children: [
|
|
1901
|
-
/* @__PURE__ */ jsx(
|
|
1902
|
-
"button",
|
|
1903
|
-
{
|
|
1904
|
-
onClick: (e) => {
|
|
1905
|
-
e.stopPropagation();
|
|
1906
|
-
handleSelection(icon);
|
|
1907
|
-
},
|
|
1908
|
-
className: "icon-action-btn",
|
|
1909
|
-
title: "Add to timeline",
|
|
1910
|
-
children: /* @__PURE__ */ jsx(Plus, { className: "icon-sm" })
|
|
1911
|
-
}
|
|
1912
|
-
),
|
|
1913
|
-
/* @__PURE__ */ jsx(
|
|
1914
|
-
"button",
|
|
1915
|
-
{
|
|
1916
|
-
onClick: (e) => {
|
|
1917
|
-
e.stopPropagation();
|
|
1918
|
-
handleDownloadIcon(icon);
|
|
1919
|
-
},
|
|
1920
|
-
className: "icon-action-btn",
|
|
1921
|
-
title: "Download SVG",
|
|
1922
|
-
children: /* @__PURE__ */ jsx(Download, { className: "icon-sm" })
|
|
1923
|
-
}
|
|
1924
|
-
)
|
|
1925
|
-
] }),
|
|
1926
|
-
/* @__PURE__ */ jsx("div", { className: "icon-name", children: icon.name })
|
|
1927
|
-
] }, index)) }),
|
|
1928
|
-
!loading && icons.length === 0 && searchQuery && /* @__PURE__ */ jsx("div", { className: "empty-state", children: /* @__PURE__ */ jsxs("div", { className: "empty-state-content", children: [
|
|
1929
|
-
/* @__PURE__ */ jsx("p", { className: "empty-state-text", children: "No icons found" }),
|
|
1930
|
-
/* @__PURE__ */ jsx("p", { className: "empty-state-subtext", children: "Try a different search term" })
|
|
1931
|
-
] }) }),
|
|
1932
|
-
!loading && totalIcons && icons.length < totalIcons && /* @__PURE__ */ jsx("div", { className: "flex panel-section", children: /* @__PURE__ */ jsx(
|
|
1933
|
-
"button",
|
|
1934
|
-
{
|
|
1935
|
-
onClick: handleLoadMore,
|
|
1936
|
-
disabled: loading,
|
|
1937
|
-
className: "btn-primary",
|
|
1938
|
-
children: "Load More Icons"
|
|
1939
|
-
}
|
|
1940
|
-
) })
|
|
1941
|
-
] })
|
|
1942
|
-
] });
|
|
1943
|
-
}
|
|
1944
|
-
const ICONS_PER_PAGE = 20;
|
|
1945
|
-
const useIconPanel = ({
|
|
1946
|
-
selectedElement,
|
|
1947
|
-
addElement,
|
|
1948
|
-
updateElement
|
|
1949
|
-
}) => {
|
|
1950
|
-
const [icons, setIcons] = useState([]);
|
|
1951
|
-
const [loading, setLoading] = useState(false);
|
|
1952
|
-
const [page, setPage] = useState(1);
|
|
1953
|
-
const [hasMore, setHasMore] = useState(true);
|
|
1954
|
-
const [totalIcons, setTotalIcons] = useState(0);
|
|
1955
|
-
const [searchQuery, setSearchQuery] = useState("");
|
|
1956
|
-
const currentQuery = useRef("");
|
|
1957
|
-
const fetchIcons = async (query, reset = false) => {
|
|
1958
|
-
try {
|
|
1959
|
-
setLoading(true);
|
|
1960
|
-
const newPage = reset ? 1 : page;
|
|
1961
|
-
const start = (newPage - 1) * ICONS_PER_PAGE;
|
|
1962
|
-
const url = `https://api.iconify.design/search?query=${query}&limit=${ICONS_PER_PAGE}&offset=${start}`;
|
|
1963
|
-
const response = await fetch(url);
|
|
1964
|
-
const data = await response.json();
|
|
1965
|
-
const iconData = data.icons || [];
|
|
1966
|
-
const total = data.total || 0;
|
|
1967
|
-
setTotalIcons(total);
|
|
1968
|
-
const formattedIcons = await Promise.all(
|
|
1969
|
-
iconData.map(async (icon) => {
|
|
1970
|
-
const svgUrl = `https://api.iconify.design/${icon}.svg`;
|
|
1971
|
-
try {
|
|
1972
|
-
const svgResponse = await fetch(svgUrl);
|
|
1973
|
-
const svg = await svgResponse.text();
|
|
1974
|
-
return { name: icon, svg };
|
|
1975
|
-
} catch (e) {
|
|
1976
|
-
console.error(`Error fetching SVG for ${icon}:`, e);
|
|
1977
|
-
return null;
|
|
1978
|
-
}
|
|
1979
|
-
})
|
|
1980
|
-
);
|
|
1981
|
-
const validIcons = formattedIcons.filter((icon) => icon !== null);
|
|
1982
|
-
if (reset) {
|
|
1983
|
-
setIcons(validIcons);
|
|
1984
|
-
} else {
|
|
1985
|
-
setIcons([...icons, ...validIcons]);
|
|
1986
|
-
}
|
|
1987
|
-
setHasMore(start + validIcons.length < total);
|
|
1988
|
-
if (!reset) {
|
|
1989
|
-
setPage(newPage + 1);
|
|
1990
|
-
} else {
|
|
1991
|
-
setPage(2);
|
|
1992
|
-
}
|
|
1993
|
-
} catch (error) {
|
|
1994
|
-
console.error("Error fetching icons:", error);
|
|
1995
|
-
} finally {
|
|
1996
|
-
setLoading(false);
|
|
1997
|
-
}
|
|
1998
|
-
};
|
|
1999
|
-
useEffect(() => {
|
|
2000
|
-
fetchIcons("media", true);
|
|
2001
|
-
}, []);
|
|
2002
|
-
const handleSearch = (query) => {
|
|
2003
|
-
currentQuery.current = query;
|
|
2004
|
-
setSearchQuery(query);
|
|
2005
|
-
fetchIcons(query, true);
|
|
2006
|
-
};
|
|
2007
|
-
const handleSelection = (icon) => {
|
|
2008
|
-
const svgBlob = new Blob([icon.svg], { type: "image/svg+xml" });
|
|
2009
|
-
const url = URL.createObjectURL(svgBlob);
|
|
2010
|
-
let iconElement;
|
|
2011
|
-
if (selectedElement instanceof IconElement) {
|
|
2012
|
-
iconElement = selectedElement;
|
|
2013
|
-
iconElement.setSrc(url);
|
|
2014
|
-
iconElement.setName(icon.name);
|
|
2015
|
-
updateElement == null ? void 0 : updateElement(iconElement);
|
|
2016
|
-
} else {
|
|
2017
|
-
iconElement = new IconElement(url, {
|
|
2018
|
-
width: 100,
|
|
2019
|
-
height: 100
|
|
2020
|
-
});
|
|
2021
|
-
iconElement.setName(icon.name);
|
|
2022
|
-
addElement == null ? void 0 : addElement(iconElement);
|
|
2023
|
-
}
|
|
2024
|
-
URL.revokeObjectURL(url);
|
|
2025
|
-
};
|
|
2026
|
-
const handleDownloadIcon = (icon) => {
|
|
2027
|
-
const blob = new Blob([icon.svg], { type: "image/svg+xml" });
|
|
2028
|
-
const url = URL.createObjectURL(blob);
|
|
2029
|
-
const a = document.createElement("a");
|
|
2030
|
-
a.href = url;
|
|
2031
|
-
a.download = `${icon.name}.svg`;
|
|
2032
|
-
document.body.appendChild(a);
|
|
2033
|
-
a.click();
|
|
2034
|
-
document.body.removeChild(a);
|
|
2035
|
-
URL.revokeObjectURL(url);
|
|
2036
|
-
};
|
|
2037
|
-
const handleLoadMore = () => {
|
|
2038
|
-
fetchIcons(currentQuery.current, false);
|
|
2039
|
-
};
|
|
2040
|
-
return {
|
|
2041
|
-
icons,
|
|
2042
|
-
loading,
|
|
2043
|
-
hasMore,
|
|
2044
|
-
totalIcons,
|
|
2045
|
-
searchQuery,
|
|
2046
|
-
handleSearch,
|
|
2047
|
-
handleSelection,
|
|
2048
|
-
handleDownloadIcon,
|
|
2049
|
-
handleLoadMore
|
|
2050
|
-
};
|
|
2051
|
-
};
|
|
2052
|
-
function IconPanelContainer(props) {
|
|
2053
|
-
const iconPanelProps = useIconPanel({
|
|
2054
|
-
selectedElement: props.selectedElement ?? null,
|
|
2055
|
-
addElement: props.addElement,
|
|
2056
|
-
updateElement: props.updateElement
|
|
2057
|
-
});
|
|
2058
|
-
return /* @__PURE__ */ jsx(IconPanel, { ...iconPanelProps });
|
|
2059
|
-
}
|
|
2060
2083
|
function RectPanel({
|
|
2061
2084
|
cornerRadius,
|
|
2062
2085
|
fillColor,
|
|
2063
|
-
opacity,
|
|
2064
2086
|
strokeColor,
|
|
2065
2087
|
lineWidth,
|
|
2066
2088
|
operation,
|
|
2067
2089
|
setCornerRadius,
|
|
2068
2090
|
setFillColor,
|
|
2069
|
-
setOpacity,
|
|
2070
2091
|
setStrokeColor,
|
|
2071
2092
|
setLineWidth,
|
|
2072
2093
|
handleApplyChanges
|
|
@@ -2116,26 +2137,6 @@ function RectPanel({
|
|
|
2116
2137
|
)
|
|
2117
2138
|
] })
|
|
2118
2139
|
] }),
|
|
2119
|
-
/* @__PURE__ */ jsxs("div", { className: "panel-section", children: [
|
|
2120
|
-
/* @__PURE__ */ jsx("label", { className: "label-dark", children: "Opacity" }),
|
|
2121
|
-
/* @__PURE__ */ jsxs("div", { className: "slider-container", children: [
|
|
2122
|
-
/* @__PURE__ */ jsx(
|
|
2123
|
-
"input",
|
|
2124
|
-
{
|
|
2125
|
-
type: "range",
|
|
2126
|
-
min: "0",
|
|
2127
|
-
max: "100",
|
|
2128
|
-
value: opacity,
|
|
2129
|
-
onChange: (e) => setOpacity(Number(e.target.value)),
|
|
2130
|
-
className: "slider-purple"
|
|
2131
|
-
}
|
|
2132
|
-
),
|
|
2133
|
-
/* @__PURE__ */ jsxs("span", { className: "slider-value", children: [
|
|
2134
|
-
opacity,
|
|
2135
|
-
"%"
|
|
2136
|
-
] })
|
|
2137
|
-
] })
|
|
2138
|
-
] }),
|
|
2139
2140
|
/* @__PURE__ */ jsxs("div", { className: "panel-section", children: [
|
|
2140
2141
|
/* @__PURE__ */ jsx("label", { className: "label-dark", children: "Stroke Color" }),
|
|
2141
2142
|
/* @__PURE__ */ jsxs("div", { className: "color-inputs", children: [
|
|
@@ -2179,7 +2180,7 @@ function RectPanel({
|
|
|
2179
2180
|
] })
|
|
2180
2181
|
] })
|
|
2181
2182
|
] }),
|
|
2182
|
-
/* @__PURE__ */ jsx("div", { className: "flex panel-section", children: /* @__PURE__ */ jsx(
|
|
2183
|
+
operation !== "Apply Changes" && /* @__PURE__ */ jsx("div", { className: "flex panel-section", children: /* @__PURE__ */ jsx(
|
|
2183
2184
|
"button",
|
|
2184
2185
|
{
|
|
2185
2186
|
onClick: handleApplyChanges,
|
|
@@ -2192,6 +2193,7 @@ function RectPanel({
|
|
|
2192
2193
|
const DEFAULT_RECT_PROPS = {
|
|
2193
2194
|
cornerRadius: 0,
|
|
2194
2195
|
fillColor: "#3b82f6",
|
|
2196
|
+
// UI uses 0–100%; element opacity is 0–1
|
|
2195
2197
|
opacity: 100,
|
|
2196
2198
|
strokeColor: "#000000",
|
|
2197
2199
|
lineWidth: 0
|
|
@@ -2206,25 +2208,56 @@ const useRectPanel = ({
|
|
|
2206
2208
|
const [opacity, setOpacity] = useState(DEFAULT_RECT_PROPS.opacity);
|
|
2207
2209
|
const [strokeColor, setStrokeColor] = useState(DEFAULT_RECT_PROPS.strokeColor);
|
|
2208
2210
|
const [lineWidth, setLineWidth] = useState(DEFAULT_RECT_PROPS.lineWidth);
|
|
2211
|
+
const applyLiveChangesToExistingRect = (overrides = {}) => {
|
|
2212
|
+
if (!(selectedElement instanceof RectElement)) {
|
|
2213
|
+
return;
|
|
2214
|
+
}
|
|
2215
|
+
const rectElement = selectedElement;
|
|
2216
|
+
const nextCornerRadius = overrides.cornerRadius ?? cornerRadius;
|
|
2217
|
+
const nextFillColor = overrides.fillColor ?? fillColor;
|
|
2218
|
+
const nextOpacityPercent = overrides.opacity ?? opacity;
|
|
2219
|
+
const nextStrokeColor = overrides.strokeColor ?? strokeColor;
|
|
2220
|
+
const nextLineWidth = overrides.lineWidth ?? lineWidth;
|
|
2221
|
+
rectElement.setCornerRadius(nextCornerRadius);
|
|
2222
|
+
rectElement.setFill(nextFillColor);
|
|
2223
|
+
rectElement.setOpacity(nextOpacityPercent / 100);
|
|
2224
|
+
rectElement.setStrokeColor(nextStrokeColor);
|
|
2225
|
+
rectElement.setLineWidth(nextLineWidth);
|
|
2226
|
+
updateElement == null ? void 0 : updateElement(rectElement);
|
|
2227
|
+
};
|
|
2228
|
+
const handleCornerRadiusChange = (radius) => {
|
|
2229
|
+
setCornerRadius(radius);
|
|
2230
|
+
applyLiveChangesToExistingRect({ cornerRadius: radius });
|
|
2231
|
+
};
|
|
2232
|
+
const handleFillColorChange = (color) => {
|
|
2233
|
+
setFillColor(color);
|
|
2234
|
+
applyLiveChangesToExistingRect({ fillColor: color });
|
|
2235
|
+
};
|
|
2236
|
+
const handleOpacityChange = (nextOpacity) => {
|
|
2237
|
+
setOpacity(nextOpacity);
|
|
2238
|
+
applyLiveChangesToExistingRect({ opacity: nextOpacity });
|
|
2239
|
+
};
|
|
2240
|
+
const handleStrokeColorChange = (color) => {
|
|
2241
|
+
setStrokeColor(color);
|
|
2242
|
+
applyLiveChangesToExistingRect({ strokeColor: color });
|
|
2243
|
+
};
|
|
2244
|
+
const handleLineWidthChange = (width) => {
|
|
2245
|
+
setLineWidth(width);
|
|
2246
|
+
applyLiveChangesToExistingRect({ lineWidth: width });
|
|
2247
|
+
};
|
|
2209
2248
|
const handleApplyChanges = () => {
|
|
2210
|
-
let rectElement;
|
|
2211
2249
|
if (selectedElement instanceof RectElement) {
|
|
2212
|
-
|
|
2213
|
-
rectElement.setCornerRadius(cornerRadius);
|
|
2214
|
-
rectElement.setOpacity(opacity);
|
|
2215
|
-
rectElement.setStrokeColor(strokeColor);
|
|
2216
|
-
rectElement.setLineWidth(lineWidth);
|
|
2217
|
-
updateElement == null ? void 0 : updateElement(rectElement);
|
|
2218
|
-
} else {
|
|
2219
|
-
rectElement = new RectElement(fillColor, { width: 200, height: 200 }).setCornerRadius(cornerRadius).setOpacity(opacity).setStrokeColor(strokeColor).setLineWidth(lineWidth);
|
|
2220
|
-
addElement == null ? void 0 : addElement(rectElement);
|
|
2250
|
+
return;
|
|
2221
2251
|
}
|
|
2252
|
+
const rectElement = new RectElement(fillColor, { width: 200, height: 200 }).setCornerRadius(cornerRadius).setOpacity(opacity / 100).setStrokeColor(strokeColor).setLineWidth(lineWidth);
|
|
2253
|
+
addElement == null ? void 0 : addElement(rectElement);
|
|
2222
2254
|
};
|
|
2223
2255
|
useEffect(() => {
|
|
2224
2256
|
if (selectedElement instanceof RectElement) {
|
|
2225
2257
|
setCornerRadius(selectedElement.getCornerRadius() ?? DEFAULT_RECT_PROPS.cornerRadius);
|
|
2226
2258
|
setFillColor(selectedElement.getFill() ?? DEFAULT_RECT_PROPS.fillColor);
|
|
2227
|
-
|
|
2259
|
+
const elementOpacity = selectedElement.getOpacity();
|
|
2260
|
+
setOpacity(elementOpacity != null ? elementOpacity * 100 : DEFAULT_RECT_PROPS.opacity);
|
|
2228
2261
|
setStrokeColor(selectedElement.getStrokeColor() ?? DEFAULT_RECT_PROPS.strokeColor);
|
|
2229
2262
|
setLineWidth(selectedElement.getLineWidth() ?? DEFAULT_RECT_PROPS.lineWidth);
|
|
2230
2263
|
}
|
|
@@ -2236,11 +2269,11 @@ const useRectPanel = ({
|
|
|
2236
2269
|
strokeColor,
|
|
2237
2270
|
lineWidth,
|
|
2238
2271
|
operation: selectedElement instanceof RectElement ? "Apply Changes" : "Add Rectangle",
|
|
2239
|
-
setCornerRadius,
|
|
2240
|
-
setFillColor,
|
|
2241
|
-
setOpacity,
|
|
2242
|
-
setStrokeColor,
|
|
2243
|
-
setLineWidth,
|
|
2272
|
+
setCornerRadius: handleCornerRadiusChange,
|
|
2273
|
+
setFillColor: handleFillColorChange,
|
|
2274
|
+
setOpacity: handleOpacityChange,
|
|
2275
|
+
setStrokeColor: handleStrokeColorChange,
|
|
2276
|
+
setLineWidth: handleLineWidthChange,
|
|
2244
2277
|
handleApplyChanges
|
|
2245
2278
|
};
|
|
2246
2279
|
};
|
|
@@ -2255,13 +2288,11 @@ function RectPanelContainer(props) {
|
|
|
2255
2288
|
function CirclePanel({
|
|
2256
2289
|
radius,
|
|
2257
2290
|
fillColor,
|
|
2258
|
-
opacity,
|
|
2259
2291
|
strokeColor,
|
|
2260
2292
|
lineWidth,
|
|
2261
2293
|
operation,
|
|
2262
2294
|
setRadius,
|
|
2263
2295
|
setFillColor,
|
|
2264
|
-
setOpacity,
|
|
2265
2296
|
setStrokeColor,
|
|
2266
2297
|
setLineWidth,
|
|
2267
2298
|
handleApplyChanges
|
|
@@ -2311,26 +2342,6 @@ function CirclePanel({
|
|
|
2311
2342
|
)
|
|
2312
2343
|
] })
|
|
2313
2344
|
] }),
|
|
2314
|
-
/* @__PURE__ */ jsxs("div", { className: "panel-section", children: [
|
|
2315
|
-
/* @__PURE__ */ jsx("label", { className: "label-dark", children: "Opacity" }),
|
|
2316
|
-
/* @__PURE__ */ jsxs("div", { className: "slider-container", children: [
|
|
2317
|
-
/* @__PURE__ */ jsx(
|
|
2318
|
-
"input",
|
|
2319
|
-
{
|
|
2320
|
-
type: "range",
|
|
2321
|
-
min: "0",
|
|
2322
|
-
max: "100",
|
|
2323
|
-
value: opacity,
|
|
2324
|
-
onChange: (e) => setOpacity(Number(e.target.value)),
|
|
2325
|
-
className: "slider-purple"
|
|
2326
|
-
}
|
|
2327
|
-
),
|
|
2328
|
-
/* @__PURE__ */ jsxs("span", { className: "slider-value", children: [
|
|
2329
|
-
opacity,
|
|
2330
|
-
"%"
|
|
2331
|
-
] })
|
|
2332
|
-
] })
|
|
2333
|
-
] }),
|
|
2334
2345
|
/* @__PURE__ */ jsxs("div", { className: "panel-section", children: [
|
|
2335
2346
|
/* @__PURE__ */ jsx("label", { className: "label-dark", children: "Stroke Color" }),
|
|
2336
2347
|
/* @__PURE__ */ jsxs("div", { className: "color-inputs", children: [
|
|
@@ -2374,12 +2385,13 @@ function CirclePanel({
|
|
|
2374
2385
|
] })
|
|
2375
2386
|
] })
|
|
2376
2387
|
] }),
|
|
2377
|
-
/* @__PURE__ */ jsx("div", { className: "flex panel-section", children: /* @__PURE__ */ jsx("button", { onClick: handleApplyChanges, className: "btn-primary w-full", children: operation }) })
|
|
2388
|
+
operation !== "Apply Changes" && /* @__PURE__ */ jsx("div", { className: "flex panel-section", children: /* @__PURE__ */ jsx("button", { onClick: handleApplyChanges, className: "btn-primary w-full", children: operation }) })
|
|
2378
2389
|
] });
|
|
2379
2390
|
}
|
|
2380
2391
|
const DEFAULT_CIRCLE_PROPS = {
|
|
2381
2392
|
radius: 50,
|
|
2382
2393
|
fillColor: "#3b82f6",
|
|
2394
|
+
// UI uses 0–100%; element opacity is 0–1
|
|
2383
2395
|
opacity: 100,
|
|
2384
2396
|
strokeColor: "#000000",
|
|
2385
2397
|
lineWidth: 0
|
|
@@ -2394,26 +2406,56 @@ const useCirclePanel = ({
|
|
|
2394
2406
|
const [opacity, setOpacity] = useState(DEFAULT_CIRCLE_PROPS.opacity);
|
|
2395
2407
|
const [strokeColor, setStrokeColor] = useState(DEFAULT_CIRCLE_PROPS.strokeColor);
|
|
2396
2408
|
const [lineWidth, setLineWidth] = useState(DEFAULT_CIRCLE_PROPS.lineWidth);
|
|
2409
|
+
const applyLiveChangesToExistingCircle = (overrides = {}) => {
|
|
2410
|
+
if (!(selectedElement instanceof CircleElement)) {
|
|
2411
|
+
return;
|
|
2412
|
+
}
|
|
2413
|
+
const circleElement = selectedElement;
|
|
2414
|
+
const nextRadius = overrides.radius ?? radius;
|
|
2415
|
+
const nextFillColor = overrides.fillColor ?? fillColor;
|
|
2416
|
+
const nextOpacityPercent = overrides.opacity ?? opacity;
|
|
2417
|
+
const nextStrokeColor = overrides.strokeColor ?? strokeColor;
|
|
2418
|
+
const nextLineWidth = overrides.lineWidth ?? lineWidth;
|
|
2419
|
+
circleElement.setRadius(nextRadius);
|
|
2420
|
+
circleElement.setFill(nextFillColor);
|
|
2421
|
+
circleElement.setOpacity(nextOpacityPercent / 100);
|
|
2422
|
+
circleElement.setStrokeColor(nextStrokeColor);
|
|
2423
|
+
circleElement.setLineWidth(nextLineWidth);
|
|
2424
|
+
updateElement == null ? void 0 : updateElement(circleElement);
|
|
2425
|
+
};
|
|
2426
|
+
const handleRadiusChange = (nextRadius) => {
|
|
2427
|
+
setRadius(nextRadius);
|
|
2428
|
+
applyLiveChangesToExistingCircle({ radius: nextRadius });
|
|
2429
|
+
};
|
|
2430
|
+
const handleFillColorChange = (color) => {
|
|
2431
|
+
setFillColor(color);
|
|
2432
|
+
applyLiveChangesToExistingCircle({ fillColor: color });
|
|
2433
|
+
};
|
|
2434
|
+
const handleOpacityChange = (nextOpacity) => {
|
|
2435
|
+
setOpacity(nextOpacity);
|
|
2436
|
+
applyLiveChangesToExistingCircle({ opacity: nextOpacity });
|
|
2437
|
+
};
|
|
2438
|
+
const handleStrokeColorChange = (color) => {
|
|
2439
|
+
setStrokeColor(color);
|
|
2440
|
+
applyLiveChangesToExistingCircle({ strokeColor: color });
|
|
2441
|
+
};
|
|
2442
|
+
const handleLineWidthChange = (width) => {
|
|
2443
|
+
setLineWidth(width);
|
|
2444
|
+
applyLiveChangesToExistingCircle({ lineWidth: width });
|
|
2445
|
+
};
|
|
2397
2446
|
const handleApplyChanges = () => {
|
|
2398
|
-
let circleElement;
|
|
2399
2447
|
if (selectedElement instanceof CircleElement) {
|
|
2400
|
-
|
|
2401
|
-
circleElement.setRadius(radius);
|
|
2402
|
-
circleElement.setFill(fillColor);
|
|
2403
|
-
circleElement.setOpacity(opacity);
|
|
2404
|
-
circleElement.setStrokeColor(strokeColor);
|
|
2405
|
-
circleElement.setLineWidth(lineWidth);
|
|
2406
|
-
updateElement == null ? void 0 : updateElement(circleElement);
|
|
2407
|
-
} else {
|
|
2408
|
-
circleElement = new CircleElement(fillColor, radius).setOpacity(opacity).setStrokeColor(strokeColor).setLineWidth(lineWidth);
|
|
2409
|
-
addElement == null ? void 0 : addElement(circleElement);
|
|
2448
|
+
return;
|
|
2410
2449
|
}
|
|
2450
|
+
const circleElement = new CircleElement(fillColor, radius).setOpacity(opacity / 100).setStrokeColor(strokeColor).setLineWidth(lineWidth);
|
|
2451
|
+
addElement == null ? void 0 : addElement(circleElement);
|
|
2411
2452
|
};
|
|
2412
2453
|
useEffect(() => {
|
|
2413
2454
|
if (selectedElement instanceof CircleElement) {
|
|
2414
2455
|
setRadius(selectedElement.getRadius() ?? DEFAULT_CIRCLE_PROPS.radius);
|
|
2415
2456
|
setFillColor(selectedElement.getFill() ?? DEFAULT_CIRCLE_PROPS.fillColor);
|
|
2416
|
-
|
|
2457
|
+
const elementOpacity = selectedElement.getOpacity();
|
|
2458
|
+
setOpacity(elementOpacity != null ? elementOpacity * 100 : DEFAULT_CIRCLE_PROPS.opacity);
|
|
2417
2459
|
setStrokeColor(selectedElement.getStrokeColor() ?? DEFAULT_CIRCLE_PROPS.strokeColor);
|
|
2418
2460
|
setLineWidth(selectedElement.getLineWidth() ?? DEFAULT_CIRCLE_PROPS.lineWidth);
|
|
2419
2461
|
}
|
|
@@ -2425,11 +2467,11 @@ const useCirclePanel = ({
|
|
|
2425
2467
|
strokeColor,
|
|
2426
2468
|
lineWidth,
|
|
2427
2469
|
operation: selectedElement instanceof CircleElement ? "Apply Changes" : "Add Circle",
|
|
2428
|
-
setRadius,
|
|
2429
|
-
setFillColor,
|
|
2430
|
-
setOpacity,
|
|
2431
|
-
setStrokeColor,
|
|
2432
|
-
setLineWidth,
|
|
2470
|
+
setRadius: handleRadiusChange,
|
|
2471
|
+
setFillColor: handleFillColorChange,
|
|
2472
|
+
setOpacity: handleOpacityChange,
|
|
2473
|
+
setStrokeColor: handleStrokeColorChange,
|
|
2474
|
+
setLineWidth: handleLineWidthChange,
|
|
2433
2475
|
handleApplyChanges
|
|
2434
2476
|
};
|
|
2435
2477
|
};
|
|
@@ -2441,16 +2483,16 @@ function CirclePanelContainer(props) {
|
|
|
2441
2483
|
});
|
|
2442
2484
|
return /* @__PURE__ */ jsx(CirclePanel, { ...circlePanelProps });
|
|
2443
2485
|
}
|
|
2444
|
-
function
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2486
|
+
function CaptionsPanel({
|
|
2487
|
+
captions,
|
|
2488
|
+
addCaption,
|
|
2489
|
+
splitCaption,
|
|
2490
|
+
deleteCaption,
|
|
2491
|
+
updateCaption
|
|
2450
2492
|
}) {
|
|
2451
2493
|
return /* @__PURE__ */ jsxs("div", { className: "panel-container", children: [
|
|
2452
|
-
/* @__PURE__ */ jsx("h3", { className: "panel-title", children: "
|
|
2453
|
-
|
|
2494
|
+
/* @__PURE__ */ jsx("h3", { className: "panel-title", children: "Captions" }),
|
|
2495
|
+
captions.map((caption, i) => /* @__PURE__ */ jsxs(
|
|
2454
2496
|
"div",
|
|
2455
2497
|
{
|
|
2456
2498
|
className: "panel-section gap-2",
|
|
@@ -2459,9 +2501,9 @@ function SubtitlesPanel({
|
|
|
2459
2501
|
"input",
|
|
2460
2502
|
{
|
|
2461
2503
|
type: "text",
|
|
2462
|
-
placeholder: "Enter
|
|
2463
|
-
value:
|
|
2464
|
-
onChange: (e) =>
|
|
2504
|
+
placeholder: "Enter caption text",
|
|
2505
|
+
value: caption.t,
|
|
2506
|
+
onChange: (e) => updateCaption(i, { ...caption, t: e.target.value }),
|
|
2465
2507
|
className: "input-dark"
|
|
2466
2508
|
}
|
|
2467
2509
|
) }),
|
|
@@ -2469,18 +2511,18 @@ function SubtitlesPanel({
|
|
|
2469
2511
|
/* @__PURE__ */ jsx(
|
|
2470
2512
|
"button",
|
|
2471
2513
|
{
|
|
2472
|
-
onClick: () =>
|
|
2514
|
+
onClick: () => splitCaption(i),
|
|
2473
2515
|
className: "btn-ghost",
|
|
2474
|
-
title: "Split
|
|
2516
|
+
title: "Split caption",
|
|
2475
2517
|
children: /* @__PURE__ */ jsx(Scissors, { className: "icon-sm" })
|
|
2476
2518
|
}
|
|
2477
2519
|
),
|
|
2478
2520
|
/* @__PURE__ */ jsx(
|
|
2479
2521
|
"button",
|
|
2480
2522
|
{
|
|
2481
|
-
onClick: () =>
|
|
2523
|
+
onClick: () => deleteCaption(i),
|
|
2482
2524
|
className: "btn-ghost",
|
|
2483
|
-
title: "Delete
|
|
2525
|
+
title: "Delete caption",
|
|
2484
2526
|
children: /* @__PURE__ */ jsx(Trash2, { className: "icon-sm", color: "var(--color-red-500)" })
|
|
2485
2527
|
}
|
|
2486
2528
|
)
|
|
@@ -2489,7 +2531,7 @@ function SubtitlesPanel({
|
|
|
2489
2531
|
},
|
|
2490
2532
|
i
|
|
2491
2533
|
)),
|
|
2492
|
-
/* @__PURE__ */ jsx("div", { className: "panel-section", children: /* @__PURE__ */ jsx("button", { onClick:
|
|
2534
|
+
/* @__PURE__ */ jsx("div", { className: "panel-section", children: /* @__PURE__ */ jsx("button", { onClick: addCaption, className: "btn-primary w-full", title: "Add caption", children: "Add" }) })
|
|
2493
2535
|
] });
|
|
2494
2536
|
}
|
|
2495
2537
|
const CAPTION_PROPS = {
|
|
@@ -2544,16 +2586,16 @@ const CAPTION_PROPS = {
|
|
|
2544
2586
|
shadowBlur: 5
|
|
2545
2587
|
}
|
|
2546
2588
|
};
|
|
2547
|
-
const
|
|
2548
|
-
const [
|
|
2549
|
-
const
|
|
2589
|
+
const useCaptionsPanel = () => {
|
|
2590
|
+
const [captions, setCaptions] = useState([]);
|
|
2591
|
+
const captionsTrack = useRef(null);
|
|
2550
2592
|
const { editor } = useTimelineContext();
|
|
2551
|
-
const
|
|
2552
|
-
const
|
|
2553
|
-
if (
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2593
|
+
const fetchCaptions = async () => {
|
|
2594
|
+
const editorCaptionsTrack = editor.getCaptionsTrack();
|
|
2595
|
+
if (editorCaptionsTrack) {
|
|
2596
|
+
captionsTrack.current = editorCaptionsTrack;
|
|
2597
|
+
setCaptions(
|
|
2598
|
+
editorCaptionsTrack.getElements().map((element) => ({
|
|
2557
2599
|
s: element.getStart(),
|
|
2558
2600
|
e: element.getEnd(),
|
|
2559
2601
|
t: element.getText()
|
|
@@ -2562,12 +2604,12 @@ const useSubtitlesPanel = () => {
|
|
|
2562
2604
|
}
|
|
2563
2605
|
};
|
|
2564
2606
|
useEffect(() => {
|
|
2565
|
-
|
|
2607
|
+
fetchCaptions();
|
|
2566
2608
|
}, []);
|
|
2567
|
-
const
|
|
2609
|
+
const checkCaptionsTrack = () => {
|
|
2568
2610
|
var _a;
|
|
2569
|
-
if (!
|
|
2570
|
-
|
|
2611
|
+
if (!captionsTrack.current) {
|
|
2612
|
+
captionsTrack.current = editor.addTrack("Caption", "caption");
|
|
2571
2613
|
const props = {
|
|
2572
2614
|
capStyle: CAPTION_STYLE.WORD_BG_HIGHLIGHT,
|
|
2573
2615
|
...CAPTION_PROPS[CAPTION_STYLE.WORD_BG_HIGHLIGHT],
|
|
@@ -2575,61 +2617,61 @@ const useSubtitlesPanel = () => {
|
|
|
2575
2617
|
y: 200,
|
|
2576
2618
|
applyToAll: true
|
|
2577
2619
|
};
|
|
2578
|
-
(_a =
|
|
2620
|
+
(_a = captionsTrack.current) == null ? void 0 : _a.setProps(props);
|
|
2579
2621
|
}
|
|
2580
2622
|
};
|
|
2581
|
-
const
|
|
2582
|
-
const
|
|
2583
|
-
if (
|
|
2584
|
-
|
|
2585
|
-
}
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2623
|
+
const addCaption = () => {
|
|
2624
|
+
const newCaption = { s: 0, e: 0, t: "New Caption" };
|
|
2625
|
+
if (captions.length > 0) {
|
|
2626
|
+
newCaption.s = captions[captions.length - 1].e;
|
|
2627
|
+
}
|
|
2628
|
+
newCaption.e = newCaption.s + 1;
|
|
2629
|
+
setCaptions([...captions, newCaption]);
|
|
2630
|
+
checkCaptionsTrack();
|
|
2589
2631
|
const captionElement = new CaptionElement(
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2632
|
+
newCaption.t,
|
|
2633
|
+
newCaption.s,
|
|
2634
|
+
newCaption.e
|
|
2593
2635
|
);
|
|
2594
|
-
editor.addElementToTrack(
|
|
2636
|
+
editor.addElementToTrack(captionsTrack.current, captionElement);
|
|
2595
2637
|
};
|
|
2596
|
-
const
|
|
2597
|
-
if (
|
|
2598
|
-
const element =
|
|
2638
|
+
const splitCaption = async (index) => {
|
|
2639
|
+
if (captionsTrack.current) {
|
|
2640
|
+
const element = captionsTrack.current.getElements()[index];
|
|
2599
2641
|
const splitResult = await editor.splitElement(
|
|
2600
2642
|
element,
|
|
2601
2643
|
element.getStart() + element.getDuration() / 2
|
|
2602
2644
|
);
|
|
2603
2645
|
if (splitResult.success) {
|
|
2604
|
-
|
|
2646
|
+
fetchCaptions();
|
|
2605
2647
|
}
|
|
2606
2648
|
}
|
|
2607
2649
|
};
|
|
2608
|
-
const
|
|
2609
|
-
|
|
2610
|
-
if (
|
|
2611
|
-
editor.removeElement(
|
|
2650
|
+
const deleteCaption = (index) => {
|
|
2651
|
+
setCaptions(captions.filter((_, i) => i !== index));
|
|
2652
|
+
if (captionsTrack.current) {
|
|
2653
|
+
editor.removeElement(captionsTrack.current.getElements()[index]);
|
|
2612
2654
|
}
|
|
2613
2655
|
};
|
|
2614
|
-
const
|
|
2615
|
-
|
|
2616
|
-
if (
|
|
2617
|
-
const element =
|
|
2618
|
-
element.setText(
|
|
2656
|
+
const updateCaption = (index, caption) => {
|
|
2657
|
+
setCaptions(captions.map((sub, i) => i === index ? caption : sub));
|
|
2658
|
+
if (captionsTrack.current) {
|
|
2659
|
+
const element = captionsTrack.current.getElements()[index];
|
|
2660
|
+
element.setText(caption.t);
|
|
2619
2661
|
editor.updateElement(element);
|
|
2620
2662
|
}
|
|
2621
2663
|
};
|
|
2622
2664
|
return {
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2665
|
+
captions,
|
|
2666
|
+
addCaption,
|
|
2667
|
+
splitCaption,
|
|
2668
|
+
deleteCaption,
|
|
2669
|
+
updateCaption
|
|
2628
2670
|
};
|
|
2629
2671
|
};
|
|
2630
|
-
function
|
|
2631
|
-
const
|
|
2632
|
-
return /* @__PURE__ */ jsx(
|
|
2672
|
+
function CaptionsPanelContainer() {
|
|
2673
|
+
const captionsPanelProps = useCaptionsPanel();
|
|
2674
|
+
return /* @__PURE__ */ jsx(CaptionsPanel, { ...captionsPanelProps });
|
|
2633
2675
|
}
|
|
2634
2676
|
const ElementPanelContainer = ({
|
|
2635
2677
|
selectedTool,
|
|
@@ -2682,16 +2724,6 @@ const ElementPanelContainer = ({
|
|
|
2682
2724
|
updateElement
|
|
2683
2725
|
}
|
|
2684
2726
|
);
|
|
2685
|
-
case "icon":
|
|
2686
|
-
return /* @__PURE__ */ jsx(
|
|
2687
|
-
IconPanelContainer,
|
|
2688
|
-
{
|
|
2689
|
-
videoResolution,
|
|
2690
|
-
selectedElement,
|
|
2691
|
-
addElement: addNewElement,
|
|
2692
|
-
updateElement
|
|
2693
|
-
}
|
|
2694
|
-
);
|
|
2695
2727
|
case "rect":
|
|
2696
2728
|
return /* @__PURE__ */ jsx(
|
|
2697
2729
|
RectPanelContainer,
|
|
@@ -2712,8 +2744,8 @@ const ElementPanelContainer = ({
|
|
|
2712
2744
|
updateElement
|
|
2713
2745
|
}
|
|
2714
2746
|
);
|
|
2715
|
-
case "
|
|
2716
|
-
return /* @__PURE__ */ jsx(
|
|
2747
|
+
case "caption":
|
|
2748
|
+
return /* @__PURE__ */ jsx(CaptionsPanelContainer, {});
|
|
2717
2749
|
default:
|
|
2718
2750
|
return /* @__PURE__ */ jsx("div", { className: "panel-container", children: /* @__PURE__ */ jsx("div", { className: "empty-state", children: /* @__PURE__ */ jsxs("div", { className: "empty-state-content", children: [
|
|
2719
2751
|
/* @__PURE__ */ jsx(WandSparkles, { className: "empty-state-icon" }),
|
|
@@ -2723,159 +2755,37 @@ const ElementPanelContainer = ({
|
|
|
2723
2755
|
};
|
|
2724
2756
|
return renderLibrary();
|
|
2725
2757
|
};
|
|
2726
|
-
|
|
2727
|
-
[
|
|
2728
|
-
"
|
|
2729
|
-
{
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
[
|
|
2737
|
-
"animations",
|
|
2738
|
-
{
|
|
2739
|
-
id: "animations",
|
|
2740
|
-
name: "Animations",
|
|
2741
|
-
icon: "Zap",
|
|
2742
|
-
description: "Animations"
|
|
2743
|
-
}
|
|
2744
|
-
],
|
|
2745
|
-
[
|
|
2746
|
-
"text-effects",
|
|
2747
|
-
{
|
|
2748
|
-
id: "text-effects",
|
|
2749
|
-
name: "Text Effects",
|
|
2750
|
-
icon: "SparklesIcon",
|
|
2751
|
-
description: "Text Effects"
|
|
2752
|
-
}
|
|
2753
|
-
],
|
|
2754
|
-
[
|
|
2755
|
-
"color-effects",
|
|
2756
|
-
{
|
|
2757
|
-
id: "color-effects",
|
|
2758
|
-
name: "Color Effects",
|
|
2759
|
-
icon: "Image",
|
|
2760
|
-
description: "Color Effects"
|
|
2761
|
-
}
|
|
2762
|
-
],
|
|
2763
|
-
[
|
|
2764
|
-
"playback-props",
|
|
2765
|
-
{
|
|
2766
|
-
id: "playback-props",
|
|
2767
|
-
name: "Playback Props",
|
|
2768
|
-
icon: "Music",
|
|
2769
|
-
description: "Playback Properties"
|
|
2770
|
-
}
|
|
2771
|
-
],
|
|
2772
|
-
[
|
|
2773
|
-
"subtitle-style",
|
|
2774
|
-
{
|
|
2775
|
-
id: "subtitle-style",
|
|
2776
|
-
name: "Subtitle Style",
|
|
2777
|
-
icon: "MessageSquare",
|
|
2778
|
-
description: "Subtitle Style"
|
|
2779
|
-
}
|
|
2780
|
-
],
|
|
2781
|
-
[
|
|
2782
|
-
"generate-subtitles",
|
|
2783
|
-
{
|
|
2784
|
-
id: "generate-subtitles",
|
|
2785
|
-
name: "Generate Subtitles",
|
|
2786
|
-
icon: "Subtitles",
|
|
2787
|
-
description: "Generate Subtitles"
|
|
2788
|
-
}
|
|
2789
|
-
]
|
|
2790
|
-
]);
|
|
2791
|
-
const getIcon = (iconName) => {
|
|
2792
|
-
switch (iconName) {
|
|
2793
|
-
case "Type":
|
|
2794
|
-
return Type;
|
|
2795
|
-
case "Infinity":
|
|
2796
|
-
return Infinity;
|
|
2797
|
-
case "Image":
|
|
2798
|
-
return Image;
|
|
2799
|
-
case "Music":
|
|
2800
|
-
return Music;
|
|
2801
|
-
case "Subtitles":
|
|
2802
|
-
return Captions;
|
|
2803
|
-
case "MessageSquare":
|
|
2804
|
-
return MessageSquare;
|
|
2805
|
-
case "Settings":
|
|
2806
|
-
return Settings;
|
|
2807
|
-
case "SparklesIcon":
|
|
2808
|
-
return Sparkles;
|
|
2809
|
-
case "Zap":
|
|
2810
|
-
return Zap;
|
|
2811
|
-
default:
|
|
2812
|
-
return Plus;
|
|
2813
|
-
}
|
|
2814
|
-
};
|
|
2815
|
-
function PropsToolbar({
|
|
2816
|
-
selectedElement,
|
|
2817
|
-
selectedProp,
|
|
2818
|
-
setSelectedProp
|
|
2819
|
-
}) {
|
|
2820
|
-
const availableSections = useMemo(() => {
|
|
2821
|
-
const sections = [];
|
|
2822
|
-
if (selectedElement instanceof TextElement) {
|
|
2823
|
-
sections.push(propsCategories.get("element-props"));
|
|
2824
|
-
sections.push(propsCategories.get("animations"));
|
|
2825
|
-
sections.push(propsCategories.get("text-effects"));
|
|
2826
|
-
} else if (selectedElement instanceof ImageElement) {
|
|
2827
|
-
sections.push(propsCategories.get("element-props"));
|
|
2828
|
-
sections.push(propsCategories.get("animations"));
|
|
2829
|
-
sections.push(propsCategories.get("color-effects"));
|
|
2830
|
-
} else if (selectedElement instanceof VideoElement) {
|
|
2831
|
-
sections.push(propsCategories.get("element-props"));
|
|
2832
|
-
sections.push(propsCategories.get("animations"));
|
|
2833
|
-
sections.push(propsCategories.get("color-effects"));
|
|
2834
|
-
sections.push(propsCategories.get("playback-props"));
|
|
2835
|
-
sections.push(propsCategories.get("generate-subtitles"));
|
|
2836
|
-
} else if (selectedElement instanceof AudioElement) {
|
|
2837
|
-
sections.push(propsCategories.get("element-props"));
|
|
2838
|
-
sections.push(propsCategories.get("playback-props"));
|
|
2839
|
-
} else if (selectedElement instanceof CircleElement) {
|
|
2840
|
-
sections.push(propsCategories.get("element-props"));
|
|
2841
|
-
sections.push(propsCategories.get("animations"));
|
|
2842
|
-
} else if (selectedElement instanceof RectElement) {
|
|
2843
|
-
sections.push(propsCategories.get("element-props"));
|
|
2844
|
-
sections.push(propsCategories.get("animations"));
|
|
2845
|
-
} else if (selectedElement instanceof IconElement) {
|
|
2846
|
-
sections.push(propsCategories.get("element-props"));
|
|
2847
|
-
sections.push(propsCategories.get("animations"));
|
|
2848
|
-
} else if (selectedElement instanceof CaptionElement) {
|
|
2849
|
-
sections.push(propsCategories.get("element-props"));
|
|
2850
|
-
sections.push(propsCategories.get("animations"));
|
|
2851
|
-
sections.push(propsCategories.get("subtitle-style"));
|
|
2852
|
-
}
|
|
2853
|
-
return sections;
|
|
2854
|
-
}, [selectedElement]);
|
|
2855
|
-
useEffect(() => {
|
|
2856
|
-
if (availableSections == null ? void 0 : availableSections.length) {
|
|
2857
|
-
if (availableSections.map((section) => section.id).indexOf(selectedProp) === -1) {
|
|
2858
|
-
setSelectedProp(availableSections[0].id);
|
|
2859
|
-
}
|
|
2860
|
-
}
|
|
2861
|
-
}, [availableSections]);
|
|
2862
|
-
return /* @__PURE__ */ jsx("div", { className: "sidebar", children: availableSections.map((tool) => {
|
|
2863
|
-
const Icon2 = getIcon(tool.icon);
|
|
2864
|
-
const isSelected = selectedProp === tool.id;
|
|
2865
|
-
return /* @__PURE__ */ jsxs(
|
|
2758
|
+
function PropertyRow({ label, children, secondary }) {
|
|
2759
|
+
return /* @__PURE__ */ jsxs("div", { className: "property-row", children: [
|
|
2760
|
+
/* @__PURE__ */ jsx("div", { className: "property-row-label", children: /* @__PURE__ */ jsx("span", { className: "property-label", children: label }) }),
|
|
2761
|
+
/* @__PURE__ */ jsx("div", { className: "property-row-control", children }),
|
|
2762
|
+
secondary && /* @__PURE__ */ jsx("div", { className: "property-row-secondary", children: secondary })
|
|
2763
|
+
] });
|
|
2764
|
+
}
|
|
2765
|
+
function AccordionItem({ title, icon, children, isOpen, onToggle }) {
|
|
2766
|
+
return /* @__PURE__ */ jsxs("div", { className: "accordion-item", children: [
|
|
2767
|
+
/* @__PURE__ */ jsxs(
|
|
2866
2768
|
"div",
|
|
2867
2769
|
{
|
|
2868
|
-
onClick:
|
|
2869
|
-
className:
|
|
2870
|
-
title: `${tool.name}${tool.shortcut ? ` (${tool.shortcut})` : ""}`,
|
|
2770
|
+
onClick: onToggle,
|
|
2771
|
+
className: "accordion-header",
|
|
2871
2772
|
children: [
|
|
2872
|
-
/* @__PURE__ */
|
|
2873
|
-
|
|
2773
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-container", children: [
|
|
2774
|
+
/* @__PURE__ */ jsx("div", { className: "accent-purple", children: icon }),
|
|
2775
|
+
/* @__PURE__ */ jsx("span", { className: "property-title", children: title })
|
|
2776
|
+
] }),
|
|
2777
|
+
isOpen ? /* @__PURE__ */ jsx(ChevronDown, { className: "icon-sm accent-purple" }) : /* @__PURE__ */ jsx(ChevronRight, { className: "icon-sm accent-purple" })
|
|
2874
2778
|
]
|
|
2875
|
-
}
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2779
|
+
}
|
|
2780
|
+
),
|
|
2781
|
+
/* @__PURE__ */ jsx(
|
|
2782
|
+
"div",
|
|
2783
|
+
{
|
|
2784
|
+
className: `accordion-content ${isOpen ? "expanded" : ""}`,
|
|
2785
|
+
children: /* @__PURE__ */ jsx("div", { className: "accordion-panel", children })
|
|
2786
|
+
}
|
|
2787
|
+
)
|
|
2788
|
+
] });
|
|
2879
2789
|
}
|
|
2880
2790
|
function ElementProps({ selectedElement, updateElement }) {
|
|
2881
2791
|
const opacity = (selectedElement == null ? void 0 : selectedElement.getOpacity()) || 1;
|
|
@@ -2899,77 +2809,134 @@ function ElementProps({ selectedElement, updateElement }) {
|
|
|
2899
2809
|
updateElement == null ? void 0 : updateElement(selectedElement);
|
|
2900
2810
|
}
|
|
2901
2811
|
};
|
|
2812
|
+
const handleDimensionsChange = (width, height) => {
|
|
2813
|
+
if (!selectedElement) return;
|
|
2814
|
+
if (selectedElement instanceof RectElement) {
|
|
2815
|
+
const size = selectedElement.getSize();
|
|
2816
|
+
selectedElement.setSize({ width: width ?? size.width, height: height ?? size.height });
|
|
2817
|
+
updateElement == null ? void 0 : updateElement(selectedElement);
|
|
2818
|
+
} else if (selectedElement instanceof CircleElement) {
|
|
2819
|
+
const dims = {
|
|
2820
|
+
width: selectedElement.getRadius() * 2,
|
|
2821
|
+
height: selectedElement.getRadius() * 2
|
|
2822
|
+
};
|
|
2823
|
+
const newDiameter = width !== void 0 && width !== dims.width ? width : height ?? dims.height;
|
|
2824
|
+
selectedElement.setRadius(newDiameter / 2);
|
|
2825
|
+
updateElement == null ? void 0 : updateElement(selectedElement);
|
|
2826
|
+
}
|
|
2827
|
+
};
|
|
2828
|
+
const hasShapeDimensions = selectedElement instanceof RectElement || selectedElement instanceof CircleElement;
|
|
2829
|
+
let dimensions = null;
|
|
2830
|
+
if (selectedElement instanceof RectElement) {
|
|
2831
|
+
dimensions = selectedElement.getSize();
|
|
2832
|
+
} else if (selectedElement instanceof CircleElement) {
|
|
2833
|
+
const r = selectedElement.getRadius();
|
|
2834
|
+
dimensions = { width: r * 2, height: r * 2 };
|
|
2835
|
+
}
|
|
2836
|
+
const [isTransformOpen, setIsTransformOpen] = useState(false);
|
|
2902
2837
|
return /* @__PURE__ */ jsxs("div", { className: "panel-container", children: [
|
|
2903
|
-
/* @__PURE__ */ jsx("div", { className: "panel-title", children: "
|
|
2904
|
-
/* @__PURE__ */
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2838
|
+
/* @__PURE__ */ jsx("div", { className: "panel-title", children: "Properties" }),
|
|
2839
|
+
/* @__PURE__ */ jsx(
|
|
2840
|
+
AccordionItem,
|
|
2841
|
+
{
|
|
2842
|
+
title: "Transform",
|
|
2843
|
+
icon: /* @__PURE__ */ jsx(Ruler, { className: "icon-sm" }),
|
|
2844
|
+
isOpen: isTransformOpen,
|
|
2845
|
+
onToggle: () => setIsTransformOpen((open) => !open),
|
|
2846
|
+
children: /* @__PURE__ */ jsxs("div", { className: "properties-group", children: [
|
|
2847
|
+
/* @__PURE__ */ jsxs("div", { className: "property-section", children: [
|
|
2848
|
+
/* @__PURE__ */ jsx(PropertyRow, { label: "Position X", children: /* @__PURE__ */ jsx(
|
|
2849
|
+
"input",
|
|
2850
|
+
{
|
|
2851
|
+
type: "number",
|
|
2852
|
+
value: position.x ?? 0,
|
|
2853
|
+
onChange: (e) => handlePositionChange({ x: Number(e.target.value) }),
|
|
2854
|
+
className: "input-dark"
|
|
2855
|
+
}
|
|
2856
|
+
) }),
|
|
2857
|
+
/* @__PURE__ */ jsx(PropertyRow, { label: "Position Y", children: /* @__PURE__ */ jsx(
|
|
2858
|
+
"input",
|
|
2859
|
+
{
|
|
2860
|
+
type: "number",
|
|
2861
|
+
value: position.y ?? 0,
|
|
2862
|
+
onChange: (e) => handlePositionChange({ y: Number(e.target.value) }),
|
|
2863
|
+
className: "input-dark"
|
|
2864
|
+
}
|
|
2865
|
+
) })
|
|
2866
|
+
] }),
|
|
2867
|
+
hasShapeDimensions && dimensions && /* @__PURE__ */ jsxs("div", { className: "property-section", children: [
|
|
2868
|
+
/* @__PURE__ */ jsx(PropertyRow, { label: "Width", children: /* @__PURE__ */ jsx(
|
|
2869
|
+
"input",
|
|
2870
|
+
{
|
|
2871
|
+
type: "number",
|
|
2872
|
+
min: 1,
|
|
2873
|
+
value: Math.round(dimensions.width),
|
|
2874
|
+
onChange: (e) => handleDimensionsChange(
|
|
2875
|
+
Number(e.target.value),
|
|
2876
|
+
dimensions.height
|
|
2877
|
+
),
|
|
2878
|
+
className: "input-dark"
|
|
2879
|
+
}
|
|
2880
|
+
) }),
|
|
2881
|
+
/* @__PURE__ */ jsx(PropertyRow, { label: "Height", children: /* @__PURE__ */ jsx(
|
|
2882
|
+
"input",
|
|
2883
|
+
{
|
|
2884
|
+
type: "number",
|
|
2885
|
+
min: 1,
|
|
2886
|
+
value: Math.round(dimensions.height),
|
|
2887
|
+
onChange: (e) => handleDimensionsChange(
|
|
2888
|
+
dimensions.width,
|
|
2889
|
+
Number(e.target.value)
|
|
2890
|
+
),
|
|
2891
|
+
className: "input-dark"
|
|
2892
|
+
}
|
|
2893
|
+
) })
|
|
2894
|
+
] }),
|
|
2895
|
+
/* @__PURE__ */ jsx("div", { className: "property-section", children: /* @__PURE__ */ jsx(
|
|
2896
|
+
PropertyRow,
|
|
2911
2897
|
{
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2898
|
+
label: "Opacity",
|
|
2899
|
+
secondary: /* @__PURE__ */ jsxs("span", { children: [
|
|
2900
|
+
Math.round((opacity ?? 1) * 100),
|
|
2901
|
+
"%"
|
|
2902
|
+
] }),
|
|
2903
|
+
children: /* @__PURE__ */ jsx(
|
|
2904
|
+
"input",
|
|
2905
|
+
{
|
|
2906
|
+
type: "range",
|
|
2907
|
+
min: "0",
|
|
2908
|
+
max: "100",
|
|
2909
|
+
value: (opacity ?? 1) * 100,
|
|
2910
|
+
onChange: (e) => handleOpacityChange(Number(e.target.value) / 100),
|
|
2911
|
+
className: "slider-purple"
|
|
2912
|
+
}
|
|
2913
|
+
)
|
|
2916
2914
|
}
|
|
2917
|
-
)
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
/* @__PURE__ */ jsx("label", { className: "label-small", children: "Y" }),
|
|
2921
|
-
/* @__PURE__ */ jsx(
|
|
2922
|
-
"input",
|
|
2915
|
+
) }),
|
|
2916
|
+
/* @__PURE__ */ jsx("div", { className: "property-section", children: /* @__PURE__ */ jsx(
|
|
2917
|
+
PropertyRow,
|
|
2923
2918
|
{
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2919
|
+
label: "Rotation",
|
|
2920
|
+
secondary: /* @__PURE__ */ jsxs("span", { children: [
|
|
2921
|
+
Math.round(rotation ?? 0),
|
|
2922
|
+
"°"
|
|
2923
|
+
] }),
|
|
2924
|
+
children: /* @__PURE__ */ jsx(
|
|
2925
|
+
"input",
|
|
2926
|
+
{
|
|
2927
|
+
type: "range",
|
|
2928
|
+
min: "0",
|
|
2929
|
+
max: "360",
|
|
2930
|
+
value: rotation ?? 0,
|
|
2931
|
+
onChange: (e) => handleRotationChange(Number(e.target.value)),
|
|
2932
|
+
className: "slider-purple"
|
|
2933
|
+
}
|
|
2934
|
+
)
|
|
2928
2935
|
}
|
|
2929
|
-
)
|
|
2930
|
-
] })
|
|
2931
|
-
] })
|
|
2932
|
-
] }),
|
|
2933
|
-
/* @__PURE__ */ jsxs("div", { className: "panel-section", children: [
|
|
2934
|
-
/* @__PURE__ */ jsx("label", { className: "label-dark", children: "Opacity" }),
|
|
2935
|
-
/* @__PURE__ */ jsxs("div", { className: "slider-container", children: [
|
|
2936
|
-
/* @__PURE__ */ jsx(
|
|
2937
|
-
"input",
|
|
2938
|
-
{
|
|
2939
|
-
type: "range",
|
|
2940
|
-
min: "0",
|
|
2941
|
-
max: "100",
|
|
2942
|
-
value: (opacity ?? 1) * 100,
|
|
2943
|
-
onChange: (e) => handleOpacityChange(Number(e.target.value) / 100),
|
|
2944
|
-
className: "slider-purple"
|
|
2945
|
-
}
|
|
2946
|
-
),
|
|
2947
|
-
/* @__PURE__ */ jsxs("span", { className: "slider-value", children: [
|
|
2948
|
-
Math.round((opacity ?? 1) * 100),
|
|
2949
|
-
"%"
|
|
2950
|
-
] })
|
|
2951
|
-
] })
|
|
2952
|
-
] }),
|
|
2953
|
-
/* @__PURE__ */ jsxs("div", { className: "panel-section", children: [
|
|
2954
|
-
/* @__PURE__ */ jsx("label", { className: "label-dark", children: "Rotation" }),
|
|
2955
|
-
/* @__PURE__ */ jsxs("div", { className: "slider-container", children: [
|
|
2956
|
-
/* @__PURE__ */ jsx(
|
|
2957
|
-
"input",
|
|
2958
|
-
{
|
|
2959
|
-
type: "range",
|
|
2960
|
-
min: "0",
|
|
2961
|
-
max: "360",
|
|
2962
|
-
value: rotation ?? 0,
|
|
2963
|
-
onChange: (e) => handleRotationChange(Number(e.target.value)),
|
|
2964
|
-
className: "slider-purple"
|
|
2965
|
-
}
|
|
2966
|
-
),
|
|
2967
|
-
/* @__PURE__ */ jsxs("span", { className: "slider-value", children: [
|
|
2968
|
-
rotation ?? 0,
|
|
2969
|
-
"°"
|
|
2936
|
+
) })
|
|
2970
2937
|
] })
|
|
2971
|
-
|
|
2972
|
-
|
|
2938
|
+
}
|
|
2939
|
+
)
|
|
2973
2940
|
] });
|
|
2974
2941
|
}
|
|
2975
2942
|
function TextEffects({
|
|
@@ -3000,70 +2967,72 @@ function TextEffects({
|
|
|
3000
2967
|
selectedElement.setTextEffect(effect);
|
|
3001
2968
|
updateElement == null ? void 0 : updateElement(selectedElement);
|
|
3002
2969
|
};
|
|
2970
|
+
const [isEffectsOpen, setIsEffectsOpen] = useState(false);
|
|
3003
2971
|
return /* @__PURE__ */ jsxs("div", { className: "panel-container", children: [
|
|
3004
2972
|
/* @__PURE__ */ jsx("div", { className: "panel-title", children: "Text Effects" }),
|
|
3005
|
-
/* @__PURE__ */
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
"
|
|
3009
|
-
{
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
children:
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
{
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
2973
|
+
/* @__PURE__ */ jsx(
|
|
2974
|
+
AccordionItem,
|
|
2975
|
+
{
|
|
2976
|
+
title: "Effects",
|
|
2977
|
+
icon: /* @__PURE__ */ jsx(Sparkles, { className: "icon-sm" }),
|
|
2978
|
+
isOpen: isEffectsOpen,
|
|
2979
|
+
onToggle: () => setIsEffectsOpen((open) => !open),
|
|
2980
|
+
children: /* @__PURE__ */ jsxs("div", { className: "properties-group", children: [
|
|
2981
|
+
/* @__PURE__ */ jsx("div", { className: "property-section", children: /* @__PURE__ */ jsx(PropertyRow, { label: "Preset", children: /* @__PURE__ */ jsxs(
|
|
2982
|
+
"select",
|
|
2983
|
+
{
|
|
2984
|
+
value: (currentEffect == null ? void 0 : currentEffect.getName()) || "",
|
|
2985
|
+
onChange: (e) => handleUpdateEffect({ name: e.target.value }),
|
|
2986
|
+
className: "select-dark w-full",
|
|
2987
|
+
children: [
|
|
2988
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "No Effect" }),
|
|
2989
|
+
TEXT_EFFECTS.map((effect) => /* @__PURE__ */ jsx("option", { value: effect.name, children: effect.name.charAt(0).toUpperCase() + effect.name.slice(1) }, effect.name))
|
|
2990
|
+
]
|
|
2991
|
+
}
|
|
2992
|
+
) }) }),
|
|
2993
|
+
currentEffect && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2994
|
+
/* @__PURE__ */ jsx("div", { className: "property-section", children: /* @__PURE__ */ jsx(PropertyRow, { label: "Delay (s)", children: /* @__PURE__ */ jsx(
|
|
2995
|
+
"input",
|
|
2996
|
+
{
|
|
2997
|
+
type: "number",
|
|
2998
|
+
min: "0",
|
|
2999
|
+
max: "5",
|
|
3000
|
+
step: "0.1",
|
|
3001
|
+
value: currentEffect.getDelay() ?? 0,
|
|
3002
|
+
onChange: (e) => handleUpdateEffect({ delay: Number(e.target.value) }),
|
|
3003
|
+
className: "input-dark"
|
|
3004
|
+
}
|
|
3005
|
+
) }) }),
|
|
3006
|
+
/* @__PURE__ */ jsx("div", { className: "property-section", children: /* @__PURE__ */ jsx(PropertyRow, { label: "Duration (s)", children: /* @__PURE__ */ jsx(
|
|
3007
|
+
"input",
|
|
3008
|
+
{
|
|
3009
|
+
type: "number",
|
|
3010
|
+
min: "0.1",
|
|
3011
|
+
max: "10",
|
|
3012
|
+
step: "0.1",
|
|
3013
|
+
value: currentEffect.getDuration() ?? 1,
|
|
3014
|
+
onChange: (e) => handleUpdateEffect({ duration: Number(e.target.value) }),
|
|
3015
|
+
className: "input-dark"
|
|
3016
|
+
}
|
|
3017
|
+
) }) }),
|
|
3018
|
+
/* @__PURE__ */ jsx("div", { className: "property-section", children: /* @__PURE__ */ jsx(PropertyRow, { label: "Buffer (s)", children: /* @__PURE__ */ jsx(
|
|
3019
|
+
"input",
|
|
3020
|
+
{
|
|
3021
|
+
type: "number",
|
|
3022
|
+
min: "0.05",
|
|
3023
|
+
max: "1",
|
|
3024
|
+
step: "0.05",
|
|
3025
|
+
value: currentEffect.getBufferTime() ?? 0.1,
|
|
3026
|
+
onChange: (e) => handleUpdateEffect({
|
|
3027
|
+
bufferTime: Number(e.target.value)
|
|
3028
|
+
}),
|
|
3029
|
+
className: "input-dark"
|
|
3030
|
+
}
|
|
3031
|
+
) }) })
|
|
3032
|
+
] })
|
|
3033
|
+
] })
|
|
3034
|
+
}
|
|
3035
|
+
)
|
|
3067
3036
|
] });
|
|
3068
3037
|
}
|
|
3069
3038
|
function Animation({
|
|
@@ -3251,37 +3220,95 @@ function Animation({
|
|
|
3251
3220
|
})() })
|
|
3252
3221
|
] });
|
|
3253
3222
|
}
|
|
3223
|
+
const MIN_DB = -60;
|
|
3224
|
+
const MAX_DB = 6;
|
|
3225
|
+
function linearToDb(linear) {
|
|
3226
|
+
if (linear <= 0) return MIN_DB;
|
|
3227
|
+
const db = 20 * Math.log10(linear);
|
|
3228
|
+
return Math.max(MIN_DB, Math.min(MAX_DB, db));
|
|
3229
|
+
}
|
|
3230
|
+
function dbToLinear(db) {
|
|
3231
|
+
if (db <= MIN_DB) return 0;
|
|
3232
|
+
const linear = Math.pow(10, db / 20);
|
|
3233
|
+
return Math.min(linear, Math.pow(10, MAX_DB / 20));
|
|
3234
|
+
}
|
|
3235
|
+
const PLAYBACK_RATE_MIN = 0.25;
|
|
3236
|
+
const PLAYBACK_RATE_MAX = 2;
|
|
3237
|
+
const PLAYBACK_RATE_STEP = 0.25;
|
|
3254
3238
|
function PlaybackPropsPanel({
|
|
3255
3239
|
selectedElement,
|
|
3256
3240
|
updateElement
|
|
3257
3241
|
}) {
|
|
3258
3242
|
const elementProps = (selectedElement == null ? void 0 : selectedElement.getProps()) || {};
|
|
3259
|
-
const
|
|
3243
|
+
const volumeLinear = elementProps.volume ?? 1;
|
|
3244
|
+
const volumeDb = linearToDb(volumeLinear);
|
|
3245
|
+
const playbackRate = elementProps.playbackRate ?? 1;
|
|
3260
3246
|
const handleUpdateElement = (props) => {
|
|
3261
3247
|
if (selectedElement) {
|
|
3262
3248
|
updateElement == null ? void 0 : updateElement(selectedElement == null ? void 0 : selectedElement.setProps({ ...elementProps, ...props }));
|
|
3263
3249
|
}
|
|
3264
3250
|
};
|
|
3251
|
+
const handleVolumeDbChange = (db) => {
|
|
3252
|
+
handleUpdateElement({ volume: dbToLinear(db) });
|
|
3253
|
+
};
|
|
3254
|
+
const handlePlaybackRateChange = (rate) => {
|
|
3255
|
+
handleUpdateElement({ playbackRate: rate });
|
|
3256
|
+
};
|
|
3257
|
+
const [isPlaybackOpen, setIsPlaybackOpen] = useState(false);
|
|
3265
3258
|
return /* @__PURE__ */ jsxs("div", { className: "panel-container", children: [
|
|
3266
|
-
/* @__PURE__ */ jsx("div", { className: "panel-title", children: "Playback
|
|
3267
|
-
/* @__PURE__ */
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3259
|
+
/* @__PURE__ */ jsx("div", { className: "panel-title", children: "Playback" }),
|
|
3260
|
+
/* @__PURE__ */ jsx(
|
|
3261
|
+
AccordionItem,
|
|
3262
|
+
{
|
|
3263
|
+
title: "Playback",
|
|
3264
|
+
icon: /* @__PURE__ */ jsx(Music2, { className: "icon-sm" }),
|
|
3265
|
+
isOpen: isPlaybackOpen,
|
|
3266
|
+
onToggle: () => setIsPlaybackOpen((open) => !open),
|
|
3267
|
+
children: /* @__PURE__ */ jsxs("div", { className: "properties-group", children: [
|
|
3268
|
+
/* @__PURE__ */ jsx("div", { className: "property-section", children: /* @__PURE__ */ jsx(
|
|
3269
|
+
PropertyRow,
|
|
3270
|
+
{
|
|
3271
|
+
label: "Playback rate",
|
|
3272
|
+
secondary: /* @__PURE__ */ jsxs("span", { children: [
|
|
3273
|
+
playbackRate,
|
|
3274
|
+
"×"
|
|
3275
|
+
] }),
|
|
3276
|
+
children: /* @__PURE__ */ jsx(
|
|
3277
|
+
"input",
|
|
3278
|
+
{
|
|
3279
|
+
type: "range",
|
|
3280
|
+
min: PLAYBACK_RATE_MIN,
|
|
3281
|
+
max: PLAYBACK_RATE_MAX,
|
|
3282
|
+
step: PLAYBACK_RATE_STEP,
|
|
3283
|
+
value: playbackRate,
|
|
3284
|
+
onChange: (e) => handlePlaybackRateChange(Number(e.target.value)),
|
|
3285
|
+
className: "slider-purple"
|
|
3286
|
+
}
|
|
3287
|
+
)
|
|
3288
|
+
}
|
|
3289
|
+
) }),
|
|
3290
|
+
/* @__PURE__ */ jsx("div", { className: "property-section", children: /* @__PURE__ */ jsx(
|
|
3291
|
+
PropertyRow,
|
|
3292
|
+
{
|
|
3293
|
+
label: "Volume",
|
|
3294
|
+
secondary: /* @__PURE__ */ jsx("span", { children: volumeDb <= MIN_DB ? "−∞" : `${Math.round(volumeDb)} dB` }),
|
|
3295
|
+
children: /* @__PURE__ */ jsx(
|
|
3296
|
+
"input",
|
|
3297
|
+
{
|
|
3298
|
+
type: "range",
|
|
3299
|
+
min: MIN_DB,
|
|
3300
|
+
max: MAX_DB,
|
|
3301
|
+
step: 1,
|
|
3302
|
+
value: volumeDb,
|
|
3303
|
+
onChange: (e) => handleVolumeDbChange(Number(e.target.value)),
|
|
3304
|
+
className: "slider-purple"
|
|
3305
|
+
}
|
|
3306
|
+
)
|
|
3307
|
+
}
|
|
3308
|
+
) })
|
|
3309
|
+
] })
|
|
3310
|
+
}
|
|
3311
|
+
)
|
|
3285
3312
|
] });
|
|
3286
3313
|
}
|
|
3287
3314
|
const hasAudio = async (src) => {
|
|
@@ -3370,11 +3397,11 @@ const saveAsFile = (content, type, name) => {
|
|
|
3370
3397
|
a.click();
|
|
3371
3398
|
URL.revokeObjectURL(url);
|
|
3372
3399
|
};
|
|
3373
|
-
function
|
|
3400
|
+
function GenerateCaptionsPanel({
|
|
3374
3401
|
selectedElement,
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3402
|
+
addCaptionsToTimeline,
|
|
3403
|
+
onGenerateCaptions,
|
|
3404
|
+
getCaptionstatus
|
|
3378
3405
|
}) {
|
|
3379
3406
|
const [containsAudio, setContainsAudio] = useState(null);
|
|
3380
3407
|
const [isLoading, setIsLoading] = useState(true);
|
|
@@ -3397,7 +3424,7 @@ function GenerateSubtitlesPanel({
|
|
|
3397
3424
|
}
|
|
3398
3425
|
};
|
|
3399
3426
|
const startPolling = async (reqId) => {
|
|
3400
|
-
if (!
|
|
3427
|
+
if (!getCaptionstatus) {
|
|
3401
3428
|
return;
|
|
3402
3429
|
}
|
|
3403
3430
|
setPollingStatus("polling");
|
|
@@ -3405,12 +3432,12 @@ function GenerateSubtitlesPanel({
|
|
|
3405
3432
|
setErrorMessage(null);
|
|
3406
3433
|
const poll = async () => {
|
|
3407
3434
|
try {
|
|
3408
|
-
const response = await
|
|
3435
|
+
const response = await getCaptionstatus(reqId);
|
|
3409
3436
|
if (response.status === "completed") {
|
|
3410
3437
|
stopPolling();
|
|
3411
3438
|
setPollingStatus("success");
|
|
3412
3439
|
setIsGenerating(false);
|
|
3413
|
-
|
|
3440
|
+
addCaptionsToTimeline(response.captions || []);
|
|
3414
3441
|
setTimeout(() => {
|
|
3415
3442
|
setPollingStatus("idle");
|
|
3416
3443
|
}, 3e3);
|
|
@@ -3419,32 +3446,32 @@ function GenerateSubtitlesPanel({
|
|
|
3419
3446
|
stopPolling();
|
|
3420
3447
|
setPollingStatus("error");
|
|
3421
3448
|
setIsGenerating(false);
|
|
3422
|
-
setErrorMessage(response.error || "Failed to generate
|
|
3423
|
-
console.error("Error generating
|
|
3449
|
+
setErrorMessage(response.error || "Failed to generate captions");
|
|
3450
|
+
console.error("Error generating captions:", response.error);
|
|
3424
3451
|
}
|
|
3425
3452
|
} catch (error) {
|
|
3426
3453
|
stopPolling();
|
|
3427
3454
|
setPollingStatus("error");
|
|
3428
3455
|
setIsGenerating(false);
|
|
3429
|
-
setErrorMessage(error instanceof Error ? error.message : "Failed to get
|
|
3430
|
-
console.error("Error polling for
|
|
3456
|
+
setErrorMessage(error instanceof Error ? error.message : "Failed to get caption status");
|
|
3457
|
+
console.error("Error polling for captions:", error);
|
|
3431
3458
|
}
|
|
3432
3459
|
};
|
|
3433
3460
|
await poll();
|
|
3434
3461
|
pollingIntervalRef.current = setInterval(poll, 2e3);
|
|
3435
3462
|
};
|
|
3436
|
-
const
|
|
3463
|
+
const handleGenerateCaptions = async () => {
|
|
3437
3464
|
if (!(selectedElement instanceof VideoElement)) {
|
|
3438
3465
|
return;
|
|
3439
3466
|
}
|
|
3440
3467
|
const videoElement = selectedElement;
|
|
3441
3468
|
try {
|
|
3442
|
-
const reqId = await
|
|
3469
|
+
const reqId = await onGenerateCaptions(videoElement);
|
|
3443
3470
|
if (!reqId) {
|
|
3444
3471
|
setPollingStatus("error");
|
|
3445
3472
|
setIsGenerating(false);
|
|
3446
|
-
setErrorMessage("Failed to start
|
|
3447
|
-
console.error("Error generating
|
|
3473
|
+
setErrorMessage("Failed to start caption generation");
|
|
3474
|
+
console.error("Error generating captions: Failed to start caption generation");
|
|
3448
3475
|
return;
|
|
3449
3476
|
}
|
|
3450
3477
|
currentReqIdRef.current = reqId;
|
|
@@ -3452,8 +3479,8 @@ function GenerateSubtitlesPanel({
|
|
|
3452
3479
|
} catch (error) {
|
|
3453
3480
|
setPollingStatus("error");
|
|
3454
3481
|
setIsGenerating(false);
|
|
3455
|
-
setErrorMessage(error instanceof Error ? error.message : "Failed to start
|
|
3456
|
-
console.error("Error generating
|
|
3482
|
+
setErrorMessage(error instanceof Error ? error.message : "Failed to start caption generation");
|
|
3483
|
+
console.error("Error generating captions:", error);
|
|
3457
3484
|
}
|
|
3458
3485
|
};
|
|
3459
3486
|
const checkAudio = async () => {
|
|
@@ -3485,7 +3512,7 @@ function GenerateSubtitlesPanel({
|
|
|
3485
3512
|
setErrorMessage(null);
|
|
3486
3513
|
}, [selectedElement]);
|
|
3487
3514
|
return /* @__PURE__ */ jsxs("div", { className: "panel-container", children: [
|
|
3488
|
-
/* @__PURE__ */ jsx("div", { className: "panel-title", children: "Generate
|
|
3515
|
+
/* @__PURE__ */ jsx("div", { className: "panel-title", children: "Generate Captions Panel" }),
|
|
3489
3516
|
isLoading && /* @__PURE__ */ jsx("div", { className: "panel-section", children: /* @__PURE__ */ jsx("div", { className: "empty-state", children: /* @__PURE__ */ jsxs("div", { className: "empty-state-content", children: [
|
|
3490
3517
|
/* @__PURE__ */ jsx(LoaderCircle, { className: "empty-state-icon animate-spin" }),
|
|
3491
3518
|
/* @__PURE__ */ jsx("p", { className: "empty-state-text", children: "Checking for audio..." })
|
|
@@ -3496,85 +3523,228 @@ function GenerateSubtitlesPanel({
|
|
|
3496
3523
|
] }) }) }),
|
|
3497
3524
|
!isLoading && containsAudio === true && pollingStatus === "idle" && !isGenerating && /* @__PURE__ */ jsx("div", { className: "panel-section", children: /* @__PURE__ */ jsx("div", { className: "empty-state", children: /* @__PURE__ */ jsxs("div", { className: "empty-state-content", children: [
|
|
3498
3525
|
/* @__PURE__ */ jsx(Volume2, { className: "empty-state-icon" }),
|
|
3499
|
-
/* @__PURE__ */ jsx("p", { className: "empty-state-text", children: "Audio detected! You can now generate
|
|
3526
|
+
/* @__PURE__ */ jsx("p", { className: "empty-state-text", children: "Audio detected! You can now generate captions" })
|
|
3500
3527
|
] }) }) }),
|
|
3501
3528
|
!isLoading && isGenerating && pollingStatus === "polling" && /* @__PURE__ */ jsx("div", { className: "panel-section", children: /* @__PURE__ */ jsx("div", { className: "empty-state", children: /* @__PURE__ */ jsxs("div", { className: "empty-state-content", children: [
|
|
3502
3529
|
/* @__PURE__ */ jsx(LoaderCircle, { className: "empty-state-icon animate-spin" }),
|
|
3503
|
-
/* @__PURE__ */ jsx("p", { className: "empty-state-text", children: "Generating
|
|
3530
|
+
/* @__PURE__ */ jsx("p", { className: "empty-state-text", children: "Generating captions... Please wait" })
|
|
3504
3531
|
] }) }) }),
|
|
3505
3532
|
!isLoading && pollingStatus === "success" && /* @__PURE__ */ jsx("div", { className: "panel-section", children: /* @__PURE__ */ jsx("div", { className: "empty-state", children: /* @__PURE__ */ jsxs("div", { className: "empty-state-content", children: [
|
|
3506
3533
|
/* @__PURE__ */ jsx(CircleCheck, { className: "empty-state-icon", color: "var(--color-green-500)" }),
|
|
3507
|
-
/* @__PURE__ */ jsx("p", { className: "empty-state-text", children: "
|
|
3534
|
+
/* @__PURE__ */ jsx("p", { className: "empty-state-text", children: "Captions generated successfully!" })
|
|
3508
3535
|
] }) }) }),
|
|
3509
3536
|
!isLoading && pollingStatus === "error" && /* @__PURE__ */ jsx("div", { className: "panel-section", children: /* @__PURE__ */ jsx("div", { className: "empty-state", children: /* @__PURE__ */ jsxs("div", { className: "empty-state-content", children: [
|
|
3510
3537
|
/* @__PURE__ */ jsx(CircleX, { className: "empty-state-icon", color: "var(--color-red-500)" }),
|
|
3511
|
-
/* @__PURE__ */ jsx("p", { className: "empty-state-text", children: errorMessage || "Failed to generate
|
|
3538
|
+
/* @__PURE__ */ jsx("p", { className: "empty-state-text", children: errorMessage || "Failed to generate captions" })
|
|
3512
3539
|
] }) }) }),
|
|
3513
3540
|
!isLoading && /* @__PURE__ */ jsx("div", { className: "flex panel-section", children: /* @__PURE__ */ jsx(
|
|
3514
3541
|
"button",
|
|
3515
3542
|
{
|
|
3516
|
-
onClick:
|
|
3543
|
+
onClick: handleGenerateCaptions,
|
|
3517
3544
|
disabled: !containsAudio || isGenerating,
|
|
3518
3545
|
className: "btn-primary w-full",
|
|
3519
|
-
children: isGenerating ? "Generating..." : "Generate
|
|
3546
|
+
children: isGenerating ? "Generating..." : "Generate Captions"
|
|
3520
3547
|
}
|
|
3521
3548
|
) })
|
|
3522
3549
|
] });
|
|
3523
3550
|
}
|
|
3524
|
-
function
|
|
3525
|
-
selectedProp,
|
|
3551
|
+
function TextPropsPanel({
|
|
3526
3552
|
selectedElement,
|
|
3527
|
-
updateElement
|
|
3528
|
-
addSubtitlesToTimeline,
|
|
3529
|
-
onGenerateSubtitles,
|
|
3530
|
-
getSubtitleStatus
|
|
3553
|
+
updateElement
|
|
3531
3554
|
}) {
|
|
3532
|
-
if (!selectedElement)
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
)
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
}
|
|
3559
|
-
),
|
|
3560
|
-
selectedProp === "animations" && /* @__PURE__ */ jsx(
|
|
3561
|
-
Animation,
|
|
3562
|
-
{
|
|
3563
|
-
selectedElement,
|
|
3564
|
-
updateElement
|
|
3565
|
-
}
|
|
3566
|
-
),
|
|
3567
|
-
selectedProp === "generate-subtitles" && /* @__PURE__ */ jsx(
|
|
3568
|
-
GenerateSubtitlesPanel,
|
|
3555
|
+
if (!(selectedElement instanceof TextElement)) return null;
|
|
3556
|
+
const textProps = selectedElement.getProps() || {};
|
|
3557
|
+
const [isTypographyOpen, setIsTypographyOpen] = useState(false);
|
|
3558
|
+
const currentAlign = textProps.textAlign ?? "center";
|
|
3559
|
+
const currentWeight = textProps.fontWeight ?? 400;
|
|
3560
|
+
const isBold = currentWeight >= 600;
|
|
3561
|
+
const isItalic = textProps.fontStyle === "italic";
|
|
3562
|
+
const handleUpdate = (patch) => {
|
|
3563
|
+
if (!selectedElement) return;
|
|
3564
|
+
const next = { ...textProps, ...patch };
|
|
3565
|
+
selectedElement.setProps(next);
|
|
3566
|
+
updateElement == null ? void 0 : updateElement(selectedElement);
|
|
3567
|
+
};
|
|
3568
|
+
const toggleBold = () => {
|
|
3569
|
+
handleUpdate({ fontWeight: isBold ? 400 : 700 });
|
|
3570
|
+
};
|
|
3571
|
+
const toggleItalic = () => {
|
|
3572
|
+
handleUpdate({ fontStyle: isItalic ? "normal" : "italic" });
|
|
3573
|
+
};
|
|
3574
|
+
const setAlign = (align) => {
|
|
3575
|
+
handleUpdate({ textAlign: align });
|
|
3576
|
+
};
|
|
3577
|
+
return /* @__PURE__ */ jsxs("div", { className: "panel-container", children: [
|
|
3578
|
+
/* @__PURE__ */ jsx("div", { className: "panel-title", children: "Typography" }),
|
|
3579
|
+
/* @__PURE__ */ jsx(
|
|
3580
|
+
AccordionItem,
|
|
3569
3581
|
{
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3582
|
+
title: "Typography",
|
|
3583
|
+
icon: /* @__PURE__ */ jsx(Type, { className: "icon-sm" }),
|
|
3584
|
+
isOpen: isTypographyOpen,
|
|
3585
|
+
onToggle: () => setIsTypographyOpen((open) => !open),
|
|
3586
|
+
children: /* @__PURE__ */ jsxs("div", { className: "properties-group", children: [
|
|
3587
|
+
/* @__PURE__ */ jsx("div", { className: "property-section", children: /* @__PURE__ */ jsx(
|
|
3588
|
+
PropertyRow,
|
|
3589
|
+
{
|
|
3590
|
+
label: "Font size",
|
|
3591
|
+
secondary: /* @__PURE__ */ jsxs("span", { children: [
|
|
3592
|
+
textProps.fontSize ?? 48,
|
|
3593
|
+
"px"
|
|
3594
|
+
] }),
|
|
3595
|
+
children: /* @__PURE__ */ jsx(
|
|
3596
|
+
"input",
|
|
3597
|
+
{
|
|
3598
|
+
type: "range",
|
|
3599
|
+
min: 8,
|
|
3600
|
+
max: 160,
|
|
3601
|
+
value: textProps.fontSize ?? 48,
|
|
3602
|
+
onChange: (e) => handleUpdate({ fontSize: Number(e.target.value) }),
|
|
3603
|
+
className: "slider-purple"
|
|
3604
|
+
}
|
|
3605
|
+
)
|
|
3606
|
+
}
|
|
3607
|
+
) }),
|
|
3608
|
+
/* @__PURE__ */ jsx("div", { className: "property-section", children: /* @__PURE__ */ jsxs(PropertyRow, { label: "Style", children: [
|
|
3609
|
+
/* @__PURE__ */ jsx(
|
|
3610
|
+
"button",
|
|
3611
|
+
{
|
|
3612
|
+
type: "button",
|
|
3613
|
+
className: `form-btn ${isBold ? "active" : ""}`,
|
|
3614
|
+
onClick: toggleBold,
|
|
3615
|
+
title: "Bold",
|
|
3616
|
+
children: /* @__PURE__ */ jsx(Bold, { className: "icon-sm" })
|
|
3617
|
+
}
|
|
3618
|
+
),
|
|
3619
|
+
/* @__PURE__ */ jsx(
|
|
3620
|
+
"button",
|
|
3621
|
+
{
|
|
3622
|
+
type: "button",
|
|
3623
|
+
className: `form-btn ${isItalic ? "active" : ""}`,
|
|
3624
|
+
onClick: toggleItalic,
|
|
3625
|
+
title: "Italic",
|
|
3626
|
+
children: /* @__PURE__ */ jsx(Italic, { className: "icon-sm" })
|
|
3627
|
+
}
|
|
3628
|
+
)
|
|
3629
|
+
] }) }),
|
|
3630
|
+
/* @__PURE__ */ jsx("div", { className: "property-section", children: /* @__PURE__ */ jsxs(PropertyRow, { label: "Align", children: [
|
|
3631
|
+
/* @__PURE__ */ jsx(
|
|
3632
|
+
"button",
|
|
3633
|
+
{
|
|
3634
|
+
type: "button",
|
|
3635
|
+
className: `form-btn ${currentAlign === "left" ? "active" : ""}`,
|
|
3636
|
+
onClick: () => setAlign("left"),
|
|
3637
|
+
title: "Align left",
|
|
3638
|
+
children: /* @__PURE__ */ jsx(AlignLeft, { className: "icon-sm" })
|
|
3639
|
+
}
|
|
3640
|
+
),
|
|
3641
|
+
/* @__PURE__ */ jsx(
|
|
3642
|
+
"button",
|
|
3643
|
+
{
|
|
3644
|
+
type: "button",
|
|
3645
|
+
className: `form-btn ${currentAlign === "center" ? "active" : ""}`,
|
|
3646
|
+
onClick: () => setAlign("center"),
|
|
3647
|
+
title: "Align center",
|
|
3648
|
+
children: /* @__PURE__ */ jsx(AlignCenter, { className: "icon-sm" })
|
|
3649
|
+
}
|
|
3650
|
+
),
|
|
3651
|
+
/* @__PURE__ */ jsx(
|
|
3652
|
+
"button",
|
|
3653
|
+
{
|
|
3654
|
+
type: "button",
|
|
3655
|
+
className: `form-btn ${currentAlign === "right" ? "active" : ""}`,
|
|
3656
|
+
onClick: () => setAlign("right"),
|
|
3657
|
+
title: "Align right",
|
|
3658
|
+
children: /* @__PURE__ */ jsx(AlignRight, { className: "icon-sm" })
|
|
3659
|
+
}
|
|
3660
|
+
)
|
|
3661
|
+
] }) })
|
|
3662
|
+
] })
|
|
3574
3663
|
}
|
|
3575
3664
|
)
|
|
3576
3665
|
] });
|
|
3577
3666
|
}
|
|
3667
|
+
function PropertiesPanelContainer({
|
|
3668
|
+
selectedElement,
|
|
3669
|
+
updateElement,
|
|
3670
|
+
addCaptionsToTimeline,
|
|
3671
|
+
onGenerateCaptions,
|
|
3672
|
+
getCaptionstatus,
|
|
3673
|
+
videoResolution
|
|
3674
|
+
}) {
|
|
3675
|
+
const title = selectedElement instanceof TextElement ? selectedElement.getText() : (selectedElement == null ? void 0 : selectedElement.getName()) || (selectedElement == null ? void 0 : selectedElement.getType()) || "Element";
|
|
3676
|
+
return /* @__PURE__ */ jsxs("aside", { className: "properties-panel", "aria-label": "Element properties inspector", children: [
|
|
3677
|
+
/* @__PURE__ */ jsxs("div", { className: "properties-header", children: [
|
|
3678
|
+
!selectedElement && /* @__PURE__ */ jsx("h3", { className: "properties-title", children: "Composition" }),
|
|
3679
|
+
selectedElement && selectedElement.getType() === "caption" && /* @__PURE__ */ jsx("h3", { className: "properties-title", children: "Subtitles are edited from the captions panel" }),
|
|
3680
|
+
selectedElement && selectedElement.getType() !== "caption" && /* @__PURE__ */ jsx("h3", { className: "properties-title", children: title })
|
|
3681
|
+
] }),
|
|
3682
|
+
/* @__PURE__ */ jsxs("div", { className: "prop-content", children: [
|
|
3683
|
+
!selectedElement && /* @__PURE__ */ jsxs("div", { className: "panel-container", children: [
|
|
3684
|
+
/* @__PURE__ */ jsx("div", { className: "panel-title", children: "Canvas & Render" }),
|
|
3685
|
+
/* @__PURE__ */ jsx("div", { className: "properties-group", children: /* @__PURE__ */ jsxs("div", { className: "property-section", children: [
|
|
3686
|
+
/* @__PURE__ */ jsx("span", { className: "property-label", children: "Size" }),
|
|
3687
|
+
/* @__PURE__ */ jsxs("span", { className: "properties-size-readonly", children: [
|
|
3688
|
+
videoResolution.width,
|
|
3689
|
+
" × ",
|
|
3690
|
+
videoResolution.height
|
|
3691
|
+
] })
|
|
3692
|
+
] }) })
|
|
3693
|
+
] }),
|
|
3694
|
+
selectedElement && selectedElement.getType() === "caption" ? null : selectedElement && /* @__PURE__ */ jsx(Fragment, { children: (() => {
|
|
3695
|
+
const isText = selectedElement instanceof TextElement;
|
|
3696
|
+
const isVideo = selectedElement instanceof VideoElement;
|
|
3697
|
+
const isAudio = selectedElement instanceof AudioElement;
|
|
3698
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3699
|
+
isText && /* @__PURE__ */ jsx(
|
|
3700
|
+
TextPropsPanel,
|
|
3701
|
+
{
|
|
3702
|
+
selectedElement,
|
|
3703
|
+
updateElement
|
|
3704
|
+
}
|
|
3705
|
+
),
|
|
3706
|
+
!isAudio && /* @__PURE__ */ jsx(
|
|
3707
|
+
ElementProps,
|
|
3708
|
+
{
|
|
3709
|
+
selectedElement,
|
|
3710
|
+
updateElement
|
|
3711
|
+
}
|
|
3712
|
+
),
|
|
3713
|
+
(isVideo || isAudio) && /* @__PURE__ */ jsx(
|
|
3714
|
+
PlaybackPropsPanel,
|
|
3715
|
+
{
|
|
3716
|
+
selectedElement,
|
|
3717
|
+
updateElement
|
|
3718
|
+
}
|
|
3719
|
+
),
|
|
3720
|
+
isText && /* @__PURE__ */ jsx(
|
|
3721
|
+
TextEffects,
|
|
3722
|
+
{
|
|
3723
|
+
selectedElement,
|
|
3724
|
+
updateElement
|
|
3725
|
+
}
|
|
3726
|
+
),
|
|
3727
|
+
!isAudio && /* @__PURE__ */ jsx(
|
|
3728
|
+
Animation,
|
|
3729
|
+
{
|
|
3730
|
+
selectedElement,
|
|
3731
|
+
updateElement
|
|
3732
|
+
}
|
|
3733
|
+
),
|
|
3734
|
+
isVideo && /* @__PURE__ */ jsx(
|
|
3735
|
+
GenerateCaptionsPanel,
|
|
3736
|
+
{
|
|
3737
|
+
selectedElement,
|
|
3738
|
+
addCaptionsToTimeline,
|
|
3739
|
+
onGenerateCaptions,
|
|
3740
|
+
getCaptionstatus
|
|
3741
|
+
}
|
|
3742
|
+
)
|
|
3743
|
+
] });
|
|
3744
|
+
})() })
|
|
3745
|
+
] })
|
|
3746
|
+
] });
|
|
3747
|
+
}
|
|
3578
3748
|
const useStudioOperation = (studioConfig) => {
|
|
3579
3749
|
const { editor, present, videoResolution } = useTimelineContext();
|
|
3580
3750
|
const { setSeekTime, setPlayerState } = useLivePlayerContext();
|
|
@@ -3637,30 +3807,30 @@ const useStudioOperation = (studioConfig) => {
|
|
|
3637
3807
|
alert("Export video not supported in demo mode");
|
|
3638
3808
|
}
|
|
3639
3809
|
};
|
|
3640
|
-
const
|
|
3641
|
-
if (studioConfig == null ? void 0 : studioConfig.
|
|
3642
|
-
const service = studioConfig.
|
|
3643
|
-
const reqId = await service.
|
|
3810
|
+
const onGenerateCaptions = async (videoElement) => {
|
|
3811
|
+
if (studioConfig == null ? void 0 : studioConfig.captionGenerationService) {
|
|
3812
|
+
const service = studioConfig.captionGenerationService;
|
|
3813
|
+
const reqId = await service.generateCaptions(videoElement, present);
|
|
3644
3814
|
return reqId;
|
|
3645
3815
|
}
|
|
3646
|
-
alert("Generate
|
|
3816
|
+
alert("Generate captions not supported in demo mode");
|
|
3647
3817
|
return null;
|
|
3648
3818
|
};
|
|
3649
|
-
const
|
|
3819
|
+
const addCaptionsToTimeline = (captions) => {
|
|
3650
3820
|
var _a;
|
|
3651
|
-
const updatedProjectJSON = (_a = studioConfig == null ? void 0 : studioConfig.
|
|
3821
|
+
const updatedProjectJSON = (_a = studioConfig == null ? void 0 : studioConfig.captionGenerationService) == null ? void 0 : _a.updateProjectWithCaptions(captions);
|
|
3652
3822
|
if (updatedProjectJSON) {
|
|
3653
3823
|
editor.loadProject(updatedProjectJSON);
|
|
3654
3824
|
}
|
|
3655
3825
|
};
|
|
3656
|
-
const
|
|
3657
|
-
if (studioConfig == null ? void 0 : studioConfig.
|
|
3658
|
-
const service = studioConfig.
|
|
3826
|
+
const getCaptionstatus = async (reqId) => {
|
|
3827
|
+
if (studioConfig == null ? void 0 : studioConfig.captionGenerationService) {
|
|
3828
|
+
const service = studioConfig.captionGenerationService;
|
|
3659
3829
|
return await service.getRequestStatus(reqId);
|
|
3660
3830
|
}
|
|
3661
3831
|
return {
|
|
3662
3832
|
status: "failed",
|
|
3663
|
-
error: "
|
|
3833
|
+
error: "Caption generation service not found"
|
|
3664
3834
|
};
|
|
3665
3835
|
};
|
|
3666
3836
|
return {
|
|
@@ -3668,48 +3838,9 @@ const useStudioOperation = (studioConfig) => {
|
|
|
3668
3838
|
onSaveProject,
|
|
3669
3839
|
onExportVideo,
|
|
3670
3840
|
onNewProject,
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
};
|
|
3675
|
-
};
|
|
3676
|
-
const useGenerateSubtitles = (studioConfig) => {
|
|
3677
|
-
const { editor, present } = useTimelineContext();
|
|
3678
|
-
const onGenerateSubtitles = async (videoElement) => {
|
|
3679
|
-
if (studioConfig == null ? void 0 : studioConfig.subtitleGenerationService) {
|
|
3680
|
-
const service = studioConfig.subtitleGenerationService;
|
|
3681
|
-
const reqId = await service.generateSubtitles(
|
|
3682
|
-
videoElement,
|
|
3683
|
-
present
|
|
3684
|
-
);
|
|
3685
|
-
return reqId;
|
|
3686
|
-
}
|
|
3687
|
-
alert("Generate subtitles not supported in demo mode");
|
|
3688
|
-
return null;
|
|
3689
|
-
};
|
|
3690
|
-
const addSubtitlesToTimeline = (subtitles) => {
|
|
3691
|
-
var _a;
|
|
3692
|
-
const updatedProjectJSON = (_a = studioConfig == null ? void 0 : studioConfig.subtitleGenerationService) == null ? void 0 : _a.updateProjectWithSubtitles(
|
|
3693
|
-
subtitles
|
|
3694
|
-
);
|
|
3695
|
-
if (updatedProjectJSON) {
|
|
3696
|
-
editor.loadProject(updatedProjectJSON);
|
|
3697
|
-
}
|
|
3698
|
-
};
|
|
3699
|
-
const getSubtitleStatus = async (reqId) => {
|
|
3700
|
-
if (studioConfig == null ? void 0 : studioConfig.subtitleGenerationService) {
|
|
3701
|
-
const service = studioConfig.subtitleGenerationService;
|
|
3702
|
-
return await service.getRequestStatus(reqId);
|
|
3703
|
-
}
|
|
3704
|
-
return {
|
|
3705
|
-
status: "failed",
|
|
3706
|
-
error: "Subtitle generation service not found"
|
|
3707
|
-
};
|
|
3708
|
-
};
|
|
3709
|
-
return {
|
|
3710
|
-
onGenerateSubtitles,
|
|
3711
|
-
addSubtitlesToTimeline,
|
|
3712
|
-
getSubtitleStatus
|
|
3841
|
+
onGenerateCaptions,
|
|
3842
|
+
addCaptionsToTimeline,
|
|
3843
|
+
getCaptionstatus
|
|
3713
3844
|
};
|
|
3714
3845
|
};
|
|
3715
3846
|
function TwickStudio({ studioConfig }) {
|
|
@@ -3717,8 +3848,6 @@ function TwickStudio({ studioConfig }) {
|
|
|
3717
3848
|
const {
|
|
3718
3849
|
selectedTool,
|
|
3719
3850
|
setSelectedTool,
|
|
3720
|
-
selectedProp,
|
|
3721
|
-
setSelectedProp,
|
|
3722
3851
|
selectedElement,
|
|
3723
3852
|
addElement,
|
|
3724
3853
|
updateElement
|
|
@@ -3730,7 +3859,7 @@ function TwickStudio({ studioConfig }) {
|
|
|
3730
3859
|
onSaveProject,
|
|
3731
3860
|
onExportVideo
|
|
3732
3861
|
} = useStudioOperation(studioConfig);
|
|
3733
|
-
const {
|
|
3862
|
+
const { onGenerateCaptions, addCaptionsToTimeline, getCaptionstatus } = useGenerateCaptions(studioConfig);
|
|
3734
3863
|
const twickStudiConfig = useMemo(
|
|
3735
3864
|
() => ({
|
|
3736
3865
|
canvasMode: true,
|
|
@@ -3762,7 +3891,7 @@ function TwickStudio({ studioConfig }) {
|
|
|
3762
3891
|
setSelectedTool
|
|
3763
3892
|
}
|
|
3764
3893
|
),
|
|
3765
|
-
/* @__PURE__ */ jsx(
|
|
3894
|
+
/* @__PURE__ */ jsx("div", { className: "studio-left-panel", children: /* @__PURE__ */ jsx(
|
|
3766
3895
|
ElementPanelContainer,
|
|
3767
3896
|
{
|
|
3768
3897
|
videoResolution,
|
|
@@ -3772,7 +3901,7 @@ function TwickStudio({ studioConfig }) {
|
|
|
3772
3901
|
addElement,
|
|
3773
3902
|
updateElement
|
|
3774
3903
|
}
|
|
3775
|
-
),
|
|
3904
|
+
) }),
|
|
3776
3905
|
/* @__PURE__ */ jsx("main", { className: "main-container", children: /* @__PURE__ */ jsx("div", { className: "canvas-wrapper", children: /* @__PURE__ */ jsx(
|
|
3777
3906
|
"div",
|
|
3778
3907
|
{
|
|
@@ -3783,28 +3912,59 @@ function TwickStudio({ studioConfig }) {
|
|
|
3783
3912
|
children: /* @__PURE__ */ jsx(VideoEditor, { editorConfig: twickStudiConfig })
|
|
3784
3913
|
}
|
|
3785
3914
|
) }) }),
|
|
3786
|
-
/* @__PURE__ */ jsx(
|
|
3915
|
+
/* @__PURE__ */ jsx("div", { className: "studio-right-panel", children: /* @__PURE__ */ jsx(
|
|
3787
3916
|
PropertiesPanelContainer,
|
|
3788
3917
|
{
|
|
3789
|
-
selectedProp,
|
|
3790
3918
|
selectedElement,
|
|
3791
3919
|
updateElement,
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
),
|
|
3797
|
-
/* @__PURE__ */ jsx(
|
|
3798
|
-
PropsToolbar,
|
|
3799
|
-
{
|
|
3800
|
-
selectedElement,
|
|
3801
|
-
selectedProp,
|
|
3802
|
-
setSelectedProp
|
|
3920
|
+
addCaptionsToTimeline,
|
|
3921
|
+
onGenerateCaptions,
|
|
3922
|
+
getCaptionstatus,
|
|
3923
|
+
videoResolution
|
|
3803
3924
|
}
|
|
3804
|
-
)
|
|
3925
|
+
) })
|
|
3805
3926
|
] })
|
|
3806
3927
|
] }) });
|
|
3807
3928
|
}
|
|
3929
|
+
const useGenerateCaptions = (studioConfig) => {
|
|
3930
|
+
const { editor, present } = useTimelineContext();
|
|
3931
|
+
const onGenerateCaptions = async (videoElement) => {
|
|
3932
|
+
if (studioConfig == null ? void 0 : studioConfig.captionGenerationService) {
|
|
3933
|
+
const service = studioConfig.captionGenerationService;
|
|
3934
|
+
const reqId = await service.generateCaptions(
|
|
3935
|
+
videoElement,
|
|
3936
|
+
present
|
|
3937
|
+
);
|
|
3938
|
+
return reqId;
|
|
3939
|
+
}
|
|
3940
|
+
alert("Generate captions not supported in demo mode");
|
|
3941
|
+
return null;
|
|
3942
|
+
};
|
|
3943
|
+
const addCaptionsToTimeline = (captions) => {
|
|
3944
|
+
var _a;
|
|
3945
|
+
const updatedProjectJSON = (_a = studioConfig == null ? void 0 : studioConfig.captionGenerationService) == null ? void 0 : _a.updateProjectWithCaptions(
|
|
3946
|
+
captions
|
|
3947
|
+
);
|
|
3948
|
+
if (updatedProjectJSON) {
|
|
3949
|
+
editor.loadProject(updatedProjectJSON);
|
|
3950
|
+
}
|
|
3951
|
+
};
|
|
3952
|
+
const getCaptionstatus = async (reqId) => {
|
|
3953
|
+
if (studioConfig == null ? void 0 : studioConfig.captionGenerationService) {
|
|
3954
|
+
const service = studioConfig.captionGenerationService;
|
|
3955
|
+
return await service.getRequestStatus(reqId);
|
|
3956
|
+
}
|
|
3957
|
+
return {
|
|
3958
|
+
status: "failed",
|
|
3959
|
+
error: "Caption generation service not found"
|
|
3960
|
+
};
|
|
3961
|
+
};
|
|
3962
|
+
return {
|
|
3963
|
+
onGenerateCaptions,
|
|
3964
|
+
addCaptionsToTimeline,
|
|
3965
|
+
getCaptionstatus
|
|
3966
|
+
};
|
|
3967
|
+
};
|
|
3808
3968
|
export {
|
|
3809
3969
|
ANIMATIONS2 as ANIMATIONS,
|
|
3810
3970
|
AudioElement2 as AudioElement,
|
|
@@ -3817,6 +3977,7 @@ export {
|
|
|
3817
3977
|
CAPTION_STYLE2 as CAPTION_STYLE,
|
|
3818
3978
|
CAPTION_STYLE_OPTIONS,
|
|
3819
3979
|
CaptionElement2 as CaptionElement,
|
|
3980
|
+
CaptionsPanel,
|
|
3820
3981
|
CircleElement2 as CircleElement,
|
|
3821
3982
|
CirclePanel,
|
|
3822
3983
|
ElementAdder,
|
|
@@ -3831,8 +3992,7 @@ export {
|
|
|
3831
3992
|
ElementUpdater,
|
|
3832
3993
|
ElementValidator,
|
|
3833
3994
|
INITIAL_TIMELINE_DATA,
|
|
3834
|
-
|
|
3835
|
-
IconPanel,
|
|
3995
|
+
IconElement,
|
|
3836
3996
|
ImageElement2 as ImageElement,
|
|
3837
3997
|
ImagePanel,
|
|
3838
3998
|
LivePlayer,
|
|
@@ -3843,7 +4003,6 @@ export {
|
|
|
3843
4003
|
RectElement2 as RectElement,
|
|
3844
4004
|
RectPanel,
|
|
3845
4005
|
StudioHeader,
|
|
3846
|
-
SubtitlesPanel,
|
|
3847
4006
|
TEXT_EFFECTS2 as TEXT_EFFECTS,
|
|
3848
4007
|
TIMELINE_ACTION,
|
|
3849
4008
|
TIMELINE_ELEMENT_TYPE,
|
|
@@ -3872,10 +4031,11 @@ export {
|
|
|
3872
4031
|
isTrackId,
|
|
3873
4032
|
setElementColors,
|
|
3874
4033
|
useEditorManager2 as useEditorManager,
|
|
3875
|
-
|
|
4034
|
+
useGenerateCaptions,
|
|
3876
4035
|
useLivePlayerContext2 as useLivePlayerContext,
|
|
3877
4036
|
usePlayerControl,
|
|
3878
4037
|
useStudioManager,
|
|
4038
|
+
useTimelineContext2 as useTimelineContext,
|
|
3879
4039
|
useTimelineControl
|
|
3880
4040
|
};
|
|
3881
4041
|
//# sourceMappingURL=index.mjs.map
|