@twick/studio 0.14.5
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 +122 -0
- package/dist/components/container/audio-panel-container.d.ts +3 -0
- package/dist/components/container/circle-panel-container.d.ts +3 -0
- package/dist/components/container/element-panel-container.d.ts +12 -0
- package/dist/components/container/icon-panel-container.d.ts +3 -0
- package/dist/components/container/image-panel-container.d.ts +3 -0
- package/dist/components/container/properties-panel-container.d.ts +9 -0
- package/dist/components/container/rect-panel-container.d.ts +3 -0
- package/dist/components/container/text-panel-container.d.ts +9 -0
- package/dist/components/container/video-panel-container.d.ts +3 -0
- package/dist/components/header.d.ts +10 -0
- package/dist/components/panel/audio-panel.d.ts +3 -0
- package/dist/components/panel/circle-panel.d.ts +4 -0
- package/dist/components/panel/icon-panel.d.ts +4 -0
- package/dist/components/panel/image-panel.d.ts +3 -0
- package/dist/components/panel/rect-panel.d.ts +4 -0
- package/dist/components/panel/subtitles-panel.d.ts +27 -0
- package/dist/components/panel/text-panel.d.ts +4 -0
- package/dist/components/panel/video-panel.d.ts +3 -0
- package/dist/components/properties/animation.d.ts +3 -0
- package/dist/components/properties/element-props.d.ts +3 -0
- package/dist/components/properties/playback-props.d.ts +3 -0
- package/dist/components/properties/subtitlte-prop.d.ts +35 -0
- package/dist/components/properties/text-effects.d.ts +3 -0
- package/dist/components/props-toolbar.d.ts +7 -0
- package/dist/components/shared/accordion-item.d.ts +9 -0
- package/dist/components/shared/color-input.d.ts +5 -0
- package/dist/components/shared/file-input.d.ts +7 -0
- package/dist/components/shared/index.d.ts +3 -0
- package/dist/components/shared/media-manager.d.ts +3 -0
- package/dist/components/shared/prop-container.d.ts +7 -0
- package/dist/components/shared/search-input.d.ts +5 -0
- package/dist/components/toolbar.d.ts +23 -0
- package/dist/components/twick-studio.d.ts +5 -0
- package/dist/context/media-context.d.ts +15 -0
- package/dist/context/video-panel-context.d.ts +15 -0
- package/dist/hooks/use-audio-preview.d.ts +11 -0
- package/dist/hooks/use-circle-panel.d.ts +29 -0
- package/dist/hooks/use-icon-panel.d.ts +24 -0
- package/dist/hooks/use-media-panel.d.ts +23 -0
- package/dist/hooks/use-rect-panel.d.ts +29 -0
- package/dist/hooks/use-studio-manager.d.ts +11 -0
- package/dist/hooks/use-studio-operation.d.ts +8 -0
- package/dist/hooks/use-text-panel.d.ts +50 -0
- package/dist/hooks/use-video-preview.d.ts +11 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.js +3254 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +3254 -0
- package/dist/index.mjs.map +1 -0
- package/dist/studio.css +284 -0
- package/dist/styles/input-styles.d.ts +39 -0
- package/dist/twick.svg +3 -0
- package/dist/types/index.d.ts +134 -0
- package/dist/types/media-panel.d.ts +21 -0
- package/package.json +50 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,3254 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
5
|
+
import { forwardRef, createElement, useState, useEffect, useRef, createContext, useContext, useCallback, useMemo } from "react";
|
|
6
|
+
import { useTimelineContext, TrackElement, Track, ImageElement, AudioElement, VideoElement, TextElement, IconElement, RectElement, CircleElement, CaptionElement, ElementTextEffect, ElementAnimation } from "@twick/timeline";
|
|
7
|
+
import VideoEditor, { BrowserMediaManager, AVAILABLE_TEXT_FONTS, TEXT_EFFECTS, ANIMATIONS } from "@twick/video-editor";
|
|
8
|
+
/**
|
|
9
|
+
* @license lucide-react v0.511.0 - ISC
|
|
10
|
+
*
|
|
11
|
+
* This source code is licensed under the ISC license.
|
|
12
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
13
|
+
*/
|
|
14
|
+
const toKebabCase = (string) => string.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
15
|
+
const toCamelCase = (string) => string.replace(
|
|
16
|
+
/^([A-Z])|[\s-_]+(\w)/g,
|
|
17
|
+
(match, p1, p2) => p2 ? p2.toUpperCase() : p1.toLowerCase()
|
|
18
|
+
);
|
|
19
|
+
const toPascalCase = (string) => {
|
|
20
|
+
const camelCase = toCamelCase(string);
|
|
21
|
+
return camelCase.charAt(0).toUpperCase() + camelCase.slice(1);
|
|
22
|
+
};
|
|
23
|
+
const mergeClasses = (...classes) => classes.filter((className, index, array) => {
|
|
24
|
+
return Boolean(className) && className.trim() !== "" && array.indexOf(className) === index;
|
|
25
|
+
}).join(" ").trim();
|
|
26
|
+
const hasA11yProp = (props) => {
|
|
27
|
+
for (const prop in props) {
|
|
28
|
+
if (prop.startsWith("aria-") || prop === "role" || prop === "title") {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* @license lucide-react v0.511.0 - ISC
|
|
35
|
+
*
|
|
36
|
+
* This source code is licensed under the ISC license.
|
|
37
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
38
|
+
*/
|
|
39
|
+
var defaultAttributes = {
|
|
40
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
41
|
+
width: 24,
|
|
42
|
+
height: 24,
|
|
43
|
+
viewBox: "0 0 24 24",
|
|
44
|
+
fill: "none",
|
|
45
|
+
stroke: "currentColor",
|
|
46
|
+
strokeWidth: 2,
|
|
47
|
+
strokeLinecap: "round",
|
|
48
|
+
strokeLinejoin: "round"
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* @license lucide-react v0.511.0 - ISC
|
|
52
|
+
*
|
|
53
|
+
* This source code is licensed under the ISC license.
|
|
54
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
55
|
+
*/
|
|
56
|
+
const Icon = forwardRef(
|
|
57
|
+
({
|
|
58
|
+
color = "currentColor",
|
|
59
|
+
size = 24,
|
|
60
|
+
strokeWidth = 2,
|
|
61
|
+
absoluteStrokeWidth,
|
|
62
|
+
className = "",
|
|
63
|
+
children,
|
|
64
|
+
iconNode,
|
|
65
|
+
...rest
|
|
66
|
+
}, ref) => createElement(
|
|
67
|
+
"svg",
|
|
68
|
+
{
|
|
69
|
+
ref,
|
|
70
|
+
...defaultAttributes,
|
|
71
|
+
width: size,
|
|
72
|
+
height: size,
|
|
73
|
+
stroke: color,
|
|
74
|
+
strokeWidth: absoluteStrokeWidth ? Number(strokeWidth) * 24 / Number(size) : strokeWidth,
|
|
75
|
+
className: mergeClasses("lucide", className),
|
|
76
|
+
...!children && !hasA11yProp(rest) && { "aria-hidden": "true" },
|
|
77
|
+
...rest
|
|
78
|
+
},
|
|
79
|
+
[
|
|
80
|
+
...iconNode.map(([tag, attrs]) => createElement(tag, attrs)),
|
|
81
|
+
...Array.isArray(children) ? children : [children]
|
|
82
|
+
]
|
|
83
|
+
)
|
|
84
|
+
);
|
|
85
|
+
/**
|
|
86
|
+
* @license lucide-react v0.511.0 - ISC
|
|
87
|
+
*
|
|
88
|
+
* This source code is licensed under the ISC license.
|
|
89
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
90
|
+
*/
|
|
91
|
+
const createLucideIcon = (iconName, iconNode) => {
|
|
92
|
+
const Component = forwardRef(
|
|
93
|
+
({ className, ...props }, ref) => createElement(Icon, {
|
|
94
|
+
ref,
|
|
95
|
+
iconNode,
|
|
96
|
+
className: mergeClasses(
|
|
97
|
+
`lucide-${toKebabCase(toPascalCase(iconName))}`,
|
|
98
|
+
`lucide-${iconName}`,
|
|
99
|
+
className
|
|
100
|
+
),
|
|
101
|
+
...props
|
|
102
|
+
})
|
|
103
|
+
);
|
|
104
|
+
Component.displayName = toPascalCase(iconName);
|
|
105
|
+
return Component;
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* @license lucide-react v0.511.0 - ISC
|
|
109
|
+
*
|
|
110
|
+
* This source code is licensed under the ISC license.
|
|
111
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
112
|
+
*/
|
|
113
|
+
const __iconNode$o = [["path", { d: "M20 6 9 17l-5-5", key: "1gmf2c" }]];
|
|
114
|
+
const Check = createLucideIcon("check", __iconNode$o);
|
|
115
|
+
/**
|
|
116
|
+
* @license lucide-react v0.511.0 - ISC
|
|
117
|
+
*
|
|
118
|
+
* This source code is licensed under the ISC license.
|
|
119
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
120
|
+
*/
|
|
121
|
+
const __iconNode$n = [["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }]];
|
|
122
|
+
const Circle = createLucideIcon("circle", __iconNode$n);
|
|
123
|
+
/**
|
|
124
|
+
* @license lucide-react v0.511.0 - ISC
|
|
125
|
+
*
|
|
126
|
+
* This source code is licensed under the ISC license.
|
|
127
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
128
|
+
*/
|
|
129
|
+
const __iconNode$m = [
|
|
130
|
+
[
|
|
131
|
+
"path",
|
|
132
|
+
{ 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" }
|
|
133
|
+
],
|
|
134
|
+
["path", { d: "m6.2 5.3 3.1 3.9", key: "iuk76l" }],
|
|
135
|
+
["path", { d: "m12.4 3.4 3.1 4", key: "6hsd6n" }],
|
|
136
|
+
["path", { d: "M3 11h18v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2Z", key: "ltgou9" }]
|
|
137
|
+
];
|
|
138
|
+
const Clapperboard = createLucideIcon("clapperboard", __iconNode$m);
|
|
139
|
+
/**
|
|
140
|
+
* @license lucide-react v0.511.0 - ISC
|
|
141
|
+
*
|
|
142
|
+
* This source code is licensed under the ISC license.
|
|
143
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
144
|
+
*/
|
|
145
|
+
const __iconNode$l = [
|
|
146
|
+
["path", { d: "M12 15V3", key: "m9g1x1" }],
|
|
147
|
+
["path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4", key: "ih7n3h" }],
|
|
148
|
+
["path", { d: "m7 10 5 5 5-5", key: "brsn70" }]
|
|
149
|
+
];
|
|
150
|
+
const Download = createLucideIcon("download", __iconNode$l);
|
|
151
|
+
/**
|
|
152
|
+
* @license lucide-react v0.511.0 - ISC
|
|
153
|
+
*
|
|
154
|
+
* This source code is licensed under the ISC license.
|
|
155
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
156
|
+
*/
|
|
157
|
+
const __iconNode$k = [
|
|
158
|
+
["path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z", key: "1rqfz7" }],
|
|
159
|
+
["path", { d: "M14 2v4a2 2 0 0 0 2 2h4", key: "tnqrlb" }]
|
|
160
|
+
];
|
|
161
|
+
const File = createLucideIcon("file", __iconNode$k);
|
|
162
|
+
/**
|
|
163
|
+
* @license lucide-react v0.511.0 - ISC
|
|
164
|
+
*
|
|
165
|
+
* This source code is licensed under the ISC license.
|
|
166
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
167
|
+
*/
|
|
168
|
+
const __iconNode$j = [
|
|
169
|
+
["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2", key: "1m3agn" }],
|
|
170
|
+
["circle", { cx: "9", cy: "9", r: "2", key: "af1f0g" }],
|
|
171
|
+
["path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21", key: "1xmnt7" }]
|
|
172
|
+
];
|
|
173
|
+
const Image = createLucideIcon("image", __iconNode$j);
|
|
174
|
+
/**
|
|
175
|
+
* @license lucide-react v0.511.0 - ISC
|
|
176
|
+
*
|
|
177
|
+
* This source code is licensed under the ISC license.
|
|
178
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
179
|
+
*/
|
|
180
|
+
const __iconNode$i = [
|
|
181
|
+
["path", { d: "M6 16c5 0 7-8 12-8a4 4 0 0 1 0 8c-5 0-7-8-12-8a4 4 0 1 0 0 8", key: "18ogeb" }]
|
|
182
|
+
];
|
|
183
|
+
const Infinity = createLucideIcon("infinity", __iconNode$i);
|
|
184
|
+
/**
|
|
185
|
+
* @license lucide-react v0.511.0 - ISC
|
|
186
|
+
*
|
|
187
|
+
* This source code is licensed under the ISC license.
|
|
188
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
189
|
+
*/
|
|
190
|
+
const __iconNode$h = [["path", { d: "M21 12a9 9 0 1 1-6.219-8.56", key: "13zald" }]];
|
|
191
|
+
const LoaderCircle = createLucideIcon("loader-circle", __iconNode$h);
|
|
192
|
+
/**
|
|
193
|
+
* @license lucide-react v0.511.0 - ISC
|
|
194
|
+
*
|
|
195
|
+
* This source code is licensed under the ISC license.
|
|
196
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
197
|
+
*/
|
|
198
|
+
const __iconNode$g = [
|
|
199
|
+
["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" }]
|
|
200
|
+
];
|
|
201
|
+
const MessageSquare = createLucideIcon("message-square", __iconNode$g);
|
|
202
|
+
/**
|
|
203
|
+
* @license lucide-react v0.511.0 - ISC
|
|
204
|
+
*
|
|
205
|
+
* This source code is licensed under the ISC license.
|
|
206
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
207
|
+
*/
|
|
208
|
+
const __iconNode$f = [
|
|
209
|
+
["path", { d: "M9 18V5l12-2v13", key: "1jmyc2" }],
|
|
210
|
+
["circle", { cx: "6", cy: "18", r: "3", key: "fqmcym" }],
|
|
211
|
+
["circle", { cx: "18", cy: "16", r: "3", key: "1hluhg" }]
|
|
212
|
+
];
|
|
213
|
+
const Music = createLucideIcon("music", __iconNode$f);
|
|
214
|
+
/**
|
|
215
|
+
* @license lucide-react v0.511.0 - ISC
|
|
216
|
+
*
|
|
217
|
+
* This source code is licensed under the ISC license.
|
|
218
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
219
|
+
*/
|
|
220
|
+
const __iconNode$e = [
|
|
221
|
+
["rect", { x: "14", y: "4", width: "4", height: "16", rx: "1", key: "zuxfzm" }],
|
|
222
|
+
["rect", { x: "6", y: "4", width: "4", height: "16", rx: "1", key: "1okwgv" }]
|
|
223
|
+
];
|
|
224
|
+
const Pause = createLucideIcon("pause", __iconNode$e);
|
|
225
|
+
/**
|
|
226
|
+
* @license lucide-react v0.511.0 - ISC
|
|
227
|
+
*
|
|
228
|
+
* This source code is licensed under the ISC license.
|
|
229
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
230
|
+
*/
|
|
231
|
+
const __iconNode$d = [["polygon", { points: "6 3 20 12 6 21 6 3", key: "1oa8hb" }]];
|
|
232
|
+
const Play = createLucideIcon("play", __iconNode$d);
|
|
233
|
+
/**
|
|
234
|
+
* @license lucide-react v0.511.0 - ISC
|
|
235
|
+
*
|
|
236
|
+
* This source code is licensed under the ISC license.
|
|
237
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
238
|
+
*/
|
|
239
|
+
const __iconNode$c = [
|
|
240
|
+
["path", { d: "M5 12h14", key: "1ays0h" }],
|
|
241
|
+
["path", { d: "M12 5v14", key: "s699le" }]
|
|
242
|
+
];
|
|
243
|
+
const Plus = createLucideIcon("plus", __iconNode$c);
|
|
244
|
+
/**
|
|
245
|
+
* @license lucide-react v0.511.0 - ISC
|
|
246
|
+
*
|
|
247
|
+
* This source code is licensed under the ISC license.
|
|
248
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
249
|
+
*/
|
|
250
|
+
const __iconNode$b = [
|
|
251
|
+
[
|
|
252
|
+
"path",
|
|
253
|
+
{
|
|
254
|
+
d: "M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z",
|
|
255
|
+
key: "1c8476"
|
|
256
|
+
}
|
|
257
|
+
],
|
|
258
|
+
["path", { d: "M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7", key: "1ydtos" }],
|
|
259
|
+
["path", { d: "M7 3v4a1 1 0 0 0 1 1h7", key: "t51u73" }]
|
|
260
|
+
];
|
|
261
|
+
const Save = createLucideIcon("save", __iconNode$b);
|
|
262
|
+
/**
|
|
263
|
+
* @license lucide-react v0.511.0 - ISC
|
|
264
|
+
*
|
|
265
|
+
* This source code is licensed under the ISC license.
|
|
266
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
267
|
+
*/
|
|
268
|
+
const __iconNode$a = [
|
|
269
|
+
["path", { d: "m21 21-4.34-4.34", key: "14j7rj" }],
|
|
270
|
+
["circle", { cx: "11", cy: "11", r: "8", key: "4ej97u" }]
|
|
271
|
+
];
|
|
272
|
+
const Search = createLucideIcon("search", __iconNode$a);
|
|
273
|
+
/**
|
|
274
|
+
* @license lucide-react v0.511.0 - ISC
|
|
275
|
+
*
|
|
276
|
+
* This source code is licensed under the ISC license.
|
|
277
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
278
|
+
*/
|
|
279
|
+
const __iconNode$9 = [
|
|
280
|
+
[
|
|
281
|
+
"path",
|
|
282
|
+
{
|
|
283
|
+
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",
|
|
284
|
+
key: "1qme2f"
|
|
285
|
+
}
|
|
286
|
+
],
|
|
287
|
+
["circle", { cx: "12", cy: "12", r: "3", key: "1v7zrd" }]
|
|
288
|
+
];
|
|
289
|
+
const Settings = createLucideIcon("settings", __iconNode$9);
|
|
290
|
+
/**
|
|
291
|
+
* @license lucide-react v0.511.0 - ISC
|
|
292
|
+
*
|
|
293
|
+
* This source code is licensed under the ISC license.
|
|
294
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
295
|
+
*/
|
|
296
|
+
const __iconNode$8 = [
|
|
297
|
+
[
|
|
298
|
+
"path",
|
|
299
|
+
{
|
|
300
|
+
d: "M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z",
|
|
301
|
+
key: "4pj2yx"
|
|
302
|
+
}
|
|
303
|
+
],
|
|
304
|
+
["path", { d: "M20 3v4", key: "1olli1" }],
|
|
305
|
+
["path", { d: "M22 5h-4", key: "1gvqau" }],
|
|
306
|
+
["path", { d: "M4 17v2", key: "vumght" }],
|
|
307
|
+
["path", { d: "M5 18H3", key: "zchphs" }]
|
|
308
|
+
];
|
|
309
|
+
const Sparkles = createLucideIcon("sparkles", __iconNode$8);
|
|
310
|
+
/**
|
|
311
|
+
* @license lucide-react v0.511.0 - ISC
|
|
312
|
+
*
|
|
313
|
+
* This source code is licensed under the ISC license.
|
|
314
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
315
|
+
*/
|
|
316
|
+
const __iconNode$7 = [
|
|
317
|
+
["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", key: "afitv7" }]
|
|
318
|
+
];
|
|
319
|
+
const Square = createLucideIcon("square", __iconNode$7);
|
|
320
|
+
/**
|
|
321
|
+
* @license lucide-react v0.511.0 - ISC
|
|
322
|
+
*
|
|
323
|
+
* This source code is licensed under the ISC license.
|
|
324
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
325
|
+
*/
|
|
326
|
+
const __iconNode$6 = [
|
|
327
|
+
["path", { d: "M3 6h18", key: "d0wm0j" }],
|
|
328
|
+
["path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6", key: "4alrt4" }],
|
|
329
|
+
["path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2", key: "v07s0e" }],
|
|
330
|
+
["line", { x1: "10", x2: "10", y1: "11", y2: "17", key: "1uufr5" }],
|
|
331
|
+
["line", { x1: "14", x2: "14", y1: "11", y2: "17", key: "xtxkd" }]
|
|
332
|
+
];
|
|
333
|
+
const Trash2 = createLucideIcon("trash-2", __iconNode$6);
|
|
334
|
+
/**
|
|
335
|
+
* @license lucide-react v0.511.0 - ISC
|
|
336
|
+
*
|
|
337
|
+
* This source code is licensed under the ISC license.
|
|
338
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
339
|
+
*/
|
|
340
|
+
const __iconNode$5 = [
|
|
341
|
+
["path", { d: "M12 4v16", key: "1654pz" }],
|
|
342
|
+
["path", { d: "M4 7V5a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v2", key: "e0r10z" }],
|
|
343
|
+
["path", { d: "M9 20h6", key: "s66wpe" }]
|
|
344
|
+
];
|
|
345
|
+
const Type = createLucideIcon("type", __iconNode$5);
|
|
346
|
+
/**
|
|
347
|
+
* @license lucide-react v0.511.0 - ISC
|
|
348
|
+
*
|
|
349
|
+
* This source code is licensed under the ISC license.
|
|
350
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
351
|
+
*/
|
|
352
|
+
const __iconNode$4 = [
|
|
353
|
+
["path", { d: "M12 3v12", key: "1x0j5s" }],
|
|
354
|
+
["path", { d: "m17 8-5-5-5 5", key: "7q97r8" }],
|
|
355
|
+
["path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4", key: "ih7n3h" }]
|
|
356
|
+
];
|
|
357
|
+
const Upload = createLucideIcon("upload", __iconNode$4);
|
|
358
|
+
/**
|
|
359
|
+
* @license lucide-react v0.511.0 - ISC
|
|
360
|
+
*
|
|
361
|
+
* This source code is licensed under the ISC license.
|
|
362
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
363
|
+
*/
|
|
364
|
+
const __iconNode$3 = [
|
|
365
|
+
[
|
|
366
|
+
"path",
|
|
367
|
+
{
|
|
368
|
+
d: "m16 13 5.223 3.482a.5.5 0 0 0 .777-.416V7.87a.5.5 0 0 0-.752-.432L16 10.5",
|
|
369
|
+
key: "ftymec"
|
|
370
|
+
}
|
|
371
|
+
],
|
|
372
|
+
["rect", { x: "2", y: "6", width: "14", height: "12", rx: "2", key: "158x01" }]
|
|
373
|
+
];
|
|
374
|
+
const Video = createLucideIcon("video", __iconNode$3);
|
|
375
|
+
/**
|
|
376
|
+
* @license lucide-react v0.511.0 - ISC
|
|
377
|
+
*
|
|
378
|
+
* This source code is licensed under the ISC license.
|
|
379
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
380
|
+
*/
|
|
381
|
+
const __iconNode$2 = [
|
|
382
|
+
[
|
|
383
|
+
"path",
|
|
384
|
+
{
|
|
385
|
+
d: "M11 4.702a.705.705 0 0 0-1.203-.498L6.413 7.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.705.705 0 0 0 11 19.298z",
|
|
386
|
+
key: "uqj9uw"
|
|
387
|
+
}
|
|
388
|
+
],
|
|
389
|
+
["path", { d: "M16 9a5 5 0 0 1 0 6", key: "1q6k2b" }],
|
|
390
|
+
["path", { d: "M19.364 18.364a9 9 0 0 0 0-12.728", key: "ijwkga" }]
|
|
391
|
+
];
|
|
392
|
+
const Volume2 = createLucideIcon("volume-2", __iconNode$2);
|
|
393
|
+
/**
|
|
394
|
+
* @license lucide-react v0.511.0 - ISC
|
|
395
|
+
*
|
|
396
|
+
* This source code is licensed under the ISC license.
|
|
397
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
398
|
+
*/
|
|
399
|
+
const __iconNode$1 = [
|
|
400
|
+
[
|
|
401
|
+
"path",
|
|
402
|
+
{
|
|
403
|
+
d: "m21.64 3.64-1.28-1.28a1.21 1.21 0 0 0-1.72 0L2.36 18.64a1.21 1.21 0 0 0 0 1.72l1.28 1.28a1.2 1.2 0 0 0 1.72 0L21.64 5.36a1.2 1.2 0 0 0 0-1.72",
|
|
404
|
+
key: "ul74o6"
|
|
405
|
+
}
|
|
406
|
+
],
|
|
407
|
+
["path", { d: "m14 7 3 3", key: "1r5n42" }],
|
|
408
|
+
["path", { d: "M5 6v4", key: "ilb8ba" }],
|
|
409
|
+
["path", { d: "M19 14v4", key: "blhpug" }],
|
|
410
|
+
["path", { d: "M10 2v2", key: "7u0qdc" }],
|
|
411
|
+
["path", { d: "M7 8H3", key: "zfb6yr" }],
|
|
412
|
+
["path", { d: "M21 16h-4", key: "1cnmox" }],
|
|
413
|
+
["path", { d: "M11 3H9", key: "1obp7u" }]
|
|
414
|
+
];
|
|
415
|
+
const WandSparkles = createLucideIcon("wand-sparkles", __iconNode$1);
|
|
416
|
+
/**
|
|
417
|
+
* @license lucide-react v0.511.0 - ISC
|
|
418
|
+
*
|
|
419
|
+
* This source code is licensed under the ISC license.
|
|
420
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
421
|
+
*/
|
|
422
|
+
const __iconNode = [
|
|
423
|
+
[
|
|
424
|
+
"path",
|
|
425
|
+
{
|
|
426
|
+
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",
|
|
427
|
+
key: "1xq2db"
|
|
428
|
+
}
|
|
429
|
+
]
|
|
430
|
+
];
|
|
431
|
+
const Zap = createLucideIcon("zap", __iconNode);
|
|
432
|
+
const toolCategories = [
|
|
433
|
+
{ id: "video", name: "Video", icon: "Video", description: "Video" },
|
|
434
|
+
{ id: "image", name: "Image", icon: "Image", description: "Image" },
|
|
435
|
+
{ id: "audio", name: "Audio", icon: "Audio", description: "Audio" },
|
|
436
|
+
{ id: "text", name: "Text", icon: "Type", description: "Add text elements", shortcut: "T" },
|
|
437
|
+
{ id: "icon", name: "Icons", icon: "Icon", description: "Icon Element", shortcut: "I" },
|
|
438
|
+
{ id: "circle", name: "Circle", icon: "Circle", description: "Circle Element", shortcut: "C" },
|
|
439
|
+
{ id: "rect", name: "Rect", icon: "Rect", description: "Rect Element" }
|
|
440
|
+
// { id: 'subtitle', name: 'Subtitles', icon: 'MessageSquare', description: 'Manage subtitles', shortcut: 'S' },
|
|
441
|
+
];
|
|
442
|
+
const getIcon$1 = (iconName) => {
|
|
443
|
+
switch (iconName) {
|
|
444
|
+
case "Plus":
|
|
445
|
+
return Plus;
|
|
446
|
+
case "Type":
|
|
447
|
+
return Type;
|
|
448
|
+
case "Icon":
|
|
449
|
+
return Infinity;
|
|
450
|
+
case "Upload":
|
|
451
|
+
return Upload;
|
|
452
|
+
case "Square":
|
|
453
|
+
return Square;
|
|
454
|
+
case "Image":
|
|
455
|
+
return Image;
|
|
456
|
+
case "Video":
|
|
457
|
+
return Video;
|
|
458
|
+
case "Audio":
|
|
459
|
+
return Music;
|
|
460
|
+
case "Circle":
|
|
461
|
+
return Circle;
|
|
462
|
+
case "Rect":
|
|
463
|
+
return Square;
|
|
464
|
+
case "MessageSquare":
|
|
465
|
+
return MessageSquare;
|
|
466
|
+
default:
|
|
467
|
+
return Plus;
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
function Toolbar({ selectedTool, setSelectedTool }) {
|
|
471
|
+
const handleToolSelect = (toolId) => {
|
|
472
|
+
setSelectedTool(toolId);
|
|
473
|
+
};
|
|
474
|
+
return /* @__PURE__ */ jsx("div", { className: "w-16 bg-neutral/80 border-r border-gray-300/50 flex flex-col items-center py-4 space-y-3 justify-start backdrop-blur-md shadow-lg", children: toolCategories.map((tool) => {
|
|
475
|
+
const Icon2 = getIcon$1(tool.icon);
|
|
476
|
+
const isSelected = selectedTool === tool.id;
|
|
477
|
+
return /* @__PURE__ */ jsxs(
|
|
478
|
+
"button",
|
|
479
|
+
{
|
|
480
|
+
onClick: () => handleToolSelect(tool.id),
|
|
481
|
+
className: `
|
|
482
|
+
toolbar-btn group ${isSelected ? "active" : ""}
|
|
483
|
+
`,
|
|
484
|
+
title: `${tool.name}${tool.shortcut ? ` (${tool.shortcut})` : ""}`,
|
|
485
|
+
children: [
|
|
486
|
+
/* @__PURE__ */ jsx(Icon2, { className: "w-4 h-4" }),
|
|
487
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] mt-1 transition-opacity duration-200", children: tool.name })
|
|
488
|
+
]
|
|
489
|
+
},
|
|
490
|
+
tool.id
|
|
491
|
+
);
|
|
492
|
+
}) });
|
|
493
|
+
}
|
|
494
|
+
const StudioHeader = ({
|
|
495
|
+
setVideoResolution,
|
|
496
|
+
onLoadProject,
|
|
497
|
+
onSaveProject,
|
|
498
|
+
onExportVideo
|
|
499
|
+
}) => {
|
|
500
|
+
const [orientation, setOrientation] = useState(
|
|
501
|
+
"vertical"
|
|
502
|
+
);
|
|
503
|
+
useEffect(() => {
|
|
504
|
+
const orientation2 = localStorage.getItem("orientation");
|
|
505
|
+
if (orientation2) {
|
|
506
|
+
setOrientation(orientation2);
|
|
507
|
+
}
|
|
508
|
+
}, []);
|
|
509
|
+
useEffect(() => {
|
|
510
|
+
if (orientation === "horizontal") {
|
|
511
|
+
localStorage.setItem("orientation", "horizontal");
|
|
512
|
+
setVideoResolution({ width: 1280, height: 720 });
|
|
513
|
+
} else {
|
|
514
|
+
localStorage.setItem("orientation", "vertical");
|
|
515
|
+
setVideoResolution({ width: 720, height: 1280 });
|
|
516
|
+
}
|
|
517
|
+
}, [orientation]);
|
|
518
|
+
return /* @__PURE__ */ jsxs("header", { className: "h-14 bg-neutral-800/90 border-b border-gray-600/50 flex items-center justify-between px-4 backdrop-blur-md shadow-lg", children: [
|
|
519
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
520
|
+
/* @__PURE__ */ jsx(Clapperboard, { className: "w-8 h-8 text-purple-400" }),
|
|
521
|
+
/* @__PURE__ */ jsx("h1", { className: "text-lg font-bold text-gradient-purple", children: "Twick Studio" })
|
|
522
|
+
] }) }),
|
|
523
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
524
|
+
/* @__PURE__ */ jsxs(
|
|
525
|
+
"button",
|
|
526
|
+
{
|
|
527
|
+
className: "btn btn-ghost w-32",
|
|
528
|
+
title: "Load Project",
|
|
529
|
+
onClick: onLoadProject,
|
|
530
|
+
children: [
|
|
531
|
+
/* @__PURE__ */ jsx(File, { className: "w-4 h-4 mr-2" }),
|
|
532
|
+
"Load Project"
|
|
533
|
+
]
|
|
534
|
+
}
|
|
535
|
+
),
|
|
536
|
+
/* @__PURE__ */ jsxs(
|
|
537
|
+
"button",
|
|
538
|
+
{
|
|
539
|
+
className: "btn btn-ghost w-32",
|
|
540
|
+
title: "Save Draft",
|
|
541
|
+
onClick: onSaveProject,
|
|
542
|
+
children: [
|
|
543
|
+
/* @__PURE__ */ jsx(Save, { className: "w-4 h-4 mr-2" }),
|
|
544
|
+
"Save Draft"
|
|
545
|
+
]
|
|
546
|
+
}
|
|
547
|
+
),
|
|
548
|
+
/* @__PURE__ */ jsxs(
|
|
549
|
+
"button",
|
|
550
|
+
{
|
|
551
|
+
className: "btn btn-primary w-32",
|
|
552
|
+
title: "Export",
|
|
553
|
+
onClick: onExportVideo,
|
|
554
|
+
children: [
|
|
555
|
+
/* @__PURE__ */ jsx(Download, { className: "w-4 h-4 mr-2" }),
|
|
556
|
+
"Export"
|
|
557
|
+
]
|
|
558
|
+
}
|
|
559
|
+
)
|
|
560
|
+
] })
|
|
561
|
+
] });
|
|
562
|
+
};
|
|
563
|
+
const useStudioManager = () => {
|
|
564
|
+
const [selectedProp, setSelectedProp] = useState("element-props");
|
|
565
|
+
const { editor, selectedItem, setSelectedItem } = useTimelineContext();
|
|
566
|
+
const selectedElement = selectedItem instanceof TrackElement ? selectedItem : null;
|
|
567
|
+
const [selectedTool, setSelectedTool] = useState("none");
|
|
568
|
+
const isToolChanged = useRef(false);
|
|
569
|
+
const addElement = (element) => {
|
|
570
|
+
if (selectedItem instanceof Track) {
|
|
571
|
+
editor.addElementToTrack(selectedItem, element);
|
|
572
|
+
} else {
|
|
573
|
+
const newTrack = editor.addTrack("Track");
|
|
574
|
+
editor.addElementToTrack(newTrack, element);
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
const updateElement = (element) => {
|
|
578
|
+
const updatedElement = editor.updateElement(element);
|
|
579
|
+
editor.refresh();
|
|
580
|
+
setSelectedItem(updatedElement);
|
|
581
|
+
};
|
|
582
|
+
useEffect(() => {
|
|
583
|
+
if (selectedItem instanceof TrackElement) {
|
|
584
|
+
setSelectedTool(selectedItem.getType());
|
|
585
|
+
isToolChanged.current = true;
|
|
586
|
+
} else if (selectedItem instanceof Track) ;
|
|
587
|
+
else {
|
|
588
|
+
if (isToolChanged.current) {
|
|
589
|
+
setSelectedTool("none");
|
|
590
|
+
} else {
|
|
591
|
+
setSelectedTool("video");
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}, [selectedItem]);
|
|
595
|
+
return {
|
|
596
|
+
selectedProp,
|
|
597
|
+
setSelectedProp,
|
|
598
|
+
selectedTool,
|
|
599
|
+
setSelectedTool,
|
|
600
|
+
selectedElement,
|
|
601
|
+
addElement,
|
|
602
|
+
updateElement
|
|
603
|
+
};
|
|
604
|
+
};
|
|
605
|
+
function SubtitlesPanel() {
|
|
606
|
+
const [subtitles, setSubtitles] = useState([]);
|
|
607
|
+
const handleGenerate = () => {
|
|
608
|
+
console.log("Generating subtitles...");
|
|
609
|
+
};
|
|
610
|
+
const handleAdd = () => {
|
|
611
|
+
const newId = (subtitles.length + 1).toString();
|
|
612
|
+
const lastEnd = subtitles.length > 0 ? subtitles[subtitles.length - 1].end : 0;
|
|
613
|
+
const newSubtitle = {
|
|
614
|
+
id: newId,
|
|
615
|
+
start: lastEnd,
|
|
616
|
+
end: lastEnd + 1,
|
|
617
|
+
text: ""
|
|
618
|
+
};
|
|
619
|
+
setSubtitles([...subtitles, newSubtitle]);
|
|
620
|
+
};
|
|
621
|
+
const handleDelete = (id) => {
|
|
622
|
+
setSubtitles(subtitles.filter((sub) => sub.id !== id));
|
|
623
|
+
};
|
|
624
|
+
const handleSave = (id) => {
|
|
625
|
+
console.log("Saving subtitle:", id);
|
|
626
|
+
};
|
|
627
|
+
const handleUpdateSubtitle = (id, field, value) => {
|
|
628
|
+
setSubtitles(subtitles.map(
|
|
629
|
+
(sub) => sub.id === id ? { ...sub, [field]: value } : sub
|
|
630
|
+
));
|
|
631
|
+
};
|
|
632
|
+
return /* @__PURE__ */ jsxs("div", { className: "w-72 h-full bg-neutral-800/80 border-l border-gray-600/50 p-3 overflow-y-auto overflow-x-hidden backdrop-blur-md shadow-lg", children: [
|
|
633
|
+
/* @__PURE__ */ jsx("h3", { className: "text-xl font-bold text-white mb-6", children: "Subtitles" }),
|
|
634
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-3 mb-6", children: [
|
|
635
|
+
/* @__PURE__ */ jsx(
|
|
636
|
+
"button",
|
|
637
|
+
{
|
|
638
|
+
onClick: handleGenerate,
|
|
639
|
+
className: "flex-1 bg-neutral-700 hover:bg-neutral-600 text-white font-medium py-2 px-4 rounded-lg transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-purple-500/20",
|
|
640
|
+
children: "Generate"
|
|
641
|
+
}
|
|
642
|
+
),
|
|
643
|
+
/* @__PURE__ */ jsx(
|
|
644
|
+
"button",
|
|
645
|
+
{
|
|
646
|
+
onClick: handleAdd,
|
|
647
|
+
className: "flex-1 bg-neutral-700 hover:bg-neutral-600 text-white font-medium py-2 px-4 rounded-lg border-2 border-purple-500 transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-purple-500/20",
|
|
648
|
+
children: "Add"
|
|
649
|
+
}
|
|
650
|
+
)
|
|
651
|
+
] }),
|
|
652
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-4", children: subtitles.map((subtitle) => /* @__PURE__ */ jsxs(
|
|
653
|
+
"div",
|
|
654
|
+
{
|
|
655
|
+
className: "bg-neutral-700/50 border border-gray-600 rounded-lg p-3",
|
|
656
|
+
children: [
|
|
657
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-3 mb-3", children: [
|
|
658
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
|
|
659
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs text-gray-400 mb-1", children: "Start" }),
|
|
660
|
+
/* @__PURE__ */ jsx(
|
|
661
|
+
"input",
|
|
662
|
+
{
|
|
663
|
+
type: "number",
|
|
664
|
+
min: "0",
|
|
665
|
+
step: "0.1",
|
|
666
|
+
value: subtitle.start,
|
|
667
|
+
onChange: (e) => handleUpdateSubtitle(subtitle.id, "start", Number(e.target.value)),
|
|
668
|
+
className: "w-full bg-neutral-800/80 border border-gray-600 rounded-lg text-white text-sm px-3 py-2 focus:border-purple-500 focus:outline-none focus:ring-2 focus:ring-purple-500/20 transition-all duration-200 backdrop-blur-sm"
|
|
669
|
+
}
|
|
670
|
+
)
|
|
671
|
+
] }),
|
|
672
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
|
|
673
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs text-gray-400 mb-1", children: "End" }),
|
|
674
|
+
/* @__PURE__ */ jsx(
|
|
675
|
+
"input",
|
|
676
|
+
{
|
|
677
|
+
type: "number",
|
|
678
|
+
min: "0",
|
|
679
|
+
step: "0.1",
|
|
680
|
+
value: subtitle.end,
|
|
681
|
+
onChange: (e) => handleUpdateSubtitle(subtitle.id, "end", Number(e.target.value)),
|
|
682
|
+
className: "w-full bg-neutral-800/80 border border-gray-600 rounded-lg text-white text-sm px-3 py-2 focus:border-purple-500 focus:outline-none focus:ring-2 focus:ring-purple-500/20 transition-all duration-200 backdrop-blur-sm"
|
|
683
|
+
}
|
|
684
|
+
)
|
|
685
|
+
] })
|
|
686
|
+
] }),
|
|
687
|
+
/* @__PURE__ */ jsx("div", { className: "mb-4", children: /* @__PURE__ */ jsx(
|
|
688
|
+
"input",
|
|
689
|
+
{
|
|
690
|
+
type: "text",
|
|
691
|
+
placeholder: "subtitle",
|
|
692
|
+
value: subtitle.text,
|
|
693
|
+
onChange: (e) => handleUpdateSubtitle(subtitle.id, "text", e.target.value),
|
|
694
|
+
className: "w-full bg-neutral-800/80 border border-gray-600 rounded-lg text-white text-sm px-3 py-2 placeholder-gray-400 focus:border-purple-500 focus:outline-none focus:ring-2 focus:ring-purple-500/20 transition-all duration-200 backdrop-blur-sm"
|
|
695
|
+
}
|
|
696
|
+
) }),
|
|
697
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
|
|
698
|
+
/* @__PURE__ */ jsx(
|
|
699
|
+
"button",
|
|
700
|
+
{
|
|
701
|
+
onClick: () => handleDelete(subtitle.id),
|
|
702
|
+
className: "p-2 text-red-400 hover:text-red-300 hover:bg-red-500/20 rounded-lg transition-all duration-200",
|
|
703
|
+
title: "Delete subtitle",
|
|
704
|
+
children: /* @__PURE__ */ jsx(Trash2, { className: "w-4 h-4" })
|
|
705
|
+
}
|
|
706
|
+
),
|
|
707
|
+
/* @__PURE__ */ jsx(
|
|
708
|
+
"button",
|
|
709
|
+
{
|
|
710
|
+
onClick: () => handleSave(subtitle.id),
|
|
711
|
+
className: "p-2 text-green-400 hover:text-green-300 hover:bg-green-500/20 rounded-lg transition-all duration-200",
|
|
712
|
+
title: "Save subtitle",
|
|
713
|
+
children: /* @__PURE__ */ jsx(Check, { className: "w-4 h-4" })
|
|
714
|
+
}
|
|
715
|
+
)
|
|
716
|
+
] })
|
|
717
|
+
]
|
|
718
|
+
},
|
|
719
|
+
subtitle.id
|
|
720
|
+
)) }),
|
|
721
|
+
subtitles.length === 0 && /* @__PURE__ */ jsxs("div", { className: "text-center py-8 text-gray-400", children: [
|
|
722
|
+
/* @__PURE__ */ jsx("p", { children: "No subtitles yet" }),
|
|
723
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm mt-2", children: 'Click "Add" to create your first subtitle' })
|
|
724
|
+
] })
|
|
725
|
+
] });
|
|
726
|
+
}
|
|
727
|
+
const FileInput = ({
|
|
728
|
+
acceptFileTypes,
|
|
729
|
+
onFileLoad,
|
|
730
|
+
buttonText,
|
|
731
|
+
id
|
|
732
|
+
}) => {
|
|
733
|
+
const onFileChange = (e) => {
|
|
734
|
+
var _a;
|
|
735
|
+
const file = (_a = e.target.files) == null ? void 0 : _a[0];
|
|
736
|
+
if (file) {
|
|
737
|
+
const reader = new FileReader();
|
|
738
|
+
reader.onload = (event) => {
|
|
739
|
+
var _a2;
|
|
740
|
+
try {
|
|
741
|
+
onFileLoad({
|
|
742
|
+
content: file.type === "application/json" ? (_a2 = event.target) == null ? void 0 : _a2.result : void 0,
|
|
743
|
+
type: file.type,
|
|
744
|
+
name: file.name,
|
|
745
|
+
file,
|
|
746
|
+
blobUrl: URL.createObjectURL(file)
|
|
747
|
+
});
|
|
748
|
+
} catch (error) {
|
|
749
|
+
console.error("Error parsing file:", error);
|
|
750
|
+
}
|
|
751
|
+
};
|
|
752
|
+
console.log("file", file);
|
|
753
|
+
if (file.type === "application/json") {
|
|
754
|
+
reader.readAsText(file);
|
|
755
|
+
} else {
|
|
756
|
+
reader.readAsDataURL(file);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
};
|
|
760
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative w-full", children: [
|
|
761
|
+
/* @__PURE__ */ jsx(
|
|
762
|
+
"input",
|
|
763
|
+
{
|
|
764
|
+
type: "file",
|
|
765
|
+
accept: acceptFileTypes.join(","),
|
|
766
|
+
className: "absolute w-0.1 h-0.1 opacity-0 overflow-hidden -z-10",
|
|
767
|
+
id,
|
|
768
|
+
onChange: onFileChange
|
|
769
|
+
}
|
|
770
|
+
),
|
|
771
|
+
/* @__PURE__ */ jsxs(
|
|
772
|
+
"label",
|
|
773
|
+
{
|
|
774
|
+
htmlFor: id,
|
|
775
|
+
className: "w-full btn btn-primary flex items-center justify-center gap-2 py-2",
|
|
776
|
+
children: [
|
|
777
|
+
/* @__PURE__ */ jsx(Upload, { className: "w-4 h-4" }),
|
|
778
|
+
buttonText ?? "Upload"
|
|
779
|
+
]
|
|
780
|
+
}
|
|
781
|
+
)
|
|
782
|
+
] });
|
|
783
|
+
};
|
|
784
|
+
const _MediaManagerSingleton = class _MediaManagerSingleton {
|
|
785
|
+
constructor() {
|
|
786
|
+
}
|
|
787
|
+
static getInstance() {
|
|
788
|
+
if (!_MediaManagerSingleton.instance) {
|
|
789
|
+
_MediaManagerSingleton.instance = new BrowserMediaManager();
|
|
790
|
+
}
|
|
791
|
+
return _MediaManagerSingleton.instance;
|
|
792
|
+
}
|
|
793
|
+
};
|
|
794
|
+
__publicField(_MediaManagerSingleton, "instance", null);
|
|
795
|
+
let MediaManagerSingleton = _MediaManagerSingleton;
|
|
796
|
+
const getMediaManager = () => MediaManagerSingleton.getInstance();
|
|
797
|
+
const initialMediaState = {
|
|
798
|
+
items: [],
|
|
799
|
+
searchQuery: "",
|
|
800
|
+
isLoading: false
|
|
801
|
+
};
|
|
802
|
+
const MediaContext = createContext(null);
|
|
803
|
+
function MediaProvider({ children }) {
|
|
804
|
+
const [videoState, setVideoState] = useState(initialMediaState);
|
|
805
|
+
const [audioState, setAudioState] = useState(initialMediaState);
|
|
806
|
+
const [imageState, setImageState] = useState(initialMediaState);
|
|
807
|
+
const mediaManager = getMediaManager();
|
|
808
|
+
const getStateAndSetter = (type) => {
|
|
809
|
+
switch (type) {
|
|
810
|
+
case "video":
|
|
811
|
+
return [videoState, setVideoState];
|
|
812
|
+
case "audio":
|
|
813
|
+
return [audioState, setAudioState];
|
|
814
|
+
case "image":
|
|
815
|
+
return [imageState, setImageState];
|
|
816
|
+
}
|
|
817
|
+
};
|
|
818
|
+
const loadItems = async (type, query) => {
|
|
819
|
+
const [state, setState] = getStateAndSetter(type);
|
|
820
|
+
setState({ ...state, isLoading: true });
|
|
821
|
+
try {
|
|
822
|
+
const results = await mediaManager.search({
|
|
823
|
+
query,
|
|
824
|
+
type
|
|
825
|
+
});
|
|
826
|
+
setState({
|
|
827
|
+
items: results,
|
|
828
|
+
searchQuery: query,
|
|
829
|
+
isLoading: false
|
|
830
|
+
});
|
|
831
|
+
} catch (error) {
|
|
832
|
+
console.error(`Error loading ${type} items:`, error);
|
|
833
|
+
setState({
|
|
834
|
+
...state,
|
|
835
|
+
isLoading: false
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
};
|
|
839
|
+
useEffect(() => {
|
|
840
|
+
loadItems("video", "");
|
|
841
|
+
loadItems("audio", "");
|
|
842
|
+
loadItems("image", "");
|
|
843
|
+
}, []);
|
|
844
|
+
const setSearchQuery = (type, query) => {
|
|
845
|
+
const [state, setState] = getStateAndSetter(type);
|
|
846
|
+
setState({ ...state, searchQuery: query });
|
|
847
|
+
loadItems(type, query);
|
|
848
|
+
};
|
|
849
|
+
const addItem = (type, newItem) => {
|
|
850
|
+
const [state, setState] = getStateAndSetter(type);
|
|
851
|
+
setState({
|
|
852
|
+
...state,
|
|
853
|
+
items: [...state.items, newItem]
|
|
854
|
+
});
|
|
855
|
+
};
|
|
856
|
+
return /* @__PURE__ */ jsx(
|
|
857
|
+
MediaContext.Provider,
|
|
858
|
+
{
|
|
859
|
+
value: {
|
|
860
|
+
videoState,
|
|
861
|
+
audioState,
|
|
862
|
+
imageState,
|
|
863
|
+
setSearchQuery,
|
|
864
|
+
addItem
|
|
865
|
+
},
|
|
866
|
+
children
|
|
867
|
+
}
|
|
868
|
+
);
|
|
869
|
+
}
|
|
870
|
+
function useMedia(type) {
|
|
871
|
+
const context = useContext(MediaContext);
|
|
872
|
+
if (!context) {
|
|
873
|
+
throw new Error("useMedia must be used within a MediaProvider");
|
|
874
|
+
}
|
|
875
|
+
const state = context[`${type}State`];
|
|
876
|
+
return {
|
|
877
|
+
items: state.items,
|
|
878
|
+
searchQuery: state.searchQuery,
|
|
879
|
+
isLoading: state.isLoading,
|
|
880
|
+
setSearchQuery: (query) => context.setSearchQuery(type, query),
|
|
881
|
+
addItem: (item) => context.addItem(type, item)
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
const mediaConfigs = {
|
|
885
|
+
video: {
|
|
886
|
+
acceptFileTypes: ["video/*"],
|
|
887
|
+
createElement: (url, parentSize) => new VideoElement(url, parentSize),
|
|
888
|
+
updateElement: async (element, url) => {
|
|
889
|
+
if (element instanceof VideoElement) {
|
|
890
|
+
element.setSrc(url);
|
|
891
|
+
await element.updateVideoMeta();
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
},
|
|
895
|
+
audio: {
|
|
896
|
+
acceptFileTypes: ["audio/*"],
|
|
897
|
+
createElement: (url, _parentSize) => new AudioElement(url),
|
|
898
|
+
updateElement: async (element, url) => {
|
|
899
|
+
if (element instanceof AudioElement) {
|
|
900
|
+
element.setSrc(url);
|
|
901
|
+
await element.updateAudioMeta();
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
},
|
|
905
|
+
image: {
|
|
906
|
+
acceptFileTypes: ["image/*"],
|
|
907
|
+
createElement: (url, parentSize) => new ImageElement(url, parentSize),
|
|
908
|
+
updateElement: async (element, url) => {
|
|
909
|
+
if (element instanceof ImageElement) {
|
|
910
|
+
element.setSrc(url);
|
|
911
|
+
await element.updateImageMeta();
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
};
|
|
916
|
+
const useMediaPanel = (type, {
|
|
917
|
+
selectedElement,
|
|
918
|
+
addElement,
|
|
919
|
+
updateElement
|
|
920
|
+
}, videoResolution) => {
|
|
921
|
+
const { items, searchQuery, setSearchQuery, addItem, isLoading } = useMedia(type);
|
|
922
|
+
const mediaManager = getMediaManager();
|
|
923
|
+
const handleSelection = async (item, forceAdd) => {
|
|
924
|
+
const config2 = mediaConfigs[type];
|
|
925
|
+
if (forceAdd) {
|
|
926
|
+
const element = config2.createElement(item.url, videoResolution);
|
|
927
|
+
addElement(element);
|
|
928
|
+
} else {
|
|
929
|
+
if (selectedElement) {
|
|
930
|
+
await config2.updateElement(selectedElement, item.url);
|
|
931
|
+
updateElement(selectedElement);
|
|
932
|
+
} else {
|
|
933
|
+
const element = config2.createElement(item.url, videoResolution);
|
|
934
|
+
addElement(element);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
};
|
|
938
|
+
const handleFileUpload = async (fileData) => {
|
|
939
|
+
const arrayBuffer = await fileData.file.arrayBuffer();
|
|
940
|
+
const newItem = await mediaManager.addItem({
|
|
941
|
+
name: fileData.file.name,
|
|
942
|
+
url: fileData.blobUrl,
|
|
943
|
+
type,
|
|
944
|
+
arrayBuffer,
|
|
945
|
+
metadata: {
|
|
946
|
+
name: fileData.file.name,
|
|
947
|
+
size: fileData.file.size,
|
|
948
|
+
type: fileData.file.type
|
|
949
|
+
}
|
|
950
|
+
});
|
|
951
|
+
addItem(newItem);
|
|
952
|
+
};
|
|
953
|
+
const config = mediaConfigs[type];
|
|
954
|
+
return {
|
|
955
|
+
items,
|
|
956
|
+
searchQuery,
|
|
957
|
+
setSearchQuery,
|
|
958
|
+
handleSelection,
|
|
959
|
+
handleFileUpload,
|
|
960
|
+
isLoading,
|
|
961
|
+
acceptFileTypes: config.acceptFileTypes
|
|
962
|
+
};
|
|
963
|
+
};
|
|
964
|
+
const SearchInput = ({
|
|
965
|
+
searchQuery,
|
|
966
|
+
setSearchQuery
|
|
967
|
+
}) => {
|
|
968
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative mb-3", children: [
|
|
969
|
+
/* @__PURE__ */ jsx(
|
|
970
|
+
"input",
|
|
971
|
+
{
|
|
972
|
+
type: "text",
|
|
973
|
+
placeholder: "Search media...",
|
|
974
|
+
value: searchQuery,
|
|
975
|
+
onChange: (e) => setSearchQuery(e.target.value),
|
|
976
|
+
className: "w-full pl-8 pr-3 py-2 bg-neutral-700/80 border border-gray-600 rounded-lg text-gray-100 text-sm placeholder-gray-400 focus:border-purple-500 focus:outline-none focus:ring-2 focus:ring-purple-500/20 transition-all duration-200 backdrop-blur-sm shadow-sm"
|
|
977
|
+
}
|
|
978
|
+
),
|
|
979
|
+
/* @__PURE__ */ jsx(Search, { className: "absolute left-2.5 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400 hover:text-gray-300 z-10 pointer-events-none" })
|
|
980
|
+
] });
|
|
981
|
+
};
|
|
982
|
+
const inputStyles = {
|
|
983
|
+
// Base container styles for form groups
|
|
984
|
+
container: "mb-6",
|
|
985
|
+
// Label styles
|
|
986
|
+
label: {
|
|
987
|
+
base: "block text-sm font-semibold text-gray-300 mb-2",
|
|
988
|
+
small: "block text-xs text-gray-400 mb-1"
|
|
989
|
+
},
|
|
990
|
+
// Text input and select styles
|
|
991
|
+
input: {
|
|
992
|
+
base: "w-full bg-neutral-700/50 border border-gray-600 rounded-lg text-white text-sm px-3 py-2 focus:border-purple-500 focus:outline-none focus:ring-2 focus:ring-purple-500/20 transition-all duration-200 backdrop-blur-sm",
|
|
993
|
+
small: "bg-neutral-700/50 border border-gray-600 rounded-lg text-white text-xs px-2 py-1 focus:border-purple-500 focus:outline-none focus:ring-2 focus:ring-purple-500/20 transition-all duration-200 backdrop-blur-sm"
|
|
994
|
+
},
|
|
995
|
+
// Range input styles
|
|
996
|
+
range: {
|
|
997
|
+
base: "flex-1 h-2 bg-neutral-600 rounded-full appearance-none cursor-pointer slider-thumb",
|
|
998
|
+
gradient: "flex-1 h-2 bg-gradient-to-r from-purple-500 to-neutral-600 rounded-full appearance-none cursor-pointer slider-thumb",
|
|
999
|
+
container: "flex items-center gap-3",
|
|
1000
|
+
value: "text-white text-sm font-medium min-w-[50px]"
|
|
1001
|
+
},
|
|
1002
|
+
// Color input styles
|
|
1003
|
+
color: {
|
|
1004
|
+
container: "flex items-center gap-2",
|
|
1005
|
+
picker: "w-6 h-6 rounded border border-gray-600 cursor-pointer",
|
|
1006
|
+
preview: "flex-1 h-8 rounded-lg border border-gray-600"
|
|
1007
|
+
},
|
|
1008
|
+
// Toggle button styles
|
|
1009
|
+
toggle: {
|
|
1010
|
+
base: "w-10 h-10 rounded-lg border-2 transition-all duration-200 hover:bg-purple-400 hover:text-white",
|
|
1011
|
+
active: "bg-purple-600 border-purple-500 text-white",
|
|
1012
|
+
inactive: "bg-transparent border-gray-600 text-gray-400 hover:border-gray-500"
|
|
1013
|
+
},
|
|
1014
|
+
// Radio button styles
|
|
1015
|
+
radio: {
|
|
1016
|
+
base: "w-4 h-4 text-purple-600 bg-neutral-700 border-gray-600 focus:ring-purple-500 focus:ring-2",
|
|
1017
|
+
label: "text-sm text-gray-300",
|
|
1018
|
+
container: "flex items-center gap-2"
|
|
1019
|
+
},
|
|
1020
|
+
// Action button styles
|
|
1021
|
+
button: {
|
|
1022
|
+
primary: "w-full bg-purple-600 hover:bg-purple-700 text-white font-medium py-3 px-4 rounded-lg transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-purple-500/20"
|
|
1023
|
+
},
|
|
1024
|
+
// Panel container styles
|
|
1025
|
+
panel: {
|
|
1026
|
+
container: "w-72 h-full bg-neutral-800/80 border-l border-gray-600/50 p-4 overflow-y-auto overflow-x-hidden backdrop-blur-md shadow-lg",
|
|
1027
|
+
title: "text-xl font-bold text-white mb-6"
|
|
1028
|
+
}
|
|
1029
|
+
};
|
|
1030
|
+
const useAudioPreview = () => {
|
|
1031
|
+
const [playingAudio, setPlayingAudio] = useState(null);
|
|
1032
|
+
const audioRef = useRef(null);
|
|
1033
|
+
useEffect(() => {
|
|
1034
|
+
return () => {
|
|
1035
|
+
if (audioRef.current) {
|
|
1036
|
+
audioRef.current.pause();
|
|
1037
|
+
audioRef.current = null;
|
|
1038
|
+
}
|
|
1039
|
+
};
|
|
1040
|
+
}, []);
|
|
1041
|
+
const stopPlayback = useCallback(() => {
|
|
1042
|
+
if (audioRef.current) {
|
|
1043
|
+
audioRef.current.pause();
|
|
1044
|
+
audioRef.current = null;
|
|
1045
|
+
}
|
|
1046
|
+
setPlayingAudio(null);
|
|
1047
|
+
}, []);
|
|
1048
|
+
const togglePlayPause = useCallback((item) => {
|
|
1049
|
+
if (playingAudio === item.id) {
|
|
1050
|
+
stopPlayback();
|
|
1051
|
+
return;
|
|
1052
|
+
}
|
|
1053
|
+
stopPlayback();
|
|
1054
|
+
const audio = new Audio(item.url);
|
|
1055
|
+
audio.addEventListener("ended", stopPlayback);
|
|
1056
|
+
audio.play();
|
|
1057
|
+
audioRef.current = audio;
|
|
1058
|
+
setPlayingAudio(item.id);
|
|
1059
|
+
}, [playingAudio, stopPlayback]);
|
|
1060
|
+
return {
|
|
1061
|
+
playingAudio,
|
|
1062
|
+
audioElement: audioRef.current,
|
|
1063
|
+
togglePlayPause,
|
|
1064
|
+
stopPlayback
|
|
1065
|
+
};
|
|
1066
|
+
};
|
|
1067
|
+
const AudioPanel = ({
|
|
1068
|
+
items,
|
|
1069
|
+
searchQuery,
|
|
1070
|
+
onSearchChange,
|
|
1071
|
+
onItemSelect,
|
|
1072
|
+
onFileUpload,
|
|
1073
|
+
acceptFileTypes
|
|
1074
|
+
}) => {
|
|
1075
|
+
const { playingAudio, togglePlayPause } = useAudioPreview();
|
|
1076
|
+
return /* @__PURE__ */ jsxs("div", { className: inputStyles.panel.container, children: [
|
|
1077
|
+
/* @__PURE__ */ jsx("h3", { className: inputStyles.panel.title, children: "Audio Library" }),
|
|
1078
|
+
/* @__PURE__ */ jsx("div", { className: inputStyles.container, children: /* @__PURE__ */ jsx(
|
|
1079
|
+
SearchInput,
|
|
1080
|
+
{
|
|
1081
|
+
searchQuery,
|
|
1082
|
+
setSearchQuery: onSearchChange
|
|
1083
|
+
}
|
|
1084
|
+
) }),
|
|
1085
|
+
/* @__PURE__ */ jsx("div", { className: `${inputStyles.container} mb-8`, children: /* @__PURE__ */ jsx(
|
|
1086
|
+
FileInput,
|
|
1087
|
+
{
|
|
1088
|
+
id: "audio-upload",
|
|
1089
|
+
acceptFileTypes,
|
|
1090
|
+
onFileLoad: onFileUpload,
|
|
1091
|
+
buttonText: "Upload"
|
|
1092
|
+
}
|
|
1093
|
+
) }),
|
|
1094
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto", children: [
|
|
1095
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-2", children: (items || []).map((item) => {
|
|
1096
|
+
var _a;
|
|
1097
|
+
return /* @__PURE__ */ jsx(
|
|
1098
|
+
"div",
|
|
1099
|
+
{
|
|
1100
|
+
onDoubleClick: () => onItemSelect(item),
|
|
1101
|
+
className: "audio-item group relative cursor-pointer p-3 bg-neutral-700/50 rounded-lg hover:bg-neutral-700/80 transition-all duration-200 border border-transparent hover:border-purple-500/30",
|
|
1102
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
1103
|
+
/* @__PURE__ */ jsx(
|
|
1104
|
+
"button",
|
|
1105
|
+
{
|
|
1106
|
+
onClick: (e) => {
|
|
1107
|
+
e.stopPropagation();
|
|
1108
|
+
togglePlayPause(item);
|
|
1109
|
+
},
|
|
1110
|
+
className: "w-8 h-8 rounded-full bg-purple-500/80 hover:bg-purple-500 flex items-center justify-center text-white transition-all duration-200 flex-shrink-0",
|
|
1111
|
+
children: playingAudio === item.id ? /* @__PURE__ */ jsx(Pause, { className: "w-4 h-4" }) : /* @__PURE__ */ jsx(Play, { className: "w-4 h-4" })
|
|
1112
|
+
}
|
|
1113
|
+
),
|
|
1114
|
+
/* @__PURE__ */ jsx("div", { className: `w-10 h-10 rounded-lg ${playingAudio === item.id ? "bg-purple-500/40" : "bg-purple-500/20"} flex items-center justify-center flex-shrink-0 transition-colors duration-200`, children: /* @__PURE__ */ jsx(Volume2, { className: `w-5 h-5 ${playingAudio === item.id ? "text-purple-300" : "text-purple-400"}` }) }),
|
|
1115
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsx("h4", { className: "text-sm font-medium text-gray-100 truncate", children: (_a = item.metadata) == null ? void 0 : _a.title }) }),
|
|
1116
|
+
/* @__PURE__ */ jsx(
|
|
1117
|
+
"button",
|
|
1118
|
+
{
|
|
1119
|
+
onClick: (e) => {
|
|
1120
|
+
e.stopPropagation();
|
|
1121
|
+
onItemSelect(item, true);
|
|
1122
|
+
},
|
|
1123
|
+
className: "w-6 h-6 rounded-full bg-purple-500/60 hover:bg-purple-500 flex items-center justify-center text-white text-xs opacity-0 group-hover:opacity-100 transition-opacity duration-200 flex-shrink-0",
|
|
1124
|
+
children: /* @__PURE__ */ jsx(Plus, { className: "w-3 h-3" })
|
|
1125
|
+
}
|
|
1126
|
+
)
|
|
1127
|
+
] })
|
|
1128
|
+
},
|
|
1129
|
+
item.id
|
|
1130
|
+
);
|
|
1131
|
+
}) }),
|
|
1132
|
+
items.length === 0 && /* @__PURE__ */ jsx("div", { className: `${inputStyles.container} flex items-center justify-center h-24`, children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
1133
|
+
/* @__PURE__ */ jsx(WandSparkles, { className: "w-10 h-10 mx-auto mb-2 text-purple-500/50" }),
|
|
1134
|
+
/* @__PURE__ */ jsx("p", { className: inputStyles.label.base, children: "No audio files found" }),
|
|
1135
|
+
searchQuery && /* @__PURE__ */ jsx("p", { className: inputStyles.label.small, children: "Try adjusting your search" })
|
|
1136
|
+
] }) })
|
|
1137
|
+
] })
|
|
1138
|
+
] });
|
|
1139
|
+
};
|
|
1140
|
+
const AudioPanelContainer = (props) => {
|
|
1141
|
+
const {
|
|
1142
|
+
items,
|
|
1143
|
+
searchQuery,
|
|
1144
|
+
setSearchQuery,
|
|
1145
|
+
handleSelection,
|
|
1146
|
+
handleFileUpload,
|
|
1147
|
+
isLoading,
|
|
1148
|
+
acceptFileTypes
|
|
1149
|
+
} = useMediaPanel(
|
|
1150
|
+
"audio",
|
|
1151
|
+
{
|
|
1152
|
+
selectedElement: props.selectedElement ?? null,
|
|
1153
|
+
addElement: props.addElement,
|
|
1154
|
+
updateElement: props.updateElement
|
|
1155
|
+
},
|
|
1156
|
+
props.videoResolution
|
|
1157
|
+
);
|
|
1158
|
+
return /* @__PURE__ */ jsx(
|
|
1159
|
+
AudioPanel,
|
|
1160
|
+
{
|
|
1161
|
+
items,
|
|
1162
|
+
searchQuery,
|
|
1163
|
+
onSearchChange: setSearchQuery,
|
|
1164
|
+
onItemSelect: handleSelection,
|
|
1165
|
+
onFileUpload: handleFileUpload,
|
|
1166
|
+
isLoading,
|
|
1167
|
+
acceptFileTypes
|
|
1168
|
+
}
|
|
1169
|
+
);
|
|
1170
|
+
};
|
|
1171
|
+
function ImagePanel({
|
|
1172
|
+
items,
|
|
1173
|
+
searchQuery,
|
|
1174
|
+
onSearchChange,
|
|
1175
|
+
onItemSelect,
|
|
1176
|
+
onFileUpload,
|
|
1177
|
+
acceptFileTypes
|
|
1178
|
+
}) {
|
|
1179
|
+
return /* @__PURE__ */ jsxs("div", { className: inputStyles.panel.container, children: [
|
|
1180
|
+
/* @__PURE__ */ jsx("h3", { className: inputStyles.panel.title, children: "Image Library" }),
|
|
1181
|
+
/* @__PURE__ */ jsx("div", { className: inputStyles.container, children: /* @__PURE__ */ jsx(
|
|
1182
|
+
SearchInput,
|
|
1183
|
+
{
|
|
1184
|
+
searchQuery,
|
|
1185
|
+
setSearchQuery: onSearchChange
|
|
1186
|
+
}
|
|
1187
|
+
) }),
|
|
1188
|
+
/* @__PURE__ */ jsx("div", { className: `${inputStyles.container} mb-8`, children: /* @__PURE__ */ jsx(
|
|
1189
|
+
FileInput,
|
|
1190
|
+
{
|
|
1191
|
+
id: "image-upload",
|
|
1192
|
+
acceptFileTypes,
|
|
1193
|
+
onFileLoad: onFileUpload,
|
|
1194
|
+
buttonText: "Upload"
|
|
1195
|
+
}
|
|
1196
|
+
) }),
|
|
1197
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto", children: [
|
|
1198
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-3 auto-rows-fr", children: (items || []).map((item) => /* @__PURE__ */ jsxs(
|
|
1199
|
+
"div",
|
|
1200
|
+
{
|
|
1201
|
+
onDoubleClick: () => onItemSelect(item),
|
|
1202
|
+
className: "media-item-compact group relative cursor-pointer overflow-hidden hover:shadow-lg hover:shadow-purple-500/20 transition-all duration-200",
|
|
1203
|
+
children: [
|
|
1204
|
+
/* @__PURE__ */ jsx(
|
|
1205
|
+
"img",
|
|
1206
|
+
{
|
|
1207
|
+
src: item.url,
|
|
1208
|
+
alt: "",
|
|
1209
|
+
className: "h-full w-full object-cover transition-transform group-hover:scale-105"
|
|
1210
|
+
}
|
|
1211
|
+
),
|
|
1212
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black bg-opacity-0 transition-opacity group-hover:bg-opacity-20" }),
|
|
1213
|
+
/* @__PURE__ */ jsx("div", { className: "absolute top-1 right-1 opacity-0 group-hover:opacity-100 transition-opacity duration-200", children: /* @__PURE__ */ jsx(
|
|
1214
|
+
"button",
|
|
1215
|
+
{
|
|
1216
|
+
onClick: (e) => {
|
|
1217
|
+
e.stopPropagation();
|
|
1218
|
+
onItemSelect(item, true);
|
|
1219
|
+
},
|
|
1220
|
+
className: "w-5 h-5 rounded-full bg-purple-500/80 hover:bg-purple-500 flex items-center justify-center text-white text-xs",
|
|
1221
|
+
children: /* @__PURE__ */ jsx(Plus, { className: "w-3 h-3" })
|
|
1222
|
+
}
|
|
1223
|
+
) })
|
|
1224
|
+
]
|
|
1225
|
+
},
|
|
1226
|
+
item.id
|
|
1227
|
+
)) }),
|
|
1228
|
+
items.length === 0 && /* @__PURE__ */ jsx("div", { className: `${inputStyles.container} flex items-center justify-center h-24`, children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
1229
|
+
/* @__PURE__ */ jsx(WandSparkles, { className: "w-10 h-10 mx-auto mb-2 text-purple-500/50" }),
|
|
1230
|
+
/* @__PURE__ */ jsx("p", { className: inputStyles.label.base, children: "No images found" }),
|
|
1231
|
+
searchQuery && /* @__PURE__ */ jsx("p", { className: inputStyles.label.small, children: "Try adjusting your search" })
|
|
1232
|
+
] }) })
|
|
1233
|
+
] })
|
|
1234
|
+
] });
|
|
1235
|
+
}
|
|
1236
|
+
function ImagePanelContainer(props) {
|
|
1237
|
+
const {
|
|
1238
|
+
items,
|
|
1239
|
+
searchQuery,
|
|
1240
|
+
setSearchQuery,
|
|
1241
|
+
handleSelection,
|
|
1242
|
+
handleFileUpload,
|
|
1243
|
+
isLoading,
|
|
1244
|
+
acceptFileTypes
|
|
1245
|
+
} = useMediaPanel(
|
|
1246
|
+
"image",
|
|
1247
|
+
{
|
|
1248
|
+
selectedElement: props.selectedElement ?? null,
|
|
1249
|
+
addElement: props.addElement,
|
|
1250
|
+
updateElement: props.updateElement
|
|
1251
|
+
},
|
|
1252
|
+
props.videoResolution
|
|
1253
|
+
);
|
|
1254
|
+
return /* @__PURE__ */ jsx(
|
|
1255
|
+
ImagePanel,
|
|
1256
|
+
{
|
|
1257
|
+
items,
|
|
1258
|
+
searchQuery,
|
|
1259
|
+
onSearchChange: setSearchQuery,
|
|
1260
|
+
onItemSelect: handleSelection,
|
|
1261
|
+
onFileUpload: handleFileUpload,
|
|
1262
|
+
isLoading,
|
|
1263
|
+
acceptFileTypes
|
|
1264
|
+
}
|
|
1265
|
+
);
|
|
1266
|
+
}
|
|
1267
|
+
const useVideoPreview = () => {
|
|
1268
|
+
const [playingVideo, setPlayingVideo] = useState(null);
|
|
1269
|
+
const videoRef = useRef(null);
|
|
1270
|
+
useEffect(() => {
|
|
1271
|
+
return () => {
|
|
1272
|
+
if (videoRef.current) {
|
|
1273
|
+
videoRef.current.pause();
|
|
1274
|
+
videoRef.current = null;
|
|
1275
|
+
}
|
|
1276
|
+
};
|
|
1277
|
+
}, []);
|
|
1278
|
+
const stopPlayback = useCallback(() => {
|
|
1279
|
+
if (videoRef.current) {
|
|
1280
|
+
videoRef.current.pause();
|
|
1281
|
+
videoRef.current = null;
|
|
1282
|
+
}
|
|
1283
|
+
setPlayingVideo(null);
|
|
1284
|
+
}, []);
|
|
1285
|
+
const togglePlayPause = useCallback((item, videoElement) => {
|
|
1286
|
+
if (playingVideo === item.id) {
|
|
1287
|
+
videoElement.pause();
|
|
1288
|
+
stopPlayback();
|
|
1289
|
+
return;
|
|
1290
|
+
}
|
|
1291
|
+
stopPlayback();
|
|
1292
|
+
videoElement.currentTime = 0;
|
|
1293
|
+
videoElement.play();
|
|
1294
|
+
videoRef.current = videoElement;
|
|
1295
|
+
setPlayingVideo(item.id);
|
|
1296
|
+
videoElement.addEventListener("ended", stopPlayback, { once: true });
|
|
1297
|
+
}, [playingVideo, stopPlayback]);
|
|
1298
|
+
return {
|
|
1299
|
+
playingVideo,
|
|
1300
|
+
videoElement: videoRef.current,
|
|
1301
|
+
togglePlayPause,
|
|
1302
|
+
stopPlayback
|
|
1303
|
+
};
|
|
1304
|
+
};
|
|
1305
|
+
function VideoPanel({
|
|
1306
|
+
items,
|
|
1307
|
+
searchQuery,
|
|
1308
|
+
onSearchChange,
|
|
1309
|
+
onItemSelect,
|
|
1310
|
+
onFileUpload,
|
|
1311
|
+
acceptFileTypes
|
|
1312
|
+
}) {
|
|
1313
|
+
const { playingVideo, togglePlayPause } = useVideoPreview();
|
|
1314
|
+
return /* @__PURE__ */ jsxs("div", { className: inputStyles.panel.container, children: [
|
|
1315
|
+
/* @__PURE__ */ jsx("h3", { className: inputStyles.panel.title, children: "Video Library" }),
|
|
1316
|
+
/* @__PURE__ */ jsx("div", { className: inputStyles.container, children: /* @__PURE__ */ jsx(
|
|
1317
|
+
SearchInput,
|
|
1318
|
+
{
|
|
1319
|
+
searchQuery,
|
|
1320
|
+
setSearchQuery: onSearchChange
|
|
1321
|
+
}
|
|
1322
|
+
) }),
|
|
1323
|
+
/* @__PURE__ */ jsx("div", { className: `${inputStyles.container} mb-8`, children: /* @__PURE__ */ jsx(
|
|
1324
|
+
FileInput,
|
|
1325
|
+
{
|
|
1326
|
+
id: "video-upload",
|
|
1327
|
+
acceptFileTypes,
|
|
1328
|
+
onFileLoad: onFileUpload,
|
|
1329
|
+
buttonText: "Upload"
|
|
1330
|
+
}
|
|
1331
|
+
) }),
|
|
1332
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto", children: [
|
|
1333
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-3 auto-rows-fr", children: (items || []).map((item) => /* @__PURE__ */ jsxs(
|
|
1334
|
+
"div",
|
|
1335
|
+
{
|
|
1336
|
+
onDoubleClick: () => onItemSelect(item),
|
|
1337
|
+
className: "media-item-compact group relative cursor-pointer overflow-hidden hover:shadow-lg hover:shadow-purple-500/20 transition-all duration-200",
|
|
1338
|
+
children: [
|
|
1339
|
+
/* @__PURE__ */ jsx(
|
|
1340
|
+
"video",
|
|
1341
|
+
{
|
|
1342
|
+
src: item.url,
|
|
1343
|
+
poster: item.thumbnail,
|
|
1344
|
+
className: `h-full w-full object-cover transition-transform ${playingVideo === item.id ? "scale-105" : "group-hover:scale-105"}`,
|
|
1345
|
+
ref: (el) => {
|
|
1346
|
+
if (el) {
|
|
1347
|
+
el.addEventListener("ended", () => {
|
|
1348
|
+
el.currentTime = 0;
|
|
1349
|
+
}, { once: true });
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
),
|
|
1354
|
+
/* @__PURE__ */ jsx("div", { className: `absolute inset-0 bg-black transition-opacity ${playingVideo === item.id ? "bg-opacity-30" : "bg-opacity-0 group-hover:bg-opacity-20"}` }),
|
|
1355
|
+
/* @__PURE__ */ jsxs("div", { className: "absolute top-1 right-1 flex gap-2", children: [
|
|
1356
|
+
/* @__PURE__ */ jsx(
|
|
1357
|
+
"button",
|
|
1358
|
+
{
|
|
1359
|
+
onClick: (e) => {
|
|
1360
|
+
var _a, _b;
|
|
1361
|
+
e.stopPropagation();
|
|
1362
|
+
const videoEl = (_b = (_a = e.currentTarget.parentElement) == null ? void 0 : _a.parentElement) == null ? void 0 : _b.querySelector("video");
|
|
1363
|
+
if (videoEl) {
|
|
1364
|
+
togglePlayPause(item, videoEl);
|
|
1365
|
+
}
|
|
1366
|
+
},
|
|
1367
|
+
className: `w-6 h-6 rounded-full bg-purple-500/80 hover:bg-purple-500 flex items-center justify-center text-white text-xs ${playingVideo === item.id ? "opacity-100" : "opacity-0 group-hover:opacity-100"} transition-opacity duration-200`,
|
|
1368
|
+
children: playingVideo === item.id ? /* @__PURE__ */ jsx(Pause, { className: "w-3 h-3" }) : /* @__PURE__ */ jsx(Play, { className: "w-3 h-3" })
|
|
1369
|
+
}
|
|
1370
|
+
),
|
|
1371
|
+
/* @__PURE__ */ jsx(
|
|
1372
|
+
"button",
|
|
1373
|
+
{
|
|
1374
|
+
onClick: (e) => {
|
|
1375
|
+
e.stopPropagation();
|
|
1376
|
+
onItemSelect(item, true);
|
|
1377
|
+
},
|
|
1378
|
+
className: "w-6 h-6 rounded-full bg-purple-500/80 hover:bg-purple-500 flex items-center justify-center text-white text-xs opacity-0 group-hover:opacity-100 transition-opacity duration-200",
|
|
1379
|
+
children: /* @__PURE__ */ jsx(Plus, { className: "w-3 h-3" })
|
|
1380
|
+
}
|
|
1381
|
+
)
|
|
1382
|
+
] })
|
|
1383
|
+
]
|
|
1384
|
+
},
|
|
1385
|
+
item.id
|
|
1386
|
+
)) }),
|
|
1387
|
+
items.length === 0 && /* @__PURE__ */ jsx("div", { className: `${inputStyles.container} flex items-center justify-center h-24`, children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
1388
|
+
/* @__PURE__ */ jsx(WandSparkles, { className: "w-10 h-10 mx-auto mb-2 text-purple-500/50" }),
|
|
1389
|
+
/* @__PURE__ */ jsx("p", { className: inputStyles.label.base, children: "No videos found" }),
|
|
1390
|
+
searchQuery && /* @__PURE__ */ jsx("p", { className: inputStyles.label.small, children: "Try adjusting your search" })
|
|
1391
|
+
] }) })
|
|
1392
|
+
] })
|
|
1393
|
+
] });
|
|
1394
|
+
}
|
|
1395
|
+
function VideoPanelContainer(props) {
|
|
1396
|
+
const {
|
|
1397
|
+
items,
|
|
1398
|
+
searchQuery,
|
|
1399
|
+
setSearchQuery,
|
|
1400
|
+
handleSelection,
|
|
1401
|
+
handleFileUpload,
|
|
1402
|
+
isLoading,
|
|
1403
|
+
acceptFileTypes
|
|
1404
|
+
} = useMediaPanel(
|
|
1405
|
+
"video",
|
|
1406
|
+
{
|
|
1407
|
+
selectedElement: props.selectedElement ?? null,
|
|
1408
|
+
addElement: props.addElement,
|
|
1409
|
+
updateElement: props.updateElement
|
|
1410
|
+
},
|
|
1411
|
+
props.videoResolution
|
|
1412
|
+
);
|
|
1413
|
+
return /* @__PURE__ */ jsx(
|
|
1414
|
+
VideoPanel,
|
|
1415
|
+
{
|
|
1416
|
+
items,
|
|
1417
|
+
searchQuery,
|
|
1418
|
+
onSearchChange: setSearchQuery,
|
|
1419
|
+
onItemSelect: handleSelection,
|
|
1420
|
+
onFileUpload: handleFileUpload,
|
|
1421
|
+
isLoading,
|
|
1422
|
+
acceptFileTypes
|
|
1423
|
+
}
|
|
1424
|
+
);
|
|
1425
|
+
}
|
|
1426
|
+
function TextPanel({
|
|
1427
|
+
textContent,
|
|
1428
|
+
fontSize,
|
|
1429
|
+
selectedFont,
|
|
1430
|
+
isBold,
|
|
1431
|
+
isItalic,
|
|
1432
|
+
textColor,
|
|
1433
|
+
strokeColor,
|
|
1434
|
+
applyShadow,
|
|
1435
|
+
shadowColor,
|
|
1436
|
+
strokeWidth,
|
|
1437
|
+
fonts,
|
|
1438
|
+
operation,
|
|
1439
|
+
setTextContent,
|
|
1440
|
+
setFontSize,
|
|
1441
|
+
setSelectedFont,
|
|
1442
|
+
setIsBold,
|
|
1443
|
+
setIsItalic,
|
|
1444
|
+
setTextColor,
|
|
1445
|
+
setStrokeColor,
|
|
1446
|
+
setApplyShadow,
|
|
1447
|
+
setShadowColor,
|
|
1448
|
+
setStrokeWidth,
|
|
1449
|
+
handleApplyChanges
|
|
1450
|
+
}) {
|
|
1451
|
+
return /* @__PURE__ */ jsxs("div", { className: inputStyles.panel.container, children: [
|
|
1452
|
+
/* @__PURE__ */ jsx("h3", { className: inputStyles.panel.title, children: "Text Element" }),
|
|
1453
|
+
/* @__PURE__ */ jsxs("div", { className: inputStyles.container, children: [
|
|
1454
|
+
/* @__PURE__ */ jsx("label", { className: inputStyles.label.base, children: "Text Content" }),
|
|
1455
|
+
/* @__PURE__ */ jsx(
|
|
1456
|
+
"input",
|
|
1457
|
+
{
|
|
1458
|
+
type: "text",
|
|
1459
|
+
value: textContent,
|
|
1460
|
+
onChange: (e) => setTextContent(e.target.value),
|
|
1461
|
+
className: inputStyles.input.base
|
|
1462
|
+
}
|
|
1463
|
+
)
|
|
1464
|
+
] }),
|
|
1465
|
+
/* @__PURE__ */ jsxs("div", { className: inputStyles.container, children: [
|
|
1466
|
+
/* @__PURE__ */ jsx("label", { className: inputStyles.label.base, children: "Font Size" }),
|
|
1467
|
+
/* @__PURE__ */ jsxs("div", { className: inputStyles.range.container, children: [
|
|
1468
|
+
/* @__PURE__ */ jsx(
|
|
1469
|
+
"input",
|
|
1470
|
+
{
|
|
1471
|
+
type: "range",
|
|
1472
|
+
min: "8",
|
|
1473
|
+
max: "120",
|
|
1474
|
+
value: fontSize,
|
|
1475
|
+
onChange: (e) => setFontSize(Number(e.target.value)),
|
|
1476
|
+
className: inputStyles.range.gradient
|
|
1477
|
+
}
|
|
1478
|
+
),
|
|
1479
|
+
/* @__PURE__ */ jsxs("span", { className: inputStyles.range.value, children: [
|
|
1480
|
+
fontSize,
|
|
1481
|
+
"px"
|
|
1482
|
+
] })
|
|
1483
|
+
] })
|
|
1484
|
+
] }),
|
|
1485
|
+
/* @__PURE__ */ jsxs("div", { className: inputStyles.container, children: [
|
|
1486
|
+
/* @__PURE__ */ jsx("label", { className: inputStyles.label.base, children: "Font" }),
|
|
1487
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1488
|
+
/* @__PURE__ */ jsx(
|
|
1489
|
+
"select",
|
|
1490
|
+
{
|
|
1491
|
+
value: selectedFont,
|
|
1492
|
+
onChange: (e) => setSelectedFont(e.target.value),
|
|
1493
|
+
className: inputStyles.input.base,
|
|
1494
|
+
children: fonts.map((font) => /* @__PURE__ */ jsx("option", { value: font, children: font }, font))
|
|
1495
|
+
}
|
|
1496
|
+
),
|
|
1497
|
+
/* @__PURE__ */ jsx(
|
|
1498
|
+
"button",
|
|
1499
|
+
{
|
|
1500
|
+
onClick: () => setIsBold(!isBold),
|
|
1501
|
+
className: `${inputStyles.toggle.base} ${isBold ? inputStyles.toggle.active : inputStyles.toggle.inactive} min-w-6`,
|
|
1502
|
+
children: /* @__PURE__ */ jsx("span", { className: "font-bold", children: "B" })
|
|
1503
|
+
}
|
|
1504
|
+
),
|
|
1505
|
+
/* @__PURE__ */ jsx(
|
|
1506
|
+
"button",
|
|
1507
|
+
{
|
|
1508
|
+
onClick: () => setIsItalic(!isItalic),
|
|
1509
|
+
className: `${inputStyles.toggle.base} ${isItalic ? inputStyles.toggle.active : inputStyles.toggle.inactive} min-w-6`,
|
|
1510
|
+
children: /* @__PURE__ */ jsx("span", { className: "italic", children: "I" })
|
|
1511
|
+
}
|
|
1512
|
+
)
|
|
1513
|
+
] })
|
|
1514
|
+
] }),
|
|
1515
|
+
/* @__PURE__ */ jsxs("div", { className: inputStyles.container, children: [
|
|
1516
|
+
/* @__PURE__ */ jsx("label", { className: inputStyles.label.base, children: "Colors" }),
|
|
1517
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
1518
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1519
|
+
/* @__PURE__ */ jsx("label", { className: inputStyles.label.small, children: "Text Color" }),
|
|
1520
|
+
/* @__PURE__ */ jsxs("div", { className: inputStyles.color.container, children: [
|
|
1521
|
+
/* @__PURE__ */ jsx(
|
|
1522
|
+
"input",
|
|
1523
|
+
{
|
|
1524
|
+
type: "color",
|
|
1525
|
+
value: textColor,
|
|
1526
|
+
onChange: (e) => setTextColor(e.target.value),
|
|
1527
|
+
className: inputStyles.color.picker
|
|
1528
|
+
}
|
|
1529
|
+
),
|
|
1530
|
+
/* @__PURE__ */ jsx(
|
|
1531
|
+
"input",
|
|
1532
|
+
{
|
|
1533
|
+
type: "text",
|
|
1534
|
+
value: textColor,
|
|
1535
|
+
onChange: (e) => setTextColor(e.target.value),
|
|
1536
|
+
className: inputStyles.input.small
|
|
1537
|
+
}
|
|
1538
|
+
)
|
|
1539
|
+
] })
|
|
1540
|
+
] }),
|
|
1541
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1542
|
+
/* @__PURE__ */ jsx("label", { className: inputStyles.label.small, children: "Stroke Color" }),
|
|
1543
|
+
/* @__PURE__ */ jsxs("div", { className: inputStyles.color.container, children: [
|
|
1544
|
+
/* @__PURE__ */ jsx(
|
|
1545
|
+
"input",
|
|
1546
|
+
{
|
|
1547
|
+
type: "color",
|
|
1548
|
+
value: strokeColor,
|
|
1549
|
+
onChange: (e) => setStrokeColor(e.target.value),
|
|
1550
|
+
className: inputStyles.color.picker
|
|
1551
|
+
}
|
|
1552
|
+
),
|
|
1553
|
+
/* @__PURE__ */ jsx(
|
|
1554
|
+
"input",
|
|
1555
|
+
{
|
|
1556
|
+
type: "text",
|
|
1557
|
+
value: strokeColor,
|
|
1558
|
+
onChange: (e) => setStrokeColor(e.target.value),
|
|
1559
|
+
className: inputStyles.input.small
|
|
1560
|
+
}
|
|
1561
|
+
)
|
|
1562
|
+
] })
|
|
1563
|
+
] }),
|
|
1564
|
+
/* @__PURE__ */ jsxs("div", { className: inputStyles.radio.container, children: [
|
|
1565
|
+
/* @__PURE__ */ jsx(
|
|
1566
|
+
"input",
|
|
1567
|
+
{
|
|
1568
|
+
type: "checkbox",
|
|
1569
|
+
id: "applyShadow",
|
|
1570
|
+
checked: applyShadow,
|
|
1571
|
+
onChange: (e) => setApplyShadow(e.target.checked),
|
|
1572
|
+
className: inputStyles.radio.base
|
|
1573
|
+
}
|
|
1574
|
+
),
|
|
1575
|
+
/* @__PURE__ */ jsx("label", { htmlFor: "applyShadow", className: inputStyles.radio.label, children: "Apply Shadow" })
|
|
1576
|
+
] }),
|
|
1577
|
+
applyShadow && /* @__PURE__ */ jsxs("div", { children: [
|
|
1578
|
+
/* @__PURE__ */ jsx("label", { className: inputStyles.label.small, children: "Shadow Color" }),
|
|
1579
|
+
/* @__PURE__ */ jsxs("div", { className: inputStyles.color.container, children: [
|
|
1580
|
+
/* @__PURE__ */ jsx(
|
|
1581
|
+
"input",
|
|
1582
|
+
{
|
|
1583
|
+
type: "color",
|
|
1584
|
+
value: shadowColor,
|
|
1585
|
+
onChange: (e) => setShadowColor(e.target.value),
|
|
1586
|
+
className: inputStyles.color.picker
|
|
1587
|
+
}
|
|
1588
|
+
),
|
|
1589
|
+
/* @__PURE__ */ jsx(
|
|
1590
|
+
"input",
|
|
1591
|
+
{
|
|
1592
|
+
type: "text",
|
|
1593
|
+
value: shadowColor,
|
|
1594
|
+
onChange: (e) => setShadowColor(e.target.value),
|
|
1595
|
+
className: inputStyles.input.small
|
|
1596
|
+
}
|
|
1597
|
+
)
|
|
1598
|
+
] })
|
|
1599
|
+
] })
|
|
1600
|
+
] })
|
|
1601
|
+
] }),
|
|
1602
|
+
/* @__PURE__ */ jsxs("div", { className: inputStyles.container, children: [
|
|
1603
|
+
/* @__PURE__ */ jsx("label", { className: inputStyles.label.base, children: "Stroke Width" }),
|
|
1604
|
+
/* @__PURE__ */ jsxs("div", { className: inputStyles.range.container, children: [
|
|
1605
|
+
/* @__PURE__ */ jsx(
|
|
1606
|
+
"input",
|
|
1607
|
+
{
|
|
1608
|
+
type: "range",
|
|
1609
|
+
min: "0",
|
|
1610
|
+
max: "2",
|
|
1611
|
+
step: 0.1,
|
|
1612
|
+
value: strokeWidth,
|
|
1613
|
+
onChange: (e) => setStrokeWidth(Number(e.target.value)),
|
|
1614
|
+
className: inputStyles.range.base
|
|
1615
|
+
}
|
|
1616
|
+
),
|
|
1617
|
+
/* @__PURE__ */ jsx("span", { className: inputStyles.range.value, children: strokeWidth })
|
|
1618
|
+
] })
|
|
1619
|
+
] }),
|
|
1620
|
+
/* @__PURE__ */ jsx("div", { className: "mt-8", children: /* @__PURE__ */ jsx("button", { onClick: handleApplyChanges, className: inputStyles.button.primary, children: operation }) })
|
|
1621
|
+
] });
|
|
1622
|
+
}
|
|
1623
|
+
const DEFAULT_TEXT_PROPS = {
|
|
1624
|
+
text: "Sample",
|
|
1625
|
+
fontSize: 48,
|
|
1626
|
+
fontFamily: "Poppins",
|
|
1627
|
+
fontWeight: 400,
|
|
1628
|
+
fontStyle: "normal",
|
|
1629
|
+
textColor: "#ffffff",
|
|
1630
|
+
strokeColor: "#4d4d4d",
|
|
1631
|
+
strokeWidth: 0,
|
|
1632
|
+
applyShadow: false,
|
|
1633
|
+
shadowColor: "#000000",
|
|
1634
|
+
textAlign: "center",
|
|
1635
|
+
shadowOffset: [0, 0],
|
|
1636
|
+
shadowBlur: 2,
|
|
1637
|
+
shadowOpacity: 1
|
|
1638
|
+
};
|
|
1639
|
+
const useTextPanel = ({
|
|
1640
|
+
selectedElement,
|
|
1641
|
+
addElement,
|
|
1642
|
+
updateElement
|
|
1643
|
+
}) => {
|
|
1644
|
+
const [textContent, setTextContent] = useState(DEFAULT_TEXT_PROPS.text);
|
|
1645
|
+
const [fontSize, setFontSize] = useState(DEFAULT_TEXT_PROPS.fontSize);
|
|
1646
|
+
const [selectedFont, setSelectedFont] = useState(DEFAULT_TEXT_PROPS.fontFamily);
|
|
1647
|
+
const [isBold, setIsBold] = useState(DEFAULT_TEXT_PROPS.fontWeight === 700);
|
|
1648
|
+
const [isItalic, setIsItalic] = useState(DEFAULT_TEXT_PROPS.fontStyle === "italic");
|
|
1649
|
+
const [textColor, setTextColor] = useState(DEFAULT_TEXT_PROPS.textColor);
|
|
1650
|
+
const [strokeColor, setStrokeColor] = useState(DEFAULT_TEXT_PROPS.strokeColor);
|
|
1651
|
+
const [applyShadow, setApplyShadow] = useState(DEFAULT_TEXT_PROPS.applyShadow);
|
|
1652
|
+
const [shadowColor, setShadowColor] = useState(DEFAULT_TEXT_PROPS.shadowColor);
|
|
1653
|
+
const [strokeWidth, setStrokeWidth] = useState(DEFAULT_TEXT_PROPS.strokeWidth);
|
|
1654
|
+
const fonts = Object.values(AVAILABLE_TEXT_FONTS);
|
|
1655
|
+
const handleApplyChanges = async () => {
|
|
1656
|
+
let textElement;
|
|
1657
|
+
if (selectedElement instanceof TextElement) {
|
|
1658
|
+
textElement = selectedElement;
|
|
1659
|
+
textElement.setText(textContent);
|
|
1660
|
+
textElement.setFontSize(fontSize);
|
|
1661
|
+
textElement.setFontFamily(selectedFont);
|
|
1662
|
+
textElement.setFontWeight(isBold ? 700 : 400);
|
|
1663
|
+
textElement.setFontStyle(isItalic ? "italic" : "normal");
|
|
1664
|
+
textElement.setFill(textColor);
|
|
1665
|
+
textElement.setStrokeColor(strokeColor);
|
|
1666
|
+
textElement.setLineWidth(strokeWidth);
|
|
1667
|
+
textElement.setTextAlign(DEFAULT_TEXT_PROPS.textAlign);
|
|
1668
|
+
if (applyShadow) {
|
|
1669
|
+
textElement.setProps({
|
|
1670
|
+
...textElement.getProps(),
|
|
1671
|
+
shadowColor,
|
|
1672
|
+
shadowOffset: DEFAULT_TEXT_PROPS.shadowOffset,
|
|
1673
|
+
shadowBlur: DEFAULT_TEXT_PROPS.shadowBlur,
|
|
1674
|
+
shadowOpacity: DEFAULT_TEXT_PROPS.shadowOpacity
|
|
1675
|
+
});
|
|
1676
|
+
} else {
|
|
1677
|
+
textElement.setProps({
|
|
1678
|
+
...textElement.getProps(),
|
|
1679
|
+
shadowColor: void 0,
|
|
1680
|
+
shadowOffset: void 0,
|
|
1681
|
+
shadowBlur: void 0,
|
|
1682
|
+
shadowOpacity: void 0
|
|
1683
|
+
});
|
|
1684
|
+
}
|
|
1685
|
+
updateElement(textElement);
|
|
1686
|
+
} else {
|
|
1687
|
+
textElement = new TextElement(textContent).setFontSize(fontSize).setFontFamily(selectedFont).setFontWeight(isBold ? 700 : 400).setFontStyle(isItalic ? "italic" : "normal").setFill(textColor).setStrokeColor(strokeColor).setLineWidth(strokeWidth).setTextAlign("center");
|
|
1688
|
+
if (applyShadow) {
|
|
1689
|
+
textElement.setProps({
|
|
1690
|
+
...textElement.getProps(),
|
|
1691
|
+
shadowColor,
|
|
1692
|
+
shadowOffset: DEFAULT_TEXT_PROPS.shadowOffset,
|
|
1693
|
+
shadowBlur: DEFAULT_TEXT_PROPS.shadowBlur,
|
|
1694
|
+
shadowOpacity: DEFAULT_TEXT_PROPS.shadowOpacity
|
|
1695
|
+
});
|
|
1696
|
+
}
|
|
1697
|
+
await addElement(textElement);
|
|
1698
|
+
}
|
|
1699
|
+
};
|
|
1700
|
+
useEffect(() => {
|
|
1701
|
+
if (selectedElement instanceof TextElement) {
|
|
1702
|
+
setTextContent(selectedElement.getText());
|
|
1703
|
+
const textProps = selectedElement.getProps();
|
|
1704
|
+
setSelectedFont(textProps.fontFamily ?? DEFAULT_TEXT_PROPS.fontFamily);
|
|
1705
|
+
setFontSize(textProps.fontSize ?? DEFAULT_TEXT_PROPS.fontSize);
|
|
1706
|
+
setIsBold(textProps.fontWeight === 700);
|
|
1707
|
+
setIsItalic(textProps.fontStyle === "italic");
|
|
1708
|
+
setTextColor(textProps.fill ?? DEFAULT_TEXT_PROPS.textColor);
|
|
1709
|
+
setStrokeColor(textProps.stroke ?? DEFAULT_TEXT_PROPS.strokeColor);
|
|
1710
|
+
setStrokeWidth(textProps.lineWidth ?? DEFAULT_TEXT_PROPS.strokeWidth);
|
|
1711
|
+
const hasShadow = textProps.shadowColor !== void 0;
|
|
1712
|
+
setApplyShadow(hasShadow);
|
|
1713
|
+
if (hasShadow) {
|
|
1714
|
+
setShadowColor(textProps.shadowColor ?? DEFAULT_TEXT_PROPS.shadowColor);
|
|
1715
|
+
}
|
|
1716
|
+
} else {
|
|
1717
|
+
setTextContent(DEFAULT_TEXT_PROPS.text);
|
|
1718
|
+
setFontSize(DEFAULT_TEXT_PROPS.fontSize);
|
|
1719
|
+
setSelectedFont(DEFAULT_TEXT_PROPS.fontFamily);
|
|
1720
|
+
setIsBold(DEFAULT_TEXT_PROPS.fontWeight === 700);
|
|
1721
|
+
setIsItalic(DEFAULT_TEXT_PROPS.fontStyle === "italic");
|
|
1722
|
+
setTextColor(DEFAULT_TEXT_PROPS.textColor);
|
|
1723
|
+
setStrokeColor(DEFAULT_TEXT_PROPS.strokeColor);
|
|
1724
|
+
setStrokeWidth(DEFAULT_TEXT_PROPS.strokeWidth);
|
|
1725
|
+
setApplyShadow(DEFAULT_TEXT_PROPS.applyShadow);
|
|
1726
|
+
setShadowColor(DEFAULT_TEXT_PROPS.shadowColor);
|
|
1727
|
+
}
|
|
1728
|
+
}, [selectedElement]);
|
|
1729
|
+
return {
|
|
1730
|
+
textContent,
|
|
1731
|
+
fontSize,
|
|
1732
|
+
selectedFont,
|
|
1733
|
+
isBold,
|
|
1734
|
+
isItalic,
|
|
1735
|
+
textColor,
|
|
1736
|
+
strokeColor,
|
|
1737
|
+
applyShadow,
|
|
1738
|
+
shadowColor,
|
|
1739
|
+
strokeWidth,
|
|
1740
|
+
fonts,
|
|
1741
|
+
operation: selectedElement instanceof TextElement ? "Apply Changes" : "Add Text",
|
|
1742
|
+
setTextContent,
|
|
1743
|
+
setFontSize,
|
|
1744
|
+
setSelectedFont,
|
|
1745
|
+
setIsBold,
|
|
1746
|
+
setIsItalic,
|
|
1747
|
+
setTextColor,
|
|
1748
|
+
setStrokeColor,
|
|
1749
|
+
setApplyShadow,
|
|
1750
|
+
setShadowColor,
|
|
1751
|
+
setStrokeWidth,
|
|
1752
|
+
handleApplyChanges
|
|
1753
|
+
};
|
|
1754
|
+
};
|
|
1755
|
+
function TextPanelContainer(props) {
|
|
1756
|
+
const textPanelProps = useTextPanel(props);
|
|
1757
|
+
return /* @__PURE__ */ jsx(TextPanel, { ...textPanelProps });
|
|
1758
|
+
}
|
|
1759
|
+
function IconPanel({
|
|
1760
|
+
icons,
|
|
1761
|
+
loading,
|
|
1762
|
+
hasMore,
|
|
1763
|
+
totalIcons,
|
|
1764
|
+
searchQuery,
|
|
1765
|
+
handleSearch,
|
|
1766
|
+
handleSelection,
|
|
1767
|
+
handleDownloadIcon,
|
|
1768
|
+
handleLoadMore
|
|
1769
|
+
}) {
|
|
1770
|
+
return /* @__PURE__ */ jsxs("div", { className: inputStyles.panel.container, children: [
|
|
1771
|
+
/* @__PURE__ */ jsx("h3", { className: inputStyles.panel.title, children: "Icon Library" }),
|
|
1772
|
+
/* @__PURE__ */ jsx("div", { className: inputStyles.container, children: /* @__PURE__ */ jsxs("div", { className: "relative mb-3", children: [
|
|
1773
|
+
/* @__PURE__ */ jsx(
|
|
1774
|
+
Search,
|
|
1775
|
+
{
|
|
1776
|
+
className: "absolute left-2.5 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400 z-10 pointer-events-none"
|
|
1777
|
+
}
|
|
1778
|
+
),
|
|
1779
|
+
/* @__PURE__ */ jsx(
|
|
1780
|
+
"input",
|
|
1781
|
+
{
|
|
1782
|
+
type: "text",
|
|
1783
|
+
placeholder: "Search icons...",
|
|
1784
|
+
value: searchQuery,
|
|
1785
|
+
onChange: (e) => handleSearch(e.target.value),
|
|
1786
|
+
className: `${inputStyles.input.base} pl-8`
|
|
1787
|
+
}
|
|
1788
|
+
)
|
|
1789
|
+
] }) }),
|
|
1790
|
+
loading && icons.length === 0 ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ jsx(LoaderCircle, { className: "h-8 w-8 animate-spin text-purple-500" }) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1791
|
+
totalIcons > 0 && /* @__PURE__ */ jsxs("div", { className: "mb-4 text-sm text-gray-400", children: [
|
|
1792
|
+
"Showing ",
|
|
1793
|
+
icons.length,
|
|
1794
|
+
" of ",
|
|
1795
|
+
totalIcons,
|
|
1796
|
+
" icons"
|
|
1797
|
+
] }),
|
|
1798
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-3 mb-2 p-2", children: (icons || []).map((icon, index) => /* @__PURE__ */ jsxs("div", { className: "group relative cursor-pointer", children: [
|
|
1799
|
+
/* @__PURE__ */ jsx(
|
|
1800
|
+
"div",
|
|
1801
|
+
{
|
|
1802
|
+
onClick: () => handleSelection(icon),
|
|
1803
|
+
className: "w-16 h-16 flex items-center justify-center bg-neutral-700/50 border border-gray-600 rounded-lg hover:border-purple-500 hover:bg-neutral-700/70 transition-all duration-200 p-2",
|
|
1804
|
+
dangerouslySetInnerHTML: { __html: icon.svg }
|
|
1805
|
+
}
|
|
1806
|
+
),
|
|
1807
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/60 opacity-0 group-hover:opacity-100 transition-opacity duration-200 rounded-lg flex items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
1808
|
+
/* @__PURE__ */ jsx(
|
|
1809
|
+
"button",
|
|
1810
|
+
{
|
|
1811
|
+
onClick: (e) => {
|
|
1812
|
+
e.stopPropagation();
|
|
1813
|
+
handleSelection(icon);
|
|
1814
|
+
},
|
|
1815
|
+
className: "p-1.5 bg-purple-600 hover:bg-purple-700 rounded transition-colors duration-200",
|
|
1816
|
+
title: "Add to timeline",
|
|
1817
|
+
children: /* @__PURE__ */ jsx("span", { className: "text-white text-xs", children: "+" })
|
|
1818
|
+
}
|
|
1819
|
+
),
|
|
1820
|
+
/* @__PURE__ */ jsx(
|
|
1821
|
+
"button",
|
|
1822
|
+
{
|
|
1823
|
+
onClick: (e) => {
|
|
1824
|
+
e.stopPropagation();
|
|
1825
|
+
handleDownloadIcon(icon);
|
|
1826
|
+
},
|
|
1827
|
+
className: "p-1.5 bg-purple-600 hover:bg-purple-700 rounded transition-colors duration-200",
|
|
1828
|
+
title: "Download SVG",
|
|
1829
|
+
children: /* @__PURE__ */ jsx(Download, { className: "w-3 h-3 text-white" })
|
|
1830
|
+
}
|
|
1831
|
+
)
|
|
1832
|
+
] }) }),
|
|
1833
|
+
/* @__PURE__ */ jsx("div", { className: "absolute -bottom-8 left-1/2 transform -translate-x-1/2 bg-black/80 text-white text-xs px-2 py-1 rounded opacity-0 group-hover:opacity-100 transition-opacity duration-200 whitespace-nowrap z-10", children: icon.name })
|
|
1834
|
+
] }, index)) }),
|
|
1835
|
+
hasMore && /* @__PURE__ */ jsx(
|
|
1836
|
+
"button",
|
|
1837
|
+
{
|
|
1838
|
+
onClick: handleLoadMore,
|
|
1839
|
+
disabled: loading,
|
|
1840
|
+
className: `${inputStyles.button.primary} disabled:opacity-50 disabled:cursor-not-allowed`,
|
|
1841
|
+
children: loading ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-2", children: [
|
|
1842
|
+
/* @__PURE__ */ jsx(LoaderCircle, { className: "h-4 w-4 animate-spin" }),
|
|
1843
|
+
"Loading..."
|
|
1844
|
+
] }) : "Load More"
|
|
1845
|
+
}
|
|
1846
|
+
),
|
|
1847
|
+
!loading && icons.length === 0 && searchQuery && /* @__PURE__ */ jsxs("div", { className: "text-center py-8 text-gray-400", children: [
|
|
1848
|
+
/* @__PURE__ */ jsx("p", { children: "No icons found" }),
|
|
1849
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm mt-2", children: "Try a different search term" })
|
|
1850
|
+
] })
|
|
1851
|
+
] })
|
|
1852
|
+
] });
|
|
1853
|
+
}
|
|
1854
|
+
const ICONS_PER_PAGE = 20;
|
|
1855
|
+
const useIconPanel = ({
|
|
1856
|
+
selectedElement,
|
|
1857
|
+
addElement,
|
|
1858
|
+
updateElement
|
|
1859
|
+
}) => {
|
|
1860
|
+
const [icons, setIcons] = useState([]);
|
|
1861
|
+
const [loading, setLoading] = useState(false);
|
|
1862
|
+
const [page, setPage] = useState(1);
|
|
1863
|
+
const [hasMore, setHasMore] = useState(true);
|
|
1864
|
+
const [totalIcons, setTotalIcons] = useState(0);
|
|
1865
|
+
const [searchQuery, setSearchQuery] = useState("");
|
|
1866
|
+
const currentQuery = useRef("");
|
|
1867
|
+
const fetchIcons = async (query, reset = false) => {
|
|
1868
|
+
try {
|
|
1869
|
+
setLoading(true);
|
|
1870
|
+
const newPage = reset ? 1 : page;
|
|
1871
|
+
const start = (newPage - 1) * ICONS_PER_PAGE;
|
|
1872
|
+
const url = `https://api.iconify.design/search?query=${query}&limit=${ICONS_PER_PAGE}&offset=${start}`;
|
|
1873
|
+
const response = await fetch(url);
|
|
1874
|
+
const data = await response.json();
|
|
1875
|
+
const iconData = data.icons || [];
|
|
1876
|
+
const total = data.total || 0;
|
|
1877
|
+
setTotalIcons(total);
|
|
1878
|
+
const formattedIcons = await Promise.all(
|
|
1879
|
+
iconData.map(async (icon) => {
|
|
1880
|
+
const svgUrl = `https://api.iconify.design/${icon}.svg`;
|
|
1881
|
+
try {
|
|
1882
|
+
const svgResponse = await fetch(svgUrl);
|
|
1883
|
+
const svg = await svgResponse.text();
|
|
1884
|
+
return { name: icon, svg };
|
|
1885
|
+
} catch (e) {
|
|
1886
|
+
console.error(`Error fetching SVG for ${icon}:`, e);
|
|
1887
|
+
return null;
|
|
1888
|
+
}
|
|
1889
|
+
})
|
|
1890
|
+
);
|
|
1891
|
+
const validIcons = formattedIcons.filter((icon) => icon !== null);
|
|
1892
|
+
if (reset) {
|
|
1893
|
+
setIcons(validIcons);
|
|
1894
|
+
} else {
|
|
1895
|
+
setIcons([...icons, ...validIcons]);
|
|
1896
|
+
}
|
|
1897
|
+
setHasMore(start + validIcons.length < total);
|
|
1898
|
+
if (!reset) {
|
|
1899
|
+
setPage(newPage + 1);
|
|
1900
|
+
} else {
|
|
1901
|
+
setPage(2);
|
|
1902
|
+
}
|
|
1903
|
+
} catch (error) {
|
|
1904
|
+
console.error("Error fetching icons:", error);
|
|
1905
|
+
} finally {
|
|
1906
|
+
setLoading(false);
|
|
1907
|
+
}
|
|
1908
|
+
};
|
|
1909
|
+
useEffect(() => {
|
|
1910
|
+
fetchIcons("media", true);
|
|
1911
|
+
}, []);
|
|
1912
|
+
const handleSearch = (query) => {
|
|
1913
|
+
currentQuery.current = query;
|
|
1914
|
+
setSearchQuery(query);
|
|
1915
|
+
fetchIcons(query, true);
|
|
1916
|
+
};
|
|
1917
|
+
const handleSelection = (icon) => {
|
|
1918
|
+
const svgBlob = new Blob([icon.svg], { type: "image/svg+xml" });
|
|
1919
|
+
const url = URL.createObjectURL(svgBlob);
|
|
1920
|
+
let iconElement;
|
|
1921
|
+
if (selectedElement instanceof IconElement) {
|
|
1922
|
+
iconElement = selectedElement;
|
|
1923
|
+
iconElement.setSrc(url);
|
|
1924
|
+
iconElement.setName(icon.name);
|
|
1925
|
+
updateElement == null ? void 0 : updateElement(iconElement);
|
|
1926
|
+
} else {
|
|
1927
|
+
iconElement = new IconElement(url, {
|
|
1928
|
+
width: 100,
|
|
1929
|
+
height: 100
|
|
1930
|
+
});
|
|
1931
|
+
iconElement.setName(icon.name);
|
|
1932
|
+
addElement == null ? void 0 : addElement(iconElement);
|
|
1933
|
+
}
|
|
1934
|
+
URL.revokeObjectURL(url);
|
|
1935
|
+
};
|
|
1936
|
+
const handleDownloadIcon = (icon) => {
|
|
1937
|
+
const blob = new Blob([icon.svg], { type: "image/svg+xml" });
|
|
1938
|
+
const url = URL.createObjectURL(blob);
|
|
1939
|
+
const a = document.createElement("a");
|
|
1940
|
+
a.href = url;
|
|
1941
|
+
a.download = `${icon.name}.svg`;
|
|
1942
|
+
document.body.appendChild(a);
|
|
1943
|
+
a.click();
|
|
1944
|
+
document.body.removeChild(a);
|
|
1945
|
+
URL.revokeObjectURL(url);
|
|
1946
|
+
};
|
|
1947
|
+
const handleLoadMore = () => {
|
|
1948
|
+
fetchIcons(currentQuery.current, false);
|
|
1949
|
+
};
|
|
1950
|
+
return {
|
|
1951
|
+
icons,
|
|
1952
|
+
loading,
|
|
1953
|
+
hasMore,
|
|
1954
|
+
totalIcons,
|
|
1955
|
+
searchQuery,
|
|
1956
|
+
handleSearch,
|
|
1957
|
+
handleSelection,
|
|
1958
|
+
handleDownloadIcon,
|
|
1959
|
+
handleLoadMore
|
|
1960
|
+
};
|
|
1961
|
+
};
|
|
1962
|
+
function IconPanelContainer(props) {
|
|
1963
|
+
const iconPanelProps = useIconPanel({
|
|
1964
|
+
selectedElement: props.selectedElement ?? null,
|
|
1965
|
+
addElement: props.addElement,
|
|
1966
|
+
updateElement: props.updateElement
|
|
1967
|
+
});
|
|
1968
|
+
return /* @__PURE__ */ jsx(IconPanel, { ...iconPanelProps });
|
|
1969
|
+
}
|
|
1970
|
+
function RectPanel({
|
|
1971
|
+
cornerRadius,
|
|
1972
|
+
fillColor,
|
|
1973
|
+
opacity,
|
|
1974
|
+
strokeColor,
|
|
1975
|
+
lineWidth,
|
|
1976
|
+
setCornerRadius,
|
|
1977
|
+
setFillColor,
|
|
1978
|
+
setOpacity,
|
|
1979
|
+
setStrokeColor,
|
|
1980
|
+
setLineWidth,
|
|
1981
|
+
handleApplyChanges
|
|
1982
|
+
}) {
|
|
1983
|
+
return /* @__PURE__ */ jsxs("div", { className: inputStyles.panel.container, children: [
|
|
1984
|
+
/* @__PURE__ */ jsx("h3", { className: inputStyles.panel.title, children: "Rectangle" }),
|
|
1985
|
+
/* @__PURE__ */ jsxs("div", { className: inputStyles.container, children: [
|
|
1986
|
+
/* @__PURE__ */ jsx("label", { className: inputStyles.label.base, children: "Corner Radius" }),
|
|
1987
|
+
/* @__PURE__ */ jsxs("div", { className: inputStyles.range.container, children: [
|
|
1988
|
+
/* @__PURE__ */ jsx(
|
|
1989
|
+
"input",
|
|
1990
|
+
{
|
|
1991
|
+
type: "range",
|
|
1992
|
+
min: "0",
|
|
1993
|
+
max: "100",
|
|
1994
|
+
value: cornerRadius,
|
|
1995
|
+
onChange: (e) => setCornerRadius(Number(e.target.value)),
|
|
1996
|
+
className: inputStyles.range.base
|
|
1997
|
+
}
|
|
1998
|
+
),
|
|
1999
|
+
/* @__PURE__ */ jsxs("span", { className: inputStyles.range.value, children: [
|
|
2000
|
+
cornerRadius,
|
|
2001
|
+
"px"
|
|
2002
|
+
] })
|
|
2003
|
+
] })
|
|
2004
|
+
] }),
|
|
2005
|
+
/* @__PURE__ */ jsxs("div", { className: inputStyles.container, children: [
|
|
2006
|
+
/* @__PURE__ */ jsx("label", { className: inputStyles.label.base, children: "Fill Color" }),
|
|
2007
|
+
/* @__PURE__ */ jsxs("div", { className: inputStyles.color.container, children: [
|
|
2008
|
+
/* @__PURE__ */ jsx(
|
|
2009
|
+
"input",
|
|
2010
|
+
{
|
|
2011
|
+
type: "color",
|
|
2012
|
+
value: fillColor,
|
|
2013
|
+
onChange: (e) => setFillColor(e.target.value),
|
|
2014
|
+
className: inputStyles.color.picker
|
|
2015
|
+
}
|
|
2016
|
+
),
|
|
2017
|
+
/* @__PURE__ */ jsx(
|
|
2018
|
+
"div",
|
|
2019
|
+
{
|
|
2020
|
+
className: inputStyles.color.preview,
|
|
2021
|
+
style: { backgroundColor: fillColor }
|
|
2022
|
+
}
|
|
2023
|
+
)
|
|
2024
|
+
] })
|
|
2025
|
+
] }),
|
|
2026
|
+
/* @__PURE__ */ jsxs("div", { className: inputStyles.container, children: [
|
|
2027
|
+
/* @__PURE__ */ jsx("label", { className: inputStyles.label.base, children: "Opacity" }),
|
|
2028
|
+
/* @__PURE__ */ jsxs("div", { className: inputStyles.range.container, children: [
|
|
2029
|
+
/* @__PURE__ */ jsx(
|
|
2030
|
+
"input",
|
|
2031
|
+
{
|
|
2032
|
+
type: "range",
|
|
2033
|
+
min: "0",
|
|
2034
|
+
max: "100",
|
|
2035
|
+
value: opacity,
|
|
2036
|
+
onChange: (e) => setOpacity(Number(e.target.value)),
|
|
2037
|
+
className: inputStyles.range.gradient
|
|
2038
|
+
}
|
|
2039
|
+
),
|
|
2040
|
+
/* @__PURE__ */ jsxs("span", { className: inputStyles.range.value, children: [
|
|
2041
|
+
opacity,
|
|
2042
|
+
"%"
|
|
2043
|
+
] })
|
|
2044
|
+
] })
|
|
2045
|
+
] }),
|
|
2046
|
+
/* @__PURE__ */ jsxs("div", { className: inputStyles.container, children: [
|
|
2047
|
+
/* @__PURE__ */ jsx("label", { className: inputStyles.label.base, children: "Stroke Color" }),
|
|
2048
|
+
/* @__PURE__ */ jsxs("div", { className: inputStyles.color.container, children: [
|
|
2049
|
+
/* @__PURE__ */ jsx(
|
|
2050
|
+
"input",
|
|
2051
|
+
{
|
|
2052
|
+
type: "color",
|
|
2053
|
+
value: strokeColor,
|
|
2054
|
+
onChange: (e) => setStrokeColor(e.target.value),
|
|
2055
|
+
className: inputStyles.color.picker
|
|
2056
|
+
}
|
|
2057
|
+
),
|
|
2058
|
+
/* @__PURE__ */ jsx(
|
|
2059
|
+
"div",
|
|
2060
|
+
{
|
|
2061
|
+
className: inputStyles.color.preview,
|
|
2062
|
+
style: { backgroundColor: strokeColor }
|
|
2063
|
+
}
|
|
2064
|
+
)
|
|
2065
|
+
] })
|
|
2066
|
+
] }),
|
|
2067
|
+
/* @__PURE__ */ jsxs("div", { className: inputStyles.container, children: [
|
|
2068
|
+
/* @__PURE__ */ jsx("label", { className: inputStyles.label.base, children: "Line Width" }),
|
|
2069
|
+
/* @__PURE__ */ jsxs("div", { className: inputStyles.range.container, children: [
|
|
2070
|
+
/* @__PURE__ */ jsx(
|
|
2071
|
+
"input",
|
|
2072
|
+
{
|
|
2073
|
+
type: "range",
|
|
2074
|
+
min: "0",
|
|
2075
|
+
max: "20",
|
|
2076
|
+
value: lineWidth,
|
|
2077
|
+
onChange: (e) => setLineWidth(Number(e.target.value)),
|
|
2078
|
+
className: inputStyles.range.base
|
|
2079
|
+
}
|
|
2080
|
+
),
|
|
2081
|
+
/* @__PURE__ */ jsxs("span", { className: inputStyles.range.value, children: [
|
|
2082
|
+
lineWidth,
|
|
2083
|
+
"px"
|
|
2084
|
+
] })
|
|
2085
|
+
] })
|
|
2086
|
+
] }),
|
|
2087
|
+
/* @__PURE__ */ jsx("div", { className: "mt-8", children: /* @__PURE__ */ jsx(
|
|
2088
|
+
"button",
|
|
2089
|
+
{
|
|
2090
|
+
onClick: handleApplyChanges,
|
|
2091
|
+
className: inputStyles.button.primary,
|
|
2092
|
+
children: "Apply Changes"
|
|
2093
|
+
}
|
|
2094
|
+
) })
|
|
2095
|
+
] });
|
|
2096
|
+
}
|
|
2097
|
+
const DEFAULT_RECT_PROPS = {
|
|
2098
|
+
cornerRadius: 0,
|
|
2099
|
+
fillColor: "#3b82f6",
|
|
2100
|
+
opacity: 100,
|
|
2101
|
+
strokeColor: "#000000",
|
|
2102
|
+
lineWidth: 0
|
|
2103
|
+
};
|
|
2104
|
+
const useRectPanel = ({
|
|
2105
|
+
selectedElement,
|
|
2106
|
+
addElement,
|
|
2107
|
+
updateElement
|
|
2108
|
+
}) => {
|
|
2109
|
+
const [cornerRadius, setCornerRadius] = useState(DEFAULT_RECT_PROPS.cornerRadius);
|
|
2110
|
+
const [fillColor, setFillColor] = useState(DEFAULT_RECT_PROPS.fillColor);
|
|
2111
|
+
const [opacity, setOpacity] = useState(DEFAULT_RECT_PROPS.opacity);
|
|
2112
|
+
const [strokeColor, setStrokeColor] = useState(DEFAULT_RECT_PROPS.strokeColor);
|
|
2113
|
+
const [lineWidth, setLineWidth] = useState(DEFAULT_RECT_PROPS.lineWidth);
|
|
2114
|
+
const handleApplyChanges = () => {
|
|
2115
|
+
let rectElement;
|
|
2116
|
+
if (selectedElement instanceof RectElement) {
|
|
2117
|
+
rectElement = selectedElement;
|
|
2118
|
+
rectElement.setCornerRadius(cornerRadius);
|
|
2119
|
+
rectElement.setOpacity(opacity);
|
|
2120
|
+
rectElement.setStrokeColor(strokeColor);
|
|
2121
|
+
rectElement.setLineWidth(lineWidth);
|
|
2122
|
+
updateElement == null ? void 0 : updateElement(rectElement);
|
|
2123
|
+
} else {
|
|
2124
|
+
rectElement = new RectElement(fillColor, { width: 200, height: 200 }).setCornerRadius(cornerRadius).setOpacity(opacity).setStrokeColor(strokeColor).setLineWidth(lineWidth);
|
|
2125
|
+
addElement == null ? void 0 : addElement(rectElement);
|
|
2126
|
+
}
|
|
2127
|
+
};
|
|
2128
|
+
useEffect(() => {
|
|
2129
|
+
if (selectedElement instanceof RectElement) {
|
|
2130
|
+
setCornerRadius(selectedElement.getCornerRadius() ?? DEFAULT_RECT_PROPS.cornerRadius);
|
|
2131
|
+
setFillColor(selectedElement.getFill() ?? DEFAULT_RECT_PROPS.fillColor);
|
|
2132
|
+
setOpacity(selectedElement.getOpacity() ?? DEFAULT_RECT_PROPS.opacity);
|
|
2133
|
+
setStrokeColor(selectedElement.getStrokeColor() ?? DEFAULT_RECT_PROPS.strokeColor);
|
|
2134
|
+
setLineWidth(selectedElement.getLineWidth() ?? DEFAULT_RECT_PROPS.lineWidth);
|
|
2135
|
+
}
|
|
2136
|
+
}, [selectedElement]);
|
|
2137
|
+
return {
|
|
2138
|
+
cornerRadius,
|
|
2139
|
+
fillColor,
|
|
2140
|
+
opacity,
|
|
2141
|
+
strokeColor,
|
|
2142
|
+
lineWidth,
|
|
2143
|
+
setCornerRadius,
|
|
2144
|
+
setFillColor,
|
|
2145
|
+
setOpacity,
|
|
2146
|
+
setStrokeColor,
|
|
2147
|
+
setLineWidth,
|
|
2148
|
+
handleApplyChanges
|
|
2149
|
+
};
|
|
2150
|
+
};
|
|
2151
|
+
function RectPanelContainer(props) {
|
|
2152
|
+
const rectPanelProps = useRectPanel({
|
|
2153
|
+
selectedElement: props.selectedElement ?? null,
|
|
2154
|
+
addElement: props.addElement,
|
|
2155
|
+
updateElement: props.updateElement
|
|
2156
|
+
});
|
|
2157
|
+
return /* @__PURE__ */ jsx(RectPanel, { ...rectPanelProps });
|
|
2158
|
+
}
|
|
2159
|
+
function CirclePanel({
|
|
2160
|
+
radius,
|
|
2161
|
+
fillColor,
|
|
2162
|
+
opacity,
|
|
2163
|
+
strokeColor,
|
|
2164
|
+
lineWidth,
|
|
2165
|
+
setRadius,
|
|
2166
|
+
setFillColor,
|
|
2167
|
+
setOpacity,
|
|
2168
|
+
setStrokeColor,
|
|
2169
|
+
setLineWidth,
|
|
2170
|
+
handleApplyChanges
|
|
2171
|
+
}) {
|
|
2172
|
+
return /* @__PURE__ */ jsxs("div", { className: inputStyles.panel.container, children: [
|
|
2173
|
+
/* @__PURE__ */ jsx("h3", { className: inputStyles.panel.title, children: "Circle" }),
|
|
2174
|
+
/* @__PURE__ */ jsxs("div", { className: inputStyles.container, children: [
|
|
2175
|
+
/* @__PURE__ */ jsx("label", { className: inputStyles.label.base, children: "Radius" }),
|
|
2176
|
+
/* @__PURE__ */ jsxs("div", { className: inputStyles.range.container, children: [
|
|
2177
|
+
/* @__PURE__ */ jsx(
|
|
2178
|
+
"input",
|
|
2179
|
+
{
|
|
2180
|
+
type: "range",
|
|
2181
|
+
min: "10",
|
|
2182
|
+
max: "300",
|
|
2183
|
+
value: radius,
|
|
2184
|
+
onChange: (e) => setRadius(Number(e.target.value)),
|
|
2185
|
+
className: inputStyles.range.base
|
|
2186
|
+
}
|
|
2187
|
+
),
|
|
2188
|
+
/* @__PURE__ */ jsxs("span", { className: inputStyles.range.value, children: [
|
|
2189
|
+
radius,
|
|
2190
|
+
"px"
|
|
2191
|
+
] })
|
|
2192
|
+
] })
|
|
2193
|
+
] }),
|
|
2194
|
+
/* @__PURE__ */ jsxs("div", { className: inputStyles.container, children: [
|
|
2195
|
+
/* @__PURE__ */ jsx("label", { className: inputStyles.label.base, children: "Fill Color" }),
|
|
2196
|
+
/* @__PURE__ */ jsxs("div", { className: inputStyles.color.container, children: [
|
|
2197
|
+
/* @__PURE__ */ jsx(
|
|
2198
|
+
"input",
|
|
2199
|
+
{
|
|
2200
|
+
type: "color",
|
|
2201
|
+
value: fillColor,
|
|
2202
|
+
onChange: (e) => setFillColor(e.target.value),
|
|
2203
|
+
className: inputStyles.color.picker
|
|
2204
|
+
}
|
|
2205
|
+
),
|
|
2206
|
+
/* @__PURE__ */ jsx(
|
|
2207
|
+
"div",
|
|
2208
|
+
{
|
|
2209
|
+
className: inputStyles.color.preview,
|
|
2210
|
+
style: { backgroundColor: fillColor }
|
|
2211
|
+
}
|
|
2212
|
+
)
|
|
2213
|
+
] })
|
|
2214
|
+
] }),
|
|
2215
|
+
/* @__PURE__ */ jsxs("div", { className: inputStyles.container, children: [
|
|
2216
|
+
/* @__PURE__ */ jsx("label", { className: inputStyles.label.base, children: "Opacity" }),
|
|
2217
|
+
/* @__PURE__ */ jsxs("div", { className: inputStyles.range.container, children: [
|
|
2218
|
+
/* @__PURE__ */ jsx(
|
|
2219
|
+
"input",
|
|
2220
|
+
{
|
|
2221
|
+
type: "range",
|
|
2222
|
+
min: "0",
|
|
2223
|
+
max: "100",
|
|
2224
|
+
value: opacity,
|
|
2225
|
+
onChange: (e) => setOpacity(Number(e.target.value)),
|
|
2226
|
+
className: inputStyles.range.gradient
|
|
2227
|
+
}
|
|
2228
|
+
),
|
|
2229
|
+
/* @__PURE__ */ jsxs("span", { className: inputStyles.range.value, children: [
|
|
2230
|
+
opacity,
|
|
2231
|
+
"%"
|
|
2232
|
+
] })
|
|
2233
|
+
] })
|
|
2234
|
+
] }),
|
|
2235
|
+
/* @__PURE__ */ jsxs("div", { className: inputStyles.container, children: [
|
|
2236
|
+
/* @__PURE__ */ jsx("label", { className: inputStyles.label.base, children: "Stroke Color" }),
|
|
2237
|
+
/* @__PURE__ */ jsxs("div", { className: inputStyles.color.container, children: [
|
|
2238
|
+
/* @__PURE__ */ jsx(
|
|
2239
|
+
"input",
|
|
2240
|
+
{
|
|
2241
|
+
type: "color",
|
|
2242
|
+
value: strokeColor,
|
|
2243
|
+
onChange: (e) => setStrokeColor(e.target.value),
|
|
2244
|
+
className: inputStyles.color.picker
|
|
2245
|
+
}
|
|
2246
|
+
),
|
|
2247
|
+
/* @__PURE__ */ jsx(
|
|
2248
|
+
"div",
|
|
2249
|
+
{
|
|
2250
|
+
className: inputStyles.color.preview,
|
|
2251
|
+
style: { backgroundColor: strokeColor }
|
|
2252
|
+
}
|
|
2253
|
+
)
|
|
2254
|
+
] })
|
|
2255
|
+
] }),
|
|
2256
|
+
/* @__PURE__ */ jsxs("div", { className: inputStyles.container, children: [
|
|
2257
|
+
/* @__PURE__ */ jsx("label", { className: inputStyles.label.base, children: "Line Width" }),
|
|
2258
|
+
/* @__PURE__ */ jsxs("div", { className: inputStyles.range.container, children: [
|
|
2259
|
+
/* @__PURE__ */ jsx(
|
|
2260
|
+
"input",
|
|
2261
|
+
{
|
|
2262
|
+
type: "range",
|
|
2263
|
+
min: "0",
|
|
2264
|
+
max: "20",
|
|
2265
|
+
value: lineWidth,
|
|
2266
|
+
onChange: (e) => setLineWidth(Number(e.target.value)),
|
|
2267
|
+
className: inputStyles.range.base
|
|
2268
|
+
}
|
|
2269
|
+
),
|
|
2270
|
+
/* @__PURE__ */ jsxs("span", { className: inputStyles.range.value, children: [
|
|
2271
|
+
lineWidth,
|
|
2272
|
+
"px"
|
|
2273
|
+
] })
|
|
2274
|
+
] })
|
|
2275
|
+
] }),
|
|
2276
|
+
/* @__PURE__ */ jsx("div", { className: "mt-8", children: /* @__PURE__ */ jsx(
|
|
2277
|
+
"button",
|
|
2278
|
+
{
|
|
2279
|
+
onClick: handleApplyChanges,
|
|
2280
|
+
className: inputStyles.button.primary,
|
|
2281
|
+
children: "Apply Changes"
|
|
2282
|
+
}
|
|
2283
|
+
) })
|
|
2284
|
+
] });
|
|
2285
|
+
}
|
|
2286
|
+
const DEFAULT_CIRCLE_PROPS = {
|
|
2287
|
+
radius: 50,
|
|
2288
|
+
fillColor: "#3b82f6",
|
|
2289
|
+
opacity: 100,
|
|
2290
|
+
strokeColor: "#000000",
|
|
2291
|
+
lineWidth: 0
|
|
2292
|
+
};
|
|
2293
|
+
const useCirclePanel = ({
|
|
2294
|
+
selectedElement,
|
|
2295
|
+
addElement,
|
|
2296
|
+
updateElement
|
|
2297
|
+
}) => {
|
|
2298
|
+
const [radius, setRadius] = useState(DEFAULT_CIRCLE_PROPS.radius);
|
|
2299
|
+
const [fillColor, setFillColor] = useState(DEFAULT_CIRCLE_PROPS.fillColor);
|
|
2300
|
+
const [opacity, setOpacity] = useState(DEFAULT_CIRCLE_PROPS.opacity);
|
|
2301
|
+
const [strokeColor, setStrokeColor] = useState(DEFAULT_CIRCLE_PROPS.strokeColor);
|
|
2302
|
+
const [lineWidth, setLineWidth] = useState(DEFAULT_CIRCLE_PROPS.lineWidth);
|
|
2303
|
+
const handleApplyChanges = () => {
|
|
2304
|
+
let circleElement;
|
|
2305
|
+
if (selectedElement instanceof CircleElement) {
|
|
2306
|
+
circleElement = selectedElement;
|
|
2307
|
+
circleElement.setRadius(radius);
|
|
2308
|
+
circleElement.setFill(fillColor);
|
|
2309
|
+
circleElement.setOpacity(opacity);
|
|
2310
|
+
circleElement.setStrokeColor(strokeColor);
|
|
2311
|
+
circleElement.setLineWidth(lineWidth);
|
|
2312
|
+
updateElement == null ? void 0 : updateElement(circleElement);
|
|
2313
|
+
} else {
|
|
2314
|
+
circleElement = new CircleElement(fillColor, radius).setOpacity(opacity).setStrokeColor(strokeColor).setLineWidth(lineWidth);
|
|
2315
|
+
addElement == null ? void 0 : addElement(circleElement);
|
|
2316
|
+
}
|
|
2317
|
+
};
|
|
2318
|
+
useEffect(() => {
|
|
2319
|
+
if (selectedElement instanceof CircleElement) {
|
|
2320
|
+
setRadius(selectedElement.getRadius() ?? DEFAULT_CIRCLE_PROPS.radius);
|
|
2321
|
+
setFillColor(selectedElement.getFill() ?? DEFAULT_CIRCLE_PROPS.fillColor);
|
|
2322
|
+
setOpacity(selectedElement.getOpacity() ?? DEFAULT_CIRCLE_PROPS.opacity);
|
|
2323
|
+
setStrokeColor(selectedElement.getStrokeColor() ?? DEFAULT_CIRCLE_PROPS.strokeColor);
|
|
2324
|
+
setLineWidth(selectedElement.getLineWidth() ?? DEFAULT_CIRCLE_PROPS.lineWidth);
|
|
2325
|
+
}
|
|
2326
|
+
}, [selectedElement]);
|
|
2327
|
+
return {
|
|
2328
|
+
radius,
|
|
2329
|
+
fillColor,
|
|
2330
|
+
opacity,
|
|
2331
|
+
strokeColor,
|
|
2332
|
+
lineWidth,
|
|
2333
|
+
setRadius,
|
|
2334
|
+
setFillColor,
|
|
2335
|
+
setOpacity,
|
|
2336
|
+
setStrokeColor,
|
|
2337
|
+
setLineWidth,
|
|
2338
|
+
handleApplyChanges
|
|
2339
|
+
};
|
|
2340
|
+
};
|
|
2341
|
+
function CirclePanelContainer(props) {
|
|
2342
|
+
const circlePanelProps = useCirclePanel({
|
|
2343
|
+
selectedElement: props.selectedElement ?? null,
|
|
2344
|
+
addElement: props.addElement,
|
|
2345
|
+
updateElement: props.updateElement
|
|
2346
|
+
});
|
|
2347
|
+
return /* @__PURE__ */ jsx(CirclePanel, { ...circlePanelProps });
|
|
2348
|
+
}
|
|
2349
|
+
const ElementPanelContainer = ({
|
|
2350
|
+
selectedTool,
|
|
2351
|
+
setSelectedTool,
|
|
2352
|
+
videoResolution,
|
|
2353
|
+
selectedElement,
|
|
2354
|
+
addElement,
|
|
2355
|
+
updateElement
|
|
2356
|
+
}) => {
|
|
2357
|
+
const addNewElement = async (element) => {
|
|
2358
|
+
await addElement(element);
|
|
2359
|
+
if (!["image", "video", "audio"].includes(selectedTool)) {
|
|
2360
|
+
setSelectedTool("none");
|
|
2361
|
+
}
|
|
2362
|
+
};
|
|
2363
|
+
const renderLibrary = () => {
|
|
2364
|
+
switch (selectedTool) {
|
|
2365
|
+
case "image":
|
|
2366
|
+
return /* @__PURE__ */ jsx(
|
|
2367
|
+
ImagePanelContainer,
|
|
2368
|
+
{
|
|
2369
|
+
videoResolution,
|
|
2370
|
+
selectedElement,
|
|
2371
|
+
addElement: addNewElement,
|
|
2372
|
+
updateElement
|
|
2373
|
+
}
|
|
2374
|
+
);
|
|
2375
|
+
case "audio":
|
|
2376
|
+
return /* @__PURE__ */ jsx(
|
|
2377
|
+
AudioPanelContainer,
|
|
2378
|
+
{
|
|
2379
|
+
videoResolution,
|
|
2380
|
+
selectedElement,
|
|
2381
|
+
addElement: addNewElement,
|
|
2382
|
+
updateElement
|
|
2383
|
+
}
|
|
2384
|
+
);
|
|
2385
|
+
case "video":
|
|
2386
|
+
return /* @__PURE__ */ jsx(
|
|
2387
|
+
VideoPanelContainer,
|
|
2388
|
+
{
|
|
2389
|
+
videoResolution,
|
|
2390
|
+
selectedElement,
|
|
2391
|
+
addElement: addNewElement,
|
|
2392
|
+
updateElement
|
|
2393
|
+
}
|
|
2394
|
+
);
|
|
2395
|
+
case "text":
|
|
2396
|
+
return /* @__PURE__ */ jsx(
|
|
2397
|
+
TextPanelContainer,
|
|
2398
|
+
{
|
|
2399
|
+
selectedElement,
|
|
2400
|
+
addElement: addNewElement,
|
|
2401
|
+
updateElement
|
|
2402
|
+
}
|
|
2403
|
+
);
|
|
2404
|
+
case "icon":
|
|
2405
|
+
return /* @__PURE__ */ jsx(
|
|
2406
|
+
IconPanelContainer,
|
|
2407
|
+
{
|
|
2408
|
+
videoResolution,
|
|
2409
|
+
selectedElement,
|
|
2410
|
+
addElement: addNewElement,
|
|
2411
|
+
updateElement
|
|
2412
|
+
}
|
|
2413
|
+
);
|
|
2414
|
+
case "rect":
|
|
2415
|
+
return /* @__PURE__ */ jsx(
|
|
2416
|
+
RectPanelContainer,
|
|
2417
|
+
{
|
|
2418
|
+
videoResolution,
|
|
2419
|
+
selectedElement,
|
|
2420
|
+
addElement: addNewElement,
|
|
2421
|
+
updateElement
|
|
2422
|
+
}
|
|
2423
|
+
);
|
|
2424
|
+
case "circle":
|
|
2425
|
+
return /* @__PURE__ */ jsx(
|
|
2426
|
+
CirclePanelContainer,
|
|
2427
|
+
{
|
|
2428
|
+
videoResolution,
|
|
2429
|
+
selectedElement,
|
|
2430
|
+
addElement: addNewElement,
|
|
2431
|
+
updateElement
|
|
2432
|
+
}
|
|
2433
|
+
);
|
|
2434
|
+
case "subtitle":
|
|
2435
|
+
return /* @__PURE__ */ jsx(SubtitlesPanel, {});
|
|
2436
|
+
default:
|
|
2437
|
+
return /* @__PURE__ */ jsx("div", { className: inputStyles.panel.container, children: /* @__PURE__ */ jsx("div", { className: "flex-1 flex h-full items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
2438
|
+
/* @__PURE__ */ jsx(WandSparkles, { className: "w-12 h-12 mx-auto mb-4 text-purple-500/50" }),
|
|
2439
|
+
/* @__PURE__ */ jsx("p", { className: inputStyles.label.base, children: "Select a Tool to Begin" })
|
|
2440
|
+
] }) }) });
|
|
2441
|
+
}
|
|
2442
|
+
};
|
|
2443
|
+
return renderLibrary();
|
|
2444
|
+
};
|
|
2445
|
+
const propsCategories = /* @__PURE__ */ new Map([
|
|
2446
|
+
[
|
|
2447
|
+
"element-props",
|
|
2448
|
+
{
|
|
2449
|
+
id: "element-props",
|
|
2450
|
+
name: "Properties",
|
|
2451
|
+
icon: "Settings",
|
|
2452
|
+
description: "Element Properties"
|
|
2453
|
+
}
|
|
2454
|
+
],
|
|
2455
|
+
[
|
|
2456
|
+
"animations",
|
|
2457
|
+
{
|
|
2458
|
+
id: "animations",
|
|
2459
|
+
name: "Animations",
|
|
2460
|
+
icon: "Zap",
|
|
2461
|
+
description: "Animations"
|
|
2462
|
+
}
|
|
2463
|
+
],
|
|
2464
|
+
[
|
|
2465
|
+
"text-effects",
|
|
2466
|
+
{
|
|
2467
|
+
id: "text-effects",
|
|
2468
|
+
name: "Text Effects",
|
|
2469
|
+
icon: "SparklesIcon",
|
|
2470
|
+
description: "Text Effects"
|
|
2471
|
+
}
|
|
2472
|
+
],
|
|
2473
|
+
[
|
|
2474
|
+
"color-effects",
|
|
2475
|
+
{
|
|
2476
|
+
id: "color-effects",
|
|
2477
|
+
name: "Color Effects",
|
|
2478
|
+
icon: "Image",
|
|
2479
|
+
description: "Color Effects"
|
|
2480
|
+
}
|
|
2481
|
+
],
|
|
2482
|
+
[
|
|
2483
|
+
"playback-props",
|
|
2484
|
+
{
|
|
2485
|
+
id: "playback-props",
|
|
2486
|
+
name: "Playback Props",
|
|
2487
|
+
icon: "Music",
|
|
2488
|
+
description: "Playback Properties"
|
|
2489
|
+
}
|
|
2490
|
+
],
|
|
2491
|
+
[
|
|
2492
|
+
"subtitle-style",
|
|
2493
|
+
{
|
|
2494
|
+
id: "subtitle-style",
|
|
2495
|
+
name: "Subtitle Style",
|
|
2496
|
+
icon: "MessageSquare",
|
|
2497
|
+
description: "Subtitle Style"
|
|
2498
|
+
}
|
|
2499
|
+
]
|
|
2500
|
+
]);
|
|
2501
|
+
const getIcon = (iconName) => {
|
|
2502
|
+
switch (iconName) {
|
|
2503
|
+
case "Type":
|
|
2504
|
+
return Type;
|
|
2505
|
+
case "Infinity":
|
|
2506
|
+
return Infinity;
|
|
2507
|
+
case "Image":
|
|
2508
|
+
return Image;
|
|
2509
|
+
case "Music":
|
|
2510
|
+
return Music;
|
|
2511
|
+
case "MessageSquare":
|
|
2512
|
+
return MessageSquare;
|
|
2513
|
+
case "Settings":
|
|
2514
|
+
return Settings;
|
|
2515
|
+
case "SparklesIcon":
|
|
2516
|
+
return Sparkles;
|
|
2517
|
+
case "Zap":
|
|
2518
|
+
return Zap;
|
|
2519
|
+
default:
|
|
2520
|
+
return Plus;
|
|
2521
|
+
}
|
|
2522
|
+
};
|
|
2523
|
+
function PropsToolbar({
|
|
2524
|
+
selectedElement,
|
|
2525
|
+
selectedProp,
|
|
2526
|
+
setSelectedProp
|
|
2527
|
+
}) {
|
|
2528
|
+
const availableSections = useMemo(() => {
|
|
2529
|
+
const sections = [];
|
|
2530
|
+
if (selectedElement instanceof TextElement) {
|
|
2531
|
+
sections.push(propsCategories.get("element-props"));
|
|
2532
|
+
sections.push(propsCategories.get("animations"));
|
|
2533
|
+
sections.push(propsCategories.get("text-effects"));
|
|
2534
|
+
} else if (selectedElement instanceof ImageElement) {
|
|
2535
|
+
sections.push(propsCategories.get("element-props"));
|
|
2536
|
+
sections.push(propsCategories.get("animations"));
|
|
2537
|
+
sections.push(propsCategories.get("color-effects"));
|
|
2538
|
+
} else if (selectedElement instanceof VideoElement) {
|
|
2539
|
+
sections.push(propsCategories.get("element-props"));
|
|
2540
|
+
sections.push(propsCategories.get("animations"));
|
|
2541
|
+
sections.push(propsCategories.get("color-effects"));
|
|
2542
|
+
sections.push(propsCategories.get("playback-props"));
|
|
2543
|
+
} else if (selectedElement instanceof AudioElement) {
|
|
2544
|
+
sections.push(propsCategories.get("element-props"));
|
|
2545
|
+
sections.push(propsCategories.get("playback-props"));
|
|
2546
|
+
} else if (selectedElement instanceof CircleElement) {
|
|
2547
|
+
sections.push(propsCategories.get("element-props"));
|
|
2548
|
+
sections.push(propsCategories.get("animations"));
|
|
2549
|
+
} else if (selectedElement instanceof RectElement) {
|
|
2550
|
+
sections.push(propsCategories.get("element-props"));
|
|
2551
|
+
sections.push(propsCategories.get("animations"));
|
|
2552
|
+
} else if (selectedElement instanceof IconElement) {
|
|
2553
|
+
sections.push(propsCategories.get("element-props"));
|
|
2554
|
+
sections.push(propsCategories.get("animations"));
|
|
2555
|
+
} else if (selectedElement instanceof CaptionElement) {
|
|
2556
|
+
sections.push(propsCategories.get("element-props"));
|
|
2557
|
+
sections.push(propsCategories.get("animations"));
|
|
2558
|
+
sections.push(propsCategories.get("subtitle-style"));
|
|
2559
|
+
}
|
|
2560
|
+
return sections;
|
|
2561
|
+
}, [selectedElement]);
|
|
2562
|
+
if (!selectedElement) {
|
|
2563
|
+
return null;
|
|
2564
|
+
}
|
|
2565
|
+
return /* @__PURE__ */ jsx("div", { className: "w-16 bg-neutral/80 border-l border-gray-300/50 flex flex-col items-center py-4 space-y-3 justify-start backdrop-blur-md shadow-lg", children: availableSections.map((tool) => {
|
|
2566
|
+
const Icon2 = getIcon(tool.icon);
|
|
2567
|
+
const isSelected = selectedProp === tool.id;
|
|
2568
|
+
return /* @__PURE__ */ jsxs(
|
|
2569
|
+
"button",
|
|
2570
|
+
{
|
|
2571
|
+
onClick: () => setSelectedProp(tool.id),
|
|
2572
|
+
className: `
|
|
2573
|
+
props-toolbar-btn group ${isSelected ? "active" : ""}
|
|
2574
|
+
`,
|
|
2575
|
+
title: `${tool.name}${tool.shortcut ? ` (${tool.shortcut})` : ""}`,
|
|
2576
|
+
children: [
|
|
2577
|
+
/* @__PURE__ */ jsx(Icon2, { className: "w-4 h-4" }),
|
|
2578
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] mt-1 transition-opacity duration-200", children: tool.name })
|
|
2579
|
+
]
|
|
2580
|
+
},
|
|
2581
|
+
tool.id
|
|
2582
|
+
);
|
|
2583
|
+
}) });
|
|
2584
|
+
}
|
|
2585
|
+
function ElementProps({ selectedElement, updateElement }) {
|
|
2586
|
+
const elementProps = (selectedElement == null ? void 0 : selectedElement.getProps()) || {};
|
|
2587
|
+
const { x, y, opacity, rotation } = elementProps;
|
|
2588
|
+
const handleUpdateElement = (props) => {
|
|
2589
|
+
if (selectedElement) {
|
|
2590
|
+
updateElement == null ? void 0 : updateElement(selectedElement == null ? void 0 : selectedElement.setProps({ ...elementProps, ...props }));
|
|
2591
|
+
}
|
|
2592
|
+
};
|
|
2593
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
2594
|
+
/* @__PURE__ */ jsxs("div", { className: "bg-neutral-800/40 rounded-lg p-2.5 border border-gray-600/20", children: [
|
|
2595
|
+
/* @__PURE__ */ jsxs("h5", { className: "text-xs font-semibold text-gray-200 mb-2 flex items-center gap-1.5", children: [
|
|
2596
|
+
/* @__PURE__ */ jsx("div", { className: "w-1.5 h-1.5 bg-purple-400 rounded-full" }),
|
|
2597
|
+
"Position & Size"
|
|
2598
|
+
] }),
|
|
2599
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
2600
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2601
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs text-gray-400 mb-1", children: "X" }),
|
|
2602
|
+
/* @__PURE__ */ jsx(
|
|
2603
|
+
"input",
|
|
2604
|
+
{
|
|
2605
|
+
type: "number",
|
|
2606
|
+
value: x ?? 0,
|
|
2607
|
+
onChange: (e) => handleUpdateElement({ x: Number(e.target.value) }),
|
|
2608
|
+
className: "w-full bg-neutral-700/60 border border-gray-600/40 rounded-md text-white text-xs px-2 py-1.5 focus:border-purple-500 focus:outline-none focus:ring-1 focus:ring-purple-500/30 transition-all duration-200"
|
|
2609
|
+
}
|
|
2610
|
+
)
|
|
2611
|
+
] }),
|
|
2612
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2613
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs text-gray-400 mb-1", children: "Y" }),
|
|
2614
|
+
/* @__PURE__ */ jsx(
|
|
2615
|
+
"input",
|
|
2616
|
+
{
|
|
2617
|
+
type: "number",
|
|
2618
|
+
value: y ?? 0,
|
|
2619
|
+
onChange: (e) => handleUpdateElement({ y: Number(e.target.value) }),
|
|
2620
|
+
className: "w-full bg-neutral-700/60 border border-gray-600/40 rounded-md text-white text-xs px-2 py-1.5 focus:border-purple-500 focus:outline-none focus:ring-1 focus:ring-purple-500/30 transition-all duration-200"
|
|
2621
|
+
}
|
|
2622
|
+
)
|
|
2623
|
+
] })
|
|
2624
|
+
] })
|
|
2625
|
+
] }),
|
|
2626
|
+
/* @__PURE__ */ jsxs("div", { className: "bg-neutral-800/40 rounded-lg p-2.5 border border-gray-600/20", children: [
|
|
2627
|
+
/* @__PURE__ */ jsxs("h5", { className: "text-xs font-semibold text-gray-200 mb-2 flex items-center gap-1.5", children: [
|
|
2628
|
+
/* @__PURE__ */ jsx("div", { className: "w-1.5 h-1.5 bg-purple-400 rounded-full" }),
|
|
2629
|
+
"Opacity"
|
|
2630
|
+
] }),
|
|
2631
|
+
/* @__PURE__ */ jsx(
|
|
2632
|
+
"input",
|
|
2633
|
+
{
|
|
2634
|
+
type: "range",
|
|
2635
|
+
min: "0",
|
|
2636
|
+
max: "100",
|
|
2637
|
+
value: (opacity ?? 1) * 100,
|
|
2638
|
+
onChange: (e) => handleUpdateElement({ opacity: Number(e.target.value) / 100 }),
|
|
2639
|
+
className: "w-full h-1.5 bg-gradient-to-r from-purple-500/30 to-neutral-600/50 rounded-full appearance-none cursor-pointer slider-thumb"
|
|
2640
|
+
}
|
|
2641
|
+
),
|
|
2642
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between text-xs text-gray-400 mt-1", children: [
|
|
2643
|
+
/* @__PURE__ */ jsx("span", { children: "0%" }),
|
|
2644
|
+
/* @__PURE__ */ jsx("span", { children: "100%" })
|
|
2645
|
+
] })
|
|
2646
|
+
] }),
|
|
2647
|
+
/* @__PURE__ */ jsxs("div", { className: "bg-neutral-800/40 rounded-lg p-2.5 border border-gray-600/20", children: [
|
|
2648
|
+
/* @__PURE__ */ jsxs("h5", { className: "text-xs font-semibold text-gray-200 mb-2 flex items-center gap-1.5", children: [
|
|
2649
|
+
/* @__PURE__ */ jsx("div", { className: "w-1.5 h-1.5 bg-purple-400 rounded-full" }),
|
|
2650
|
+
"Rotation"
|
|
2651
|
+
] }),
|
|
2652
|
+
/* @__PURE__ */ jsx(
|
|
2653
|
+
"input",
|
|
2654
|
+
{
|
|
2655
|
+
type: "range",
|
|
2656
|
+
min: "0",
|
|
2657
|
+
max: "360",
|
|
2658
|
+
value: rotation ?? 0,
|
|
2659
|
+
onChange: (e) => handleUpdateElement({ rotation: Number(e.target.value) }),
|
|
2660
|
+
className: "w-full h-1.5 bg-gradient-to-r from-purple-500/30 to-neutral-600/50 rounded-full appearance-none cursor-pointer slider-thumb"
|
|
2661
|
+
}
|
|
2662
|
+
),
|
|
2663
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between text-xs text-gray-400 mt-1", children: [
|
|
2664
|
+
/* @__PURE__ */ jsx("span", { children: "0°" }),
|
|
2665
|
+
/* @__PURE__ */ jsx("span", { children: "360°" })
|
|
2666
|
+
] })
|
|
2667
|
+
] })
|
|
2668
|
+
] });
|
|
2669
|
+
}
|
|
2670
|
+
function TextEffects({
|
|
2671
|
+
selectedElement,
|
|
2672
|
+
updateElement
|
|
2673
|
+
}) {
|
|
2674
|
+
if (!(selectedElement instanceof TextElement)) return null;
|
|
2675
|
+
const currentEffect = selectedElement.getTextEffect();
|
|
2676
|
+
const handleUpdateEffect = (props) => {
|
|
2677
|
+
if (!selectedElement || !(selectedElement instanceof TextElement)) return;
|
|
2678
|
+
let effect = currentEffect;
|
|
2679
|
+
if (props.name === "") {
|
|
2680
|
+
selectedElement.setTextEffect(void 0);
|
|
2681
|
+
updateElement == null ? void 0 : updateElement(selectedElement);
|
|
2682
|
+
return;
|
|
2683
|
+
}
|
|
2684
|
+
if (!effect || props.name && props.name !== effect.getName()) {
|
|
2685
|
+
effect = new ElementTextEffect(
|
|
2686
|
+
props.name || (currentEffect == null ? void 0 : currentEffect.getName()) || TEXT_EFFECTS[0].name
|
|
2687
|
+
);
|
|
2688
|
+
effect.setDelay(0);
|
|
2689
|
+
effect.setDuration(1);
|
|
2690
|
+
effect.setBufferTime(0.1);
|
|
2691
|
+
}
|
|
2692
|
+
if (props.delay !== void 0) effect.setDelay(props.delay);
|
|
2693
|
+
if (props.duration !== void 0) effect.setDuration(props.duration);
|
|
2694
|
+
if (props.bufferTime !== void 0) effect.setBufferTime(props.bufferTime);
|
|
2695
|
+
selectedElement.setTextEffect(effect);
|
|
2696
|
+
updateElement == null ? void 0 : updateElement(selectedElement);
|
|
2697
|
+
};
|
|
2698
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
2699
|
+
/* @__PURE__ */ jsxs("div", { className: "bg-neutral-800/40 rounded-lg p-2.5 border border-gray-600/20", children: [
|
|
2700
|
+
/* @__PURE__ */ jsxs("h5", { className: "text-xs font-semibold text-gray-200 mb-2 flex items-center gap-1.5", children: [
|
|
2701
|
+
/* @__PURE__ */ jsx("div", { className: "w-1.5 h-1.5 bg-purple-400 rounded-full" }),
|
|
2702
|
+
"Text Effect Type"
|
|
2703
|
+
] }),
|
|
2704
|
+
/* @__PURE__ */ jsxs(
|
|
2705
|
+
"select",
|
|
2706
|
+
{
|
|
2707
|
+
value: (currentEffect == null ? void 0 : currentEffect.getName()) || "",
|
|
2708
|
+
onChange: (e) => handleUpdateEffect({ name: e.target.value }),
|
|
2709
|
+
className: "w-full bg-neutral-700/60 border border-gray-600/40 rounded-md text-white text-xs px-2 py-1.5 focus:border-purple-500 focus:outline-none focus:ring-1 focus:ring-purple-500/30 transition-all duration-200",
|
|
2710
|
+
children: [
|
|
2711
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "No Effect" }),
|
|
2712
|
+
TEXT_EFFECTS.map((effect) => /* @__PURE__ */ jsx("option", { value: effect.name, children: effect.name.charAt(0).toUpperCase() + effect.name.slice(1) }, effect.name))
|
|
2713
|
+
]
|
|
2714
|
+
}
|
|
2715
|
+
)
|
|
2716
|
+
] }),
|
|
2717
|
+
currentEffect && /* @__PURE__ */ jsxs("div", { className: "bg-neutral-800/40 rounded-lg p-2.5 border border-gray-600/20", children: [
|
|
2718
|
+
/* @__PURE__ */ jsxs("h5", { className: "text-xs font-semibold text-gray-200 mb-2 flex items-center gap-1.5", children: [
|
|
2719
|
+
/* @__PURE__ */ jsx("div", { className: "w-1.5 h-1.5 bg-purple-400 rounded-full" }),
|
|
2720
|
+
"Effect Options"
|
|
2721
|
+
] }),
|
|
2722
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2723
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2724
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs text-gray-400 mb-1", children: "Delay (seconds)" }),
|
|
2725
|
+
/* @__PURE__ */ jsx(
|
|
2726
|
+
"input",
|
|
2727
|
+
{
|
|
2728
|
+
type: "number",
|
|
2729
|
+
min: "0",
|
|
2730
|
+
max: "5",
|
|
2731
|
+
step: "0.1",
|
|
2732
|
+
value: currentEffect.getDelay() ?? 0,
|
|
2733
|
+
onChange: (e) => handleUpdateEffect({ delay: Number(e.target.value) }),
|
|
2734
|
+
className: "w-full bg-neutral-700/60 border border-gray-600/40 rounded-md text-white text-xs px-2 py-1.5 focus:border-purple-500 focus:outline-none focus:ring-1 focus:ring-purple-500/30 transition-all duration-200"
|
|
2735
|
+
}
|
|
2736
|
+
)
|
|
2737
|
+
] }),
|
|
2738
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2739
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs text-gray-400 mb-1", children: "Duration (seconds)" }),
|
|
2740
|
+
/* @__PURE__ */ jsx(
|
|
2741
|
+
"input",
|
|
2742
|
+
{
|
|
2743
|
+
type: "number",
|
|
2744
|
+
min: "0.1",
|
|
2745
|
+
max: "10",
|
|
2746
|
+
step: "0.1",
|
|
2747
|
+
value: currentEffect.getDuration() ?? 1,
|
|
2748
|
+
onChange: (e) => handleUpdateEffect({ duration: Number(e.target.value) }),
|
|
2749
|
+
className: "w-full bg-neutral-700/60 border border-gray-600/40 rounded-md text-white text-xs px-2 py-1.5 focus:border-purple-500 focus:outline-none focus:ring-1 focus:ring-purple-500/30 transition-all duration-200"
|
|
2750
|
+
}
|
|
2751
|
+
)
|
|
2752
|
+
] }),
|
|
2753
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2754
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs text-gray-400 mb-1", children: "Buffer Time (seconds)" }),
|
|
2755
|
+
/* @__PURE__ */ jsx(
|
|
2756
|
+
"input",
|
|
2757
|
+
{
|
|
2758
|
+
type: "number",
|
|
2759
|
+
min: "0.05",
|
|
2760
|
+
max: "1",
|
|
2761
|
+
step: "0.05",
|
|
2762
|
+
value: currentEffect.getBufferTime() ?? 0.1,
|
|
2763
|
+
onChange: (e) => handleUpdateEffect({ bufferTime: Number(e.target.value) }),
|
|
2764
|
+
className: "w-full bg-neutral-700/60 border border-gray-600/40 rounded-md text-white text-xs px-2 py-1.5 focus:border-purple-500 focus:outline-none focus:ring-1 focus:ring-purple-500/30 transition-all duration-200"
|
|
2765
|
+
}
|
|
2766
|
+
)
|
|
2767
|
+
] })
|
|
2768
|
+
] })
|
|
2769
|
+
] })
|
|
2770
|
+
] });
|
|
2771
|
+
}
|
|
2772
|
+
function Animation({
|
|
2773
|
+
selectedElement,
|
|
2774
|
+
updateElement
|
|
2775
|
+
}) {
|
|
2776
|
+
if (!(selectedElement instanceof TrackElement)) return null;
|
|
2777
|
+
const currentAnimation = selectedElement == null ? void 0 : selectedElement.getAnimation();
|
|
2778
|
+
const handleUpdateAnimation = (props) => {
|
|
2779
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
2780
|
+
if (!selectedElement) return;
|
|
2781
|
+
let animation = currentAnimation;
|
|
2782
|
+
if (props.name === "") {
|
|
2783
|
+
selectedElement.setAnimation(void 0);
|
|
2784
|
+
updateElement == null ? void 0 : updateElement(selectedElement);
|
|
2785
|
+
return;
|
|
2786
|
+
}
|
|
2787
|
+
const animationDef = ANIMATIONS.find(
|
|
2788
|
+
(a) => a.name === (props.name || (currentAnimation == null ? void 0 : currentAnimation.getName()))
|
|
2789
|
+
);
|
|
2790
|
+
if (!animationDef) return;
|
|
2791
|
+
if (!animation || props.name && props.name !== animation.getName()) {
|
|
2792
|
+
animation = new ElementAnimation(
|
|
2793
|
+
props.name || (currentAnimation == null ? void 0 : currentAnimation.getName()) || ANIMATIONS[0].name
|
|
2794
|
+
);
|
|
2795
|
+
animation.setInterval(animationDef.interval || 1);
|
|
2796
|
+
animation.setDuration(animationDef.duration || 1);
|
|
2797
|
+
animation.setIntensity(animationDef.intensity || 1);
|
|
2798
|
+
animation.setAnimate(animationDef.animate || "enter");
|
|
2799
|
+
if (animationDef.mode) animation.setMode(animationDef.mode);
|
|
2800
|
+
if (animationDef.direction)
|
|
2801
|
+
animation.setDirection(animationDef.direction);
|
|
2802
|
+
}
|
|
2803
|
+
if (props.interval !== void 0) {
|
|
2804
|
+
const [min, max] = ((_a = animationDef.options) == null ? void 0 : _a.interval) || [0.1, 5];
|
|
2805
|
+
animation.setInterval(Math.min(Math.max(props.interval, min), max));
|
|
2806
|
+
}
|
|
2807
|
+
if (props.duration !== void 0) {
|
|
2808
|
+
const [min, max] = ((_b = animationDef.options) == null ? void 0 : _b.duration) || [0.1, 5];
|
|
2809
|
+
animation.setDuration(Math.min(Math.max(props.duration, min), max));
|
|
2810
|
+
}
|
|
2811
|
+
if (props.intensity !== void 0) {
|
|
2812
|
+
const [min, max] = ((_c = animationDef.options) == null ? void 0 : _c.intensity) || [0.1, 2];
|
|
2813
|
+
animation.setIntensity(Math.min(Math.max(props.intensity, min), max));
|
|
2814
|
+
}
|
|
2815
|
+
if (props.animate && ((_e = (_d = animationDef.options) == null ? void 0 : _d.animate) == null ? void 0 : _e.includes(props.animate))) {
|
|
2816
|
+
animation.setAnimate(props.animate);
|
|
2817
|
+
}
|
|
2818
|
+
if (props.mode && ((_g = (_f = animationDef.options) == null ? void 0 : _f.mode) == null ? void 0 : _g.includes(props.mode))) {
|
|
2819
|
+
animation.setMode(props.mode);
|
|
2820
|
+
}
|
|
2821
|
+
if (props.direction && ((_i = (_h = animationDef.options) == null ? void 0 : _h.direction) == null ? void 0 : _i.includes(props.direction))) {
|
|
2822
|
+
animation.setDirection(props.direction);
|
|
2823
|
+
}
|
|
2824
|
+
selectedElement.setAnimation(animation);
|
|
2825
|
+
updateElement == null ? void 0 : updateElement(selectedElement);
|
|
2826
|
+
};
|
|
2827
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
2828
|
+
/* @__PURE__ */ jsxs("div", { className: "bg-neutral-800/40 rounded-lg p-2.5 border border-gray-600/20", children: [
|
|
2829
|
+
/* @__PURE__ */ jsxs("h5", { className: "text-xs font-semibold text-gray-200 mb-2 flex items-center gap-1.5", children: [
|
|
2830
|
+
/* @__PURE__ */ jsx("div", { className: "w-1.5 h-1.5 bg-purple-400 rounded-full" }),
|
|
2831
|
+
"Animation Type"
|
|
2832
|
+
] }),
|
|
2833
|
+
/* @__PURE__ */ jsxs(
|
|
2834
|
+
"select",
|
|
2835
|
+
{
|
|
2836
|
+
value: (currentAnimation == null ? void 0 : currentAnimation.getName()) || "",
|
|
2837
|
+
onChange: (e) => handleUpdateAnimation({ name: e.target.value }),
|
|
2838
|
+
className: "w-full bg-neutral-700/60 border border-gray-600/40 rounded-md text-white text-xs px-2 py-1.5 focus:border-purple-500 focus:outline-none focus:ring-1 focus:ring-purple-500/30 transition-all duration-200",
|
|
2839
|
+
children: [
|
|
2840
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "No Animation" }),
|
|
2841
|
+
ANIMATIONS.map((animation) => /* @__PURE__ */ jsx("option", { value: animation.name, children: animation.name.charAt(0).toUpperCase() + animation.name.slice(1) }, animation.name))
|
|
2842
|
+
]
|
|
2843
|
+
}
|
|
2844
|
+
)
|
|
2845
|
+
] }),
|
|
2846
|
+
currentAnimation && /* @__PURE__ */ jsxs("div", { className: "bg-neutral-800/40 rounded-lg p-2.5 border border-gray-600/20", children: [
|
|
2847
|
+
/* @__PURE__ */ jsxs("h5", { className: "text-xs font-semibold text-gray-200 mb-2 flex items-center gap-1.5", children: [
|
|
2848
|
+
/* @__PURE__ */ jsx("div", { className: "w-1.5 h-1.5 bg-purple-400 rounded-full" }),
|
|
2849
|
+
"Animation Options"
|
|
2850
|
+
] }),
|
|
2851
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-2", children: (() => {
|
|
2852
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
|
|
2853
|
+
const animationDef = ANIMATIONS.find(
|
|
2854
|
+
(a) => a.name === currentAnimation.getName()
|
|
2855
|
+
);
|
|
2856
|
+
if (!animationDef || !animationDef.options) return null;
|
|
2857
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2858
|
+
((_a = animationDef.options) == null ? void 0 : _a.animate) && /* @__PURE__ */ jsxs("div", { children: [
|
|
2859
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs text-gray-400 mb-1", children: "When to Animate" }),
|
|
2860
|
+
/* @__PURE__ */ jsx(
|
|
2861
|
+
"select",
|
|
2862
|
+
{
|
|
2863
|
+
value: currentAnimation.getAnimate(),
|
|
2864
|
+
onChange: (e) => handleUpdateAnimation({
|
|
2865
|
+
animate: e.target.value
|
|
2866
|
+
}),
|
|
2867
|
+
className: "w-full bg-neutral-700/60 border border-gray-600/40 rounded-md text-white text-xs px-2 py-1.5 focus:border-purple-500 focus:outline-none focus:ring-1 focus:ring-purple-500/30 transition-all duration-200",
|
|
2868
|
+
children: (_b = animationDef.options) == null ? void 0 : _b.animate.map((option) => /* @__PURE__ */ jsx("option", { value: option, children: option.charAt(0).toUpperCase() + option.slice(1) }, option))
|
|
2869
|
+
}
|
|
2870
|
+
)
|
|
2871
|
+
] }),
|
|
2872
|
+
((_c = animationDef.options) == null ? void 0 : _c.direction) && /* @__PURE__ */ jsxs("div", { children: [
|
|
2873
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs text-gray-400 mb-1", children: "Direction" }),
|
|
2874
|
+
/* @__PURE__ */ jsx(
|
|
2875
|
+
"select",
|
|
2876
|
+
{
|
|
2877
|
+
value: currentAnimation.getDirection(),
|
|
2878
|
+
onChange: (e) => handleUpdateAnimation({
|
|
2879
|
+
direction: e.target.value
|
|
2880
|
+
}),
|
|
2881
|
+
className: "w-full bg-neutral-700/60 border border-gray-600/40 rounded-md text-white text-xs px-2 py-1.5 focus:border-purple-500 focus:outline-none focus:ring-1 focus:ring-purple-500/30 transition-all duration-200",
|
|
2882
|
+
children: (_d = animationDef.options) == null ? void 0 : _d.direction.map((option) => /* @__PURE__ */ jsx("option", { value: option, children: option.charAt(0).toUpperCase() + option.slice(1) }, option))
|
|
2883
|
+
}
|
|
2884
|
+
)
|
|
2885
|
+
] }),
|
|
2886
|
+
((_e = animationDef.options) == null ? void 0 : _e.mode) && /* @__PURE__ */ jsxs("div", { children: [
|
|
2887
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs text-gray-400 mb-1", children: "Mode" }),
|
|
2888
|
+
/* @__PURE__ */ jsx(
|
|
2889
|
+
"select",
|
|
2890
|
+
{
|
|
2891
|
+
value: currentAnimation.getMode(),
|
|
2892
|
+
onChange: (e) => handleUpdateAnimation({
|
|
2893
|
+
mode: e.target.value
|
|
2894
|
+
}),
|
|
2895
|
+
className: "w-full bg-neutral-700/60 border border-gray-600/40 rounded-md text-white text-xs px-2 py-1.5 focus:border-purple-500 focus:outline-none focus:ring-1 focus:ring-purple-500/30 transition-all duration-200",
|
|
2896
|
+
children: (_f = animationDef.options) == null ? void 0 : _f.mode.map((option) => /* @__PURE__ */ jsx("option", { value: option, children: option.charAt(0).toUpperCase() + option.slice(1) }, option))
|
|
2897
|
+
}
|
|
2898
|
+
)
|
|
2899
|
+
] }),
|
|
2900
|
+
((_g = animationDef.options) == null ? void 0 : _g.duration) && /* @__PURE__ */ jsxs("div", { children: [
|
|
2901
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs text-gray-400 mb-1", children: "Duration (seconds)" }),
|
|
2902
|
+
/* @__PURE__ */ jsx(
|
|
2903
|
+
"input",
|
|
2904
|
+
{
|
|
2905
|
+
type: "number",
|
|
2906
|
+
min: (_h = animationDef.options) == null ? void 0 : _h.duration[0],
|
|
2907
|
+
max: (_i = animationDef.options) == null ? void 0 : _i.duration[1],
|
|
2908
|
+
step: "0.1",
|
|
2909
|
+
value: currentAnimation.getDuration(),
|
|
2910
|
+
onChange: (e) => handleUpdateAnimation({
|
|
2911
|
+
duration: Number(e.target.value)
|
|
2912
|
+
}),
|
|
2913
|
+
className: "w-full bg-neutral-700/60 border border-gray-600/40 rounded-md text-white text-xs px-2 py-1.5 focus:border-purple-500 focus:outline-none focus:ring-1 focus:ring-purple-500/30 transition-all duration-200"
|
|
2914
|
+
}
|
|
2915
|
+
)
|
|
2916
|
+
] }),
|
|
2917
|
+
((_j = animationDef.options) == null ? void 0 : _j.interval) && /* @__PURE__ */ jsxs("div", { children: [
|
|
2918
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs text-gray-400 mb-1", children: "Interval (seconds)" }),
|
|
2919
|
+
/* @__PURE__ */ jsx(
|
|
2920
|
+
"input",
|
|
2921
|
+
{
|
|
2922
|
+
type: "number",
|
|
2923
|
+
min: (_k = animationDef.options) == null ? void 0 : _k.interval[0],
|
|
2924
|
+
max: (_l = animationDef.options) == null ? void 0 : _l.interval[1],
|
|
2925
|
+
step: "0.1",
|
|
2926
|
+
value: currentAnimation.getInterval(),
|
|
2927
|
+
onChange: (e) => handleUpdateAnimation({
|
|
2928
|
+
interval: Number(e.target.value)
|
|
2929
|
+
}),
|
|
2930
|
+
className: "w-full bg-neutral-700/60 border border-gray-600/40 rounded-md text-white text-xs px-2 py-1.5 focus:border-purple-500 focus:outline-none focus:ring-1 focus:ring-purple-500/30 transition-all duration-200"
|
|
2931
|
+
}
|
|
2932
|
+
)
|
|
2933
|
+
] }),
|
|
2934
|
+
((_m = animationDef.options) == null ? void 0 : _m.intensity) && /* @__PURE__ */ jsxs("div", { children: [
|
|
2935
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs text-gray-400 mb-1", children: "Intensity" }),
|
|
2936
|
+
/* @__PURE__ */ jsx(
|
|
2937
|
+
"input",
|
|
2938
|
+
{
|
|
2939
|
+
type: "number",
|
|
2940
|
+
min: (_n = animationDef.options) == null ? void 0 : _n.intensity[0],
|
|
2941
|
+
max: (_o = animationDef.options) == null ? void 0 : _o.intensity[1],
|
|
2942
|
+
step: "0.1",
|
|
2943
|
+
value: currentAnimation.getIntensity(),
|
|
2944
|
+
onChange: (e) => handleUpdateAnimation({
|
|
2945
|
+
intensity: Number(e.target.value)
|
|
2946
|
+
}),
|
|
2947
|
+
className: "w-full bg-neutral-700/60 border border-gray-600/40 rounded-md text-white text-xs px-2 py-1.5 focus:border-purple-500 focus:outline-none focus:ring-1 focus:ring-purple-500/30 transition-all duration-200"
|
|
2948
|
+
}
|
|
2949
|
+
)
|
|
2950
|
+
] })
|
|
2951
|
+
] });
|
|
2952
|
+
})() })
|
|
2953
|
+
] })
|
|
2954
|
+
] });
|
|
2955
|
+
}
|
|
2956
|
+
function PlaybackPropsPanel({
|
|
2957
|
+
selectedElement,
|
|
2958
|
+
updateElement
|
|
2959
|
+
}) {
|
|
2960
|
+
const elementProps = (selectedElement == null ? void 0 : selectedElement.getProps()) || {};
|
|
2961
|
+
const { volume } = elementProps;
|
|
2962
|
+
const handleUpdateElement = (props) => {
|
|
2963
|
+
if (selectedElement) {
|
|
2964
|
+
updateElement == null ? void 0 : updateElement(selectedElement == null ? void 0 : selectedElement.setProps({ ...elementProps, ...props }));
|
|
2965
|
+
}
|
|
2966
|
+
};
|
|
2967
|
+
return /* @__PURE__ */ jsx("div", { className: "space-y-3", children: /* @__PURE__ */ jsxs("div", { className: "bg-neutral-800/40 rounded-lg p-2.5 border border-gray-600/20", children: [
|
|
2968
|
+
/* @__PURE__ */ jsxs("h5", { className: "text-xs font-semibold text-gray-200 mb-2 flex items-center gap-1.5", children: [
|
|
2969
|
+
/* @__PURE__ */ jsx("div", { className: "w-1.5 h-1.5 bg-purple-400 rounded-full" }),
|
|
2970
|
+
"Volume"
|
|
2971
|
+
] }),
|
|
2972
|
+
/* @__PURE__ */ jsx(
|
|
2973
|
+
"input",
|
|
2974
|
+
{
|
|
2975
|
+
type: "range",
|
|
2976
|
+
min: "0",
|
|
2977
|
+
max: "3",
|
|
2978
|
+
step: 0.1,
|
|
2979
|
+
value: volume ?? 0,
|
|
2980
|
+
onChange: (e) => handleUpdateElement({ volume: Number(e.target.value) }),
|
|
2981
|
+
className: "w-full h-1.5 bg-gradient-to-r from-purple-500/30 to-neutral-600/50 rounded-full appearance-none cursor-pointer slider-thumb"
|
|
2982
|
+
}
|
|
2983
|
+
),
|
|
2984
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between text-xs text-gray-400 mt-1", children: [
|
|
2985
|
+
/* @__PURE__ */ jsx("span", { children: "0" }),
|
|
2986
|
+
/* @__PURE__ */ jsx("span", { children: "3" })
|
|
2987
|
+
] })
|
|
2988
|
+
] }) });
|
|
2989
|
+
}
|
|
2990
|
+
function PropContainer({ title, icon, children }) {
|
|
2991
|
+
return /* @__PURE__ */ jsxs("div", { className: "border-b border-gray-600/30 last:border-b-0 bg-gradient-to-b from-neutral-800/40 to-neutral-800/20 backdrop-blur-sm", children: [
|
|
2992
|
+
/* @__PURE__ */ jsx("div", { className: "w-full flex items-center justify-between px-3 py-2.5 text-left text-gray-200 hover:text-white hover:bg-gradient-to-r hover:from-purple-600/20 hover:to-purple-500/10 transition-all duration-200 rounded-none border-l-2 border-transparent hover:border-purple-500/50", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5", children: [
|
|
2993
|
+
/* @__PURE__ */ jsx("div", { className: "text-purple-400/80", children: icon }),
|
|
2994
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-sm", children: title })
|
|
2995
|
+
] }) }),
|
|
2996
|
+
/* @__PURE__ */ jsx(
|
|
2997
|
+
"div",
|
|
2998
|
+
{
|
|
2999
|
+
className: `overflow-x-hidden overflow-y-auto p-1 transition-all duration-300 ease-in-out`,
|
|
3000
|
+
children
|
|
3001
|
+
}
|
|
3002
|
+
)
|
|
3003
|
+
] });
|
|
3004
|
+
}
|
|
3005
|
+
function PropertiesPanelContainer({
|
|
3006
|
+
selectedProp,
|
|
3007
|
+
selectedElement,
|
|
3008
|
+
updateElement
|
|
3009
|
+
}) {
|
|
3010
|
+
if (!selectedElement) {
|
|
3011
|
+
return /* @__PURE__ */ jsx("div", { className: "w-72 h-full bg-gradient-to-b from-neutral-800/90 to-neutral-900/80 border-l border-gray-600/40 overflow-y-auto overflow-x-hidden backdrop-blur-xl shadow-2xl", children: /* @__PURE__ */ jsx("div", { className: "px-3 py-3 border-b border-gray-600/30 bg-gradient-to-r from-purple-600/10 to-transparent", children: /* @__PURE__ */ jsx("h3", { className: "text-lg font-bold text-white", children: "Select Element to see properties" }) }) });
|
|
3012
|
+
}
|
|
3013
|
+
return /* @__PURE__ */ jsxs("div", { className: "w-72 h-full bg-gradient-to-b from-neutral-800/90 to-neutral-900/80 border-l border-gray-600/40 overflow-y-hidden overflow-x-hidden backdrop-blur-xl shadow-2xl", children: [
|
|
3014
|
+
selectedProp === "element-props" && /* @__PURE__ */ jsx(
|
|
3015
|
+
PropContainer,
|
|
3016
|
+
{
|
|
3017
|
+
title: "All Properties",
|
|
3018
|
+
icon: /* @__PURE__ */ jsx(Settings, { className: "w-4 h-4" }),
|
|
3019
|
+
children: /* @__PURE__ */ jsx(
|
|
3020
|
+
ElementProps,
|
|
3021
|
+
{
|
|
3022
|
+
selectedElement,
|
|
3023
|
+
updateElement
|
|
3024
|
+
}
|
|
3025
|
+
)
|
|
3026
|
+
}
|
|
3027
|
+
),
|
|
3028
|
+
selectedProp === "playback-props" && /* @__PURE__ */ jsx(
|
|
3029
|
+
PropContainer,
|
|
3030
|
+
{
|
|
3031
|
+
title: "Playback Properties",
|
|
3032
|
+
icon: /* @__PURE__ */ jsx(Music, { className: "w-4 h-4" }),
|
|
3033
|
+
children: /* @__PURE__ */ jsx(
|
|
3034
|
+
PlaybackPropsPanel,
|
|
3035
|
+
{
|
|
3036
|
+
selectedElement,
|
|
3037
|
+
updateElement
|
|
3038
|
+
}
|
|
3039
|
+
)
|
|
3040
|
+
}
|
|
3041
|
+
),
|
|
3042
|
+
selectedProp === "text-effects" && /* @__PURE__ */ jsx(
|
|
3043
|
+
PropContainer,
|
|
3044
|
+
{
|
|
3045
|
+
title: "Text Effects",
|
|
3046
|
+
icon: /* @__PURE__ */ jsx(Sparkles, { className: "w-4 h-4" }),
|
|
3047
|
+
children: /* @__PURE__ */ jsx(
|
|
3048
|
+
TextEffects,
|
|
3049
|
+
{
|
|
3050
|
+
selectedElement,
|
|
3051
|
+
updateElement
|
|
3052
|
+
}
|
|
3053
|
+
)
|
|
3054
|
+
}
|
|
3055
|
+
),
|
|
3056
|
+
selectedProp === "animations" && /* @__PURE__ */ jsx(PropContainer, { title: "Animations", icon: /* @__PURE__ */ jsx(Zap, { className: "w-4 h-4" }), children: /* @__PURE__ */ jsx(
|
|
3057
|
+
Animation,
|
|
3058
|
+
{
|
|
3059
|
+
selectedElement,
|
|
3060
|
+
updateElement
|
|
3061
|
+
}
|
|
3062
|
+
) })
|
|
3063
|
+
] });
|
|
3064
|
+
}
|
|
3065
|
+
const loadFile = (accept) => {
|
|
3066
|
+
return new Promise((resolve, reject) => {
|
|
3067
|
+
try {
|
|
3068
|
+
const input = document.createElement("input");
|
|
3069
|
+
input.type = "file";
|
|
3070
|
+
input.accept = accept;
|
|
3071
|
+
input.style.display = "none";
|
|
3072
|
+
document.body.appendChild(input);
|
|
3073
|
+
const cleanup = () => {
|
|
3074
|
+
input.value = "";
|
|
3075
|
+
document.body.removeChild(input);
|
|
3076
|
+
};
|
|
3077
|
+
input.onchange = () => {
|
|
3078
|
+
const file = input.files && input.files[0];
|
|
3079
|
+
cleanup();
|
|
3080
|
+
if (!file) {
|
|
3081
|
+
reject(new Error("No file selected"));
|
|
3082
|
+
return;
|
|
3083
|
+
}
|
|
3084
|
+
resolve(file);
|
|
3085
|
+
};
|
|
3086
|
+
input.click();
|
|
3087
|
+
} catch (error) {
|
|
3088
|
+
reject(error);
|
|
3089
|
+
}
|
|
3090
|
+
});
|
|
3091
|
+
};
|
|
3092
|
+
const saveAsFile = (content, type, name) => {
|
|
3093
|
+
const blob = typeof content === "string" ? new Blob([content], { type }) : content;
|
|
3094
|
+
const url = URL.createObjectURL(blob);
|
|
3095
|
+
const a = document.createElement("a");
|
|
3096
|
+
a.href = url;
|
|
3097
|
+
a.download = name;
|
|
3098
|
+
a.click();
|
|
3099
|
+
URL.revokeObjectURL(url);
|
|
3100
|
+
};
|
|
3101
|
+
const useStudioOperation = (studioConfig) => {
|
|
3102
|
+
const { editor, present } = useTimelineContext();
|
|
3103
|
+
const [projectName, setProjectName] = useState("");
|
|
3104
|
+
const onLoadProject = async () => {
|
|
3105
|
+
let project;
|
|
3106
|
+
if (studioConfig == null ? void 0 : studioConfig.loadProject) {
|
|
3107
|
+
project = await studioConfig.loadProject();
|
|
3108
|
+
} else {
|
|
3109
|
+
const file = await loadFile("application/json");
|
|
3110
|
+
const text = await file.text();
|
|
3111
|
+
setProjectName(file.name);
|
|
3112
|
+
project = JSON.parse(text);
|
|
3113
|
+
}
|
|
3114
|
+
console.log("Editor", editor);
|
|
3115
|
+
editor.loadProject(project);
|
|
3116
|
+
};
|
|
3117
|
+
const onSaveProject = async () => {
|
|
3118
|
+
let fileName;
|
|
3119
|
+
if (projectName) {
|
|
3120
|
+
fileName = projectName;
|
|
3121
|
+
} else {
|
|
3122
|
+
fileName = prompt("Enter the name of the project") || "untitled-project";
|
|
3123
|
+
fileName = fileName + ".json";
|
|
3124
|
+
setProjectName(fileName);
|
|
3125
|
+
}
|
|
3126
|
+
if ((studioConfig == null ? void 0 : studioConfig.saveProject) && present) {
|
|
3127
|
+
await studioConfig.saveProject(present, fileName);
|
|
3128
|
+
} else {
|
|
3129
|
+
const file = await saveAsFile(
|
|
3130
|
+
JSON.stringify(present),
|
|
3131
|
+
"application/json",
|
|
3132
|
+
fileName
|
|
3133
|
+
);
|
|
3134
|
+
if (file) {
|
|
3135
|
+
console.log("File saved", file);
|
|
3136
|
+
}
|
|
3137
|
+
}
|
|
3138
|
+
};
|
|
3139
|
+
const onExportVideo = async () => {
|
|
3140
|
+
var _a, _b;
|
|
3141
|
+
if ((studioConfig == null ? void 0 : studioConfig.exportVideo) && present) {
|
|
3142
|
+
await studioConfig.exportVideo(present, {
|
|
3143
|
+
outFile: "output.mp4",
|
|
3144
|
+
fps: 30,
|
|
3145
|
+
resolution: {
|
|
3146
|
+
width: (_a = studioConfig.videoProps) == null ? void 0 : _a.width,
|
|
3147
|
+
height: (_b = studioConfig.videoProps) == null ? void 0 : _b.height
|
|
3148
|
+
}
|
|
3149
|
+
});
|
|
3150
|
+
} else {
|
|
3151
|
+
alert("Export video not supported in demo mode");
|
|
3152
|
+
}
|
|
3153
|
+
};
|
|
3154
|
+
return { onLoadProject, onSaveProject, onExportVideo };
|
|
3155
|
+
};
|
|
3156
|
+
function TwickStudio({ studioConfig }) {
|
|
3157
|
+
var _a;
|
|
3158
|
+
const {
|
|
3159
|
+
selectedTool,
|
|
3160
|
+
setSelectedTool,
|
|
3161
|
+
selectedProp,
|
|
3162
|
+
setSelectedProp,
|
|
3163
|
+
selectedElement,
|
|
3164
|
+
addElement,
|
|
3165
|
+
updateElement
|
|
3166
|
+
} = useStudioManager();
|
|
3167
|
+
const { videoResolution, setVideoResolution } = useTimelineContext();
|
|
3168
|
+
const { onLoadProject, onSaveProject, onExportVideo } = useStudioOperation(studioConfig);
|
|
3169
|
+
const twickStudiConfig = useMemo(
|
|
3170
|
+
() => ({
|
|
3171
|
+
canvasMode: true,
|
|
3172
|
+
...studioConfig || {},
|
|
3173
|
+
videoProps: {
|
|
3174
|
+
...(studioConfig == null ? void 0 : studioConfig.videoProps) || {},
|
|
3175
|
+
width: videoResolution.width,
|
|
3176
|
+
height: videoResolution.height
|
|
3177
|
+
}
|
|
3178
|
+
}),
|
|
3179
|
+
[videoResolution, studioConfig]
|
|
3180
|
+
);
|
|
3181
|
+
return /* @__PURE__ */ jsx(MediaProvider, { children: /* @__PURE__ */ jsxs("div", { className: "h-screen w-screen overflow-hidden bg-neutral-900 text-gray-100", children: [
|
|
3182
|
+
/* @__PURE__ */ jsx(
|
|
3183
|
+
StudioHeader,
|
|
3184
|
+
{
|
|
3185
|
+
setVideoResolution,
|
|
3186
|
+
onLoadProject,
|
|
3187
|
+
onSaveProject,
|
|
3188
|
+
onExportVideo
|
|
3189
|
+
}
|
|
3190
|
+
),
|
|
3191
|
+
/* @__PURE__ */ jsxs("div", { className: "flex h-[calc(100vh-56px)]", children: [
|
|
3192
|
+
/* @__PURE__ */ jsx(
|
|
3193
|
+
Toolbar,
|
|
3194
|
+
{
|
|
3195
|
+
selectedTool,
|
|
3196
|
+
setSelectedTool
|
|
3197
|
+
}
|
|
3198
|
+
),
|
|
3199
|
+
/* @__PURE__ */ jsx("aside", { className: "border-r border-gray-600/50 backdrop-blur-md shadow-lg", children: /* @__PURE__ */ jsx(
|
|
3200
|
+
ElementPanelContainer,
|
|
3201
|
+
{
|
|
3202
|
+
videoResolution,
|
|
3203
|
+
selectedTool,
|
|
3204
|
+
setSelectedTool,
|
|
3205
|
+
selectedElement,
|
|
3206
|
+
addElement,
|
|
3207
|
+
updateElement
|
|
3208
|
+
}
|
|
3209
|
+
) }),
|
|
3210
|
+
/* @__PURE__ */ jsx("main", { className: "flex-1 flex flex-col bg-neutral-700 main-container", children: /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto p-1", children: /* @__PURE__ */ jsx(
|
|
3211
|
+
"div",
|
|
3212
|
+
{
|
|
3213
|
+
className: "canvas-container",
|
|
3214
|
+
style: {
|
|
3215
|
+
maxWidth: ((_a = twickStudiConfig.playerProps) == null ? void 0 : _a.maxWidth) ?? 960
|
|
3216
|
+
},
|
|
3217
|
+
children: /* @__PURE__ */ jsx(VideoEditor, { editorConfig: twickStudiConfig })
|
|
3218
|
+
}
|
|
3219
|
+
) }) }),
|
|
3220
|
+
/* @__PURE__ */ jsx("aside", { className: "border-r border-gray-600/50 backdrop-blur-md shadow-lg", children: /* @__PURE__ */ jsx(
|
|
3221
|
+
PropertiesPanelContainer,
|
|
3222
|
+
{
|
|
3223
|
+
selectedProp,
|
|
3224
|
+
selectedElement,
|
|
3225
|
+
updateElement
|
|
3226
|
+
}
|
|
3227
|
+
) }),
|
|
3228
|
+
/* @__PURE__ */ jsx(
|
|
3229
|
+
PropsToolbar,
|
|
3230
|
+
{
|
|
3231
|
+
selectedElement,
|
|
3232
|
+
selectedProp,
|
|
3233
|
+
setSelectedProp
|
|
3234
|
+
}
|
|
3235
|
+
)
|
|
3236
|
+
] })
|
|
3237
|
+
] }) });
|
|
3238
|
+
}
|
|
3239
|
+
export {
|
|
3240
|
+
AudioPanel,
|
|
3241
|
+
CirclePanel,
|
|
3242
|
+
IconPanel,
|
|
3243
|
+
ImagePanel,
|
|
3244
|
+
RectPanel,
|
|
3245
|
+
StudioHeader,
|
|
3246
|
+
SubtitlesPanel,
|
|
3247
|
+
TextPanel,
|
|
3248
|
+
Toolbar,
|
|
3249
|
+
TwickStudio,
|
|
3250
|
+
VideoPanel,
|
|
3251
|
+
TwickStudio as default,
|
|
3252
|
+
useStudioManager
|
|
3253
|
+
};
|
|
3254
|
+
//# sourceMappingURL=index.mjs.map
|