Gaurav vashistha commited on
Commit
0a9c963
·
1 Parent(s): 0cb75bf

Update server.py and code.html

Browse files
Files changed (2) hide show
  1. server.py +1 -1
  2. stitch_continuity_dashboard/code.html +293 -243
server.py CHANGED
@@ -7,7 +7,7 @@ import shutil
7
  import uuid
8
 
9
  # Import from the subpackage as before
10
- from continuity_agent.agent import continuity_graph
11
 
12
  app = FastAPI(title="Continuity", description="AI Video Bridging Service")
13
 
 
7
  import uuid
8
 
9
  # Import from the subpackage as before
10
+ from continuity_agent.agent import app as continuity_graph
11
 
12
  app = FastAPI(title="Continuity", description="AI Video Bridging Service")
13
 
stitch_continuity_dashboard/code.html CHANGED
@@ -1,271 +1,313 @@
1
  <!DOCTYPE html>
2
 
3
- <html class="dark" lang="en"><head>
4
- <meta charset="utf-8"/>
5
- <meta content="width=device-width, initial-scale=1.0" name="viewport"/>
6
- <title>Continuity Dashboard</title>
7
- <!-- Google Fonts -->
8
- <link href="https://fonts.googleapis.com" rel="preconnect"/>
9
- <link crossorigin="" href="https://fonts.gstatic.com" rel="preconnect"/>
10
- <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&amp;display=swap" rel="stylesheet"/>
11
- <link href="https://fonts.googleapis.com/css2?family=Noto+Sans:wght@300;400;500;600;700&amp;display=swap" rel="stylesheet"/>
12
- <!-- Material Symbols -->
13
- <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
14
- <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
15
- <!-- Tailwind CSS -->
16
- <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
17
- <script id="tailwind-config">
18
- tailwind.config = {
19
- darkMode: "class",
20
- theme: {
21
- extend: {
22
- colors: {
23
- "primary": "#7f0df2",
24
- "background-light": "#f7f5f8",
25
- "background-dark": "#191022",
26
- "surface-dark": "#2a1d35",
27
- "border-dark": "#4d3168",
28
- },
29
- fontFamily: {
30
- "display": ["Space Grotesk", "sans-serif"],
31
- "body": ["Noto Sans", "sans-serif"],
32
- },
33
- borderRadius: {"DEFAULT": "0.25rem", "lg": "0.5rem", "xl": "0.75rem", "2xl": "1rem", "full": "9999px"},
34
- boxShadow: {
35
- "neon": "0 0 20px rgba(127, 13, 242, 0.4)",
36
- "neon-strong": "0 0 35px rgba(127, 13, 242, 0.6)",
37
- }
38
- },
39
- },
40
- }
41
- </script>
42
- <style>
43
- /* Custom scrollbar for dark theme */
44
- ::-webkit-scrollbar {
45
- width: 8px;
46
- height: 8px;
47
- }
48
- ::-webkit-scrollbar-track {
49
- background: #191022;
50
- }
51
- ::-webkit-scrollbar-thumb {
52
- background: #4d3168;
53
- border-radius: 4px;
54
- }
55
- ::-webkit-scrollbar-thumb:hover {
56
- background: #7f0df2;
57
- }
58
- .glass-panel {
59
- background: rgba(25, 16, 34, 0.6);
60
- backdrop-filter: blur(12px);
61
- -webkit-backdrop-filter: blur(12px);
62
- border: 1px solid rgba(255, 255, 255, 0.05);
63
- }
64
- .connection-line {
65
- height: 2px;
66
- flex-grow: 1;
67
- background: linear-gradient(90deg, #4d3168 0%, #7f0df2 50%, #4d3168 100%);
68
- opacity: 0.5;
69
- position: relative;
70
- }
71
- .connection-line::after {
72
- content: '';
73
- position: absolute;
74
- right: 0;
75
- top: 50%;
76
- transform: translateY(-50%);
77
- width: 6px;
78
- height: 6px;
79
- background: #7f0df2;
80
- border-radius: 50%;
81
- box-shadow: 0 0 10px #7f0df2;
82
- }
83
- </style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  </head>
85
- <body class="relative flex h-screen w-full flex-col bg-background-light dark:bg-background-dark font-display overflow-hidden text-white selection:bg-primary selection:text-white">
86
- <!-- Background Ambient Glows -->
87
- <div class="fixed top-0 left-0 w-full h-full pointer-events-none overflow-hidden z-0">
88
- <div class="absolute top-[-10%] left-[-10%] w-[40%] h-[40%] bg-primary/20 rounded-full blur-[120px]"></div>
89
- <div class="absolute bottom-[-10%] right-[-10%] w-[40%] h-[40%] bg-blue-600/10 rounded-full blur-[120px]"></div>
90
- </div>
91
- <!-- Header -->
92
- <header class="fixed top-0 left-0 right-0 z-50 flex items-center justify-between whitespace-nowrap border-b border-white/5 glass-panel px-8 py-4">
93
- <div class="flex items-center gap-4 text-white">
94
- <div class="size-8 flex items-center justify-center text-primary">
95
- <span class="material-symbols-outlined text-3xl">emergency_recording</span>
96
- </div>
97
- <h2 class="text-white text-xl font-bold leading-tight tracking-[-0.015em] bg-clip-text text-transparent bg-gradient-to-r from-white to-gray-400">
98
- Continuity <span class="text-xs font-normal text-gray-400 ml-2 opacity-60 tracking-widest uppercase">The Missing Link</span>
99
- </h2>
100
- </div>
101
- <div class="flex gap-3">
102
- <button class="flex size-10 cursor-pointer items-center justify-center overflow-hidden rounded-lg bg-surface-dark hover:bg-primary/20 transition-colors border border-white/5 text-white">
103
- <span class="material-symbols-outlined text-[20px]">settings</span>
104
- </button>
105
- <button class="flex size-10 cursor-pointer items-center justify-center overflow-hidden rounded-lg bg-surface-dark hover:bg-primary/20 transition-colors border border-white/5 text-white">
106
- <span class="material-symbols-outlined text-[20px]">account_circle</span>
107
- </button>
108
- </div>
109
- </header>
110
- <!-- Main Workspace -->
111
- <main class="relative z-10 flex flex-1 flex-col items-center justify-center w-full px-8 pt-20 pb-28">
112
- <!-- Timeline Container -->
113
- <div class="w-full max-w-[1400px] flex items-center justify-center gap-6 xl:gap-10">
114
- <!-- SCENE A: Upload State -->
115
- <div class="flex flex-col gap-4 flex-1 max-w-[320px] group">
116
- <div class="flex items-center justify-between px-1">
117
- <span class="text-xs font-bold tracking-widest text-gray-400 uppercase">Input Source</span>
118
- <span class="text-xs font-bold tracking-widest text-primary">SCENE A</span>
119
- </div>
120
- <div class="relative flex flex-col items-center justify-center gap-6 rounded-2xl border-2 border-dashed border-border-dark bg-surface-dark/30 hover:bg-surface-dark/50 hover:border-primary/50 transition-all duration-300 px-6 py-12 h-[360px]">
121
- <div class="size-16 rounded-full bg-surface-dark flex items-center justify-center mb-2 shadow-lg group-hover:scale-110 transition-transform">
122
- <span class="material-symbols-outlined text-3xl text-gray-300 group-hover:text-white">video_file</span>
123
- </div>
124
- <div class="flex flex-col items-center gap-2 text-center">
125
- <p class="text-white text-lg font-bold">Start Scene</p>
126
- <p class="text-gray-400 text-sm max-w-[200px]">Drag &amp; drop your starting video clip here</p>
127
- </div>
128
- <input type="file" id="video-upload-a" accept="video/*,image/*" class="hidden" onchange="handleFileSelect(this, 'label-a')">
129
- <button onclick="document.getElementById('video-upload-a').click()" class="mt-2 flex items-center justify-center rounded-lg h-9 px-4 bg-surface-dark hover:bg-primary hover:text-white border border-white/10 transition-all text-sm font-bold tracking-wide text-gray-200 shadow-lg">
130
- <span class="material-symbols-outlined text-[18px] mr-2">upload</span>
131
- <span id="label-a">Select File</span>
132
- </button>
133
- </div>
134
- </div>
135
- <!-- Connector Left -->
136
- <div class="hidden md:flex flex-col items-center justify-center w-16 xl:w-24 opacity-40">
137
- <div class="w-full h-[2px] bg-gradient-to-r from-transparent via-primary to-transparent relative">
138
- <div class="absolute right-0 -top-1.5 text-primary animate-pulse">
139
- <span class="material-symbols-outlined text-lg">chevron_right</span>
140
- </div>
141
- </div>
142
- </div>
143
- <!-- THE BRIDGE: AI Generation Core -->
144
- <div class="flex flex-col gap-4 flex-[1.2] max-w-[480px] relative z-20">
145
- <div class="flex items-center justify-center px-1">
146
- <span class="text-xs font-bold tracking-[0.2em] text-primary drop-shadow-[0_0_8px_rgba(127,13,242,0.8)] uppercase animate-pulse">The Bridge</span>
147
- </div>
148
- <!-- Active Card Container -->
149
- <div class="relative w-full aspect-[4/3] rounded-2xl overflow-hidden shadow-neon border border-primary/30 group">
150
- <!-- Placeholder Gradient Background -->
151
- <div class="absolute inset-0 bg-gradient-to-br from-surface-dark to-[#0f0a16] z-0"></div>
152
- <!-- Abstract Visualization -->
153
- <div class="absolute inset-0 opacity-40 mix-blend-overlay bg-[url('https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?q=80&amp;w=1000&amp;auto=format&amp;fit=crop')] bg-cover bg-center" data-alt="Abstract digital waves representing AI processing"></div>
154
- <!-- Glowing Core Animation (CSS Only representation) -->
155
- <div class="absolute inset-0 flex items-center justify-center">
156
- <div class="relative size-32">
157
- <div class="absolute inset-0 rounded-full border-2 border-primary/20 animate-[spin_4s_linear_infinite]"></div>
158
- <div class="absolute inset-2 rounded-full border-2 border-t-primary border-r-transparent border-b-primary/50 border-l-transparent animate-[spin_3s_linear_infinite_reverse]"></div>
159
- <div class="absolute inset-0 flex items-center justify-center">
160
- <span class="material-symbols-outlined text-4xl text-primary drop-shadow-[0_0_10px_rgba(127,13,242,1)]">auto_awesome</span>
161
- </div>
162
- </div>
163
- </div>
164
- <!-- Content Overlay -->
165
- <div class="absolute bottom-0 left-0 w-full p-6 bg-gradient-to-t from-black/90 via-black/50 to-transparent flex items-end justify-between">
166
- <div class="flex flex-col gap-1">
167
- <p class="text-white text-xl font-bold leading-tight">AI Transition</p>
168
- <div class="flex items-center gap-2">
169
- <div class="size-2 rounded-full bg-yellow-500 animate-pulse"></div>
170
- <p class="text-gray-300 text-xs font-medium uppercase tracking-wide">Waiting for inputs...</p>
171
- </div>
172
- </div>
173
- </div>
174
- </div>
175
- </div>
176
- <!-- Connector Right -->
177
- <div class="hidden md:flex flex-col items-center justify-center w-16 xl:w-24 opacity-40">
178
- <div class="w-full h-[2px] bg-gradient-to-r from-transparent via-primary to-transparent relative">
179
- <div class="absolute right-0 -top-1.5 text-primary animate-pulse">
180
- <span class="material-symbols-outlined text-lg">chevron_right</span>
181
- </div>
182
- </div>
183
- </div>
184
- <!-- SCENE C: Upload State -->
185
- <div class="flex flex-col gap-4 flex-1 max-w-[320px] group">
186
- <div class="flex items-center justify-between px-1">
187
- <span class="text-xs font-bold tracking-widest text-primary">SCENE C</span>
188
- <span class="text-xs font-bold tracking-widest text-gray-400 uppercase">Target Source</span>
189
- </div>
190
- <div class="relative flex flex-col items-center justify-center gap-6 rounded-2xl border-2 border-dashed border-border-dark bg-surface-dark/30 hover:bg-surface-dark/50 hover:border-primary/50 transition-all duration-300 px-6 py-12 h-[360px]">
191
- <div class="size-16 rounded-full bg-surface-dark flex items-center justify-center mb-2 shadow-lg group-hover:scale-110 transition-transform">
192
- <span class="material-symbols-outlined text-3xl text-gray-300 group-hover:text-white">movie_edit</span>
193
- </div>
194
- <div class="flex flex-col items-center gap-2 text-center">
195
- <p class="text-white text-lg font-bold">End Scene</p>
196
- <p class="text-gray-400 text-sm max-w-[200px]">Drag &amp; drop your target video clip here</p>
197
- </div>
198
- <input type="file" id="video-upload-c" accept="video/*,image/*" class="hidden" onchange="handleFileSelect(this, 'label-c')">
199
- <button onclick="document.getElementById('video-upload-c').click()" class="mt-2 flex items-center justify-center rounded-lg h-9 px-4 bg-surface-dark hover:bg-primary hover:text-white border border-white/10 transition-all text-sm font-bold tracking-wide text-gray-200 shadow-lg">
200
- <span class="material-symbols-outlined text-[18px] mr-2">upload</span>
201
- <span id="label-c">Select File</span>
202
- </button>
203
- </div>
204
- </div>
205
- </div>
206
- </main>
207
- <!-- Floating Action Bar (Director Controls) -->
208
- <div class="fixed bottom-10 left-1/2 -translate-x-1/2 z-50 w-full max-w-2xl px-4">
209
- <div class="glass-panel rounded-full p-2 pl-6 flex items-center shadow-neon">
210
- <div class="flex-1 flex items-center gap-3">
211
- <span class="material-symbols-outlined text-gray-400">edit_note</span>
212
- <input id="director-notes" class="w-full bg-transparent border-none text-white placeholder-gray-500 focus:ring-0 text-sm font-medium" placeholder="Add Director Notes (e.g., 'Slow dissolve', 'Cyber glitch')..." type="text"/>
213
- </div>
214
- <div class="h-8 w-[1px] bg-white/10 mx-2"></div>
215
- <button id="generate-btn" class="flex items-center gap-2 bg-primary hover:bg-[#6b0bc9] text-white px-6 py-3 rounded-full font-bold text-sm transition-all shadow-[0_0_15px_rgba(127,13,242,0.4)] hover:shadow-[0_0_25px_rgba(127,13,242,0.6)] whitespace-nowrap">
216
- <span class="material-symbols-outlined text-[20px]">movie_filter</span>
217
- Generate Transition
218
- </button>
219
- </div>
220
- </div>
221
 
222
- <script>
223
- // UI Helper: Update filename text when file is selected
224
- function handleFileSelect(input, labelId) {
225
- if (input.files && input.files[0]) {
226
- const label = document.getElementById(labelId);
227
- label.innerText = input.files[0].name;
228
- label.classList.add("text-primary"); // Turn purple to indicate success
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
  }
230
- }
231
 
232
- // Main Logic: Send to Agent
233
- const generateBtn = document.getElementById("generate-btn");
234
- if (generateBtn) {
235
  generateBtn.addEventListener("click", async () => {
236
  const fileA = document.getElementById("video-upload-a").files[0];
237
  const fileC = document.getElementById("video-upload-c").files[0];
238
  const notes = document.getElementById("director-notes").value;
239
  const btn = document.getElementById("generate-btn");
240
-
241
  // 1. Validation
242
  if (!fileA || !fileC) {
243
  alert("⚠️ Action Required: Please upload both Scene A and Scene C videos.");
244
  return;
245
  }
246
-
247
  // 2. Loading State
248
  const originalContent = btn.innerHTML;
249
  btn.innerHTML = `<span class="material-symbols-outlined animate-spin text-[20px] mr-2">progress_activity</span> Director is working...`;
250
  btn.disabled = true;
251
  btn.classList.add("opacity-70", "cursor-not-allowed");
252
-
253
  const formData = new FormData();
254
  formData.append("video_a", fileA);
255
  formData.append("video_c", fileC);
256
  formData.append("prompt", notes || "Cinematic transition between scenes");
257
-
258
  try {
259
  // 3. Send to Server
260
  const response = await fetch("http://127.0.0.1:8000/generate-transition", {
261
  method: "POST",
262
  body: formData
263
  });
264
-
265
  if (!response.ok) throw new Error("Agent backend returned an error.");
266
-
267
  const data = await response.json();
268
-
269
  // 4. Success: Inject Video into the Middle Card
270
  // We target the middle "Bridge" div
271
  const bridgeCard = document.querySelector(".aspect-\\[4\\/3\\]");
@@ -276,25 +318,31 @@
276
  // Scene A: .aspect-[4/3]
277
  // Bridge: .aspect-[4/3] (line 148)
278
  // Scene C: .aspect-[4/3]
279
-
280
  // User script used document.querySelector(".aspect-\\[4\\/3\\]").
281
  // This matches the first one (Scene A)! That's a bug in the user's snippet.
282
  // I will fix it to target the BRIDGE card.
283
  // The bridge card container has text "The Bridge".
284
  // I'll add an ID to the bridge container or select by index.
285
  // Safer to select the middle one: document.querySelectorAll(".aspect-\\[4\\/3\\]")[1]
286
-
 
287
  const cards = document.querySelectorAll(".aspect-\\[4\\/3\\]");
288
  if (cards.length >= 2) {
289
- const bridgeCard = cards[1];
290
- bridgeCard.innerHTML = `
 
 
291
  <video controls autoplay loop class="w-full h-full object-cover rounded-2xl border-2 border-primary shadow-neon">
292
  <source src="http://127.0.0.1:8000${data.video_url}" type="video/mp4">
293
  Your browser does not support the video tag.
294
  </video>
295
  `;
 
 
 
296
  }
297
-
298
  } catch (error) {
299
  console.error(error);
300
  alert("❌ Generation Failed: " + error.message + "\n\nMake sure 'python server.py' is running!");
@@ -304,6 +352,8 @@
304
  btn.classList.remove("opacity-70", "cursor-not-allowed");
305
  }
306
  });
307
- }
308
- </script>
309
- </body></html>
 
 
 
1
  <!DOCTYPE html>
2
 
3
+ <html class="dark" lang="en">
4
+
5
+ <head>
6
+ <meta charset="utf-8" />
7
+ <meta content="width=device-width, initial-scale=1.0" name="viewport" />
8
+ <title>Continuity Dashboard</title>
9
+ <!-- Google Fonts -->
10
+ <link href="https://fonts.googleapis.com" rel="preconnect" />
11
+ <link crossorigin="" href="https://fonts.gstatic.com" rel="preconnect" />
12
+ <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&amp;display=swap"
13
+ rel="stylesheet" />
14
+ <link href="https://fonts.googleapis.com/css2?family=Noto+Sans:wght@300;400;500;600;700&amp;display=swap"
15
+ rel="stylesheet" />
16
+ <!-- Material Symbols -->
17
+ <link
18
+ href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap"
19
+ rel="stylesheet" />
20
+ <link
21
+ href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap"
22
+ rel="stylesheet" />
23
+ <!-- Tailwind CSS -->
24
+ <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
25
+ <script id="tailwind-config">
26
+ tailwind.config = {
27
+ darkMode: "class",
28
+ theme: {
29
+ extend: {
30
+ colors: {
31
+ "primary": "#7f0df2",
32
+ "background-light": "#f7f5f8",
33
+ "background-dark": "#191022",
34
+ "surface-dark": "#2a1d35",
35
+ "border-dark": "#4d3168",
36
+ },
37
+ fontFamily: {
38
+ "display": ["Space Grotesk", "sans-serif"],
39
+ "body": ["Noto Sans", "sans-serif"],
40
+ },
41
+ borderRadius: { "DEFAULT": "0.25rem", "lg": "0.5rem", "xl": "0.75rem", "2xl": "1rem", "full": "9999px" },
42
+ boxShadow: {
43
+ "neon": "0 0 20px rgba(127, 13, 242, 0.4)",
44
+ "neon-strong": "0 0 35px rgba(127, 13, 242, 0.6)",
45
+ }
46
+ },
47
+ },
48
+ }
49
+ </script>
50
+ <style>
51
+ /* Custom scrollbar for dark theme */
52
+ ::-webkit-scrollbar {
53
+ width: 8px;
54
+ height: 8px;
55
+ }
56
+
57
+ ::-webkit-scrollbar-track {
58
+ background: #191022;
59
+ }
60
+
61
+ ::-webkit-scrollbar-thumb {
62
+ background: #4d3168;
63
+ border-radius: 4px;
64
+ }
65
+
66
+ ::-webkit-scrollbar-thumb:hover {
67
+ background: #7f0df2;
68
+ }
69
+
70
+ .glass-panel {
71
+ background: rgba(25, 16, 34, 0.6);
72
+ backdrop-filter: blur(12px);
73
+ -webkit-backdrop-filter: blur(12px);
74
+ border: 1px solid rgba(255, 255, 255, 0.05);
75
+ }
76
+
77
+ .connection-line {
78
+ height: 2px;
79
+ flex-grow: 1;
80
+ background: linear-gradient(90deg, #4d3168 0%, #7f0df2 50%, #4d3168 100%);
81
+ opacity: 0.5;
82
+ position: relative;
83
+ }
84
+
85
+ .connection-line::after {
86
+ content: '';
87
+ position: absolute;
88
+ right: 0;
89
+ top: 50%;
90
+ transform: translateY(-50%);
91
+ width: 6px;
92
+ height: 6px;
93
+ background: #7f0df2;
94
+ border-radius: 50%;
95
+ box-shadow: 0 0 10px #7f0df2;
96
+ }
97
+ </style>
98
  </head>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
 
100
+ <body
101
+ class="relative flex h-screen w-full flex-col bg-background-light dark:bg-background-dark font-display overflow-hidden text-white selection:bg-primary selection:text-white">
102
+ <!-- Background Ambient Glows -->
103
+ <div class="fixed top-0 left-0 w-full h-full pointer-events-none overflow-hidden z-0">
104
+ <div class="absolute top-[-10%] left-[-10%] w-[40%] h-[40%] bg-primary/20 rounded-full blur-[120px]"></div>
105
+ <div class="absolute bottom-[-10%] right-[-10%] w-[40%] h-[40%] bg-blue-600/10 rounded-full blur-[120px]"></div>
106
+ </div>
107
+ <!-- Header -->
108
+ <header
109
+ class="fixed top-0 left-0 right-0 z-50 flex items-center justify-between whitespace-nowrap border-b border-white/5 glass-panel px-8 py-4">
110
+ <div class="flex items-center gap-4 text-white">
111
+ <div class="size-8 flex items-center justify-center text-primary">
112
+ <span class="material-symbols-outlined text-3xl">emergency_recording</span>
113
+ </div>
114
+ <h2
115
+ class="text-white text-xl font-bold leading-tight tracking-[-0.015em] bg-clip-text text-transparent bg-gradient-to-r from-white to-gray-400">
116
+ Continuity <span class="text-xs font-normal text-gray-400 ml-2 opacity-60 tracking-widest uppercase">The Missing
117
+ Link</span>
118
+ </h2>
119
+ </div>
120
+ <div class="flex gap-3">
121
+ <button
122
+ class="flex size-10 cursor-pointer items-center justify-center overflow-hidden rounded-lg bg-surface-dark hover:bg-primary/20 transition-colors border border-white/5 text-white">
123
+ <span class="material-symbols-outlined text-[20px]">settings</span>
124
+ </button>
125
+ <button
126
+ class="flex size-10 cursor-pointer items-center justify-center overflow-hidden rounded-lg bg-surface-dark hover:bg-primary/20 transition-colors border border-white/5 text-white">
127
+ <span class="material-symbols-outlined text-[20px]">account_circle</span>
128
+ </button>
129
+ </div>
130
+ </header>
131
+ <!-- Main Workspace -->
132
+ <main class="relative z-10 flex flex-1 flex-col items-center justify-center w-full px-8 pt-20 pb-28">
133
+ <!-- Timeline Container -->
134
+ <div class="w-full max-w-[1400px] flex items-center justify-center gap-6 xl:gap-10">
135
+ <!-- SCENE A: Upload State -->
136
+ <div class="flex flex-col gap-4 flex-1 max-w-[320px] group">
137
+ <div class="flex items-center justify-between px-1">
138
+ <span class="text-xs font-bold tracking-widest text-gray-400 uppercase">Input Source</span>
139
+ <span class="text-xs font-bold tracking-widest text-primary">SCENE A</span>
140
+ </div>
141
+ <div
142
+ class="relative flex flex-col items-center justify-center gap-6 rounded-2xl border-2 border-dashed border-border-dark bg-surface-dark/30 hover:bg-surface-dark/50 hover:border-primary/50 transition-all duration-300 px-6 py-12 h-[360px]">
143
+ <div
144
+ class="size-16 rounded-full bg-surface-dark flex items-center justify-center mb-2 shadow-lg group-hover:scale-110 transition-transform">
145
+ <span class="material-symbols-outlined text-3xl text-gray-300 group-hover:text-white">video_file</span>
146
+ </div>
147
+ <div class="flex flex-col items-center gap-2 text-center">
148
+ <p class="text-white text-lg font-bold">Start Scene</p>
149
+ <p class="text-gray-400 text-sm max-w-[200px]">Drag &amp; drop your starting video clip here</p>
150
+ </div>
151
+ <input type="file" id="video-upload-a" accept="video/*,image/*" class="hidden"
152
+ onchange="handleFileSelect(this, 'label-a')">
153
+ <button onclick="document.getElementById('video-upload-a').click()"
154
+ class="mt-2 flex items-center justify-center rounded-lg h-9 px-4 bg-surface-dark hover:bg-primary hover:text-white border border-white/10 transition-all text-sm font-bold tracking-wide text-gray-200 shadow-lg">
155
+ <span class="material-symbols-outlined text-[18px] mr-2">upload</span>
156
+ <span id="label-a">Select File</span>
157
+ </button>
158
+ </div>
159
+ </div>
160
+ <!-- Connector Left -->
161
+ <div class="hidden md:flex flex-col items-center justify-center w-16 xl:w-24 opacity-40">
162
+ <div class="w-full h-[2px] bg-gradient-to-r from-transparent via-primary to-transparent relative">
163
+ <div class="absolute right-0 -top-1.5 text-primary animate-pulse">
164
+ <span class="material-symbols-outlined text-lg">chevron_right</span>
165
+ </div>
166
+ </div>
167
+ </div>
168
+ <!-- THE BRIDGE: AI Generation Core -->
169
+ <div class="flex flex-col gap-4 flex-[1.2] max-w-[480px] relative z-20">
170
+ <div class="flex items-center justify-center px-1">
171
+ <span
172
+ class="text-xs font-bold tracking-[0.2em] text-primary drop-shadow-[0_0_8px_rgba(127,13,242,0.8)] uppercase animate-pulse">The
173
+ Bridge</span>
174
+ </div>
175
+ <!-- Active Card Container -->
176
+ <div
177
+ class="relative w-full aspect-[4/3] rounded-2xl overflow-hidden shadow-neon border border-primary/30 group">
178
+ <!-- Placeholder Gradient Background -->
179
+ <div class="absolute inset-0 bg-gradient-to-br from-surface-dark to-[#0f0a16] z-0"></div>
180
+ <!-- Abstract Visualization -->
181
+ <div
182
+ class="absolute inset-0 opacity-40 mix-blend-overlay bg-[url('https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?q=80&amp;w=1000&amp;auto=format&amp;fit=crop')] bg-cover bg-center"
183
+ data-alt="Abstract digital waves representing AI processing"></div>
184
+ <!-- Glowing Core Animation (CSS Only representation) -->
185
+ <div class="absolute inset-0 flex items-center justify-center">
186
+ <div class="relative size-32">
187
+ <div class="absolute inset-0 rounded-full border-2 border-primary/20 animate-[spin_4s_linear_infinite]">
188
+ </div>
189
+ <div
190
+ class="absolute inset-2 rounded-full border-2 border-t-primary border-r-transparent border-b-primary/50 border-l-transparent animate-[spin_3s_linear_infinite_reverse]">
191
+ </div>
192
+ <div class="absolute inset-0 flex items-center justify-center">
193
+ <span
194
+ class="material-symbols-outlined text-4xl text-primary drop-shadow-[0_0_10px_rgba(127,13,242,1)]">auto_awesome</span>
195
+ </div>
196
+ </div>
197
+ </div>
198
+ <!-- Content Overlay -->
199
+ <div
200
+ class="absolute bottom-0 left-0 w-full p-6 bg-gradient-to-t from-black/90 via-black/50 to-transparent flex items-end justify-between">
201
+ <div class="flex flex-col gap-1">
202
+ <p class="text-white text-xl font-bold leading-tight">AI Transition</p>
203
+ <div class="flex items-center gap-2">
204
+ <div class="size-2 rounded-full bg-yellow-500 animate-pulse"></div>
205
+ <p class="text-gray-300 text-xs font-medium uppercase tracking-wide">Waiting for inputs...</p>
206
+ </div>
207
+ </div>
208
+ </div>
209
+ </div>
210
+ </div>
211
+ <!-- Connector Right -->
212
+ <div class="hidden md:flex flex-col items-center justify-center w-16 xl:w-24 opacity-40">
213
+ <div class="w-full h-[2px] bg-gradient-to-r from-transparent via-primary to-transparent relative">
214
+ <div class="absolute right-0 -top-1.5 text-primary animate-pulse">
215
+ <span class="material-symbols-outlined text-lg">chevron_right</span>
216
+ </div>
217
+ </div>
218
+ </div>
219
+ <!-- SCENE C: Upload State -->
220
+ <div class="flex flex-col gap-4 flex-1 max-w-[320px] group">
221
+ <div class="flex items-center justify-between px-1">
222
+ <span class="text-xs font-bold tracking-widest text-primary">SCENE C</span>
223
+ <span class="text-xs font-bold tracking-widest text-gray-400 uppercase">Target Source</span>
224
+ </div>
225
+ <div
226
+ class="relative flex flex-col items-center justify-center gap-6 rounded-2xl border-2 border-dashed border-border-dark bg-surface-dark/30 hover:bg-surface-dark/50 hover:border-primary/50 transition-all duration-300 px-6 py-12 h-[360px]">
227
+ <div
228
+ class="size-16 rounded-full bg-surface-dark flex items-center justify-center mb-2 shadow-lg group-hover:scale-110 transition-transform">
229
+ <span class="material-symbols-outlined text-3xl text-gray-300 group-hover:text-white">movie_edit</span>
230
+ </div>
231
+ <div class="flex flex-col items-center gap-2 text-center">
232
+ <p class="text-white text-lg font-bold">End Scene</p>
233
+ <p class="text-gray-400 text-sm max-w-[200px]">Drag &amp; drop your target video clip here</p>
234
+ </div>
235
+ <input type="file" id="video-upload-c" accept="video/*,image/*" class="hidden"
236
+ onchange="handleFileSelect(this, 'label-c')">
237
+ <button onclick="document.getElementById('video-upload-c').click()"
238
+ class="mt-2 flex items-center justify-center rounded-lg h-9 px-4 bg-surface-dark hover:bg-primary hover:text-white border border-white/10 transition-all text-sm font-bold tracking-wide text-gray-200 shadow-lg">
239
+ <span class="material-symbols-outlined text-[18px] mr-2">upload</span>
240
+ <span id="label-c">Select File</span>
241
+ </button>
242
+ </div>
243
+ </div>
244
+ </div>
245
+ </main>
246
+ <!-- Floating Action Bar (Director Controls) -->
247
+ <div class="fixed bottom-10 left-1/2 -translate-x-1/2 z-50 w-full max-w-2xl px-4">
248
+ <div class="glass-panel rounded-full p-2 pl-6 flex items-center shadow-neon">
249
+ <div class="flex-1 flex items-center gap-3">
250
+ <span class="material-symbols-outlined text-gray-400">edit_note</span>
251
+ <input id="director-notes"
252
+ class="w-full bg-transparent border-none text-white placeholder-gray-500 focus:ring-0 text-sm font-medium"
253
+ placeholder="Add Director Notes (e.g., 'Slow dissolve', 'Cyber glitch')..." type="text" />
254
+ </div>
255
+ <div class="h-8 w-[1px] bg-white/10 mx-2"></div>
256
+ <button id="generate-btn"
257
+ class="flex items-center gap-2 bg-primary hover:bg-[#6b0bc9] text-white px-6 py-3 rounded-full font-bold text-sm transition-all shadow-[0_0_15px_rgba(127,13,242,0.4)] hover:shadow-[0_0_25px_rgba(127,13,242,0.6)] whitespace-nowrap">
258
+ <span class="material-symbols-outlined text-[20px]">movie_filter</span>
259
+ Generate Transition
260
+ </button>
261
+ </div>
262
+ </div>
263
+
264
+ <script>
265
+ // UI Helper: Update filename text when file is selected
266
+ function handleFileSelect(input, labelId) {
267
+ if (input.files && input.files[0]) {
268
+ const label = document.getElementById(labelId);
269
+ label.innerText = input.files[0].name;
270
+ label.classList.add("text-primary"); // Turn purple to indicate success
271
+ }
272
  }
 
273
 
274
+ // Main Logic: Send to Agent
275
+ const generateBtn = document.getElementById("generate-btn");
276
+ if (generateBtn) {
277
  generateBtn.addEventListener("click", async () => {
278
  const fileA = document.getElementById("video-upload-a").files[0];
279
  const fileC = document.getElementById("video-upload-c").files[0];
280
  const notes = document.getElementById("director-notes").value;
281
  const btn = document.getElementById("generate-btn");
282
+
283
  // 1. Validation
284
  if (!fileA || !fileC) {
285
  alert("⚠️ Action Required: Please upload both Scene A and Scene C videos.");
286
  return;
287
  }
288
+
289
  // 2. Loading State
290
  const originalContent = btn.innerHTML;
291
  btn.innerHTML = `<span class="material-symbols-outlined animate-spin text-[20px] mr-2">progress_activity</span> Director is working...`;
292
  btn.disabled = true;
293
  btn.classList.add("opacity-70", "cursor-not-allowed");
294
+
295
  const formData = new FormData();
296
  formData.append("video_a", fileA);
297
  formData.append("video_c", fileC);
298
  formData.append("prompt", notes || "Cinematic transition between scenes");
299
+
300
  try {
301
  // 3. Send to Server
302
  const response = await fetch("http://127.0.0.1:8000/generate-transition", {
303
  method: "POST",
304
  body: formData
305
  });
306
+
307
  if (!response.ok) throw new Error("Agent backend returned an error.");
308
+
309
  const data = await response.json();
310
+
311
  // 4. Success: Inject Video into the Middle Card
312
  // We target the middle "Bridge" div
313
  const bridgeCard = document.querySelector(".aspect-\\[4\\/3\\]");
 
318
  // Scene A: .aspect-[4/3]
319
  // Bridge: .aspect-[4/3] (line 148)
320
  // Scene C: .aspect-[4/3]
321
+
322
  // User script used document.querySelector(".aspect-\\[4\\/3\\]").
323
  // This matches the first one (Scene A)! That's a bug in the user's snippet.
324
  // I will fix it to target the BRIDGE card.
325
  // The bridge card container has text "The Bridge".
326
  // I'll add an ID to the bridge container or select by index.
327
  // Safer to select the middle one: document.querySelectorAll(".aspect-\\[4\\/3\\]")[1]
328
+
329
+ // Target the middle "Bridge" div (index 1)
330
  const cards = document.querySelectorAll(".aspect-\\[4\\/3\\]");
331
  if (cards.length >= 2) {
332
+ // Scene A is [0], Bridge is [1], Scene C is [2]
333
+ const bridgeCard = cards[1];
334
+ // Ensure we are replacing the inner content
335
+ bridgeCard.innerHTML = `
336
  <video controls autoplay loop class="w-full h-full object-cover rounded-2xl border-2 border-primary shadow-neon">
337
  <source src="http://127.0.0.1:8000${data.video_url}" type="video/mp4">
338
  Your browser does not support the video tag.
339
  </video>
340
  `;
341
+ } else {
342
+ console.error("Could not find the bridge card");
343
+ alert("Video generated, but could not place it in UI. Check console.");
344
  }
345
+
346
  } catch (error) {
347
  console.error(error);
348
  alert("❌ Generation Failed: " + error.message + "\n\nMake sure 'python server.py' is running!");
 
352
  btn.classList.remove("opacity-70", "cursor-not-allowed");
353
  }
354
  });
355
+ }
356
+ </script>
357
+ </body>
358
+
359
+ </html>