@effectnode/media 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backend/movie-backend/agent/agent-backend.js +4 -4
- package/dist/backend/movie-backend/agent/prompt/story-writer.txt +137 -0
- package/dist/backend/movie-backend/agent/tools/edit-image.d.ts +1 -1
- package/dist/backend/movie-backend/agent/tools/edit-image.js +1 -1
- package/dist/backend/movie-backend/agent/tools/get-time.d.ts +1 -1
- package/dist/backend/movie-backend/agent/tools/grep-files.d.ts +1 -1
- package/dist/backend/movie-backend/agent/tools/grep-files.js +1 -1
- package/dist/backend/movie-backend/agent/tools/image-to-video-generation.d.ts +1 -1
- package/dist/backend/movie-backend/agent/tools/image-to-video-generation.js +1 -1
- package/dist/backend/movie-backend/agent/tools/index.d.ts +2 -2
- package/dist/backend/movie-backend/agent/tools/index.js +13 -13
- package/dist/backend/movie-backend/agent/tools/list-files.d.ts +1 -1
- package/dist/backend/movie-backend/agent/tools/list-files.js +1 -1
- package/dist/backend/movie-backend/agent/tools/read-file.d.ts +1 -1
- package/dist/backend/movie-backend/agent/tools/read-file.js +1 -1
- package/dist/backend/movie-backend/agent/tools/remove-file.d.ts +1 -1
- package/dist/backend/movie-backend/agent/tools/remove-file.js +1 -1
- package/dist/backend/movie-backend/agent/tools/rename-file.d.ts +1 -1
- package/dist/backend/movie-backend/agent/tools/rename-file.js +1 -1
- package/dist/backend/movie-backend/agent/tools/show-image.d.ts +1 -1
- package/dist/backend/movie-backend/agent/tools/show-image.js +1 -1
- package/dist/backend/movie-backend/agent/tools/stitch-videos.d.ts +1 -1
- package/dist/backend/movie-backend/agent/tools/stitch-videos.js +1 -1
- package/dist/backend/movie-backend/agent/tools/text-to-video-generation.d.ts +1 -1
- package/dist/backend/movie-backend/agent/tools/text-to-video-generation.js +1 -1
- package/dist/backend/movie-backend/agent/tools/update-file.d.ts +1 -1
- package/dist/backend/movie-backend/agent/tools/update-file.js +1 -1
- package/dist/backend/movie-backend/agent/tools/write-file.d.ts +1 -1
- package/dist/backend/movie-backend/agent/tools/write-file.js +1 -1
- package/dist/backend/movie-backend/core.js +4 -4
- package/dist/backend/movie-backend/generation-queue.js +102 -46
- package/dist/backend/movie-backend/render-media.js +1 -1
- package/frontend/index.html +8 -1
- package/frontend/public/lambobo.png +0 -0
- package/frontend/src/movie-app/MediaStudio.tsx +6 -2
- package/frontend/src/movie-app/SetupPage.tsx +98 -63
- package/frontend/src/movie-app/components/Aurora.tsx +13 -0
- package/frontend/src/movie-app/components/EditorTabs/GenerateVideoTab.tsx +5 -8
- package/frontend/src/movie-app/components/EditorTabs/MovieStudioTab.tsx +297 -189
- package/frontend/src/movie-app/components/EditorTabs/SetupAiModelTab.tsx +345 -0
- package/frontend/src/movie-app/components/ProjectEditorPage.tsx +40 -34
- package/frontend/src/movie-app/components/ProjectManager.tsx +80 -52
- package/frontend/src/movie-app/index.css +260 -19
- package/frontend/src/movie-app/stores/aiModelStore.ts +163 -0
- package/frontend/src/movie-app/stores/generationStore.ts +2 -2
- package/frontend/src/movie-app/stores/movieStudioStore.ts +45 -0
- package/package.json +2 -2
- package/frontend/src/movie-app/components/EditorTabs/ReferencesToVideoTab.tsx +0 -881
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import { useEffect, type ReactNode } from "react";
|
|
2
|
+
import { useAiModelStore, type AiModelId } from "../../stores/aiModelStore";
|
|
3
|
+
|
|
4
|
+
const DownloadIcon = (
|
|
5
|
+
<svg
|
|
6
|
+
width="14"
|
|
7
|
+
height="14"
|
|
8
|
+
viewBox="0 0 24 24"
|
|
9
|
+
fill="none"
|
|
10
|
+
stroke="currentColor"
|
|
11
|
+
strokeWidth="2"
|
|
12
|
+
strokeLinecap="round"
|
|
13
|
+
strokeLinejoin="round"
|
|
14
|
+
>
|
|
15
|
+
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
|
|
16
|
+
<polyline points="7 10 12 15 17 10" />
|
|
17
|
+
<line x1="12" y1="15" x2="12" y2="3" />
|
|
18
|
+
</svg>
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
const InstallIcon = (
|
|
22
|
+
<svg
|
|
23
|
+
width="14"
|
|
24
|
+
height="14"
|
|
25
|
+
viewBox="0 0 24 24"
|
|
26
|
+
fill="none"
|
|
27
|
+
stroke="currentColor"
|
|
28
|
+
strokeWidth="2"
|
|
29
|
+
strokeLinecap="round"
|
|
30
|
+
strokeLinejoin="round"
|
|
31
|
+
>
|
|
32
|
+
<polyline points="16 18 22 12 16 6" />
|
|
33
|
+
<polyline points="8 6 2 12 8 18" />
|
|
34
|
+
</svg>
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const CheckIcon = (
|
|
38
|
+
<svg
|
|
39
|
+
width="14"
|
|
40
|
+
height="14"
|
|
41
|
+
viewBox="0 0 24 24"
|
|
42
|
+
fill="none"
|
|
43
|
+
stroke="currentColor"
|
|
44
|
+
strokeWidth="2"
|
|
45
|
+
strokeLinecap="round"
|
|
46
|
+
strokeLinejoin="round"
|
|
47
|
+
>
|
|
48
|
+
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" />
|
|
49
|
+
<polyline points="22 4 12 14.01 9 11.01" />
|
|
50
|
+
</svg>
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const AlertIcon = (
|
|
54
|
+
<svg
|
|
55
|
+
width="14"
|
|
56
|
+
height="14"
|
|
57
|
+
viewBox="0 0 24 24"
|
|
58
|
+
fill="none"
|
|
59
|
+
stroke="currentColor"
|
|
60
|
+
strokeWidth="2"
|
|
61
|
+
strokeLinecap="round"
|
|
62
|
+
strokeLinejoin="round"
|
|
63
|
+
>
|
|
64
|
+
<circle cx="12" cy="12" r="10" />
|
|
65
|
+
<line x1="12" y1="8" x2="12" y2="12" />
|
|
66
|
+
<line x1="12" y1="16" x2="12.01" y2="16" />
|
|
67
|
+
</svg>
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const SpinnerIcon = (
|
|
71
|
+
<svg
|
|
72
|
+
className="animate-spin"
|
|
73
|
+
width="14"
|
|
74
|
+
height="14"
|
|
75
|
+
viewBox="0 0 24 24"
|
|
76
|
+
fill="none"
|
|
77
|
+
stroke="currentColor"
|
|
78
|
+
strokeWidth="2"
|
|
79
|
+
>
|
|
80
|
+
<circle cx="12" cy="12" r="10" strokeOpacity="0.25" />
|
|
81
|
+
<path d="M12 2a10 10 0 0 1 10 10" strokeOpacity="0.75" />
|
|
82
|
+
</svg>
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const RefreshIcon = (
|
|
86
|
+
<svg
|
|
87
|
+
width="14"
|
|
88
|
+
height="14"
|
|
89
|
+
viewBox="0 0 24 24"
|
|
90
|
+
fill="none"
|
|
91
|
+
stroke="currentColor"
|
|
92
|
+
strokeWidth="2"
|
|
93
|
+
strokeLinecap="round"
|
|
94
|
+
strokeLinejoin="round"
|
|
95
|
+
>
|
|
96
|
+
<polyline points="23 4 23 10 17 10" />
|
|
97
|
+
<path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10" />
|
|
98
|
+
</svg>
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const ChipIcon = (
|
|
102
|
+
<svg
|
|
103
|
+
width="18"
|
|
104
|
+
height="18"
|
|
105
|
+
viewBox="0 0 24 24"
|
|
106
|
+
fill="none"
|
|
107
|
+
stroke="currentColor"
|
|
108
|
+
strokeWidth="2"
|
|
109
|
+
strokeLinecap="round"
|
|
110
|
+
strokeLinejoin="round"
|
|
111
|
+
>
|
|
112
|
+
<rect x="4" y="4" width="16" height="16" rx="2" ry="2" />
|
|
113
|
+
<rect x="9" y="9" width="6" height="6" />
|
|
114
|
+
<line x1="9" y1="1" x2="9" y2="4" />
|
|
115
|
+
<line x1="15" y1="1" x2="15" y2="4" />
|
|
116
|
+
<line x1="9" y1="20" x2="9" y2="23" />
|
|
117
|
+
<line x1="15" y1="20" x2="15" y2="23" />
|
|
118
|
+
<line x1="20" y1="9" x2="23" y2="9" />
|
|
119
|
+
<line x1="20" y1="14" x2="23" y2="14" />
|
|
120
|
+
<line x1="1" y1="9" x2="4" y2="9" />
|
|
121
|
+
<line x1="1" y1="14" x2="4" y2="14" />
|
|
122
|
+
</svg>
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
function StatusBadge({
|
|
126
|
+
value,
|
|
127
|
+
okText = "Downloaded",
|
|
128
|
+
missingText = "Not downloaded",
|
|
129
|
+
}: {
|
|
130
|
+
value: boolean | null;
|
|
131
|
+
okText?: string;
|
|
132
|
+
missingText?: string;
|
|
133
|
+
}) {
|
|
134
|
+
if (value === null) {
|
|
135
|
+
return (
|
|
136
|
+
<span className="inline-flex items-center gap-1.5 text-xs text-ink-500">
|
|
137
|
+
{SpinnerIcon}
|
|
138
|
+
Checking…
|
|
139
|
+
</span>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
if (value) {
|
|
143
|
+
return (
|
|
144
|
+
<span className="inline-flex items-center gap-1.5 text-xs font-medium text-emerald-600">
|
|
145
|
+
{CheckIcon}
|
|
146
|
+
{okText}
|
|
147
|
+
</span>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
return (
|
|
151
|
+
<span className="inline-flex items-center gap-1.5 text-xs font-medium text-amber-600">
|
|
152
|
+
{AlertIcon}
|
|
153
|
+
{missingText}
|
|
154
|
+
</span>
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function ModelRow({
|
|
159
|
+
name,
|
|
160
|
+
desc,
|
|
161
|
+
children,
|
|
162
|
+
}: {
|
|
163
|
+
name: string;
|
|
164
|
+
desc: string;
|
|
165
|
+
children?: ReactNode;
|
|
166
|
+
}) {
|
|
167
|
+
return (
|
|
168
|
+
<div className="flex items-center gap-3 rounded-2xl border border-ink-200 bg-white px-4 py-3">
|
|
169
|
+
<div className="min-w-0 flex-1">
|
|
170
|
+
<p className="truncate font-mono text-sm text-ink-900">{name}</p>
|
|
171
|
+
<p className="mt-0.5 text-xs text-ink-500">{desc}</p>
|
|
172
|
+
</div>
|
|
173
|
+
{children}
|
|
174
|
+
</div>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const DOWNLOAD_MODELS: {
|
|
179
|
+
id: AiModelId;
|
|
180
|
+
name: string;
|
|
181
|
+
desc: string;
|
|
182
|
+
}[] = [
|
|
183
|
+
{
|
|
184
|
+
id: "z-image",
|
|
185
|
+
name: "AbstractFramework/z-image-turbo-8bit",
|
|
186
|
+
desc: "Text-to-image",
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
id: "flux",
|
|
190
|
+
name: "AbstractFramework/flux.2-klein-4b-8bit",
|
|
191
|
+
desc: "Character, place & scene images",
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
id: "qwen",
|
|
195
|
+
name: "AbstractFramework/qwen-image-edit-2511-8bit",
|
|
196
|
+
desc: "Image editing",
|
|
197
|
+
},
|
|
198
|
+
];
|
|
199
|
+
|
|
200
|
+
export default function SetupAiModelTab() {
|
|
201
|
+
const store = useAiModelStore();
|
|
202
|
+
|
|
203
|
+
useEffect(() => {
|
|
204
|
+
store.checkStatus();
|
|
205
|
+
const interval = setInterval(() => store.checkStatus(), 3000);
|
|
206
|
+
return () => clearInterval(interval);
|
|
207
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
208
|
+
}, []);
|
|
209
|
+
|
|
210
|
+
const busy = store.downloading !== null;
|
|
211
|
+
|
|
212
|
+
return (
|
|
213
|
+
<div className="flex flex-col gap-6">
|
|
214
|
+
{/* Header */}
|
|
215
|
+
<div className="flex items-center gap-2">
|
|
216
|
+
<span className="text-tiffany-600">{ChipIcon}</span>
|
|
217
|
+
<h2 className="text-base font-semibold text-ink-900">AI Models</h2>
|
|
218
|
+
<button
|
|
219
|
+
onClick={() => store.checkStatus()}
|
|
220
|
+
className="ml-auto flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-xl border border-ink-200 text-ink-600 hover:border-ink-300 hover:text-ink-900 transition-colors"
|
|
221
|
+
>
|
|
222
|
+
{RefreshIcon}
|
|
223
|
+
Refresh
|
|
224
|
+
</button>
|
|
225
|
+
</div>
|
|
226
|
+
|
|
227
|
+
{/* Engines */}
|
|
228
|
+
<section>
|
|
229
|
+
<h3 className="mb-2 text-sm font-semibold text-ink-900">Engines</h3>
|
|
230
|
+
<div className="space-y-2">
|
|
231
|
+
<ModelRow name="mlx-gen" desc="Image generation engine (z-image · flux · qwen)">
|
|
232
|
+
<StatusBadge
|
|
233
|
+
value={store.mlxgenInstalled}
|
|
234
|
+
okText="Installed"
|
|
235
|
+
missingText="Not installed"
|
|
236
|
+
/>
|
|
237
|
+
<button
|
|
238
|
+
onClick={() => store.installTool("mlxgen")}
|
|
239
|
+
disabled={busy}
|
|
240
|
+
className="flex items-center gap-1.5 whitespace-nowrap rounded-xl border border-tiffany-500 px-3 py-1.5 text-xs font-medium text-tiffany-700 transition-colors hover:bg-tiffany-50 disabled:opacity-50"
|
|
241
|
+
>
|
|
242
|
+
{store.downloading === "mlxgen" ? SpinnerIcon : InstallIcon}
|
|
243
|
+
{store.downloading === "mlxgen" ? "Installing…" : "Install"}
|
|
244
|
+
</button>
|
|
245
|
+
</ModelRow>
|
|
246
|
+
|
|
247
|
+
<ModelRow name="mlx-vlm" desc="LLM server (gemma) for story planning">
|
|
248
|
+
<StatusBadge
|
|
249
|
+
value={store.mlxVlmInstalled}
|
|
250
|
+
okText="Installed"
|
|
251
|
+
missingText="Not installed"
|
|
252
|
+
/>
|
|
253
|
+
<button
|
|
254
|
+
onClick={() => store.installTool("mlx-vlm")}
|
|
255
|
+
disabled={busy}
|
|
256
|
+
className="flex items-center gap-1.5 whitespace-nowrap rounded-xl border border-tiffany-500 px-3 py-1.5 text-xs font-medium text-tiffany-700 transition-colors hover:bg-tiffany-50 disabled:opacity-50"
|
|
257
|
+
>
|
|
258
|
+
{store.downloading === "mlx-vlm" ? SpinnerIcon : InstallIcon}
|
|
259
|
+
{store.downloading === "mlx-vlm" ? "Installing…" : "Install"}
|
|
260
|
+
</button>
|
|
261
|
+
</ModelRow>
|
|
262
|
+
</div>
|
|
263
|
+
</section>
|
|
264
|
+
|
|
265
|
+
{/* Models */}
|
|
266
|
+
<section>
|
|
267
|
+
<h3 className="mb-2 text-sm font-semibold text-ink-900">Models</h3>
|
|
268
|
+
<div className="space-y-2">
|
|
269
|
+
<ModelRow
|
|
270
|
+
name="mlx-community/gemma-4-e2b-it-4bit"
|
|
271
|
+
desc="Story planning & dialogue (LLM)"
|
|
272
|
+
>
|
|
273
|
+
<StatusBadge
|
|
274
|
+
value={store.mlxVlmInstalled}
|
|
275
|
+
okText="Ready"
|
|
276
|
+
missingText="Not set up"
|
|
277
|
+
/>
|
|
278
|
+
<span className="text-xs italic text-ink-400">
|
|
279
|
+
Downloads on first server start
|
|
280
|
+
</span>
|
|
281
|
+
</ModelRow>
|
|
282
|
+
|
|
283
|
+
{DOWNLOAD_MODELS.map((m) => {
|
|
284
|
+
const downloaded =
|
|
285
|
+
m.id === "z-image"
|
|
286
|
+
? store.zImageDownloaded
|
|
287
|
+
: m.id === "flux"
|
|
288
|
+
? store.fluxDownloaded
|
|
289
|
+
: store.qwenDownloaded;
|
|
290
|
+
return (
|
|
291
|
+
<ModelRow key={m.id} name={m.name} desc={m.desc}>
|
|
292
|
+
<StatusBadge value={downloaded} />
|
|
293
|
+
<button
|
|
294
|
+
onClick={() => store.downloadModel(m.id)}
|
|
295
|
+
disabled={busy}
|
|
296
|
+
className="flex items-center gap-1.5 whitespace-nowrap rounded-xl bg-tiffany-500 px-3 py-1.5 text-xs font-medium text-ink-950 transition-colors hover:bg-tiffany-600 disabled:opacity-50"
|
|
297
|
+
>
|
|
298
|
+
{store.downloading === m.id ? SpinnerIcon : DownloadIcon}
|
|
299
|
+
{store.downloading === m.id ? "Downloading…" : "Download"}
|
|
300
|
+
</button>
|
|
301
|
+
</ModelRow>
|
|
302
|
+
);
|
|
303
|
+
})}
|
|
304
|
+
|
|
305
|
+
<ModelRow
|
|
306
|
+
name="dgrauet/ltx-2.3-mlx-q8"
|
|
307
|
+
desc="Video generation"
|
|
308
|
+
>
|
|
309
|
+
<span className="text-xs italic text-ink-400">
|
|
310
|
+
Downloads on first render
|
|
311
|
+
</span>
|
|
312
|
+
</ModelRow>
|
|
313
|
+
|
|
314
|
+
<ModelRow
|
|
315
|
+
name="Qwen/Qwen3-TTS-12Hz-1.7B-Base"
|
|
316
|
+
desc="Voice-over & speech"
|
|
317
|
+
>
|
|
318
|
+
<span className="text-xs italic text-ink-400">
|
|
319
|
+
Downloads on first use
|
|
320
|
+
</span>
|
|
321
|
+
</ModelRow>
|
|
322
|
+
</div>
|
|
323
|
+
</section>
|
|
324
|
+
|
|
325
|
+
{/* Error */}
|
|
326
|
+
{store.error && (
|
|
327
|
+
<div className="rounded-2xl border border-rose-200 bg-rose-50/70 px-4 py-3 text-xs text-rose-600">
|
|
328
|
+
{store.error}
|
|
329
|
+
</div>
|
|
330
|
+
)}
|
|
331
|
+
|
|
332
|
+
{/* Download logs */}
|
|
333
|
+
{store.logs.length > 0 && (
|
|
334
|
+
<div className="rounded-2xl border border-ink-200 bg-ink-50 p-4">
|
|
335
|
+
<p className="mb-2 text-[11px] font-semibold uppercase tracking-wider text-ink-500">
|
|
336
|
+
Output
|
|
337
|
+
</p>
|
|
338
|
+
<pre className="max-h-48 overflow-y-auto whitespace-pre-wrap font-mono text-xs leading-relaxed text-ink-700">
|
|
339
|
+
{store.logs.join("")}
|
|
340
|
+
</pre>
|
|
341
|
+
</div>
|
|
342
|
+
)}
|
|
343
|
+
</div>
|
|
344
|
+
);
|
|
345
|
+
}
|
|
@@ -12,11 +12,11 @@ import CharactersTab from "./EditorTabs/CharactersTab";
|
|
|
12
12
|
import ExtractImageTab from "./EditorTabs/ExtractImageTab";
|
|
13
13
|
import SceneVisualTab from "./EditorTabs/SceneVisualTab";
|
|
14
14
|
import TextToImageTab from "./EditorTabs/TextToImageTab";
|
|
15
|
-
import ReferencesToVideoTab from "./EditorTabs/ReferencesToVideoTab";
|
|
16
15
|
import BatchVideoTab from "./EditorTabs/BatchVideoTab";
|
|
17
16
|
import BatchImageToVideoTab from "./EditorTabs/BatchImageToVideoTab";
|
|
18
17
|
import BatchVoiceVideoTab from "./EditorTabs/BatchVoiceVideoTab";
|
|
19
18
|
import LlmServerTab from "./EditorTabs/LlmServerTab";
|
|
19
|
+
import SetupAiModelTab from "./EditorTabs/SetupAiModelTab";
|
|
20
20
|
|
|
21
21
|
export default function ProjectEditorPage() {
|
|
22
22
|
const { id } = useParams<{ id: string }>();
|
|
@@ -237,22 +237,6 @@ export default function ProjectEditorPage() {
|
|
|
237
237
|
</svg>
|
|
238
238
|
);
|
|
239
239
|
|
|
240
|
-
const ReferencesToVideoIcon = (
|
|
241
|
-
<svg
|
|
242
|
-
width="18"
|
|
243
|
-
height="18"
|
|
244
|
-
viewBox="0 0 24 24"
|
|
245
|
-
fill="none"
|
|
246
|
-
stroke="currentColor"
|
|
247
|
-
strokeWidth="2"
|
|
248
|
-
strokeLinecap="round"
|
|
249
|
-
strokeLinejoin="round"
|
|
250
|
-
>
|
|
251
|
-
<path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20" />
|
|
252
|
-
<path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z" />
|
|
253
|
-
</svg>
|
|
254
|
-
);
|
|
255
|
-
|
|
256
240
|
const BatchVideoIcon = (
|
|
257
241
|
<svg
|
|
258
242
|
width="18"
|
|
@@ -335,6 +319,30 @@ export default function ProjectEditorPage() {
|
|
|
335
319
|
/>
|
|
336
320
|
);
|
|
337
321
|
|
|
322
|
+
const AiModelIcon = (
|
|
323
|
+
<svg
|
|
324
|
+
width="18"
|
|
325
|
+
height="18"
|
|
326
|
+
viewBox="0 0 24 24"
|
|
327
|
+
fill="none"
|
|
328
|
+
stroke="currentColor"
|
|
329
|
+
strokeWidth="2"
|
|
330
|
+
strokeLinecap="round"
|
|
331
|
+
strokeLinejoin="round"
|
|
332
|
+
>
|
|
333
|
+
<rect x="4" y="4" width="16" height="16" rx="2" ry="2" />
|
|
334
|
+
<rect x="9" y="9" width="6" height="6" />
|
|
335
|
+
<line x1="9" y1="1" x2="9" y2="4" />
|
|
336
|
+
<line x1="15" y1="1" x2="15" y2="4" />
|
|
337
|
+
<line x1="9" y1="20" x2="9" y2="23" />
|
|
338
|
+
<line x1="15" y1="20" x2="15" y2="23" />
|
|
339
|
+
<line x1="20" y1="9" x2="23" y2="9" />
|
|
340
|
+
<line x1="20" y1="14" x2="23" y2="14" />
|
|
341
|
+
<line x1="1" y1="9" x2="4" y2="9" />
|
|
342
|
+
<line x1="1" y1="14" x2="4" y2="14" />
|
|
343
|
+
</svg>
|
|
344
|
+
);
|
|
345
|
+
|
|
338
346
|
// ========== Loading / Not Found ==========
|
|
339
347
|
|
|
340
348
|
if (!project) {
|
|
@@ -417,7 +425,7 @@ export default function ProjectEditorPage() {
|
|
|
417
425
|
}`}
|
|
418
426
|
>
|
|
419
427
|
{VideoIcon}
|
|
420
|
-
Generate Avatar
|
|
428
|
+
Generate Avatar Video
|
|
421
429
|
</button>
|
|
422
430
|
<button
|
|
423
431
|
onClick={() => store.setActiveTab("extend")}
|
|
@@ -507,17 +515,6 @@ export default function ProjectEditorPage() {
|
|
|
507
515
|
{TextToImageIcon}
|
|
508
516
|
Text-to-Image
|
|
509
517
|
</button>
|
|
510
|
-
<button
|
|
511
|
-
onClick={() => store.setActiveTab("referencesToVideo")}
|
|
512
|
-
className={`flex items-center gap-2 px-5 py-2.5 text-sm font-medium rounded-2xl transition-all ${
|
|
513
|
-
store.activeTab === "referencesToVideo"
|
|
514
|
-
? "bg-tiffany-500/10 text-tiffany-700 shadow-glow-sm"
|
|
515
|
-
: "text-ink-600 hover:bg-ink-100 hover:text-ink-800"
|
|
516
|
-
}`}
|
|
517
|
-
>
|
|
518
|
-
{ReferencesToVideoIcon}
|
|
519
|
-
References to Video
|
|
520
|
-
</button>
|
|
521
518
|
<button
|
|
522
519
|
onClick={() => store.setActiveTab("batchVideo")}
|
|
523
520
|
className={`flex items-center gap-2 px-5 py-2.5 text-sm font-medium rounded-2xl transition-all ${
|
|
@@ -563,6 +560,17 @@ export default function ProjectEditorPage() {
|
|
|
563
560
|
LLM Server
|
|
564
561
|
<ServerStatusLight />
|
|
565
562
|
</button>
|
|
563
|
+
<button
|
|
564
|
+
onClick={() => store.setActiveTab("aiModels")}
|
|
565
|
+
className={`flex items-center gap-2 px-5 py-2.5 text-sm font-medium rounded-2xl transition-all ${
|
|
566
|
+
store.activeTab === "aiModels"
|
|
567
|
+
? "bg-tiffany-500/10 text-tiffany-700 shadow-glow-sm"
|
|
568
|
+
: "text-ink-600 hover:bg-ink-100 hover:text-ink-800"
|
|
569
|
+
}`}
|
|
570
|
+
>
|
|
571
|
+
{AiModelIcon}
|
|
572
|
+
Setup AI Models
|
|
573
|
+
</button>
|
|
566
574
|
</div>
|
|
567
575
|
|
|
568
576
|
{/* ========== MOVIE STUDIO PANEL ========== */}
|
|
@@ -607,11 +615,6 @@ export default function ProjectEditorPage() {
|
|
|
607
615
|
<TextToImageTab projectId={id!} />
|
|
608
616
|
)}
|
|
609
617
|
|
|
610
|
-
{/* ========== REFERENCES TO VIDEO PANEL ========== */}
|
|
611
|
-
{store.activeTab === "referencesToVideo" && (
|
|
612
|
-
<ReferencesToVideoTab projectId={id!} />
|
|
613
|
-
)}
|
|
614
|
-
|
|
615
618
|
{/* ========== BATCH VIDEO PANEL ========== */}
|
|
616
619
|
{store.activeTab === "batchVideo" && (
|
|
617
620
|
<BatchVideoTab projectId={id!} />
|
|
@@ -629,6 +632,9 @@ export default function ProjectEditorPage() {
|
|
|
629
632
|
|
|
630
633
|
{/* ========== LLM SERVER PANEL ========== */}
|
|
631
634
|
{store.activeTab === "llmServer" && <LlmServerTab />}
|
|
635
|
+
|
|
636
|
+
{/* ========== SETUP AI MODELS PANEL ========== */}
|
|
637
|
+
{store.activeTab === "aiModels" && <SetupAiModelTab />}
|
|
632
638
|
</div>
|
|
633
639
|
</div>
|
|
634
640
|
</div>
|