00Boobs00 commited on
Commit
930cf1b
·
verified ·
1 Parent(s): 695454b

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +939 -12
index.html CHANGED
@@ -3,32 +3,959 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Full Screen Iframe</title>
 
 
 
 
 
7
  <style>
8
- /* Remove padding and margin for body and html */
9
- html, body {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  margin: 0;
11
  padding: 0;
12
- height: 100%;
13
  }
14
 
15
- /* Make the iframe full screen and remove the border */
16
- iframe {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  position: absolute;
18
  top: 0;
19
  left: 0;
20
  width: 100%;
21
  height: 100%;
22
- border: none; /* Removes the border */
23
- margin: 0;
24
- padding: 0;
25
- overflow: hidden; /* Prevents scrollbars */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  }
27
  </style>
28
  </head>
29
  <body>
30
 
31
- <iframe src="https://bolt.aidark.net/" frameborder="0" scrolling="no"></iframe>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
 
33
  </body>
34
- </html>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>NeuroArch Studio | AI Architecture & Prompt Engineering Suite</title>
7
+ <!-- Importing Inter and JetBrains Mono for a clean, technical aesthetic -->
8
+ <link rel="preconnect" href="https://fonts.googleapis.com">
9
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">
11
+
12
  <style>
13
+ :root {
14
+ /* Color Palette - Deep Space & Neon Cyber */
15
+ --bg-deep: #050507;
16
+ --bg-panel: #0f1115;
17
+ --bg-surface: #181b21;
18
+ --primary: #3b82f6; /* Electric Blue */
19
+ --primary-glow: rgba(59, 130, 246, 0.5);
20
+ --accent: #8b5cf6; /* Violet */
21
+ --accent-glow: rgba(139, 92, 246, 0.5);
22
+ --success: #10b981;
23
+ --warning: #f59e0b;
24
+ --text-main: #e2e8f0;
25
+ --text-muted: #94a3b8;
26
+ --border: #2d3748;
27
+ --grid-line: rgba(255, 255, 255, 0.03);
28
+
29
+ /* Spacing & Radius */
30
+ --radius-md: 8px;
31
+ --radius-lg: 16px;
32
+ --header-height: 64px;
33
+ }
34
+
35
+ * {
36
+ box-sizing: border-box;
37
  margin: 0;
38
  padding: 0;
 
39
  }
40
 
