@effectnode/media 0.1.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.
Files changed (133) hide show
  1. package/bin/effectnode-media +2 -0
  2. package/dist/backend/index.d.ts +8 -0
  3. package/dist/backend/index.js +13 -0
  4. package/dist/backend/movie-backend/agent/agent-backend.d.ts +16 -0
  5. package/dist/backend/movie-backend/agent/agent-backend.js +703 -0
  6. package/dist/backend/movie-backend/agent/storyprompt.d.ts +1 -0
  7. package/dist/backend/movie-backend/agent/storyprompt.js +90 -0
  8. package/dist/backend/movie-backend/agent/tools/edit-image.d.ts +3 -0
  9. package/dist/backend/movie-backend/agent/tools/edit-image.js +192 -0
  10. package/dist/backend/movie-backend/agent/tools/get-time.d.ts +3 -0
  11. package/dist/backend/movie-backend/agent/tools/get-time.js +7 -0
  12. package/dist/backend/movie-backend/agent/tools/grep-files.d.ts +3 -0
  13. package/dist/backend/movie-backend/agent/tools/grep-files.js +59 -0
  14. package/dist/backend/movie-backend/agent/tools/image-to-video-generation.d.ts +3 -0
  15. package/dist/backend/movie-backend/agent/tools/image-to-video-generation.js +160 -0
  16. package/dist/backend/movie-backend/agent/tools/index.d.ts +7 -0
  17. package/dist/backend/movie-backend/agent/tools/index.js +59 -0
  18. package/dist/backend/movie-backend/agent/tools/list-files.d.ts +3 -0
  19. package/dist/backend/movie-backend/agent/tools/list-files.js +17 -0
  20. package/dist/backend/movie-backend/agent/tools/read-file.d.ts +3 -0
  21. package/dist/backend/movie-backend/agent/tools/read-file.js +28 -0
  22. package/dist/backend/movie-backend/agent/tools/remove-file.d.ts +3 -0
  23. package/dist/backend/movie-backend/agent/tools/remove-file.js +22 -0
  24. package/dist/backend/movie-backend/agent/tools/rename-file.d.ts +3 -0
  25. package/dist/backend/movie-backend/agent/tools/rename-file.js +37 -0
  26. package/dist/backend/movie-backend/agent/tools/show-image.d.ts +3 -0
  27. package/dist/backend/movie-backend/agent/tools/show-image.js +28 -0
  28. package/dist/backend/movie-backend/agent/tools/stitch-videos.d.ts +3 -0
  29. package/dist/backend/movie-backend/agent/tools/stitch-videos.js +96 -0
  30. package/dist/backend/movie-backend/agent/tools/text-to-video-generation.d.ts +3 -0
  31. package/dist/backend/movie-backend/agent/tools/text-to-video-generation.js +125 -0
  32. package/dist/backend/movie-backend/agent/tools/types.d.ts +15 -0
  33. package/dist/backend/movie-backend/agent/tools/types.js +1 -0
  34. package/dist/backend/movie-backend/agent/tools/update-file.d.ts +3 -0
  35. package/dist/backend/movie-backend/agent/tools/update-file.js +27 -0
  36. package/dist/backend/movie-backend/agent/tools/write-file.d.ts +3 -0
  37. package/dist/backend/movie-backend/agent/tools/write-file.js +26 -0
  38. package/dist/backend/movie-backend/agent/workspace.d.ts +25 -0
  39. package/dist/backend/movie-backend/agent/workspace.js +124 -0
  40. package/dist/backend/movie-backend/core.d.ts +4 -0
  41. package/dist/backend/movie-backend/core.js +1167 -0
  42. package/dist/backend/movie-backend/generation-queue.d.ts +24 -0
  43. package/dist/backend/movie-backend/generation-queue.js +766 -0
  44. package/dist/backend/movie-backend/process.d.ts +21 -0
  45. package/dist/backend/movie-backend/process.js +98 -0
  46. package/dist/backend/movie-backend/render-media.d.ts +48 -0
  47. package/dist/backend/movie-backend/render-media.js +3311 -0
  48. package/dist/backend/routes/health.d.ts +1 -0
  49. package/dist/backend/routes/health.js +9 -0
  50. package/dist/backend/routes/index.d.ts +1 -0
  51. package/dist/backend/routes/index.js +11 -0
  52. package/dist/backend/routes/media.d.ts +1 -0
  53. package/dist/backend/routes/media.js +13 -0
  54. package/dist/backend/routes/projects.d.ts +8 -0
  55. package/dist/backend/routes/projects.js +68 -0
  56. package/dist/backend/routes/voice.d.ts +1 -0
  57. package/dist/backend/routes/voice.js +39 -0
  58. package/dist/backend/routes.d.ts +1 -0
  59. package/dist/backend/routes.js +20 -0
  60. package/dist/backend/server.d.ts +1 -0
  61. package/dist/backend/server.js +23 -0
  62. package/dist/backend/workspace.d.ts +5 -0
  63. package/dist/backend/workspace.js +27 -0
  64. package/dist/backend/ws/index.d.ts +3 -0
  65. package/dist/backend/ws/index.js +27 -0
  66. package/dist/backend/ws/messages.d.ts +8 -0
  67. package/dist/backend/ws/messages.js +21 -0
  68. package/dist/backend/ws.d.ts +3 -0
  69. package/dist/backend/ws.js +29 -0
  70. package/dist/index.d.ts +2 -0
  71. package/dist/index.js +80 -0
  72. package/frontend/index.html +22 -0
  73. package/frontend/public/ffmpeg/ffmpeg-core.js +16 -0
  74. package/frontend/public/ffmpeg/ffmpeg-core.wasm +0 -0
  75. package/frontend/src/index.css +46 -0
  76. package/frontend/src/main.tsx +13 -0
  77. package/frontend/src/movie-app/AppRouter.tsx +14 -0
  78. package/frontend/src/movie-app/MediaStudio.tsx +9 -0
  79. package/frontend/src/movie-app/SetupPage.tsx +293 -0
  80. package/frontend/src/movie-app/components/AgentWorkspace.tsx +574 -0
  81. package/frontend/src/movie-app/components/ChatUI/ChatUI.tsx +655 -0
  82. package/frontend/src/movie-app/components/ChatUI/MarkdownMessage.tsx +267 -0
  83. package/frontend/src/movie-app/components/EditorTabs/AgentTab.tsx +61 -0
  84. package/frontend/src/movie-app/components/EditorTabs/BatchImageToVideoTab.tsx +927 -0
  85. package/frontend/src/movie-app/components/EditorTabs/BatchVideoTab.tsx +777 -0
  86. package/frontend/src/movie-app/components/EditorTabs/BatchVoiceVideoTab.tsx +913 -0
  87. package/frontend/src/movie-app/components/EditorTabs/CharacterSheet.tsx +234 -0
  88. package/frontend/src/movie-app/components/EditorTabs/CharactersTab.tsx +664 -0
  89. package/frontend/src/movie-app/components/EditorTabs/CropTool.tsx +251 -0
  90. package/frontend/src/movie-app/components/EditorTabs/ExtendVideoTab.tsx +305 -0
  91. package/frontend/src/movie-app/components/EditorTabs/ExtractImageTab.tsx +249 -0
  92. package/frontend/src/movie-app/components/EditorTabs/FastImageEditTab.tsx +555 -0
  93. package/frontend/src/movie-app/components/EditorTabs/GenerateVideoTab.tsx +722 -0
  94. package/frontend/src/movie-app/components/EditorTabs/LlmServerTab.tsx +40 -0
  95. package/frontend/src/movie-app/components/EditorTabs/MlxVlmServerPanel.tsx +490 -0
  96. package/frontend/src/movie-app/components/EditorTabs/MovieStudioTab.tsx +938 -0
  97. package/frontend/src/movie-app/components/EditorTabs/ReferencesToVideoTab.tsx +881 -0
  98. package/frontend/src/movie-app/components/EditorTabs/SceneVisualTab.tsx +267 -0
  99. package/frontend/src/movie-app/components/EditorTabs/StoryWriterTab.tsx +51 -0
  100. package/frontend/src/movie-app/components/EditorTabs/TaskQueuePanel.tsx +246 -0
  101. package/frontend/src/movie-app/components/EditorTabs/TerminalLogPanel.tsx +49 -0
  102. package/frontend/src/movie-app/components/EditorTabs/TextToImageTab.tsx +574 -0
  103. package/frontend/src/movie-app/components/EditorTabs/VoiceChatPanel.tsx +320 -0
  104. package/frontend/src/movie-app/components/ProjectEditorPage.tsx +636 -0
  105. package/frontend/src/movie-app/components/ProjectManager.tsx +381 -0
  106. package/frontend/src/movie-app/index.css +46 -0
  107. package/frontend/src/movie-app/index.html +16 -0
  108. package/frontend/src/movie-app/lambobo.png +0 -0
  109. package/frontend/src/movie-app/lib/batchImageToVideoStorage.ts +69 -0
  110. package/frontend/src/movie-app/lib/batchVideoStorage.ts +68 -0
  111. package/frontend/src/movie-app/lib/batchVoiceStorage.ts +75 -0
  112. package/frontend/src/movie-app/lib/ffmpeg.ts +42 -0
  113. package/frontend/src/movie-app/lib/referencesToVideoStorage.ts +64 -0
  114. package/frontend/src/movie-app/lib/textToImageStorage.ts +61 -0
  115. package/frontend/src/movie-app/public/ffmpeg/ffmpeg-core.js +16 -0
  116. package/frontend/src/movie-app/public/ffmpeg/ffmpeg-core.wasm +0 -0
  117. package/frontend/src/movie-app/stores/batchImageToVideoStore.ts +736 -0
  118. package/frontend/src/movie-app/stores/batchVideoStore.ts +670 -0
  119. package/frontend/src/movie-app/stores/batchVoiceStore.ts +990 -0
  120. package/frontend/src/movie-app/stores/characterStore.ts +109 -0
  121. package/frontend/src/movie-app/stores/chatStore.ts +459 -0
  122. package/frontend/src/movie-app/stores/generationStore.ts +2140 -0
  123. package/frontend/src/movie-app/stores/logStore.ts +25 -0
  124. package/frontend/src/movie-app/stores/movieStudioStore.ts +709 -0
  125. package/frontend/src/movie-app/stores/projectStore.ts +124 -0
  126. package/frontend/src/movie-app/stores/queueStore.ts +214 -0
  127. package/frontend/src/movie-app/stores/referencesToVideoStore.ts +467 -0
  128. package/frontend/src/movie-app/stores/sceneVisualStore.ts +251 -0
  129. package/frontend/src/movie-app/stores/voiceChatStore.ts +170 -0
  130. package/frontend/src/movie-app/stores/workspaceStore.ts +158 -0
  131. package/frontend/src/vite-env.d.ts +1 -0
  132. package/frontend/tsconfig.json +19 -0
  133. package/package.json +65 -0
