@timmeck/brain-core 2.19.0 → 2.21.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.
@@ -0,0 +1,1085 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Brain Ecosystem — Unified Dashboard</title>
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
+ }
454
+ </style>
455
+ </head>
456
+ <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>
525
+ </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>
530
+ </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>
535
+ </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>
540
+ </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>
554
+ </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>
564
+ </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>
574
+ </div>
575
+ </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>
589
+ </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>
602
+ </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>
608
+ </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>
622
+ </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>
626
+ </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>
630
+ </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>
634
+ </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>
640
+ </div>
641
+ <div class="card">
642
+ <div class="card-title">&#9889; Cross-Domain Rules</div>
643
+ <div class="rule-list" id="ruleList"></div>
644
+ </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
+
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>
666
+ </div>
667
+ </div>
668
+
669
+ </main>
670
+
671
+ <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: [],
684
+ };
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
+ });
703
+ });
704
+
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
+ });
716
+
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' });
722
+ }
723
+
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';
731
+ }
732
+
733
+ function escapeHtml(text) {
734
+ const div = document.createElement('div');
735
+ div.textContent = text;
736
+ return div.innerHTML;
737
+ }
738
+
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';
763
+ }
764
+
765
+ // Health badge
766
+ renderHealthBadge(ov);
767
+
768
+ // Brain cards
769
+ renderBrainCards(ov);
770
+
771
+ // Recent notifications (top 30)
772
+ renderNotifFeed('overviewNotifs', state.thoughts.slice(0, 30));
773
+ }
774
+
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
+ }
786
+ }
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 || '--');
818
+ }
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');
824
+ }
825
+
826
+ function setText(id, value) {
827
+ const el = document.getElementById(id);
828
+ if (el) el.textContent = value != null ? value : '--';
829
+ }
830
+
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('');
844
+ }
845
+
846
+ function renderAllNotifications() {
847
+ renderNotifFeed('allNotifs', state.thoughts.slice(0, 200));
848
+ }
849
+
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;
856
+ }
857
+
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>';
862
+
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>';
867
+ } 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('');
877
+ }
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
+ }
911
+
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('');
966
+ }
967
+ }
968
+
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('');
997
+ }
998
+
999
+ // ── Render All ─────────────────────────────
1000
+ function renderAll() {
1001
+ renderOverview();
1002
+ renderAllNotifications();
1003
+ renderAttention();
1004
+ renderTransfer();
1005
+ renderEngines();
1006
+ }
1007
+
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
+ });
1040
+
1041
+ es.addEventListener('heartbeat', () => {
1042
+ // Keep-alive
1043
+ });
1044
+
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
+ }
1052
+
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);
1070
+ }
1071
+ }
1072
+
1073
+ // ── Uptime Timer ───────────────────────────
1074
+ setInterval(() => {
1075
+ document.getElementById('uptimeLabel').textContent = 'Uptime: ' + timeSince(Date.now() - startTime);
1076
+ }, 1000);
1077
+
1078
+ // ── Boot ───────────────────────────────────
1079
+ loadInitialState();
1080
+ connectSSE();
1081
+
1082
+ })();
1083
+ </script>
1084
+ </body>
1085
+ </html>