eaglelandsonce commited on
Commit
89a67f1
·
verified ·
1 Parent(s): 86aea4e

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +687 -19
index.html CHANGED
@@ -1,19 +1,687 @@
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>Agent Architecture 3D Interactive Simulation</title>
7
+ <style>
8
+ * {
9
+ box-sizing: border-box;
10
+ margin: 0;
11
+ padding: 0;
12
+ user-select: none;
13
+ -webkit-user-select: none;
14
+ }
15
+ body, html {
16
+ width: 100%;
17
+ height: 100%;
18
+ overflow: hidden;
19
+ background-color: #030712;
20
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
21
+ color: #f8fafc;
22
+ }
23
+ #canvas3d {
24
+ position: absolute;
25
+ top: 0;
26
+ left: 0;
27
+ width: 100%;
28
+ height: 100%;
29
+ cursor: grab;
30
+ z-index: 1;
31
+ }
32
+ #canvas3d:active {
33
+ cursor: grabbing;
34
+ }
35
+ .hud-layer {
36
+ position: relative;
37
+ z-index: 10;
38
+ pointer-events: none;
39
+ width: 100%;
40
+ height: 100%;
41
+ display: flex;
42
+ flex-direction: column;
43
+ justify-content: space-between;
44
+ padding: 16px;
45
+ }
46
+ .glass {
47
+ background: rgba(15, 23, 42, 0.75);
48
+ backdrop-filter: blur(16px);
49
+ -webkit-backdrop-filter: blur(16px);
50
+ border: 1px solid rgba(255, 255, 255, 0.12);
51
+ box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.5);
52
+ pointer-events: auto;
53
+ }
54
+ .pill {
55
+ border-radius: 9999px;
56
+ padding: 6px 14px;
57
+ display: flex;
58
+ align-items: center;
59
+ gap: 10px;
60
+ }
61
+ .btn {
62
+ background: rgba(30, 41, 59, 0.8);
63
+ border: 1px solid rgba(255, 255, 255, 0.1);
64
+ color: #cbd5e1;
65
+ padding: 6px 12px;
66
+ border-radius: 8px;
67
+ font-size: 12px;
68
+ font-weight: 600;
69
+ cursor: pointer;
70
+ transition: all 0.2s ease;
71
+ display: inline-flex;
72
+ align-items: center;
73
+ gap: 6px;
74
+ }
75
+ .btn:hover {
76
+ background: rgba(51, 65, 85, 0.9);
77
+ color: #ffffff;
78
+ border-color: rgba(255, 255, 255, 0.25);
79
+ }
80
+ .btn-primary {
81
+ background: linear-gradient(135deg, #2563eb, #1d4ed8);
82
+ color: #ffffff;
83
+ border: none;
84
+ box-shadow: 0 4px 14px rgba(37, 99, 235, 0.4);
85
+ }
86
+ .btn-primary:hover {
87
+ background: linear-gradient(135deg, #3b82f6, #2563eb);
88
+ }
89
+ .top-bar {
90
+ display: flex;
91
+ justify-content: space-between;
92
+ align-items: center;
93
+ gap: 12px;
94
+ flex-wrap: wrap;
95
+ }
96
+ .pulse-dot {
97
+ width: 8px;
98
+ height: 8px;
99
+ background: #38bdf8;
100
+ border-radius: 50%;
101
+ box-shadow: 0 0 10px #38bdf8;
102
+ animation: pulse 2s infinite;
103
+ }
104
+ @keyframes pulse {
105
+ 0%, 100% { transform: scale(1); opacity: 1; }
106
+ 50% { transform: scale(1.6); opacity: 0.5; }
107
+ }
108
+ .info-card {
109
+ border-radius: 16px;
110
+ padding: 16px;
111
+ max-width: 440px;
112
+ border-left: 4px solid #3b82f6;
113
+ transition: transform 0.3s ease, opacity 0.3s ease;
114
+ }
115
+ .info-card.hidden {
116
+ opacity: 0;
117
+ transform: translateY(20px);
118
+ pointer-events: none;
119
+ }
120
+ .bottom-bar {
121
+ display: flex;
122
+ flex-direction: column;
123
+ gap: 10px;
124
+ align-items: center;
125
+ max-width: 800px;
126
+ margin: 0 auto;
127
+ width: 100%;
128
+ }
129
+ .stage-track {
130
+ display: flex;
131
+ gap: 6px;
132
+ width: 100%;
133
+ justify-content: space-between;
134
+ }
135
+ .stage-btn {
136
+ flex: 1;
137
+ padding: 8px 4px;
138
+ text-align: center;
139
+ font-size: 11px;
140
+ font-weight: 600;
141
+ border-radius: 10px;
142
+ cursor: pointer;
143
+ background: rgba(15, 23, 42, 0.8);
144
+ border: 1px solid rgba(255, 255, 255, 0.08);
145
+ color: #94a3b8;
146
+ transition: all 0.2s ease;
147
+ white-space: nowrap;
148
+ overflow: hidden;
149
+ text-overflow: ellipsis;
150
+ }
151
+ .stage-btn:hover {
152
+ background: rgba(30, 41, 59, 0.9);
153
+ color: #f1f5f9;
154
+ }
155
+ .stage-btn.active {
156
+ background: #2563eb;
157
+ color: #ffffff;
158
+ border-color: #60a5fa;
159
+ box-shadow: 0 0 15px rgba(37, 99, 235, 0.5);
160
+ }
161
+ .camera-presets {
162
+ display: flex;
163
+ gap: 6px;
164
+ }
165
+ </style>
166
+ </head>
167
+ <body>
168
+
169
+ <!-- Fullscreen 3D Canvas -->
170
+ <canvas id="canvas3d"></canvas>
171
+
172
+ <!-- Interactive HUD Overlay -->
173
+ <div class="hud-layer">
174
+
175
+ <!-- Top Header & Controls -->
176
+ <header class="top-bar">
177
+ <div class="glass pill">
178
+ <span class="pulse-dot"></span>
179
+ <div style="font-size: 13px; font-weight: 700; letter-spacing: 0.5px;">
180
+ Agent Architecture <span style="color: #38bdf8; font-family: monospace;">3D</span>
181
+ </div>
182
+ <span style="font-size: 11px; color: #64748b;">| Drag to Rotate · Scroll to Zoom</span>
183
+ </div>
184
+
185
+ <div class="glass pill" style="gap: 6px;">
186
+ <button id="btn-prev" class="btn" title="Previous Stage">◀</button>
187
+ <button id="btn-play" class="btn btn-primary" style="padding: 6px 14px;">
188
+ <span id="play-text">⏸ Pause</span>
189
+ </button>
190
+ <button id="btn-next" class="btn" title="Next Stage">▶</button>
191
+ <div style="width: 1px; height: 16px; background: rgba(255,255,255,0.15); margin: 0 4px;"></div>
192
+ <button id="btn-orbit" class="btn" style="color: #38bdf8;">Spin: ON</button>
193
+ <button id="btn-toggle-info" class="btn">Info Box: ON</button>
194
+ </div>
195
+
196
+ <div class="glass pill camera-presets" style="padding: 4px 8px;">
197
+ <button onclick="setCamera('iso')" class="btn" style="font-size: 11px; padding: 4px 8px;">Isometric</button>
198
+ <button onclick="setCamera('top')" class="btn" style="font-size: 11px; padding: 4px 8px;">Top</button>
199
+ <button onclick="setCamera('front')" class="btn" style="font-size: 11px; padding: 4px 8px;">Front</button>
200
+ </div>
201
+ </header>
202
+
203
+ <!-- Floating Component Inspector (Toggleable) -->
204
+ <div id="inspector-card" class="glass info-card">
205
+ <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
206
+ <span id="card-tag" style="font-size: 10px; font-family: monospace; font-weight: 700; padding: 2px 8px; border-radius: 9999px; background: rgba(59, 130, 246, 0.2); color: #60a5fa;">
207
+ STAGE 1
208
+ </span>
209
+ <span id="card-sub" style="font-size: 11px; font-family: monospace; color: #94a3b8;">GEMINI.md · AGENTS.md</span>
210
+ </div>
211
+ <h2 id="card-title" style="font-size: 16px; font-weight: 700; margin-bottom: 6px; color: #ffffff;">1. CONTEXT FILES</h2>
212
+ <p id="card-desc" style="font-size: 12px; color: #cbd5e1; line-height: 1.5;">
213
+ The project's constitution loaded on every turn to ground the agent with workspace rules and project conventions.
214
+ </p>
215
+ <div id="card-detail" style="margin-top: 10px; padding: 8px 12px; background: rgba(2, 6, 23, 0.7); border-radius: 8px; border: 1px solid rgba(255,255,255,0.06); font-size: 11px; font-family: monospace; color: #38bdf8;">
216
+ ⚡ Immutable root constraints & user preferences
217
+ </div>
218
+ </div>
219
+
220
+ <!-- Bottom Stage Timeline -->
221
+ <footer class="bottom-bar">
222
+ <div class="stage-track glass" style="border-radius: 14px; padding: 6px;">
223
+ <div onclick="jumpToStep(0)" class="stage-btn active" data-step="0">1. Context</div>
224
+ <div onclick="jumpToStep(1)" class="stage-btn" data-step="1">2. Skills</div>
225
+ <div onclick="jumpToStep(2)" class="stage-btn" data-step="2">3. Subagents</div>
226
+ <div onclick="jumpToStep(3)" class="stage-btn" data-step="3">4. MCP & Tools</div>
227
+ <div onclick="jumpToStep(4)" class="stage-btn" data-step="4">5. Hooks</div>
228
+ <div onclick="jumpToStep(5)" class="stage-btn" data-step="5">6. Output</div>
229
+ </div>
230
+ </footer>
231
+
232
+ </div>
233
+
234
+ <script>
235
+ const canvas = document.getElementById("canvas3d");
236
+ const ctx = canvas.getContext("2d");
237
+
238
+ let width = 0, height = 0, dpr = window.devicePixelRatio || 1;
239
+
240
+ function resize() {
241
+ width = window.innerWidth;
242
+ height = window.innerHeight;
243
+ canvas.width = width * dpr;
244
+ canvas.height = height * dpr;
245
+ ctx.scale(dpr, dpr);
246
+ }
247
+ window.addEventListener("resize", resize);
248
+ resize();
249
+
250
+ let yaw = 0.55, pitch = 0.38, zoom = 1.0;
251
+ let targetYaw = 0.55, targetPitch = 0.38, targetZoom = 1.0;
252
+ let isDragging = false, lastMouseX = 0, lastMouseY = 0;
253
+ let autoRotate = true, activeStep = 0, isPlaying = true, stepTimer = 0, showInfo = true;
254
+
255
+ const nodesData = [
256
+ {
257
+ id: "context",
258
+ title: "1. CONTEXT FILES",
259
+ sub: "GEMINI.md · AGENTS.md",
260
+ tag: "CONSTITUTION",
261
+ desc: "The project's constitution loaded on every turn to ground the agent with workspace rules and project conventions.",
262
+ detail: "Immutable root constraints & user preferences",
263
+ color: "#3b82f6",
264
+ glow: "rgba(59, 130, 246, 0.7)",
265
+ pos: { x: -190, y: -45, z: 110 },
266
+ radius: 30
267
+ },
268
+ {
269
+ id: "skills",
270
+ title: "2. SKILLS",
271
+ sub: "Reusable capability packages",
272
+ tag: "ON-DEMAND",
273
+ desc: "Conditional knowledge packages loaded dynamically only when task intent requires specific workflows or domain knowledge.",
274
+ detail: "Zero prompt bloat: loads only relevant guidelines",
275
+ color: "#14b8a6",
276
+ glow: "rgba(20, 184, 166, 0.7)",
277
+ pos: { x: 0, y: -150, z: 140 },
278
+ radius: 30
279
+ },
280
+ {
281
+ id: "subagents",
282
+ title: "3. SUBAGENTS",
283
+ sub: "Persona-driven specialists",
284
+ tag: "DELEGATION",
285
+ desc: "Narrow specialized agents (auditor, reviewer, doc-writer) spawned in isolated sandboxes to complete focused sub-tasks.",
286
+ detail: "Autonomous parallel execution & focused scratchpads",
287
+ color: "#f97316",
288
+ glow: "rgba(249, 115, 22, 0.7)",
289
+ pos: { x: 190, y: -45, z: 110 },
290
+ radius: 30
291
+ },
292
+ {
293
+ id: "mcp",
294
+ title: "4. MCP SERVERS & TOOLS",
295
+ sub: "Model Context Protocol + Native Tools",
296
+ tag: "EXECUTION",
297
+ desc: "USB-C of agent tooling. Standardized protocol connecting cloud APIs, databases, git, and local bash execution seamlessly.",
298
+ detail: "Universal integration layer (Postgres, GitHub, Slack, CLI)",
299
+ color: "#6366f1",
300
+ glow: "rgba(99, 102, 241, 0.7)",
301
+ pos: { x: 170, y: 120, z: -70 },
302
+ radius: 30
303
+ },
304
+ {
305
+ id: "hooks",
306
+ title: "5. HOOKS",
307
+ sub: "Deterministic guardrails",
308
+ tag: "SAFETY SHIELD",
309
+ desc: "Outside LLM cognition. Deterministic code that intercepts pre/post tool calls to block destructive commands and data leaks.",
310
+ detail: "Hard security perimeter you never delegate to an LLM",
311
+ color: "#f43f5e",
312
+ glow: "rgba(244, 63, 94, 0.8)",
313
+ pos: { x: 0, y: 170, z: -120 },
314
+ radius: 34
315
+ },
316
+ {
317
+ id: "delivery",
318
+ title: "6. SAFE EXECUTION",
319
+ sub: "Verified Output & Artifacts",
320
+ tag: "DELIVERY",
321
+ desc: "Safe, scoped response delivered back to user with full audit transparency, rich artifacts, and verified changes.",
322
+ detail: "Rendered interactive widgets, reports & code diffs",
323
+ color: "#eab308",
324
+ glow: "rgba(234, 179, 8, 0.7)",
325
+ pos: { x: -170, y: 120, z: -70 },
326
+ radius: 30
327
+ }
328
+ ];
329
+
330
+ const coreNode = {
331
+ title: "ORCHESTRATOR",
332
+ color: "#38bdf8",
333
+ glow: "rgba(56, 189, 248, 0.8)",
334
+ pos: { x: 0, y: 0, z: 0 },
335
+ radius: 38
336
+ };
337
+
338
+ const particles = [];
339
+ const NUM_PARTICLES = 160;
340
+ for (let i = 0; i < NUM_PARTICLES; i++) {
341
+ const radius = 150 + Math.random() * 280;
342
+ const theta = Math.random() * Math.PI * 2;
343
+ const phi = (Math.random() - 0.5) * Math.PI;
344
+ particles.push({
345
+ x: radius * Math.cos(theta) * Math.cos(phi),
346
+ y: radius * Math.sin(phi),
347
+ z: radius * Math.sin(theta) * Math.cos(phi),
348
+ size: 1 + Math.random() * 2,
349
+ color: Math.random() > 0.5 ? "rgba(56, 189, 248, 0.4)" : "rgba(147, 197, 253, 0.25)"
350
+ });
351
+ }
352
+
353
+ const flowPath = [
354
+ { from: 0, to: 1 },
355
+ { from: 1, to: 2 },
356
+ { from: 2, to: 3 },
357
+ { from: 3, to: 4 },
358
+ { from: 4, to: 5 },
359
+ { from: 5, to: 0 }
360
+ ];
361
+
362
+ const energyPackets = [];
363
+ for (let i = 0; i < flowPath.length; i++) {
364
+ for (let j = 0; j < 3; j++) {
365
+ energyPackets.push({
366
+ pathIndex: i,
367
+ progress: (j / 3) + Math.random() * 0.2,
368
+ speed: 0.008 + Math.random() * 0.004,
369
+ size: 4 + Math.random() * 2.5
370
+ });
371
+ }
372
+ }
373
+
374
+ function project3D(p, cx, cy) {
375
+ const cosY = Math.cos(yaw), sinY = Math.sin(yaw);
376
+ const cosP = Math.cos(pitch), sinP = Math.sin(pitch);
377
+
378
+ let x1 = p.x * cosY - p.z * sinY;
379
+ let z1 = p.x * sinY + p.z * cosY;
380
+
381
+ let y2 = p.y * cosP - z1 * sinP;
382
+ let z2 = p.y * sinP + z1 * cosP;
383
+
384
+ const cameraDist = 520 / zoom;
385
+ const fov = 440;
386
+ const depth = cameraDist + z2;
387
+ const scale = fov / Math.max(depth, 40);
388
+
389
+ return { x: cx + x1 * scale, y: cy + y2 * scale, scale, depth: z2 };
390
+ }
391
+
392
+ window.addEventListener("mousedown", (e) => {
393
+ if (e.target === canvas) {
394
+ isDragging = true;
395
+ lastMouseX = e.clientX;
396
+ lastMouseY = e.clientY;
397
+ }
398
+ });
399
+
400
+ window.addEventListener("mousemove", (e) => {
401
+ if (isDragging) {
402
+ const dx = e.clientX - lastMouseX;
403
+ const dy = e.clientY - lastMouseY;
404
+ targetYaw += dx * 0.006;
405
+ targetPitch = Math.max(-1.1, Math.min(1.1, targetPitch - dy * 0.006));
406
+ lastMouseX = e.clientX;
407
+ lastMouseY = e.clientY;
408
+ autoRotate = false;
409
+ document.getElementById("btn-orbit").textContent = "Spin: OFF";
410
+ }
411
+ });
412
+
413
+ window.addEventListener("mouseup", () => { isDragging = false; });
414
+
415
+ window.addEventListener("wheel", (e) => {
416
+ if (e.target === canvas) {
417
+ e.preventDefault();
418
+ targetZoom = Math.max(0.6, Math.min(2.2, targetZoom - e.deltaY * 0.001));
419
+ }
420
+ }, { passive: false });
421
+
422
+ function updateUI(stepIdx) {
423
+ activeStep = stepIdx;
424
+ const data = nodesData[stepIdx];
425
+
426
+ const inspector = document.getElementById("inspector-card");
427
+ if (inspector) {
428
+ inspector.style.borderLeftColor = data.color;
429
+ document.getElementById("card-tag").textContent = data.tag;
430
+ document.getElementById("card-tag").style.backgroundColor = `${data.color}25`;
431
+ document.getElementById("card-tag").style.color = data.color;
432
+ document.getElementById("card-title").textContent = data.title;
433
+ document.getElementById("card-sub").textContent = data.sub;
434
+ document.getElementById("card-desc").textContent = data.desc;
435
+ document.getElementById("card-detail").textContent = "⚡ " + data.detail;
436
+ }
437
+
438
+ document.querySelectorAll(".stage-btn").forEach((btn, idx) => {
439
+ if (idx === stepIdx) btn.classList.add("active");
440
+ else btn.classList.remove("active");
441
+ });
442
+ }
443
+
444
+ function jumpToStep(idx) {
445
+ updateUI(idx);
446
+ stepTimer = 0;
447
+ }
448
+
449
+ function nextStep() {
450
+ jumpToStep((activeStep + 1) % nodesData.length);
451
+ }
452
+
453
+ function prevStep() {
454
+ jumpToStep((activeStep - 1 + nodesData.length) % nodesData.length);
455
+ }
456
+
457
+ function setCamera(type) {
458
+ autoRotate = false;
459
+ document.getElementById("btn-orbit").textContent = "Spin: OFF";
460
+ if (type === 'iso') { targetYaw = 0.55; targetPitch = 0.38; targetZoom = 1.0; }
461
+ else if (type === 'top') { targetYaw = 0; targetPitch = 1.45; targetZoom = 0.95; }
462
+ else if (type === 'front') { targetYaw = 0; targetPitch = 0.05; targetZoom = 1.05; }
463
+ }
464
+
465
+ document.getElementById("btn-play").addEventListener("click", () => {
466
+ isPlaying = !isPlaying;
467
+ document.getElementById("play-text").textContent = isPlaying ? "⏸ Pause" : "▶ Play";
468
+ });
469
+ document.getElementById("btn-next").addEventListener("click", nextStep);
470
+ document.getElementById("btn-prev").addEventListener("click", prevStep);
471
+ document.getElementById("btn-orbit").addEventListener("click", () => {
472
+ autoRotate = !autoRotate;
473
+ document.getElementById("btn-orbit").textContent = autoRotate ? "Spin: ON" : "Spin: OFF";
474
+ });
475
+ document.getElementById("btn-toggle-info").addEventListener("click", () => {
476
+ showInfo = !showInfo;
477
+ const card = document.getElementById("inspector-card");
478
+ if (showInfo) {
479
+ card.classList.remove("hidden");
480
+ document.getElementById("btn-toggle-info").textContent = "Info Box: ON";
481
+ } else {
482
+ card.classList.add("hidden");
483
+ document.getElementById("btn-toggle-info").textContent = "Info Box: OFF";
484
+ }
485
+ });
486
+
487
+ let lastTime = performance.now();
488
+
489
+ function render(currentTime) {
490
+ const dt = (currentTime - lastTime) / 1000;
491
+ lastTime = currentTime;
492
+
493
+ yaw += (targetYaw - yaw) * 0.08;
494
+ pitch += (targetPitch - pitch) * 0.08;
495
+ zoom += (targetZoom - zoom) * 0.08;
496
+
497
+ if (autoRotate) targetYaw += 0.003;
498
+
499
+ if (isPlaying) {
500
+ stepTimer += dt;
501
+ if (stepTimer > 3.8) {
502
+ stepTimer = 0;
503
+ nextStep();
504
+ }
505
+ }
506
+
507
+ ctx.clearRect(0, 0, width, height);
508
+ const cx = width / 2, cy = height / 2;
509
+
510
+ ctx.save();
511
+ ctx.strokeStyle = "rgba(51, 65, 85, 0.22)";
512
+ ctx.lineWidth = 1;
513
+ for (let r = 110; r <= 290; r += 60) {
514
+ ctx.beginPath();
515
+ for (let angle = 0; angle <= Math.PI * 2; angle += 0.1) {
516
+ const pt = project3D({ x: Math.cos(angle) * r, y: 40, z: Math.sin(angle) * r }, cx, cy);
517
+ if (angle === 0) ctx.moveTo(pt.x, pt.y);
518
+ else ctx.lineTo(pt.x, pt.y);
519
+ }
520
+ ctx.closePath();
521
+ ctx.stroke();
522
+ }
523
+ ctx.restore();
524
+
525
+ for (let p of particles) {
526
+ const proj = project3D(p, cx, cy);
527
+ if (proj.scale > 0) {
528
+ ctx.beginPath();
529
+ ctx.arc(proj.x, proj.y, Math.max(0.5, p.size * proj.scale), 0, Math.PI * 2);
530
+ ctx.fillStyle = p.color;
531
+ ctx.fill();
532
+ }
533
+ }
534
+
535
+ const projCore = project3D(coreNode.pos, cx, cy);
536
+
537
+ nodesData.forEach((node, i) => {
538
+ const projNode = project3D(node.pos, cx, cy);
539
+ ctx.save();
540
+ ctx.beginPath();
541
+ ctx.moveTo(projCore.x, projCore.y);
542
+ ctx.lineTo(projNode.x, projNode.y);
543
+ ctx.strokeStyle = (i === activeStep) ? "rgba(56, 189, 248, 0.55)" : "rgba(71, 85, 105, 0.18)";
544
+ ctx.lineWidth = (i === activeStep) ? 2.2 * projNode.scale : 1 * projNode.scale;
545
+ if (i === activeStep) ctx.setLineDash([5, 5]);
546
+ ctx.stroke();
547
+ ctx.restore();
548
+
549
+ const nextNode = nodesData[(i + 1) % nodesData.length];
550
+ const projNext = project3D(nextNode.pos, cx, cy);
551
+ ctx.save();
552
+ ctx.beginPath();
553
+ ctx.moveTo(projNode.x, projNode.y);
554
+ ctx.lineTo(projNext.x, projNext.y);
555
+ ctx.strokeStyle = (i === activeStep) ? "rgba(59, 130, 246, 0.65)" : "rgba(51, 65, 85, 0.28)";
556
+ ctx.lineWidth = (i === activeStep) ? 2.8 * projNode.scale : 1.2 * projNode.scale;
557
+ ctx.stroke();
558
+ ctx.restore();
559
+ });
560
+
561
+ energyPackets.forEach(ep => {
562
+ ep.progress += ep.speed;
563
+ if (ep.progress > 1) ep.progress = 0;
564
+
565
+ const fromNode = nodesData[flowPath[ep.pathIndex].from];
566
+ const toNode = nodesData[flowPath[ep.pathIndex].to];
567
+ const interpPos = {
568
+ x: fromNode.pos.x + (toNode.pos.x - fromNode.pos.x) * ep.progress,
569
+ y: fromNode.pos.y + (toNode.pos.y - fromNode.pos.y) * ep.progress,
570
+ z: fromNode.pos.z + (toNode.pos.z - fromNode.pos.z) * ep.progress
571
+ };
572
+
573
+ const proj = project3D(interpPos, cx, cy);
574
+ if (proj.scale > 0) {
575
+ ctx.save();
576
+ ctx.beginPath();
577
+ const r = ep.size * proj.scale;
578
+ ctx.arc(proj.x, proj.y, Math.max(1, r), 0, Math.PI * 2);
579
+ ctx.fillStyle = (ep.pathIndex === activeStep) ? "#38bdf8" : "#93c5fd";
580
+ ctx.shadowColor = "#38bdf8";
581
+ ctx.shadowBlur = 8;
582
+ ctx.fill();
583
+ ctx.restore();
584
+ }
585
+ });
586
+
587
+ if (projCore.scale > 0) {
588
+ ctx.save();
589
+ const coreR = coreNode.radius * projCore.scale;
590
+ const glowGrad = ctx.createRadialGradient(projCore.x, projCore.y, coreR * 0.2, projCore.x, projCore.y, coreR * 2.2);
591
+ glowGrad.addColorStop(0, "rgba(56, 189, 248, 0.35)");
592
+ glowGrad.addColorStop(1, "rgba(56, 189, 248, 0)");
593
+ ctx.fillStyle = glowGrad;
594
+ ctx.beginPath();
595
+ ctx.arc(projCore.x, projCore.y, coreR * 2.2, 0, Math.PI * 2);
596
+ ctx.fill();
597
+
598
+ const pulse = 1 + Math.sin(currentTime * 0.004) * 0.1;
599
+ ctx.beginPath();
600
+ ctx.arc(projCore.x, projCore.y, coreR * pulse, 0, Math.PI * 2);
601
+ ctx.strokeStyle = "rgba(56, 189, 248, 0.6)";
602
+ ctx.lineWidth = 1.6 * projCore.scale;
603
+ ctx.stroke();
604
+
605
+ const sphereGrad = ctx.createRadialGradient(projCore.x - coreR * 0.3, projCore.y - coreR * 0.3, coreR * 0.1, projCore.x, projCore.y, coreR);
606
+ sphereGrad.addColorStop(0, "#e0f2fe");
607
+ sphereGrad.addColorStop(0.5, "#0284c7");
608
+ sphereGrad.addColorStop(1, "#0369a1");
609
+ ctx.fillStyle = sphereGrad;
610
+ ctx.beginPath();
611
+ ctx.arc(projCore.x, projCore.y, coreR, 0, Math.PI * 2);
612
+ ctx.fill();
613
+
614
+ ctx.font = `bold ${Math.max(8, 10 * projCore.scale)}px sans-serif`;
615
+ ctx.fillStyle = "#ffffff";
616
+ ctx.textAlign = "center";
617
+ ctx.textBaseline = "middle";
618
+ ctx.fillText("ORCHESTRATOR", projCore.x, projCore.y);
619
+ ctx.restore();
620
+ }
621
+
622
+ const sortedNodes = nodesData.map((n, idx) => {
623
+ return { data: n, index: idx, proj: project3D(n.pos, cx, cy) };
624
+ }).sort((a, b) => a.proj.depth - b.proj.depth);
625
+
626
+ sortedNodes.forEach(item => {
627
+ const n = item.data;
628
+ const proj = item.proj;
629
+ const isActive = (item.index === activeStep);
630
+
631
+ if (proj.scale > 0) {
632
+ ctx.save();
633
+ const r = n.radius * proj.scale;
634
+
635
+ if (isActive) {
636
+ const activeGlow = ctx.createRadialGradient(proj.x, proj.y, r * 0.3, proj.x, proj.y, r * 2.4);
637
+ activeGlow.addColorStop(0, n.glow);
638
+ activeGlow.addColorStop(1, "rgba(0, 0, 0, 0)");
639
+ ctx.fillStyle = activeGlow;
640
+ ctx.beginPath();
641
+ ctx.arc(proj.x, proj.y, r * 2.4, 0, Math.PI * 2);
642
+ ctx.fill();
643
+
644
+ const pulseR = r * (1.25 + Math.sin(currentTime * 0.006) * 0.15);
645
+ ctx.beginPath();
646
+ ctx.arc(proj.x, proj.y, pulseR, 0, Math.PI * 2);
647
+ ctx.strokeStyle = n.color;
648
+ ctx.lineWidth = 2 * proj.scale;
649
+ ctx.stroke();
650
+ }
651
+
652
+ const nodeGrad = ctx.createRadialGradient(proj.x - r * 0.3, proj.y - r * 0.3, r * 0.1, proj.x, proj.y, r);
653
+ nodeGrad.addColorStop(0, "#ffffff");
654
+ nodeGrad.addColorStop(0.4, n.color);
655
+ nodeGrad.addColorStop(1, "#0f172a");
656
+
657
+ ctx.fillStyle = nodeGrad;
658
+ ctx.beginPath();
659
+ ctx.arc(proj.x, proj.y, r, 0, Math.PI * 2);
660
+ ctx.fill();
661
+
662
+ ctx.strokeStyle = isActive ? "#ffffff" : "rgba(255, 255, 255, 0.25)";
663
+ ctx.lineWidth = isActive ? 2 * proj.scale : 1 * proj.scale;
664
+ ctx.stroke();
665
+
666
+ ctx.font = `bold ${Math.max(9, 11 * proj.scale)}px sans-serif`;
667
+ ctx.fillStyle = isActive ? "#ffffff" : "#cbd5e1";
668
+ ctx.textAlign = "center";
669
+ ctx.textBaseline = "bottom";
670
+ ctx.fillText(n.title.split(". ")[1] || n.title, proj.x, proj.y - r - 6);
671
+
672
+ ctx.font = `${Math.max(8, 9 * proj.scale)}px monospace`;
673
+ ctx.fillStyle = isActive ? n.color : "#94a3b8";
674
+ ctx.fillText(n.tag, proj.x, proj.y - r - 18);
675
+
676
+ ctx.restore();
677
+ }
678
+ });
679
+
680
+ requestAnimationFrame(render);
681
+ }
682
+
683
+ updateUI(0);
684
+ requestAnimationFrame(render);
685
+ </script>
686
+ </body>
687
+ </html>