@@ -0,0 +1,636 @@
1
+ import { useEffect, useState } from "react";
2
+ import { useParams, useNavigate } from "react-router-dom";
3
+ import { useProjectStore, type Project } from "../stores/projectStore";
4
+ import { useGenerationStore } from "../stores/generationStore";
5
+ import ExtendVideoTab from "./EditorTabs/ExtendVideoTab";
6
+ import FastImageEditTab from "./EditorTabs/FastImageEditTab";
7
+ import MovieStudioTab from "./EditorTabs/MovieStudioTab";
8
+ import GenerateVideoTab from "./EditorTabs/GenerateVideoTab";
9
+ import AgentTab from "./EditorTabs/AgentTab";
10
+ import StoryWriterTab from "./EditorTabs/StoryWriterTab";
11
+ import CharactersTab from "./EditorTabs/CharactersTab";
12
+ import ExtractImageTab from "./EditorTabs/ExtractImageTab";
13
+ import SceneVisualTab from "./EditorTabs/SceneVisualTab";
14
+ import TextToImageTab from "./EditorTabs/TextToImageTab";
15
+ import ReferencesToVideoTab from "./EditorTabs/ReferencesToVideoTab";
16
+ import BatchVideoTab from "./EditorTabs/BatchVideoTab";
17
+ import BatchImageToVideoTab from "./EditorTabs/BatchImageToVideoTab";
18
+ import BatchVoiceVideoTab from "./EditorTabs/BatchVoiceVideoTab";
19
+ import LlmServerTab from "./EditorTabs/LlmServerTab";
20
+
21
+ export default function ProjectEditorPage() {
22
+ const { id } = useParams<{ id: string }>();
23
+ const navigate = useNavigate();
24
+ const { projects, fetchProjects } = useProjectStore();
25
+ const [project, setProject] = useState<Project | null>(null);
26
+
27
+ // Zustand generation store
28
+ const store = useGenerationStore();
29
+
30
+ useEffect(() => {
31
+ if (projects.length === 0) {
32
+ fetchProjects();
33
+ }
34
+ }, []);
35
+
36
+ useEffect(() => {
37
+ const found = projects.find((p) => p.id === id) || null;
38
+ setProject(found);
39
+ }, [id, projects]);
40
+
41
+ // Fetch project images and videos on mount
42
+ useEffect(() => {
43
+ if (id) {
44
+ store.fetchProjectImages(id);
45
+ store.fetchProjectVideos(id);
46
+ }
47
+ }, [id]);
48
+
49
+ // Poll the LLM server status so the tab label light stays accurate
50
+ // regardless of which tab is active.
51
+ useEffect(() => {
52
+ store.checkAgentStatus();
53
+ store.checkServerOnline();
54
+ const interval = setInterval(() => {
55
+ store.checkServerOnline();
56
+ }, 3000);
57
+ return () => clearInterval(interval);
58
+ // eslint-disable-next-line react-hooks/exhaustive-deps
59
+ }, []);
60
+
61
+ // ========== SVG Icons ==========
62
+
63
+ const BackIcon = (
64
+ <svg
65
+ width="16"
66
+ height="16"
67
+ viewBox="0 0 24 24"
68
+ fill="none"
69
+ stroke="currentColor"
70
+ strokeWidth="2"
71
+ >
72
+ <line x1="19" y1="12" x2="5" y2="12" />
73
+ <polyline points="12 19 5 12 12 5" />
74
+ </svg>
75
+ );
76
+
77
+ const VideoIcon = (
78
+ <svg
79
+ width="18"
80
+ height="18"
81
+ viewBox="0 0 24 24"
82
+ fill="none"
83
+ stroke="currentColor"
84
+ strokeWidth="2"
85
+ >
86
+ <polygon points="23 7 16 12 23 17 23 7" />
87
+ <rect x="1" y="5" width="15" height="14" rx="2" ry="2" />
88
+ </svg>
89
+ );
90
+
91
+ const ExtendIcon = (
92
+ <svg
93
+ width="18"
94
+ height="18"
95
+ viewBox="0 0 24 24"
96
+ fill="none"
97
+ stroke="currentColor"
98
+ strokeWidth="2"
99
+ strokeLinecap="round"
100
+ strokeLinejoin="round"
101
+ >
102
+ <polygon points="23 7 16 12 23 17 23 7" />
103
+ <rect x="1" y="5" width="15" height="14" rx="2" ry="2" />
104
+ <line x1="5" y1="12" x2="11" y2="12" />
105
+ <line x1="8" y1="9" x2="8" y2="15" />
106
+ </svg>
107
+ );
108
+
109
+ const FastImageEditIcon = (
110
+ <svg
111
+ width="18"
112
+ height="18"
113
+ viewBox="0 0 24 24"
114
+ fill="none"
115
+ stroke="currentColor"
116
+ strokeWidth="2"
117
+ strokeLinecap="round"
118
+ strokeLinejoin="round"
119
+ >
120
+ <polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2" />
121
+ </svg>
122
+ );
123
+
124
+ const MovieStudioIcon = (
125
+ <svg
126
+ width="18"
127
+ height="18"
128
+ viewBox="0 0 24 24"
129
+ fill="none"
130
+ stroke="currentColor"
131
+ strokeWidth="2"
132
+ strokeLinecap="round"
133
+ strokeLinejoin="round"
134
+ >
135
+ <path 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" />
136
+ <path d="m6.2 5.3 3.1 3.9" />
137
+ <path d="m12.4 3.4 3.1 4" />
138
+ <path d="M3 11h18v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2Z" />
139
+ </svg>
140
+ );
141
+
142
+ const AgentIcon = (
143
+ <svg
144
+ width="18"
145
+ height="18"
146
+ viewBox="0 0 24 24"
147
+ fill="none"
148
+ stroke="currentColor"
149
+ strokeWidth="2"
150
+ strokeLinecap="round"
151
+ strokeLinejoin="round"
152
+ >
153
+ <rect x="4" y="4" width="16" height="16" rx="2" />
154
+ <circle cx="9" cy="9" r="1" />
155
+ <circle cx="15" cy="9" r="1" />
156
+ <line x1="9" y1="14" x2="15" y2="14" />
157
+ </svg>
158
+ );
159
+
160
+ const StoryWriterIcon = (
161
+ <svg
162
+ width="18"
163
+ height="18"
164
+ viewBox="0 0 24 24"
165
+ fill="none"
166
+ stroke="currentColor"
167
+ strokeWidth="2"
168
+ strokeLinecap="round"
169
+ strokeLinejoin="round"
170
+ >
171
+ <path d="M12 20h9" />
172
+ <path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z" />
173
+ </svg>
174
+ );
175
+
176
+ const CharacterIcon = (
177
+ <svg
178
+ width="18"
179
+ height="18"
180
+ viewBox="0 0 24 24"
181
+ fill="none"
182
+ stroke="currentColor"
183
+ strokeWidth="2"
184
+ strokeLinecap="round"
185
+ strokeLinejoin="round"
186
+ >
187
+ <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" />
188
+ <circle cx="12" cy="7" r="4" />
189
+ </svg>
190
+ );
191
+
192
+ const ExtractIcon = (
193
+ <svg
194
+ width="18"
195
+ height="18"
196
+ viewBox="0 0 24 24"
197
+ fill="none"
198
+ stroke="currentColor"
199
+ strokeWidth="2"
200
+ strokeLinecap="round"
201
+ strokeLinejoin="round"
202
+ >
203
+ <path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z" />
204
+ <circle cx="12" cy="13" r="4" />
205
+ </svg>
206
+ );
207
+
208
+ const SceneVisualIcon = (
209
+ <svg
210
+ width="18"
211
+ height="18"
212
+ viewBox="0 0 24 24"
213
+ fill="none"
214
+ stroke="currentColor"
215
+ strokeWidth="2"
216
+ strokeLinecap="round"
217
+ strokeLinejoin="round"
218
+ >
219
+ <rect x="3" y="3" width="18" height="18" rx="2" ry="2" />
220
+ <circle cx="8.5" cy="8.5" r="1.5" />
221
+ <polyline points="21 15 16 10 5 21" />
222
+ </svg>
223
+ );
224
+
225
+ const TextToImageIcon = (
226
+ <svg
227
+ width="18"
228
+ height="18"
229
+ viewBox="0 0 24 24"
230
+ fill="none"
231
+ stroke="currentColor"
232
+ strokeWidth="2"
233
+ strokeLinecap="round"
234
+ strokeLinejoin="round"
235
+ >
236
+ <polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" />
237
+ </svg>
238
+ );
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
+ const BatchVideoIcon = (
257
+ <svg
258
+ width="18"
259
+ height="18"
260
+ viewBox="0 0 24 24"
261
+ fill="none"
262
+ stroke="currentColor"
263
+ strokeWidth="2"
264
+ strokeLinecap="round"
265
+ strokeLinejoin="round"
266
+ >
267
+ <rect x="3" y="3" width="18" height="18" rx="2" ry="2" />
268
+ <line x1="3" y1="9" x2="21" y2="9" />
269
+ <line x1="3" y1="15" x2="21" y2="15" />
270
+ <line x1="9" y1="3" x2="9" y2="21" />
271
+ </svg>
272
+ );
273
+
274
+ const BatchVoiceIcon = (
275
+ <svg
276
+ width="18"
277
+ height="18"
278
+ viewBox="0 0 24 24"
279
+ fill="none"
280
+ stroke="currentColor"
281
+ strokeWidth="2"
282
+ strokeLinecap="round"
283
+ strokeLinejoin="round"
284
+ >
285
+ <path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" />
286
+ <path d="M19 10v2a7 7 0 0 1-14 0v-2" />
287
+ <line x1="12" y1="19" x2="12" y2="23" />
288
+ </svg>
289
+ );
290
+
291
+ const BatchImageToVideoIcon = (
292
+ <svg
293
+ width="18"
294
+ height="18"
295
+ viewBox="0 0 24 24"
296
+ fill="none"
297
+ stroke="currentColor"
298
+ strokeWidth="2"
299
+ strokeLinecap="round"
300
+ strokeLinejoin="round"
301
+ >
302
+ <polygon points="12 2 2 7 12 12 22 7 12 2" />
303
+ <polyline points="2 17 12 22 22 17" />
304
+ <polyline points="2 12 12 17 22 12" />
305
+ </svg>
306
+ );
307
+
308
+ const ServerIcon = (
309
+ <svg
310
+ width="18"
311
+ height="18"
312
+ viewBox="0 0 24 24"
313
+ fill="none"
314
+ stroke="currentColor"
315
+ strokeWidth="2"
316
+ strokeLinecap="round"
317
+ strokeLinejoin="round"
318
+ >
319
+ <rect x="2" y="2" width="20" height="8" rx="2" ry="2" />
320
+ <rect x="2" y="14" width="20" height="8" rx="2" ry="2" />
321
+ <line x1="6" y1="6" x2="6.01" y2="6" />
322
+ <line x1="6" y1="18" x2="6.01" y2="18" />
323
+ </svg>
324
+ );
325
+
326
+ const ServerStatusLight = () => (
327
+ <span
328
+ className={`w-2.5 h-2.5 rounded-full ${
329
+ store.agent.serverOnline === true
330
+ ? "bg-emerald-500 shadow-[0_0_6px_2px_rgba(16,185,129,0.5)]"
331
+ : store.agent.serverOnline === false
332
+ ? "bg-red-500 shadow-[0_0_6px_2px_rgba(239,68,68,0.5)]"
333
+ : "bg-tiffany-500"
334
+ }`}
335
+ />
336
+ );
337
+
338
+ // ========== Loading / Not Found ==========
339
+
340
+ if (!project) {
341
+ return (
342
+ <div className="flex flex-col items-center justify-center h-screen bg-ink-50">
343
+ <div className="w-16 h-16 bg-ink-100 rounded-3xl flex items-center justify-center mb-4">
344
+ <svg
345
+ width="28"
346
+ height="28"
347
+ viewBox="0 0 24 24"
348
+ fill="none"
349
+ stroke="#81d8d0"
350
+ strokeWidth="1.5"
351
+ >
352
+ <path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z" />
353
+ </svg>
354
+ </div>
355
+ <p className="text-sm font-medium text-ink-800 mb-3">
356
+ Project not found
357
+ </p>
358
+ <button
359
+ onClick={() => navigate("/app")}
360
+ className="flex items-center gap-1.5 px-4 py-2 bg-ink-100 hover:bg-ink-300 text-ink-700 text-sm font-medium rounded-2xl transition-colors"
361
+ >
362
+ {BackIcon}
363
+ Back to Projects
364
+ </button>
365
+ </div>
366
+ );
367
+ }
368
+
369
+ return (
370
+ <div className="flex flex-col h-screen bg-ink-50">
371
+ {/* Top bar */}
372
+ <div className="flex items-center gap-6 px-8 py-5 bg-white border-b border-ink-200 mb-8">
373
+ <button
374
+ onClick={() => {
375
+ store.resetAll();
376
+ navigate("/app");
377
+ }}
378
+ className="flex items-center gap-1.5 px-3 py-1.5 bg-ink-50 hover:bg-ink-200 text-ink-700 text-sm font-medium rounded-2xl transition-colors border border-ink-200/60"
379
+ >
380
+ {BackIcon}
381
+ Back
382
+ </button>
383
+ <div className="flex-1">
384
+ <h1 className="text-lg font-semibold text-ink-900 tracking-tight">
385
+ {project.name}
386
+ </h1>
387
+ {project.description && (
388
+ <p className="text-xs text-ink-600/60 mt-0.5">
389
+ {project.description}
390
+ </p>
391
+ )}
392
+ </div>
393
+ </div>
394
+
395
+ {/* Content */}
396
+ <div className="flex-1 px-8 pb-8">
397
+ <div className="bg-white border border-ink-200 rounded-3xl shadow-card p-8 min-h-full">
398
+ {/* ========== TAB BAR ========== */}
399
+ <div className="flex items-center gap-1.5 border-b border-ink-200 pb-5 mb-4 flex-wrap">
400
+ <button
401
+ onClick={() => store.setActiveTab("movieStudio")}
402
+ className={`flex items-center gap-2 px-5 py-2.5 text-sm font-medium rounded-2xl transition-all ${
403
+ store.activeTab === "movieStudio"
404
+ ? "bg-tiffany-500/10 text-tiffany-700 shadow-glow-sm"
405
+ : "text-ink-600 hover:bg-ink-100 hover:text-ink-800"
406
+ }`}
407
+ >
408
+ {MovieStudioIcon}
409
+ Movie Studio
410
+ </button>
411
+ <button
412
+ onClick={() => store.setActiveTab("video")}
413
+ className={`flex items-center gap-2 px-5 py-2.5 text-sm font-medium rounded-2xl transition-all ${
414
+ store.activeTab === "video"
415
+ ? "bg-tiffany-500/10 text-tiffany-700 shadow-glow-sm"
416
+ : "text-ink-600 hover:bg-ink-100 hover:text-ink-800"
417
+ }`}
418
+ >
419
+ {VideoIcon}
420
+ Generate Avatar ideo
421
+ </button>
422
+ <button
423
+ onClick={() => store.setActiveTab("extend")}
424
+ className={`flex items-center gap-2 px-5 py-2.5 text-sm font-medium rounded-2xl transition-all ${
425
+ store.activeTab === "extend"
426
+ ? "bg-tiffany-500/10 text-tiffany-700 shadow-glow-sm"
427
+ : "text-ink-600 hover:bg-ink-100 hover:text-ink-800"
428
+ }`}
429
+ >
430
+ {ExtendIcon}
431
+ Extend Video
432
+ </button>
433
+ <button
434
+ onClick={() => store.setActiveTab("fastImageEdit")}
435
+ className={`flex items-center gap-2 px-5 py-2.5 text-sm font-medium rounded-2xl transition-all ${
436
+ store.activeTab === "fastImageEdit"
437
+ ? "bg-tiffany-500/10 text-tiffany-700 shadow-glow-sm"
438
+ : "text-ink-600 hover:bg-ink-100 hover:text-ink-800"
439
+ }`}
440
+ >
441
+ {FastImageEditIcon}
442
+ Fast Image Edit
443
+ </button>
444
+ <button
445
+ onClick={() => store.setActiveTab("agent")}
446
+ className={`flex items-center gap-2 px-5 py-2.5 text-sm font-medium rounded-2xl transition-all ${
447
+ store.activeTab === "agent"
448
+ ? "bg-tiffany-500/10 text-tiffany-700 shadow-glow-sm"
449
+ : "text-ink-600 hover:bg-ink-100 hover:text-ink-800"
450
+ }`}
451
+ >
452
+ {AgentIcon}
453
+ Agent
454
+ </button>
455
+ <button
456
+ onClick={() => store.setActiveTab("storyWriter")}
457
+ className={`flex items-center gap-2 px-5 py-2.5 text-sm font-medium rounded-2xl transition-all ${
458
+ store.activeTab === "storyWriter"
459
+ ? "bg-tiffany-500/10 text-tiffany-700 shadow-glow-sm"
460
+ : "text-ink-600 hover:bg-ink-100 hover:text-ink-800"
461
+ }`}
462
+ >
463
+ {StoryWriterIcon}
464
+ Story Writer
465
+ </button>
466
+ <button
467
+ onClick={() => store.setActiveTab("characters")}
468
+ className={`flex items-center gap-2 px-5 py-2.5 text-sm font-medium rounded-2xl transition-all ${
469
+ store.activeTab === "characters"
470
+ ? "bg-tiffany-500/10 text-tiffany-700 shadow-glow-sm"
471
+ : "text-ink-600 hover:bg-ink-100 hover:text-ink-800"
472
+ }`}
473
+ >
474
+ {CharacterIcon}
475
+ Characters
476
+ </button>
477
+ <button
478
+ onClick={() => store.setActiveTab("extract")}
479
+ className={`flex items-center gap-2 px-5 py-2.5 text-sm font-medium rounded-2xl transition-all ${
480
+ store.activeTab === "extract"
481
+ ? "bg-tiffany-500/10 text-tiffany-700 shadow-glow-sm"
482
+ : "text-ink-600 hover:bg-ink-100 hover:text-ink-800"
483
+ }`}
484
+ >
485
+ {ExtractIcon}
486
+ Extract Image
487
+ </button>
488
+ <button
489
+ onClick={() => store.setActiveTab("sceneVisual")}
490
+ className={`flex items-center gap-2 px-5 py-2.5 text-sm font-medium rounded-2xl transition-all ${
491
+ store.activeTab === "sceneVisual"
492
+ ? "bg-tiffany-500/10 text-tiffany-700 shadow-glow-sm"
493
+ : "text-ink-600 hover:bg-ink-100 hover:text-ink-800"
494
+ }`}
495
+ >
496
+ {SceneVisualIcon}
497
+ Scene Visual
498
+ </button>
499
+ <button
500
+ onClick={() => store.setActiveTab("textToImage")}
501
+ className={`flex items-center gap-2 px-5 py-2.5 text-sm font-medium rounded-2xl transition-all ${
502
+ store.activeTab === "textToImage"
503
+ ? "bg-tiffany-500/10 text-tiffany-700 shadow-glow-sm"
504
+ : "text-ink-600 hover:bg-ink-100 hover:text-ink-800"
505
+ }`}
506
+ >
507
+ {TextToImageIcon}
508
+ Text-to-Image
509
+ </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
+ <button
522
+ onClick={() => store.setActiveTab("batchVideo")}
523
+ className={`flex items-center gap-2 px-5 py-2.5 text-sm font-medium rounded-2xl transition-all ${
524
+ store.activeTab === "batchVideo"
525
+ ? "bg-tiffany-500/10 text-tiffany-700 shadow-glow-sm"
526
+ : "text-ink-600 hover:bg-ink-100 hover:text-ink-800"
527
+ }`}
528
+ >
529
+ {BatchVideoIcon}
530
+ Batch Video
531
+ </button>
532
+ <button
533
+ onClick={() => store.setActiveTab("batchImageToVideo")}
534
+ className={`flex items-center gap-2 px-5 py-2.5 text-sm font-medium rounded-2xl transition-all ${
535
+ store.activeTab === "batchImageToVideo"
536
+ ? "bg-tiffany-500/10 text-tiffany-700 shadow-glow-sm"
537
+ : "text-ink-600 hover:bg-ink-100 hover:text-ink-800"
538
+ }`}
539
+ >
540
+ {BatchImageToVideoIcon}
541
+ Batch Image to Video
542
+ </button>
543
+ <button
544
+ onClick={() => store.setActiveTab("batchVoice")}
545
+ className={`flex items-center gap-2 px-5 py-2.5 text-sm font-medium rounded-2xl transition-all ${
546
+ store.activeTab === "batchVoice"
547
+ ? "bg-tiffany-500/10 text-tiffany-700 shadow-glow-sm"
548
+ : "text-ink-600 hover:bg-ink-100 hover:text-ink-800"
549
+ }`}
550
+ >
551
+ {BatchVoiceIcon}
552
+ Batch Videos with Voice Over
553
+ </button>
554
+ <button
555
+ onClick={() => store.setActiveTab("llmServer")}
556
+ className={`flex items-center gap-2 px-5 py-2.5 text-sm font-medium rounded-2xl transition-all ${
557
+ store.activeTab === "llmServer"
558
+ ? "bg-tiffany-500/10 text-tiffany-700 shadow-glow-sm"
559
+ : "text-ink-600 hover:bg-ink-100 hover:text-ink-800"
560
+ }`}
561
+ >
562
+ {ServerIcon}
563
+ LLM Server
564
+ <ServerStatusLight />
565
+ </button>
566
+ </div>
567
+
568
+ {/* ========== MOVIE STUDIO PANEL ========== */}
569
+ {store.activeTab === "movieStudio" && (
570
+ <MovieStudioTab projectId={id!} />
571
+ )}
572
+
573
+ {/* ========== FAST IMAGE EDIT PANEL ========== */}
574
+ {store.activeTab === "fastImageEdit" && (
575
+ <FastImageEditTab projectId={id!} />
576
+ )}
577
+
578
+ {/* ========== VIDEO GENERATION PANEL ========== */}
579
+ {store.activeTab === "video" && <GenerateVideoTab projectId={id!} />}
580
+
581
+ {/* ========== EXTEND VIDEO PANEL ========== */}
582
+ {store.activeTab === "extend" && <ExtendVideoTab projectId={id!} />}
583
+
584
+ {/* ========== AGENT PANEL ========== */}
585
+ {store.activeTab === "agent" && <AgentTab projectId={id!} />}
586
+
587
+ {/* ========== STORY WRITER PANEL ========== */}
588
+ {store.activeTab === "storyWriter" && (
589
+ <StoryWriterTab projectId={id!} />
590
+ )}
591
+
592
+ {/* ========== CHARACTERS PANEL ========== */}
593
+ {store.activeTab === "characters" && (
594
+ <CharactersTab projectId={id!} />
595
+ )}
596
+
597
+ {/* ========== EXTRACT IMAGE PANEL ========== */}
598
+ {store.activeTab === "extract" && <ExtractImageTab projectId={id!} />}
599
+
600
+ {/* ========== SCENE VISUAL PANEL ========== */}
601
+ {store.activeTab === "sceneVisual" && (
602
+ <SceneVisualTab projectId={id!} />
603
+ )}
604
+
605
+ {/* ========== TEXT-TO-IMAGE PANEL ========== */}
606
+ {store.activeTab === "textToImage" && (
607
+ <TextToImageTab projectId={id!} />
608
+ )}
609
+
610
+ {/* ========== REFERENCES TO VIDEO PANEL ========== */}
611
+ {store.activeTab === "referencesToVideo" && (
612
+ <ReferencesToVideoTab projectId={id!} />
613
+ )}
614
+
615
+ {/* ========== BATCH VIDEO PANEL ========== */}
616
+ {store.activeTab === "batchVideo" && (
617
+ <BatchVideoTab projectId={id!} />
618
+ )}
619
+
620
+ {/* ========== BATCH IMAGE TO VIDEO PANEL ========== */}
621
+ {store.activeTab === "batchImageToVideo" && (
622
+ <BatchImageToVideoTab projectId={id!} />
623
+ )}
624
+
625
+ {/* ========== BATCH VOICE VIDEO PANEL ========== */}
626
+ {store.activeTab === "batchVoice" && (
627
+ <BatchVoiceVideoTab projectId={id!} />
628
+ )}
629
+
630
+ {/* ========== LLM SERVER PANEL ========== */}
631
+ {store.activeTab === "llmServer" && <LlmServerTab />}
632
+ </div>
633
+ </div>
634
+ </div>
635
+ );
636
+ }