41
+ body {
42
+ font-family: 'Inter', sans-serif;
43
+ background-color: var(--bg-deep);
44
+ color: var(--text-main);
45
+ height: 100vh;
46
+ overflow: hidden;
47
+ display: flex;
48
+ flex-direction: column;
49
+ }
50
+
51
+ /* --- Header --- */
52
+ header {
53
+ height: var(--header-height);
54
+ background: rgba(15, 17, 21, 0.8);
55
+ backdrop-filter: blur(12px);
56
+ border-bottom: 1px solid var(--border);
57
+ display: flex;
58
+ align-items: center;
59
+ justify-content: space-between;
60
+ padding: 0 24px;
61
+ z-index: 100;
62
+ }
63
+
64
+ .brand {
65
+ display: flex;
66
+ align-items: center;
67
+ gap: 12px;
68
+ font-weight: 700;
69
+ font-size: 1.25rem;
70
+ letter-spacing: -0.025em;
71
+ background: linear-gradient(to right, var(--primary), var(--accent));
72
+ -webkit-background-clip: text;
73
+ -webkit-text-fill-color: transparent;
74
+ }
75
+
76
+ .brand-icon {
77
+ width: 32px;
78
+ height: 32px;
79
+ background: linear-gradient(135deg, var(--primary), var(--accent));
80
+ border-radius: var(--radius-md);
81
+ display: grid;
82
+ place-items: center;
83
+ color: white;
84
+ font-size: 1.2rem;
85
+ }
86
+
87
+ .header-actions {
88
+ display: flex;
89
+ align-items: center;
90
+ gap: 20px;
91
+ }
92
+
93
+ a.built-with {
94
+ color: var(--text-muted);
95
+ text-decoration: none;
96
+ font-size: 0.85rem;
97
+ transition: color 0.2s;
98
+ display: flex;
99
+ align-items: center;
100
+ gap: 6px;
101
+ }
102
+
103
+ a.built-with:hover {
104
+ color: var(--primary);
105
+ }
106
+
107
+ /* --- Main Layout --- */
108
+ main {
109
+ flex: 1;
110
+ display: grid;
111
+ grid-template-columns: 280px 1fr 320px;
112
+ height: calc(100vh - var(--header-height));
113
+ position: relative;
114
+ }
115
+
116
+ /* --- Sidebar (Tools) --- */
117
+ .sidebar {
118
+ background: var(--bg-panel);
119
+ border-right: 1px solid var(--border);
120
+ padding: 20px;
121
+ display: flex;
122
+ flex-direction: column;
123
+ gap: 24px;
124
+ overflow-y: auto;
125
+ }
126
+
127
+ .tool-group h3 {
128
+ font-size: 0.75rem;
129
+ text-transform: uppercase;
130
+ letter-spacing: 0.05em;
131
+ color: var(--text-muted);
132
+ margin-bottom: 12px;
133
+ }
134
+
135
+ .node-btn {
136
+ width: 100%;
137
+ background: var(--bg-surface);
138
+ border: 1px solid var(--border);
139
+ color: var(--text-main);
140
+ padding: 12px;
141
+ border-radius: var(--radius-md);
142
+ cursor: pointer;
143
+ text-align: left;
144
+ transition: all 0.2s ease;
145
+ display: flex;
146
+ align-items: center;
147
+ gap: 10px;
148
+ margin-bottom: 8px;
149
+ font-size: 0.9rem;
150
+ }
151
+
152
+ .node-btn:hover {
153
+ border-color: var(--primary);
154
+ background: rgba(59, 130, 246, 0.05);
155
+ transform: translateX(4px);
156
+ }
157
+
158
+ .node-btn .icon {
159
+ width: 24px;
160
+ height: 24px;
161
+ background: rgba(255,255,255,0.1);
162
+ border-radius: 4px;
163
+ display: flex;
164
+ align-items: center;
165
+ justify-content: center;
166
+ font-size: 0.8rem;
167
+ }
168
+
169
+ /* --- Canvas Area --- */
170
+ .canvas-container {
171
+ background-color: var(--bg-deep);
172
+ background-image:
173
+ linear-gradient(var(--grid-line) 1px, transparent 1px),
174
+ linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
175
+ background-size: 40px 40px;
176
+ position: relative;
177
+ overflow: hidden;
178
+ user-select: none;
179
+ cursor: grab;
180
+ }
181
+
182
+ .canvas-container:active {
183
+ cursor: grabbing;
184
+ }
185
+
186
+ /* SVG Overlay for Connections */
187
+ #connections-layer {
188
  position: absolute;
189
  top: 0;
190
  left: 0;
191
  width: 100%;
192
  height: 100%;
