everydaytok commited on
Commit
562bf5e
Β·
verified Β·
1 Parent(s): f3748f0

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +447 -211
index.html CHANGED
@@ -1,219 +1,455 @@
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>Topological Engine Lab</title>
7
- <script src="https://cdn.tailwindcss.com"></script>
8
- <script src="https://cdn.plot.ly/plotly-2.24.1.min.js"></script>
9
- <style>
10
- body { background: #06090e; color: #cbd5e1; font-family: monospace; overflow: hidden; }
11
- .glass { background: rgba(15,23,42,0.95); border: 1px solid #334155; }
12
- #drawer { transition: transform 0.3s ease-in-out; z-index: 100; }
13
- .drawer-hidden { transform: translateY(100%); }
14
- </style>
 
 
 
15
  </head>
16
  <body class="flex flex-col h-screen overflow-hidden">
17
 
18
- <header class="glass p-3 flex justify-between items-center z-10 shadow-lg">
19
- <h1 class="text-blue-400 font-bold text-xs uppercase">MULTICELLULAR TOPOLOGY</h1>
20
- <button onclick="toggleDrawer()" class="bg-blue-600 px-4 py-1 rounded text-white text-xs font-bold shadow hover:bg-blue-500">βš™οΈ DIALS & TESTING</button>
21
- </header>
22
-
23
- <aside id="drawer" class="fixed inset-x-0 bottom-0 drawer-hidden glass p-5 flex flex-col gap-4 z-50 max-h-[90vh] overflow-y-auto shadow-2xl">
24
- <div class="flex justify-between items-center border-b border-slate-700 pb-2">
25
- <span class="text-orange-400 font-bold">LABORATORY CONTROLS</span>
26
- <button onclick="toggleDrawer()" class="text-slate-400 text-2xl">βœ•</button>
27
- </div>
28
-
29
- <div class="space-y-4">
30
- <!-- 1. TOPOLOGY ARCHITECTURE -->
31
- <section class="bg-slate-900 border border-blue-900 p-3 rounded">
32
- <h3 class="text-blue-400 font-bold text-[10px] mb-2 uppercase">1. Mesh Topology & Physics</h3>
33
-
34
- <label class="text-[9px] text-slate-500">PHYSICS BASE</label>
35
- <select id="architecture" class="w-full bg-black border border-slate-700 p-2 text-white mb-2 text-xs">
36
- <option value="additive">Additive Base[(A) + (B) = C]</option>
37
- <option value="multiplicative">Multiplicative Base[(A) * (B) = C]</option>
38
- </select>
39
-
40
- <div class="grid grid-cols-2 gap-2 mb-2">
41
- <div>
42
- <label class="text-[9px] text-slate-500">MESH DEPTH (CELLS)</label>
43
- <input type="number" id="depth" value="1" min="1" max="5" class="w-full bg-black border border-slate-700 p-2 text-white text-xs text-center">
44
- </div>
45
- <div>
46
- <label class="text-[9px] text-slate-500">STIFFNESS (K-COEFFICIENTS)</label>
47
- <select id="k_mode" class="w-full bg-black border border-slate-700 p-2 text-white text-xs">
48
- <option value="individual">Individual K per Node</option>
49
- <option value="uniform">Uniform (1 Global K)</option>
50
- </select>
51
- </div>
52
- </div>
53
-
54
- <select id="mode" class="w-full bg-black border border-slate-700 p-2 text-white text-xs">
55
- <option value="training">TRAINING PHASE (Adapt K Factors)</option>
56
- <option value="inference">INFERENCE PHASE (Predict C)</option>
57
- </select>
58
- <button onclick="applyConfig()" class="w-full bg-blue-700 mt-2 py-2 font-bold text-xs rounded hover:bg-blue-600">APPLY & REBUILD MESH</button>
59
- </section>
60
-
61
- <!-- 2. DATASET INJECTOR -->
62
- <section class="bg-slate-900 border border-purple-900 p-3 rounded">
63
- <h3 class="text-purple-400 font-bold text-[10px] mb-2 uppercase">2. Dataset Environment</h3>
64
- <select id="dataset" class="w-full bg-black border border-slate-700 p-2 text-white text-xs">
65
- <option value="housing">Housing Data (Weight: A*2.5, B*1.2)</option>
66
- <option value="subtraction">Subtraction Data (Weight: A*1.0, B*-1.0)</option>
67
- <option value="multiplication">Factorization Data</option>
68
- </select>
69
- <button onclick="genData()" class="w-full bg-purple-700 mt-2 py-2 font-bold text-xs rounded hover:bg-purple-600">START TRAINING BATCH</button>
70
- </section>
71
-
72
- <!-- 3. MANUAL TESTING -->
73
- <section class="bg-slate-900 border border-emerald-900 p-3 rounded">
74
- <h3 class="text-emerald-400 font-bold text-[10px] mb-2 uppercase">3. Manual Prediction Test</h3>
75
- <button onclick="loadExample()" class="w-full bg-slate-800 border border-slate-600 text-slate-300 py-1 mb-2 text-[10px] rounded hover:bg-slate-700">Auto-Fill Example for Selected Dataset</button>
76
- <div class="grid grid-cols-2 gap-2 mb-2">
77
- <input type="number" id="test_a" placeholder="Input A" class="w-full bg-black border border-slate-700 p-2 text-white text-center text-xs">
78
- <input type="number" id="test_b" placeholder="Input B" class="w-full bg-black border border-slate-700 p-2 text-white text-center text-xs">
79
- </div>
80
- <button onclick="runManual()" class="w-full bg-emerald-700 py-2 font-bold text-xs rounded hover:bg-emerald-600 text-white">TEST FORWARD PASS</button>
81
- </section>
82
-
83
- <button onclick="halt()" class="w-full border border-red-500 text-red-500 py-2 rounded font-bold text-xs hover:bg-red-900">HALT / RESET SYSTEM</button>
84
- </div>
85
- </aside>
86
-
87
- <main class="flex-grow flex flex-col min-h-0 relative">
88
- <div id="plot" class="flex-grow z-0"></div>
89
-
90
- <div class="h-48 glass border-t border-slate-700 p-3 flex flex-col z-10">
91
- <div class="flex justify-between items-center mb-1 border-b border-slate-800 pb-1">
92
- <h4 class="text-blue-300 text-[10px] font-bold tracking-widest">NETWORK TENSION & STIFFNESS</h4>
93
- <div id="error-val" class="text-sm font-bold text-red-400">ERR: 0.000</div>
94
- </div>
95
- <!-- Dynamic Grid for K Values -->
96
- <div id="nodes-ui" class="text-[10px] font-mono mt-1 flex-grow overflow-y-auto space-y-1"></div>
97
- </div>
98
- </main>
99
-
100
- <script>
101
- function toggleDrawer() { document.getElementById('drawer').classList.toggle('drawer-hidden'); }
102
-
103
- async function applyConfig() {
104
- await fetch('/config', {
105
- method: 'POST', headers: {'Content-Type': 'application/json'},
106
- body: JSON.stringify({
107
- mode: document.getElementById('mode').value,
108
- architecture: document.getElementById('architecture').value,
109
- dataset: document.getElementById('dataset').value,
110
- k_mode: document.getElementById('k_mode').value,
111
- depth: document.getElementById('depth').value
112
- })
113
- });
114
- window.plotted = false; // Force Plotly Redraw
115
- toggleDrawer();
116
- }
117
-
118
- async function genData() {
119
- await fetch('/generate', { method: 'POST' });
120
- toggleDrawer();
121
- }
122
-
123
- function loadExample() {
124
- const ds = document.getElementById('dataset').value;
125
- if (ds === 'housing') { document.getElementById('test_a').value = 4; document.getElementById('test_b').value = 5; } // Target = 16
126
- if (ds === 'subtraction') { document.getElementById('test_a').value = 20; document.getElementById('test_b').value = 8; } // Target = 12
127
- if (ds === 'multiplication') { document.getElementById('test_a').value = 6; document.getElementById('test_b').value = 7; } // Target = 42
128
- }
129
-
130
- async function runManual() {
131
- const a = document.getElementById('test_a').value;
132
- const b = document.getElementById('test_b').value;
133
- if (!a || !b) return alert("Please fill A and B");
134
- await fetch('/test_manual', {
135
- method: 'POST', headers: {'Content-Type':'application/json'},
136
- body: JSON.stringify({ a: a, b: b })
137
- });
138
- toggleDrawer();
139
- }
140
-
141
- async function halt() { await fetch('/halt', {method: 'POST'}); toggleDrawer(); }
142
-
143
- setInterval(async () => {
144
- try {
145
- const res = await fetch('/state');
146
- const d = await res.json();
147
-
148
- const err = Math.abs(d.error);
149
- document.getElementById('error-val').innerText = "TENSION: " + d.error.toFixed(4);
150
- document.getElementById('error-val').className = err < 0.05 ? "text-green-400 font-bold" : "text-red-400 font-bold";
151
-
152
- let ui = "";
153
- if (d.k_mode === 'uniform') {
154
- ui += `<div class="bg-purple-900/40 p-1 mb-2 text-center text-purple-300 font-bold rounded">UNIFORM GLOBAL STIFFNESS (K): ${d.global_k.toFixed(4)}</div>`;
155
- }
156
-
157
- // Plotly Line generation (The Mesh Web)
158
- let px=[], py=[], pz=[], ids=[];
159
- let path_a_x = [], path_a_y = [], path_a_z =[];
160
- let path_b_x = [], path_b_y = [], path_b_z =[];
161
-
162
- // Format Node Readouts
163
- Object.keys(d.nodes).sort().forEach(id => {
164
- const n = d.nodes[id];
165
- let k_display = d.k_mode === 'individual' ? `K: ${n.k.toFixed(3)}` : `(Global K)`;
166
- ui += `<div class="flex justify-between border-b border-slate-900 pb-0.5">
167
- <span class="${n.anchored ? 'text-red-300' : 'text-sky-300'}">NODE ${id}: <b class="text-white">${n.x.toFixed(2)}</b></span>
168
- <span class="text-slate-500">${k_display}</span>
169
- </div>`;
170
-
171
- // Route points for Line Tracing
172
- if(id === 'A') { path_a_x.push(n.x); path_a_y.push(n.y); path_a_z.push(n.z); px.push(n.x); py.push(n.y); pz.push(n.z); ids.push(id); }
173
- if(id === 'B') { path_b_x.push(n.x); path_b_y.push(n.y); path_b_z.push(n.z); px.push(n.x); py.push(n.y); pz.push(n.z); ids.push(id); }
174
- if(id.startsWith('Ha_')) { path_a_x.push(n.x); path_a_y.push(n.y); path_a_z.push(n.z); px.push(n.x); py.push(n.y); pz.push(n.z); ids.push(id); }
175
- if(id.startsWith('Hb_')) { path_b_x.push(n.x); path_b_y.push(n.y); path_b_z.push(n.z); px.push(n.x); py.push(n.y); pz.push(n.z); ids.push(id); }
176
- });
177
-
178
- // Add C to both paths to close the web
179
- path_a_x.push(d.nodes['C'].x); path_a_y.push(d.nodes['C'].y); path_a_z.push(d.nodes['C'].z);
180
- path_b_x.push(d.nodes['C'].x); path_b_y.push(d.nodes['C'].y); path_b_z.push(d.nodes['C'].z);
181
- px.push(d.nodes['C'].x); py.push(d.nodes['C'].y); pz.push(d.nodes['C'].z); ids.push('C');
182
-
183
- document.getElementById('nodes-ui').innerHTML = ui;
184
-
185
- // Creating the Multicellular Web Traces
186
- const traces =[
187
- { type: 'scatter3d', mode: 'markers+text', x: px, y: py, z: pz, text: ids, marker: {size: 6, color:'#38bdf8'}, name: 'Nodes' },
188
- { type: 'scatter3d', mode: 'lines', x: path_a_x, y: path_a_y, z: path_a_z, line: {color:'#475569', width:3}, name: 'Path A' },
189
- { type: 'scatter3d', mode: 'lines', x: path_b_x, y: path_b_y, z: path_b_z, line: {color:'#475569', width:3}, name: 'Path B' }
190
- ];
191
-
192
- // Add Cross-links if Multicellular to make it a "Mesh"
193
- if (d.depth > 1) {
194
- for(let i=0; i < d.depth - 1; i++) {
195
- traces.push({
196
- type: 'scatter3d', mode: 'lines',
197
- x: [d.nodes[`Ha_${i}`].x, d.nodes[`Hb_${i}`].x],
198
- y: [d.nodes[`Ha_${i}`].y, d.nodes[`Hb_${i}`].y],
199
- z: [d.nodes[`Ha_${i}`].z, d.nodes[`Hb_${i}`].z],
200
- line: {color:'rgba(71,85,105,0.4)', width:1}, showlegend: false
201
- });
202
- }
203
- }
204
-
205
- const layout = {
206
- margin:{l:0,r:0,t:0,b:0}, paper_bgcolor:'transparent', showlegend: false,
207
- scene:{
208
- xaxis:{color:'#475569'}, yaxis:{range:[-3,3], visible:false}, zaxis:{range:[-1,1], visible:false},
209
- camera: {eye:{x:0, y:-1.5, z:0.5}}
210
- }
211
- };
212
- if(!window.plotted){ Plotly.newPlot('plot', traces, layout, {displayModeBar:false}); window.plotted=true; }
213
- else { Plotly.react('plot', traces, layout); }
214
-
215
- } catch(e){}
216
- }, 200);
217
- </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
  </body>
219
  </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>Topological Engine Lab</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdn.plot.ly/plotly-2.24.1.min.js"></script>
9
+ <style>
10
+ body { background:#06090e; color:#cbd5e1; font-family:'Courier New',monospace; }
11
+ .glass { background:rgba(10,18,35,0.98); border:1px solid #1e2d40; }
12
+ #drawer { transition:transform .3s ease; z-index:200; }
13
+ .drawer-closed { transform:translateY(100%); }
14
+ .tog { transition:all .15s; }
15
+ .tog.on { color:#fff; }
16
+ .tog.off { background:#1e293b !important; color:#64748b; }
17
+ </style>
18
  </head>
19
  <body class="flex flex-col h-screen overflow-hidden">
20
 
21
+ <!-- ── HEADER ──────────────────────────────────────────────────────────── -->
22
+ <header class="glass flex-shrink-0 px-3 py-2 flex justify-between items-center">
23
+ <div class="flex flex-wrap gap-1.5 text-[9px] font-bold">
24
+ <span id="b-mode" class="px-2 py-0.5 rounded bg-yellow-900/60 text-yellow-300 border border-yellow-800/60">TRAIN</span>
25
+ <span id="b-arch" class="px-2 py-0.5 rounded bg-blue-900/60 text-blue-300 border border-blue-800/60">ADDITIVE</span>
26
+ <span id="b-k" class="px-2 py-0.5 rounded bg-purple-900/60 text-purple-300 border border-purple-800/60">SINGLE-K</span>
27
+ <span id="b-cell" class="px-2 py-0.5 rounded bg-green-900/60 text-green-300 border border-green-800/60">1-CELL</span>
28
+ <span id="b-data" class="px-2 py-0.5 rounded bg-pink-900/60 text-pink-300 border border-pink-800/60">HOUSING</span>
29
+ </div>
30
+ <div class="flex items-center gap-2 ml-2">
31
+ <span id="q-lbl" class="text-[9px] text-slate-600">Q:0</span>
32
+ <div id="run-dot" class="w-2 h-2 rounded-full bg-slate-700 flex-shrink-0"></div>
33
+ <button onclick="toggleDrawer()" class="text-[11px] bg-blue-700 hover:bg-blue-600 px-3 py-1 rounded font-bold">βš™ DIALS</button>
34
+ </div>
35
+ </header>
36
+
37
+ <!-- ── PLOTS ────────────────────────────────────────────────────────────���─ -->
38
+ <div class="flex-grow flex flex-col min-h-0 overflow-hidden">
39
+ <div id="mesh-plot" class="flex-grow min-h-0"></div>
40
+ <div id="err-plot" style="height:72px" class="flex-shrink-0 border-t border-slate-900"></div>
41
+ </div>
42
+
43
+ <!-- ── BOTTOM PANEL ─────────────────────────────────────────────────────── -->
44
+ <div class="glass flex-shrink-0 border-t border-slate-800" style="height:186px">
45
+ <div class="flex border-b border-slate-800 text-[10px]">
46
+ <button onclick="tab('nodes')" id="tab-nodes" class="flex-1 py-1.5 bg-blue-900/40 text-blue-300 font-bold">NODES</button>
47
+ <button onclick="tab('k')" id="tab-k" class="flex-1 py-1.5 text-slate-500">K VALUES</button>
48
+ <button onclick="tab('logs')" id="tab-logs" class="flex-1 py-1.5 text-slate-500">LOGS</button>
49
+ </div>
50
+ <div class="flex h-full overflow-hidden">
51
+ <!-- tension meter -->
52
+ <div class="w-20 flex-shrink-0 border-r border-slate-800 flex flex-col items-center justify-center p-2 gap-1">
53
+ <div class="text-[8px] text-slate-600">TENSION</div>
54
+ <div id="err-big" class="text-xl font-bold text-red-400 leading-tight text-center">0.00</div>
55
+ <div id="pred-val" class="text-[8px] text-slate-500 text-center">P:β€”</div>
56
+ <div id="iter-lbl" class="text-[8px] text-slate-700">IT:0</div>
57
+ </div>
58
+ <!-- panes -->
59
+ <div class="flex-grow overflow-y-auto p-2 text-[10px]">
60
+ <div id="pane-nodes"></div>
61
+ <div id="pane-k" class="hidden"></div>
62
+ <div id="pane-logs" class="hidden text-[9px] text-slate-500 space-y-0.5"></div>
63
+ </div>
64
+ </div>
65
+ </div>
66
+
67
+ <!-- ── DRAWER ───────────────────────────────────────────────────────────── -->
68
+ <aside id="drawer" class="drawer-closed fixed inset-x-0 bottom-0 glass border-t border-slate-700 p-4 flex flex-col gap-4 max-h-[93vh] overflow-y-auto">
69
+ <div class="flex justify-between items-center flex-shrink-0">
70
+ <span class="text-orange-400 font-bold">LABORATORY DIALS</span>
71
+ <button onclick="toggleDrawer()" class="text-slate-400 text-2xl leading-none">βœ•</button>
72
+ </div>
73
+
74
+ <!-- CONFIG TOGGLES -->
75
+ <div class="grid grid-cols-2 gap-2">
76
+
77
+ <div class="col-span-2 bg-slate-900 rounded p-3 border border-yellow-900/50">
78
+ <div class="text-yellow-400 text-[9px] font-bold mb-2">EXECUTION MODE</div>
79
+ <div class="flex gap-2">
80
+ <button class="tog on flex-1 py-2 rounded text-xs font-bold bg-yellow-700"
81
+ onclick="pick('mode','training',this,'bg-yellow-700')">TRAINING</button>
82
+ <button class="tog off flex-1 py-2 rounded text-xs font-bold"
83
+ onclick="pick('mode','inference',this,'bg-yellow-700')">INFERENCE</button>
84
+ </div>
85
+ </div>
86
+
87
+ <div class="bg-slate-900 rounded p-3 border border-blue-900/50">
88
+ <div class="text-blue-400 text-[9px] font-bold mb-2">MESH PHYSICS</div>
89
+ <button class="tog on w-full mb-1 py-2 rounded text-[10px] font-bold bg-blue-700"
90
+ onclick="pick('architecture','additive',this,'bg-blue-700')">ADDITIVE</button>
91
+ <button class="tog off w-full py-2 rounded text-[10px] font-bold"
92
+ onclick="pick('architecture','multiplicative',this,'bg-blue-700')">MULTIPLICATIVE</button>
93
+ </div>
94
+
95
+ <div class="bg-slate-900 rounded p-3 border border-purple-900/50">
96
+ <div class="text-purple-400 text-[9px] font-bold mb-2">K COEFFICIENT</div>
97
+ <button class="tog on w-full mb-1 py-2 rounded text-[10px] font-bold bg-purple-700"
98
+ onclick="pick('k_mode','single',this,'bg-purple-700')">SINGLE / NODE</button>
99
+ <button class="tog off w-full py-2 rounded text-[10px] font-bold"
100
+ onclick="pick('k_mode','triple',this,'bg-purple-700')">TRIPLE / EDGE</button>
101
+ </div>
102
+
103
+ <div class="col-span-2 bg-slate-900 rounded p-3 border border-green-900/50">
104
+ <div class="text-green-400 text-[9px] font-bold mb-2">CELL TOPOLOGY</div>
105
+ <div class="flex gap-2">
106
+ <button class="tog on flex-1 py-2 rounded text-xs font-bold bg-green-700"
107
+ onclick="pick('cell_mode','single',this,'bg-green-700')">SINGLE CELL β–³</button>
108
+ <button class="tog off flex-1 py-2 rounded text-xs font-bold"
109
+ onclick="pick('cell_mode','multi',this,'bg-green-700')">MULTI CELL β—‡</button>
110
+ </div>
111
+ </div>
112
+ </div>
113
+
114
+ <button onclick="applyConfig()"
115
+ class="w-full bg-white text-black py-3 rounded font-bold text-sm hover:bg-slate-200">
116
+ APPLY CONFIG &amp; RESET MESH
117
+ </button>
118
+
119
+ <!-- DATASET -->
120
+ <div class="bg-slate-900 rounded p-3 border border-pink-900/50">
121
+ <div class="text-pink-400 text-[9px] font-bold mb-2">DATASET ENVIRONMENT
122
+ <span class="text-slate-600 font-normal ml-1">(mesh never sees this law)</span>
123
+ </div>
124
+ <select id="ds-sel" onchange="refreshDS()"
125
+ class="w-full bg-black border border-slate-700 p-2 text-white text-xs rounded mb-2">
126
+ <option value="housing">Housing β€” AΓ—2.5 + BΓ—1.2</option>
127
+ <option value="subtraction">Subtraction β€” A βˆ’ B</option>
128
+ <option value="multiplication">Factorization β€” A Γ— B</option>
129
+ <option value="quadratic">Quadratic β€” AΒ² + B</option>
130
+ </select>
131
+ <p id="ds-hint" class="text-[9px] text-slate-500 italic mb-2"></p>
132
+ <div class="flex gap-2">
133
+ <input id="batch-n" type="number" value="30" min="5" max="200"
134
+ class="w-16 bg-black border border-slate-700 p-2 text-white text-sm text-center rounded">
135
+ <button onclick="startBatch()"
136
+ class="flex-1 bg-pink-800 hover:bg-pink-700 py-2 text-xs font-bold rounded">START BATCH</button>
137
+ </div>
138
+ </div>
139
+
140
+ <!-- CUSTOM INPUT -->
141
+ <div class="bg-slate-900 rounded p-3 border border-cyan-900/50">
142
+ <div class="text-cyan-400 text-[9px] font-bold mb-1">CUSTOM MANUAL INPUT</div>
143
+ <div id="ds-examples" class="text-[9px] text-slate-500 mb-2"></div>
144
+
145
+ <div class="grid grid-cols-3 gap-2 mb-2">
146
+ <div>
147
+ <div class="text-[8px] text-slate-600 mb-1">A (Input 1)</div>
148
+ <input id="ca" type="number" value="5" step="0.1"
149
+ class="w-full bg-black border border-slate-700 p-2 text-white text-sm text-center rounded"
150
+ oninput="updateExpected()">
151
+ </div>
152
+ <div>
153
+ <div class="text-[8px] text-slate-600 mb-1">B (Input 2)</div>
154
+ <input id="cb" type="number" value="3" step="0.1"
155
+ class="w-full bg-black border border-slate-700 p-2 text-white text-sm text-center rounded"
156
+ oninput="updateExpected()">
157
+ </div>
158
+ <div>
159
+ <div class="text-[8px] text-slate-600 mb-1">C Target (opt)</div>
160
+ <input id="cc" type="number" placeholder="auto" step="0.1"
161
+ class="w-full bg-black border border-slate-700 p-2 text-white text-sm text-center rounded">
162
+ </div>
163
+ </div>
164
+
165
+ <div id="expected-lbl" class="text-[9px] text-yellow-400 mb-2 min-h-[14px]"></div>
166
+ <button onclick="runCustom()"
167
+ class="w-full bg-cyan-800 hover:bg-cyan-700 py-2 text-xs font-bold rounded">RUN CUSTOM</button>
168
+ </div>
169
+
170
+ <button onclick="halt()"
171
+ class="w-full border border-red-700 text-red-500 py-2 rounded text-xs font-bold">HALT ENGINE</button>
172
+ </aside>
173
+
174
+ <script>
175
+ // ── CONFIG STATE ─────────────────────────────────────────────────────────────
176
+ const cfg = { mode:'training', architecture:'additive', k_mode:'single', cell_mode:'single' };
177
+
178
+ const TOG_GROUPS = {
179
+ mode: 'bg-yellow-700', architecture: 'bg-blue-700',
180
+ k_mode: 'bg-purple-700', cell_mode: 'bg-green-700'
181
+ };
182
+
183
+ function pick(key, val, btn, activeClass) {
184
+ cfg[key] = val;
185
+ // reset siblings in same group
186
+ btn.closest('.grid, .flex').querySelectorAll('.tog').forEach(b => {
187
+ if (b.dataset.key === undefined) b.dataset.key = key; // tag on first run
188
+ });
189
+ // find all buttons in this group by shared parent
190
+ const siblings = btn.parentElement.querySelectorAll('.tog');
191
+ siblings.forEach(b => { b.classList.add('off'); b.classList.remove('on', activeClass); });
192
+ btn.classList.remove('off'); btn.classList.add('on', activeClass);
193
+ }
194
+
195
+ // ── DRAWER ───────────────────────────────────────────────────────────────────
196
+ function toggleDrawer() {
197
+ document.getElementById('drawer').classList.toggle('drawer-closed');
198
+ refreshDS();
199
+ }
200
+
201
+ // ── APPLY CONFIG ─────────────────────────────────────────────────────────────
202
+ async function applyConfig() {
203
+ const ds = document.getElementById('ds-sel').value;
204
+ await fetch('/config', {
205
+ method:'POST', headers:{'Content-Type':'application/json'},
206
+ body: JSON.stringify({ ...cfg, dataset: ds })
207
+ });
208
+ document.getElementById('b-mode').innerText = cfg.mode==='training' ? 'TRAIN' : 'INFER';
209
+ document.getElementById('b-arch').innerText = cfg.architecture.toUpperCase().slice(0,5);
210
+ document.getElementById('b-k').innerText = cfg.k_mode==='single' ? 'SNGL-K' : 'TRPL-K';
211
+ document.getElementById('b-cell').innerText = cfg.cell_mode==='single' ? '1-CELL' : '3-CELL';
212
+ document.getElementById('b-data').innerText = ds.toUpperCase().slice(0,6);
213
+ meshPlotted = errPlotted = false;
214
+ toggleDrawer();
215
+ }
216
+
217
+ // ── BATCH ─────────────────────────────────────────────────────────────────────
218
+ async function startBatch() {
219
+ const n = document.getElementById('batch-n').value;
220
+ await fetch('/generate', {
221
+ method:'POST', headers:{'Content-Type':'application/json'},
222
+ body: JSON.stringify({ count: n })
223
+ });
224
+ toggleDrawer();
225
+ }
226
+
227
+ // ── CUSTOM RUN ────────────────────────────────────────────────────────────────
228
+ async function runCustom() {
229
+ const a = document.getElementById('ca').value;
230
+ const b = document.getElementById('cb').value;
231
+ const c = document.getElementById('cc').value;
232
+ await fetch('/run_custom', {
233
+ method:'POST', headers:{'Content-Type':'application/json'},
234
+ body: JSON.stringify({ a, b, c: c !== '' ? c : null })
235
+ });
236
+ toggleDrawer();
237
+ }
238
+
239
+ async function halt() {
240
+ await fetch('/halt', { method:'POST' });
241
+ toggleDrawer();
242
+ }
243
+
244
+ // ── DATASET META ──────────────────────────────────────────────────────────────
245
+ const DS = {
246
+ housing: {
247
+ hint: 'Hidden law: AΓ—2.5 + BΓ—1.2. Tests if mesh learns unequal weights.',
248
+ fn: (a,b) => (a*2.5 + b*1.2).toFixed(3),
249
+ ex: [{a:4,b:2},{a:6,b:3},{a:2,b:1}]
250
+ },
251
+ subtraction: {
252
+ hint: 'Hidden law: A βˆ’ B. Tests whether mesh learns a negative Kb. Best with additive arch.',
253
+ fn: (a,b) => (a - b).toFixed(3),
254
+ ex: [{a:8,b:3},{a:10,b:7},{a:5,b:5}]
255
+ },
256
+ multiplication: {
257
+ hint: 'Hidden law: AΓ—B. Multiplicative arch maps cleanly; additive must approximate.',
258
+ fn: (a,b) => (a*b).toFixed(3),
259
+ ex: [{a:6,b:7},{a:3,b:9},{a:5,b:5}]
260
+ },
261
+ quadratic: {
262
+ hint: 'Hidden law: AΒ²+B. Neither arch maps this perfectly β€” good stress test.',
263
+ fn: (a,b) => (a*a + b).toFixed(3),
264
+ ex: [{a:3,b:2},{a:4,b:5},{a:2,b:8}]
265
+ }
266
+ };
267
+
268
+ function gt(a, b) {
269
+ const ds = document.getElementById('ds-sel').value;
270
+ return DS[ds] ? DS[ds].fn(parseFloat(a), parseFloat(b)) : '?';
271
+ }
272
+
273
+ function refreshDS() {
274
+ const ds = document.getElementById('ds-sel').value;
275
+ const meta = DS[ds];
276
+ document.getElementById('ds-hint').innerText = meta.hint;
277
+ const exHtml = meta.ex.map(e => {
278
+ const v = meta.fn(e.a, e.b);
279
+ return `<span class="cursor-pointer text-cyan-500 underline"
280
+ onclick="fillCustom(${e.a},${e.b})">A=${e.a},B=${e.b}β†’${v}</span>`;
281
+ }).join(' ');
282
+ document.getElementById('ds-examples').innerHTML = 'Try: ' + exHtml;
283
+ updateExpected();
284
+ }
285
+
286
+ function fillCustom(a, b) {
287
+ document.getElementById('ca').value = a;
288
+ document.getElementById('cb').value = b;
289
+ document.getElementById('cc').value = '';
290
+ updateExpected();
291
+ }
292
+
293
+ function updateExpected() {
294
+ const a = document.getElementById('ca').value;
295
+ const b = document.getElementById('cb').value;
296
+ if (a && b) {
297
+ document.getElementById('expected-lbl').innerText = `Ground truth for this dataset: ${gt(a,b)}`;
298
+ }
299
+ }
300
+
301
+ refreshDS();
302
+
303
+ // ── TABS ──────────────────────────────────────────────────────────────────────
304
+ function tab(name) {
305
+ ['nodes','k','logs'].forEach(t => {
306
+ document.getElementById(`pane-${t}`).classList.toggle('hidden', t !== name);
307
+ const btn = document.getElementById(`tab-${t}`);
308
+ if (t === name) {
309
+ btn.className = 'flex-1 py-1.5 bg-blue-900/40 text-blue-300 font-bold text-[10px]';
310
+ } else {
311
+ btn.className = 'flex-1 py-1.5 text-slate-500 text-[10px]';
312
+ }
313
+ });
314
+ }
315
+
316
+ // ── VISUALIZATION ─────────────────────────────────────────────────────────────
317
+ const POS = {
318
+ single: { A:[-3,2], B:[-3,-2], C:[3,0] },
319
+ multi: { A:[-4,2.5],B:[-4,-2.5],H1:[0,1.3],H2:[0,-1.3],C:[4,0] }
320
+ };
321
+ const EDGEMAP = {
322
+ single: [['A','B'],['A','C'],['B','C']],
323
+ multi: [['A','B'],['A','H1'],['B','H1'],['A','H2'],['B','H2'],['H1','H2'],['H1','C'],['H2','C']]
324
+ };
325
+ const NCOL = { A:'#fb923c', B:'#c084fc', H1:'#4ade80', H2:'#facc15', C:'#38bdf8' };
326
+
327
+ let meshPlotted = false, errPlotted = false;
328
+
329
+ function buildMeshTraces(nodes, error, cellMode) {
330
+ const pos = POS[cellMode] || POS.single;
331
+ const edges = EDGEMAP[cellMode] || EDGEMAP.single;
332
+ const t = Math.min(Math.abs(error) / 8, 1); // tension 0–1
333
+ const ec = `rgb(${Math.round(t*230)},${Math.round((1-t)*160+40)},60)`;
334
+ const ew = 1.5 + t * 2.5;
335
+
336
+ const edgeTraces = edges
337
+ .filter(([u,v]) => nodes[u] && nodes[v] && pos[u] && pos[v])
338
+ .map(([u,v]) => ({
339
+ type:'scatter', mode:'lines',
340
+ x:[pos[u][0], pos[v][0]], y:[pos[u][1], pos[v][1]],
341
+ line:{ color: ec, width: ew },
342
+ hoverinfo:'none', showlegend:false
343
+ }));
344
+
345
+ const ids = Object.keys(nodes).filter(id => pos[id]);
346
+ const nodeTrace = {
347
+ type:'scatter', mode:'markers+text',
348
+ x: ids.map(id => pos[id][0]),
349
+ y: ids.map(id => pos[id][1]),
350
+ text: ids.map(id => `${id}\n${Number(nodes[id].x).toFixed(3)}`),
351
+ textposition:'top center',
352
+ textfont:{ size:10, color:'#94a3b8' },
353
+ marker:{
354
+ size: 18,
355
+ color: ids.map(id => NCOL[id] || '#64748b'),
356
+ line:{ width:2, color: ids.map(id => nodes[id].anchored ? '#ef4444' : '#22c55e') }
357
+ },
358
+ hoverinfo:'none', showlegend:false
359
+ };
360
+ return [...edgeTraces, nodeTrace];
361
+ }
362
+
363
+ const MESH_LAYOUT = {
364
+ margin:{l:10,r:10,t:10,b:10},
365
+ paper_bgcolor:'transparent', plot_bgcolor:'transparent',
366
+ xaxis:{ visible:false, range:[-5.5, 5.5] },
367
+ yaxis:{ visible:false, range:[-4.2, 4.2] },
368
+ showlegend:false
369
+ };
370
+ const ERR_LAYOUT = {
371
+ margin:{l:36,r:8,t:4,b:18},
372
+ paper_bgcolor:'transparent', plot_bgcolor:'transparent',
373
+ xaxis:{ visible:false },
374
+ yaxis:{ color:'#334155', gridcolor:'#0f172a', zeroline:true, zerolinecolor:'#22c55e', zerolinewidth:1 },
375
+ showlegend:false
376
+ };
377
+
378
+ // ── POLL ──────────────────────────────────────────────────────────────────────
379
+ setInterval(async () => {
380
+ try {
381
+ const r = await fetch('/state');
382
+ const d = await r.json();
383
+
384
+ // Header indicators
385
+ document.getElementById('run-dot').className =
386
+ `w-2 h-2 rounded-full flex-shrink-0 ${d.running ? 'bg-green-500' : 'bg-slate-700'}`;
387
+ document.getElementById('q-lbl').innerText = `Q:${d.queue_size}`;
388
+ document.getElementById('iter-lbl').innerText = `IT:${d.iter}`;
389
+
390
+ // Tension meter
391
+ const e = Math.abs(d.error);
392
+ const col = e < 0.1 ? 'text-green-400' : e < 2 ? 'text-yellow-400' : 'text-red-400';
393
+ document.getElementById('err-big').className = `text-xl font-bold ${col} leading-tight text-center`;
394
+ document.getElementById('err-big').innerText = d.error.toFixed(3);
395
+ document.getElementById('pred-val').innerText = `P:${d.prediction.toFixed(3)}`;
396
+
397
+ // Nodes pane
398
+ let nh = '';
399
+ Object.keys(d.nodes).sort().forEach(id => {
400
+ const n = d.nodes[id];
401
+ const icon = n.anchored ? '<span class="text-red-500">⊠</span>' : '<span class="text-green-500">β—Ž</span>';
402
+ const vcol = n.anchored ? 'text-slate-400' : 'text-sky-300';
403
+ nh += `<div class="flex justify-between items-center py-0.5 border-b border-slate-900">
404
+ ${icon} <span class="${vcol} ml-1">${id}</span>
405
+ <span class="text-white font-bold">${Number(n.x).toFixed(4)}</span>
406
+ </div>`;
407
+ });
408
+ document.getElementById('pane-nodes').innerHTML = nh;
409
+
410
+ // K pane
411
+ let kh = '';
412
+ Object.keys(d.k).forEach(key => {
413
+ const v = d.k[key];
414
+ const kc = v < 0 ? 'text-red-400' : v > 3 ? 'text-yellow-300' : 'text-purple-300';
415
+ kh += `<div class="flex justify-between items-center py-0.5 border-b border-slate-900">
416
+ <span class="text-slate-500">${key}</span>
417
+ <span class="${kc} font-bold">${v.toFixed(5)}</span>
418
+ </div>`;
419
+ });
420
+ document.getElementById('pane-k').innerHTML = kh;
421
+
422
+ // Logs pane
423
+ document.getElementById('pane-logs').innerHTML =
424
+ d.logs.map(l => `<div class="py-0.5 border-b border-slate-900/50">${l}</div>`).join('');
425
+
426
+ // Mesh plot β€” nodes at FIXED display positions; value shown as label
427
+ const meshTraces = buildMeshTraces(d.nodes, d.error, d.cell_mode);
428
+ if (!meshPlotted) {
429
+ Plotly.newPlot('mesh-plot', meshTraces, MESH_LAYOUT, { displayModeBar:false, responsive:true });
430
+ meshPlotted = true;
431
+ } else {
432
+ Plotly.react('mesh-plot', meshTraces, MESH_LAYOUT);
433
+ }
434
+
435
+ // Error history chart
436
+ const hist = d.history;
437
+ const errTrace = {
438
+ type:'scatter', mode:'lines',
439
+ x: hist.map((_,i) => i),
440
+ y: hist,
441
+ line:{ color:'#f97316', width:1.5 },
442
+ fill:'tozeroy', fillcolor:'rgba(249,115,22,0.08)'
443
+ };
444
+ if (!errPlotted) {
445
+ Plotly.newPlot('err-plot', [errTrace], ERR_LAYOUT, { displayModeBar:false, responsive:true });
446
+ errPlotted = true;
447
+ } else {
448
+ Plotly.react('err-plot', [errTrace], ERR_LAYOUT);
449
+ }
450
+
451
+ } catch(ex) { /* silent */ }
452
+ }, 200);
453
+ </script>
454
  </body>
455
  </html>