HI7RAI commited on
Commit
2db0892
·
verified ·
1 Parent(s): 97d9894

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +1320 -19
index.html CHANGED
@@ -1,19 +1,1320 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Video Processing Workflow</title>
7
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
8
+ <style>
9
+ * {
10
+ margin: 0;
11
+ padding: 0;
12
+ box-sizing: border-box;
13
+ }
14
+
15
+ :root {
16
+ --bg-dark: #1a1a1f;
17
+ --bg-card: #252530;
18
+ --bg-input: #1e1e28;
19
+ --text-primary: #e0e0e0;
20
+ --text-secondary: #888;
21
+ --border-color: #3a3a4a;
22
+ --accent: #6366f1;
23
+ --accent-hover: #818cf8;
24
+ --node-header-height: 28px;
25
+ }
26
+
27
+ body {
28
+ font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
29
+ background: var(--bg-dark);
30
+ color: var(--text-primary);
31
+ overflow: hidden;
32
+ height: 100vh;
33
+ }
34
+
35
+ /* Header */
36
+ .header {
37
+ background: linear-gradient(135deg, #252530 0%, #1a1a1f 100%);
38
+ padding: 12px 24px;
39
+ display: flex;
40
+ align-items: center;
41
+ justify-content: space-between;
42
+ border-bottom: 1px solid var(--border-color);
43
+ box-shadow: 0 2px 10px rgba(0,0,0,0.3);
44
+ z-index: 100;
45
+ position: relative;
46
+ }
47
+
48
+ .header-left {
49
+ display: flex;
50
+ align-items: center;
51
+ gap: 16px;
52
+ }
53
+
54
+ .logo {
55
+ font-size: 20px;
56
+ font-weight: 700;
57
+ background: linear-gradient(135deg, #6366f1, #a855f7);
58
+ -webkit-background-clip: text;
59
+ -webkit-text-fill-color: transparent;
60
+ background-clip: text;
61
+ }
62
+
63
+ .logo a {
64
+ text-decoration: none;
65
+ color: inherit;
66
+ }
67
+
68
+ .anycoder-link {
69
+ font-size: 12px;
70
+ color: var(--text-secondary);
71
+ text-decoration: none;
72
+ padding: 4px 10px;
73
+ background: rgba(99, 102, 241, 0.1);
74
+ border-radius: 12px;
75
+ border: 1px solid rgba(99, 102, 241, 0.3);
76
+ transition: all 0.3s ease;
77
+ }
78
+
79
+ .anycoder-link:hover {
80
+ background: rgba(99, 102, 241, 0.2);
81
+ border-color: rgba(99, 102, 241, 0.5);
82
+ }
83
+
84
+ .header-title {
85
+ font-size: 14px;
86
+ color: var(--text-secondary);
87
+ }
88
+
89
+ .header-actions {
90
+ display: flex;
91
+ gap: 12px;
92
+ }
93
+
94
+ .btn {
95
+ padding: 8px 16px;
96
+ border: none;
97
+ border-radius: 6px;
98
+ cursor: pointer;
99
+ font-size: 13px;
100
+ font-weight: 500;
101
+ transition: all 0.2s ease;
102
+ display: flex;
103
+ align-items: center;
104
+ gap: 6px;
105
+ }
106
+
107
+ .btn-primary {
108
+ background: var(--accent);
109
+ color: white;
110
+ }
111
+
112
+ .btn-primary:hover {
113
+ background: var(--accent-hover);
114
+ transform: translateY(-1px);
115
+ }
116
+
117
+ .btn-secondary {
118
+ background: var(--bg-card);
119
+ color: var(--text-primary);
120
+ border: 1px solid var(--border-color);
121
+ }
122
+
123
+ .btn-secondary:hover {
124
+ background: var(--border-color);
125
+ }
126
+
127
+ /* Toolbar */
128
+ .toolbar {
129
+ background: var(--bg-card);
130
+ padding: 8px 16px;
131
+ display: flex;
132
+ align-items: center;
133
+ gap: 8px;
134
+ border-bottom: 1px solid var(--border-color);
135
+ }
136
+
137
+ .toolbar-btn {
138
+ width: 32px;
139
+ height: 32px;
140
+ border: none;
141
+ background: transparent;
142
+ color: var(--text-secondary);
143
+ border-radius: 6px;
144
+ cursor: pointer;
145
+ display: flex;
146
+ align-items: center;
147
+ justify-content: center;
148
+ transition: all 0.2s ease;
149
+ }
150
+
151
+ .toolbar-btn:hover {
152
+ background: var(--border-color);
153
+ color: var(--text-primary);
154
+ }
155
+
156
+ .toolbar-btn.active {
157
+ background: var(--accent);
158
+ color: white;
159
+ }
160
+
161
+ .toolbar-divider {
162
+ width: 1px;
163
+ height: 24px;
164
+ background: var(--border-color);
165
+ margin: 0 8px;
166
+ }
167
+
168
+ /* Main Workspace */
169
+ .workspace {
170
+ display: flex;
171
+ height: calc(100vh - 100px);
172
+ }
173
+
174
+ /* Sidebar */
175
+ .sidebar {
176
+ width: 260px;
177
+ background: var(--bg-card);
178
+ border-right: 1px solid var(--border-color);
179
+ display: flex;
180
+ flex-direction: column;
181
+ transition: transform 0.3s ease;
182
+ }
183
+
184
+ .sidebar.collapsed {
185
+ transform: translateX(-260px);
186
+ }
187
+
188
+ .sidebar-header {
189
+ padding: 16px;
190
+ border-bottom: 1px solid var(--border-color);
191
+ font-weight: 600;
192
+ font-size: 14px;
193
+ display: flex;
194
+ align-items: center;
195
+ gap: 8px;
196
+ }
197
+
198
+ .sidebar-content {
199
+ flex: 1;
200
+ overflow-y: auto;
201
+ padding: 12px;
202
+ }
203
+
204
+ .node-template {
205
+ background: var(--bg-input);
206
+ border: 1px solid var(--border-color);
207
+ border-radius: 8px;
208
+ padding: 12px;
209
+ margin-bottom: 8px;
210
+ cursor: grab;
211
+ transition: all 0.2s ease;
212
+ }
213
+
214
+ .node-template:hover {
215
+ border-color: var(--accent);
216
+ transform: translateX(4px);
217
+ }
218
+
219
+ .node-template-name {
220
+ font-weight: 500;
221
+ font-size: 13px;
222
+ margin-bottom: 4px;
223
+ }
224
+
225
+ .node-template-type {
226
+ font-size: 11px;
227
+ color: var(--text-secondary);
228
+ }
229
+
230
+ /* Canvas Area */
231
+ .canvas-container {
232
+ flex: 1;
233
+ position: relative;
234
+ overflow: hidden;
235
+ background:
236
+ radial-gradient(circle at 50% 50%, rgba(99, 102, 241, 0.03) 0%, transparent 50%),
237
+ linear-gradient(rgba(40, 40, 55, 0.3) 1px, transparent 1px),
238
+ linear-gradient(90deg, rgba(40, 40, 55, 0.3) 1px, transparent 1px);
239
+ background-size: 100% 100%, 30px 30px, 30px 30px;
240
+ }
241
+
242
+ .canvas {
243
+ position: absolute;
244
+ width: 100%;
245
+ height: 100%;
246
+ transform-origin: 0 0;
247
+ }
248
+
249
+ /* Groups */
250
+ .group {
251
+ position: absolute;
252
+ border: 2px dashed;
253
+ border-radius: 8px;
254
+ pointer-events: none;
255
+ }
256
+
257
+ .group-title {
258
+ position: absolute;
259
+ top: -20px;
260
+ left: 8px;
261
+ font-size: 12px;
262
+ font-weight: 600;
263
+ padding: 2px 8px;
264
+ border-radius: 4px;
265
+ white-space: nowrap;
266
+ }
267
+
268
+ /* Nodes */
269
+ .node {
270
+ position: absolute;
271
+ background: var(--bg-card);
272
+ border-radius: 8px;
273
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
274
+ border: 1px solid var(--border-color);
275
+ min-width: 200px;
276
+ cursor: move;
277
+ transition: box-shadow 0.2s ease, border-color 0.2s ease;
278
+ user-select: none;
279
+ }
280
+
281
+ .node:hover {
282
+ box-shadow: 0 6px 30px rgba(0, 0, 0, 0.5);
283
+ }
284
+
285
+ .node.selected {
286
+ border-color: var(--accent);
287
+ box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.3), 0 6px 30px rgba(0, 0, 0, 0.5);
288
+ }
289
+
290
+ .node-header {
291
+ height: var(--node-header-height);
292
+ padding: 0 12px;
293
+ display: flex;
294
+ align-items: center;
295
+ justify-content: space-between;
296
+ border-radius: 8px 8px 0 0;
297
+ font-size: 12px;
298
+ font-weight: 600;
299
+ }
300
+
301
+ .node-title {
302
+ display: flex;
303
+ align-items: center;
304
+ gap: 6px;
305
+ white-space: nowrap;
306
+ overflow: hidden;
307
+ text-overflow: ellipsis;
308
+ }
309
+
310
+ .node-actions {
311
+ display: flex;
312
+ gap: 4px;
313
+ opacity: 0;
314
+ transition: opacity 0.2s ease;
315
+ }
316
+
317
+ .node:hover .node-actions {
318
+ opacity: 1;
319
+ }
320
+
321
+ .node-action-btn {
322
+ width: 20px;
323
+ height: 20px;
324
+ border: none;
325
+ background: rgba(255, 255, 255, 0.1);
326
+ color: var(--text-primary);
327
+ border-radius: 4px;
328
+ cursor: pointer;
329
+ font-size: 10px;
330
+ display: flex;
331
+ align-items: center;
332
+ justify-content: center;
333
+ transition: background 0.2s ease;
334
+ }
335
+
336
+ .node-action-btn:hover {
337
+ background: rgba(255, 255, 255, 0.2);
338
+ }
339
+
340
+ .node-action-btn.close:hover {
341
+ background: #ef4444;
342
+ }
343
+
344
+ .node-content {
345
+ padding: 8px 12px 12px;
346
+ }
347
+
348
+ /* Slots */
349
+ .node-slots {
350
+ display: flex;
351
+ justify-content: space-between;
352
+ margin-bottom: 8px;
353
+ }
354
+
355
+ .slots-group {
356
+ display: flex;
357
+ flex-direction: column;
358
+ gap: 6px;
359
+ }
360
+
361
+ .slot {
362
+ display: flex;
363
+ align-items: center;
364
+ gap: 8px;
365
+ padding: 4px 8px;
366
+ border-radius: 4px;
367
+ cursor: pointer;
368
+ transition: background 0.2s ease;
369
+ min-width: 100px;
370
+ }
371
+
372
+ .slot:hover {
373
+ background: rgba(255, 255, 255, 0.05);
374
+ }
375
+
376
+ .slot.input {
377
+ justify-content: flex-start;
378
+ }
379
+
380
+ .slot.output {
381
+ justify-content: flex-end;
382
+ flex-direction: row-reverse;
383
+ }
384
+
385
+ .slot-socket {
386
+ width: 12px;
387
+ height: 12px;
388
+ border-radius: 50%;
389
+ border: 2px solid;
390
+ flex-shrink: 0;
391
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
392
+ }
393
+
394
+ .slot-socket:hover {
395
+ transform: scale(1.3);
396
+ box-shadow: 0 0 10px currentColor;
397
+ }
398
+
399
+ .slot.input .slot-socket {
400
+ background: var(--bg-input);
401
+ }
402
+
403
+ .slot.output .slot-socket {
404
+ background: var(--bg-input);
405
+ }
406
+
407
+ .slot-label {
408
+ font-size: 11px;
409
+ color: var(--text-secondary);
410
+ white-space: nowrap;
411
+ }
412
+
413
+ .slot-type {
414
+ font-size: 9px;
415
+ padding: 1px 4px;
416
+ border-radius: 3px;
417
+ opacity: 0.7;
418
+ }
419
+
420
+ /* Widgets */
421
+ .node-widgets {
422
+ display: flex;
423
+ flex-direction: column;
424
+ gap: 8px;
425
+ }
426
+
427
+ .widget {
428
+ display: flex;
429
+ flex-direction: column;
430
+ gap: 4px;
431
+ }
432
+
433
+ .widget-label {
434
+ font-size: 10px;
435
+ color: var(--text-secondary);
436
+ text-transform: uppercase;
437
+ letter-spacing: 0.5px;
438
+ }
439
+
440
+ .widget input[type="text"],
441
+ .widget input[type="number"] {
442
+ background: var(--bg-input);
443
+ border: 1px solid var(--border-color);
444
+ border-radius: 4px;
445
+ padding: 6px 10px;
446
+ color: var(--text-primary);
447
+ font-size: 12px;
448
+ width: 100%;
449
+ transition: border-color 0.2s ease;
450
+ }
451
+
452
+ .widget input:focus {
453
+ outline: none;
454
+ border-color: var(--accent);
455
+ }
456
+
457
+ .widget input[type="range"] {
458
+ width: 100%;
459
+ height: 4px;
460
+ border-radius: 2px;
461
+ background: var(--bg-input);
462
+ -webkit-appearance: none;
463
+ appearance: none;
464
+ cursor: pointer;
465
+ }
466
+
467
+ .widget input[type="range"]::-webkit-slider-thumb {
468
+ -webkit-appearance: none;
469
+ width: 14px;
470
+ height: 14px;
471
+ border-radius: 50%;
472
+ background: var(--accent);
473
+ cursor: pointer;
474
+ transition: transform 0.2s ease;
475
+ }
476
+
477
+ .widget input[type="range"]::-webkit-slider-thumb:hover {
478
+ transform: scale(1.2);
479
+ }
480
+
481
+ .widget select {
482
+ background: var(--bg-input);
483
+ border: 1px solid var(--border-color);
484
+ border-radius: 4px;
485
+ padding: 6px 10px;
486
+ color: var(--text-primary);
487
+ font-size: 12px;
488
+ width: 100%;
489
+ cursor: pointer;
490
+ }
491
+
492
+ .widget button {
493
+ background: var(--accent);
494
+ border: none;
495
+ border-radius: 4px;
496
+ padding: 8px 16px;
497
+ color: white;
498
+ font-size: 12px;
499
+ font-weight: 500;
500
+ cursor: pointer;
501
+ transition: all 0.2s ease;
502
+ }
503
+
504
+ .widget button:hover {
505
+ background: var(--accent-hover);
506
+ }
507
+
508
+ .widget button:active {
509
+ transform: scale(0.98);
510
+ }
511
+
512
+ .widget-checkbox {
513
+ display: flex;
514
+ align-items: center;
515
+ gap: 8px;
516
+ }
517
+
518
+ .widget-checkbox input {
519
+ width: 16px;
520
+ height: 16px;
521
+ accent-color: var(--accent);
522
+ cursor: pointer;
523
+ }
524
+
525
+ .widget-checkbox label {
526
+ font-size: 12px;
527
+ cursor: pointer;
528
+ }
529
+
530
+ /* Connections SVG */
531
+ .connections-svg {
532
+ position: absolute;
533
+ top: 0;
534
+ left: 0;
535
+ width: 100%;
536
+ height: 100%;
537
+ pointer-events: none;
538
+ z-index: 1;
539
+ }
540
+
541
+ .connection-path {
542
+ fill: none;
543
+ stroke-width: 2;
544
+ stroke-linecap: round;
545
+ transition: stroke-width 0.2s ease;
546
+ }
547
+
548
+ .connection-path:hover {
549
+ stroke-width: 3;
550
+ }
551
+
552
+ /* Mini Map */
553
+ .minimap {
554
+ position: absolute;
555
+ bottom: 20px;
556
+ right: 20px;
557
+ width: 200px;
558
+ height: 150px;
559
+ background: rgba(37, 37, 48, 0.9);
560
+ border: 1px solid var(--border-color);
561
+ border-radius: 8px;
562
+ overflow: hidden;
563
+ z-index: 50;
564
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
565
+ }
566
+
567
+ .minimap-canvas {
568
+ width: 100%;
569
+ height: 100%;
570
+ }
571
+
572
+ /* Properties Panel */
573
+ .properties-panel {
574
+ width: 300px;
575
+ background: var(--bg-card);
576
+ border-left: 1px solid var(--border-color);
577
+ display: flex;
578
+ flex-direction: column;
579
+ transition: transform 0.3s ease;
580
+ }
581
+
582
+ .properties-panel.collapsed {
583
+ transform: translateX(300px);
584
+ }
585
+
586
+ .properties-header {
587
+ padding: 16px;
588
+ border-bottom: 1px solid var(--border-color);
589
+ font-weight: 600;
590
+ display: flex;
591
+ align-items: center;
592
+ justify-content: space-between;
593
+ }
594
+
595
+ .properties-content {
596
+ flex: 1;
597
+ overflow-y: auto;
598
+ padding: 16px;
599
+ }
600
+
601
+ .property-item {
602
+ margin-bottom: 16px;
603
+ }
604
+
605
+ .property-label {
606
+ font-size: 12px;
607
+ color: var(--text-secondary);
608
+ margin-bottom: 6px;
609
+ }
610
+
611
+ .property-value {
612
+ font-size: 14px;
613
+ padding: 8px 12px;
614
+ background: var(--bg-input);
615
+ border-radius: 4px;
616
+ border: 1px solid var(--border-color);
617
+ }
618
+
619
+ /* Status Bar */
620
+ .status-bar {
621
+ background: var(--bg-card);
622
+ padding: 6px 16px;
623
+ display: flex;
624
+ align-items: center;
625
+ justify-content: space-between;
626
+ border-top: 1px solid var(--border-color);
627
+ font-size: 11px;
628
+ color: var(--text-secondary);
629
+ }
630
+
631
+ .status-items {
632
+ display: flex;
633
+ gap: 16px;
634
+ }
635
+
636
+ .status-item {
637
+ display: flex;
638
+ align-items: center;
639
+ gap: 6px;
640
+ }
641
+
642
+ .status-dot {
643
+ width: 8px;
644
+ height: 8px;
645
+ border-radius: 50%;
646
+ background: #22c55e;
647
+ }
648
+
649
+ /* Preview Panel */
650
+ .preview-panel {
651
+ position: absolute;
652
+ top: 20px;
653
+ right: 20px;
654
+ width: 320px;
655
+ background: var(--bg-card);
656
+ border: 1px solid var(--border-color);
657
+ border-radius: 8px;
658
+ z-index: 50;
659
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
660
+ display: none;
661
+ }
662
+
663
+ .preview-panel.visible {
664
+ display: block;
665
+ }
666
+
667
+ .preview-header {
668
+ padding: 12px 16px;
669
+ border-bottom: 1px solid var(--border-color);
670
+ display: flex;
671
+ align-items: center;
672
+ justify-content: space-between;
673
+ font-weight: 600;
674
+ }
675
+
676
+ .preview-close {
677
+ background: none;
678
+ border: none;
679
+ color: var(--text-secondary);
680
+ cursor: pointer;
681
+ font-size: 14px;
682
+ }
683
+
684
+ .preview-content {
685
+ padding: 16px;
686
+ }
687
+
688
+ .preview-canvas {
689
+ width: 100%;
690
+ aspect-ratio: 16/9;
691
+ background: var(--bg-input);
692
+ border-radius: 4px;
693
+ display: flex;
694
+ align-items: center;
695
+ justify-content: center;
696
+ color: var(--text-secondary);
697
+ }
698
+
699
+ .preview-controls {
700
+ display: flex;
701
+ gap: 8px;
702
+ margin-top: 12px;
703
+ }
704
+
705
+ /* Animations */
706
+ @keyframes nodeAppear {
707
+ from {
708
+ opacity: 0;
709
+ transform: scale(0.9);
710
+ }
711
+ to {
712
+ opacity: 1;
713
+ transform: scale(1);
714
+ }
715
+ }
716
+
717
+ .node {
718
+ animation: nodeAppear 0.3s ease;
719
+ }
720
+
721
+ /* Scrollbar */
722
+ ::-webkit-scrollbar {
723
+ width: 8px;
724
+ height: 8px;
725
+ }
726
+
727
+ ::-webkit-scrollbar-track {
728
+ background: var(--bg-dark);
729
+ }
730
+
731
+ ::-webkit-scrollbar-thumb {
732
+ background: var(--border-color);
733
+ border-radius: 4px;
734
+ }
735
+
736
+ ::-webkit-scrollbar-thumb:hover {
737
+ background: var(--text-secondary);
738
+ }
739
+
740
+ /* Context Menu */
741
+ .context-menu {
742
+ position: fixed;
743
+ background: var(--bg-card);
744
+ border: 1px solid var(--border-color);
745
+ border-radius: 8px;
746
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
747
+ padding: 8px 0;
748
+ z-index: 1000;
749
+ min-width: 180px;
750
+ display: none;
751
+ }
752
+
753
+ .context-menu.visible {
754
+ display: block;
755
+ }
756
+
757
+ .context-menu-item {
758
+ padding: 8px 16px;
759
+ cursor: pointer;
760
+ display: flex;
761
+ align-items: center;
762
+ gap: 10px;
763
+ font-size: 13px;
764
+ transition: background 0.2s ease;
765
+ }
766
+
767
+ .context-menu-item:hover {
768
+ background: var(--border-color);
769
+ }
770
+
771
+ .context-menu-item i {
772
+ width: 16px;
773
+ color: var(--text-secondary);
774
+ }
775
+
776
+ .context-menu-divider {
777
+ height: 1px;
778
+ background: var(--border-color);
779
+ margin: 4px 0;
780
+ }
781
+
782
+ /* Tooltip */
783
+ .tooltip {
784
+ position: fixed;
785
+ background: var(--bg-dark);
786
+ border: 1px solid var(--border-color);
787
+ padding: 6px 10px;
788
+ border-radius: 4px;
789
+ font-size: 11px;
790
+ z-index: 1000;
791
+ pointer-events: none;
792
+ display: none;
793
+ }
794
+
795
+ .tooltip.visible {
796
+ display: block;
797
+ }
798
+
799
+ /* Responsive */
800
+ @media (max-width: 1024px) {
801
+ .sidebar {
802
+ position: absolute;
803
+ z-index: 50;
804
+ height: calc(100vh - 100px);
805
+ }
806
+
807
+ .properties-panel {
808
+ position: absolute;
809
+ right: 0;
810
+ height: calc(100vh - 100px);
811
+ }
812
+ }
813
+
814
+ @media (max-width: 768px) {
815
+ .header {
816
+ padding: 8px 16px;
817
+ }
818
+
819
+ .logo {
820
+ font-size: 16px;
821
+ }
822
+
823
+ .anycoder-link {
824
+ display: none;
825
+ }
826
+ }
827
+ </style>
828
+ </head>
829
+ <body>
830
+ <!-- Header -->
831
+ <header class="header">
832
+ <div class="header-left">
833
+ <div class="logo">
834
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank">🎬 VideoFlow</a>
835
+ </div>
836
+ <span class="header-title">Video Processing Workflow</span>
837
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">Built with anycoder</a>
838
+ </div>
839
+ <div class="header-actions">
840
+ <button class="btn btn-secondary" onclick="exportWorkflow()">
841
+ <i class="fas fa-download"></i> Export
842
+ </button>
843
+ <button class="btn btn-secondary" onclick="importWorkflow()">
844
+ <i class="fas fa-upload"></i> Import
845
+ </button>
846
+ <button class="btn btn-primary" onclick="runWorkflow()">
847
+ <i class="fas fa-play"></i> Run
848
+ </button>
849
+ </div>
850
+ </header>
851
+
852
+ <!-- Toolbar -->
853
+ <div class="toolbar">
854
+ <button class="toolbar-btn" title="Zoom In" onclick="zoomIn()">
855
+ <i class="fas fa-plus"></i>
856
+ </button>
857
+ <button class="toolbar-btn" title="Zoom Out" onclick="zoomOut()">
858
+ <i class="fas fa-minus"></i>
859
+ </button>
860
+ <button class="toolbar-btn" title="Fit View" onclick="fitView()">
861
+ <i class="fas fa-expand"></i>
862
+ </button>
863
+ <button class="toolbar-btn" title="Grid" id="gridToggle" onclick="toggleGrid()">
864
+ <i class="fas fa-th"></i>
865
+ </button>
866
+ <div class="toolbar-divider"></div>
867
+ <button class="toolbar-btn" title="Add Node" onclick="showNodePalette()">
868
+ <i class="fas fa-plus-circle"></i>
869
+ </button>
870
+ <button class="toolbar-btn" title="Clear All" onclick="clearAll()">
871
+ <i class="fas fa-trash-alt"></i>
872
+ </button>
873
+ <div class="toolbar-divider"></div>
874
+ <button class="toolbar-btn" title="Play/Pause" id="playPauseBtn" onclick="togglePlayback()">
875
+ <i class="fas fa-play"></i>
876
+ </button>
877
+ <button class="toolbar-btn" title="Preview" onclick="togglePreview()">
878
+ <i class="fas fa-eye"></i>
879
+ </button>
880
+ </div>
881
+
882
+ <!-- Main Workspace -->
883
+ <div class="workspace">
884
+ <!-- Sidebar -->
885
+ <aside class="sidebar" id="sidebar">
886
+ <div class="sidebar-header">
887
+ <i class="fas fa-cube"></i> Node Library
888
+ </div>
889
+ <div class="sidebar-content">
890
+ <div class="node-template" draggable="true" data-type="VideoInput">
891
+ <div class="node-template-name"><i class="fas fa-video"></i> Video Input</div>
892
+ <div class="node-template-type">Input source for video files</div>
893
+ </div>
894
+ <div class="node-template" draggable="true" data-type="PrimitiveNode">
895
+ <div class="node-template-name"><i class="fas fa-hashtag"></i> Primitive</div>
896
+ <div class="node-template-type">Basic value input node</div>
897
+ </div>
898
+ <div class="node-template" draggable="true" data-type="VideoFrameSampler">
899
+ <div class="node-template-name"><i class="fas fa-images"></i> Frame Sampler</div>
900
+ <div class="node-template-type">Extract frames from video</div>
901
+ </div>
902
+ <div class="node-template" draggable="true" data-type="CanvasAutoCrop">
903
+ <div class="node-template-name"><i class="fas fa-crop-alt"></i> Auto Crop</div>
904
+ <div class="node-template-type">Automatically crop images</div>
905
+ </div>
906
+ <div class="node-template" draggable="true" data-type="VideoMatchMixer">
907
+ <div class="node-template-name"><i class="fas fa-layer-group"></i> Match Mixer</div>
908
+ <div class="node-template-type">Blend frames with formula</div>
909
+ </div>
910
+ <div class="node-template" draggable="true" data-type="CanvasEffect">
911
+ <div class="node-template-name"><i class="fas fa-magic"></i> Canvas Effect</div>
912
+ <div class="node-template-type">Apply image effects</div>
913
+ </div>
914
+ <div class="node-template" draggable="true" data-type="SaveVideo">
915
+ <div class="node-template-name"><i class="fas fa-save"></i> Save Video</div>
916
+ <div class="node-template-type">Export processed video</div>
917
+ </div>
918
+ <div class="node-template" draggable="true" data-type="PreviewVideo">
919
+ <div class="node-template-name"><i class="fas fa-play-circle"></i> Preview</div>
920
+ <div class="node-template-type">Preview output frames</div>
921
+ </div>
922
+ <div class="node-template" draggable="true" data-type="JavaScriptExecute">
923
+ <div class="node-template-name"><i class="fab fa-js"></i> JS Controller</div>
924
+ <div class="node-template-type">JavaScript execution</div>
925
+ </div>
926
+ <div class="node-template" draggable="true" data-type="CanvasDisplay">
927
+ <div class="node-template-name"><i class="fas fa-desktop"></i> Canvas Display</div>
928
+ <div class="node-template-type">Display processed frames</div>
929
+ </div>
930
+ </div>
931
+ </aside>
932
+
933
+ <!-- Canvas -->
934
+ <div class="canvas-container" id="canvasContainer">
935
+ <div class="canvas" id="canvas">
936
+ <svg class="connections-svg" id="connectionsSvg"></svg>
937
+ <div class="groups" id="groups"></div>
938
+ <div class="nodes" id="nodes"></div>
939
+ </div>
940
+
941
+ <!-- Mini Map -->
942
+ <div class="minimap" id="minimap">
943
+ <canvas class="minimap-canvas" id="minimapCanvas"></canvas>
944
+ </div>
945
+
946
+ <!-- Preview Panel -->
947
+ <div class="preview-panel" id="previewPanel">
948
+ <div class="preview-header">
949
+ <span>Preview Output</span>
950
+ <button class="preview-close" onclick="togglePreview()">
951
+ <i class="fas fa-times"></i>
952
+ </button>
953
+ </div>
954
+ <div class="preview-content">
955
+ <div class="preview-canvas" id="previewCanvas">
956
+ <i class="fas fa-film" style="font-size: 48px; margin-bottom: 8px;"></i>
957
+ <div>Preview will appear here</div>
958
+ </div>
959
+ <div class="preview-controls">
960
+ <button class="btn btn-primary" style="flex: 1;" onclick="playPreview()">
961
+ <i class="fas fa-play"></i> Play
962
+ </button>
963
+ <button class="btn btn-secondary" onclick="pausePreview()">
964
+ <i class="fas fa-pause"></i>
965
+ </button>
966
+ <button class="btn btn-secondary" onclick="stopPreview()">
967
+ <i class="fas fa-stop"></i>
968
+ </button>
969
+ </div>
970
+ </div>
971
+ </div>
972
+ </div>
973
+
974
+ <!-- Properties Panel -->
975
+ <aside class="properties-panel" id="propertiesPanel">
976
+ <div class="properties-header">
977
+ <span>Properties</span>
978
+ <button class="toolbar-btn" onclick="togglePropertiesPanel()">
979
+ <i class="fas fa-chevron-right"></i>
980
+ </button>
981
+ </div>
982
+ <div class="properties-content" id="propertiesContent">
983
+ <div style="color: var(--text-secondary); text-align: center; padding: 20px;">
984
+ <i class="fas fa-info-circle" style="font-size: 24px; margin-bottom: 8px;"></i>
985
+ <div>Select a node to view properties</div>
986
+ </div>
987
+ </div>
988
+ </aside>
989
+ </div>
990
+
991
+ <!-- Status Bar -->
992
+ <footer class="status-bar">
993
+ <div class="status-items">
994
+ <div class="status-item">
995
+ <div class="status-dot" id="statusDot"></div>
996
+ <span id="statusText">Ready</span>
997
+ </div>
998
+ <div class="status-item">
999
+ <i class="fas fa-mouse-pointer"></i>
1000
+ <span id="cursorPos">0, 0</span>
1001
+ </div>
1002
+ </div>
1003
+ <div class="status-items">
1004
+ <div class="status-item">
1005
+ <i class="fas fa-layer-group"></i>
1006
+ <span id="nodeCount">0 nodes</span>
1007
+ </div>
1008
+ <div class="status-item">
1009
+ <i class="fas fa-link"></i>
1010
+ <span id="linkCount">0 links</span>
1011
+ </div>
1012
+ <div class="status-item">
1013
+ <i class="fas fa-search"></i>
1014
+ <span id="zoomLevel">100%</span>
1015
+ </div>
1016
+ </div>
1017
+ </footer>
1018
+
1019
+ <!-- Context Menu -->
1020
+ <div class="context-menu" id="contextMenu">
1021
+ <div class="context-menu-item" onclick="duplicateSelected()">
1022
+ <i class="fas fa-copy"></i> Duplicate
1023
+ </div>
1024
+ <div class="context-menu-item" onclick="copySelected()">
1025
+ <i class="fas fa-cut"></i> Copy
1026
+ </div>
1027
+ <div class="context-menu-item" onclick="pasteSelected()">
1028
+ <i class="fas fa-paste"></i> Paste
1029
+ </div>
1030
+ <div class="context-menu-divider"></div>
1031
+ <div class="context-menu-item" onclick="bringToFront()">
1032
+ <i class="fas fa-arrow-up"></i> Bring to Front
1033
+ </div>
1034
+ <div class="context-menu-item" onclick="sendToBack()">
1035
+ <i class="fas fa-arrow-down"></i> Send to Back
1036
+ </div>
1037
+ <div class="context-menu-divider"></div>
1038
+ <div class="context-menu-item" onclick="deleteSelected()">
1039
+ <i class="fas fa-trash"></i> Delete
1040
+ </div>
1041
+ </div>
1042
+
1043
+ <!-- Tooltip -->
1044
+ <div class="tooltip" id="tooltip"></div>
1045
+
1046
+ <script>
1047
+ // Workflow Data
1048
+ let workflowData = null;
1049
+ let nodes = [];
1050
+ let links = [];
1051
+ let groups = [];
1052
+ let selectedNodes = [];
1053
+ let currentZoom = 1;
1054
+ let panOffset = { x: 0, y: 0 };
1055
+ let isDragging = false;
1056
+ let isPanning = false;
1057
+ let dragStart = { x: 0, y: 0 };
1058
+ let draggedNode = null;
1059
+ let connectionStart = null;
1060
+ let clipboard = null;
1061
+
1062
+ // Initialize
1063
+ document.addEventListener('DOMContentLoaded', () => {
1064
+ loadWorkflowFromJSON();
1065
+ setupEventListeners();
1066
+ startRenderLoop();
1067
+ });
1068
+
1069
+ // Load workflow from JSON
1070
+ function loadWorkflowFromJSON() {
1071
+ const jsonData = `<?json_output>`;
1072
+ try {
1073
+ workflowData = JSON.parse(jsonData.replace(/```json|```/g, '').trim());
1074
+ if (workflowData.nodes) {
1075
+ nodes = workflowData.nodes.map(node => ({
1076
+ ...node,
1077
+ x: node.pos[0],
1078
+ y: node.pos[1]
1079
+ }));
1080
+ }
1081
+ if (workflowData.links) {
1082
+ links = workflowData.links.map(link => ({
1083
+ source: link[2],
1084
+ target: link[0],
1085
+ sourceOutput: link[3],
1086
+ targetInput: link[1]
1087
+ }));
1088
+ }
1089
+ if (workflowData.groups) {
1090
+ groups = workflowData.groups;
1091
+ }
1092
+ updateStats();
1093
+ renderAll();
1094
+ } catch (e) {
1095
+ console.error('Failed to load workflow:', e);
1096
+ // Use default workflow data
1097
+ initializeDefaultWorkflow();
1098
+ }
1099
+ }
1100
+
1101
+ function initializeDefaultWorkflow() {
1102
+ // Default nodes
1103
+ nodes = [
1104
+ {
1105
+ id: 10,
1106
+ type: 'JavaScriptExecute',
1107
+ title: 'JS Controller',
1108
+ x: 100,
1109
+ y: 100,
1110
+ width: 400,
1111
+ height: 150,
1112
+ color: '#442266',
1113
+ bgcolor: '#553377',
1114
+ inputs: [],
1115
+ outputs: [{ name: 'trigger', type: 'EVENT', slotIndex: 0 }],
1116
+ widgets: [
1117
+ { name: 'video_button', label: 'Video Input Button', type: 'button', value: 'Upload Video' },
1118
+ { name: 'fps_input', label: 'FPS Input', type: 'number', value: 30, min: 1, max: 120 },
1119
+ { name: 'formula_input', label: 'JS Math Formula', type: 'text', value: 'Math.sin(x) * Math.cos(y)' },
1120
+ { name: 'rematch_button', label: 'Rematch Button', type: 'button', value: 'Rematch' }
1121
+ ]
1122
+ },
1123
+ {
1124
+ id: 1,
1125
+ type: 'VideoInput',
1126
+ title: 'VideoInput',
1127
+ x: 100,
1128
+ y: 300,
1129
+ width: 315,
1130
+ height: 106,
1131
+ color: '#323224',
1132
+ bgcolor: '#434330',
1133
+ inputs: [],
1134
+ outputs: [{ name: 'VIDEO', type: 'VIDEO', slotIndex: 0 }],
1135
+ widgets: [
1136
+ { name: 'video', label: 'Select Video', type: 'file' }
1137
+ ]
1138
+ },
1139
+ {
1140
+ id: 2,
1141
+ type: 'PrimitiveNode',
1142
+ title: 'FPS_Input',
1143
+ x: 100,
1144
+ y: 450,
1145
+ width: 200,
1146
+ height: 50,
1147
+ color: '#223322',
1148
+ bgcolor: '#334433',
1149
+ inputs: [],
1150
+ outputs: [{ name: 'value', type: 'FLOAT', slotIndex: 0 }],
1151
+ widgets: [
1152
+ { name: 'value', label: 'FPS', type: 'number', value: 30, min: 1, max: 120 }
1153
+ ]
1154
+ },
1155
+ {
1156
+ id: 3,
1157
+ type: 'VideoFrameSampler',
1158
+ title: 'VideoFrameSampler',
1159
+ x: 500,
1160
+ y: 300,
1161
+ width: 315,
1162
+ height: 150,
1163
+ color: '#222244',
1164
+ bgcolor: '#333355',
1165
+ inputs: [
1166
+ { name: 'video', type: 'VIDEO' },
1167
+ { name: 'fps', type: 'FLOAT' }
1168
+ ],
1169
+ outputs: [
1170
+ { name: 'frames', type: 'IMAGE', slotIndex: 0 },
1171
+ { name: 'frame_count', type: 'INT', slotIndex: 1 }
1172
+ ],
1173
+ widgets: [
1174
+ { name: 'start_frame', label: 'Start Frame', type: 'number', value: 0 },
1175
+ { name: 'max_frames', label: 'Max Frames', type: 'number', value: 100 }
1176
+ ]
1177
+ },
1178
+ {
1179
+ id: 4,
1180
+ type: 'PrimitiveNode',
1181
+ title: 'Math_Formula_Input',
1182
+ x: 500,
1183
+ y: 500,
1184
+ width: 280,
1185
+ height: 50,
1186
+ color: '#442222',
1187
+ bgcolor: '#553333',
1188
+ inputs: [],
1189
+ outputs: [{ name: 'value', type: 'STRING', slotIndex: 0 }],
1190
+ widgets: [
1191
+ { name: 'value', label: 'Math Formula (JS)', type: 'text', value: 'x * y' }
1192
+ ]
1193
+ },
1194
+ {
1195
+ id: 5,
1196
+ type: 'CanvasAutoCrop',
1197
+ title: 'CanvasAutoCrop',
1198
+ x: 900,
1199
+ y: 300,
1200
+ width: 315,
1201
+ height: 120,
1202
+ color: '#224422',
1203
+ bgcolor: '#335533',
1204
+ inputs: [{ name: 'images', type: 'IMAGE' }],
1205
+ outputs: [
1206
+ { name: 'cropped_images', type: 'IMAGE', slotIndex: 0 },
1207
+ { name: 'crop_coords', type: 'INT', slotIndex: 1 }
1208
+ ],
1209
+ widgets: [
1210
+ { name: 'canvas_width', label: 'Canvas Width', type: 'number', value: 512, step: 8 },
1211
+ { name: 'canvas_height', label: 'Canvas Height', type: 'number', value: 512, step: 8 },
1212
+ { name: 'crop_mode', label: 'Crop Mode', type: 'select', value: 'center', options: ['center', 'top', 'bottom', 'left', 'right'] }
1213
+ ]
1214
+ },
1215
+ {
1216
+ id: 6,
1217
+ type: 'VideoMatchMixer',
1218
+ title: 'VideoMatchMixer',
1219
+ x: 1300,
1220
+ y: 300,
1221
+ width: 350,
1222
+ height: 200,
1223
+ color: '#332266',
1224
+ bgcolor: '#443377',
1225
+ inputs: [
1226
+ { name: 'frames', type: 'IMAGE' },
1227
+ { name: 'formula', type: 'STRING' }
1228
+ ],
1229
+ outputs: [
1230
+ { name: 'mixed_frames', type: 'IMAGE', slotIndex: 0 },
1231
+ { name: 'match_data', type: 'DATA', slotIndex: 1 }
1232
+ ],
1233
+ widgets: [
1234
+ { name: 'match_strength', label: 'Match Strength', type: 'range', value: 0.5, min: 0, max: 1, step: 0.01 },
1235
+ { name: 'mix_mode', label: 'Mix Mode', type: 'select', value: 'multiply', options: ['multiply', 'screen', 'overlay', 'soft_light', 'hard_light'] },
1236
+ { name: 'rematch_on_click', label: 'Enable Rematch on Click', type: 'checkbox', value: true }
1237
+ ]
1238
+ },
1239
+ {
1240
+ id: 7,
1241
+ type: 'CanvasEffect',
1242
+ title: 'CanvasEffect',
1243
+ x: 1700,
1244
+ y: 300,
1245
+ width: 315,
1246
+ height: 180,
1247
+ color: '#224466',
1248
+ bgcolor: '#335577',
1249
+ inputs: [{ name: 'images', type: 'IMAGE' }],
1250
+ outputs: [{ name: 'processed', type: 'IMAGE', slotIndex: 0 }],
1251
+ widgets: [
1252
+ { name: 'brightness', label: 'Brightness', type: 'range', value: 0, min: -1, max: 1, step: 0.1 },
1253
+ { name: 'contrast', label: 'Contrast', type: 'range', value: 1, min: 0, max: 3, step: 0.1 },
1254
+ { name: 'saturation', label: 'Saturation', type: 'range', value: 1, min: 0, max: 3, step: 0.1 },
1255
+ { name: 'hue_shift', label: 'Hue Shift', type: 'range', value: 0, min: 0, max: 360, step: 1 }
1256
+ ]
1257
+ },
1258
+ {
1259
+ id: 8,
1260
+ type: 'SaveVideo',
1261
+ title: 'SaveVideo',
1262
+ x: 2100,
1263
+ y: 300,
1264
+ width: 315,
1265
+ height: 140,
1266
+ color: '#224422',
1267
+ bgcolor: '#335533',
1268
+ inputs: [
1269
+ { name: 'images', type: 'IMAGE' },
1270
+ { name: 'fps', type: 'FLOAT' }
1271
+ ],
1272
+ outputs: [{ name: 'output_path', type: 'STRING', slotIndex: 0 }],
1273
+ widgets: [
1274
+ { name: 'output_path', label: 'Output Path', type: 'text', value: './output/video_output.mp4' },
1275
+ { name: 'codec', label: 'Codec', type: 'select', value: 'libx264', options: ['libx264', 'libx265', 'vp9'] },
1276
+ { name: 'quality', label: 'CRF Quality', type: 'number', value: 23, min: 0, max: 51 }
1277
+ ]
1278
+ },
1279
+ {
1280
+ id: 9,
1281
+ type: 'PreviewVideo',
1282
+ title: 'PreviewVideo',
1283
+ x: 2100,
1284
+ y: 500,
1285
+ width: 315,
1286
+ height: 80,
1287
+ color: '#333333',
1288
+ bgcolor: '#444444',
1289
+ inputs: [{ name: 'images', type: 'IMAGE' }],
1290
+ outputs: [],
1291
+ widgets: []
1292
+ },
1293
+ {
1294
+ id: 11,
1295
+ type: 'CanvasDisplay',
1296
+ title: 'CanvasDisplay',
1297
+ x: 1700,
1298
+ y: 550,
1299
+ width: 350,
1300
+ height: 200,
1301
+ color: '#224444',
1302
+ bgcolor: '#335555',
1303
+ inputs: [{ name: 'images', type: 'IMAGE' }],
1304
+ outputs: [{ name: 'canvas_data', type: 'DATA', slotIndex: 0 }],
1305
+ widgets: [
1306
+ { name: 'auto_play', label: 'Auto Play', type: 'checkbox', value: true },
1307
+ { name: 'loop', label: 'Loop', type: 'checkbox', value: true }
1308
+ ]
1309
+ }
1310
+ ];
1311
+
1312
+ links = [
1313
+ { source: 1, target: 3, sourceOutput: 'VIDEO', targetInput: 'video' },
1314
+ { source: 2, target: 3, sourceOutput: 'value', targetInput: 'fps' },
1315
+ { source: 2, target: 8, sourceOutput: 'value', targetInput: 'fps' },
1316
+ { source: 3, target: 5, sourceOutput: 'frames', targetInput: 'images' },
1317
+ { source: 4, target: 6, sourceOutput: 'value', targetInput: 'formula' },
1318
+ { source: 5, target: 6, sourceOutput: 'cropped_images', targetInput: 'frames' },
1319
+ { source: 6, target: 7, sourceOutput: 'mixed_frames', targetInput: 'images' },
1320
+ { source: 7