@timmeck/brain-core 2.36.17 → 2.36.19
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/command-center.html +516 -74
- package/dist/dashboard/__tests__/command-center-server.test.js +134 -4
- package/dist/dashboard/__tests__/command-center-server.test.js.map +1 -1
- package/dist/dashboard/command-center-server.d.ts +13 -0
- package/dist/dashboard/command-center-server.js +131 -1
- package/dist/dashboard/command-center-server.js.map +1 -1
- package/package.json +1 -1
package/command-center.html
CHANGED
|
@@ -143,6 +143,53 @@ canvas{display:block;width:100%;height:100%}
|
|
|
143
143
|
.tbl td{padding:8px 10px;border-bottom:1px solid rgba(100,140,255,0.06)}
|
|
144
144
|
.tbl tr:hover td{background:rgba(0,229,255,0.03)}
|
|
145
145
|
|
|
146
|
+
/* ── Error Feed ───────────────────────────────────────── */
|
|
147
|
+
.error-feed{max-height:260px;overflow-y:auto;font-size:12px}
|
|
148
|
+
.error-item{padding:8px 10px;border-bottom:1px solid rgba(255,68,102,0.08);display:flex;gap:10px;align-items:flex-start}
|
|
149
|
+
.error-level{font-size:10px;font-weight:700;padding:1px 6px;border-radius:6px;white-space:nowrap}
|
|
150
|
+
.error-level.err{color:var(--red);background:rgba(255,68,102,0.12)}
|
|
151
|
+
.error-level.warn{color:var(--orange);background:rgba(255,153,51,0.12)}
|
|
152
|
+
.error-msg{color:var(--text);flex:1;line-height:1.4;word-break:break-word}
|
|
153
|
+
|
|
154
|
+
/* ── Self-Mod ─────────────────────────────────────────── */
|
|
155
|
+
.mod-item{padding:10px;border-bottom:1px solid rgba(100,140,255,0.06);display:flex;gap:12px;align-items:flex-start}
|
|
156
|
+
.mod-status{font-size:9px;font-weight:700;padding:2px 8px;border-radius:8px;white-space:nowrap}
|
|
157
|
+
.mod-applied{color:var(--green);background:rgba(0,255,136,0.12)}
|
|
158
|
+
.mod-testing{color:var(--yellow);background:rgba(255,204,0,0.12)}
|
|
159
|
+
.mod-proposed{color:var(--cyan);background:rgba(0,229,255,0.1)}
|
|
160
|
+
.mod-failed{color:var(--red);background:rgba(255,68,102,0.12)}
|
|
161
|
+
.mod-title{font-weight:600;font-size:12px;margin-bottom:2px}
|
|
162
|
+
.mod-detail{font-size:11px;color:var(--text-dim)}
|
|
163
|
+
|
|
164
|
+
/* ── Mission Card ─────────────────────────────────────── */
|
|
165
|
+
.mission-card{background:var(--bg-card);border:1px solid var(--glass-border);border-radius:var(--radius-sm);padding:12px;margin-bottom:8px}
|
|
166
|
+
.mission-topic{font-weight:600;font-size:13px;margin-bottom:6px}
|
|
167
|
+
.mission-phases{display:flex;gap:4px;margin-top:8px}
|
|
168
|
+
.mission-phase{flex:1;height:4px;border-radius:2px;background:rgba(100,140,255,0.1)}
|
|
169
|
+
.mission-phase.done{background:var(--green)}
|
|
170
|
+
.mission-phase.running{background:var(--cyan);animation:pulse-glow 1.5s infinite}
|
|
171
|
+
.mission-phase.fail{background:var(--red)}
|
|
172
|
+
.mission-meta{font-size:10px;color:var(--text-dim);margin-top:6px;display:flex;gap:12px}
|
|
173
|
+
|
|
174
|
+
/* ── Knowledge Chart ──────────────────────────────────── */
|
|
175
|
+
.chart-wrap{position:relative;height:200px;padding:10px 0}
|
|
176
|
+
.chart-wrap svg{width:100%;height:100%}
|
|
177
|
+
|
|
178
|
+
/* ── Quick Actions ────────────────────────────────────── */
|
|
179
|
+
.quick-actions{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:16px}
|
|
180
|
+
.action-btn{padding:8px 16px;border-radius:var(--radius-sm);border:1px solid var(--border);background:var(--bg-card);color:var(--text);cursor:pointer;font-size:12px;transition:all .2s;display:flex;align-items:center;gap:6px}
|
|
181
|
+
.action-btn:hover{border-color:var(--cyan);color:var(--cyan);background:var(--bg-card-hover)}
|
|
182
|
+
.action-btn:active{transform:scale(0.97)}
|
|
183
|
+
.action-btn.running{border-color:var(--yellow);color:var(--yellow);pointer-events:none}
|
|
184
|
+
.action-btn .action-icon{font-size:14px}
|
|
185
|
+
|
|
186
|
+
/* ── Dependency Flow ──────────────────────────────────── */
|
|
187
|
+
.dep-flow{display:flex;align-items:stretch;gap:4px;padding:12px 0;overflow-x:auto}
|
|
188
|
+
.dep-group{background:var(--bg-card);border:1px solid var(--glass-border);border-radius:var(--radius-sm);padding:10px;min-width:140px;flex:1}
|
|
189
|
+
.dep-group-title{font-size:10px;text-transform:uppercase;letter-spacing:1px;color:var(--text-dim);margin-bottom:8px;text-align:center}
|
|
190
|
+
.dep-engine{font-size:11px;padding:3px 6px;margin:2px 0;border-radius:4px;background:rgba(0,229,255,0.06);color:var(--text);text-align:center}
|
|
191
|
+
.dep-arrow{display:flex;align-items:center;color:var(--cyan);font-size:18px;opacity:.4;animation:pulse-arrow 2s ease-in-out infinite}
|
|
192
|
+
|
|
146
193
|
/* ── Misc ──────────────────────────────────────────────── */
|
|
147
194
|
.section{margin-bottom:24px}
|
|
148
195
|
.section-title{font-size:14px;font-weight:600;margin-bottom:12px;display:flex;align-items:center;gap:8px}
|
|
@@ -187,6 +234,7 @@ canvas{display:block;width:100%;height:100%}
|
|
|
187
234
|
<div class="nav-item" data-page="marketing"><span class="nav-icon">📣</span><span class="nav-label">Marketing Flow</span></div>
|
|
188
235
|
<div class="nav-section">System</div>
|
|
189
236
|
<div class="nav-item" data-page="crossbrain"><span class="nav-icon">🔗</span><span class="nav-label">Cross-Brain</span></div>
|
|
237
|
+
<div class="nav-item" data-page="activity"><span class="nav-icon">⚡</span><span class="nav-label">Aktivität</span></div>
|
|
190
238
|
<div class="nav-item" data-page="infra"><span class="nav-icon">⚙</span><span class="nav-label">Infrastruktur</span></div>
|
|
191
239
|
</div>
|
|
192
240
|
|
|
@@ -207,6 +255,17 @@ canvas{display:block;width:100%;height:100%}
|
|
|
207
255
|
<div class="content">
|
|
208
256
|
<!-- ════ Page 1: Ecosystem Overview ═════════════════ -->
|
|
209
257
|
<div class="page active" id="page-overview">
|
|
258
|
+
<!-- Quick Actions -->
|
|
259
|
+
<div class="section">
|
|
260
|
+
<div class="section-title"><span class="icon">🎮</span> Schnellaktionen</div>
|
|
261
|
+
<div class="quick-actions" id="quickActions">
|
|
262
|
+
<button class="action-btn" data-action="learning-cycle"><span class="action-icon">🔄</span> Lernzyklus starten</button>
|
|
263
|
+
<button class="action-btn" data-action="borg-sync"><span class="action-icon">👾</span> Borg Sync</button>
|
|
264
|
+
<button class="action-btn" data-action="tech-scan"><span class="action-icon">📡</span> Tech-Radar Scan</button>
|
|
265
|
+
<button class="action-btn" data-action="health-check"><span class="action-icon">🏥</span> Health Check</button>
|
|
266
|
+
</div>
|
|
267
|
+
</div>
|
|
268
|
+
|
|
210
269
|
<div class="section">
|
|
211
270
|
<div class="section-title"><span class="icon">🤖</span> Brain Status</div>
|
|
212
271
|
<div class="grid grid-3" id="brainCards"></div>
|
|
@@ -228,19 +287,27 @@ canvas{display:block;width:100%;height:100%}
|
|
|
228
287
|
</div>
|
|
229
288
|
</div>
|
|
230
289
|
<div class="section">
|
|
231
|
-
<div class="section-title"><span class="icon">📊</span>
|
|
232
|
-
<div class="card" id="llmCard"><div class="empty">
|
|
290
|
+
<div class="section-title"><span class="icon">📊</span> KI-Nutzung</div>
|
|
291
|
+
<div class="card" id="llmCard"><div class="empty">Warte auf KI-Daten...</div></div>
|
|
233
292
|
</div>
|
|
234
293
|
</div>
|
|
235
294
|
|
|
236
295
|
<div class="section">
|
|
237
296
|
<div class="section-title"><span class="icon">💭</span> Brain denkt gerade... <span id="thoughtCount" style="color:var(--text-dim);font-weight:400;font-size:12px"></span></div>
|
|
238
297
|
<div class="card" style="padding:0">
|
|
239
|
-
<div class="thought-stream" id="thoughtStream"><div class="empty">
|
|
298
|
+
<div class="thought-stream" id="thoughtStream"><div class="empty">Warte auf Gedanken...</div></div>
|
|
240
299
|
</div>
|
|
241
300
|
</div>
|
|
242
301
|
</div>
|
|
243
302
|
|
|
303
|
+
<!-- Error Log -->
|
|
304
|
+
<div class="section">
|
|
305
|
+
<div class="section-title"><span class="icon">⚠</span> Letzte Fehler & Warnungen <span id="errorCount" style="color:var(--text-dim);font-weight:400;font-size:12px"></span></div>
|
|
306
|
+
<div class="card" style="padding:0">
|
|
307
|
+
<div class="error-feed" id="errorFeed"><div class="empty">Keine Fehler — alles läuft!</div></div>
|
|
308
|
+
</div>
|
|
309
|
+
</div>
|
|
310
|
+
|
|
244
311
|
<div class="section">
|
|
245
312
|
<div class="section-title"><span class="icon">🌐</span> Peer Verbindungen</div>
|
|
246
313
|
<div class="card canvas-wrap" style="height:180px">
|
|
@@ -252,21 +319,96 @@ canvas{display:block;width:100%;height:100%}
|
|
|
252
319
|
<!-- ════ Page 2: Lern-Kreislauf ═════════════════════ -->
|
|
253
320
|
<div class="page" id="page-learning">
|
|
254
321
|
<div class="section">
|
|
255
|
-
<div class="section-title"><span class="icon">🔄</span> Der
|
|
322
|
+
<div class="section-title"><span class="icon">🔄</span> So lernt Brain — Der Kreislauf</div>
|
|
323
|
+
<p style="font-size:12px;color:var(--text-dim);margin-bottom:12px">Daten kommen rein, Brain analysiert sie, bildet Hypothesen, testet sie als Experimente und macht daraus Wissen. Dieser Kreislauf läuft ständig.</p>
|
|
256
324
|
<div class="pipeline" id="learnPipeline">
|
|
257
325
|
<div class="pipe-stage"><div class="pipe-box"><div class="pipe-icon">📥</div><div class="pipe-label">Daten rein</div><div class="pipe-value" id="lp-data">0</div></div></div>
|
|
258
326
|
<div class="pipe-arrow">➡</div>
|
|
259
|
-
<div class="pipe-stage"><div class="pipe-box"><div class="pipe-icon">🔍</div><div class="pipe-label">
|
|
327
|
+
<div class="pipe-stage"><div class="pipe-box"><div class="pipe-icon">🔍</div><div class="pipe-label">Analysieren</div><div class="pipe-value" id="lp-analysis">0</div></div></div>
|
|
260
328
|
<div class="pipe-arrow">➡</div>
|
|
261
|
-
<div class="pipe-stage"><div class="pipe-box"><div class="pipe-icon">💡</div><div class="pipe-label">
|
|
329
|
+
<div class="pipe-stage"><div class="pipe-box"><div class="pipe-icon">💡</div><div class="pipe-label">Ideen bilden</div><div class="pipe-value" id="lp-hypotheses">0</div></div></div>
|
|
262
330
|
<div class="pipe-arrow">➡</div>
|
|
263
|
-
<div class="pipe-stage"><div class="pipe-box"><div class="pipe-icon">🧪</div><div class="pipe-label">
|
|
331
|
+
<div class="pipe-stage"><div class="pipe-box"><div class="pipe-icon">🧪</div><div class="pipe-label">Ausprobieren</div><div class="pipe-value" id="lp-experiments">0</div></div></div>
|
|
264
332
|
<div class="pipe-arrow">➡</div>
|
|
265
|
-
<div class="pipe-stage"><div class="pipe-box"><div class="pipe-icon">📜</div><div class="pipe-label">
|
|
333
|
+
<div class="pipe-stage"><div class="pipe-box"><div class="pipe-icon">📜</div><div class="pipe-label">Wissen merken</div><div class="pipe-value" id="lp-principles">0</div></div></div>
|
|
266
334
|
<div class="pipe-arrow">➡</div>
|
|
267
|
-
<div class="pipe-stage"><div class="pipe-box"><div class="pipe-icon">⚡</div><div class="pipe-label">
|
|
335
|
+
<div class="pipe-stage"><div class="pipe-box"><div class="pipe-icon">⚡</div><div class="pipe-label">Umsetzen</div><div class="pipe-value" id="lp-actions">0</div></div></div>
|
|
268
336
|
</div>
|
|
269
337
|
</div>
|
|
338
|
+
|
|
339
|
+
<!-- Engine Dependency Flow -->
|
|
340
|
+
<div class="section">
|
|
341
|
+
<div class="section-title"><span class="icon">🔀</span> Wie die Engines zusammenarbeiten</div>
|
|
342
|
+
<p style="font-size:12px;color:var(--text-dim);margin-bottom:12px">Jede Engine hat eine Aufgabe. Zusammen bilden sie eine Kette — von der Beobachtung bis zur Aktion.</p>
|
|
343
|
+
<div class="dep-flow" id="engineDependencyFlow">
|
|
344
|
+
<div class="dep-group">
|
|
345
|
+
<div class="dep-group-title">👁 Beobachten</div>
|
|
346
|
+
<div class="dep-engine">DataScout</div>
|
|
347
|
+
<div class="dep-engine">TechRadar</div>
|
|
348
|
+
<div class="dep-engine">SignalScanner</div>
|
|
349
|
+
<div class="dep-engine">AnomalyDetective</div>
|
|
350
|
+
</div>
|
|
351
|
+
<div class="dep-arrow">➡</div>
|
|
352
|
+
<div class="dep-group">
|
|
353
|
+
<div class="dep-group-title">🧠 Verstehen</div>
|
|
354
|
+
<div class="dep-engine">CausalEngine</div>
|
|
355
|
+
<div class="dep-engine">PatternEngine</div>
|
|
356
|
+
<div class="dep-engine">Curiosity</div>
|
|
357
|
+
<div class="dep-engine">Attention</div>
|
|
358
|
+
</div>
|
|
359
|
+
<div class="dep-arrow">➡</div>
|
|
360
|
+
<div class="dep-group">
|
|
361
|
+
<div class="dep-group-title">💡 Ideen</div>
|
|
362
|
+
<div class="dep-engine">HypothesisEngine</div>
|
|
363
|
+
<div class="dep-engine">DreamEngine</div>
|
|
364
|
+
<div class="dep-engine">DebateEngine</div>
|
|
365
|
+
</div>
|
|
366
|
+
<div class="dep-arrow">➡</div>
|
|
367
|
+
<div class="dep-group">
|
|
368
|
+
<div class="dep-group-title">🧪 Testen</div>
|
|
369
|
+
<div class="dep-engine">ExperimentEngine</div>
|
|
370
|
+
<div class="dep-engine">SimulationEngine</div>
|
|
371
|
+
<div class="dep-engine">Prediction</div>
|
|
372
|
+
</div>
|
|
373
|
+
<div class="dep-arrow">➡</div>
|
|
374
|
+
<div class="dep-group">
|
|
375
|
+
<div class="dep-group-title">📚 Wissen</div>
|
|
376
|
+
<div class="dep-engine">KnowledgeDistiller</div>
|
|
377
|
+
<div class="dep-engine">MemoryPalace</div>
|
|
378
|
+
<div class="dep-engine">ResearchJournal</div>
|
|
379
|
+
</div>
|
|
380
|
+
<div class="dep-arrow">➡</div>
|
|
381
|
+
<div class="dep-group">
|
|
382
|
+
<div class="dep-group-title">⚡ Handeln</div>
|
|
383
|
+
<div class="dep-engine">SelfMod</div>
|
|
384
|
+
<div class="dep-engine">GoalEngine</div>
|
|
385
|
+
<div class="dep-engine">Strategy</div>
|
|
386
|
+
<div class="dep-engine">MetaCognition</div>
|
|
387
|
+
</div>
|
|
388
|
+
</div>
|
|
389
|
+
</div>
|
|
390
|
+
|
|
391
|
+
<!-- Knowledge Growth -->
|
|
392
|
+
<div class="section">
|
|
393
|
+
<div class="section-title"><span class="icon">📈</span> Wissens-Wachstum — Wird Brain schlauer?</div>
|
|
394
|
+
<div class="card">
|
|
395
|
+
<div class="grid grid-4" id="knowledgeTotals">
|
|
396
|
+
<div style="text-align:center"><div class="card-value" id="kn-principles" style="color:var(--cyan)">0</div><div class="card-sub">Regeln gelernt</div></div>
|
|
397
|
+
<div style="text-align:center"><div class="card-value" id="kn-hypotheses" style="color:var(--yellow)">0</div><div class="card-sub">Hypothesen getestet</div></div>
|
|
398
|
+
<div style="text-align:center"><div class="card-value" id="kn-experiments" style="color:var(--green)">0</div><div class="card-sub">Experimente gemacht</div></div>
|
|
399
|
+
<div style="text-align:center"><div class="card-value" id="kn-errors" style="color:var(--red)">0</div><div class="card-sub">Fehler gelöst</div></div>
|
|
400
|
+
</div>
|
|
401
|
+
<div class="chart-wrap" id="knowledgeChart">
|
|
402
|
+
<svg id="knowledgeSvg" viewBox="0 0 600 180" preserveAspectRatio="none"></svg>
|
|
403
|
+
</div>
|
|
404
|
+
<div style="display:flex;gap:16px;justify-content:center;font-size:10px;color:var(--text-dim)">
|
|
405
|
+
<span><span style="color:var(--cyan)">⬤</span> Regeln</span>
|
|
406
|
+
<span><span style="color:var(--green)">⬤</span> Lösungen</span>
|
|
407
|
+
<span><span style="color:var(--red)">⬤</span> Fehler</span>
|
|
408
|
+
</div>
|
|
409
|
+
</div>
|
|
410
|
+
</div>
|
|
411
|
+
|
|
270
412
|
<div class="section">
|
|
271
413
|
<div class="section-title"><span class="icon">🏭</span> Engine Stationen</div>
|
|
272
414
|
<div class="engine-grid" id="brainEngineGrid"><div class="empty">Loading...</div></div>
|
|
@@ -276,7 +418,8 @@ canvas{display:block;width:100%;height:100%}
|
|
|
276
418
|
<!-- ════ Page 3: Trading Flow ═══════════════════════ -->
|
|
277
419
|
<div class="page" id="page-trading">
|
|
278
420
|
<div class="section">
|
|
279
|
-
<div class="section-title"><span class="icon">📈</span> Trading
|
|
421
|
+
<div class="section-title"><span class="icon">📈</span> So tradet Trading-Brain</div>
|
|
422
|
+
<p style="font-size:12px;color:var(--text-dim);margin-bottom:12px">Signale kommen rein, werden analysiert, als Paper Trade getestet und das Ergebnis fließt zurück ins Lernen.</p>
|
|
280
423
|
<div class="pipeline">
|
|
281
424
|
<div class="pipe-stage"><div class="pipe-box" style="border-color:rgba(0,255,136,0.2)"><div class="pipe-icon">📡</div><div class="pipe-label">Signale</div><div class="pipe-value" id="tp-signals" style="color:var(--green)">0</div></div></div>
|
|
282
425
|
<div class="pipe-arrow" style="color:var(--green)">➡</div>
|
|
@@ -290,9 +433,9 @@ canvas{display:block;width:100%;height:100%}
|
|
|
290
433
|
</div>
|
|
291
434
|
</div>
|
|
292
435
|
<div class="grid grid-3">
|
|
293
|
-
<div class="card"><div class="card-title">Equity</div><div class="card-value" id="tradingEquity" style="color:var(--green)">--</div><div class="card-sub">Paper Trading
|
|
294
|
-
<div class="card"><div class="card-title">Win Rate</div><div class="card-value" id="tradingWinRate" style="color:var(--green)">--</div><div class="card-sub">Gewinnrate</div></div>
|
|
295
|
-
<div class="card"><div class="card-title">Offene Positionen</div><div class="card-value" id="tradingPositions" style="color:var(--green)">--</div><div class="card-sub">Aktive Trades</div></div>
|
|
436
|
+
<div class="card"><div class="card-title">Equity</div><div class="card-value" id="tradingEquity" style="color:var(--green)">--</div><div class="card-sub">Paper Trading Guthaben</div></div>
|
|
437
|
+
<div class="card"><div class="card-title">Win Rate</div><div class="card-value" id="tradingWinRate" style="color:var(--green)">--</div><div class="card-sub">Gewinnrate aller Trades</div></div>
|
|
438
|
+
<div class="card"><div class="card-title">Offene Positionen</div><div class="card-value" id="tradingPositions" style="color:var(--green)">--</div><div class="card-sub">Aktive Trades gerade</div></div>
|
|
296
439
|
</div>
|
|
297
440
|
<div class="section" style="margin-top:16px">
|
|
298
441
|
<div class="section-title"><span class="icon">🏭</span> Trading Engines</div>
|
|
@@ -303,7 +446,8 @@ canvas{display:block;width:100%;height:100%}
|
|
|
303
446
|
<!-- ════ Page 4: Marketing Flow ═════════════════════ -->
|
|
304
447
|
<div class="page" id="page-marketing">
|
|
305
448
|
<div class="section">
|
|
306
|
-
<div class="section-title"><span class="icon">📣</span> Marketing
|
|
449
|
+
<div class="section-title"><span class="icon">📣</span> So lernt Marketing-Brain</div>
|
|
450
|
+
<p style="font-size:12px;color:var(--text-dim);margin-bottom:12px">Posts werden veröffentlicht, das Engagement wird gemessen, Muster erkannt und die Strategie angepasst.</p>
|
|
307
451
|
<div class="pipeline">
|
|
308
452
|
<div class="pipe-stage"><div class="pipe-box" style="border-color:rgba(255,68,204,0.2)"><div class="pipe-icon">✍</div><div class="pipe-label">Posts</div><div class="pipe-value" id="mp-posts" style="color:var(--magenta)">0</div></div></div>
|
|
309
453
|
<div class="pipe-arrow" style="color:var(--magenta)">➡</div>
|
|
@@ -328,19 +472,21 @@ canvas{display:block;width:100%;height:100%}
|
|
|
328
472
|
<!-- ════ Page 5: Cross-Brain & Borg ═════════════════ -->
|
|
329
473
|
<div class="page" id="page-crossbrain">
|
|
330
474
|
<div class="section">
|
|
331
|
-
<div class="section-title"><span class="icon">🔗</span>
|
|
475
|
+
<div class="section-title"><span class="icon">🔗</span> So kommunizieren die Brains</div>
|
|
476
|
+
<p style="font-size:12px;color:var(--text-dim);margin-bottom:12px">Die drei Brains tauschen ständig Nachrichten aus. Jeder fliegende Punkt ist eine Nachricht. Lila = Borg-Wissens-Sync.</p>
|
|
332
477
|
<div class="card canvas-wrap" style="height:280px">
|
|
333
478
|
<canvas id="crossBrainCanvas"></canvas>
|
|
334
479
|
</div>
|
|
335
480
|
</div>
|
|
336
481
|
<div class="grid grid-2">
|
|
337
482
|
<div class="section">
|
|
338
|
-
<div class="section-title"><span class="icon">👾</span> Borg Sync</div>
|
|
483
|
+
<div class="section-title"><span class="icon">👾</span> Borg Sync — Geteiltes Wissen</div>
|
|
339
484
|
<div class="card" id="borgCard">
|
|
485
|
+
<p style="font-size:11px;color:var(--text-dim);margin-bottom:10px">Borg synchronisiert Wissen zwischen allen Brains. Was einer lernt, wissen alle.</p>
|
|
340
486
|
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:12px">
|
|
341
487
|
<div>
|
|
342
488
|
<div class="card-title" style="margin-bottom:4px">Collective Sync</div>
|
|
343
|
-
<div id="borgStatus" style="font-size:13px;color:var(--text-dim)">
|
|
489
|
+
<div id="borgStatus" style="font-size:13px;color:var(--text-dim)">Nicht verfügbar</div>
|
|
344
490
|
</div>
|
|
345
491
|
<button class="btn" id="borgToggle" disabled>Toggle</button>
|
|
346
492
|
</div>
|
|
@@ -348,30 +494,61 @@ canvas{display:block;width:100%;height:100%}
|
|
|
348
494
|
</div>
|
|
349
495
|
</div>
|
|
350
496
|
<div class="section">
|
|
351
|
-
<div class="section-title"><span class="icon">📜</span> Korrelationen
|
|
352
|
-
<div class="card"><div id="correlationList"><div class="empty">
|
|
497
|
+
<div class="section-title"><span class="icon">📜</span> Korrelationen — Was hängt zusammen?</div>
|
|
498
|
+
<div class="card"><div id="correlationList"><div class="empty">Noch keine Korrelationen gefunden</div></div></div>
|
|
499
|
+
</div>
|
|
500
|
+
</div>
|
|
501
|
+
<div class="section">
|
|
502
|
+
<div class="section-title"><span class="icon">📃</span> Sync Verlauf</div>
|
|
503
|
+
<div class="card"><div id="borgHistory"><div class="empty">Noch kein Sync passiert</div></div></div>
|
|
504
|
+
</div>
|
|
505
|
+
</div>
|
|
506
|
+
|
|
507
|
+
<!-- ════ Page 6: Activity (Self-Mod + Missions) ═════ -->
|
|
508
|
+
<div class="page" id="page-activity">
|
|
509
|
+
<!-- Self-Modification -->
|
|
510
|
+
<div class="section">
|
|
511
|
+
<div class="section-title"><span class="icon">🔧</span> Brain verbessert sich selbst</div>
|
|
512
|
+
<p style="font-size:12px;color:var(--text-dim);margin-bottom:12px">Brain erkennt Probleme im eigenen Code und schlägt Verbesserungen vor. Jede Änderung wird erst getestet, bevor sie angewendet wird.</p>
|
|
513
|
+
<div class="grid grid-4" id="selfModStats">
|
|
514
|
+
<div class="card" style="text-align:center"><div class="card-value" id="sm-total" style="color:var(--cyan)">0</div><div class="card-sub">Vorschläge insgesamt</div></div>
|
|
515
|
+
<div class="card" style="text-align:center"><div class="card-value" id="sm-applied" style="color:var(--green)">0</div><div class="card-sub">Erfolgreich angewendet</div></div>
|
|
516
|
+
<div class="card" style="text-align:center"><div class="card-value" id="sm-testing" style="color:var(--yellow)">0</div><div class="card-sub">Wird gerade getestet</div></div>
|
|
517
|
+
<div class="card" style="text-align:center"><div class="card-value" id="sm-failed" style="color:var(--red)">0</div><div class="card-sub">Fehlgeschlagen</div></div>
|
|
518
|
+
</div>
|
|
519
|
+
<div class="card" style="padding:0;margin-top:12px">
|
|
520
|
+
<div style="max-height:300px;overflow-y:auto" id="selfModFeed"><div class="empty">Brain hat sich noch nicht selbst verbessert</div></div>
|
|
353
521
|
</div>
|
|
354
522
|
</div>
|
|
523
|
+
|
|
524
|
+
<!-- Mission Tracker -->
|
|
355
525
|
<div class="section">
|
|
356
|
-
<div class="section-title"><span class="icon">&#
|
|
357
|
-
<
|
|
526
|
+
<div class="section-title"><span class="icon">🚀</span> Aktive Recherche-Missionen</div>
|
|
527
|
+
<p style="font-size:12px;color:var(--text-dim);margin-bottom:12px">Brain forscht eigenständig zu Themen. Jede Mission durchläuft 5 Phasen: Aufteilen, Sammeln, Hypothesen bilden, Analysieren, Zusammenfassen.</p>
|
|
528
|
+
<div class="grid grid-3" id="missionStats">
|
|
529
|
+
<div class="card" style="text-align:center"><div class="card-value" id="ms-active" style="color:var(--cyan)">0</div><div class="card-sub">Aktive Missionen</div></div>
|
|
530
|
+
<div class="card" style="text-align:center"><div class="card-value" id="ms-completed" style="color:var(--green)">0</div><div class="card-sub">Abgeschlossen</div></div>
|
|
531
|
+
<div class="card" style="text-align:center"><div class="card-value" id="ms-sources" style="color:var(--purple)">0</div><div class="card-sub">Quellen gesammelt</div></div>
|
|
532
|
+
</div>
|
|
533
|
+
<div id="missionList" style="margin-top:12px"><div class="empty">Keine aktiven Missionen</div></div>
|
|
358
534
|
</div>
|
|
359
535
|
</div>
|
|
360
536
|
|
|
361
|
-
<!-- ════ Page
|
|
537
|
+
<!-- ════ Page 7: Infrastruktur ══════════════════════ -->
|
|
362
538
|
<div class="page" id="page-infra">
|
|
363
539
|
<div class="grid grid-2">
|
|
364
540
|
<div class="section">
|
|
365
|
-
<div class="section-title"><span class="icon">🛡</span> Watchdog</div>
|
|
541
|
+
<div class="section-title"><span class="icon">🛡</span> Watchdog — Hält alles am Laufen</div>
|
|
366
542
|
<div class="card" id="watchdogCard"><div class="empty">Loading...</div></div>
|
|
367
543
|
</div>
|
|
368
544
|
<div class="section">
|
|
369
|
-
<div class="section-title"><span class="icon">🧩</span> Plugins</div>
|
|
545
|
+
<div class="section-title"><span class="icon">🧩</span> Plugins — Erweiterungen</div>
|
|
370
546
|
<div class="card" id="pluginCard"><div class="empty">Loading...</div></div>
|
|
371
547
|
</div>
|
|
372
548
|
</div>
|
|
373
549
|
<div class="section">
|
|
374
|
-
<div class="section-title"><span class="icon">🏭</span> Alle Engines</div>
|
|
550
|
+
<div class="section-title"><span class="icon">🏭</span> Alle Engines auf einen Blick</div>
|
|
551
|
+
<p style="font-size:12px;color:var(--text-dim);margin-bottom:8px">Grün = aktiv, Gelb = bereit, Grau = aus. Die Zahl zeigt wie viele Gedanken die Engine produziert hat.</p>
|
|
375
552
|
<div class="engine-grid" id="allEngineGrid"><div class="empty">Loading...</div></div>
|
|
376
553
|
</div>
|
|
377
554
|
<div class="section">
|
|
@@ -385,10 +562,22 @@ canvas{display:block;width:100%;height:100%}
|
|
|
385
562
|
|
|
386
563
|
<script>
|
|
387
564
|
// ── State ─────────────────────────────────────────────────
|
|
388
|
-
const state = {
|
|
565
|
+
const state = {
|
|
566
|
+
ecosystem:null, engines:[], watchdog:[], plugins:[], borg:null, analytics:null,
|
|
567
|
+
llm:null, thoughts:[], connected:false, lastThoughtTime:0,
|
|
568
|
+
errors:null, selfmod:null, missions:null, knowledge:null
|
|
569
|
+
};
|
|
389
570
|
|
|
390
571
|
// ── Navigation ────────────────────────────────────────────
|
|
391
|
-
const titles = {
|
|
572
|
+
const titles = {
|
|
573
|
+
overview:'Ecosystem Overview',
|
|
574
|
+
learning:'Der Lern-Kreislauf',
|
|
575
|
+
trading:'Trading Flow',
|
|
576
|
+
marketing:'Marketing Flow',
|
|
577
|
+
crossbrain:'Cross-Brain & Borg',
|
|
578
|
+
activity:'Aktivität & Missionen',
|
|
579
|
+
infra:'Infrastruktur'
|
|
580
|
+
};
|
|
392
581
|
document.querySelectorAll('.nav-item').forEach(item => {
|
|
393
582
|
item.addEventListener('click', () => {
|
|
394
583
|
document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
|
|
@@ -410,12 +599,16 @@ function connectSSE() {
|
|
|
410
599
|
es.addEventListener('analytics', e => { state.analytics = JSON.parse(e.data); renderAnalytics(); });
|
|
411
600
|
es.addEventListener('llm', e => { state.llm = JSON.parse(e.data); renderLLM(); });
|
|
412
601
|
es.addEventListener('thought', e => { addThought(JSON.parse(e.data)); });
|
|
602
|
+
es.addEventListener('errors', e => { state.errors = JSON.parse(e.data); renderErrors(); });
|
|
603
|
+
es.addEventListener('selfmod', e => { state.selfmod = JSON.parse(e.data); renderSelfMod(); });
|
|
604
|
+
es.addEventListener('missions', e => { state.missions = JSON.parse(e.data); renderMissions(); });
|
|
605
|
+
es.addEventListener('knowledge', e => { state.knowledge = JSON.parse(e.data); renderKnowledge(); });
|
|
413
606
|
es.onerror = () => { state.connected = false; updateConnection(); };
|
|
414
607
|
}
|
|
415
608
|
|
|
416
609
|
function updateConnection() {
|
|
417
610
|
const b = document.getElementById('connectionBadge');
|
|
418
|
-
b.textContent = state.connected ? '
|
|
611
|
+
b.textContent = state.connected ? 'Verbunden' : 'Getrennt';
|
|
419
612
|
b.className = 'badge ' + (state.connected ? 'badge-ok' : 'badge-err');
|
|
420
613
|
}
|
|
421
614
|
|
|
@@ -426,8 +619,12 @@ async function loadInitial() {
|
|
|
426
619
|
state.ecosystem = data.ecosystem; state.engines = data.engines || [];
|
|
427
620
|
state.watchdog = data.watchdog || []; state.plugins = data.plugins || [];
|
|
428
621
|
state.borg = data.borg; state.analytics = data.analytics; state.llm = data.llm;
|
|
622
|
+
state.errors = data.errors; state.selfmod = data.selfmod;
|
|
623
|
+
state.missions = data.missions; state.knowledge = data.knowledge;
|
|
429
624
|
if (data.thoughts) { state.thoughts = data.thoughts; renderThoughts(); }
|
|
430
|
-
renderEcosystem(); renderEngines(); renderWatchdog(); renderPlugins();
|
|
625
|
+
renderEcosystem(); renderEngines(); renderWatchdog(); renderPlugins();
|
|
626
|
+
renderBorg(); renderAnalytics(); renderLLM(); renderErrors();
|
|
627
|
+
renderSelfMod(); renderMissions(); renderKnowledge();
|
|
431
628
|
} catch {}
|
|
432
629
|
}
|
|
433
630
|
|
|
@@ -443,12 +640,12 @@ function addThought(t) {
|
|
|
443
640
|
function renderThoughts() {
|
|
444
641
|
const el = document.getElementById('thoughtStream');
|
|
445
642
|
const cnt = document.getElementById('thoughtCount');
|
|
446
|
-
if (!state.thoughts.length) { el.innerHTML = '<div class="empty">
|
|
643
|
+
if (!state.thoughts.length) { el.innerHTML = '<div class="empty">Warte auf Gedanken...</div>'; cnt.textContent = ''; return; }
|
|
447
644
|
cnt.textContent = `(${state.thoughts.length})`;
|
|
448
645
|
el.innerHTML = state.thoughts.slice(0, 50).map(t => {
|
|
449
646
|
const time = new Date(t.timestamp).toLocaleTimeString('de-DE', { hour:'2-digit', minute:'2-digit', second:'2-digit' });
|
|
450
647
|
const sigClass = t.significance === 'breakthrough' ? 'sig-breakthrough' : t.significance === 'notable' ? 'sig-notable' : 'sig-routine';
|
|
451
|
-
const sigLabel = t.significance === 'breakthrough' ? 'Durchbruch' : t.significance === 'notable' ? 'Bemerkenswert' : '';
|
|
648
|
+
const sigLabel = t.significance === 'breakthrough' ? 'Durchbruch!' : t.significance === 'notable' ? 'Bemerkenswert' : '';
|
|
452
649
|
return `<div class="thought-item">
|
|
453
650
|
<span class="thought-time">${time}</span>
|
|
454
651
|
<span class="thought-engine" style="color:var(--cyan)">${t.engine}</span>
|
|
@@ -463,7 +660,7 @@ function updatePulse() {
|
|
|
463
660
|
const dot = document.getElementById('pulseDot');
|
|
464
661
|
const label = document.getElementById('pulseLabel');
|
|
465
662
|
const age = Date.now() - state.lastThoughtTime;
|
|
466
|
-
if (state.lastThoughtTime === 0) { dot.className = 'pulse-dot off'; label.textContent = '
|
|
663
|
+
if (state.lastThoughtTime === 0) { dot.className = 'pulse-dot off'; label.textContent = 'Warte...'; return; }
|
|
467
664
|
if (age < 30000) { dot.className = 'pulse-dot'; label.textContent = 'Brain arbeitet'; }
|
|
468
665
|
else if (age < 120000) { dot.className = 'pulse-dot idle'; label.textContent = 'Idle'; }
|
|
469
666
|
else { dot.className = 'pulse-dot off'; label.textContent = 'Ruhig'; }
|
|
@@ -474,57 +671,203 @@ setInterval(updatePulse, 5000);
|
|
|
474
671
|
function renderLLM() {
|
|
475
672
|
const el = document.getElementById('llmCard');
|
|
476
673
|
const s = state.llm;
|
|
477
|
-
if (!s) { el.innerHTML = '<div class="empty">Kein
|
|
674
|
+
if (!s) { el.innerHTML = '<div class="empty">Kein KI-Service aktiv</div>'; return; }
|
|
478
675
|
|
|
479
676
|
const hourPct = s.tokensThisHour && s.budgetRemainingHour != null ? Math.min(100, (s.tokensThisHour / (s.tokensThisHour + s.budgetRemainingHour)) * 100) : 0;
|
|
480
677
|
const dayPct = s.tokensToday && s.budgetRemainingDay != null ? Math.min(100, (s.tokensToday / (s.tokensToday + s.budgetRemainingDay)) * 100) : 0;
|
|
481
678
|
|
|
482
679
|
el.innerHTML = `
|
|
483
680
|
<div class="llm-grid">
|
|
484
|
-
<div class="llm-stat"><div class="llm-stat-value">${s.totalCalls ?? 0}</div><div class="llm-stat-label">
|
|
485
|
-
<div class="llm-stat"><div class="llm-stat-value">${formatK(s.totalTokens ?? 0)}</div><div class="llm-stat-label">Tokens
|
|
486
|
-
<div class="llm-stat"><div class="llm-stat-value">${s.cacheHitRate != null ? (s.cacheHitRate * 100).toFixed(0) + '%' : '--'}</div><div class="llm-stat-label">Cache
|
|
487
|
-
<div class="llm-stat"><div class="llm-stat-value">${s.callsThisHour ?? 0}</div><div class="llm-stat-label">
|
|
488
|
-
<div class="llm-stat"><div class="llm-stat-value">${s.averageLatencyMs ? Math.round(s.averageLatencyMs) + 'ms' : '--'}</div><div class="llm-stat-label">
|
|
489
|
-
<div class="llm-stat"><div class="llm-stat-value">${s.errors ?? 0}</div><div class="llm-stat-label">Fehler</div></div>
|
|
681
|
+
<div class="llm-stat"><div class="llm-stat-value">${s.totalCalls ?? 0}</div><div class="llm-stat-label">KI-Anfragen</div></div>
|
|
682
|
+
<div class="llm-stat"><div class="llm-stat-value">${formatK(s.totalTokens ?? 0)}</div><div class="llm-stat-label">Tokens verbraucht</div></div>
|
|
683
|
+
<div class="llm-stat"><div class="llm-stat-value">${s.cacheHitRate != null ? (s.cacheHitRate * 100).toFixed(0) + '%' : '--'}</div><div class="llm-stat-label">Cache Treffer</div></div>
|
|
684
|
+
<div class="llm-stat"><div class="llm-stat-value">${s.callsThisHour ?? 0}</div><div class="llm-stat-label">Anfragen/Stunde</div></div>
|
|
685
|
+
<div class="llm-stat"><div class="llm-stat-value">${s.averageLatencyMs ? Math.round(s.averageLatencyMs) + 'ms' : '--'}</div><div class="llm-stat-label">Antwortzeit</div></div>
|
|
686
|
+
<div class="llm-stat"><div class="llm-stat-value">${s.errors ?? 0}</div><div class="llm-stat-label">KI-Fehler</div></div>
|
|
490
687
|
</div>
|
|
491
688
|
<div style="margin-top:12px">
|
|
492
|
-
<div style="display:flex;justify-content:space-between;font-size:10px;color:var(--text-dim);margin-bottom:2px"><span>Budget Stunde</span><span>${formatK(s.tokensThisHour ?? 0)} / ${formatK((s.tokensThisHour ?? 0) + (s.budgetRemainingHour ?? 0))}</span></div>
|
|
689
|
+
<div style="display:flex;justify-content:space-between;font-size:10px;color:var(--text-dim);margin-bottom:2px"><span>Budget pro Stunde</span><span>${formatK(s.tokensThisHour ?? 0)} / ${formatK((s.tokensThisHour ?? 0) + (s.budgetRemainingHour ?? 0))}</span></div>
|
|
493
690
|
<div class="llm-bar"><div class="llm-bar-fill" style="width:${hourPct}%;background:${hourPct > 80 ? 'var(--red)' : hourPct > 50 ? 'var(--orange)' : 'var(--cyan)'}"></div></div>
|
|
494
|
-
<div style="display:flex;justify-content:space-between;font-size:10px;color:var(--text-dim);margin:8px 0 2px"><span>Budget Tag</span><span>${formatK(s.tokensToday ?? 0)} / ${formatK((s.tokensToday ?? 0) + (s.budgetRemainingDay ?? 0))}</span></div>
|
|
691
|
+
<div style="display:flex;justify-content:space-between;font-size:10px;color:var(--text-dim);margin:8px 0 2px"><span>Budget pro Tag</span><span>${formatK(s.tokensToday ?? 0)} / ${formatK((s.tokensToday ?? 0) + (s.budgetRemainingDay ?? 0))}</span></div>
|
|
495
692
|
<div class="llm-bar"><div class="llm-bar-fill" style="width:${dayPct}%;background:${dayPct > 80 ? 'var(--red)' : dayPct > 50 ? 'var(--orange)' : 'var(--green)'}"></div></div>
|
|
496
693
|
</div>
|
|
497
|
-
${s.model ? `<div style="margin-top:10px;font-size:10px;color:var(--text-dim)">
|
|
498
|
-
${s.providers ? `<div style="margin-top:4px;font-size:10px;color:var(--text-dim)">
|
|
694
|
+
${s.model ? `<div style="margin-top:10px;font-size:10px;color:var(--text-dim)">KI-Modell: ${s.model}</div>` : ''}
|
|
695
|
+
${s.providers ? `<div style="margin-top:4px;font-size:10px;color:var(--text-dim)">Anbieter: ${s.providers.map(p => `<span style="color:${p.available ? 'var(--green)' : 'var(--red)'}">${p.name}</span>`).join(', ')}</div>` : ''}
|
|
499
696
|
`;
|
|
500
697
|
}
|
|
501
698
|
|
|
699
|
+
// ── Error Log ─────────────────────────────────────────────
|
|
700
|
+
function renderErrors() {
|
|
701
|
+
const el = document.getElementById('errorFeed');
|
|
702
|
+
const cnt = document.getElementById('errorCount');
|
|
703
|
+
const d = state.errors;
|
|
704
|
+
if (!d) { el.innerHTML = '<div class="empty">Keine Fehler — alles läuft!</div>'; cnt.textContent = ''; return; }
|
|
705
|
+
const errors = d.errors || d;
|
|
706
|
+
if (!Array.isArray(errors) || !errors.length) { el.innerHTML = '<div class="empty">Keine Fehler — alles läuft!</div>'; cnt.textContent = ''; return; }
|
|
707
|
+
cnt.textContent = `(${errors.length})`;
|
|
708
|
+
el.innerHTML = errors.slice(0, 20).map(e => {
|
|
709
|
+
const time = e.timestamp ? new Date(e.timestamp).toLocaleTimeString('de-DE', { hour:'2-digit', minute:'2-digit' }) : '';
|
|
710
|
+
const level = e.resolved ? 'warn' : 'err';
|
|
711
|
+
const msg = e.message || e.error_message || e.title || 'Unbekannter Fehler';
|
|
712
|
+
const source = e.source || e.project || '';
|
|
713
|
+
return `<div class="error-item">
|
|
714
|
+
<span class="thought-time">${time}</span>
|
|
715
|
+
<span class="error-level ${level}">${e.resolved ? 'Gelöst' : 'Fehler'}</span>
|
|
716
|
+
${source ? `<span style="font-size:11px;color:var(--cyan);min-width:60px">${escHtml(source)}</span>` : ''}
|
|
717
|
+
<span class="error-msg">${escHtml(msg)}</span>
|
|
718
|
+
</div>`;
|
|
719
|
+
}).join('');
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// ── Self-Mod Activity ─────────────────────────────────────
|
|
723
|
+
function renderSelfMod() {
|
|
724
|
+
const d = state.selfmod;
|
|
725
|
+
if (!d || !d.status) {
|
|
726
|
+
setText('sm-total', 0); setText('sm-applied', 0); setText('sm-testing', 0); setText('sm-failed', 0);
|
|
727
|
+
document.getElementById('selfModFeed').innerHTML = '<div class="empty">Brain hat sich noch nicht selbst verbessert</div>';
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
const s = d.status;
|
|
731
|
+
setText('sm-total', s.totalModifications || 0);
|
|
732
|
+
const byStatus = s.byStatus || {};
|
|
733
|
+
setText('sm-applied', byStatus.applied || 0);
|
|
734
|
+
setText('sm-testing', (byStatus.testing || 0) + (byStatus.generating || 0));
|
|
735
|
+
setText('sm-failed', (byStatus.failed || 0) + (byStatus.rejected || 0));
|
|
736
|
+
|
|
737
|
+
const hist = d.history || [];
|
|
738
|
+
const el = document.getElementById('selfModFeed');
|
|
739
|
+
if (!hist.length) { el.innerHTML = '<div class="empty">Noch keine Verbesserungen</div>'; return; }
|
|
740
|
+
el.innerHTML = hist.map(m => {
|
|
741
|
+
const statusMap = { applied:'Angewendet', testing:'Wird getestet', ready:'Bereit', proposed:'Vorgeschlagen', generating:'Generiert', failed:'Fehlgeschlagen', rejected:'Abgelehnt', rolled_back:'Zurückgesetzt' };
|
|
742
|
+
const statusClass = m.status === 'applied' ? 'mod-applied' : m.status === 'testing' || m.status === 'generating' ? 'mod-testing' : m.status === 'failed' || m.status === 'rejected' ? 'mod-failed' : 'mod-proposed';
|
|
743
|
+
const time = m.created_at ? new Date(m.created_at).toLocaleString('de-DE', { day:'2-digit', month:'2-digit', hour:'2-digit', minute:'2-digit' }) : '';
|
|
744
|
+
return `<div class="mod-item">
|
|
745
|
+
<span class="mod-status ${statusClass}">${statusMap[m.status] || m.status}</span>
|
|
746
|
+
<div style="flex:1">
|
|
747
|
+
<div class="mod-title">${escHtml(m.title || 'Verbesserung')}</div>
|
|
748
|
+
<div class="mod-detail">${escHtml(m.problem_description || '')} ${time ? `<span style="float:right">${time}</span>` : ''}</div>
|
|
749
|
+
${m.target_files?.length ? `<div style="font-size:10px;color:var(--text-dim);margin-top:2px">Dateien: ${m.target_files.map(f => f.split('/').pop()).join(', ')}</div>` : ''}
|
|
750
|
+
</div>
|
|
751
|
+
</div>`;
|
|
752
|
+
}).join('');
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
// ── Mission Tracker ───────────────────────────────────────
|
|
756
|
+
function renderMissions() {
|
|
757
|
+
const d = state.missions;
|
|
758
|
+
if (!d || !d.status) {
|
|
759
|
+
setText('ms-active', 0); setText('ms-completed', 0); setText('ms-sources', 0);
|
|
760
|
+
document.getElementById('missionList').innerHTML = '<div class="empty">Keine aktiven Missionen</div>';
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
const s = d.status;
|
|
764
|
+
setText('ms-active', s.activeMissions || 0);
|
|
765
|
+
setText('ms-completed', s.completedMissions || 0);
|
|
766
|
+
setText('ms-sources', s.totalSources || 0);
|
|
767
|
+
|
|
768
|
+
const list = d.list || [];
|
|
769
|
+
const el = document.getElementById('missionList');
|
|
770
|
+
if (!list.length) { el.innerHTML = '<div class="empty">Keine aktiven Missionen</div>'; return; }
|
|
771
|
+
const phases = ['decomposing','gathering','hypothesizing','analyzing','synthesizing'];
|
|
772
|
+
const phaseLabels = { decomposing:'Aufteilen', gathering:'Sammeln', hypothesizing:'Ideen bilden', analyzing:'Analysieren', synthesizing:'Zusammenfassen' };
|
|
773
|
+
el.innerHTML = list.slice(0, 10).map(m => {
|
|
774
|
+
const statusIdx = phases.indexOf(m.status);
|
|
775
|
+
const phaseHtml = phases.map((p, i) => {
|
|
776
|
+
const cls = m.status === 'complete' ? 'done' : m.status === 'failed' ? (i <= statusIdx ? 'fail' : '') : (i < statusIdx ? 'done' : i === statusIdx ? 'running' : '');
|
|
777
|
+
return `<div class="mission-phase ${cls}" title="${phaseLabels[p] || p}"></div>`;
|
|
778
|
+
}).join('');
|
|
779
|
+
const statusText = m.status === 'complete' ? 'Fertig' : m.status === 'failed' ? 'Fehlgeschlagen' : phaseLabels[m.status] || m.status;
|
|
780
|
+
const depth = m.depth === 'deep' ? 'Tiefgehend' : m.depth === 'quick' ? 'Schnell' : 'Standard';
|
|
781
|
+
return `<div class="mission-card">
|
|
782
|
+
<div class="mission-topic">${escHtml(m.topic || 'Mission')}</div>
|
|
783
|
+
<div style="font-size:11px;color:var(--text-dim)">${statusText} — ${depth} — ${m.sourceCount || 0} Quellen</div>
|
|
784
|
+
<div class="mission-phases">${phaseHtml}</div>
|
|
785
|
+
<div class="mission-meta">
|
|
786
|
+
<span>Gestartet: ${m.createdAt ? new Date(m.createdAt).toLocaleString('de-DE', { day:'2-digit', month:'2-digit', hour:'2-digit', minute:'2-digit' }) : '?'}</span>
|
|
787
|
+
${m.completedAt ? `<span>Fertig: ${new Date(m.completedAt).toLocaleString('de-DE', { day:'2-digit', month:'2-digit', hour:'2-digit', minute:'2-digit' })}</span>` : ''}
|
|
788
|
+
</div>
|
|
789
|
+
</div>`;
|
|
790
|
+
}).join('');
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
// ── Knowledge Growth ──────────────────────────────────────
|
|
794
|
+
function renderKnowledge() {
|
|
795
|
+
const d = state.knowledge;
|
|
796
|
+
if (!d) return;
|
|
797
|
+
// Totals
|
|
798
|
+
if (d.totals) {
|
|
799
|
+
setText('kn-principles', d.totals.principles || d.totals.rules || 0);
|
|
800
|
+
setText('kn-hypotheses', d.totals.hypotheses || 0);
|
|
801
|
+
setText('kn-experiments', d.totals.experiments || 0);
|
|
802
|
+
setText('kn-errors', d.totals.errorsSolved || d.totals.solutions || 0);
|
|
803
|
+
}
|
|
804
|
+
// Time series chart
|
|
805
|
+
const ts = d.timeSeries || [];
|
|
806
|
+
if (!ts.length) return;
|
|
807
|
+
drawKnowledgeChart(ts);
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
function drawKnowledgeChart(data) {
|
|
811
|
+
const svg = document.getElementById('knowledgeSvg');
|
|
812
|
+
if (!svg || !data.length) return;
|
|
813
|
+
const w = 600, h = 180, pad = 30;
|
|
814
|
+
const maxVal = Math.max(1, ...data.map(d => Math.max(d.errors || 0, d.solutions || 0)));
|
|
815
|
+
const xStep = (w - pad * 2) / Math.max(1, data.length - 1);
|
|
816
|
+
|
|
817
|
+
function path(key, color) {
|
|
818
|
+
const pts = data.map((d, i) => {
|
|
819
|
+
const x = pad + i * xStep;
|
|
820
|
+
const y = h - pad - ((d[key] || 0) / maxVal) * (h - pad * 2);
|
|
821
|
+
return `${i === 0 ? 'M' : 'L'}${x},${y}`;
|
|
822
|
+
}).join(' ');
|
|
823
|
+
return `<path d="${pts}" fill="none" stroke="${color}" stroke-width="2" stroke-linecap="round"/>`;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// Grid lines
|
|
827
|
+
let grid = '';
|
|
828
|
+
for (let i = 0; i <= 4; i++) {
|
|
829
|
+
const y = pad + (i / 4) * (h - pad * 2);
|
|
830
|
+
grid += `<line x1="${pad}" y1="${y}" x2="${w-pad}" y2="${y}" stroke="rgba(100,140,255,0.08)" stroke-width="1"/>`;
|
|
831
|
+
}
|
|
832
|
+
// X labels (show every few)
|
|
833
|
+
let labels = '';
|
|
834
|
+
const step = Math.max(1, Math.floor(data.length / 7));
|
|
835
|
+
for (let i = 0; i < data.length; i += step) {
|
|
836
|
+
const x = pad + i * xStep;
|
|
837
|
+
const d = data[i].date || '';
|
|
838
|
+
labels += `<text x="${x}" y="${h-5}" text-anchor="middle" fill="var(--text-dim)" font-size="9">${d.slice(5)}</text>`;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
svg.innerHTML = grid + labels + path('errors', '#ff4466') + path('solutions', '#00ff88');
|
|
842
|
+
}
|
|
843
|
+
|
|
502
844
|
// ── Render: Ecosystem ─────────────────────────────────────
|
|
503
845
|
function renderEcosystem() {
|
|
504
846
|
const eco = state.ecosystem; if (!eco) return;
|
|
505
847
|
const hb = document.getElementById('healthBadge');
|
|
506
848
|
const h = eco.health;
|
|
507
849
|
if (h) {
|
|
508
|
-
hb.textContent = h.status === 'healthy' ? '
|
|
850
|
+
hb.textContent = h.status === 'healthy' ? 'Gesund' : h.status === 'degraded' ? 'Eingeschränkt' : 'Kritisch';
|
|
509
851
|
hb.className = 'badge ' + (h.status === 'healthy' ? 'badge-ok' : h.status === 'degraded' ? 'badge-warn' : 'badge-err');
|
|
510
852
|
const score = h.score || 0;
|
|
511
853
|
document.getElementById('gaugeArc').setAttribute('stroke-dasharray', `${(score / 100) * 172} 172`);
|
|
512
854
|
document.getElementById('gaugeArc').setAttribute('stroke', score > 70 ? 'var(--green)' : score > 40 ? 'var(--orange)' : 'var(--red)');
|
|
513
855
|
document.getElementById('gaugeValue').textContent = Math.round(score);
|
|
514
856
|
}
|
|
515
|
-
// Brain cards
|
|
516
857
|
const container = document.getElementById('brainCards');
|
|
517
858
|
const brainNames = ['brain', 'trading-brain', 'marketing-brain'];
|
|
518
859
|
const labels = { brain:'Brain', 'trading-brain':'Trading-Brain', 'marketing-brain':'Marketing-Brain' };
|
|
860
|
+
const descs = { brain:'Lernt aus Fehlern, verbessert Code', 'trading-brain':'Paper Trading, Signal-Analyse', 'marketing-brain':'Content-Strategie, Engagement' };
|
|
519
861
|
const classes = { brain:'brain-brain', 'trading-brain':'brain-trading', 'marketing-brain':'brain-marketing' };
|
|
520
862
|
container.innerHTML = brainNames.map(name => {
|
|
521
863
|
const b = (eco.brains || []).find(x => x.name === name) || { name, available:false };
|
|
522
864
|
return `<div class="card brain-card ${classes[name]}">
|
|
523
865
|
<div class="brain-status"><span class="dot ${b.available ? 'dot-on' : 'dot-off'}"></span><span class="brain-name">${labels[name]}</span></div>
|
|
866
|
+
<div style="font-size:11px;color:var(--text-dim);margin-bottom:6px">${descs[name]}</div>
|
|
524
867
|
<div class="brain-meta">
|
|
525
868
|
<span>${b.available ? 'Online' : 'Offline'}</span>
|
|
526
869
|
${b.version ? `<span>v${b.version}</span>` : ''}${b.pid ? `<span>PID ${b.pid}</span>` : ''}
|
|
527
|
-
${b.uptime ? `<span>${fmtUp(b.uptime)}</span>` : ''}${b.methods ? `<span>${b.methods}
|
|
870
|
+
${b.uptime ? `<span>${fmtUp(b.uptime)}</span>` : ''}${b.methods ? `<span>${b.methods} Tools</span>` : ''}
|
|
528
871
|
</div></div>`;
|
|
529
872
|
}).join('');
|
|
530
873
|
drawPeerGraph(eco.brains || []);
|
|
@@ -533,7 +876,7 @@ function renderEcosystem() {
|
|
|
533
876
|
|
|
534
877
|
function renderCorrelations(c) {
|
|
535
878
|
const el = document.getElementById('correlationList');
|
|
536
|
-
if (!c.length) { el.innerHTML = '<div class="empty">
|
|
879
|
+
if (!c.length) { el.innerHTML = '<div class="empty">Noch keine Zusammenhänge gefunden</div>'; return; }
|
|
537
880
|
el.innerHTML = c.slice(0, 10).map(x => `<div style="padding:6px 0;border-bottom:1px solid var(--border);font-size:12px">
|
|
538
881
|
<span style="color:var(--cyan)">${x.sourceA}/${x.eventA}</span> <span style="color:var(--text-dim)">↔</span>
|
|
539
882
|
<span style="color:var(--magenta)">${x.sourceB}/${x.eventB}</span>
|
|
@@ -555,7 +898,7 @@ function renderEngines() {
|
|
|
555
898
|
|
|
556
899
|
function renderGrid(id, engines, showBrain = false) {
|
|
557
900
|
const el = document.getElementById(id); if (!el) return;
|
|
558
|
-
if (!engines.length) { el.innerHTML = '<div class="empty">
|
|
901
|
+
if (!engines.length) { el.innerHTML = '<div class="empty">Keine Engines</div>'; return; }
|
|
559
902
|
el.innerHTML = engines.map(e => {
|
|
560
903
|
const st = e.lastActivity && (Date.now() - e.lastActivity < 60000) ? 'active' : e.thoughtCount > 0 ? 'idle' : 'off';
|
|
561
904
|
const lbl = showBrain && e.brain ? `${e.brain}/${e.engine}` : (e.engine || e.name || '?');
|
|
@@ -581,48 +924,82 @@ function updateLearningPipeline(engines) {
|
|
|
581
924
|
// ── Render: Analytics ─────────────────────────────────────
|
|
582
925
|
function renderAnalytics() {
|
|
583
926
|
const a = state.analytics; if (!a) return;
|
|
584
|
-
if (a.trading) {
|
|
585
|
-
|
|
927
|
+
if (a.trading) {
|
|
928
|
+
const t = a.trading;
|
|
929
|
+
setText('tp-signals', num(t.signals));
|
|
930
|
+
setText('tp-trades', num(t.trades));
|
|
931
|
+
setText('tradingWinRate', t.winRate ? (Number(t.winRate)*100).toFixed(1)+'%' : '--');
|
|
932
|
+
setText('tradingEquity', t.equity ? '$'+Number(t.equity).toLocaleString() : '--');
|
|
933
|
+
setText('tradingPositions', num(t.positions));
|
|
934
|
+
}
|
|
935
|
+
if (a.marketing) {
|
|
936
|
+
const m = a.marketing;
|
|
937
|
+
setText('mp-posts', num(m.posts));
|
|
938
|
+
setText('mp-engagement', num(m.engagement));
|
|
939
|
+
setText('marketingCampaigns', num(m.campaigns));
|
|
940
|
+
}
|
|
586
941
|
}
|
|
942
|
+
function num(v) { if (v == null) return 0; if (typeof v === 'object') return v.total || v.count || v.length || Object.keys(v).length || 0; return Number(v) || 0; }
|
|
587
943
|
|
|
588
944
|
// ── Render: Watchdog ──────────────────────────────────────
|
|
589
945
|
function renderWatchdog() {
|
|
590
946
|
const el = document.getElementById('watchdogCard');
|
|
591
947
|
const d = state.watchdog || [];
|
|
592
|
-
if (!d.length) { el.innerHTML = '<div class="empty">
|
|
593
|
-
el.innerHTML = `<table class="tbl"><thead><tr><th>
|
|
594
|
-
d.map(x => `<tr><td>${x.name}</td><td><span class="dot ${x.running?'dot-on':'dot-off'}" style="margin-right:6px"></span>${x.running?(x.healthy?'
|
|
948
|
+
if (!d.length) { el.innerHTML = '<div class="empty">Kein Watchdog konfiguriert</div>'; return; }
|
|
949
|
+
el.innerHTML = `<table class="tbl"><thead><tr><th>Dienst</th><th>Status</th><th>PID</th><th>Laufzeit</th><th>Neustarts</th></tr></thead><tbody>` +
|
|
950
|
+
d.map(x => `<tr><td>${x.name}</td><td><span class="dot ${x.running?'dot-on':'dot-off'}" style="margin-right:6px"></span>${x.running?(x.healthy?'Gesund':'Problem'):'Gestoppt'}</td><td>${x.pid||'-'}</td><td>${x.uptime?fmtUp(x.uptime/1000):'-'}</td><td>${x.restarts||0}</td></tr>`).join('') + '</tbody></table>';
|
|
595
951
|
const hc = document.getElementById('healthCheckCard');
|
|
596
952
|
const ok = d.every(x => x.running && x.healthy), off = d.filter(x => !x.running).length;
|
|
597
|
-
hc.innerHTML = `<div style="display:flex;gap:20px;align-items:center"><span style="font-size:32px">${ok?'✅':'⚠'}</span><div><div style="font-size:15px;font-weight:600;color:${ok?'var(--green)':'var(--orange)'}">${ok?'
|
|
953
|
+
hc.innerHTML = `<div style="display:flex;gap:20px;align-items:center"><span style="font-size:32px">${ok?'✅':'⚠'}</span><div><div style="font-size:15px;font-weight:600;color:${ok?'var(--green)':'var(--orange)'}">${ok?'Alle Systeme laufen':`${off} Dienste offline`}</div><div style="font-size:12px;color:var(--text-dim);margin-top:4px">${d.length} Dienste überwacht</div></div></div>`;
|
|
598
954
|
}
|
|
599
955
|
|
|
600
956
|
function renderPlugins() {
|
|
601
957
|
const el = document.getElementById('pluginCard');
|
|
602
958
|
const p = state.plugins || [];
|
|
603
|
-
if (!p.length) { el.innerHTML = '<div class="empty">
|
|
604
|
-
el.innerHTML = p.map(x => `<div style="padding:8px 0;border-bottom:1px solid var(--border);display:flex;justify-content:space-between"><div><div style="font-weight:600;font-size:13px">${x.name}</div><div style="font-size:11px;color:var(--text-dim)">${x.description||''} v${x.version}</div></div><span class="tag tag-green">${x.status||'
|
|
959
|
+
if (!p.length) { el.innerHTML = '<div class="empty">Keine Plugins installiert</div>'; return; }
|
|
960
|
+
el.innerHTML = p.map(x => `<div style="padding:8px 0;border-bottom:1px solid var(--border);display:flex;justify-content:space-between"><div><div style="font-weight:600;font-size:13px">${x.name}</div><div style="font-size:11px;color:var(--text-dim)">${x.description||''} v${x.version}</div></div><span class="tag tag-green">${x.status||'Geladen'}</span></div>`).join('');
|
|
605
961
|
}
|
|
606
962
|
|
|
607
963
|
// ── Render: Borg ──────────────────────────────────────────
|
|
608
964
|
function renderBorg() {
|
|
609
965
|
const b = state.borg;
|
|
610
966
|
const sEl = document.getElementById('borgStatus'), btn = document.getElementById('borgToggle'), det = document.getElementById('borgDetails'), hist = document.getElementById('borgHistory');
|
|
611
|
-
if (!b || !b.status) { sEl.textContent = '
|
|
967
|
+
if (!b || !b.status) { sEl.textContent = 'Nicht verfügbar'; btn.disabled = true; return; }
|
|
612
968
|
const s = b.status;
|
|
613
|
-
sEl.innerHTML = `<span class="dot ${s.enabled?'dot-on':'dot-off'}" style="margin-right:6px"></span>${s.enabled?'
|
|
614
|
-
btn.disabled = false; btn.className = s.enabled ? 'btn btn-active' : 'btn'; btn.textContent = s.enabled ? '
|
|
615
|
-
det.innerHTML = `<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px;font-size:12px;margin-top:8px"><div><span style="color:var(--text-dim)">Syncs</span><br><strong>${s.totalSyncs||0}</strong></div><div><span style="color:var(--text-dim)">
|
|
969
|
+
sEl.innerHTML = `<span class="dot ${s.enabled?'dot-on':'dot-off'}" style="margin-right:6px"></span>${s.enabled?'Aktiv':'Deaktiviert'} — ${s.mode || 'Standard'}`;
|
|
970
|
+
btn.disabled = false; btn.className = s.enabled ? 'btn btn-active' : 'btn'; btn.textContent = s.enabled ? 'Deaktivieren' : 'Aktivieren';
|
|
971
|
+
det.innerHTML = `<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px;font-size:12px;margin-top:8px"><div><span style="color:var(--text-dim)">Syncs</span><br><strong>${s.totalSyncs||0}</strong></div><div><span style="color:var(--text-dim)">Gesendet</span><br><strong>${s.totalSent||0}</strong></div><div><span style="color:var(--text-dim)">Empfangen</span><br><strong>${s.totalReceived||0}</strong></div></div>`;
|
|
616
972
|
const h = b.history || [];
|
|
617
|
-
if (!h.length) { hist.innerHTML = '<div class="empty">
|
|
618
|
-
hist.innerHTML = `<table class="tbl"><thead><tr><th>Zeit</th><th>Richtung</th><th>
|
|
619
|
-
h.slice(-15).reverse().map(x => `<tr><td>${new Date(x.timestamp).toLocaleTimeString('de-DE')}</td><td>${x.direction==='sent'?'⬆':'⬇'} ${x.direction}</td><td>${x.peer}</td><td>${x.itemCount}</td><td>${x.accepted}</td></tr>`).join('') + '</tbody></table>';
|
|
973
|
+
if (!h.length) { hist.innerHTML = '<div class="empty">Noch kein Sync passiert</div>'; return; }
|
|
974
|
+
hist.innerHTML = `<table class="tbl"><thead><tr><th>Zeit</th><th>Richtung</th><th>Partner</th><th>Elemente</th><th>Akzeptiert</th></tr></thead><tbody>` +
|
|
975
|
+
h.slice(-15).reverse().map(x => `<tr><td>${new Date(x.timestamp).toLocaleTimeString('de-DE')}</td><td>${x.direction==='sent'?'⬆':'⬇'} ${x.direction==='sent'?'Gesendet':'Empfangen'}</td><td>${x.peer}</td><td>${x.itemCount}</td><td>${x.accepted}</td></tr>`).join('') + '</tbody></table>';
|
|
620
976
|
}
|
|
621
977
|
document.getElementById('borgToggle').addEventListener('click', async () => {
|
|
622
978
|
if (!state.borg?.status) return;
|
|
623
979
|
try { await fetch('/api/borg/toggle', { method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({enabled:!state.borg.status.enabled}) }); } catch {}
|
|
624
980
|
});
|
|
625
981
|
|
|
982
|
+
// ── Quick Actions ─────────────────────────────────────────
|
|
983
|
+
document.querySelectorAll('.action-btn').forEach(btn => {
|
|
984
|
+
btn.addEventListener('click', async () => {
|
|
985
|
+
const action = btn.dataset.action;
|
|
986
|
+
btn.classList.add('running');
|
|
987
|
+
btn.querySelector('.action-icon').textContent = '\u23F3';
|
|
988
|
+
try {
|
|
989
|
+
const res = await fetch('/api/action', { method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({ action }) });
|
|
990
|
+
const data = await res.json();
|
|
991
|
+
btn.querySelector('.action-icon').textContent = data.ok ? '\u2705' : '\u274C';
|
|
992
|
+
} catch {
|
|
993
|
+
btn.querySelector('.action-icon').textContent = '\u274C';
|
|
994
|
+
}
|
|
995
|
+
setTimeout(() => {
|
|
996
|
+
btn.classList.remove('running');
|
|
997
|
+
const icons = { 'learning-cycle':'\u{1F504}', 'borg-sync':'\u{1F47E}', 'tech-scan':'\u{1F4E1}', 'health-check':'\u{1F3E5}' };
|
|
998
|
+
btn.querySelector('.action-icon').textContent = icons[action] || '\u26A1';
|
|
999
|
+
}, 2000);
|
|
1000
|
+
});
|
|
1001
|
+
});
|
|
1002
|
+
|
|
626
1003
|
// ── Canvas: Peer Graph ────────────────────────────────────
|
|
627
1004
|
function drawPeerGraph(brains) {
|
|
628
1005
|
const canvas = document.getElementById('peerCanvas'); if (!canvas) return;
|
|
@@ -648,31 +1025,96 @@ function drawPeerGraph(brains) {
|
|
|
648
1025
|
}
|
|
649
1026
|
}
|
|
650
1027
|
|
|
651
|
-
// ── Canvas: Cross-Brain Animation
|
|
1028
|
+
// ── Canvas: Cross-Brain + Borg Animation ──────────────────
|
|
652
1029
|
let particles = [];
|
|
1030
|
+
let borgPackets = [];
|
|
1031
|
+
function spawnBorgPacket(src, dst, items) {
|
|
1032
|
+
borgPackets.push({ src, dst, items, progress: 0, speed: 0.004 + Math.random() * 0.003 });
|
|
1033
|
+
}
|
|
1034
|
+
let lastBorgHistLen = 0;
|
|
1035
|
+
function checkBorgPackets() {
|
|
1036
|
+
const h = state.borg?.history || [];
|
|
1037
|
+
if (h.length > lastBorgHistLen && lastBorgHistLen > 0) {
|
|
1038
|
+
const newEntries = h.slice(0, h.length - lastBorgHistLen);
|
|
1039
|
+
for (const e of newEntries) {
|
|
1040
|
+
const src = e.direction === 'sent' ? 'brain' : e.peer;
|
|
1041
|
+
const dst = e.direction === 'sent' ? e.peer : 'brain';
|
|
1042
|
+
spawnBorgPacket(src, dst, e.itemCount || 1);
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
lastBorgHistLen = h.length;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
653
1048
|
function initCrossBrainCanvas() {
|
|
654
1049
|
const canvas = document.getElementById('crossBrainCanvas'); if (!canvas) return;
|
|
655
1050
|
function resize() { const r=canvas.parentElement.getBoundingClientRect(); canvas.width=r.width*2; canvas.height=r.height*2; }
|
|
656
1051
|
resize(); window.addEventListener('resize', resize);
|
|
657
1052
|
const positions = {};
|
|
658
|
-
function getPos() {
|
|
659
|
-
|
|
1053
|
+
function getPos() {
|
|
1054
|
+
const w=canvas.width/2, h=canvas.height/2;
|
|
1055
|
+
positions.brain={x:w/2,y:55,color:'#00e5ff',label:'Brain'};
|
|
1056
|
+
positions['trading-brain']={x:w*.2,y:h-55,color:'#00ff88',label:'Trading'};
|
|
1057
|
+
positions['marketing-brain']={x:w*.8,y:h-55,color:'#ff44cc',label:'Marketing'};
|
|
1058
|
+
}
|
|
1059
|
+
function spawnMsg() {
|
|
1060
|
+
const n=Object.keys(positions); if(n.length<2)return;
|
|
1061
|
+
let s=n[Math.random()*n.length|0],d=n[Math.random()*n.length|0];
|
|
1062
|
+
while(d===s)d=n[Math.random()*n.length|0];
|
|
1063
|
+
const a=positions[s],b=positions[d];
|
|
1064
|
+
particles.push({x:a.x,y:a.y,tx:b.x,ty:b.y,color:a.color,progress:0,speed:.008+Math.random()*.012});
|
|
1065
|
+
}
|
|
1066
|
+
|
|
660
1067
|
function draw() {
|
|
661
|
-
const ctx=canvas.getContext('2d'),w=canvas.width/2,h=canvas.height/2;
|
|
1068
|
+
const ctx=canvas.getContext('2d'),w=canvas.width/2,h=canvas.height/2;
|
|
1069
|
+
ctx.save(); ctx.scale(2,2); ctx.clearRect(0,0,w,h); getPos();
|
|
1070
|
+
checkBorgPackets();
|
|
1071
|
+
const borgEnabled = state.borg?.status?.enabled;
|
|
1072
|
+
const borgSent = state.borg?.status?.totalSent || 0;
|
|
1073
|
+
const borgRecv = state.borg?.status?.totalReceived || 0;
|
|
662
1074
|
const n=Object.keys(positions);
|
|
663
|
-
for(let i=0;i<n.length;i++) for(let j=i+1;j<n.length;j++) {
|
|
664
|
-
|
|
1075
|
+
for(let i=0;i<n.length;i++) for(let j=i+1;j<n.length;j++) {
|
|
1076
|
+
const a=positions[n[i]],b=positions[n[j]];
|
|
1077
|
+
ctx.beginPath(); ctx.moveTo(a.x,a.y); ctx.lineTo(b.x,b.y);
|
|
1078
|
+
ctx.strokeStyle = borgEnabled ? 'rgba(170,136,255,0.15)' : 'rgba(100,140,255,0.1)';
|
|
1079
|
+
ctx.lineWidth = borgEnabled ? 2 : 1; ctx.stroke();
|
|
1080
|
+
if (borgEnabled) { ctx.beginPath(); ctx.setLineDash([4,8]); ctx.moveTo(a.x,a.y); ctx.lineTo(b.x,b.y); ctx.strokeStyle='rgba(170,136,255,0.12)'; ctx.lineWidth=1; ctx.stroke(); ctx.setLineDash([]); }
|
|
1081
|
+
}
|
|
1082
|
+
if(Math.random()<.05&&state.connected) spawnMsg();
|
|
665
1083
|
particles=particles.filter(p=>p.progress<1);
|
|
666
|
-
for(const p of particles) {
|
|
1084
|
+
for(const p of particles) {
|
|
1085
|
+
p.progress+=p.speed;
|
|
1086
|
+
const t=p.progress,cx=p.x+(p.tx-p.x)*t,cy=p.y+(p.ty-p.y)*t-Math.sin(t*Math.PI)*20;
|
|
667
1087
|
ctx.beginPath(); ctx.arc(cx,cy,3,0,Math.PI*2); ctx.fillStyle=p.color; ctx.fill();
|
|
668
|
-
|
|
1088
|
+
const g=ctx.createRadialGradient(cx,cy,0,cx,cy,6); g.addColorStop(0,p.color+'40'); g.addColorStop(1,p.color+'00');
|
|
1089
|
+
ctx.beginPath(); ctx.arc(cx,cy,6,0,Math.PI*2); ctx.fillStyle=g; ctx.fill();
|
|
669
1090
|
}
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
1091
|
+
borgPackets=borgPackets.filter(p=>p.progress<1);
|
|
1092
|
+
for(const bp of borgPackets) {
|
|
1093
|
+
bp.progress+=bp.speed;
|
|
1094
|
+
const t=bp.progress;
|
|
1095
|
+
const sp=positions[bp.src]||positions.brain, dp=positions[bp.dst]||positions.brain;
|
|
1096
|
+
const cx=sp.x+(dp.x-sp.x)*t, cy=sp.y+(dp.y-sp.y)*t-Math.sin(t*Math.PI)*35;
|
|
1097
|
+
ctx.beginPath(); ctx.arc(cx,cy,14,0,Math.PI*2);
|
|
1098
|
+
const gg=ctx.createRadialGradient(cx,cy,0,cx,cy,14);
|
|
1099
|
+
gg.addColorStop(0,'rgba(170,136,255,0.35)'); gg.addColorStop(1,'rgba(170,136,255,0)');
|
|
1100
|
+
ctx.fillStyle=gg; ctx.fill();
|
|
1101
|
+
ctx.beginPath(); ctx.arc(cx,cy,6,0,Math.PI*2); ctx.fillStyle='#aa88ff'; ctx.fill();
|
|
1102
|
+
ctx.beginPath(); ctx.arc(cx,cy,4,0,Math.PI*2); ctx.fillStyle='#ddccff'; ctx.fill();
|
|
1103
|
+
if(bp.items>0) { ctx.fillStyle='#fff'; ctx.font='bold 9px Segoe UI,sans-serif'; ctx.textAlign='center'; ctx.fillText(bp.items+'x',cx,cy-12); }
|
|
1104
|
+
}
|
|
1105
|
+
for(const[name,p]of Object.entries(positions)) {
|
|
1106
|
+
if(borgEnabled) { ctx.beginPath(); ctx.arc(p.x,p.y,24,0,Math.PI*2); ctx.strokeStyle='rgba(170,136,255,0.25)'; ctx.lineWidth=1; ctx.stroke(); }
|
|
1107
|
+
ctx.beginPath(); ctx.arc(p.x,p.y,28,0,Math.PI*2);
|
|
1108
|
+
const g=ctx.createRadialGradient(p.x,p.y,0,p.x,p.y,28); g.addColorStop(0,p.color+'25'); g.addColorStop(1,p.color+'00'); ctx.fillStyle=g; ctx.fill();
|
|
1109
|
+
ctx.beginPath(); ctx.arc(p.x,p.y,18,0,Math.PI*2);
|
|
1110
|
+
ctx.fillStyle=p.color+'15'; ctx.strokeStyle=p.color; ctx.lineWidth=2; ctx.fill(); ctx.stroke();
|
|
673
1111
|
ctx.beginPath(); ctx.arc(p.x,p.y,5,0,Math.PI*2); ctx.fillStyle=p.color; ctx.fill();
|
|
674
1112
|
ctx.fillStyle='#fff'; ctx.font='12px Segoe UI,sans-serif'; ctx.textAlign='center'; ctx.fillText(p.label,p.x,p.y+34);
|
|
675
1113
|
}
|
|
1114
|
+
if(borgEnabled) {
|
|
1115
|
+
ctx.fillStyle='rgba(170,136,255,0.7)'; ctx.font='10px Segoe UI,sans-serif'; ctx.textAlign='center';
|
|
1116
|
+
ctx.fillText(`BORG AKTIV \u2022 ${borgSent} gesendet \u2022 ${borgRecv} empfangen`, w/2, h-8);
|
|
1117
|
+
}
|
|
676
1118
|
ctx.restore(); requestAnimationFrame(draw);
|
|
677
1119
|
}
|
|
678
1120
|
draw();
|