@timmeck/brain-core 2.20.0 → 2.22.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.
- package/dist/consciousness/types.d.ts +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/narrative/index.d.ts +2 -0
- package/dist/narrative/index.js +2 -0
- package/dist/narrative/index.js.map +1 -0
- package/dist/narrative/narrative-engine.d.ts +117 -0
- package/dist/narrative/narrative-engine.js +582 -0
- package/dist/narrative/narrative-engine.js.map +1 -0
- package/dist/research/research-orchestrator.d.ts +4 -0
- package/dist/research/research-orchestrator.js +6 -0
- package/dist/research/research-orchestrator.js.map +1 -1
- package/dist/unified/index.d.ts +2 -0
- package/dist/unified/index.js +2 -0
- package/dist/unified/index.js.map +1 -0
- package/dist/unified/unified-server.d.ts +25 -0
- package/dist/unified/unified-server.js +176 -0
- package/dist/unified/unified-server.js.map +1 -0
- package/package.json +1 -1
- package/unified-dashboard.html +1085 -0
|
@@ -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">☍</span> Overview
|
|
468
|
+
</div>
|
|
469
|
+
<div class="nav-item" data-page="notifications">
|
|
470
|
+
<span class="nav-icon">⚠</span> Notifications
|
|
471
|
+
</div>
|
|
472
|
+
<div class="nav-section">Intelligence</div>
|
|
473
|
+
<div class="nav-item" data-page="attention">
|
|
474
|
+
<span class="nav-icon">◎</span> Attention
|
|
475
|
+
</div>
|
|
476
|
+
<div class="nav-item" data-page="transfer">
|
|
477
|
+
<span class="nav-icon">⇄</span> Transfer
|
|
478
|
+
</div>
|
|
479
|
+
<div class="nav-item" data-page="engines">
|
|
480
|
+
<span class="nav-icon">⚙</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)">●</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)">●</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)">●</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)">●</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">⚡ 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 →</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 →</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 →</a>
|
|
574
|
+
</div>
|
|
575
|
+
</div>
|
|
576
|
+
|
|
577
|
+
<!-- Recent Notifications -->
|
|
578
|
+
<div class="card">
|
|
579
|
+
<div class="card-title">⚠ 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">⚠ 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">◎ 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">↔ Cross-Domain Analogies</div>
|
|
639
|
+
<div id="analogyList"></div>
|
|
640
|
+
</div>
|
|
641
|
+
<div class="card">
|
|
642
|
+
<div class="card-title">⚡ 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">⚙ 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 = '⚡ 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) + ' — ' +
|
|
896
|
+
'<span class="context-badge ' + (s.from || '') + '">' + escapeHtml(s.from || '?') + '</span>' +
|
|
897
|
+
' → <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) + ' — ' +
|
|
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 || '?') + ' ↔ ' + 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 || '?') + ' → ' + 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>
|