193
+ pointer-events: none;
194
+ z-index: 1;
195
+ }
196
+
197
+ .connection-path {
198
+ fill: none;
199
+ stroke: var(--border);
200
+ stroke-width: 2px;
201
+ transition: stroke 0.3s;
202
+ }
203
+
204
+ .connection-path.active {
205
+ stroke: var(--primary);
206
+ stroke-dasharray: 10;
207
+ animation: flowAnimation 1s linear infinite;
208
+ }
209
+
210
+ @keyframes flowAnimation {
211
+ from { stroke-dashoffset: 20; }
212
+ to { stroke-dashoffset: 0; }
213
+ }
214
+
215
+ /* Nodes on Canvas */
216
+ .node {
217
+ position: absolute;
218
+ width: 180px;
219
+ background: rgba(24, 27, 33, 0.9);
220
+ backdrop-filter: blur(8px);
221
+ border: 1px solid var(--border);
222
+ border-radius: var(--radius-md);
223
+ padding: 16px;
224
+ z-index: 2;
225
+ cursor: pointer;
226
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
227
+ transition: box-shadow 0.2s, border-color 0.2s;
228
+ }
229
+
230
+ .node:hover {
231
+ border-color: var(--text-muted);
232
+ }
233
+
234
+ .node.selected {
235
+ border-color: var(--primary);
236
+ box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2), 0 10px 15px -3px rgba(0, 0, 0, 0.5);
237
+ }
238
+
239
+ .node-header {
240
+ display: flex;
241
+ justify-content: space-between;
242
+ align-items: center;
243
+ margin-bottom: 8px;
244
+ font-weight: 600;
245
+ font-size: 0.9rem;
246
+ }
247
+
248
+ .node-status {
249
+ width: 8px;
250
+ height: 8px;
251
+ border-radius: 50%;
252
+ background-color: var(--text-muted);
253
+ }
254
+
255
+ .node-status.active { background-color: var(--success); box-shadow: 0 0 8px var(--success); }
256
+ .node-status.processing { background-color: var(--warning); box-shadow: 0 0 8px var(--warning); }
257
+
258
+ .node-body {
259
+ font-size: 0.8rem;
260
+ color: var(--text-muted);
261
+ font-family: 'JetBrains Mono', monospace;
262
+ }
263
+
264
+ .node-ports {
265
+ position: absolute;
266
+ width: 12px;
267
+ height: 12px;
268
+ background: var(--text-main);
269
+ border-radius: 50%;
270
+ border: 2px solid var(--bg-surface);
271
+ cursor: crosshair;
272
+ }
273
+ .port-in { top: -6px; left: 50%; transform: translateX(-50%); }
274
+ .port-out { bottom: -6px; left: 50%; transform: translateX(-50%); }
275
+
276
+ /* --- Right Panel (Properties) --- */
277
+ .properties-panel {
278
+ background: var(--bg-panel);
279
+ border-left: 1px solid var(--border);
280
+ padding: 20px;
281
+ overflow-y: auto;
282
+ display: flex;
283
+ flex-direction: column;
284
+ gap: 20px;
285
+ }
286
+
287
+ .panel-header {
288
+ font-size: 1rem;
289
+ font-weight: 600;
290
+ padding-bottom: 12px;
291
+ border-bottom: 1px solid var(--border);
292
+ }
293
+
294
+ .prop-group {
295
+ display: flex;
296
+ flex-direction: column;
297
+ gap: 8px;
298
+ }
299
+
300
+ .prop-group label {
301
+ font-size: 0.8rem;
302
+ color: var(--text-muted);
303
+ }
304
+
305
+ .prop-input, .prop-select, .prop-textarea {
306
+ background: var(--bg-surface);
307
+ border: 1px solid var(--border);
308
+ color: var(--text-main);
309
+ padding: 10px;
310
+ border-radius: var(--radius-md);
311
+ font-family: inherit;
312
+ font-size: 0.9rem;
313
+ outline: none;
314
+ transition: border-color 0.2s;
315
+ }
316
+
317
+ .prop-input:focus, .prop-select:focus, .prop-textarea:focus {
318
+ border-color: var(--primary);
319
+ }
320
+
321
+ .prop-textarea {
322
+ resize: vertical;
323
+ min-height: 80px;
324
+ font-family: 'JetBrains Mono', monospace;
325
+ }
326
+
327
+ .action-btn {
328
+ background: var(--primary);
329
+ color: white;
330
+ border: none;
331
+ padding: 12px;
332
+ border-radius: var(--radius-md);
333
+ font-weight: 600;
334
+ cursor: pointer;
335
+ transition: background 0.2s;
336
+ margin-top: auto;
337
+ }
338
+
339
+ .action-btn:hover {
340
+ background: #2563eb;
341
+ }
342
+
343
+ .action-btn.secondary {
344
+ background: transparent;
345
+ border: 1px solid var(--border);
346
+ color: var(--text-main);
347
+ }
348
+
349
+ .action-btn.secondary:hover {
350
+ border-color: var(--text-muted);
351
+ }
352
+
353
+ /* --- Console/Logs --- */
354
+ .console-output {
355
+ background: #000;
356
+ border-radius: var(--radius-md);
357
+ padding: 12px;
358
+ font-family: 'JetBrains Mono', monospace;
359
+ font-size: 0.75rem;
360
+ height: 150px;
361
+ overflow-y: auto;
362
+ color: #33ff00;
363
+ border: 1px solid #333;
364
+ margin-top: 20px;
365
+ }
366
+
367
+ .log-entry { margin-bottom: 4px; }
368
+ .log-time { color: #666; margin-right: 8px; }
369
+ .log-info { color: #3b82f6; }
370
+ .log-warn { color: #f59e0b; }
371
+ .log-success { color: #10b981; }
372
+
373
+ /* --- Toast Notification --- */
374
+ .toast-container {
375
+ position: fixed;
376
+ bottom: 24px;
377
+ right: 24px;
378
+ z-index: 1000;
379
+ display: flex;
380
+ flex-direction: column;
381
+ gap: 10px;
382
+ }
383
+
384
+ .toast {
385
+ background: var(--bg-surface);
386
+ border: 1px solid var(--border);
387
+ border-left: 4px solid var(--primary);
388
+ padding: 16px;
389
+ border-radius: var(--radius-md);
390
+ min-width: 300px;
391
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.5);
392
+ animation: slideIn 0.3s ease-out;
393
+ display: flex;
394
+ align-items: center;
395
+ justify-content: space-between;
396
+ }
397
+
398
+ @keyframes slideIn {
399
+ from { transform: translateX(100%); opacity: 0; }
400
+ to { transform: translateX(0); opacity: 1; }
401
+ }
402
+
403
+ /* --- Responsive --- */
404
+ @media (max-width: 1024px) {
405
+ main {
406
+ grid-template-columns: 240px 1fr;
407
+ }
408
+ .properties-panel {
409
+ position: absolute;
410
+ right: 0;
411
+ top: 0;
412
+ bottom: 0;
413
+ width: 300px;
414
+ transform: translateX(100%);
415
+ transition: transform 0.3s;
416
+ z-index: 50;
417
+ background: var(--bg-panel);
418
+ }
419
+ .properties-panel.open {
420
+ transform: translateX(0);
421
+ }
422
+ }
423
+
424
+ @media (max-width: 768px) {
425
+ main {
426
+ grid-template-columns: 1fr;
427
+ }
428
+ .sidebar {
429
+ display: none; /* Hide sidebar on mobile for simplicity in this demo, would use hamburger in full app */
430
+ }
431
+ /* Add a floating action button for mobile to add nodes would be ideal here */
432
  }
433
  </style>
434
  </head>
435
  <body>
436
 
437
+ <header>
438
+ <div class="brand">
439
+ <div class="brand-icon">N</div>
440
+ <span>NeuroArch Studio</span>
441
+ </div>
442
+ <div class="header-actions">
443
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="built-with">
444
+ <span>Built with anycoder</span>
445
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
446
+ <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path>
447
+ <polyline points="15 3 21 3 21 9"></polyline>
448
+ <line x1="10" y1="14" x2="21" y2="3"></line>
449
+ </svg>
450
+ </a>
451
+ </div>
452
+ </header>
453
+
454
+ <main>
455
+ <!-- Sidebar: Component Library -->
456
+ <aside class="sidebar">
457
+ <div class="tool-group">
458
+ <h3>Data Ingestion</h3>
459
+ <button class="node-btn" onclick="app.addNode('input')">
460
+ <div class="icon">📥</div> Data Source
461
+ </button>
462
+ <button class="node-btn" onclick="app.addNode('vector-db')">
463
+ <div class="icon">🗃️</div> Vector Store
464
+ </button>
465
+ </div>
466
+
467
+ <div class="tool-group">
468
+ <h3>AI Processing</h3>
469
+ <button class="node-btn" onclick="app.addNode('llm')">
470
+ <div class="icon">🧠</div> LLM Core
471
+ </button>
472
+ <button class="node-btn" onclick="app.addNode('embedder')">
473
+ <div class="icon">🔢</div> Embeddings
474
+ </button>
475
+ </div>
476
+
477
+ <div class="tool-group">
478
+ <h3>Output & Logic</h3>
479
+ <button class="node-btn" onclick="app.addNode('router')">
480
+ <div class="icon">🔀</div> Semantic Router
481
+ </button>
482
+ <button class="node-btn" onclick="app.addNode('output')">
483
+ <div class="icon">📤</div> Response
484
+ </button>
485
+ </div>
486
+
487
+ <div style="margin-top: auto;">
488
+ <button class="action-btn secondary" onclick="app.clearCanvas()" style="width: 100%; font-size: 0.8rem; padding: 8px;">Clear Canvas</button>
489
+ </div>
490
+ </aside>
491
+
492
+ <!-- Main Workspace -->
493
+ <section class="canvas-container" id="canvas">
494
+ <svg id="connections-layer"></svg>
495
+ <!-- Nodes will be injected here via JS -->
496
+ </section>
497
+
498
+ <!-- Right Panel: Properties & Config -->
499
+ <aside class="properties-panel" id="properties-panel">
500
+ <div class="panel-header">Properties</div>
501
+
502
+ <div id="no-selection-msg" style="color: var(--text-muted); font-size: 0.9rem; text-align: center; margin-top: 40px;">
503
+ Select a node to configure its parameters.
504
+ </div>
505
+
506
+ <div id="prop-form" style="display: none; flex-direction: column; height: 100%;">
507
+ <div class="prop-group">
508
+ <label>Node ID</label>
509
+ <input type="text" id="prop-id" class="prop-input" disabled>
510
+ </div>
511
+
512
+ <div class="prop-group">
513
+ <label>Label</label>
514
+ <input type="text" id="prop-label" class="prop-input">
515
+ </div>
516
+
517
+ <div class="prop-group" id="group-model">
518
+ <label>Model</label>
519
+ <select id="prop-model" class="prop-select">
520
+ <option value="gpt-4-turbo">GPT-4 Turbo</option>
521
+ <option value="gpt-3.5-turbo">GPT-3.5 Turbo</option>
522
+ <option value="claude-3-opus">Claude 3 Opus</option>
523
+ <option value="mistral-large">Mistral Large</option>
524
+ <option value="llama-3-70b">Llama 3 70B</option>
525
+ </select>
526
+ </div>
527
+
528
+ <div class="prop-group" id="group-temp">
529
+ <label>Temperature (0.0 - 1.0)</label>
530
+ <input type="range" id="prop-temp" min="0" max="1" step="0.1" value="0.7" style="width: 100%">
531
+ <div style="display: flex; justify-content: space-between; font-size: 0.75rem; color: var(--text-muted);">
532
+ <span>Precise</span>
533
+ <span id="temp-val">0.7</span>
534
+ <span>Creative</span>
535
+ </div>
536
+ </div>
537
+
538
+ <div class="prop-group">
539
+ <label>System Prompt / Context</label>
540
+ <textarea id="prop-prompt" class="prop-textarea" placeholder="Enter system instructions..."></textarea>
541
+ </div>
542
+
543
+ <button class="action-btn" onclick="app.runSimulation()">Run Simulation</button>
544
+ <button class="action-btn secondary" onclick="app.deleteSelectedNode()">Delete Node</button>
545
+ </div>
546
+
547
+ <div class="console-output" id="console-logs">
548
+ <div class="log-entry"><span class="log-time">[System]</span> Ready. Awaiting architecture input.</div>
549
+ </div>
550
+ </aside>
551
+ </main>
552
+
553
+ <div class="toast-container" id="toast-container"></div>
554
+
555
+ <script>
556
+ /**
557
+ * NeuroArch Studio Core Application Logic
558
+ * Implements a node-based architecture for AI system design.
559
+ * Designed with modularity and scalability in mind.
560
+ */
561
+
562
+ class NeuroArchApp {
563
+ constructor() {
564
+ this.nodes = [];
565
+ this.connections = [];
566
+ this.selectedNodeId = null;
567
+ this.canvas = document.getElementById('canvas');
568
+ this.svgLayer = document.getElementById('connections-layer');
569
+ this.isDragging = false;
570
+ this.dragNodeId = null;
571
+ this.dragOffset = { x: 0, y: 0 };
572
+
573
+ // UI References
574
+ this.propPanel = document.getElementById('prop-form');
575
+ this.noSelectionMsg = document.getElementById('no-selection-msg');
576
+ this.consoleLogs = document.getElementById('console-logs');
577
+
578
+ // Bind events
579
+ this.canvas.addEventListener('mousedown', (e) => this.handleCanvasMouseDown(e));
580
+ document.addEventListener('mousemove', (e) => this.handleMouseMove(e));
581
+ document.addEventListener('mouseup', (e) => this.handleMouseUp(e));
582
+
583
+ // Initialize with a default node
584
+ this.addNode('input', 100, 150);
585
+ }
586
+
587
+ /**
588
+ * Generates a unique ID for nodes
589
+ */
590
+ generateId() {
591
+ return 'node_' + Math.random().toString(36).substr(2, 9);
592
+ }
593
+
594
+ /**
595
+ * Logs messages to the system console
596
+ */
597
+ log(message, type = 'info') {
598
+ const time = new Date().toLocaleTimeString('en-US', { hour12: false });
599
+ const entry = document.createElement('div');
600
+ entry.className = 'log-entry';
601
+ entry.innerHTML = `<span class="log-time">[${time}]</span> <span class="log-${type}">${message}</span>`;
602
+ this.consoleLogs.appendChild(entry);
603
+ this.consoleLogs.scrollTop = this.consoleLogs.scrollHeight;
604
+ }
605
+
606
+ /**
607
+ * Creates a new node and adds it to the canvas
608
+ */
609
+ addNode(type, x = 50, y = 50) {
610
+ const id = this.generateId();
611
+ const nodeData = {
612
+ id,
613
+ type,
614
+ x,
615
+ y,
616
+ label: this.getDefaultLabel(type),
617
+ model: type === 'llm' ? 'gpt-4-turbo' : 'N/A',
618
+ temp: 0.7,
619
+ prompt: ''
620
+ };
621
+
622
+ this.nodes.push(nodeData);
623
+ this.renderNode(nodeData);
624
+ this.log(`Added node: ${nodeData.label} (${type})`, 'info');
625
+ return id;
626
+ }
627
+
628
+ getDefaultLabel(type) {
629
+ const labels = {
630
+ 'input': 'Data Source',
631
+ 'vector-db': 'Vector DB',
632
+ 'llm': 'LLM Processor',
633
+ 'embedder': 'Text Embedder',
634
+ 'router': 'Semantic Router',
635
+ 'output': 'Final Output'
636
+ };
637
+ return labels[type] || 'Unknown Node';
638
+ }
639
+
640
+ /**
641
+ * Renders the DOM element for a node
642
+ */
643
+ renderNode(node) {
644
+ const el = document.createElement('div');
645
+ el.className = 'node';
646
+ el.id = node.id;
647
+ el.style.left = `${node.x}px`;
648
+ el.style.top = `${node.y}px`;
649
+
650
+ // Inner HTML structure
651
+ el.innerHTML = `
652
+ <div class="node-ports port-in" data-port="in" title="Input"></div>
653
+ <div class="node-header">
654
+ <span>${node.label}</span>
655
+ <div class="node-status" id="status-${node.id}"></div>
656
+ </div>
657
+ <div class="node-body">
658
+ ${node.type.toUpperCase()}<br>
659
+ Latency: <span id="latency-${node.id}">--</span>ms
660
+ </div>
661
+ <div class="node-ports port-out" data-port="out" title="Output"></div>
662
+ `;
663
+
664
+ // Selection Event
665
+ el.addEventListener('mousedown', (e) => {
666
+ if (e.target.classList.contains('node-ports')) return; // Let port logic handle it
667
+ this.selectNode(node.id);
668
+ this.isDragging = true;
669
+ this.dragNodeId = node.id;
670
+ const rect = el.getBoundingClientRect();
671
+ this.dragOffset.x = e.clientX - rect.left;
672
+ this.dragOffset.y = e.clientY - rect.top;
673
+ });
674
+
675
+ this.canvas.appendChild(el);
676
+
677
+ // Simple animation entry
678
+ el.animate([
679
+ { transform: 'scale(0.8)', opacity: 0 },
680
+ { transform: 'scale(1)', opacity: 1 }
681
+ ], { duration: 200, easing: 'ease-out' });
682
+ }
683
+
684
+ /**
685
+ * Handles mouse down on the canvas (deselect or start drag)
686
+ */
687
+ handleCanvasMouseDown(e) {
688
+ if (e.target === this.canvas || e.target === this.svgLayer) {
689
+ this.deselectAll();
690
+ }
691
+ }
692
+
693
+ handleMouseMove(e) {
694
+ if (this.isDragging && this.dragNodeId) {
695
+ const node = this.nodes.find(n => n.id === this.dragNodeId);
696
+ if (node) {
697
+ // Calculate new position relative to canvas
698
+ const canvasRect = this.canvas.getBoundingClientRect();
699
+ let newX = e.clientX - canvasRect.left - this.dragOffset.x;
700
+ let newY = e.clientY - canvasRect.top - this.dragOffset.y;
701
+
702
+ // Boundary checks
703
+ newX = Math.max(0, newX);
704
+ newY = Math.max(0, newY);
705
+
706
+ node.x = newX;
707
+ node.y = newY;
708
+
709
+ // Update DOM
710
+ const el = document.getElementById(node.id);
711
+ el.style.left = `${newX}px`;
712
+ el.style.top = `${newY}px`;
713
+
714
+ this.updateConnections();
715
+ }
716
+ }
717
+ }
718
+
719
+ handleMouseUp(e) {
720
+ this.isDragging = false;
721
+ this.dragNodeId = null;
722
+ }
723
+
724
+ /**
725
+ * Selects a node and populates the property panel
726
+ */
727
+ selectNode(id) {
728
+ this.selectedNodeId = id;
729
+
730
+ // Visual feedback
731
+ document.querySelectorAll('.node').forEach(n => n.classList.remove('selected'));
732
+ const el = document.getElementById(id);
733
+ if(el) el.classList.add('selected');
734
+
735
+ // Show properties
736
+ const node = this.nodes.find(n => n.id === id);
737
+ if (node) {
738
+ this.noSelectionMsg.style.display = 'none';
739
+ this.propPanel.style.display = 'flex';
740
+
741
+ // Populate fields
742
+ document.getElementById('prop-id').value = node.id;
743
+ document.getElementById('prop-label').value = node.label;
744
+ document.getElementById('prop-temp').value = node.temp;
745
+ document.getElementById('temp-val').innerText = node.temp;
746
+ document.getElementById('prop-prompt').value = node.prompt;
747
+
748
+ const modelSelect = document.getElementById('prop-model');
749
+ if(node.type === 'llm') {
750
+ document.getElementById('group-model').style.display = 'flex';
751
+ document.getElementById('group-temp').style.display = 'flex';
752
+ modelSelect.value = node.model;
753
+ } else {
754
+ document.getElementById('group-model').style.display = 'none';
755
+ document.getElementById('group-temp').style.display = 'none';
756
+ }
757
+
758
+ // Add event listeners for input changes to update state immediately
759
+ this.setupPropertyListeners(node);
760
+ }
761
+ }
762
+
763
+ setupPropertyListeners(node) {
764
+ const labelInput = document.getElementById('prop-label');
765
+ const tempInput = document.getElementById('prop-temp');
766
+ const promptInput = document.getElementById('prop-prompt');
767
+ const modelSelect = document.getElementById('prop-model');
768
+
769
+ // Remove old listeners to prevent stacking (simplified approach)
770
+ const newLabelInput = labelInput.cloneNode(true);
771
+ labelInput.parentNode.replaceChild(newLabelInput, labelInput);
772
+ newLabelInput.addEventListener('input', (e) => {
773
+ node.label = e.target.value;
774
+ document.querySelector(`#${node.id} .node-header span`).innerText = node.label;
775
+ });
776
+
777
+ const newTempInput = tempInput.cloneNode(true);
778
+ tempInput.parentNode.replaceChild(newTempInput, tempInput);
779
+ newTempInput.addEventListener('input', (e) => {
780
+ node.temp = e.target.value;
781
+ document.getElementById('temp-val').innerText = node.temp;
782
+ });
783
+
784
+ const newPromptInput = promptInput.cloneNode(true);
785
+ promptInput.parentNode.replaceChild(newPromptInput, promptInput);
786
+ newPromptInput.addEventListener('input', (e) => {
787
+ node.prompt = e.target.value;
788
+ });
789
+
790
+ const newModelSelect = modelSelect.cloneNode(true);
791
+ modelSelect.parentNode.replaceChild(newModelSelect, modelSelect);
792
+ newModelSelect.addEventListener('change', (e) => {
793
+ node.model = e.target.value;
794
+ this.log(`Updated model for ${node.id} to ${node.model}`, 'info');
795
+ });
796
+ }
797
+
798
+ deselectAll() {
799
+ this.selectedNodeId = null;
800
+ document.querySelectorAll('.node').forEach(n => n.classList.remove('selected'));
801
+ this.noSelectionMsg.style.display = 'block';
802
+ this.propPanel.style.display = 'none';
803
+ }
804
+
805
+ /**
806
+ * Logic to connect nodes (simplified auto-connection for demo)
807
+ */
808
+ connectNodes(sourceId, targetId) {
809
+ // Check if already connected
810
+ const exists = this.connections.find(c => c.from === sourceId && c.to === targetId);
811
+ if (!exists) {
812
+ this.connections.push({ from: sourceId, to: targetId });
813
+ this.updateConnections();
814
+ }
815
+ }
816
+
817
+ /**
818
+ * Draws SVG lines between connected nodes
819
+ */
820
+ updateConnections() {
821
+ this.svgLayer.innerHTML = ''; // Clear existing lines
822
+
823
+ this.connections.forEach(conn => {
824
+ const fromNode = document.getElementById(conn.from);
825
+ const toNode = document.getElementById(conn.to);
826
+
827
+ if (fromNode && toNode) {
828
+ const fromRect = fromNode.getBoundingClientRect();
829
+ const toRect = toNode.getBoundingClientRect();
830
+ const canvasRect = this.canvas.getBoundingClientRect();
831
+
832
+ // Calculate port positions (centers of bottom/top borders)
833
+ const x1 = fromRect.left - canvasRect.left + fromRect.width / 2;
834
+ const y1 = fromRect.top - canvasRect.top + fromRect.height;
835
+ const x2 = toRect.left - canvasRect.left + toRect.width / 2;
836
+ const y2 = toRect.top - canvasRect.top;
837
+
838
+ // Create Bezier Curve
839
+ const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
840
+ const controlY1 = y1 + 50;
841
+ const controlY2 = y2 - 50;
842
+
843
+ const d = `M ${x1} ${y1} C ${x1} ${controlY1}, ${x2} ${controlY2}, ${x2} ${y2}`;
844
+
845
+ path.setAttribute('d', d);
846
+ path.setAttribute('class', 'connection-path');
847
+ path.setAttribute('id', `conn-${conn.from}-${conn.to}`);
848
+
849
+ this.svgLayer.appendChild(path);
850
+ }
851
+ });
852
+ }
853
+
854
+ deleteSelectedNode() {
855
+ if(!this.selectedNodeId) return;
856
+
857
+ // Remove DOM
858
+ const el = document.getElementById(this.selectedNodeId);
859
+ if(el) el.remove();
860
+
861
+ // Remove from data
862
+ this.nodes = this.nodes.filter(n => n.id !== this.selectedNodeId);
863
+ this.connections = this.connections.filter(c => c.from !== this.selectedNodeId && c.to !== this.selectedNodeId);
864
+
865
+ this.log(`Deleted node ${this.selectedNodeId}`, 'warn');
866
+ this.deselectAll();
867
+ this.updateConnections();
868
+ }
869
+
870
+ clearCanvas() {
871
+ this.nodes.forEach(n => {
872
+ const el = document.getElementById(n.id);
873
+ if(el) el.remove();
874
+ });
875
+ this.nodes = [];
876
+ this.connections = [];
877
+ this.svgLayer.innerHTML = '';
878
+ this.log('Canvas cleared.', 'info');
879
+ this.deselectAll();
880
+ }
881
+
882
+ /**
883
+ * Simulates a data pipeline run through the connected nodes
884
+ */
885
+ async runSimulation() {
886
+ if (this.connections.length === 0) {
887
+ this.showToast('No connections detected. Connect nodes to run pipeline.', 'warning');
888
+ return;
889
+ }
890
+
891
+ this.log('Initializing pipeline simulation...', 'info');
892
+
893
+ // Reset visuals
894
+ document.querySelectorAll('.node-status').forEach(el => {
895
+ el.className = 'node-status';
896
+ });
897
+ document.querySelectorAll('.connection-path').forEach(el => el.classList.remove('active'));
898
+
899
+ // Simplified Topological Sort / Flow Simulation
900
+ // For this demo, we just animate sequentially or breadth-first based on connections
901
+
902
+ const visited = new Set();
903
+ const queue = this.nodes.filter(n => this.connections.some(c => c.from === n.id)); // Start from sources
904
+
905
+ // Actually, let's just animate everything for effect in this UI demo
906
+ // In a real app, we'd traverse the graph.
907
+
908
+ for (const node of this.nodes) {
909
+ const statusEl = document.getElementById(`status-${node.id}`);
910
+ statusEl.classList.add('processing');
911
+
912
+ // Simulate processing time
913
+ const latency = Math.floor(Math.random() * 200) + 50;
914
+ document.getElementById(`latency-${node.id}`).innerText = latency;
915
+
916
+ await new Promise(r => setTimeout(r, latency));
917
+
918
+ statusEl.classList.remove('processing');
919
+ statusEl.classList.add('active');
920
+
921
+ // Animate outgoing connections
922
+ this.connections
923
+ .filter(c => c.from === node.id)
924
+ .forEach(c => {
925
+ const path = document.getElementById(`conn-${c.from}-${c.to}`);
926
+ if(path) path.classList.add('active');
927
+ });
928
+
929
+ this.log(`Executed ${node.label}: ${latency}ms`, 'success');
930
+ }
931
+
932
+ this.showToast('Pipeline execution completed successfully.', 'success');
933
+ }
934
+
935
+ showToast(message, type = 'info') {
936
+ const container = document.getElementById('toast-container');
937
+ const toast = document.createElement('div');
938
+ toast.className = 'toast';
939
+ toast.style.borderLeftColor = type === 'success' ? 'var(--success)' : (type === 'warning' ? 'var(--warning)' : 'var(--primary)');
940
+
941
+ toast.innerHTML = `
942
+ <span>${message}</span>
943
+ <button onclick="this.parentElement.remove()" style="background:none;border:none;color:white;cursor:pointer;">✕</button>
944
+ `;
945
+
946
+ container.appendChild(toast);
947
+
948
+ // Auto remove
949
+ setTimeout(() => {
950
+ toast.style.opacity = '0';
951
+ setTimeout(() => toast.remove(), 300);
952
+ }, 3000);
953
+ }
954
+ }
955
+
956
+ // Initialize Application
957
+ const app = new NeuroArchApp();
958
 
959
+ </script>
960
  </body>
961
+ </html>