@timmeck/brain-core 2.33.0 → 2.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,1083 +3,1275 @@
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Brain Ecosystem — Unified Dashboard</title>
6
+ <title>Brain Mission Control</title>
7
7
  <style>
8
- :root {
9
- --bg-primary: #06080f;
10
- --bg-secondary: #0c1017;
11
- --bg-card: rgba(14, 18, 30, 0.85);
12
- --border: rgba(100, 120, 180, 0.15);
13
- --text-primary: #e8ecf4;
14
- --text-secondary: #8090b0;
15
- --text-muted: #506080;
16
- --cyan: #00e5ff;
17
- --green: #00ff88;
18
- --magenta: #ff0090;
19
- --gold: #ffd700;
20
- --purple: #b388ff;
21
- --orange: #ff9100;
22
- --red: #ff3d3d;
23
- --blue: #448aff;
24
- --sidebar-width: 220px;
25
- --header-height: 52px;
26
- }
27
-
28
- * { margin: 0; padding: 0; box-sizing: border-box; }
29
-
30
- body {
31
- background: var(--bg-primary);
32
- color: var(--text-primary);
33
- font-family: 'SF Mono', 'Cascadia Code', 'JetBrains Mono', 'Fira Code', monospace;
34
- font-size: 13px;
35
- line-height: 1.5;
36
- overflow: hidden;
37
- height: 100vh;
38
- }
39
-
40
- /* ── Sidebar ──────────────────────────────────── */
41
- .sidebar {
42
- position: fixed;
43
- left: 0; top: 0; bottom: 0;
44
- width: var(--sidebar-width);
45
- background: var(--bg-secondary);
46
- border-right: 1px solid var(--border);
47
- display: flex;
48
- flex-direction: column;
49
- z-index: 100;
50
- }
51
-
52
- .sidebar-brand {
53
- padding: 14px 16px;
54
- border-bottom: 1px solid var(--border);
55
- font-size: 14px;
56
- font-weight: 600;
57
- color: var(--cyan);
58
- text-shadow: 0 0 20px rgba(0, 229, 255, 0.3);
59
- display: flex;
60
- align-items: center;
61
- gap: 8px;
62
- }
63
-
64
- .sidebar-brand .logo {
65
- width: 24px; height: 24px;
66
- border-radius: 50%;
67
- background: radial-gradient(circle, var(--cyan) 0%, transparent 70%);
68
- animation: pulse 3s ease-in-out infinite;
69
- }
70
-
71
- .sidebar-nav { flex: 1; padding: 8px 0; overflow-y: auto; }
72
-
73
- .nav-section {
74
- padding: 8px 16px 4px;
75
- font-size: 10px;
76
- text-transform: uppercase;
77
- letter-spacing: 1.5px;
78
- color: var(--text-muted);
79
- }
80
-
81
- .nav-item {
82
- display: flex;
83
- align-items: center;
84
- gap: 10px;
85
- padding: 8px 16px;
86
- cursor: pointer;
87
- color: var(--text-secondary);
88
- transition: all 0.2s;
89
- border-left: 3px solid transparent;
90
- font-size: 12.5px;
91
- }
92
-
93
- .nav-item:hover {
94
- background: rgba(0, 229, 255, 0.05);
95
- color: var(--text-primary);
96
- }
97
-
98
- .nav-item.active {
99
- background: rgba(0, 229, 255, 0.08);
100
- color: var(--cyan);
101
- border-left-color: var(--cyan);
102
- }
103
-
104
- .nav-icon { font-size: 15px; width: 20px; text-align: center; }
105
-
106
- .sidebar-footer {
107
- padding: 10px 16px;
108
- border-top: 1px solid var(--border);
109
- font-size: 11px;
110
- color: var(--text-muted);
111
- }
112
-
113
- .connection-dot {
114
- display: inline-block;
115
- width: 7px; height: 7px;
116
- border-radius: 50%;
117
- background: var(--red);
118
- margin-right: 6px;
119
- transition: background 0.3s;
120
- }
121
-
122
- .connection-dot.connected { background: var(--green); box-shadow: 0 0 6px var(--green); }
123
-
124
- /* ── Header ───────────────────────────────────── */
125
- .header {
126
- position: fixed;
127
- left: var(--sidebar-width);
128
- top: 0; right: 0;
129
- height: var(--header-height);
130
- background: var(--bg-secondary);
131
- border-bottom: 1px solid var(--border);
132
- display: flex;
133
- align-items: center;
134
- justify-content: space-between;
135
- padding: 0 20px;
136
- z-index: 90;
137
- }
138
-
139
- .header-title { font-size: 15px; font-weight: 600; }
140
- .header-actions { display: flex; align-items: center; gap: 12px; }
141
-
142
- .btn {
143
- padding: 6px 14px;
144
- border: 1px solid var(--border);
145
- border-radius: 6px;
146
- background: rgba(0, 229, 255, 0.08);
147
- color: var(--cyan);
148
- font-family: inherit;
149
- font-size: 11.5px;
150
- cursor: pointer;
151
- transition: all 0.2s;
152
- }
153
- .btn:hover { background: rgba(0, 229, 255, 0.18); border-color: var(--cyan); }
154
-
155
- .health-badge {
156
- display: flex;
157
- align-items: center;
158
- gap: 6px;
159
- padding: 4px 12px;
160
- border-radius: 20px;
161
- font-size: 12px;
162
- font-weight: 600;
163
- }
164
- .health-badge.healthy { background: rgba(0, 255, 136, 0.1); color: var(--green); border: 1px solid rgba(0, 255, 136, 0.3); }
165
- .health-badge.degraded { background: rgba(255, 145, 0, 0.1); color: var(--orange); border: 1px solid rgba(255, 145, 0, 0.3); }
166
- .health-badge.critical { background: rgba(255, 61, 61, 0.1); color: var(--red); border: 1px solid rgba(255, 61, 61, 0.3); }
167
-
168
- /* ── Main Content ─────────────────────────────── */
169
- .main {
170
- margin-left: var(--sidebar-width);
171
- margin-top: var(--header-height);
172
- height: calc(100vh - var(--header-height));
173
- overflow-y: auto;
174
- padding: 20px;
175
- }
176
-
177
- .main::-webkit-scrollbar { width: 5px; }
178
- .main::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
179
- .main::-webkit-scrollbar-track { background: transparent; }
180
-
181
- .page { display: none; }
182
- .page.active { display: block; }
183
-
184
- /* ── Cards ────────────────────────────────────── */
185
- .card {
186
- background: var(--bg-card);
187
- border: 1px solid var(--border);
188
- border-radius: 10px;
189
- padding: 16px;
190
- margin-bottom: 16px;
191
- backdrop-filter: blur(8px);
192
- transition: border-color 0.3s;
193
- }
194
- .card:hover { border-color: rgba(100, 120, 180, 0.3); }
195
-
196
- .card-title {
197
- font-size: 11px;
198
- text-transform: uppercase;
199
- letter-spacing: 1.2px;
200
- color: var(--text-muted);
201
- margin-bottom: 12px;
202
- display: flex;
203
- align-items: center;
204
- gap: 8px;
205
- }
206
-
207
- .card-value {
208
- font-size: 28px;
209
- font-weight: 700;
210
- letter-spacing: -0.5px;
211
- }
212
-
213
- .card-sub { font-size: 11px; color: var(--text-secondary); margin-top: 4px; }
214
-
215
- /* ── Stat Grid ────────────────────────────────── */
216
- .stat-grid {
217
- display: grid;
218
- grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
219
- gap: 14px;
220
- margin-bottom: 20px;
221
- }
222
-
223
- /* ── Brain Cards ──────────────────────────────── */
224
- .brain-grid {
225
- display: grid;
226
- grid-template-columns: repeat(3, 1fr);
227
- gap: 16px;
228
- margin-bottom: 20px;
229
- }
230
-
231
- .brain-card { position: relative; overflow: hidden; }
232
- .brain-card .glow {
233
- position: absolute;
234
- top: -40px; right: -40px;
235
- width: 120px; height: 120px;
236
- border-radius: 50%;
237
- opacity: 0.08;
238
- pointer-events: none;
239
- }
240
- .brain-card.brain .glow { background: var(--cyan); }
241
- .brain-card.trading .glow { background: var(--green); }
242
- .brain-card.marketing .glow { background: var(--magenta); }
243
-
244
- .brain-card .brain-name {
245
- font-size: 14px;
246
- font-weight: 600;
247
- margin-bottom: 10px;
248
- display: flex;
249
- align-items: center;
250
- gap: 8px;
251
- }
252
- .brain-card.brain .brain-name { color: var(--cyan); }
253
- .brain-card.trading .brain-name { color: var(--green); }
254
- .brain-card.marketing .brain-name { color: var(--magenta); }
255
-
256
- .brain-stat-row {
257
- display: flex;
258
- justify-content: space-between;
259
- padding: 3px 0;
260
- font-size: 12px;
261
- }
262
- .brain-stat-row .label { color: var(--text-secondary); }
263
- .brain-stat-row .value { color: var(--text-primary); font-weight: 500; }
264
-
265
- .brain-status-dot {
266
- width: 8px; height: 8px;
267
- border-radius: 50%;
268
- display: inline-block;
269
- }
270
- .brain-status-dot.running { background: var(--green); box-shadow: 0 0 6px var(--green); }
271
- .brain-status-dot.stopped { background: var(--red); }
272
- .brain-status-dot.unknown { background: var(--text-muted); }
273
-
274
- .dash-link {
275
- display: inline-block;
276
- margin-top: 10px;
277
- font-size: 11px;
278
- color: var(--text-muted);
279
- text-decoration: none;
280
- transition: color 0.2s;
281
- }
282
- .dash-link:hover { color: var(--cyan); }
283
-
284
- /* ── Notification Feed ────────────────────────── */
285
- .notification-feed {
286
- max-height: 500px;
287
- overflow-y: auto;
288
- }
289
- .notification-feed::-webkit-scrollbar { width: 4px; }
290
- .notification-feed::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
291
-
292
- .notif-item {
293
- display: flex;
294
- gap: 10px;
295
- padding: 8px 10px;
296
- border-bottom: 1px solid rgba(100, 120, 180, 0.06);
297
- transition: background 0.2s;
298
- animation: slideIn 0.3s ease;
299
- }
300
- .notif-item:hover { background: rgba(255, 255, 255, 0.02); }
301
- .notif-item.breakthrough { border-left: 3px solid var(--gold); }
302
- .notif-item.notable { border-left: 3px solid var(--purple); }
303
- .notif-item.routine { border-left: 3px solid transparent; }
304
-
305
- .notif-time { font-size: 10px; color: var(--text-muted); white-space: nowrap; min-width: 55px; padding-top: 2px; }
306
- .notif-engine {
307
- font-size: 10px;
308
- padding: 1px 6px;
309
- border-radius: 3px;
310
- background: rgba(0, 229, 255, 0.1);
311
- color: var(--cyan);
312
- white-space: nowrap;
313
- height: fit-content;
314
- }
315
- .notif-content { flex: 1; font-size: 12px; color: var(--text-secondary); }
316
-
317
- /* ── Transfer View ─────────────────────────────── */
318
- .rule-list { display: flex; flex-direction: column; gap: 6px; }
319
- .rule-item {
320
- display: flex;
321
- align-items: center;
322
- gap: 10px;
323
- padding: 8px 12px;
324
- background: rgba(0, 0, 0, 0.2);
325
- border-radius: 6px;
326
- font-size: 12px;
327
- }
328
- .rule-arrow { color: var(--cyan); font-weight: bold; }
329
- .rule-enabled { color: var(--green); }
330
- .rule-disabled { color: var(--red); }
331
-
332
- .analogy-item {
333
- padding: 10px 12px;
334
- margin-bottom: 8px;
335
- background: rgba(0, 0, 0, 0.2);
336
- border-radius: 6px;
337
- border-left: 3px solid var(--purple);
338
- }
339
- .analogy-narrative { font-size: 12px; color: var(--text-secondary); margin-bottom: 4px; }
340
- .analogy-meta { font-size: 10px; color: var(--text-muted); display: flex; gap: 12px; }
341
-
342
- /* ── Attention View ────────────────────────────── */
343
- .topic-bar {
344
- display: flex;
345
- align-items: center;
346
- gap: 10px;
347
- margin-bottom: 6px;
348
- font-size: 12px;
349
- }
350
- .topic-name { min-width: 120px; color: var(--text-secondary); }
351
- .topic-bar-fill {
352
- height: 16px;
353
- border-radius: 3px;
354
- background: linear-gradient(90deg, var(--cyan), var(--blue));
355
- transition: width 0.5s;
356
- min-width: 2px;
357
- }
358
- .topic-score { min-width: 50px; text-align: right; color: var(--text-muted); font-size: 11px; }
359
-
360
- .context-badge {
361
- display: inline-block;
362
- padding: 3px 10px;
363
- border-radius: 12px;
364
- font-size: 11px;
365
- font-weight: 500;
366
- margin-right: 6px;
367
- }
368
- .context-badge.debugging { background: rgba(255, 61, 61, 0.15); color: var(--red); }
369
- .context-badge.coding { background: rgba(0, 229, 255, 0.15); color: var(--cyan); }
370
- .context-badge.reviewing { background: rgba(179, 136, 255, 0.15); color: var(--purple); }
371
- .context-badge.trading { background: rgba(0, 255, 136, 0.15); color: var(--green); }
372
- .context-badge.publishing { background: rgba(255, 145, 0, 0.15); color: var(--orange); }
373
- .context-badge.monitoring { background: rgba(68, 138, 255, 0.15); color: var(--blue); }
374
-
375
- /* ── Engines Table ──────────────────────────────── */
376
- .engine-grid {
377
- display: grid;
378
- grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
379
- gap: 8px;
380
- }
381
- .engine-chip {
382
- display: flex;
383
- align-items: center;
384
- gap: 6px;
385
- padding: 6px 10px;
386
- background: rgba(0, 0, 0, 0.2);
387
- border-radius: 6px;
388
- font-size: 11px;
389
- }
390
- .engine-dot {
391
- width: 6px; height: 6px;
392
- border-radius: 50%;
393
- }
394
- .engine-dot.active { background: var(--green); box-shadow: 0 0 4px var(--green); }
395
- .engine-dot.idle { background: var(--text-muted); }
396
-
397
- /* ── Two Column Layout ───────────────────────── */
398
- .two-col {
399
- display: grid;
400
- grid-template-columns: 1fr 1fr;
401
- gap: 16px;
402
- }
403
-
404
- /* ── Transfer History Table ──────────────────── */
405
- .transfer-table {
406
- width: 100%;
407
- border-collapse: collapse;
408
- font-size: 12px;
409
- }
410
- .transfer-table th {
411
- text-align: left;
412
- padding: 6px 10px;
413
- font-size: 10px;
414
- text-transform: uppercase;
415
- letter-spacing: 1px;
416
- color: var(--text-muted);
417
- border-bottom: 1px solid var(--border);
418
- }
419
- .transfer-table td {
420
- padding: 6px 10px;
421
- border-bottom: 1px solid rgba(100, 120, 180, 0.06);
422
- color: var(--text-secondary);
423
- }
424
-
425
- .status-badge {
426
- display: inline-block;
427
- padding: 1px 8px;
428
- border-radius: 3px;
429
- font-size: 10px;
430
- font-weight: 600;
431
- text-transform: uppercase;
432
- }
433
- .status-badge.pending { background: rgba(255, 215, 0, 0.15); color: var(--gold); }
434
- .status-badge.applied { background: rgba(0, 229, 255, 0.15); color: var(--cyan); }
435
- .status-badge.validated { background: rgba(0, 255, 136, 0.15); color: var(--green); }
436
- .status-badge.rejected { background: rgba(255, 61, 61, 0.15); color: var(--red); }
437
-
438
- /* ── Animations ──────────────────────────────── */
439
- @keyframes pulse {
440
- 0%, 100% { opacity: 0.6; transform: scale(1); }
441
- 50% { opacity: 1; transform: scale(1.1); }
442
- }
443
-
444
- @keyframes slideIn {
445
- from { opacity: 0; transform: translateY(-8px); }
446
- to { opacity: 1; transform: translateY(0); }
447
- }
448
-
449
- /* ── Responsive ──────────────────────────────── */
450
- @media (max-width: 1000px) {
451
- .brain-grid { grid-template-columns: 1fr; }
452
- .two-col { grid-template-columns: 1fr; }
453
- }
8
+ /* ── Reset + Variables ─────────────────────────────── */
9
+ *,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
10
+ :root{
11
+ --bg-deep:#050810;
12
+ --bg-primary:#0a0e1a;
13
+ --bg-card:rgba(12,16,28,0.65);
14
+ --bg-card-hover:rgba(16,22,40,0.75);
15
+ --bg-glass:rgba(20,28,50,0.45);
16
+ --border:rgba(80,120,200,0.12);
17
+ --border-bright:rgba(80,120,200,0.25);
18
+ --text:#e0e6f0;
19
+ --text-dim:#7a8ba8;
20
+ --text-muted:#4a5a78;
21
+ --cyan:#00e5ff;
22
+ --green:#00ff88;
23
+ --magenta:#ff0090;
24
+ --gold:#ffd700;
25
+ --purple:#b388ff;
26
+ --orange:#ff9100;
27
+ --red:#ff3d3d;
28
+ --blue:#448aff;
29
+ --sidebar-w:60px;
30
+ --sidebar-exp:220px;
31
+ --header-h:52px;
32
+ --font:'SF Mono','Cascadia Code','JetBrains Mono','Fira Code',monospace;
33
+ --radius:10px;
34
+ }
35
+ html,body{height:100%;overflow:hidden;font-family:var(--font);font-size:12px;color:var(--text);background:var(--bg-deep)}
36
+
37
+ /* ── Animated Background ───────────────────────────── */
38
+ body::before{
39
+ content:'';position:fixed;inset:0;z-index:0;
40
+ background:linear-gradient(135deg,#050810 0%,#0d0a20 30%,#080c1a 60%,#0a0818 100%);
41
+ animation:bgShift 30s ease infinite alternate;
42
+ }
43
+ @keyframes bgShift{
44
+ 0%{filter:hue-rotate(0deg) brightness(1)}
45
+ 50%{filter:hue-rotate(8deg) brightness(1.05)}
46
+ 100%{filter:hue-rotate(-5deg) brightness(0.98)}
47
+ }
48
+
49
+ /* ── Layout ────────────────────────────────────────── */
50
+ .app{display:flex;height:100vh;position:relative;z-index:1}
51
+
52
+ /* ── Sidebar ───────────────────────────────────────── */
53
+ .sidebar{
54
+ width:var(--sidebar-w);height:100vh;
55
+ background:rgba(8,12,24,0.85);
56
+ backdrop-filter:blur(20px);
57
+ border-right:1px solid var(--border);
58
+ display:flex;flex-direction:column;
59
+ transition:width 0.3s cubic-bezier(0.4,0,0.2,1);
60
+ overflow:hidden;flex-shrink:0;z-index:10;
61
+ }
62
+ .sidebar:hover{width:var(--sidebar-exp)}
63
+ .sidebar-logo{
64
+ height:var(--header-h);display:flex;align-items:center;
65
+ padding:0 16px;gap:10px;border-bottom:1px solid var(--border);
66
+ white-space:nowrap;overflow:hidden;
67
+ }
68
+ .sidebar-logo .dot{width:10px;height:10px;border-radius:50%;background:var(--cyan);flex-shrink:0;animation:pulse 3s infinite}
69
+ .sidebar-logo span{font-size:13px;font-weight:600;letter-spacing:0.5px;opacity:0;transition:opacity 0.3s}
70
+ .sidebar:hover .sidebar-logo span{opacity:1}
71
+ @keyframes pulse{0%,100%{opacity:1;box-shadow:0 0 6px var(--cyan)}50%{opacity:0.5;box-shadow:0 0 2px var(--cyan)}}
72
+
73
+ .nav{flex:1;display:flex;flex-direction:column;gap:2px;padding:8px 6px}
74
+ .nav-item{
75
+ display:flex;align-items:center;gap:12px;
76
+ padding:10px;border-radius:8px;cursor:pointer;
77
+ color:var(--text-dim);transition:all 0.2s;
78
+ white-space:nowrap;overflow:hidden;
79
+ }
80
+ .nav-item:hover{background:rgba(0,229,255,0.06);color:var(--text)}
81
+ .nav-item.active{background:rgba(0,229,255,0.1);color:var(--cyan)}
82
+ .nav-item svg{width:20px;height:20px;flex-shrink:0}
83
+ .nav-item span{font-size:11px;letter-spacing:0.5px;opacity:0;transition:opacity 0.3s}
84
+ .sidebar:hover .nav-item span{opacity:1}
85
+
86
+ .sidebar-footer{padding:12px;border-top:1px solid var(--border);overflow:hidden;white-space:nowrap}
87
+ .conn-dot{display:inline-block;width:7px;height:7px;border-radius:50%;margin-right:6px;animation:pulse 2s infinite}
88
+ .conn-dot.on{background:var(--green)}
89
+ .conn-dot.off{background:var(--red);animation:none}
90
+ .sidebar-footer .label{font-size:10px;color:var(--text-muted);opacity:0;transition:opacity 0.3s}
91
+ .sidebar:hover .sidebar-footer .label{opacity:1}
92
+
93
+ /* ── Main Content ──────────────────────────────────── */
94
+ .main{flex:1;display:flex;flex-direction:column;overflow:hidden}
95
+
96
+ /* ── Header ────────────────────────────────────────── */
97
+ .header{
98
+ height:var(--header-h);display:flex;align-items:center;justify-content:space-between;
99
+ padding:0 20px;border-bottom:1px solid var(--border);
100
+ background:rgba(8,12,24,0.6);backdrop-filter:blur(12px);flex-shrink:0;
101
+ }
102
+ .header-left{display:flex;align-items:center;gap:16px}
103
+ .header-title{font-size:14px;font-weight:600;letter-spacing:0.5px}
104
+ .header-badge{font-size:10px;padding:2px 8px;border-radius:4px;background:rgba(0,229,255,0.15);color:var(--cyan);letter-spacing:0.5px}
105
+ .header-right{display:flex;align-items:center;gap:12px}
106
+ .uptime{font-size:10px;color:var(--text-muted)}
107
+ .trigger-btn{
108
+ padding:6px 14px;border-radius:6px;border:1px solid var(--cyan);
109
+ background:transparent;color:var(--cyan);cursor:pointer;font-family:var(--font);
110
+ font-size:10px;letter-spacing:0.5px;transition:all 0.2s;
111
+ }
112
+ .trigger-btn:hover{background:rgba(0,229,255,0.15);box-shadow:0 0 12px rgba(0,229,255,0.2)}
113
+ .trigger-btn:active{transform:scale(0.97)}
114
+
115
+ /* ── Content ───────────────────────────────────────── */
116
+ .content{flex:1;overflow-y:auto;padding:16px 20px;position:relative}
117
+ .content::-webkit-scrollbar{width:4px}
118
+ .content::-webkit-scrollbar-track{background:transparent}
119
+ .content::-webkit-scrollbar-thumb{background:var(--border-bright);border-radius:2px}
120
+
121
+ .page{display:none;animation:fadeIn 0.25s ease}
122
+ .page.active{display:block}
123
+ @keyframes fadeIn{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}
124
+
125
+ /* ── Cards ─────────────────────────────────────────── */
126
+ .glass{
127
+ background:var(--bg-card);
128
+ backdrop-filter:blur(16px);
129
+ border:1px solid var(--border);
130
+ border-radius:var(--radius);
131
+ padding:16px;
132
+ transition:border-color 0.3s, box-shadow 0.3s;
133
+ }
134
+ .glass:hover{border-color:var(--border-bright);box-shadow:0 4px 24px rgba(0,0,0,0.3)}
135
+
136
+ .section-title{
137
+ font-size:10px;text-transform:uppercase;letter-spacing:2px;
138
+ color:var(--text-muted);margin-bottom:12px;
139
+ }
140
+
141
+ /* ── Stat Grid ─────────────────────────────────────── */
142
+ .stat-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:12px;margin-bottom:16px}
143
+ .stat-card{text-align:center;padding:14px 10px}
144
+ .stat-val{font-size:26px;font-weight:700;line-height:1.2}
145
+ .stat-label{font-size:10px;color:var(--text-dim);text-transform:uppercase;letter-spacing:1px;margin-top:4px}
146
+
147
+ /* ── Brain Cards ───────────────────────────────────── */
148
+ .brain-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px;margin-bottom:16px}
149
+ .brain-card{position:relative;overflow:hidden}
150
+ .brain-card::before{
151
+ content:'';position:absolute;top:0;left:0;right:0;height:2px;
152
+ }
153
+ .brain-card.brain-main::before{background:var(--cyan)}
154
+ .brain-card.brain-trading::before{background:var(--green)}
155
+ .brain-card.brain-marketing::before{background:var(--magenta)}
156
+ .brain-name{font-size:12px;font-weight:600;margin-bottom:8px}
157
+ .brain-stat{display:flex;justify-content:space-between;font-size:10px;color:var(--text-dim);margin-top:4px}
158
+ .brain-stat .val{color:var(--text)}
159
+
160
+ /* ── Notification Feed ─────────────────────────────── */
161
+ .notif-feed{max-height:300px;overflow-y:auto;display:flex;flex-direction:column;gap:6px}
162
+ .notif-feed::-webkit-scrollbar{width:3px}
163
+ .notif-feed::-webkit-scrollbar-thumb{background:var(--border-bright);border-radius:2px}
164
+ .notif-item{
165
+ display:flex;gap:10px;padding:8px 10px;border-radius:6px;
166
+ background:rgba(10,14,26,0.5);border-left:2px solid var(--border);
167
+ animation:slideIn 0.3s ease;font-size:11px;
168
+ }
169
+ @keyframes slideIn{from{opacity:0;transform:translateX(-8px)}to{opacity:1;transform:none}}
170
+ .notif-item.breakthrough{border-left-color:var(--gold);background:rgba(255,215,0,0.04)}
171
+ .notif-item.notable{border-left-color:var(--cyan)}
172
+ .notif-time{color:var(--text-muted);font-size:10px;flex-shrink:0;width:50px}
173
+ .notif-engine{
174
+ font-size:9px;padding:1px 6px;border-radius:3px;
175
+ background:rgba(0,229,255,0.1);color:var(--cyan);flex-shrink:0;
176
+ }
177
+ .notif-msg{flex:1;color:var(--text-dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
178
+
179
+ /* ── Neural Canvas ─────────────────────────────────── */
180
+ .neural-wrap{position:relative;border-radius:var(--radius);overflow:hidden;border:1px solid var(--border)}
181
+ .neural-canvas{display:block;width:100%;background:transparent;cursor:grab}
182
+ .neural-canvas:active{cursor:grabbing}
183
+ .neural-controls{
184
+ position:absolute;bottom:10px;right:10px;display:flex;gap:4px;
185
+ }
186
+ .neural-btn{
187
+ width:28px;height:28px;border-radius:6px;border:1px solid var(--border);
188
+ background:var(--bg-card);color:var(--text-dim);cursor:pointer;
189
+ display:flex;align-items:center;justify-content:center;font-size:14px;
190
+ transition:all 0.2s;font-family:var(--font);
191
+ }
192
+ .neural-btn:hover{background:var(--bg-card-hover);color:var(--cyan);border-color:var(--cyan)}
193
+ .neural-info{
194
+ position:absolute;top:10px;left:10px;font-size:10px;color:var(--text-muted);
195
+ background:rgba(5,8,16,0.7);padding:4px 8px;border-radius:4px;pointer-events:none;
196
+ }
197
+
198
+ /* ── Thought Stream ────────────────────────────────── */
199
+ .thought-filters{display:flex;gap:6px;margin-bottom:12px;flex-wrap:wrap}
200
+ .thought-filter{
201
+ font-size:10px;padding:3px 10px;border-radius:4px;cursor:pointer;
202
+ border:1px solid var(--border);background:transparent;color:var(--text-dim);
203
+ font-family:var(--font);transition:all 0.2s;
204
+ }
205
+ .thought-filter.active{border-color:var(--cyan);color:var(--cyan);background:rgba(0,229,255,0.08)}
206
+ .thought-list{display:flex;flex-direction:column;gap:4px;max-height:calc(100vh - 200px);overflow-y:auto}
207
+ .thought-list::-webkit-scrollbar{width:3px}
208
+ .thought-list::-webkit-scrollbar-thumb{background:var(--border-bright);border-radius:2px}
209
+ .thought-item{
210
+ display:flex;gap:8px;padding:6px 10px;border-radius:6px;
211
+ background:rgba(10,14,26,0.4);font-size:11px;animation:slideIn 0.2s ease;
212
+ }
213
+ .thought-item .time{color:var(--text-muted);font-size:10px;flex-shrink:0;width:55px}
214
+ .thought-item .engine-tag{
215
+ font-size:9px;padding:1px 6px;border-radius:3px;flex-shrink:0;
216
+ }
217
+ .thought-item .msg{flex:1;color:var(--text-dim)}
218
+ .thought-item.sig-breakthrough{background:rgba(255,215,0,0.05);border-left:2px solid var(--gold)}
219
+ .thought-item.sig-notable{border-left:2px solid var(--cyan)}
220
+
221
+ /* ── CodeGen ───────────────────────────────────────── */
222
+ .codegen-form{display:flex;gap:8px;margin-bottom:16px}
223
+ .codegen-form textarea{
224
+ flex:1;height:60px;resize:vertical;padding:10px;
225
+ background:rgba(10,14,26,0.6);border:1px solid var(--border);
226
+ border-radius:8px;color:var(--text);font-family:var(--font);font-size:11px;
227
+ }
228
+ .codegen-form textarea:focus{outline:none;border-color:var(--cyan)}
229
+ .codegen-form select{
230
+ padding:8px;background:rgba(10,14,26,0.6);border:1px solid var(--border);
231
+ border-radius:8px;color:var(--text);font-family:var(--font);font-size:11px;
232
+ }
233
+ .codegen-form select:focus{outline:none;border-color:var(--cyan)}
234
+ .gen-btn{
235
+ padding:8px 20px;border-radius:8px;border:none;
236
+ background:linear-gradient(135deg,rgba(0,229,255,0.2),rgba(0,229,255,0.1));
237
+ color:var(--cyan);cursor:pointer;font-family:var(--font);font-size:11px;
238
+ font-weight:600;transition:all 0.2s;align-self:flex-end;
239
+ }
240
+ .gen-btn:hover{background:linear-gradient(135deg,rgba(0,229,255,0.3),rgba(0,229,255,0.15));box-shadow:0 0 16px rgba(0,229,255,0.2)}
241
+
242
+ .pending-card{margin-bottom:12px;position:relative}
243
+ .pending-card .code-block{
244
+ background:rgba(5,8,16,0.8);border-radius:6px;padding:12px;
245
+ overflow-x:auto;font-size:11px;line-height:1.6;max-height:300px;overflow-y:auto;
246
+ margin:8px 0;
247
+ }
248
+ .pending-card .code-block::-webkit-scrollbar{width:3px;height:3px}
249
+ .pending-card .code-block::-webkit-scrollbar-thumb{background:var(--border-bright);border-radius:2px}
250
+ .review-actions{display:flex;gap:8px;align-items:center;margin-top:8px}
251
+ .review-actions input{
252
+ flex:1;padding:6px 10px;background:rgba(10,14,26,0.5);border:1px solid var(--border);
253
+ border-radius:6px;color:var(--text);font-family:var(--font);font-size:11px;
254
+ }
255
+ .review-actions input:focus{outline:none;border-color:var(--cyan)}
256
+ .btn-approve{
257
+ padding:5px 14px;border-radius:6px;border:1px solid var(--green);
258
+ background:rgba(0,255,136,0.08);color:var(--green);cursor:pointer;
259
+ font-family:var(--font);font-size:10px;transition:all 0.2s;
260
+ }
261
+ .btn-approve:hover{background:rgba(0,255,136,0.15)}
262
+ .btn-reject{
263
+ padding:5px 14px;border-radius:6px;border:1px solid var(--red);
264
+ background:rgba(255,61,61,0.08);color:var(--red);cursor:pointer;
265
+ font-family:var(--font);font-size:10px;transition:all 0.2s;
266
+ }
267
+ .btn-reject:hover{background:rgba(255,61,61,0.15)}
268
+ .btn-test{
269
+ padding:5px 14px;border-radius:6px;border:1px solid var(--gold);
270
+ background:rgba(255,215,0,0.08);color:var(--gold);cursor:pointer;
271
+ font-family:var(--font);font-size:10px;transition:all 0.2s;
272
+ }
273
+ .btn-test:hover{background:rgba(255,215,0,0.15)}
274
+
275
+ .gen-history{margin-top:16px}
276
+ .gen-table{width:100%;border-collapse:collapse;font-size:10px}
277
+ .gen-table th{text-align:left;padding:6px 8px;color:var(--text-muted);text-transform:uppercase;letter-spacing:1px;border-bottom:1px solid var(--border)}
278
+ .gen-table td{padding:6px 8px;border-bottom:1px solid rgba(80,120,200,0.06);color:var(--text-dim)}
279
+ .gen-table tr:hover td{background:rgba(0,229,255,0.03)}
280
+ .status-badge{font-size:9px;padding:1px 6px;border-radius:3px;display:inline-block}
281
+ .status-badge.approved{background:rgba(0,255,136,0.12);color:var(--green)}
282
+ .status-badge.rejected{background:rgba(255,61,61,0.12);color:var(--red)}
283
+ .status-badge.generated{background:rgba(0,229,255,0.12);color:var(--cyan)}
284
+ .status-badge.generating{background:rgba(255,145,0,0.12);color:var(--orange)}
285
+ .status-badge.ready{background:rgba(0,255,136,0.12);color:var(--green)}
286
+ .status-badge.testing{background:rgba(255,215,0,0.12);color:var(--gold)}
287
+ .status-badge.proposed{background:rgba(179,136,255,0.12);color:var(--purple)}
288
+ .status-badge.applied{background:rgba(0,255,136,0.12);color:var(--green)}
289
+ .status-badge.failed{background:rgba(255,61,61,0.12);color:var(--red)}
290
+
291
+ /* ── Self-Mod ──────────────────────────────────────── */
292
+ .diff-viewer{display:grid;grid-template-columns:1fr 1fr;gap:2px;margin:8px 0;font-size:10px;border-radius:6px;overflow:hidden}
293
+ .diff-old,.diff-new{padding:10px;background:rgba(5,8,16,0.8);overflow-x:auto;max-height:250px;overflow-y:auto;line-height:1.6}
294
+ .diff-old::-webkit-scrollbar,.diff-new::-webkit-scrollbar{width:3px;height:3px}
295
+ .diff-old::-webkit-scrollbar-thumb,.diff-new::-webkit-scrollbar-thumb{background:var(--border-bright);border-radius:2px}
296
+ .diff-label{font-size:9px;color:var(--text-muted);text-transform:uppercase;letter-spacing:1px;margin-bottom:4px}
297
+ .diff-line-del{color:var(--red);opacity:0.8}
298
+ .diff-line-add{color:var(--green);opacity:0.8}
299
+
300
+ /* ── Engine Grid ───────────────────────────────────── */
301
+ .engine-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:10px}
302
+ .engine-card{padding:12px}
303
+ .engine-card .e-name{font-size:11px;font-weight:600;margin-bottom:6px;display:flex;align-items:center;gap:6px}
304
+ .engine-card .e-dot{width:6px;height:6px;border-radius:50%}
305
+ .engine-card .e-stat{display:flex;justify-content:space-between;font-size:10px;color:var(--text-dim);margin-top:3px}
306
+ .engine-card .e-bar{height:3px;border-radius:2px;background:rgba(80,120,200,0.1);margin-top:6px;overflow:hidden}
307
+ .engine-card .e-bar-fill{height:100%;border-radius:2px;transition:width 0.5s ease}
308
+
309
+ /* ── Intelligence (Attention + Transfer) ───────────── */
310
+ .topic-list{display:flex;flex-direction:column;gap:6px}
311
+ .topic-item{display:flex;align-items:center;gap:10px;padding:6px 10px;border-radius:6px;background:rgba(10,14,26,0.4)}
312
+ .topic-name{flex:1;font-size:11px}
313
+ .topic-bar-bg{width:120px;height:4px;border-radius:2px;background:rgba(80,120,200,0.1)}
314
+ .topic-bar-fill{height:100%;border-radius:2px;background:var(--cyan);transition:width 0.4s ease}
315
+ .topic-score{font-size:10px;color:var(--text-dim);width:40px;text-align:right}
316
+
317
+ .rule-list{display:flex;flex-direction:column;gap:4px;margin-top:8px}
318
+ .rule-item{padding:6px 10px;border-radius:6px;background:rgba(10,14,26,0.4);font-size:10px;display:flex;justify-content:space-between}
319
+ .rule-item .r-name{color:var(--text)}
320
+ .rule-item .r-status{text-transform:uppercase;letter-spacing:0.5px}
321
+
322
+ /* ── Syntax Coloring ───────────────────────────────── */
323
+ .syn-keyword{color:var(--magenta)}
324
+ .syn-string{color:var(--green)}
325
+ .syn-comment{color:var(--text-muted);font-style:italic}
326
+ .syn-number{color:var(--gold)}
327
+ .syn-type{color:var(--cyan)}
328
+ .syn-fn{color:var(--blue)}
329
+
330
+ /* ── Two-Column Layout ─────────────────────────────── */
331
+ .two-col{display:grid;grid-template-columns:1fr 1fr;gap:12px}
332
+ @media(max-width:1000px){.two-col{grid-template-columns:1fr}}
333
+
334
+ /* ── Unavailable Message ───────────────────────────── */
335
+ .unavailable{text-align:center;padding:40px;color:var(--text-muted);font-size:12px}
336
+ .unavailable .icon{font-size:32px;margin-bottom:12px;opacity:0.3}
454
337
  </style>
455
338
  </head>
456
339
  <body>
457
-
458
- <!-- ═══════ Sidebar ═══════ -->
459
- <nav class="sidebar">
460
- <div class="sidebar-brand">
461
- <div class="logo"></div>
462
- Brain Ecosystem
463
- </div>
464
- <div class="sidebar-nav">
465
- <div class="nav-section">Dashboard</div>
466
- <div class="nav-item active" data-page="overview">
467
- <span class="nav-icon">&#9741;</span> Overview
468
- </div>
469
- <div class="nav-item" data-page="notifications">
470
- <span class="nav-icon">&#9888;</span> Notifications
471
- </div>
472
- <div class="nav-section">Intelligence</div>
473
- <div class="nav-item" data-page="attention">
474
- <span class="nav-icon">&#9678;</span> Attention
475
- </div>
476
- <div class="nav-item" data-page="transfer">
477
- <span class="nav-icon">&#8644;</span> Transfer
478
- </div>
479
- <div class="nav-item" data-page="engines">
480
- <span class="nav-icon">&#9881;</span> Engines
481
- </div>
482
- <div class="nav-section">Links</div>
483
- <a class="nav-item" href="http://localhost:7784" target="_blank" style="text-decoration:none">
484
- <span class="nav-icon" style="color:var(--cyan)">&#9679;</span> Brain :7784
485
- </a>
486
- <a class="nav-item" href="http://localhost:7785" target="_blank" style="text-decoration:none">
487
- <span class="nav-icon" style="color:var(--green)">&#9679;</span> Trading :7785
488
- </a>
489
- <a class="nav-item" href="http://localhost:7786" target="_blank" style="text-decoration:none">
490
- <span class="nav-icon" style="color:var(--magenta)">&#9679;</span> Marketing :7786
491
- </a>
492
- <a class="nav-item" href="http://localhost:7787" target="_blank" style="text-decoration:none">
493
- <span class="nav-icon" style="color:var(--gold)">&#9679;</span> CodeGen :7787
494
- </a>
495
- </div>
496
- <div class="sidebar-footer">
497
- <span class="connection-dot" id="connDot"></span>
498
- <span id="connLabel">Connecting...</span>
499
- <div style="margin-top:4px;font-size:10px" id="uptimeLabel">Uptime: --</div>
500
- </div>
501
- </nav>
502
-
503
- <!-- ═══════ Header ═══════ -->
504
- <header class="header">
505
- <div class="header-title" id="pageTitle">Overview</div>
506
- <div class="header-actions">
507
- <div class="health-badge healthy" id="healthBadge">
508
- <span id="healthScore">--</span>
509
- </div>
510
- <button class="btn" id="triggerBtn" title="Trigger Brain Feedback Cycle">&#9889; Trigger Cycle</button>
511
- </div>
512
- </header>
513
-
514
- <!-- ═══════ Main Content ═══════ -->
515
- <main class="main">
516
-
517
- <!-- ── Overview Page ─────────── -->
518
- <div class="page active" id="page-overview">
519
- <!-- Stat Cards -->
520
- <div class="stat-grid" id="overviewStats">
521
- <div class="card">
522
- <div class="card-title">Total Thoughts</div>
523
- <div class="card-value" id="statThoughts" style="color:var(--cyan)">0</div>
524
- <div class="card-sub" id="statThoughtsSub">across all engines</div>
340
+ <div class="app">
341
+ <!-- SIDEBAR -->
342
+ <nav class="sidebar">
343
+ <div class="sidebar-logo"><div class="dot"></div><span>Mission Control</span></div>
344
+ <div class="nav">
345
+ <div class="nav-item active" data-page="overview">
346
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg>
347
+ <span>Overview</span>
525
348
  </div>
526
- <div class="card">
527
- <div class="card-title">Discoveries</div>
528
- <div class="card-value" id="statDiscoveries" style="color:var(--gold)">0</div>
529
- <div class="card-sub">breakthroughs + notable</div>
349
+ <div class="nav-item" data-page="neural">
350
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><circle cx="5" cy="6" r="2"/><circle cx="19" cy="6" r="2"/><circle cx="5" cy="18" r="2"/><circle cx="19" cy="18" r="2"/><line x1="7" y1="7" x2="10" y2="10"/><line x1="14" y1="10" x2="17" y2="7"/><line x1="7" y1="17" x2="10" y2="14"/><line x1="14" y1="14" x2="17" y2="17"/></svg>
351
+ <span>Neural</span>
530
352
  </div>
531
- <div class="card">
532
- <div class="card-title">Active Engines</div>
533
- <div class="card-value" id="statEngines" style="color:var(--green)">0</div>
534
- <div class="card-sub" id="statEnginesSub">of 16 running</div>
353
+ <div class="nav-item" data-page="thoughts">
354
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
355
+ <span>Thoughts</span>
535
356
  </div>
536
- <div class="card">
537
- <div class="card-title">Transfer Score</div>
538
- <div class="card-value" id="statTransfer" style="color:var(--purple)">0</div>
539
- <div class="card-sub">cross-domain effectiveness</div>
357
+ <div class="nav-item" data-page="codegen">
358
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>
359
+ <span>CodeGen</span>
540
360
  </div>
541
- </div>
542
-
543
- <!-- Brain Cards -->
544
- <div class="brain-grid" id="brainGrid">
545
- <div class="card brain-card brain">
546
- <div class="glow"></div>
547
- <div class="brain-name"><span class="brain-status-dot unknown" id="brainDot"></span> Brain</div>
548
- <div class="brain-stat-row"><span class="label">Cycle</span><span class="value" id="brainCycle">--</span></div>
549
- <div class="brain-stat-row"><span class="label">Principles</span><span class="value" id="brainPrinciples">--</span></div>
550
- <div class="brain-stat-row"><span class="label">Hypotheses</span><span class="value" id="brainHypotheses">--</span></div>
551
- <div class="brain-stat-row"><span class="label">Experiments</span><span class="value" id="brainExperiments">--</span></div>
552
- <div class="brain-stat-row"><span class="label">Focus</span><span class="value" id="brainFocus">--</span></div>
553
- <a class="dash-link" href="http://localhost:7784" target="_blank">Open Consciousness Dashboard &rarr;</a>
361
+ <div class="nav-item" data-page="selfmod">
362
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/></svg>
363
+ <span>Self-Mod</span>
554
364
  </div>
555
- <div class="card brain-card trading">
556
- <div class="glow"></div>
557
- <div class="brain-name"><span class="brain-status-dot unknown" id="tradingDot"></span> Trading Brain</div>
558
- <div class="brain-stat-row"><span class="label">Cycle</span><span class="value" id="tradingCycle">--</span></div>
559
- <div class="brain-stat-row"><span class="label">Principles</span><span class="value" id="tradingPrinciples">--</span></div>
560
- <div class="brain-stat-row"><span class="label">Hypotheses</span><span class="value" id="tradingHypotheses">--</span></div>
561
- <div class="brain-stat-row"><span class="label">Experiments</span><span class="value" id="tradingExperiments">--</span></div>
562
- <div class="brain-stat-row"><span class="label">Focus</span><span class="value" id="tradingFocus">--</span></div>
563
- <a class="dash-link" href="http://localhost:7785" target="_blank">Open Consciousness Dashboard &rarr;</a>
365
+ <div class="nav-item" data-page="engines">
366
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg>
367
+ <span>Engines</span>
564
368
  </div>
565
- <div class="card brain-card marketing">
566
- <div class="glow"></div>
567
- <div class="brain-name"><span class="brain-status-dot unknown" id="marketingDot"></span> Marketing Brain</div>
568
- <div class="brain-stat-row"><span class="label">Cycle</span><span class="value" id="marketingCycle">--</span></div>
569
- <div class="brain-stat-row"><span class="label">Principles</span><span class="value" id="marketingPrinciples">--</span></div>
570
- <div class="brain-stat-row"><span class="label">Hypotheses</span><span class="value" id="marketingHypotheses">--</span></div>
571
- <div class="brain-stat-row"><span class="label">Experiments</span><span class="value" id="marketingExperiments">--</span></div>
572
- <div class="brain-stat-row"><span class="label">Focus</span><span class="value" id="marketingFocus">--</span></div>
573
- <a class="dash-link" href="http://localhost:7786" target="_blank">Open Consciousness Dashboard &rarr;</a>
369
+ <div class="nav-item" data-page="intelligence">
370
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/></svg>
371
+ <span>Intelligence</span>
574
372
  </div>
575
373
  </div>
576
-
577
- <!-- Recent Notifications -->
578
- <div class="card">
579
- <div class="card-title">&#9888; Recent Notifications</div>
580
- <div class="notification-feed" id="overviewNotifs" style="max-height:280px"></div>
581
- </div>
582
- </div>
583
-
584
- <!-- ── Notifications Page ────── -->
585
- <div class="page" id="page-notifications">
586
- <div class="card">
587
- <div class="card-title">&#9888; Live Thought Stream</div>
588
- <div class="notification-feed" id="allNotifs"></div>
374
+ <div class="sidebar-footer">
375
+ <span class="conn-dot off" id="connDot"></span>
376
+ <span class="label" id="connLabel">Disconnected</span>
589
377
  </div>
590
- </div>
591
-
592
- <!-- ── Attention Page ────────── -->
593
- <div class="page" id="page-attention">
594
- <div class="two-col">
595
- <div class="card">
596
- <div class="card-title">&#9678; Current Context</div>
597
- <div id="currentContext" style="margin-bottom:10px">
598
- <span class="context-badge monitoring">unknown</span>
599
- </div>
600
- <div class="card-title" style="margin-top:14px">Top Attention Topics</div>
601
- <div id="topTopics"></div>
378
+ </nav>
379
+
380
+ <!-- MAIN -->
381
+ <div class="main">
382
+ <header class="header">
383
+ <div class="header-left">
384
+ <span class="header-title">Brain Ecosystem</span>
385
+ <span class="header-badge" id="healthBadge">--</span>
602
386
  </div>
603
- <div class="card">
604
- <div class="card-title">Urgent Topics</div>
605
- <div id="urgentTopics"></div>
606
- <div class="card-title" style="margin-top:20px">Context Switches</div>
607
- <div id="contextSwitches" style="font-size:12px;color:var(--text-secondary)"></div>
387
+ <div class="header-right">
388
+ <span class="uptime" id="uptimeEl">Uptime: --</span>
389
+ <button class="trigger-btn" onclick="triggerCycle()">Trigger Cycle</button>
608
390
  </div>
609
- </div>
610
- <div class="card">
611
- <div class="card-title">Focus Timeline (Last 20)</div>
612
- <div id="focusTimeline" style="font-size:12px;color:var(--text-secondary)"></div>
613
- </div>
614
- </div>
615
-
616
- <!-- ── Transfer Page ─────────── -->
617
- <div class="page" id="page-transfer">
618
- <div class="stat-grid">
619
- <div class="card">
620
- <div class="card-title">Total Analogies</div>
621
- <div class="card-value" id="transferAnalogies" style="color:var(--purple)">0</div>
391
+ </header>
392
+
393
+ <div class="content">
394
+ <!-- OVERVIEW PAGE -->
395
+ <div class="page active" id="page-overview">
396
+ <div class="stat-grid" id="overviewStats"></div>
397
+ <div class="brain-grid" id="brainCards"></div>
398
+ <div class="two-col">
399
+ <div>
400
+ <div class="section-title">Live Notifications</div>
401
+ <div class="glass"><div class="notif-feed" id="notifFeed"></div></div>
402
+ </div>
403
+ <div>
404
+ <div class="section-title">Neural Preview</div>
405
+ <div class="neural-wrap glass" style="padding:0">
406
+ <canvas class="neural-canvas" id="overviewCanvas" height="280"></canvas>
407
+ <div class="neural-info" id="overviewInfo">0 nodes, 0 edges</div>
408
+ </div>
409
+ </div>
410
+ </div>
622
411
  </div>
623
- <div class="card">
624
- <div class="card-title">Total Transfers</div>
625
- <div class="card-value" id="transferTotal" style="color:var(--cyan)">0</div>
412
+
413
+ <!-- NEURAL PAGE -->
414
+ <div class="page" id="page-neural">
415
+ <div class="section-title">Neural Network</div>
416
+ <div class="neural-wrap" style="height:calc(100vh - 130px)">
417
+ <canvas class="neural-canvas" id="neuralCanvas"></canvas>
418
+ <div class="neural-info" id="neuralInfo">0 nodes, 0 edges</div>
419
+ <div class="neural-controls">
420
+ <button class="neural-btn" onclick="neuralZoom(1.2)" title="Zoom In">+</button>
421
+ <button class="neural-btn" onclick="neuralZoom(0.8)" title="Zoom Out">-</button>
422
+ <button class="neural-btn" onclick="neuralReset()" title="Reset">R</button>
423
+ </div>
424
+ </div>
626
425
  </div>
627
- <div class="card">
628
- <div class="card-title">Active Rules</div>
629
- <div class="card-value" id="transferRules" style="color:var(--green)">0</div>
426
+
427
+ <!-- THOUGHTS PAGE -->
428
+ <div class="page" id="page-thoughts">
429
+ <div class="section-title">Thought Stream</div>
430
+ <div class="thought-filters" id="thoughtFilters"></div>
431
+ <div class="thought-list" id="thoughtList"></div>
630
432
  </div>
631
- <div class="card">
632
- <div class="card-title">Transfer Score</div>
633
- <div class="card-value" id="transferScoreVal" style="color:var(--gold)">0</div>
433
+
434
+ <!-- CODEGEN PAGE -->
435
+ <div class="page" id="page-codegen">
436
+ <div class="section-title">Code Generation</div>
437
+ <div id="codegenContent"></div>
634
438
  </div>
635
- </div>
636
- <div class="two-col">
637
- <div class="card">
638
- <div class="card-title">&#8596; Cross-Domain Analogies</div>
639
- <div id="analogyList"></div>
439
+
440
+ <!-- SELF-MOD PAGE -->
441
+ <div class="page" id="page-selfmod">
442
+ <div class="section-title">Self-Modification</div>
443
+ <div id="selfmodContent"></div>
640
444
  </div>
641
- <div class="card">
642
- <div class="card-title">&#9889; Cross-Domain Rules</div>
643
- <div class="rule-list" id="ruleList"></div>
445
+
446
+ <!-- ENGINES PAGE -->
447
+ <div class="page" id="page-engines">
448
+ <div class="section-title">Engine Status</div>
449
+ <div class="engine-grid" id="engineGrid"></div>
644
450
  </div>
645
- </div>
646
- <div class="card">
647
- <div class="card-title">Transfer History</div>
648
- <table class="transfer-table" id="transferHistory">
649
- <thead>
650
- <tr><th>Source</th><th>Knowledge</th><th>Status</th><th>Confidence</th><th>Date</th></tr>
651
- </thead>
652
- <tbody></tbody>
653
- </table>
654
- </div>
655
- </div>
656
451
 
657
- <!-- ── Engines Page ──────────── -->
658
- <div class="page" id="page-engines">
659
- <div class="card">
660
- <div class="card-title">&#9881; Engine Status</div>
661
- <div class="engine-grid" id="engineGrid"></div>
662
- </div>
663
- <div class="card">
664
- <div class="card-title">Engine Thought Counts</div>
665
- <div id="engineCounts"></div>
452
+ <!-- INTELLIGENCE PAGE -->
453
+ <div class="page" id="page-intelligence">
454
+ <div class="two-col">
455
+ <div>
456
+ <div class="section-title">Attention Topics</div>
457
+ <div class="glass"><div class="topic-list" id="attentionTopics"></div></div>
458
+ </div>
459
+ <div>
460
+ <div class="section-title">Transfer Rules</div>
461
+ <div class="glass"><div class="rule-list" id="transferRules"></div></div>
462
+ </div>
463
+ </div>
464
+ <div style="margin-top:16px">
465
+ <div class="section-title">Transfer Analogies</div>
466
+ <div class="glass"><div id="analogies"></div></div>
467
+ </div>
468
+ </div>
666
469
  </div>
667
470
  </div>
668
-
669
- </main>
471
+ </div>
670
472
 
671
473
  <script>
672
- (function() {
673
- 'use strict';
674
-
675
- // ── State ──────────────────────────────────
676
- let state = {
677
- overview: null,
678
- transfer: null,
679
- attention: null,
680
- thoughts: [],
681
- engines: {},
682
- stats: {},
683
- notifications: [],
474
+ // ═══ STATE ═══
475
+ const MAX_THOUGHTS = 500;
476
+ const MAX_NOTIFS = 100;
477
+ let state = {
478
+ overview: null, transfer: null, attention: null,
479
+ thoughts: [], engines: {}, stats: {},
480
+ notifications: [], network: { nodes: [], edges: [] },
481
+ codegen: null, selfmod: null,
482
+ };
483
+ let connected = false;
484
+ const startTime = Date.now();
485
+
486
+ // Engine colors
487
+ const EC = {
488
+ orchestrator:'#00e5ff', self_observer:'#448aff', anomaly_detective:'#ff9100',
489
+ dream:'#b388ff', cross_domain:'#ff4081', journal:'#00ff88',
490
+ knowledge_distiller:'#ffd700', prediction:'#00e5ff', attention:'#ff6e40',
491
+ transfer:'#ea80fc', narrative:'#69f0ae', curiosity:'#40c4ff',
492
+ emergence:'#ffab40', debate:'#ff80ab', metacognition:'#b2ff59',
493
+ auto_experiment:'#ffd740', selftest:'#84ffff', teach:'#a7ffeb',
494
+ datascout:'#f48fb1', simulation:'#80d8ff', memory_palace:'#ce93d8',
495
+ goal:'#fff176', evolution:'#a5d6a7', reasoning:'#90caf9',
496
+ emotional:'#ef9a9a', self_scanner:'#80cbc4', self_modification:'#ffcc80',
497
+ codegen:'#b0bec5', code_miner:'#8d6e63', bootstrap:'#78909c',
498
+ };
499
+ function engineColor(name) {
500
+ const key = name ? name.toLowerCase().replace(/[^a-z_]/g, '') : '';
501
+ return EC[key] || '#6e7a8a';
502
+ }
503
+
504
+ // ═══ SSE CONNECTION ═══
505
+ let evtSource = null;
506
+ function connect() {
507
+ evtSource = new EventSource('/events');
508
+ evtSource.addEventListener('connected', function() {
509
+ connected = true;
510
+ document.getElementById('connDot').className = 'conn-dot on';
511
+ document.getElementById('connLabel').textContent = 'Connected';
512
+ });
513
+ evtSource.addEventListener('thought', function(e) {
514
+ const t = JSON.parse(e.data);
515
+ state.thoughts.unshift(t);
516
+ if (state.thoughts.length > MAX_THOUGHTS) state.thoughts.pop();
517
+ if (t.significance === 'breakthrough' || t.significance === 'notable') {
518
+ state.notifications.unshift(t);
519
+ if (state.notifications.length > MAX_NOTIFS) state.notifications.pop();
520
+ }
521
+ renderThoughts();
522
+ renderNotifications();
523
+ });
524
+ evtSource.addEventListener('status', function(e) {
525
+ const d = JSON.parse(e.data);
526
+ if (d.overview) state.overview = d.overview;
527
+ if (d.engines) state.engines = d.engines;
528
+ if (d.stats) state.stats = d.stats;
529
+ renderOverview();
530
+ renderEngines();
531
+ });
532
+ evtSource.addEventListener('network', function(e) {
533
+ state.network = JSON.parse(e.data);
534
+ renderNetwork();
535
+ });
536
+ evtSource.addEventListener('codegen:generated', function() { fetchCodegen(); });
537
+ evtSource.addEventListener('codegen:approved', function() { fetchCodegen(); });
538
+ evtSource.addEventListener('codegen:rejected', function() { fetchCodegen(); });
539
+ evtSource.addEventListener('selfmod:approved', function() { fetchSelfmod(); });
540
+ evtSource.addEventListener('selfmod:rejected', function() { fetchSelfmod(); });
541
+ evtSource.addEventListener('selfmod:ready', function() { fetchSelfmod(); });
542
+ evtSource.addEventListener('heartbeat', function() {});
543
+ evtSource.onerror = function() {
544
+ connected = false;
545
+ document.getElementById('connDot').className = 'conn-dot off';
546
+ document.getElementById('connLabel').textContent = 'Reconnecting...';
684
547
  };
685
- let connected = false;
686
- const startTime = Date.now();
687
- const MAX_THOUGHTS = 500;
688
-
689
- // ── Navigation ─────────────────────────────
690
- const navItems = document.querySelectorAll('.nav-item[data-page]');
691
- const pages = document.querySelectorAll('.page');
692
- const pageTitle = document.getElementById('pageTitle');
693
-
694
- navItems.forEach(item => {
695
- item.addEventListener('click', () => {
696
- const page = item.dataset.page;
697
- navItems.forEach(n => n.classList.remove('active'));
698
- item.classList.add('active');
699
- pages.forEach(p => p.classList.remove('active'));
700
- document.getElementById('page-' + page).classList.add('active');
701
- pageTitle.textContent = item.textContent.trim();
702
- });
548
+ }
549
+
550
+ // ═══ INITIAL FETCH ═══
551
+ async function fetchInitial() {
552
+ try {
553
+ const r = await fetch('/api/state');
554
+ const d = await r.json();
555
+ state.overview = d.overview;
556
+ state.transfer = d.transfer;
557
+ state.attention = d.attention;
558
+ state.thoughts = (d.thoughts || []).slice(0, MAX_THOUGHTS);
559
+ state.engines = d.engines || {};
560
+ state.stats = d.stats || {};
561
+ state.notifications = (d.notifications || []).slice(0, MAX_NOTIFS);
562
+ renderAll();
563
+ } catch(e) { console.warn('fetchInitial failed', e); }
564
+ try {
565
+ const r = await fetch('/api/network');
566
+ state.network = await r.json();
567
+ renderNetwork();
568
+ } catch(e) { console.warn('fetchNetwork failed', e); }
569
+ fetchCodegen();
570
+ fetchSelfmod();
571
+ }
572
+
573
+ async function fetchCodegen() {
574
+ try {
575
+ const r = await fetch('/api/codegen/state');
576
+ state.codegen = await r.json();
577
+ renderCodegen();
578
+ } catch(e) { console.warn('fetchCodegen failed', e); }
579
+ }
580
+
581
+ async function fetchSelfmod() {
582
+ try {
583
+ const r = await fetch('/api/selfmod/list');
584
+ state.selfmod = await r.json();
585
+ renderSelfmod();
586
+ } catch(e) { console.warn('fetchSelfmod failed', e); }
587
+ }
588
+
589
+ // ═══ NAVIGATION ═══
590
+ document.querySelectorAll('.nav-item').forEach(function(el) {
591
+ el.addEventListener('click', function() {
592
+ document.querySelectorAll('.nav-item').forEach(function(n) { n.classList.remove('active'); });
593
+ document.querySelectorAll('.page').forEach(function(p) { p.classList.remove('active'); });
594
+ el.classList.add('active');
595
+ var page = el.dataset.page;
596
+ document.getElementById('page-' + page).classList.add('active');
597
+ if (page === 'neural') requestAnimationFrame(function() { resizeNeuralCanvas(); });
598
+ if (page === 'codegen') fetchCodegen();
599
+ if (page === 'selfmod') fetchSelfmod();
703
600
  });
601
+ });
602
+
603
+ // ═══ RENDER ═══
604
+ function renderAll() {
605
+ renderOverview();
606
+ renderNotifications();
607
+ renderThoughts();
608
+ renderEngines();
609
+ renderIntelligence();
610
+ }
611
+
612
+ function escapeHtml(s) {
613
+ if (!s) return '';
614
+ return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
615
+ }
616
+
617
+ function timeSince(ts) {
618
+ var s = Math.floor((Date.now() - ts) / 1000);
619
+ if (s < 60) return s + 's';
620
+ if (s < 3600) return Math.floor(s / 60) + 'm';
621
+ if (s < 86400) return Math.floor(s / 3600) + 'h';
622
+ return Math.floor(s / 86400) + 'd';
623
+ }
624
+
625
+ function formatTime(ts) {
626
+ if (!ts) return '--';
627
+ var d = new Date(typeof ts === 'number' ? ts : ts);
628
+ return d.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
629
+ }
630
+
631
+ // ── Overview ──
632
+ function renderOverview() {
633
+ var o = state.overview;
634
+ var healthScore = o && o.healthScore != null ? o.healthScore : '--';
635
+ document.getElementById('healthBadge').textContent = typeof healthScore === 'number' ? 'Health: ' + healthScore : '--';
636
+
637
+ var stats = state.stats;
638
+ var el = document.getElementById('overviewStats');
639
+ var brains = o && o.brains ? o.brains : {};
640
+ var brain = brains.brain || {};
641
+ el.innerHTML =
642
+ '<div class="glass stat-card"><div class="stat-val" style="color:var(--cyan)">' + (stats.total || 0) + '</div><div class="stat-label">Thoughts</div></div>' +
643
+ '<div class="glass stat-card"><div class="stat-val" style="color:var(--green)">' + (brain.principles || 0) + '</div><div class="stat-label">Principles</div></div>' +
644
+ '<div class="glass stat-card"><div class="stat-val" style="color:var(--gold)">' + (brain.hypotheses || 0) + '</div><div class="stat-label">Hypotheses</div></div>' +
645
+ '<div class="glass stat-card"><div class="stat-val" style="color:var(--purple)">' + (brain.experiments || 0) + '</div><div class="stat-label">Experiments</div></div>' +
646
+ '<div class="glass stat-card"><div class="stat-val" style="color:var(--orange)">' + (brain.cycle || 0) + '</div><div class="stat-label">Cycle</div></div>';
647
+
648
+ var grid = document.getElementById('brainCards');
649
+ var html = '';
650
+ for (var name in brains) {
651
+ var b = brains[name];
652
+ var cls = name === 'brain' ? 'brain-main' : name.indexOf('trading') >= 0 ? 'brain-trading' : 'brain-marketing';
653
+ html += '<div class="glass brain-card ' + cls + '">' +
654
+ '<div class="brain-name">' + escapeHtml(name) + '</div>' +
655
+ '<div class="brain-stat"><span>Status</span><span class="val">' + (b.status || '--') + '</span></div>' +
656
+ '<div class="brain-stat"><span>Cycle</span><span class="val">' + (b.cycle || 0) + '</span></div>' +
657
+ '<div class="brain-stat"><span>Focus</span><span class="val">' + escapeHtml(b.focus || 'idle') + '</span></div>' +
658
+ '</div>';
659
+ }
660
+ grid.innerHTML = html;
661
+ }
662
+
663
+ // ── Notifications ──
664
+ function renderNotifications() {
665
+ var el = document.getElementById('notifFeed');
666
+ var items = state.notifications.slice(0, 50);
667
+ var html = '';
668
+ for (var i = 0; i < items.length; i++) {
669
+ var t = items[i];
670
+ var cls = t.significance === 'breakthrough' ? 'breakthrough' : t.significance === 'notable' ? 'notable' : '';
671
+ html += '<div class="notif-item ' + cls + '">' +
672
+ '<span class="notif-time">' + formatTime(t.timestamp) + '</span>' +
673
+ '<span class="notif-engine" style="background:' + engineColor(t.engine) + '22;color:' + engineColor(t.engine) + '">' + escapeHtml(t.engine) + '</span>' +
674
+ '<span class="notif-msg">' + escapeHtml(t.content) + '</span></div>';
675
+ }
676
+ el.innerHTML = html || '<div style="color:var(--text-muted);padding:20px;text-align:center">No notifications yet</div>';
677
+ }
704
678
 
705
- // ── Trigger Button ─────────────────────────
706
- document.getElementById('triggerBtn').addEventListener('click', async () => {
707
- try {
708
- const res = await fetch('/api/trigger', { method: 'POST' });
709
- const data = await res.json();
710
- if (data.triggered) {
711
- document.getElementById('triggerBtn').textContent = '\u2713 Triggered!';
712
- setTimeout(() => { document.getElementById('triggerBtn').innerHTML = '&#9889; Trigger Cycle'; }, 2000);
713
- }
714
- } catch (e) { console.error('Trigger failed', e); }
715
- });
679
+ // ── Thoughts ──
680
+ var thoughtFilter = 'all';
681
+ var knownEngines = {};
716
682
 
717
- // ── Format Helpers ─────────────────────────
718
- function formatTime(ts) {
719
- if (!ts) return '--';
720
- const d = new Date(ts);
721
- return d.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
683
+ function renderThoughtFilters() {
684
+ for (var i = 0; i < state.thoughts.length; i++) {
685
+ if (state.thoughts[i].engine) knownEngines[state.thoughts[i].engine] = true;
686
+ }
687
+ var el = document.getElementById('thoughtFilters');
688
+ var engines = ['all'].concat(Object.keys(knownEngines).sort());
689
+ var html = '';
690
+ for (var j = 0; j < engines.length; j++) {
691
+ html += '<button class="thought-filter ' + (engines[j] === thoughtFilter ? 'active' : '') + '" onclick="setThoughtFilter(\'' + engines[j] + '\')">' + engines[j] + '</button>';
692
+ }
693
+ el.innerHTML = html;
694
+ }
695
+
696
+ function setThoughtFilter(f) {
697
+ thoughtFilter = f;
698
+ renderThoughts();
699
+ }
700
+
701
+ function renderThoughts() {
702
+ renderThoughtFilters();
703
+ var filtered = thoughtFilter === 'all' ? state.thoughts : state.thoughts.filter(function(t) { return t.engine === thoughtFilter; });
704
+ var el = document.getElementById('thoughtList');
705
+ var html = '';
706
+ var items = filtered.slice(0, 200);
707
+ for (var i = 0; i < items.length; i++) {
708
+ var t = items[i];
709
+ var sig = t.significance === 'breakthrough' ? 'sig-breakthrough' : t.significance === 'notable' ? 'sig-notable' : '';
710
+ html += '<div class="thought-item ' + sig + '">' +
711
+ '<span class="time">' + formatTime(t.timestamp) + '</span>' +
712
+ '<span class="engine-tag" style="background:' + engineColor(t.engine) + '18;color:' + engineColor(t.engine) + '">' + escapeHtml(t.engine) + '</span>' +
713
+ '<span class="msg">' + escapeHtml(t.content) + '</span></div>';
714
+ }
715
+ el.innerHTML = html || '<div style="color:var(--text-muted);padding:20px;text-align:center">No thoughts yet</div>';
716
+ }
717
+
718
+ // ── CodeGen ──
719
+ function renderCodegen() {
720
+ var el = document.getElementById('codegenContent');
721
+ var cg = state.codegen;
722
+ if (!cg || !cg.available) {
723
+ el.innerHTML = '<div class="unavailable glass"><div class="icon">&lt;/&gt;</div><div>CodeGenerator not available.</div>' +
724
+ '<div style="margin-top:12px;font-size:11px;color:var(--text-dim)">Set the <code style="color:var(--cyan)">ANTHROPIC_API_KEY</code> environment variable before starting the Brain daemon to enable autonomous code generation and self-modification.</div>' +
725
+ '<div style="margin-top:12px;padding:10px;background:rgba(5,8,16,0.6);border-radius:6px;font-size:10px;color:var(--text-muted);text-align:left;max-width:500px;margin-left:auto;margin-right:auto">' +
726
+ '<div style="margin-bottom:4px;color:var(--text-dim)">Linux / macOS:</div>' +
727
+ '<code style="color:var(--green)">export ANTHROPIC_API_KEY=sk-ant-...</code>' +
728
+ '<div style="margin-top:8px;margin-bottom:4px;color:var(--text-dim)">Windows:</div>' +
729
+ '<code style="color:var(--green)">set ANTHROPIC_API_KEY=sk-ant-...</code>' +
730
+ '</div><div style="margin-top:10px;font-size:10px;color:var(--text-muted)">Then restart the Brain process.</div></div>';
731
+ return;
722
732
  }
723
733
 
724
- function timeSince(ms) {
725
- const s = Math.floor(ms / 1000);
726
- if (s < 60) return s + 's';
727
- const m = Math.floor(s / 60);
728
- if (m < 60) return m + 'm ' + (s % 60) + 's';
729
- const h = Math.floor(m / 60);
730
- return h + 'h ' + (m % 60) + 'm';
734
+ var summary = cg.summary || {};
735
+ var pending = cg.pending || [];
736
+ var gens = cg.generations || [];
737
+ var html = '';
738
+
739
+ // Stats
740
+ html += '<div class="stat-grid" style="margin-bottom:16px">' +
741
+ '<div class="glass stat-card"><div class="stat-val" style="color:var(--cyan)">' + (summary.totalGenerations || 0) + '</div><div class="stat-label">Total</div></div>' +
742
+ '<div class="glass stat-card"><div class="stat-val" style="color:var(--green)">' + (summary.approvalRate != null ? Math.round(summary.approvalRate * 100) + '%' : '--') + '</div><div class="stat-label">Approval</div></div>' +
743
+ '<div class="glass stat-card"><div class="stat-val" style="color:var(--gold)">' + (summary.totalTokens || 0) + '</div><div class="stat-label">Tokens</div></div></div>';
744
+
745
+ // Generate form
746
+ html += '<div class="glass" style="margin-bottom:16px"><div class="section-title">Generate Code</div>' +
747
+ '<div class="codegen-form"><textarea id="codegenTask" placeholder="Describe the task..."></textarea>' +
748
+ '<div style="display:flex;flex-direction:column;gap:6px">' +
749
+ '<select id="codegenLang"><option>typescript</option><option>javascript</option><option>python</option><option>rust</option><option>go</option></select>' +
750
+ '<button class="gen-btn" onclick="generateCode()">Generate</button></div></div></div>';
751
+
752
+ // Pending reviews
753
+ for (var i = 0; i < pending.length; i++) {
754
+ var g = pending[i];
755
+ html += '<div class="glass pending-card">' +
756
+ '<div style="font-size:11px;font-weight:600;margin-bottom:4px">#' + g.id + ' &mdash; ' + escapeHtml(g.task) + '</div>' +
757
+ '<div class="code-block">' + highlightSyntax(escapeHtml(g.generated_code || '')) + '</div>';
758
+ if (g.generated_explanation) {
759
+ html += '<div style="font-size:10px;color:var(--text-dim);margin:4px 0;padding:6px 10px;border-left:2px solid var(--purple);background:rgba(179,136,255,0.04)">' + escapeHtml(g.generated_explanation) + '</div>';
760
+ }
761
+ html += '<div class="review-actions"><input type="text" id="notes-' + g.id + '" placeholder="Notes (optional)...">' +
762
+ '<button class="btn-approve" onclick="reviewCode(' + g.id + ',\'approve\')">Approve</button>' +
763
+ '<button class="btn-reject" onclick="reviewCode(' + g.id + ',\'reject\')">Reject</button></div></div>';
731
764
  }
732
765
 
733
- function escapeHtml(text) {
734
- const div = document.createElement('div');
735
- div.textContent = text;
736
- return div.innerHTML;
766
+ // History table
767
+ html += '<div class="gen-history"><div class="section-title">Generation History</div><div class="glass" style="overflow-x:auto"><table class="gen-table"><thead><tr><th>ID</th><th>Task</th><th>Status</th><th>Tokens</th><th>Time</th><th>Created</th></tr></thead><tbody>';
768
+ for (var j = 0; j < Math.min(gens.length, 30); j++) {
769
+ var gen = gens[j];
770
+ html += '<tr><td>#' + gen.id + '</td><td>' + escapeHtml((gen.task || '').substring(0, 60)) + '</td>' +
771
+ '<td><span class="status-badge ' + gen.status + '">' + gen.status + '</span></td>' +
772
+ '<td>' + (gen.tokens_used || 0) + '</td><td>' + (gen.generation_time_ms ? gen.generation_time_ms + 'ms' : '--') + '</td>' +
773
+ '<td>' + formatTime(gen.created_at) + '</td></tr>';
774
+ }
775
+ html += '</tbody></table></div></div>';
776
+ el.innerHTML = html;
777
+ }
778
+
779
+ async function generateCode() {
780
+ var task = document.getElementById('codegenTask');
781
+ var lang = document.getElementById('codegenLang');
782
+ if (!task || !task.value.trim()) return;
783
+ try {
784
+ await fetch('/api/codegen/generate', {
785
+ method: 'POST',
786
+ headers: { 'Content-Type': 'application/json' },
787
+ body: JSON.stringify({ task: task.value.trim(), language: lang ? lang.value : 'typescript' }),
788
+ });
789
+ task.value = '';
790
+ } catch(e) { console.warn('generateCode failed', e); }
791
+ }
792
+
793
+ async function reviewCode(id, action) {
794
+ var notes = document.getElementById('notes-' + id);
795
+ try {
796
+ await fetch('/api/codegen/' + action + '/' + id, {
797
+ method: 'POST',
798
+ headers: { 'Content-Type': 'application/json' },
799
+ body: JSON.stringify({ notes: notes ? notes.value : '' }),
800
+ });
801
+ fetchCodegen();
802
+ } catch(e) { console.warn('reviewCode failed', e); }
803
+ }
804
+
805
+ // ── Self-Mod ──
806
+ function renderSelfmod() {
807
+ var el = document.getElementById('selfmodContent');
808
+ var sm = state.selfmod;
809
+ if (!sm || !sm.available) {
810
+ el.innerHTML = '<div class="unavailable glass"><div class="icon">&#9998;</div><div>SelfModificationEngine not available.</div></div>';
811
+ return;
737
812
  }
738
813
 
739
- // ── Render: Overview ───────────────────────
740
- function renderOverview() {
741
- const ov = state.overview;
742
- const st = state.stats || {};
743
-
744
- // Stats
745
- document.getElementById('statThoughts').textContent = st.totalThoughts || 0;
746
- document.getElementById('statDiscoveries').textContent = st.discoveries || 0;
747
-
748
- // Engine count from engines map
749
- const engines = state.engines || {};
750
- const engineNames = Object.keys(engines);
751
- const activeCount = engineNames.filter(n => {
752
- const e = engines[n];
753
- return e && (Date.now() - (e.lastActive || 0)) < 60000;
754
- }).length;
755
- document.getElementById('statEngines').textContent = activeCount;
756
- document.getElementById('statEnginesSub').textContent = 'of ' + engineNames.length + ' tracked';
757
-
758
- // Transfer score
759
- const tr = state.transfer;
760
- if (tr) {
761
- const score = tr.transferScore?.score ?? tr.score ?? 0;
762
- document.getElementById('statTransfer').textContent = typeof score === 'number' ? score.toFixed(1) : '0';
814
+ var status = sm.status || {};
815
+ var pending = sm.pending || [];
816
+ var history = sm.history || [];
817
+ var html = '';
818
+
819
+ // Stats
820
+ var byStatus = status.byStatus || {};
821
+ html += '<div class="stat-grid" style="margin-bottom:16px">' +
822
+ '<div class="glass stat-card"><div class="stat-val" style="color:var(--cyan)">' + (status.totalModifications || 0) + '</div><div class="stat-label">Total</div></div>' +
823
+ '<div class="glass stat-card"><div class="stat-val" style="color:var(--green)">' + (byStatus.applied || 0) + '</div><div class="stat-label">Applied</div></div>' +
824
+ '<div class="glass stat-card"><div class="stat-val" style="color:var(--gold)">' + (byStatus.ready || 0) + '</div><div class="stat-label">Ready</div></div>' +
825
+ '<div class="glass stat-card"><div class="stat-val" style="color:var(--red)">' + (byStatus.rejected || 0) + '</div><div class="stat-label">Rejected</div></div></div>';
826
+
827
+ // Pending modifications
828
+ for (var i = 0; i < pending.length; i++) {
829
+ var m = pending[i];
830
+ html += '<div class="glass pending-card" style="margin-bottom:12px">' +
831
+ '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px">' +
832
+ '<span style="font-size:11px;font-weight:600">#' + m.id + ' &mdash; ' + escapeHtml(m.title) + '</span>' +
833
+ '<span class="status-badge ' + m.status + '">' + m.status + '</span></div>' +
834
+ '<div style="font-size:10px;color:var(--text-dim);margin-bottom:8px">' + escapeHtml(m.problem_description) + '</div>' +
835
+ '<div style="font-size:10px;color:var(--text-muted);margin-bottom:4px">Files: ' + (m.target_files || []).map(function(f) { return escapeHtml(f); }).join(', ') + '</div>';
836
+
837
+ // Diff viewer
838
+ if (m.generated_diff && m.generated_diff.length > 0) {
839
+ for (var d = 0; d < m.generated_diff.length; d++) {
840
+ var diff = m.generated_diff[d];
841
+ html += '<div style="font-size:9px;color:var(--text-muted);margin-top:6px">' + escapeHtml(diff.filePath) + '</div>' +
842
+ '<div class="diff-viewer">' +
843
+ '<div class="diff-old"><div class="diff-label">Old</div>' + renderDiffLines(diff.oldContent, 'del') + '</div>' +
844
+ '<div class="diff-new"><div class="diff-label">New</div>' + renderDiffLines(diff.newContent, 'add') + '</div></div>';
845
+ }
763
846
  }
764
847
 
765
- // Health badge
766
- renderHealthBadge(ov);
767
-
768
- // Brain cards
769
- renderBrainCards(ov);
848
+ if (m.test_output) {
849
+ html += '<div style="font-size:10px;margin-top:6px;padding:8px;background:rgba(5,8,16,0.6);border-radius:4px;color:var(--text-dim);max-height:100px;overflow-y:auto"><pre style="margin:0;white-space:pre-wrap">' + escapeHtml(m.test_output) + '</pre></div>';
850
+ }
770
851
 
771
- // Recent notifications (top 30)
772
- renderNotifFeed('overviewNotifs', state.thoughts.slice(0, 30));
852
+ html += '<div class="review-actions">' +
853
+ '<button class="btn-test" onclick="selfmodAction(' + m.id + ',\'test\')">Test</button>' +
854
+ '<button class="btn-approve" onclick="selfmodAction(' + m.id + ',\'approve\')">Approve</button>' +
855
+ '<button class="btn-reject" onclick="selfmodAction(' + m.id + ',\'reject\')">Reject</button></div></div>';
773
856
  }
774
857
 
775
- function renderHealthBadge(ov) {
776
- const badge = document.getElementById('healthBadge');
777
- const scoreEl = document.getElementById('healthScore');
778
- if (ov && typeof ov.healthScore === 'number') {
779
- const s = ov.healthScore;
780
- scoreEl.textContent = s + '/100';
781
- badge.className = 'health-badge ' + (s >= 70 ? 'healthy' : s >= 40 ? 'degraded' : 'critical');
782
- } else if (connected) {
783
- scoreEl.textContent = 'Connected';
784
- badge.className = 'health-badge healthy';
785
- }
858
+ // History table
859
+ html += '<div style="margin-top:16px"><div class="section-title">Modification History</div><div class="glass" style="overflow-x:auto"><table class="gen-table"><thead><tr><th>ID</th><th>Title</th><th>Status</th><th>Test</th><th>Files</th><th>Created</th></tr></thead><tbody>';
860
+ for (var j = 0; j < history.length; j++) {
861
+ var h = history[j];
862
+ var testCls = h.test_result === 'passed' ? 'approved' : h.test_result === 'failed' ? 'failed' : 'proposed';
863
+ html += '<tr><td>#' + h.id + '</td><td>' + escapeHtml((h.title || '').substring(0, 50)) + '</td>' +
864
+ '<td><span class="status-badge ' + h.status + '">' + h.status + '</span></td>' +
865
+ '<td><span class="status-badge ' + testCls + '">' + (h.test_result || 'pending') + '</span></td>' +
866
+ '<td>' + (h.target_files || []).length + '</td><td>' + formatTime(h.created_at) + '</td></tr>';
786
867
  }
787
-
788
- function renderBrainCards(ov) {
789
- if (!ov) return;
790
- const brains = ov.brains || {};
791
-
792
- // Brain
793
- const brain = brains.brain || {};
794
- setDot('brainDot', brain.status);
795
- setText('brainCycle', brain.cycle);
796
- setText('brainPrinciples', brain.principles);
797
- setText('brainHypotheses', brain.hypotheses);
798
- setText('brainExperiments', brain.experiments);
799
- setText('brainFocus', brain.focus || '--');
800
-
801
- // Trading
802
- const trading = brains['trading-brain'] || brains.trading || {};
803
- setDot('tradingDot', trading.status);
804
- setText('tradingCycle', trading.cycle);
805
- setText('tradingPrinciples', trading.principles);
806
- setText('tradingHypotheses', trading.hypotheses);
807
- setText('tradingExperiments', trading.experiments);
808
- setText('tradingFocus', trading.focus || '--');
809
-
810
- // Marketing
811
- const marketing = brains['marketing-brain'] || brains.marketing || {};
812
- setDot('marketingDot', marketing.status);
813
- setText('marketingCycle', marketing.cycle);
814
- setText('marketingPrinciples', marketing.principles);
815
- setText('marketingHypotheses', marketing.hypotheses);
816
- setText('marketingExperiments', marketing.experiments);
817
- setText('marketingFocus', marketing.focus || '--');
868
+ html += '</tbody></table></div></div>';
869
+ el.innerHTML = html;
870
+ }
871
+
872
+ function renderDiffLines(content, type) {
873
+ if (!content) return '<span style="color:var(--text-muted)">Empty</span>';
874
+ return escapeHtml(content).split('\n').map(function(line) {
875
+ return '<div class="diff-line-' + type + '">' + (line || '&nbsp;') + '</div>';
876
+ }).join('');
877
+ }
878
+
879
+ async function selfmodAction(id, action) {
880
+ try {
881
+ await fetch('/api/selfmod/' + id + '/' + action, { method: 'POST' });
882
+ fetchSelfmod();
883
+ } catch(e) { console.warn('selfmodAction failed', e); }
884
+ }
885
+
886
+ // ── Engines ──
887
+ function renderEngines() {
888
+ var el = document.getElementById('engineGrid');
889
+ var engines = state.engines || {};
890
+ var entries = [];
891
+ for (var name in engines) entries.push([name, engines[name]]);
892
+ entries.sort(function(a, b) { return (b[1].count || 0) - (a[1].count || 0); });
893
+ if (entries.length === 0) {
894
+ el.innerHTML = '<div style="color:var(--text-muted);padding:20px;text-align:center;grid-column:1/-1">No engine data yet</div>';
895
+ return;
818
896
  }
819
-
820
- function setDot(id, status) {
821
- const el = document.getElementById(id);
822
- if (!el) return;
823
- el.className = 'brain-status-dot ' + (status === 'running' ? 'running' : status === 'stopped' ? 'stopped' : 'unknown');
897
+ var maxCount = 1;
898
+ for (var i = 0; i < entries.length; i++) maxCount = Math.max(maxCount, entries[i][1].count || 0);
899
+ var html = '';
900
+ for (var j = 0; j < entries.length; j++) {
901
+ var n = entries[j][0], data = entries[j][1];
902
+ var col = engineColor(n);
903
+ var pct = Math.round(((data.count || 0) / maxCount) * 100);
904
+ html += '<div class="glass engine-card">' +
905
+ '<div class="e-name"><div class="e-dot" style="background:' + col + '"></div>' + escapeHtml(n) + '</div>' +
906
+ '<div class="e-stat"><span>Thoughts</span><span>' + (data.count || 0) + '</span></div>' +
907
+ '<div class="e-stat"><span>Last</span><span>' + (data.lastTimestamp ? timeSince(data.lastTimestamp) + ' ago' : '--') + '</span></div>' +
908
+ '<div class="e-bar"><div class="e-bar-fill" style="width:' + pct + '%;background:' + col + '"></div></div></div>';
824
909
  }
825
-
826
- function setText(id, value) {
827
- const el = document.getElementById(id);
828
- if (el) el.textContent = value != null ? value : '--';
910
+ el.innerHTML = html;
911
+ }
912
+
913
+ // ── Intelligence ──
914
+ function renderIntelligence() {
915
+ var att = state.attention;
916
+ var topEl = document.getElementById('attentionTopics');
917
+ if (att && att.topTopics && att.topTopics.length) {
918
+ var max = 1;
919
+ for (var i = 0; i < att.topTopics.length; i++) max = Math.max(max, att.topTopics[i].score || 0);
920
+ var html = '';
921
+ var items = att.topTopics.slice(0, 15);
922
+ for (var j = 0; j < items.length; j++) {
923
+ var t = items[j];
924
+ var pct = Math.round(((t.score || 0) / max) * 100);
925
+ html += '<div class="topic-item"><span class="topic-name">' + escapeHtml(t.topic) + '</span>' +
926
+ '<div class="topic-bar-bg"><div class="topic-bar-fill" style="width:' + pct + '%"></div></div>' +
927
+ '<span class="topic-score">' + (t.score || 0).toFixed(1) + '</span></div>';
928
+ }
929
+ topEl.innerHTML = html;
930
+ } else {
931
+ topEl.innerHTML = '<div style="color:var(--text-muted);text-align:center;padding:16px">No attention data</div>';
829
932
  }
830
933
 
831
- // ── Render: Notifications ──────────────────
832
- function renderNotifFeed(containerId, items) {
833
- const container = document.getElementById(containerId);
834
- if (!container || !items) return;
835
-
836
- container.innerHTML = items.map(t => {
837
- const sig = t.significance || 'routine';
838
- return '<div class="notif-item ' + sig + '">' +
839
- '<span class="notif-time">' + formatTime(t.timestamp) + '</span>' +
840
- '<span class="notif-engine">' + escapeHtml(t.engine || '?') + '</span>' +
841
- '<span class="notif-content">' + escapeHtml(t.content || t.message || '') + '</span>' +
842
- '</div>';
843
- }).join('');
934
+ var tr = state.transfer;
935
+ var ruleEl = document.getElementById('transferRules');
936
+ if (tr && tr.rules && tr.rules.length) {
937
+ var rHtml = '';
938
+ for (var k = 0; k < tr.rules.length; k++) {
939
+ var r = tr.rules[k];
940
+ rHtml += '<div class="rule-item"><span class="r-name">' + escapeHtml(r.name || r.event_pattern || 'Rule') + '</span>' +
941
+ '<span class="r-status" style="color:' + (r.active ? 'var(--green)' : 'var(--text-muted)') + '">' + (r.active ? 'active' : 'inactive') + '</span></div>';
942
+ }
943
+ ruleEl.innerHTML = rHtml;
944
+ } else {
945
+ ruleEl.innerHTML = '<div style="color:var(--text-muted);text-align:center;padding:16px">No transfer rules</div>';
844
946
  }
845
947
 
846
- function renderAllNotifications() {
847
- renderNotifFeed('allNotifs', state.thoughts.slice(0, 200));
948
+ var anaEl = document.getElementById('analogies');
949
+ if (tr && tr.analogies && tr.analogies.length) {
950
+ var aHtml = '<div style="display:flex;flex-direction:column;gap:4px">';
951
+ var anas = tr.analogies.slice(0, 15);
952
+ for (var l = 0; l < anas.length; l++) {
953
+ var a = anas[l];
954
+ aHtml += '<div style="display:flex;justify-content:space-between;font-size:10px;padding:4px 0;border-bottom:1px solid var(--border)">' +
955
+ '<span>' + escapeHtml(a.source_concept) + ' &harr; ' + escapeHtml(a.target_concept) + '</span>' +
956
+ '<span style="color:var(--gold)">' + (a.similarity || 0).toFixed(2) + '</span></div>';
957
+ }
958
+ aHtml += '</div>';
959
+ anaEl.innerHTML = aHtml;
960
+ } else {
961
+ anaEl.innerHTML = '<div style="color:var(--text-muted);text-align:center;padding:16px">No analogies found</div>';
848
962
  }
963
+ }
964
+
965
+ // ═══ NEURAL GRAPH ═══
966
+ var nodeTypeColors = {
967
+ project:'#00e5ff', error:'#ff3d3d', solution:'#00ff88',
968
+ code_module:'#448aff', insight:'#ffd700', memory:'#b388ff'
969
+ };
970
+
971
+ var neuralNodes = [];
972
+ var neuralEdges = [];
973
+ var neuralZoomLevel = 1;
974
+ var neuralPanX = 0, neuralPanY = 0;
975
+ var neuralDragging = null;
976
+ var neuralPanning = false;
977
+ var neuralPanStart = { x: 0, y: 0 };
978
+ var neuralHover = null;
979
+ var neuralAnimId = null;
980
+
981
+ function initNeuralGraph(canvasId) {
982
+ var canvas = document.getElementById(canvasId);
983
+ if (!canvas) return;
984
+
985
+ canvas.addEventListener('mousedown', function(e) {
986
+ var pos = getCanvasPos(canvas, e);
987
+ var node = findNodeAt(pos.x, pos.y);
988
+ if (node) {
989
+ neuralDragging = node;
990
+ node.pinned = true;
991
+ } else {
992
+ neuralPanning = true;
993
+ neuralPanStart = { x: e.clientX - neuralPanX, y: e.clientY - neuralPanY };
994
+ }
995
+ });
849
996
 
850
- // ── Render: Attention ──────────────────────
851
- function renderAttention() {
852
- const att = state.attention;
853
- if (!att) {
854
- document.getElementById('topTopics').innerHTML = '<span style="color:var(--text-muted)">No attention data available</span>';
855
- return;
997
+ canvas.addEventListener('mousemove', function(e) {
998
+ var pos = getCanvasPos(canvas, e);
999
+ if (neuralDragging) {
1000
+ neuralDragging.x = pos.x;
1001
+ neuralDragging.y = pos.y;
1002
+ } else if (neuralPanning) {
1003
+ neuralPanX = e.clientX - neuralPanStart.x;
1004
+ neuralPanY = e.clientY - neuralPanStart.y;
1005
+ } else {
1006
+ neuralHover = findNodeAt(pos.x, pos.y);
1007
+ canvas.style.cursor = neuralHover ? 'pointer' : 'grab';
856
1008
  }
1009
+ });
857
1010
 
858
- // Current context
859
- const ctx = att.currentContext || att.context || 'unknown';
860
- document.getElementById('currentContext').innerHTML =
861
- '<span class="context-badge ' + ctx + '">' + escapeHtml(ctx) + '</span>';
1011
+ canvas.addEventListener('mouseup', function() {
1012
+ if (neuralDragging) neuralDragging.pinned = false;
1013
+ neuralDragging = null;
1014
+ neuralPanning = false;
1015
+ });
862
1016
 
863
- // Top topics
864
- const topics = att.topTopics || att.scores || [];
865
- if (topics.length === 0) {
866
- document.getElementById('topTopics').innerHTML = '<span style="color:var(--text-muted)">No topics tracked</span>';
1017
+ canvas.addEventListener('mouseleave', function() {
1018
+ neuralDragging = null;
1019
+ neuralPanning = false;
1020
+ neuralHover = null;
1021
+ });
1022
+
1023
+ canvas.addEventListener('wheel', function(e) {
1024
+ e.preventDefault();
1025
+ var factor = e.deltaY < 0 ? 1.1 : 0.9;
1026
+ neuralZoomLevel = Math.max(0.2, Math.min(5, neuralZoomLevel * factor));
1027
+ }, { passive: false });
1028
+ }
1029
+
1030
+ function getCanvasPos(canvas, e) {
1031
+ var rect = canvas.getBoundingClientRect();
1032
+ return {
1033
+ x: (e.clientX - rect.left - neuralPanX) / neuralZoomLevel,
1034
+ y: (e.clientY - rect.top - neuralPanY) / neuralZoomLevel,
1035
+ };
1036
+ }
1037
+
1038
+ function findNodeAt(x, y) {
1039
+ for (var i = 0; i < neuralNodes.length; i++) {
1040
+ var n = neuralNodes[i];
1041
+ var dx = n.x - x, dy = n.y - y;
1042
+ if (dx * dx + dy * dy < (n.radius || 5) * (n.radius || 5) * 4) return n;
1043
+ }
1044
+ return null;
1045
+ }
1046
+
1047
+ function neuralZoom(factor) {
1048
+ neuralZoomLevel = Math.max(0.2, Math.min(5, neuralZoomLevel * factor));
1049
+ }
1050
+
1051
+ function neuralReset() {
1052
+ neuralZoomLevel = 1;
1053
+ neuralPanX = 0;
1054
+ neuralPanY = 0;
1055
+ }
1056
+
1057
+ function updateNeuralNodes(network) {
1058
+ var existingMap = {};
1059
+ for (var i = 0; i < neuralNodes.length; i++) existingMap[neuralNodes[i].id] = neuralNodes[i];
1060
+ var newNodes = [];
1061
+ var nodes = network.nodes || [];
1062
+
1063
+ for (var j = 0; j < nodes.length; j++) {
1064
+ var n = nodes[j];
1065
+ var existing = existingMap[n.id];
1066
+ if (existing) {
1067
+ existing.label = n.label;
1068
+ existing.type = n.type;
1069
+ existing.importance = n.importance != null ? n.importance : 0.5;
1070
+ newNodes.push(existing);
867
1071
  } else {
868
- const maxScore = Math.max(...topics.map(t => t.score || 0), 1);
869
- document.getElementById('topTopics').innerHTML = topics.slice(0, 15).map(t => {
870
- const pct = Math.round(((t.score || 0) / maxScore) * 100);
871
- return '<div class="topic-bar">' +
872
- '<span class="topic-name">' + escapeHtml(t.topic || t.name || '?') + '</span>' +
873
- '<div style="flex:1"><div class="topic-bar-fill" style="width:' + pct + '%"></div></div>' +
874
- '<span class="topic-score">' + (t.score || 0).toFixed(2) + '</span>' +
875
- '</div>';
876
- }).join('');
1072
+ var angle = (j / Math.max(1, nodes.length)) * Math.PI * 2;
1073
+ var r = 150 + Math.random() * 100;
1074
+ newNodes.push({
1075
+ id: n.id, label: n.label, type: n.type,
1076
+ importance: n.importance != null ? n.importance : 0.5,
1077
+ x: 300 + Math.cos(angle) * r + (Math.random() - 0.5) * 50,
1078
+ y: 250 + Math.sin(angle) * r + (Math.random() - 0.5) * 50,
1079
+ vx: 0, vy: 0,
1080
+ radius: 3 + (n.importance != null ? n.importance : 0.5) * 5,
1081
+ pinned: false,
1082
+ });
877
1083
  }
878
-
879
- // Urgent topics
880
- const urgent = att.urgentTopics || [];
881
- document.getElementById('urgentTopics').innerHTML = urgent.length === 0
882
- ? '<span style="color:var(--text-muted)">No urgent topics</span>'
883
- : urgent.map(t =>
884
- '<div class="topic-bar">' +
885
- '<span class="topic-name" style="color:var(--gold)">' + escapeHtml(t.topic || t.name || '?') + '</span>' +
886
- '<span class="topic-score" style="color:var(--gold)">' + (t.urgency || t.score || 0).toFixed(2) + '</span>' +
887
- '</div>'
888
- ).join('');
889
-
890
- // Context switches
891
- const switches = att.recentSwitches || [];
892
- document.getElementById('contextSwitches').innerHTML = switches.length === 0
893
- ? '<span style="color:var(--text-muted)">No context switches</span>'
894
- : switches.slice(0, 10).map(s =>
895
- '<div style="padding:3px 0">' + formatTime(s.timestamp) + ' &mdash; ' +
896
- '<span class="context-badge ' + (s.from || '') + '">' + escapeHtml(s.from || '?') + '</span>' +
897
- ' &rarr; <span class="context-badge ' + (s.to || '') + '">' + escapeHtml(s.to || '?') + '</span>' +
898
- '</div>'
899
- ).join('');
900
-
901
- // Focus timeline
902
- const timeline = att.focusTimeline || att.timeline || [];
903
- document.getElementById('focusTimeline').innerHTML = timeline.length === 0
904
- ? '<span style="color:var(--text-muted)">No focus history</span>'
905
- : timeline.slice(0, 20).map(f =>
906
- '<div style="padding:2px 0">' + formatTime(f.timestamp) + ' &mdash; ' +
907
- '<strong>' + escapeHtml(f.topic || '?') + '</strong> (score: ' + (f.score || 0).toFixed(2) + ')' +
908
- '</div>'
909
- ).join('');
910
1084
  }
911
1085
 
912
- // ── Render: Transfer ───────────────────────
913
- function renderTransfer() {
914
- const tr = state.transfer;
915
- if (!tr) return;
916
-
917
- // Stats
918
- setText('transferAnalogies', tr.totalAnalogies || 0);
919
- setText('transferTotal', tr.totalTransfers || 0);
920
- setText('transferRules', tr.activeRules || 0);
921
- const score = tr.transferScore?.score ?? tr.score ?? 0;
922
- document.getElementById('transferScoreVal').textContent = typeof score === 'number' ? score.toFixed(1) : '0';
923
-
924
- // Analogies
925
- const analogies = tr.analogies || [];
926
- document.getElementById('analogyList').innerHTML = analogies.length === 0
927
- ? '<span style="color:var(--text-muted)">No analogies found yet</span>'
928
- : analogies.slice(0, 15).map(a =>
929
- '<div class="analogy-item">' +
930
- '<div class="analogy-narrative">' + escapeHtml(a.narrative || a.description || '?') + '</div>' +
931
- '<div class="analogy-meta">' +
932
- '<span>Similarity: ' + ((a.similarity || 0) * 100).toFixed(0) + '%</span>' +
933
- '<span>' + escapeHtml(a.source_brain || '?') + ' &harr; ' + escapeHtml(a.target_brain || '?') + '</span>' +
934
- '</div>' +
935
- '</div>'
936
- ).join('');
937
-
938
- // Rules
939
- const rules = tr.rules || [];
940
- document.getElementById('ruleList').innerHTML = rules.length === 0
941
- ? '<span style="color:var(--text-muted)">No rules configured</span>'
942
- : rules.map(r =>
943
- '<div class="rule-item">' +
944
- '<span class="' + (r.enabled ? 'rule-enabled' : 'rule-disabled') + '">' + (r.enabled ? '\u25CF' : '\u25CB') + '</span>' +
945
- '<span style="color:var(--text-primary)">' + escapeHtml(r.name || '?') + '</span>' +
946
- '<span class="rule-arrow">' + escapeHtml(r.source_brain || '?') + ' &rarr; ' + escapeHtml(r.target_brain || '?') + '</span>' +
947
- '<span style="color:var(--text-muted);margin-left:auto">fired: ' + (r.fire_count || 0) + '</span>' +
948
- '</div>'
949
- ).join('');
950
-
951
- // Transfer history
952
- const history = tr.history || [];
953
- const tbody = document.querySelector('#transferHistory tbody');
954
- if (tbody) {
955
- tbody.innerHTML = history.length === 0
956
- ? '<tr><td colspan="5" style="color:var(--text-muted)">No transfers yet</td></tr>'
957
- : history.slice(0, 30).map(h =>
958
- '<tr>' +
959
- '<td>' + escapeHtml(h.source_brain || '?') + '</td>' +
960
- '<td style="max-width:300px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">' + escapeHtml(h.knowledge || h.statement || '?') + '</td>' +
961
- '<td><span class="status-badge ' + (h.status || '') + '">' + escapeHtml(h.status || '?') + '</span></td>' +
962
- '<td>' + ((h.confidence || 0) * 100).toFixed(0) + '%</td>' +
963
- '<td>' + formatTime(h.created_at || h.timestamp) + '</td>' +
964
- '</tr>'
965
- ).join('');
1086
+ neuralNodes = newNodes;
1087
+ neuralEdges = (network.edges || []).map(function(e) {
1088
+ return {
1089
+ source: e.source || e.source_id,
1090
+ target: e.target || e.target_id,
1091
+ weight: e.weight != null ? e.weight : 0.5,
1092
+ };
1093
+ });
1094
+ }
1095
+
1096
+ function simulatePhysics() {
1097
+ var k = 0.008;
1098
+ var repulsion = 800;
1099
+ var damping = 0.92;
1100
+ var gravity = 0.01;
1101
+ var cx = 300, cy = 250;
1102
+
1103
+ for (var i = 0; i < neuralNodes.length; i++) {
1104
+ for (var j = i + 1; j < neuralNodes.length; j++) {
1105
+ var a = neuralNodes[i], b = neuralNodes[j];
1106
+ var dx = a.x - b.x, dy = a.y - b.y;
1107
+ var dist = Math.sqrt(dx * dx + dy * dy) || 1;
1108
+ var f = repulsion / (dist * dist);
1109
+ dx = (dx / dist) * f;
1110
+ dy = (dy / dist) * f;
1111
+ if (!a.pinned) { a.vx += dx; a.vy += dy; }
1112
+ if (!b.pinned) { b.vx -= dx; b.vy -= dy; }
966
1113
  }
967
1114
  }
968
1115
 
969
- // ── Render: Engines ────────────────────────
970
- function renderEngines() {
971
- const engines = state.engines || {};
972
- const names = Object.keys(engines).sort();
973
-
974
- document.getElementById('engineGrid').innerHTML = names.length === 0
975
- ? '<span style="color:var(--text-muted)">No engines tracked</span>'
976
- : names.map(name => {
977
- const e = engines[name];
978
- const active = e && (Date.now() - (e.lastActive || 0)) < 60000;
979
- return '<div class="engine-chip">' +
980
- '<span class="engine-dot ' + (active ? 'active' : 'idle') + '"></span>' +
981
- '<span>' + escapeHtml(name) + '</span>' +
982
- '<span style="margin-left:auto;color:var(--text-muted)">' + (e?.count || 0) + '</span>' +
983
- '</div>';
984
- }).join('');
985
-
986
- // Engine thought counts as bars
987
- const sorted = names.map(n => ({ name: n, count: engines[n]?.count || 0 })).sort((a, b) => b.count - a.count);
988
- const maxCount = Math.max(...sorted.map(s => s.count), 1);
989
- document.getElementById('engineCounts').innerHTML = sorted.map(s => {
990
- const pct = Math.round((s.count / maxCount) * 100);
991
- return '<div class="topic-bar">' +
992
- '<span class="topic-name">' + escapeHtml(s.name) + '</span>' +
993
- '<div style="flex:1"><div class="topic-bar-fill" style="width:' + pct + '%;background:linear-gradient(90deg,var(--green),var(--cyan))"></div></div>' +
994
- '<span class="topic-score">' + s.count + '</span>' +
995
- '</div>';
996
- }).join('');
1116
+ var nodeMap = {};
1117
+ for (var m = 0; m < neuralNodes.length; m++) nodeMap[neuralNodes[m].id] = neuralNodes[m];
1118
+ for (var e = 0; e < neuralEdges.length; e++) {
1119
+ var edge = neuralEdges[e];
1120
+ var ea = nodeMap[edge.source], eb = nodeMap[edge.target];
1121
+ if (!ea || !eb) continue;
1122
+ var edx = eb.x - ea.x, edy = eb.y - ea.y;
1123
+ var edist = Math.sqrt(edx * edx + edy * edy) || 1;
1124
+ var ideal = 60;
1125
+ var ef = k * (edist - ideal);
1126
+ var efx = (edx / edist) * ef, efy = (edy / edist) * ef;
1127
+ if (!ea.pinned) { ea.vx += efx; ea.vy += efy; }
1128
+ if (!eb.pinned) { eb.vx -= efx; eb.vy -= efy; }
997
1129
  }
998
1130
 
999
- // ── Render All ─────────────────────────────
1000
- function renderAll() {
1001
- renderOverview();
1002
- renderAllNotifications();
1003
- renderAttention();
1004
- renderTransfer();
1005
- renderEngines();
1131
+ for (var n = 0; n < neuralNodes.length; n++) {
1132
+ var node = neuralNodes[n];
1133
+ if (node.pinned) continue;
1134
+ node.vx += (cx - node.x) * gravity;
1135
+ node.vy += (cy - node.y) * gravity;
1136
+ node.vx *= damping;
1137
+ node.vy *= damping;
1138
+ node.x += node.vx;
1139
+ node.y += node.vy;
1140
+ }
1141
+ }
1142
+
1143
+ function drawNeuralCanvas(canvasId, isOverview) {
1144
+ var canvas = document.getElementById(canvasId);
1145
+ if (!canvas) return;
1146
+ var ctx = canvas.getContext('2d');
1147
+ var w = canvas.width, h = canvas.height;
1148
+
1149
+ ctx.clearRect(0, 0, w, h);
1150
+ ctx.save();
1151
+ ctx.translate(neuralPanX, neuralPanY);
1152
+ ctx.scale(neuralZoomLevel, neuralZoomLevel);
1153
+
1154
+ var nodeMap = {};
1155
+ for (var m = 0; m < neuralNodes.length; m++) nodeMap[neuralNodes[m].id] = neuralNodes[m];
1156
+
1157
+ // Edges
1158
+ for (var e = 0; e < neuralEdges.length; e++) {
1159
+ var edge = neuralEdges[e];
1160
+ var a = nodeMap[edge.source], b = nodeMap[edge.target];
1161
+ if (!a || !b) continue;
1162
+ ctx.beginPath();
1163
+ ctx.moveTo(a.x, a.y);
1164
+ ctx.lineTo(b.x, b.y);
1165
+ ctx.strokeStyle = 'rgba(0,229,255,' + Math.min(0.3, edge.weight * 0.4) + ')';
1166
+ ctx.lineWidth = 0.5 + edge.weight;
1167
+ ctx.stroke();
1006
1168
  }
1007
1169
 
1008
- // ── SSE Connection ─────────────────────────
1009
- function connectSSE() {
1010
- const es = new EventSource('/events');
1011
-
1012
- es.addEventListener('connected', () => {
1013
- connected = true;
1014
- document.getElementById('connDot').classList.add('connected');
1015
- document.getElementById('connLabel').textContent = 'Connected';
1016
- });
1017
-
1018
- es.addEventListener('thought', (e) => {
1019
- try {
1020
- const thought = JSON.parse(e.data);
1021
- state.thoughts.unshift(thought);
1022
- if (state.thoughts.length > MAX_THOUGHTS) state.thoughts.length = MAX_THOUGHTS;
1023
-
1024
- // Live update notification feeds
1025
- renderNotifFeed('overviewNotifs', state.thoughts.slice(0, 30));
1026
- renderNotifFeed('allNotifs', state.thoughts.slice(0, 200));
1027
- } catch { /* ignore parse errors */ }
1028
- });
1029
-
1030
- es.addEventListener('status', (e) => {
1031
- try {
1032
- const data = JSON.parse(e.data);
1033
- if (data.overview) state.overview = data.overview;
1034
- if (data.engines) state.engines = data.engines;
1035
- if (data.stats) state.stats = data.stats;
1036
- renderOverview();
1037
- renderEngines();
1038
- } catch { /* ignore */ }
1039
- });
1170
+ // Nodes
1171
+ for (var i = 0; i < neuralNodes.length; i++) {
1172
+ var n = neuralNodes[i];
1173
+ var col = nodeTypeColors[n.type] || '#6e7a8a';
1174
+ var r = n.radius || 4;
1175
+ var isHovered = neuralHover && neuralHover.id === n.id;
1176
+
1177
+ if (isHovered || n.importance > 0.7) {
1178
+ ctx.beginPath();
1179
+ ctx.arc(n.x, n.y, r * 2.5, 0, Math.PI * 2);
1180
+ ctx.fillStyle = col + '14';
1181
+ ctx.fill();
1182
+ }
1040
1183
 
1041
- es.addEventListener('heartbeat', () => {
1042
- // Keep-alive
1043
- });
1184
+ ctx.beginPath();
1185
+ ctx.arc(n.x, n.y, r, 0, Math.PI * 2);
1186
+ ctx.fillStyle = col;
1187
+ ctx.fill();
1044
1188
 
1045
- es.onerror = () => {
1046
- connected = false;
1047
- document.getElementById('connDot').classList.remove('connected');
1048
- document.getElementById('connLabel').textContent = 'Reconnecting...';
1049
- // EventSource auto-reconnects
1050
- };
1051
- }
1189
+ if (isHovered) {
1190
+ ctx.strokeStyle = '#fff';
1191
+ ctx.lineWidth = 1.5;
1192
+ ctx.stroke();
1193
+ }
1052
1194
 
1053
- // ── Initial Load ───────────────────────────
1054
- async function loadInitialState() {
1055
- try {
1056
- const res = await fetch('/api/state');
1057
- const data = await res.json();
1058
- state = {
1059
- overview: data.overview || null,
1060
- transfer: data.transfer || null,
1061
- attention: data.attention || null,
1062
- thoughts: (data.thoughts || []).reverse(),
1063
- engines: data.engines || {},
1064
- stats: data.stats || {},
1065
- notifications: data.notifications || [],
1066
- };
1067
- renderAll();
1068
- } catch (e) {
1069
- console.error('Failed to load initial state', e);
1195
+ if (isHovered || (!isOverview && neuralZoomLevel > 0.8 && n.importance > 0.6)) {
1196
+ ctx.fillStyle = 'rgba(224,230,240,0.8)';
1197
+ ctx.font = '9px ' + getComputedStyle(document.body).fontFamily;
1198
+ ctx.fillText((n.label || '').substring(0, 30), n.x + r + 3, n.y + 3);
1070
1199
  }
1071
1200
  }
1072
1201
 
1073
- // ── Uptime Timer ───────────────────────────
1074
- setInterval(() => {
1075
- document.getElementById('uptimeLabel').textContent = 'Uptime: ' + timeSince(Date.now() - startTime);
1076
- }, 1000);
1077
-
1078
- // ── Boot ───────────────────────────────────
1079
- loadInitialState();
1080
- connectSSE();
1202
+ ctx.restore();
1203
+ }
1204
+
1205
+ function resizeNeuralCanvas() {
1206
+ var canvas = document.getElementById('neuralCanvas');
1207
+ if (!canvas) return;
1208
+ var wrap = canvas.parentElement;
1209
+ canvas.width = wrap.clientWidth;
1210
+ canvas.height = wrap.clientHeight;
1211
+ }
1212
+
1213
+ function renderNetwork() {
1214
+ updateNeuralNodes(state.network);
1215
+ var txt = neuralNodes.length + ' nodes, ' + neuralEdges.length + ' edges';
1216
+ var info1 = document.getElementById('overviewInfo');
1217
+ var info2 = document.getElementById('neuralInfo');
1218
+ if (info1) info1.textContent = txt;
1219
+ if (info2) info2.textContent = txt;
1220
+ }
1221
+
1222
+ function neuralLoop() {
1223
+ simulatePhysics();
1224
+
1225
+ var oc = document.getElementById('overviewCanvas');
1226
+ if (oc) {
1227
+ if (oc.width !== oc.parentElement.clientWidth) oc.width = oc.parentElement.clientWidth;
1228
+ drawNeuralCanvas('overviewCanvas', true);
1229
+ }
1081
1230
 
1082
- })();
1231
+ var nc = document.getElementById('neuralCanvas');
1232
+ if (nc && nc.offsetParent !== null) drawNeuralCanvas('neuralCanvas', false);
1233
+
1234
+ neuralAnimId = requestAnimationFrame(neuralLoop);
1235
+ }
1236
+
1237
+ // ═══ SYNTAX HIGHLIGHTING ═══
1238
+ function highlightSyntax(code) {
1239
+ return code
1240
+ .replace(/(\/\/.*)/g, '<span class="syn-comment">$1</span>')
1241
+ .replace(/(\/\*[\s\S]*?\*\/)/g, '<span class="syn-comment">$1</span>')
1242
+ .replace(/('(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*`)/g, '<span class="syn-string">$1</span>')
1243
+ .replace(/\b(\d+\.?\d*)\b/g, '<span class="syn-number">$1</span>')
1244
+ .replace(/\b(const|let|var|function|return|if|else|for|while|class|import|export|from|async|await|new|this|type|interface|extends|implements|try|catch|throw|switch|case|break|default)\b/g, '<span class="syn-keyword">$1</span>')
1245
+ .replace(/\b(string|number|boolean|void|null|undefined|any|unknown|never|Promise|Array|Record|Map|Set)\b/g, '<span class="syn-type">$1</span>');
1246
+ }
1247
+
1248
+ // ═══ TRIGGER ═══
1249
+ async function triggerCycle() {
1250
+ try {
1251
+ var btn = document.querySelector('.trigger-btn');
1252
+ btn.textContent = 'Triggering...';
1253
+ btn.style.opacity = '0.6';
1254
+ await fetch('/api/trigger', { method: 'POST' });
1255
+ setTimeout(function() { btn.textContent = 'Trigger Cycle'; btn.style.opacity = '1'; }, 1500);
1256
+ } catch(e) { console.warn('triggerCycle failed', e); }
1257
+ }
1258
+
1259
+ // ═══ UPTIME ═══
1260
+ setInterval(function() {
1261
+ var s = Math.floor((Date.now() - startTime) / 1000);
1262
+ var h = Math.floor(s / 3600);
1263
+ var m = Math.floor((s % 3600) / 60);
1264
+ document.getElementById('uptimeEl').textContent = 'Uptime: ' + h + 'h ' + m + 'm';
1265
+ }, 10000);
1266
+
1267
+ // ═══ BOOT ═══
1268
+ initNeuralGraph('overviewCanvas');
1269
+ initNeuralGraph('neuralCanvas');
1270
+ window.addEventListener('resize', resizeNeuralCanvas);
1271
+
1272
+ connect();
1273
+ fetchInitial();
1274
+ neuralLoop();
1083
1275
  </script>
1084
1276
  </body>
1085
1277
  </html>