nodebench-mcp 2.34.0 → 2.35.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/benchmarks/ambientBench.d.ts +27 -0
- package/dist/benchmarks/ambientBench.js +900 -0
- package/dist/benchmarks/ambientBench.js.map +1 -0
- package/dist/benchmarks/index.d.ts +1 -0
- package/dist/benchmarks/index.js +2 -0
- package/dist/benchmarks/index.js.map +1 -0
- package/dist/dashboard/operatingDashboardHtml.d.ts +23 -0
- package/dist/dashboard/operatingDashboardHtml.js +2036 -0
- package/dist/dashboard/operatingDashboardHtml.js.map +1 -0
- package/dist/dashboard/operatingServer.d.ts +23 -0
- package/dist/dashboard/operatingServer.js +704 -0
- package/dist/dashboard/operatingServer.js.map +1 -0
- package/dist/index.js +13 -4
- package/dist/index.js.map +1 -1
- package/dist/tools/dogfoodJudgeTools.d.ts +13 -0
- package/dist/tools/dogfoodJudgeTools.js +809 -0
- package/dist/tools/dogfoodJudgeTools.js.map +1 -0
- package/dist/tools/localDashboardTools.js +36 -0
- package/dist/tools/localDashboardTools.js.map +1 -1
- package/dist/tools/progressiveDiscoveryTools.js +1 -1
- package/dist/tools/progressiveDiscoveryTools.js.map +1 -1
- package/dist/tools/toolRegistry.js +166 -0
- package/dist/tools/toolRegistry.js.map +1 -1
- package/dist/toolsetRegistry.js +2 -0
- package/dist/toolsetRegistry.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,2036 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NodeBench MCP — Operating Dashboard HTML
|
|
3
|
+
*
|
|
4
|
+
* Self-contained single-page dashboard matching the NodeBench AI design system:
|
|
5
|
+
* glass cards, warm terracotta accent (#d97757), Manrope + JetBrains Mono typography.
|
|
6
|
+
*
|
|
7
|
+
* Sections:
|
|
8
|
+
* 1. Header Bar — branding, local badge, auto-refresh indicator, clock
|
|
9
|
+
* 2. Session Delta — hero card with strategy/competitors/contradictions/attention
|
|
10
|
+
* 3. Trajectory Score — sparkline + 7 dimension bars
|
|
11
|
+
* 4. Event Ledger — last 20 events with actor badges and type filters
|
|
12
|
+
* 5. Important Changes — status summary + actionable change cards
|
|
13
|
+
* 6. Path Replay — horizontal scrollable session path
|
|
14
|
+
* 7. Time Rollups — period tabs + metric grid with delta badges
|
|
15
|
+
* 8. Packet Readiness — staleness scores per packet type
|
|
16
|
+
* 9. Recent Actions — last 10 tracked actions
|
|
17
|
+
* 10. Footer — version, tool count, table count, auto-refresh interval
|
|
18
|
+
*
|
|
19
|
+
* Auto-refreshes every 10s with hash-based diffing (only re-renders changed sections).
|
|
20
|
+
* Fully responsive: single column mobile, 2-col grid desktop.
|
|
21
|
+
* No external JS/CSS libraries — inline styles with exact design tokens.
|
|
22
|
+
*/
|
|
23
|
+
export function getOperatingDashboardHtml() {
|
|
24
|
+
return `<!DOCTYPE html>
|
|
25
|
+
<html lang="en">
|
|
26
|
+
<head>
|
|
27
|
+
<meta charset="UTF-8">
|
|
28
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
29
|
+
<title>NodeBench — Operating Dashboard</title>
|
|
30
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
31
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
32
|
+
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
|
33
|
+
<style>
|
|
34
|
+
/* ── Reset ──────────────────────────────────────────────── */
|
|
35
|
+
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
36
|
+
|
|
37
|
+
/* ── Design Tokens ─────────────────────────────────────── */
|
|
38
|
+
:root {
|
|
39
|
+
--bg-primary: #09090b;
|
|
40
|
+
--bg-card: rgba(255, 255, 255, 0.12);
|
|
41
|
+
--bg-card-subtle: rgba(255, 255, 255, 0.04);
|
|
42
|
+
--border-card: rgba(255, 255, 255, 0.20);
|
|
43
|
+
--border-subtle: rgba(255, 255, 255, 0.08);
|
|
44
|
+
--border-accent: rgba(217, 119, 87, 0.5);
|
|
45
|
+
|
|
46
|
+
--accent: #d97757;
|
|
47
|
+
--accent-dim: rgba(217, 119, 87, 0.15);
|
|
48
|
+
--accent-glow: rgba(217, 119, 87, 0.25);
|
|
49
|
+
|
|
50
|
+
--text-primary: #fafafa;
|
|
51
|
+
--text-secondary: rgba(255, 255, 255, 0.7);
|
|
52
|
+
--text-muted: rgba(255, 255, 255, 0.4);
|
|
53
|
+
--text-dim: rgba(255, 255, 255, 0.25);
|
|
54
|
+
|
|
55
|
+
--emerald: #4ade80;
|
|
56
|
+
--amber: #fbbf24;
|
|
57
|
+
--red: #f87171;
|
|
58
|
+
--blue: #60a5fa;
|
|
59
|
+
--violet: #a78bfa;
|
|
60
|
+
--cyan: #22d3ee;
|
|
61
|
+
|
|
62
|
+
--radius-sm: 6px;
|
|
63
|
+
--radius-md: 8px;
|
|
64
|
+
--radius-lg: 12px;
|
|
65
|
+
--radius-xl: 16px;
|
|
66
|
+
|
|
67
|
+
--font-body: 'Manrope', system-ui, -apple-system, sans-serif;
|
|
68
|
+
--font-mono: 'JetBrains Mono', 'SF Mono', 'Cascadia Code', monospace;
|
|
69
|
+
|
|
70
|
+
--transition-fast: 150ms ease;
|
|
71
|
+
--transition-base: 250ms ease;
|
|
72
|
+
--transition-slow: 400ms ease;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* ── Base ───────────────────────────────────────────────── */
|
|
76
|
+
html { scroll-behavior: smooth; }
|
|
77
|
+
body {
|
|
78
|
+
background: var(--bg-primary);
|
|
79
|
+
color: var(--text-primary);
|
|
80
|
+
font-family: var(--font-body);
|
|
81
|
+
font-size: 14px;
|
|
82
|
+
line-height: 1.6;
|
|
83
|
+
-webkit-font-smoothing: antialiased;
|
|
84
|
+
-moz-osx-font-smoothing: grayscale;
|
|
85
|
+
min-height: 100vh;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* ── Scrollbar ─────────────────────────────────────────── */
|
|
89
|
+
::-webkit-scrollbar { width: 6px; height: 6px; }
|
|
90
|
+
::-webkit-scrollbar-track { background: transparent; }
|
|
91
|
+
::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.12); border-radius: 3px; }
|
|
92
|
+
::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.2); }
|
|
93
|
+
|
|
94
|
+
/* ── Layout ────────────────────────────────────────────── */
|
|
95
|
+
.dashboard {
|
|
96
|
+
max-width: 1200px;
|
|
97
|
+
margin: 0 auto;
|
|
98
|
+
padding: 24px 20px 80px;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.grid-2 {
|
|
102
|
+
display: grid;
|
|
103
|
+
grid-template-columns: 1fr;
|
|
104
|
+
gap: 16px;
|
|
105
|
+
}
|
|
106
|
+
@media (min-width: 768px) {
|
|
107
|
+
.grid-2 { grid-template-columns: 1fr 1fr; }
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.grid-4 {
|
|
111
|
+
display: grid;
|
|
112
|
+
grid-template-columns: repeat(2, 1fr);
|
|
113
|
+
gap: 10px;
|
|
114
|
+
}
|
|
115
|
+
@media (min-width: 640px) {
|
|
116
|
+
.grid-4 { grid-template-columns: repeat(4, 1fr); }
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* ── Section Header ────────────────────────────────────── */
|
|
120
|
+
.section-header {
|
|
121
|
+
font-size: 11px;
|
|
122
|
+
font-weight: 600;
|
|
123
|
+
text-transform: uppercase;
|
|
124
|
+
letter-spacing: 0.2em;
|
|
125
|
+
color: var(--text-muted);
|
|
126
|
+
margin-bottom: 12px;
|
|
127
|
+
font-family: var(--font-body);
|
|
128
|
+
}
|
|
129
|
+
.section-header .count {
|
|
130
|
+
font-family: var(--font-mono);
|
|
131
|
+
color: var(--text-dim);
|
|
132
|
+
font-weight: 400;
|
|
133
|
+
margin-left: 8px;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/* ── Glass Card ────────────────────────────────────────── */
|
|
137
|
+
.card {
|
|
138
|
+
background: var(--bg-card);
|
|
139
|
+
border: 1px solid var(--border-card);
|
|
140
|
+
border-radius: var(--radius-lg);
|
|
141
|
+
backdrop-filter: blur(8px);
|
|
142
|
+
-webkit-backdrop-filter: blur(8px);
|
|
143
|
+
padding: 20px;
|
|
144
|
+
transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
|
|
145
|
+
}
|
|
146
|
+
.card:hover {
|
|
147
|
+
border-color: rgba(255, 255, 255, 0.28);
|
|
148
|
+
}
|
|
149
|
+
.card-hero {
|
|
150
|
+
border-color: var(--border-accent);
|
|
151
|
+
box-shadow: 0 0 40px var(--accent-dim), inset 0 1px 0 rgba(255,255,255,0.05);
|
|
152
|
+
}
|
|
153
|
+
.card-hero:hover {
|
|
154
|
+
border-color: var(--accent);
|
|
155
|
+
box-shadow: 0 0 60px var(--accent-glow), inset 0 1px 0 rgba(255,255,255,0.08);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/* ── Mini Stat Card ────────────────────────────────────── */
|
|
159
|
+
.mini-stat {
|
|
160
|
+
background: var(--bg-card-subtle);
|
|
161
|
+
border: 1px solid var(--border-subtle);
|
|
162
|
+
border-radius: var(--radius-md);
|
|
163
|
+
padding: 12px 8px;
|
|
164
|
+
text-align: center;
|
|
165
|
+
transition: border-color var(--transition-fast);
|
|
166
|
+
}
|
|
167
|
+
.mini-stat:hover {
|
|
168
|
+
border-color: rgba(255,255,255,0.15);
|
|
169
|
+
}
|
|
170
|
+
.mini-stat-label {
|
|
171
|
+
font-size: 10px;
|
|
172
|
+
text-transform: uppercase;
|
|
173
|
+
letter-spacing: 0.15em;
|
|
174
|
+
color: var(--text-muted);
|
|
175
|
+
margin-bottom: 4px;
|
|
176
|
+
font-family: var(--font-body);
|
|
177
|
+
font-weight: 600;
|
|
178
|
+
}
|
|
179
|
+
.mini-stat-value {
|
|
180
|
+
font-family: var(--font-mono);
|
|
181
|
+
font-variant-numeric: tabular-nums;
|
|
182
|
+
font-size: 22px;
|
|
183
|
+
font-weight: 500;
|
|
184
|
+
color: var(--text-primary);
|
|
185
|
+
line-height: 1.2;
|
|
186
|
+
}
|
|
187
|
+
.mini-stat-delta {
|
|
188
|
+
font-family: var(--font-mono);
|
|
189
|
+
font-size: 11px;
|
|
190
|
+
margin-top: 2px;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/* ── Stat (generic) ────────────────────────────────────── */
|
|
194
|
+
.stat {
|
|
195
|
+
font-family: var(--font-mono);
|
|
196
|
+
font-variant-numeric: tabular-nums;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/* ── Badge ─────────────────────────────────────────────── */
|
|
200
|
+
.badge {
|
|
201
|
+
display: inline-flex;
|
|
202
|
+
align-items: center;
|
|
203
|
+
padding: 2px 8px;
|
|
204
|
+
border-radius: 9999px;
|
|
205
|
+
font-size: 10px;
|
|
206
|
+
font-weight: 600;
|
|
207
|
+
text-transform: uppercase;
|
|
208
|
+
letter-spacing: 0.08em;
|
|
209
|
+
line-height: 1.6;
|
|
210
|
+
}
|
|
211
|
+
.badge-emerald { background: rgba(74,222,128,0.12); color: var(--emerald); border: 1px solid rgba(74,222,128,0.2); }
|
|
212
|
+
.badge-amber { background: rgba(251,191,36,0.12); color: var(--amber); border: 1px solid rgba(251,191,36,0.2); }
|
|
213
|
+
.badge-red { background: rgba(248,113,113,0.12); color: var(--red); border: 1px solid rgba(248,113,113,0.2); }
|
|
214
|
+
.badge-blue { background: rgba(96,165,250,0.12); color: var(--blue); border: 1px solid rgba(96,165,250,0.2); }
|
|
215
|
+
.badge-violet { background: rgba(167,139,250,0.12); color: var(--violet); border: 1px solid rgba(167,139,250,0.2); }
|
|
216
|
+
.badge-cyan { background: rgba(34,211,238,0.12); color: var(--cyan); border: 1px solid rgba(34,211,238,0.2); }
|
|
217
|
+
.badge-accent { background: var(--accent-dim); color: var(--accent); border: 1px solid rgba(217,119,87,0.3); }
|
|
218
|
+
|
|
219
|
+
/* ── Actor Badge ───────────────────────────────────────── */
|
|
220
|
+
.actor-badge {
|
|
221
|
+
display: inline-flex;
|
|
222
|
+
align-items: center;
|
|
223
|
+
justify-content: center;
|
|
224
|
+
width: 24px;
|
|
225
|
+
height: 24px;
|
|
226
|
+
border-radius: 6px;
|
|
227
|
+
font-size: 11px;
|
|
228
|
+
font-weight: 700;
|
|
229
|
+
font-family: var(--font-mono);
|
|
230
|
+
flex-shrink: 0;
|
|
231
|
+
}
|
|
232
|
+
.actor-F { background: rgba(217,119,87,0.2); color: var(--accent); }
|
|
233
|
+
.actor-A { background: rgba(34,211,238,0.15); color: var(--cyan); }
|
|
234
|
+
.actor-S { background: rgba(167,139,250,0.15); color: var(--violet); }
|
|
235
|
+
.actor-B { background: rgba(96,165,250,0.15); color: var(--blue); }
|
|
236
|
+
|
|
237
|
+
/* ── Buttons ────────────────────────────────────────────── */
|
|
238
|
+
.btn {
|
|
239
|
+
display: inline-flex;
|
|
240
|
+
align-items: center;
|
|
241
|
+
gap: 4px;
|
|
242
|
+
padding: 4px 10px;
|
|
243
|
+
border-radius: var(--radius-sm);
|
|
244
|
+
font-size: 11px;
|
|
245
|
+
font-weight: 600;
|
|
246
|
+
font-family: var(--font-body);
|
|
247
|
+
cursor: pointer;
|
|
248
|
+
border: 1px solid var(--border-subtle);
|
|
249
|
+
background: var(--bg-card-subtle);
|
|
250
|
+
color: var(--text-secondary);
|
|
251
|
+
transition: all var(--transition-fast);
|
|
252
|
+
text-transform: uppercase;
|
|
253
|
+
letter-spacing: 0.06em;
|
|
254
|
+
}
|
|
255
|
+
.btn:hover {
|
|
256
|
+
background: rgba(255,255,255,0.08);
|
|
257
|
+
border-color: rgba(255,255,255,0.2);
|
|
258
|
+
color: var(--text-primary);
|
|
259
|
+
}
|
|
260
|
+
.btn-accent {
|
|
261
|
+
background: var(--accent-dim);
|
|
262
|
+
border-color: rgba(217,119,87,0.3);
|
|
263
|
+
color: var(--accent);
|
|
264
|
+
}
|
|
265
|
+
.btn-accent:hover {
|
|
266
|
+
background: rgba(217,119,87,0.25);
|
|
267
|
+
border-color: var(--accent);
|
|
268
|
+
}
|
|
269
|
+
.btn-emerald {
|
|
270
|
+
background: rgba(74,222,128,0.08);
|
|
271
|
+
border-color: rgba(74,222,128,0.2);
|
|
272
|
+
color: var(--emerald);
|
|
273
|
+
}
|
|
274
|
+
.btn-emerald:hover {
|
|
275
|
+
background: rgba(74,222,128,0.15);
|
|
276
|
+
}
|
|
277
|
+
.btn-red {
|
|
278
|
+
background: rgba(248,113,113,0.08);
|
|
279
|
+
border-color: rgba(248,113,113,0.2);
|
|
280
|
+
color: var(--red);
|
|
281
|
+
}
|
|
282
|
+
.btn-red:hover {
|
|
283
|
+
background: rgba(248,113,113,0.15);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/* ── Dimension Bar ─────────────────────────────────────── */
|
|
287
|
+
.dim-bar-row {
|
|
288
|
+
display: flex;
|
|
289
|
+
align-items: center;
|
|
290
|
+
gap: 10px;
|
|
291
|
+
margin-bottom: 8px;
|
|
292
|
+
}
|
|
293
|
+
.dim-bar-label {
|
|
294
|
+
width: 160px;
|
|
295
|
+
flex-shrink: 0;
|
|
296
|
+
font-size: 12px;
|
|
297
|
+
color: var(--text-secondary);
|
|
298
|
+
text-align: right;
|
|
299
|
+
}
|
|
300
|
+
.dim-bar-track {
|
|
301
|
+
flex: 1;
|
|
302
|
+
height: 6px;
|
|
303
|
+
background: rgba(255,255,255,0.06);
|
|
304
|
+
border-radius: 3px;
|
|
305
|
+
overflow: hidden;
|
|
306
|
+
}
|
|
307
|
+
.dim-bar-fill {
|
|
308
|
+
height: 100%;
|
|
309
|
+
border-radius: 3px;
|
|
310
|
+
transition: width var(--transition-slow);
|
|
311
|
+
}
|
|
312
|
+
.dim-bar-value {
|
|
313
|
+
width: 36px;
|
|
314
|
+
font-family: var(--font-mono);
|
|
315
|
+
font-size: 12px;
|
|
316
|
+
font-variant-numeric: tabular-nums;
|
|
317
|
+
color: var(--text-secondary);
|
|
318
|
+
text-align: right;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
@media (max-width: 640px) {
|
|
322
|
+
.dim-bar-label { width: 100px; font-size: 11px; }
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/* ── Tab Bar ────────────────────────────────────────────── */
|
|
326
|
+
.tab-bar {
|
|
327
|
+
display: flex;
|
|
328
|
+
gap: 4px;
|
|
329
|
+
margin-bottom: 16px;
|
|
330
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
331
|
+
padding-bottom: 8px;
|
|
332
|
+
overflow-x: auto;
|
|
333
|
+
}
|
|
334
|
+
.tab {
|
|
335
|
+
padding: 6px 14px;
|
|
336
|
+
border-radius: var(--radius-sm);
|
|
337
|
+
font-size: 12px;
|
|
338
|
+
font-weight: 500;
|
|
339
|
+
color: var(--text-muted);
|
|
340
|
+
cursor: pointer;
|
|
341
|
+
border: none;
|
|
342
|
+
background: transparent;
|
|
343
|
+
transition: all var(--transition-fast);
|
|
344
|
+
white-space: nowrap;
|
|
345
|
+
font-family: var(--font-body);
|
|
346
|
+
}
|
|
347
|
+
.tab:hover { color: var(--text-secondary); background: rgba(255,255,255,0.04); }
|
|
348
|
+
.tab.active {
|
|
349
|
+
color: var(--accent);
|
|
350
|
+
background: var(--accent-dim);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/* ── Event Row ─────────────────────────────────────────── */
|
|
354
|
+
.event-row {
|
|
355
|
+
display: flex;
|
|
356
|
+
align-items: flex-start;
|
|
357
|
+
gap: 10px;
|
|
358
|
+
padding: 10px 0;
|
|
359
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
360
|
+
transition: background var(--transition-fast);
|
|
361
|
+
}
|
|
362
|
+
.event-row:last-child { border-bottom: none; }
|
|
363
|
+
.event-row:hover { background: rgba(255,255,255,0.02); }
|
|
364
|
+
.event-summary {
|
|
365
|
+
flex: 1;
|
|
366
|
+
font-size: 13px;
|
|
367
|
+
color: var(--text-secondary);
|
|
368
|
+
line-height: 1.5;
|
|
369
|
+
}
|
|
370
|
+
.event-time {
|
|
371
|
+
font-family: var(--font-mono);
|
|
372
|
+
font-size: 11px;
|
|
373
|
+
color: var(--text-dim);
|
|
374
|
+
white-space: nowrap;
|
|
375
|
+
flex-shrink: 0;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/* ── Path Chip ─────────────────────────────────────────── */
|
|
379
|
+
.path-scroll {
|
|
380
|
+
display: flex;
|
|
381
|
+
gap: 0;
|
|
382
|
+
overflow-x: auto;
|
|
383
|
+
padding: 12px 0;
|
|
384
|
+
align-items: center;
|
|
385
|
+
}
|
|
386
|
+
.path-chip {
|
|
387
|
+
display: flex;
|
|
388
|
+
flex-direction: column;
|
|
389
|
+
align-items: center;
|
|
390
|
+
padding: 8px 14px;
|
|
391
|
+
border-radius: var(--radius-md);
|
|
392
|
+
background: var(--bg-card-subtle);
|
|
393
|
+
border: 1px solid var(--border-subtle);
|
|
394
|
+
font-size: 12px;
|
|
395
|
+
white-space: nowrap;
|
|
396
|
+
flex-shrink: 0;
|
|
397
|
+
transition: border-color var(--transition-fast);
|
|
398
|
+
}
|
|
399
|
+
.path-chip:hover { border-color: rgba(255,255,255,0.2); }
|
|
400
|
+
.path-chip-name {
|
|
401
|
+
font-weight: 500;
|
|
402
|
+
color: var(--text-primary);
|
|
403
|
+
margin-bottom: 2px;
|
|
404
|
+
}
|
|
405
|
+
.path-chip-dur {
|
|
406
|
+
font-family: var(--font-mono);
|
|
407
|
+
font-size: 10px;
|
|
408
|
+
color: var(--text-dim);
|
|
409
|
+
}
|
|
410
|
+
.path-arrow {
|
|
411
|
+
color: var(--text-dim);
|
|
412
|
+
font-size: 14px;
|
|
413
|
+
padding: 0 4px;
|
|
414
|
+
flex-shrink: 0;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/* ── Change Card ───────────────────────────────────────── */
|
|
418
|
+
.change-card {
|
|
419
|
+
background: var(--bg-card-subtle);
|
|
420
|
+
border: 1px solid var(--border-subtle);
|
|
421
|
+
border-radius: var(--radius-md);
|
|
422
|
+
padding: 14px;
|
|
423
|
+
margin-bottom: 10px;
|
|
424
|
+
transition: border-color var(--transition-fast);
|
|
425
|
+
}
|
|
426
|
+
.change-card:hover { border-color: rgba(255,255,255,0.15); }
|
|
427
|
+
.change-card:last-child { margin-bottom: 0; }
|
|
428
|
+
.change-header {
|
|
429
|
+
display: flex;
|
|
430
|
+
align-items: center;
|
|
431
|
+
gap: 8px;
|
|
432
|
+
margin-bottom: 6px;
|
|
433
|
+
}
|
|
434
|
+
.change-summary {
|
|
435
|
+
font-size: 13px;
|
|
436
|
+
color: var(--text-secondary);
|
|
437
|
+
margin-bottom: 8px;
|
|
438
|
+
line-height: 1.5;
|
|
439
|
+
}
|
|
440
|
+
.change-actions {
|
|
441
|
+
display: flex;
|
|
442
|
+
gap: 6px;
|
|
443
|
+
flex-wrap: wrap;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/* ── Packet Card ───────────────────────────────────────── */
|
|
447
|
+
.packet-card {
|
|
448
|
+
background: var(--bg-card-subtle);
|
|
449
|
+
border: 1px solid var(--border-subtle);
|
|
450
|
+
border-radius: var(--radius-md);
|
|
451
|
+
padding: 14px;
|
|
452
|
+
}
|
|
453
|
+
.packet-header {
|
|
454
|
+
display: flex;
|
|
455
|
+
justify-content: space-between;
|
|
456
|
+
align-items: center;
|
|
457
|
+
margin-bottom: 8px;
|
|
458
|
+
}
|
|
459
|
+
.packet-name {
|
|
460
|
+
font-weight: 500;
|
|
461
|
+
font-size: 13px;
|
|
462
|
+
color: var(--text-primary);
|
|
463
|
+
}
|
|
464
|
+
.staleness-track {
|
|
465
|
+
height: 4px;
|
|
466
|
+
background: rgba(255,255,255,0.06);
|
|
467
|
+
border-radius: 2px;
|
|
468
|
+
overflow: hidden;
|
|
469
|
+
margin-top: 8px;
|
|
470
|
+
}
|
|
471
|
+
.staleness-fill {
|
|
472
|
+
height: 100%;
|
|
473
|
+
border-radius: 2px;
|
|
474
|
+
transition: width var(--transition-slow);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/* ── Action Row ────────────────────────────────────────── */
|
|
478
|
+
.action-row {
|
|
479
|
+
display: flex;
|
|
480
|
+
align-items: flex-start;
|
|
481
|
+
gap: 10px;
|
|
482
|
+
padding: 10px 0;
|
|
483
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
484
|
+
}
|
|
485
|
+
.action-row:last-child { border-bottom: none; }
|
|
486
|
+
.action-meta {
|
|
487
|
+
display: flex;
|
|
488
|
+
gap: 6px;
|
|
489
|
+
align-items: center;
|
|
490
|
+
flex-shrink: 0;
|
|
491
|
+
}
|
|
492
|
+
.action-detail {
|
|
493
|
+
flex: 1;
|
|
494
|
+
min-width: 0;
|
|
495
|
+
}
|
|
496
|
+
.action-title {
|
|
497
|
+
font-size: 13px;
|
|
498
|
+
font-weight: 500;
|
|
499
|
+
color: var(--text-primary);
|
|
500
|
+
margin-bottom: 2px;
|
|
501
|
+
}
|
|
502
|
+
.action-states {
|
|
503
|
+
display: flex;
|
|
504
|
+
gap: 8px;
|
|
505
|
+
font-size: 11px;
|
|
506
|
+
font-family: var(--font-mono);
|
|
507
|
+
color: var(--text-dim);
|
|
508
|
+
}
|
|
509
|
+
.action-states .arrow { color: var(--accent); margin: 0 2px; }
|
|
510
|
+
|
|
511
|
+
/* ── Status Summary Bar ────────────────────────────────── */
|
|
512
|
+
.status-bar {
|
|
513
|
+
display: flex;
|
|
514
|
+
gap: 12px;
|
|
515
|
+
flex-wrap: wrap;
|
|
516
|
+
margin-bottom: 14px;
|
|
517
|
+
}
|
|
518
|
+
.status-item {
|
|
519
|
+
display: flex;
|
|
520
|
+
align-items: center;
|
|
521
|
+
gap: 4px;
|
|
522
|
+
font-size: 12px;
|
|
523
|
+
color: var(--text-muted);
|
|
524
|
+
}
|
|
525
|
+
.status-dot {
|
|
526
|
+
width: 6px;
|
|
527
|
+
height: 6px;
|
|
528
|
+
border-radius: 50%;
|
|
529
|
+
}
|
|
530
|
+
.status-num {
|
|
531
|
+
font-family: var(--font-mono);
|
|
532
|
+
font-weight: 500;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/* ── Metric Grid ───────────────────────────────────────── */
|
|
536
|
+
.metric-grid {
|
|
537
|
+
display: grid;
|
|
538
|
+
grid-template-columns: repeat(2, 1fr);
|
|
539
|
+
gap: 10px;
|
|
540
|
+
}
|
|
541
|
+
@media (min-width: 640px) {
|
|
542
|
+
.metric-grid { grid-template-columns: repeat(3, 1fr); }
|
|
543
|
+
}
|
|
544
|
+
@media (min-width: 900px) {
|
|
545
|
+
.metric-grid { grid-template-columns: repeat(4, 1fr); }
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/* ── Empty State ───────────────────────────────────────── */
|
|
549
|
+
.empty-state {
|
|
550
|
+
text-align: center;
|
|
551
|
+
padding: 32px 20px;
|
|
552
|
+
color: var(--text-dim);
|
|
553
|
+
font-size: 13px;
|
|
554
|
+
line-height: 1.6;
|
|
555
|
+
}
|
|
556
|
+
.empty-state code {
|
|
557
|
+
font-family: var(--font-mono);
|
|
558
|
+
background: rgba(255,255,255,0.06);
|
|
559
|
+
padding: 2px 6px;
|
|
560
|
+
border-radius: 4px;
|
|
561
|
+
font-size: 12px;
|
|
562
|
+
color: var(--text-muted);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/* ── Filter Dropdown ───────────────────────────────────── */
|
|
566
|
+
.filter-select {
|
|
567
|
+
background: var(--bg-card-subtle);
|
|
568
|
+
border: 1px solid var(--border-subtle);
|
|
569
|
+
border-radius: var(--radius-sm);
|
|
570
|
+
color: var(--text-secondary);
|
|
571
|
+
font-size: 11px;
|
|
572
|
+
font-family: var(--font-body);
|
|
573
|
+
padding: 4px 8px;
|
|
574
|
+
cursor: pointer;
|
|
575
|
+
outline: none;
|
|
576
|
+
}
|
|
577
|
+
.filter-select:focus {
|
|
578
|
+
border-color: var(--accent);
|
|
579
|
+
}
|
|
580
|
+
.filter-select option {
|
|
581
|
+
background: #18181b;
|
|
582
|
+
color: var(--text-primary);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/* ── Header ────────────────────────────────────────────── */
|
|
586
|
+
.header {
|
|
587
|
+
display: flex;
|
|
588
|
+
align-items: center;
|
|
589
|
+
justify-content: space-between;
|
|
590
|
+
padding: 0 0 24px;
|
|
591
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
592
|
+
margin-bottom: 24px;
|
|
593
|
+
flex-wrap: wrap;
|
|
594
|
+
gap: 12px;
|
|
595
|
+
}
|
|
596
|
+
.header-left {
|
|
597
|
+
display: flex;
|
|
598
|
+
align-items: center;
|
|
599
|
+
gap: 12px;
|
|
600
|
+
}
|
|
601
|
+
.header-logo {
|
|
602
|
+
font-size: 20px;
|
|
603
|
+
font-weight: 700;
|
|
604
|
+
color: var(--text-primary);
|
|
605
|
+
letter-spacing: -0.02em;
|
|
606
|
+
}
|
|
607
|
+
.header-logo span { color: var(--accent); }
|
|
608
|
+
.header-subtitle {
|
|
609
|
+
font-size: 13px;
|
|
610
|
+
color: var(--text-muted);
|
|
611
|
+
font-weight: 400;
|
|
612
|
+
}
|
|
613
|
+
.header-right {
|
|
614
|
+
display: flex;
|
|
615
|
+
align-items: center;
|
|
616
|
+
gap: 14px;
|
|
617
|
+
}
|
|
618
|
+
.local-badge {
|
|
619
|
+
display: inline-flex;
|
|
620
|
+
align-items: center;
|
|
621
|
+
gap: 4px;
|
|
622
|
+
padding: 3px 10px;
|
|
623
|
+
border-radius: 9999px;
|
|
624
|
+
font-size: 10px;
|
|
625
|
+
font-weight: 700;
|
|
626
|
+
text-transform: uppercase;
|
|
627
|
+
letter-spacing: 0.1em;
|
|
628
|
+
background: var(--accent-dim);
|
|
629
|
+
color: var(--accent);
|
|
630
|
+
border: 1px solid rgba(217,119,87,0.3);
|
|
631
|
+
}
|
|
632
|
+
.refresh-dot {
|
|
633
|
+
width: 8px;
|
|
634
|
+
height: 8px;
|
|
635
|
+
border-radius: 50%;
|
|
636
|
+
background: var(--emerald);
|
|
637
|
+
box-shadow: 0 0 6px rgba(74,222,128,0.4);
|
|
638
|
+
animation: pulse-dot 2s ease-in-out infinite;
|
|
639
|
+
}
|
|
640
|
+
.refresh-dot.paused {
|
|
641
|
+
background: var(--amber);
|
|
642
|
+
box-shadow: 0 0 6px rgba(251,191,36,0.4);
|
|
643
|
+
animation: none;
|
|
644
|
+
}
|
|
645
|
+
@keyframes pulse-dot {
|
|
646
|
+
0%, 100% { opacity: 1; }
|
|
647
|
+
50% { opacity: 0.4; }
|
|
648
|
+
}
|
|
649
|
+
.header-clock {
|
|
650
|
+
font-family: var(--font-mono);
|
|
651
|
+
font-size: 12px;
|
|
652
|
+
color: var(--text-dim);
|
|
653
|
+
font-variant-numeric: tabular-nums;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
/* ── Section Spacing ───────────────────────────────────── */
|
|
657
|
+
.section {
|
|
658
|
+
margin-bottom: 28px;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/* ── Footer ────────────────────────────────────────────── */
|
|
662
|
+
.footer {
|
|
663
|
+
text-align: center;
|
|
664
|
+
padding: 32px 0 0;
|
|
665
|
+
border-top: 1px solid var(--border-subtle);
|
|
666
|
+
margin-top: 20px;
|
|
667
|
+
}
|
|
668
|
+
.footer-text {
|
|
669
|
+
font-size: 11px;
|
|
670
|
+
color: var(--text-dim);
|
|
671
|
+
font-family: var(--font-mono);
|
|
672
|
+
}
|
|
673
|
+
.footer-text span { color: var(--accent); }
|
|
674
|
+
|
|
675
|
+
/* ── Sparkline ─────────────────────────────────────────── */
|
|
676
|
+
.sparkline-container {
|
|
677
|
+
display: flex;
|
|
678
|
+
align-items: center;
|
|
679
|
+
gap: 16px;
|
|
680
|
+
margin-bottom: 20px;
|
|
681
|
+
}
|
|
682
|
+
.score-big {
|
|
683
|
+
font-family: var(--font-mono);
|
|
684
|
+
font-size: 48px;
|
|
685
|
+
font-weight: 500;
|
|
686
|
+
font-variant-numeric: tabular-nums;
|
|
687
|
+
line-height: 1;
|
|
688
|
+
color: var(--text-primary);
|
|
689
|
+
}
|
|
690
|
+
.score-trend {
|
|
691
|
+
font-size: 14px;
|
|
692
|
+
font-family: var(--font-mono);
|
|
693
|
+
margin-left: 6px;
|
|
694
|
+
}
|
|
695
|
+
.trend-up { color: var(--emerald); }
|
|
696
|
+
.trend-down { color: var(--red); }
|
|
697
|
+
.trend-flat { color: var(--text-dim); }
|
|
698
|
+
|
|
699
|
+
/* ── Fade In Animation ─────────────────────────────────── */
|
|
700
|
+
@keyframes fadeInUp {
|
|
701
|
+
from { opacity: 0; transform: translateY(8px); }
|
|
702
|
+
to { opacity: 1; transform: translateY(0); }
|
|
703
|
+
}
|
|
704
|
+
.fade-in {
|
|
705
|
+
animation: fadeInUp 0.4s ease both;
|
|
706
|
+
}
|
|
707
|
+
.fade-in-1 { animation-delay: 0.05s; }
|
|
708
|
+
.fade-in-2 { animation-delay: 0.10s; }
|
|
709
|
+
.fade-in-3 { animation-delay: 0.15s; }
|
|
710
|
+
.fade-in-4 { animation-delay: 0.20s; }
|
|
711
|
+
.fade-in-5 { animation-delay: 0.25s; }
|
|
712
|
+
.fade-in-6 { animation-delay: 0.30s; }
|
|
713
|
+
.fade-in-7 { animation-delay: 0.35s; }
|
|
714
|
+
.fade-in-8 { animation-delay: 0.40s; }
|
|
715
|
+
.fade-in-9 { animation-delay: 0.45s; }
|
|
716
|
+
|
|
717
|
+
/* ── Reduced Motion ────────────────────────────────────── */
|
|
718
|
+
@media (prefers-reduced-motion: reduce) {
|
|
719
|
+
.fade-in, .fade-in-1, .fade-in-2, .fade-in-3,
|
|
720
|
+
.fade-in-4, .fade-in-5, .fade-in-6, .fade-in-7,
|
|
721
|
+
.fade-in-8, .fade-in-9 {
|
|
722
|
+
animation: none;
|
|
723
|
+
opacity: 1;
|
|
724
|
+
transform: none;
|
|
725
|
+
}
|
|
726
|
+
.refresh-dot { animation: none; }
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/* ── Tooltip ────────────────────────────────────────────── */
|
|
730
|
+
[data-tooltip] {
|
|
731
|
+
position: relative;
|
|
732
|
+
}
|
|
733
|
+
[data-tooltip]:hover::after {
|
|
734
|
+
content: attr(data-tooltip);
|
|
735
|
+
position: absolute;
|
|
736
|
+
bottom: calc(100% + 6px);
|
|
737
|
+
left: 50%;
|
|
738
|
+
transform: translateX(-50%);
|
|
739
|
+
padding: 4px 8px;
|
|
740
|
+
border-radius: 4px;
|
|
741
|
+
background: #27272a;
|
|
742
|
+
border: 1px solid rgba(255,255,255,0.12);
|
|
743
|
+
color: var(--text-secondary);
|
|
744
|
+
font-size: 11px;
|
|
745
|
+
white-space: nowrap;
|
|
746
|
+
z-index: 10;
|
|
747
|
+
pointer-events: none;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
/* ── Business Intelligence Styles ─────────────────────── */
|
|
751
|
+
.biz-hero {
|
|
752
|
+
display: flex;
|
|
753
|
+
align-items: flex-start;
|
|
754
|
+
gap: 20px;
|
|
755
|
+
flex-wrap: wrap;
|
|
756
|
+
}
|
|
757
|
+
.biz-hero-main { flex: 1; min-width: 200px; }
|
|
758
|
+
.biz-hero-name {
|
|
759
|
+
font-size: 22px;
|
|
760
|
+
font-weight: 700;
|
|
761
|
+
color: var(--text-primary);
|
|
762
|
+
letter-spacing: -0.02em;
|
|
763
|
+
margin-bottom: 4px;
|
|
764
|
+
}
|
|
765
|
+
.biz-hero-mission {
|
|
766
|
+
font-size: 14px;
|
|
767
|
+
color: var(--text-secondary);
|
|
768
|
+
margin-bottom: 2px;
|
|
769
|
+
}
|
|
770
|
+
.biz-hero-wedge {
|
|
771
|
+
font-size: 12px;
|
|
772
|
+
color: var(--text-muted);
|
|
773
|
+
font-style: italic;
|
|
774
|
+
}
|
|
775
|
+
.biz-hero-meta {
|
|
776
|
+
display: flex;
|
|
777
|
+
flex-direction: column;
|
|
778
|
+
gap: 8px;
|
|
779
|
+
align-items: flex-end;
|
|
780
|
+
}
|
|
781
|
+
.confidence-bar-wrap {
|
|
782
|
+
display: flex;
|
|
783
|
+
align-items: center;
|
|
784
|
+
gap: 8px;
|
|
785
|
+
}
|
|
786
|
+
.confidence-label {
|
|
787
|
+
font-size: 10px;
|
|
788
|
+
text-transform: uppercase;
|
|
789
|
+
letter-spacing: 0.12em;
|
|
790
|
+
color: var(--text-muted);
|
|
791
|
+
font-weight: 600;
|
|
792
|
+
white-space: nowrap;
|
|
793
|
+
}
|
|
794
|
+
.confidence-track {
|
|
795
|
+
width: 100px;
|
|
796
|
+
height: 6px;
|
|
797
|
+
background: rgba(255,255,255,0.06);
|
|
798
|
+
border-radius: 3px;
|
|
799
|
+
overflow: hidden;
|
|
800
|
+
}
|
|
801
|
+
.confidence-fill {
|
|
802
|
+
height: 100%;
|
|
803
|
+
border-radius: 3px;
|
|
804
|
+
background: var(--accent);
|
|
805
|
+
transition: width var(--transition-slow);
|
|
806
|
+
}
|
|
807
|
+
.confidence-val {
|
|
808
|
+
font-family: var(--font-mono);
|
|
809
|
+
font-size: 13px;
|
|
810
|
+
font-weight: 500;
|
|
811
|
+
color: var(--accent);
|
|
812
|
+
min-width: 36px;
|
|
813
|
+
text-align: right;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
.biz-init-card {
|
|
817
|
+
background: var(--bg-card-subtle);
|
|
818
|
+
border: 1px solid var(--border-subtle);
|
|
819
|
+
border-radius: var(--radius-md);
|
|
820
|
+
padding: 14px;
|
|
821
|
+
transition: border-color var(--transition-fast);
|
|
822
|
+
}
|
|
823
|
+
.biz-init-card:hover { border-color: rgba(255,255,255,0.15); }
|
|
824
|
+
.biz-init-header {
|
|
825
|
+
display: flex;
|
|
826
|
+
align-items: center;
|
|
827
|
+
gap: 8px;
|
|
828
|
+
margin-bottom: 6px;
|
|
829
|
+
flex-wrap: wrap;
|
|
830
|
+
}
|
|
831
|
+
.biz-init-title {
|
|
832
|
+
font-size: 13px;
|
|
833
|
+
font-weight: 600;
|
|
834
|
+
color: var(--text-primary);
|
|
835
|
+
flex: 1;
|
|
836
|
+
min-width: 120px;
|
|
837
|
+
}
|
|
838
|
+
.biz-init-summary {
|
|
839
|
+
font-size: 12px;
|
|
840
|
+
color: var(--text-muted);
|
|
841
|
+
line-height: 1.5;
|
|
842
|
+
}
|
|
843
|
+
.biz-init-meta {
|
|
844
|
+
display: flex;
|
|
845
|
+
gap: 8px;
|
|
846
|
+
margin-top: 6px;
|
|
847
|
+
align-items: center;
|
|
848
|
+
}
|
|
849
|
+
.biz-priority {
|
|
850
|
+
font-family: var(--font-mono);
|
|
851
|
+
font-size: 11px;
|
|
852
|
+
color: var(--accent);
|
|
853
|
+
font-weight: 500;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
.biz-intv-card {
|
|
857
|
+
background: var(--bg-card-subtle);
|
|
858
|
+
border: 1px solid var(--border-subtle);
|
|
859
|
+
border-radius: var(--radius-md);
|
|
860
|
+
padding: 14px;
|
|
861
|
+
margin-bottom: 10px;
|
|
862
|
+
transition: border-color var(--transition-fast);
|
|
863
|
+
}
|
|
864
|
+
.biz-intv-card:hover { border-color: rgba(255,255,255,0.15); }
|
|
865
|
+
.biz-intv-card:last-child { margin-bottom: 0; }
|
|
866
|
+
.biz-intv-header {
|
|
867
|
+
display: flex;
|
|
868
|
+
align-items: center;
|
|
869
|
+
gap: 8px;
|
|
870
|
+
margin-bottom: 6px;
|
|
871
|
+
}
|
|
872
|
+
.biz-intv-rank {
|
|
873
|
+
font-family: var(--font-mono);
|
|
874
|
+
font-size: 18px;
|
|
875
|
+
font-weight: 600;
|
|
876
|
+
color: var(--accent);
|
|
877
|
+
width: 28px;
|
|
878
|
+
text-align: center;
|
|
879
|
+
flex-shrink: 0;
|
|
880
|
+
}
|
|
881
|
+
.biz-intv-title {
|
|
882
|
+
font-size: 13px;
|
|
883
|
+
font-weight: 600;
|
|
884
|
+
color: var(--text-primary);
|
|
885
|
+
flex: 1;
|
|
886
|
+
}
|
|
887
|
+
.biz-intv-desc {
|
|
888
|
+
font-size: 12px;
|
|
889
|
+
color: var(--text-muted);
|
|
890
|
+
margin-bottom: 8px;
|
|
891
|
+
line-height: 1.5;
|
|
892
|
+
padding-left: 36px;
|
|
893
|
+
}
|
|
894
|
+
.biz-intv-meta {
|
|
895
|
+
display: flex;
|
|
896
|
+
gap: 12px;
|
|
897
|
+
align-items: center;
|
|
898
|
+
padding-left: 36px;
|
|
899
|
+
}
|
|
900
|
+
.biz-intv-confidence {
|
|
901
|
+
display: flex;
|
|
902
|
+
align-items: center;
|
|
903
|
+
gap: 6px;
|
|
904
|
+
}
|
|
905
|
+
.biz-intv-impact {
|
|
906
|
+
font-size: 11px;
|
|
907
|
+
color: var(--text-dim);
|
|
908
|
+
flex: 1;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
.biz-comp-card {
|
|
912
|
+
background: var(--bg-card-subtle);
|
|
913
|
+
border: 1px solid var(--border-subtle);
|
|
914
|
+
border-radius: var(--radius-md);
|
|
915
|
+
padding: 14px;
|
|
916
|
+
transition: border-color var(--transition-fast);
|
|
917
|
+
}
|
|
918
|
+
.biz-comp-card:hover { border-color: rgba(255,255,255,0.15); }
|
|
919
|
+
.biz-comp-name {
|
|
920
|
+
font-size: 14px;
|
|
921
|
+
font-weight: 600;
|
|
922
|
+
color: var(--text-primary);
|
|
923
|
+
margin-bottom: 4px;
|
|
924
|
+
}
|
|
925
|
+
.biz-comp-desc {
|
|
926
|
+
font-size: 12px;
|
|
927
|
+
color: var(--text-muted);
|
|
928
|
+
margin-bottom: 8px;
|
|
929
|
+
line-height: 1.4;
|
|
930
|
+
}
|
|
931
|
+
.biz-comp-row {
|
|
932
|
+
display: flex;
|
|
933
|
+
gap: 6px;
|
|
934
|
+
align-items: flex-start;
|
|
935
|
+
margin-bottom: 4px;
|
|
936
|
+
}
|
|
937
|
+
.biz-comp-label {
|
|
938
|
+
font-size: 10px;
|
|
939
|
+
text-transform: uppercase;
|
|
940
|
+
letter-spacing: 0.1em;
|
|
941
|
+
font-weight: 700;
|
|
942
|
+
flex-shrink: 0;
|
|
943
|
+
width: 80px;
|
|
944
|
+
padding-top: 1px;
|
|
945
|
+
}
|
|
946
|
+
.biz-comp-label-threat { color: var(--red); }
|
|
947
|
+
.biz-comp-label-opp { color: var(--emerald); }
|
|
948
|
+
.biz-comp-text {
|
|
949
|
+
font-size: 12px;
|
|
950
|
+
color: var(--text-secondary);
|
|
951
|
+
line-height: 1.4;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
.biz-contra-card {
|
|
955
|
+
background: var(--bg-card-subtle);
|
|
956
|
+
border: 1px solid var(--border-subtle);
|
|
957
|
+
border-radius: var(--radius-md);
|
|
958
|
+
padding: 14px;
|
|
959
|
+
margin-bottom: 10px;
|
|
960
|
+
transition: border-color var(--transition-fast);
|
|
961
|
+
}
|
|
962
|
+
.biz-contra-card:hover { border-color: rgba(255,255,255,0.15); }
|
|
963
|
+
.biz-contra-card:last-child { margin-bottom: 0; }
|
|
964
|
+
.biz-contra-header {
|
|
965
|
+
display: flex;
|
|
966
|
+
align-items: center;
|
|
967
|
+
gap: 8px;
|
|
968
|
+
margin-bottom: 6px;
|
|
969
|
+
}
|
|
970
|
+
.biz-contra-title {
|
|
971
|
+
font-size: 13px;
|
|
972
|
+
font-weight: 600;
|
|
973
|
+
color: var(--text-primary);
|
|
974
|
+
flex: 1;
|
|
975
|
+
}
|
|
976
|
+
.biz-contra-desc {
|
|
977
|
+
font-size: 12px;
|
|
978
|
+
color: var(--text-muted);
|
|
979
|
+
line-height: 1.5;
|
|
980
|
+
margin-bottom: 6px;
|
|
981
|
+
}
|
|
982
|
+
.biz-contra-entities {
|
|
983
|
+
font-size: 11px;
|
|
984
|
+
font-family: var(--font-mono);
|
|
985
|
+
color: var(--text-dim);
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
.biz-agent-card {
|
|
989
|
+
background: var(--bg-card-subtle);
|
|
990
|
+
border: 1px solid var(--border-subtle);
|
|
991
|
+
border-radius: var(--radius-md);
|
|
992
|
+
padding: 14px;
|
|
993
|
+
transition: border-color var(--transition-fast);
|
|
994
|
+
}
|
|
995
|
+
.biz-agent-card:hover { border-color: rgba(255,255,255,0.15); }
|
|
996
|
+
.biz-agent-header {
|
|
997
|
+
display: flex;
|
|
998
|
+
align-items: center;
|
|
999
|
+
gap: 8px;
|
|
1000
|
+
margin-bottom: 6px;
|
|
1001
|
+
}
|
|
1002
|
+
.biz-agent-dot {
|
|
1003
|
+
width: 8px;
|
|
1004
|
+
height: 8px;
|
|
1005
|
+
border-radius: 50%;
|
|
1006
|
+
flex-shrink: 0;
|
|
1007
|
+
}
|
|
1008
|
+
.biz-agent-dot-healthy { background: var(--emerald); box-shadow: 0 0 6px rgba(74,222,128,0.4); }
|
|
1009
|
+
.biz-agent-dot-waiting { background: var(--amber); box-shadow: 0 0 6px rgba(251,191,36,0.4); }
|
|
1010
|
+
.biz-agent-dot-drifting { background: var(--red); box-shadow: 0 0 6px rgba(248,113,113,0.4); }
|
|
1011
|
+
.biz-agent-name {
|
|
1012
|
+
font-size: 13px;
|
|
1013
|
+
font-weight: 600;
|
|
1014
|
+
color: var(--text-primary);
|
|
1015
|
+
}
|
|
1016
|
+
.biz-agent-type {
|
|
1017
|
+
font-size: 10px;
|
|
1018
|
+
font-family: var(--font-mono);
|
|
1019
|
+
color: var(--text-dim);
|
|
1020
|
+
text-transform: uppercase;
|
|
1021
|
+
letter-spacing: 0.08em;
|
|
1022
|
+
}
|
|
1023
|
+
.biz-agent-goal {
|
|
1024
|
+
font-size: 12px;
|
|
1025
|
+
color: var(--text-muted);
|
|
1026
|
+
line-height: 1.4;
|
|
1027
|
+
}
|
|
1028
|
+
.biz-agent-heartbeat {
|
|
1029
|
+
font-size: 11px;
|
|
1030
|
+
font-family: var(--font-mono);
|
|
1031
|
+
color: var(--text-dim);
|
|
1032
|
+
margin-top: 6px;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
.biz-decision-row {
|
|
1036
|
+
display: flex;
|
|
1037
|
+
align-items: flex-start;
|
|
1038
|
+
gap: 10px;
|
|
1039
|
+
padding: 10px 0;
|
|
1040
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
1041
|
+
}
|
|
1042
|
+
.biz-decision-row:last-child { border-bottom: none; }
|
|
1043
|
+
.biz-decision-title {
|
|
1044
|
+
font-size: 13px;
|
|
1045
|
+
font-weight: 500;
|
|
1046
|
+
color: var(--text-primary);
|
|
1047
|
+
margin-bottom: 2px;
|
|
1048
|
+
}
|
|
1049
|
+
.biz-decision-rationale {
|
|
1050
|
+
font-size: 12px;
|
|
1051
|
+
color: var(--text-muted);
|
|
1052
|
+
line-height: 1.4;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
.grid-3 {
|
|
1056
|
+
display: grid;
|
|
1057
|
+
grid-template-columns: 1fr;
|
|
1058
|
+
gap: 12px;
|
|
1059
|
+
}
|
|
1060
|
+
@media (min-width: 768px) {
|
|
1061
|
+
.grid-3 { grid-template-columns: 1fr 1fr 1fr; }
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
.section-divider {
|
|
1065
|
+
display: flex;
|
|
1066
|
+
align-items: center;
|
|
1067
|
+
gap: 16px;
|
|
1068
|
+
margin: 36px 0 24px;
|
|
1069
|
+
color: var(--text-dim);
|
|
1070
|
+
}
|
|
1071
|
+
.section-divider::before,
|
|
1072
|
+
.section-divider::after {
|
|
1073
|
+
content: '';
|
|
1074
|
+
flex: 1;
|
|
1075
|
+
height: 1px;
|
|
1076
|
+
background: var(--border-subtle);
|
|
1077
|
+
}
|
|
1078
|
+
.section-divider-text {
|
|
1079
|
+
font-size: 10px;
|
|
1080
|
+
font-weight: 700;
|
|
1081
|
+
text-transform: uppercase;
|
|
1082
|
+
letter-spacing: 0.25em;
|
|
1083
|
+
color: var(--text-dim);
|
|
1084
|
+
white-space: nowrap;
|
|
1085
|
+
}
|
|
1086
|
+
</style>
|
|
1087
|
+
</head>
|
|
1088
|
+
<body>
|
|
1089
|
+
<div class="dashboard" id="app">
|
|
1090
|
+
<!-- 1. Header Bar -->
|
|
1091
|
+
<header class="header fade-in">
|
|
1092
|
+
<div class="header-left">
|
|
1093
|
+
<div class="header-logo">Node<span>Bench</span></div>
|
|
1094
|
+
<div class="header-subtitle">Operating Dashboard</div>
|
|
1095
|
+
<div class="local-badge">
|
|
1096
|
+
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" style="flex-shrink:0">
|
|
1097
|
+
<circle cx="5" cy="5" r="4" stroke="currentColor" stroke-width="1.5" fill="none"/>
|
|
1098
|
+
<circle cx="5" cy="5" r="1.5" fill="currentColor"/>
|
|
1099
|
+
</svg>
|
|
1100
|
+
Local
|
|
1101
|
+
</div>
|
|
1102
|
+
</div>
|
|
1103
|
+
<div class="header-right">
|
|
1104
|
+
<div style="display:flex;align-items:center;gap:6px;">
|
|
1105
|
+
<div class="refresh-dot" id="refreshDot" data-tooltip="Auto-refreshing every 10s"></div>
|
|
1106
|
+
<span style="font-size:11px;color:var(--text-dim)">Auto-refresh</span>
|
|
1107
|
+
</div>
|
|
1108
|
+
<div class="header-clock" id="clock"></div>
|
|
1109
|
+
</div>
|
|
1110
|
+
</header>
|
|
1111
|
+
|
|
1112
|
+
<!-- ═══ BUSINESS INTELLIGENCE ═══════════════════════════════ -->
|
|
1113
|
+
|
|
1114
|
+
<!-- B1. Company Truth (Hero) -->
|
|
1115
|
+
<div class="section fade-in fade-in-1" id="section-biz-company">
|
|
1116
|
+
<div class="section-header">Company Truth</div>
|
|
1117
|
+
<div class="card card-hero" id="biz-company-card">
|
|
1118
|
+
<div class="biz-hero">
|
|
1119
|
+
<div class="biz-hero-main">
|
|
1120
|
+
<div class="biz-hero-name" id="biz-company-name">Loading...</div>
|
|
1121
|
+
<div class="biz-hero-mission" id="biz-company-mission"></div>
|
|
1122
|
+
<div class="biz-hero-wedge" id="biz-company-wedge"></div>
|
|
1123
|
+
</div>
|
|
1124
|
+
<div class="biz-hero-meta">
|
|
1125
|
+
<span class="badge badge-accent" id="biz-company-state">--</span>
|
|
1126
|
+
<div class="confidence-bar-wrap">
|
|
1127
|
+
<span class="confidence-label">Identity Confidence</span>
|
|
1128
|
+
<div class="confidence-track">
|
|
1129
|
+
<div class="confidence-fill" id="biz-confidence-fill" style="width:0%"></div>
|
|
1130
|
+
</div>
|
|
1131
|
+
<span class="confidence-val" id="biz-confidence-val">--</span>
|
|
1132
|
+
</div>
|
|
1133
|
+
</div>
|
|
1134
|
+
</div>
|
|
1135
|
+
</div>
|
|
1136
|
+
</div>
|
|
1137
|
+
|
|
1138
|
+
<!-- B2. Initiatives -->
|
|
1139
|
+
<div class="section fade-in fade-in-2" id="section-biz-initiatives">
|
|
1140
|
+
<div class="section-header">Initiatives <span class="count" id="biz-init-count"></span></div>
|
|
1141
|
+
<div class="grid-2" id="biz-init-grid">
|
|
1142
|
+
<div class="empty-state" style="grid-column:1/-1;">Loading initiatives...</div>
|
|
1143
|
+
</div>
|
|
1144
|
+
</div>
|
|
1145
|
+
|
|
1146
|
+
<!-- B3. Ranked Interventions -->
|
|
1147
|
+
<div class="section fade-in fade-in-3" id="section-biz-interventions">
|
|
1148
|
+
<div class="section-header">Ranked Interventions <span class="count" id="biz-intv-count"></span></div>
|
|
1149
|
+
<div class="card" id="biz-intv-list">
|
|
1150
|
+
<div class="empty-state">Loading interventions...</div>
|
|
1151
|
+
</div>
|
|
1152
|
+
</div>
|
|
1153
|
+
|
|
1154
|
+
<!-- B4 & B5: Two-column grid -->
|
|
1155
|
+
<div class="grid-2 fade-in fade-in-4">
|
|
1156
|
+
<!-- B4. Competitor Intelligence -->
|
|
1157
|
+
<div class="section" id="section-biz-competitors">
|
|
1158
|
+
<div class="section-header">Competitor Intelligence <span class="count" id="biz-comp-count"></span></div>
|
|
1159
|
+
<div id="biz-comp-list">
|
|
1160
|
+
<div class="empty-state">Loading competitors...</div>
|
|
1161
|
+
</div>
|
|
1162
|
+
</div>
|
|
1163
|
+
|
|
1164
|
+
<!-- B5. Active Contradictions -->
|
|
1165
|
+
<div class="section" id="section-biz-contradictions">
|
|
1166
|
+
<div class="section-header">Active Contradictions <span class="count" id="biz-contra-count"></span></div>
|
|
1167
|
+
<div id="biz-contra-list">
|
|
1168
|
+
<div class="empty-state">Loading contradictions...</div>
|
|
1169
|
+
</div>
|
|
1170
|
+
</div>
|
|
1171
|
+
</div>
|
|
1172
|
+
|
|
1173
|
+
<!-- B6. Agent Status -->
|
|
1174
|
+
<div class="section fade-in fade-in-5" id="section-biz-agents">
|
|
1175
|
+
<div class="section-header">Agent Status <span class="count" id="biz-agent-count"></span></div>
|
|
1176
|
+
<div class="grid-3" id="biz-agent-grid">
|
|
1177
|
+
<div class="empty-state" style="grid-column:1/-1;">Loading agents...</div>
|
|
1178
|
+
</div>
|
|
1179
|
+
</div>
|
|
1180
|
+
|
|
1181
|
+
<!-- B7. Recent Decisions -->
|
|
1182
|
+
<div class="section fade-in fade-in-6" id="section-biz-decisions">
|
|
1183
|
+
<div class="section-header">Recent Decisions <span class="count" id="biz-dec-count"></span></div>
|
|
1184
|
+
<div class="card" id="biz-dec-list">
|
|
1185
|
+
<div class="empty-state">Loading decisions...</div>
|
|
1186
|
+
</div>
|
|
1187
|
+
</div>
|
|
1188
|
+
|
|
1189
|
+
<!-- ═══ DIVIDER ═══════════════════════════════════════════════ -->
|
|
1190
|
+
<div class="section-divider">
|
|
1191
|
+
<span class="section-divider-text">System Intelligence</span>
|
|
1192
|
+
</div>
|
|
1193
|
+
|
|
1194
|
+
<!-- 2. Session Delta (Hero Card) -->
|
|
1195
|
+
<div class="section fade-in fade-in-1" id="section-delta">
|
|
1196
|
+
<div class="section-header">Since Your Last Session</div>
|
|
1197
|
+
<div class="card card-hero">
|
|
1198
|
+
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:14px;flex-wrap:wrap;gap:8px;">
|
|
1199
|
+
<div style="font-size:15px;font-weight:500;color:var(--text-primary);" id="delta-ago">Loading...</div>
|
|
1200
|
+
<div class="badge badge-accent" id="delta-badge">syncing</div>
|
|
1201
|
+
</div>
|
|
1202
|
+
<div class="grid-4" id="delta-stats">
|
|
1203
|
+
<div class="mini-stat">
|
|
1204
|
+
<div class="mini-stat-label">Strategy</div>
|
|
1205
|
+
<div class="mini-stat-value" id="delta-strategy">--</div>
|
|
1206
|
+
<div class="mini-stat-delta" id="delta-strategy-d"></div>
|
|
1207
|
+
</div>
|
|
1208
|
+
<div class="mini-stat">
|
|
1209
|
+
<div class="mini-stat-label">Competitors</div>
|
|
1210
|
+
<div class="mini-stat-value" id="delta-competitors">--</div>
|
|
1211
|
+
<div class="mini-stat-delta" id="delta-competitors-d"></div>
|
|
1212
|
+
</div>
|
|
1213
|
+
<div class="mini-stat">
|
|
1214
|
+
<div class="mini-stat-label">Contradictions</div>
|
|
1215
|
+
<div class="mini-stat-value" id="delta-contradictions">--</div>
|
|
1216
|
+
<div class="mini-stat-delta" id="delta-contradictions-d"></div>
|
|
1217
|
+
</div>
|
|
1218
|
+
<div class="mini-stat">
|
|
1219
|
+
<div class="mini-stat-label">Attention</div>
|
|
1220
|
+
<div class="mini-stat-value" id="delta-attention">--</div>
|
|
1221
|
+
<div class="mini-stat-delta" id="delta-attention-d"></div>
|
|
1222
|
+
</div>
|
|
1223
|
+
</div>
|
|
1224
|
+
</div>
|
|
1225
|
+
</div>
|
|
1226
|
+
|
|
1227
|
+
<!-- 3. Trajectory Score -->
|
|
1228
|
+
<div class="section fade-in fade-in-2" id="section-trajectory">
|
|
1229
|
+
<div class="section-header">Trajectory Score</div>
|
|
1230
|
+
<div class="card">
|
|
1231
|
+
<div class="sparkline-container">
|
|
1232
|
+
<div>
|
|
1233
|
+
<div style="display:flex;align-items:baseline;gap:4px;">
|
|
1234
|
+
<div class="score-big" id="traj-score">--</div>
|
|
1235
|
+
<span class="score-trend" id="traj-trend"></span>
|
|
1236
|
+
</div>
|
|
1237
|
+
<div style="font-size:12px;color:var(--text-muted);margin-top:4px;" id="traj-subtitle">Composite score</div>
|
|
1238
|
+
</div>
|
|
1239
|
+
<div style="flex:1;min-width:120px;">
|
|
1240
|
+
<svg id="sparkline" width="100%" height="48" viewBox="0 0 300 48" preserveAspectRatio="none">
|
|
1241
|
+
<defs>
|
|
1242
|
+
<linearGradient id="sparkGrad" x1="0" y1="0" x2="0" y2="1">
|
|
1243
|
+
<stop offset="0%" stop-color="var(--accent)" stop-opacity="0.3"/>
|
|
1244
|
+
<stop offset="100%" stop-color="var(--accent)" stop-opacity="0"/>
|
|
1245
|
+
</linearGradient>
|
|
1246
|
+
</defs>
|
|
1247
|
+
<path id="sparkArea" fill="url(#sparkGrad)" d=""/>
|
|
1248
|
+
<path id="sparkLine" fill="none" stroke="var(--accent)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d=""/>
|
|
1249
|
+
</svg>
|
|
1250
|
+
</div>
|
|
1251
|
+
</div>
|
|
1252
|
+
<div id="dim-bars">
|
|
1253
|
+
<!-- Dimension bars populated by JS -->
|
|
1254
|
+
</div>
|
|
1255
|
+
</div>
|
|
1256
|
+
</div>
|
|
1257
|
+
|
|
1258
|
+
<!-- 4. Event Ledger -->
|
|
1259
|
+
<div class="section fade-in fade-in-3" id="section-events">
|
|
1260
|
+
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;">
|
|
1261
|
+
<div class="section-header" style="margin-bottom:0;">Event Ledger <span class="count" id="event-count"></span></div>
|
|
1262
|
+
<select class="filter-select" id="event-filter">
|
|
1263
|
+
<option value="all">All types</option>
|
|
1264
|
+
<option value="decision">Decision</option>
|
|
1265
|
+
<option value="insight">Insight</option>
|
|
1266
|
+
<option value="contradiction">Contradiction</option>
|
|
1267
|
+
<option value="milestone">Milestone</option>
|
|
1268
|
+
<option value="alert">Alert</option>
|
|
1269
|
+
<option value="observation">Observation</option>
|
|
1270
|
+
</select>
|
|
1271
|
+
</div>
|
|
1272
|
+
<div class="card" style="padding:12px 16px;">
|
|
1273
|
+
<div id="event-list">
|
|
1274
|
+
<div class="empty-state">
|
|
1275
|
+
Loading events...
|
|
1276
|
+
</div>
|
|
1277
|
+
</div>
|
|
1278
|
+
</div>
|
|
1279
|
+
</div>
|
|
1280
|
+
|
|
1281
|
+
<!-- 5. Important Changes -->
|
|
1282
|
+
<div class="section fade-in fade-in-4" id="section-changes">
|
|
1283
|
+
<div class="section-header">Important Changes</div>
|
|
1284
|
+
<div class="card">
|
|
1285
|
+
<div class="status-bar" id="change-status-bar">
|
|
1286
|
+
<!-- Status counts populated by JS -->
|
|
1287
|
+
</div>
|
|
1288
|
+
<div id="change-list">
|
|
1289
|
+
<div class="empty-state">
|
|
1290
|
+
Loading changes...
|
|
1291
|
+
</div>
|
|
1292
|
+
</div>
|
|
1293
|
+
</div>
|
|
1294
|
+
</div>
|
|
1295
|
+
|
|
1296
|
+
<!-- 6. Path Replay -->
|
|
1297
|
+
<div class="section fade-in fade-in-5" id="section-path">
|
|
1298
|
+
<div class="section-header">Session Path</div>
|
|
1299
|
+
<div class="card" style="padding:12px 16px;">
|
|
1300
|
+
<div class="path-scroll" id="path-replay">
|
|
1301
|
+
<div class="empty-state" style="width:100%;">
|
|
1302
|
+
Loading path...
|
|
1303
|
+
</div>
|
|
1304
|
+
</div>
|
|
1305
|
+
</div>
|
|
1306
|
+
</div>
|
|
1307
|
+
|
|
1308
|
+
<!-- 7. Time Rollups -->
|
|
1309
|
+
<div class="section fade-in fade-in-6" id="section-rollups">
|
|
1310
|
+
<div class="section-header">Time Rollups</div>
|
|
1311
|
+
<div class="card">
|
|
1312
|
+
<div class="tab-bar" id="rollup-tabs">
|
|
1313
|
+
<button class="tab active" data-period="day">Day</button>
|
|
1314
|
+
<button class="tab" data-period="week">Week</button>
|
|
1315
|
+
<button class="tab" data-period="month">Month</button>
|
|
1316
|
+
<button class="tab" data-period="quarter">Quarter</button>
|
|
1317
|
+
<button class="tab" data-period="year">Year</button>
|
|
1318
|
+
</div>
|
|
1319
|
+
<div class="metric-grid" id="rollup-grid">
|
|
1320
|
+
<div class="empty-state" style="grid-column:1/-1;">Loading rollups...</div>
|
|
1321
|
+
</div>
|
|
1322
|
+
</div>
|
|
1323
|
+
</div>
|
|
1324
|
+
|
|
1325
|
+
<!-- 8 & 9: Two-column grid on desktop -->
|
|
1326
|
+
<div class="grid-2 fade-in fade-in-7">
|
|
1327
|
+
<!-- 8. Packet Readiness -->
|
|
1328
|
+
<div class="section" id="section-packets">
|
|
1329
|
+
<div class="section-header">Packet Readiness</div>
|
|
1330
|
+
<div class="card">
|
|
1331
|
+
<div id="packet-list">
|
|
1332
|
+
<div class="empty-state">Loading packets...</div>
|
|
1333
|
+
</div>
|
|
1334
|
+
</div>
|
|
1335
|
+
</div>
|
|
1336
|
+
|
|
1337
|
+
<!-- 9. Recent Actions -->
|
|
1338
|
+
<div class="section" id="section-actions">
|
|
1339
|
+
<div class="section-header">Recent Actions <span class="count" id="action-count"></span></div>
|
|
1340
|
+
<div class="card" style="padding:12px 16px;">
|
|
1341
|
+
<div id="action-list">
|
|
1342
|
+
<div class="empty-state">Loading actions...</div>
|
|
1343
|
+
</div>
|
|
1344
|
+
</div>
|
|
1345
|
+
</div>
|
|
1346
|
+
</div>
|
|
1347
|
+
|
|
1348
|
+
<!-- 10. Footer -->
|
|
1349
|
+
<footer class="footer fade-in fade-in-9">
|
|
1350
|
+
<div class="footer-text">
|
|
1351
|
+
<span>NodeBench</span> MCP v2.34.0 · 325 tools · 30 tables · auto-refresh: 10s
|
|
1352
|
+
</div>
|
|
1353
|
+
</footer>
|
|
1354
|
+
</div>
|
|
1355
|
+
|
|
1356
|
+
<script>
|
|
1357
|
+
(function() {
|
|
1358
|
+
'use strict';
|
|
1359
|
+
|
|
1360
|
+
// ── Config ──────────────────────────────────────────────
|
|
1361
|
+
const REFRESH_INTERVAL = 10000;
|
|
1362
|
+
const API_BASE = '';
|
|
1363
|
+
const ENDPOINTS = {
|
|
1364
|
+
// Business Intelligence
|
|
1365
|
+
bizCompany: '/api/business/company',
|
|
1366
|
+
bizInitiatives: '/api/business/initiatives',
|
|
1367
|
+
bizInterventions: '/api/business/interventions',
|
|
1368
|
+
bizAgents: '/api/business/agents',
|
|
1369
|
+
bizCompetitors: '/api/business/competitors',
|
|
1370
|
+
bizContradictions:'/api/business/contradictions',
|
|
1371
|
+
bizDecisions: '/api/business/decisions',
|
|
1372
|
+
// System Intelligence
|
|
1373
|
+
delta: '/api/ambient/session-delta',
|
|
1374
|
+
trajectory: '/api/causal/trajectory',
|
|
1375
|
+
events: '/api/causal/events',
|
|
1376
|
+
changes: '/api/causal/changes',
|
|
1377
|
+
path: '/api/causal/path',
|
|
1378
|
+
rollups: '/api/causal/rollups',
|
|
1379
|
+
packets: '/api/ambient/packets',
|
|
1380
|
+
actions: '/api/tracking/actions',
|
|
1381
|
+
};
|
|
1382
|
+
|
|
1383
|
+
// ── State ───────────────────────────────────────────────
|
|
1384
|
+
let _hashes = {};
|
|
1385
|
+
let _rollupPeriod = 'day';
|
|
1386
|
+
let _eventFilter = 'all';
|
|
1387
|
+
let _rollupData = null;
|
|
1388
|
+
|
|
1389
|
+
// ── Utils ───────────────────────────────────────────────
|
|
1390
|
+
function esc(s) {
|
|
1391
|
+
if (s == null) return '';
|
|
1392
|
+
return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
function simpleHash(str) {
|
|
1396
|
+
let h = 0;
|
|
1397
|
+
for (let i = 0; i < str.length; i++) {
|
|
1398
|
+
h = ((h << 5) - h + str.charCodeAt(i)) | 0;
|
|
1399
|
+
}
|
|
1400
|
+
return h;
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
function timeAgo(ts) {
|
|
1404
|
+
if (!ts) return 'never';
|
|
1405
|
+
const diff = Date.now() - new Date(ts).getTime();
|
|
1406
|
+
if (diff < 0) return 'just now';
|
|
1407
|
+
const s = Math.floor(diff / 1000);
|
|
1408
|
+
if (s < 60) return s + 's ago';
|
|
1409
|
+
const m = Math.floor(s / 60);
|
|
1410
|
+
if (m < 60) return m + 'm ago';
|
|
1411
|
+
const h = Math.floor(m / 60);
|
|
1412
|
+
if (h < 24) return h + 'h ago';
|
|
1413
|
+
const d = Math.floor(h / 24);
|
|
1414
|
+
return d + 'd ago';
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
function deltaStr(val) {
|
|
1418
|
+
if (val == null || val === 0) return '';
|
|
1419
|
+
const cls = val > 0 ? 'trend-up' : 'trend-down';
|
|
1420
|
+
const arrow = val > 0 ? '\\u2191' : '\\u2193';
|
|
1421
|
+
return '<span class="' + cls + '">' + arrow + Math.abs(val) + '</span>';
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
function barColor(val) {
|
|
1425
|
+
if (val >= 80) return 'var(--emerald)';
|
|
1426
|
+
if (val >= 60) return 'var(--accent)';
|
|
1427
|
+
if (val >= 40) return 'var(--amber)';
|
|
1428
|
+
return 'var(--red)';
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
function stalenessColor(pct) {
|
|
1432
|
+
if (pct <= 30) return 'var(--emerald)';
|
|
1433
|
+
if (pct <= 60) return 'var(--amber)';
|
|
1434
|
+
return 'var(--red)';
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
function actorBadge(actor) {
|
|
1438
|
+
const a = String(actor || 'S').charAt(0).toUpperCase();
|
|
1439
|
+
const labels = { F: 'Founder', A: 'Agent', S: 'System', B: 'Background' };
|
|
1440
|
+
return '<div class="actor-badge actor-' + a + '" data-tooltip="' + esc(labels[a] || actor) + '">' + a + '</div>';
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
function eventTypeBadge(type) {
|
|
1444
|
+
const colors = {
|
|
1445
|
+
decision: 'accent', insight: 'cyan', contradiction: 'amber',
|
|
1446
|
+
milestone: 'emerald', alert: 'red', observation: 'blue'
|
|
1447
|
+
};
|
|
1448
|
+
return '<span class="badge badge-' + (colors[type] || 'blue') + '">' + esc(type || 'event') + '</span>';
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
function impactBadge(level) {
|
|
1452
|
+
const colors = { critical: 'red', high: 'amber', medium: 'blue', low: 'violet' };
|
|
1453
|
+
return '<span class="badge badge-' + (colors[level] || 'blue') + '">' + esc(level || 'info') + '</span>';
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
function chipColor(surfaceType) {
|
|
1457
|
+
if (surfaceType === 'entity') return 'var(--cyan)';
|
|
1458
|
+
if (surfaceType === 'artifact') return 'var(--violet)';
|
|
1459
|
+
return 'var(--text-primary)';
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
// ── Sparkline SVG ───────────────────────────────────────
|
|
1463
|
+
function renderSparkline(data) {
|
|
1464
|
+
if (!data || data.length < 2) return;
|
|
1465
|
+
const svgW = 300, svgH = 48, pad = 2;
|
|
1466
|
+
const min = Math.min(...data);
|
|
1467
|
+
const max = Math.max(...data);
|
|
1468
|
+
const range = max - min || 1;
|
|
1469
|
+
const points = data.map(function(v, i) {
|
|
1470
|
+
const x = pad + (i / (data.length - 1)) * (svgW - 2 * pad);
|
|
1471
|
+
const y = pad + (1 - (v - min) / range) * (svgH - 2 * pad);
|
|
1472
|
+
return [x, y];
|
|
1473
|
+
});
|
|
1474
|
+
const lineD = points.map(function(p, i) { return (i === 0 ? 'M' : 'L') + p[0].toFixed(1) + ',' + p[1].toFixed(1); }).join(' ');
|
|
1475
|
+
var areaD = lineD + ' L' + points[points.length-1][0].toFixed(1) + ',' + svgH + ' L' + points[0][0].toFixed(1) + ',' + svgH + ' Z';
|
|
1476
|
+
document.getElementById('sparkLine').setAttribute('d', lineD);
|
|
1477
|
+
document.getElementById('sparkArea').setAttribute('d', areaD);
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
// ── Fetcher ─────────────────────────────────────────────
|
|
1481
|
+
async function fetchData(endpoint) {
|
|
1482
|
+
try {
|
|
1483
|
+
const res = await fetch(API_BASE + endpoint, { signal: AbortSignal.timeout(8000) });
|
|
1484
|
+
if (!res.ok) return null;
|
|
1485
|
+
return await res.json();
|
|
1486
|
+
} catch (e) {
|
|
1487
|
+
return null;
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
// ── Business Renderers ─────────────────────────────────
|
|
1492
|
+
|
|
1493
|
+
function bizStatusBadge(status) {
|
|
1494
|
+
var colors = {
|
|
1495
|
+
active: 'emerald', completed: 'blue', in_progress: 'accent',
|
|
1496
|
+
blocked: 'red', paused: 'amber', suggested: 'cyan',
|
|
1497
|
+
accepted: 'emerald', rejected: 'red', deferred: 'amber',
|
|
1498
|
+
resolved: 'blue', healthy: 'emerald', waiting: 'amber', drifting: 'red'
|
|
1499
|
+
};
|
|
1500
|
+
return '<span class="badge badge-' + (colors[status] || 'blue') + '">' + esc(status || 'unknown') + '</span>';
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
function bizSeverityBadge(severity) {
|
|
1504
|
+
var colors = { high: 'red', medium: 'amber', low: 'blue' };
|
|
1505
|
+
return '<span class="badge badge-' + (colors[severity] || 'blue') + '">' + esc(severity || 'medium') + '</span>';
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
function bizRiskBadge(risk) {
|
|
1509
|
+
var colors = { high: 'red', medium: 'amber', low: 'emerald' };
|
|
1510
|
+
return '<span class="badge badge-' + (colors[risk] || 'blue') + '">' + esc(risk || 'medium') + ' risk</span>';
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
function renderBizCompany(data) {
|
|
1514
|
+
var c = data && data.company;
|
|
1515
|
+
if (!c) {
|
|
1516
|
+
document.getElementById('biz-company-name').textContent = 'No company data';
|
|
1517
|
+
return;
|
|
1518
|
+
}
|
|
1519
|
+
document.getElementById('biz-company-name').textContent = c.name || '--';
|
|
1520
|
+
document.getElementById('biz-company-mission').textContent = c.canonicalMission || '';
|
|
1521
|
+
document.getElementById('biz-company-wedge').textContent = c.wedge ? 'Wedge: ' + c.wedge : '';
|
|
1522
|
+
document.getElementById('biz-company-state').textContent = c.companyState || 'operating';
|
|
1523
|
+
var conf = Math.round((c.identityConfidence || 0) * 100);
|
|
1524
|
+
document.getElementById('biz-confidence-fill').style.width = conf + '%';
|
|
1525
|
+
document.getElementById('biz-confidence-val').textContent = conf + '%';
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
function renderBizInitiatives(data) {
|
|
1529
|
+
var container = document.getElementById('biz-init-grid');
|
|
1530
|
+
if (!data || !data.initiatives || data.initiatives.length === 0) {
|
|
1531
|
+
container.innerHTML = '<div class="empty-state" style="grid-column:1/-1;">No initiatives tracked yet.</div>';
|
|
1532
|
+
document.getElementById('biz-init-count').textContent = '';
|
|
1533
|
+
return;
|
|
1534
|
+
}
|
|
1535
|
+
document.getElementById('biz-init-count').textContent = '(' + data.initiatives.length + ')';
|
|
1536
|
+
var html = '';
|
|
1537
|
+
data.initiatives.forEach(function(init) {
|
|
1538
|
+
html += '<div class="biz-init-card">'
|
|
1539
|
+
+ '<div class="biz-init-header">'
|
|
1540
|
+
+ '<span class="biz-init-title">' + esc(init.title) + '</span>'
|
|
1541
|
+
+ bizStatusBadge(init.status)
|
|
1542
|
+
+ '</div>';
|
|
1543
|
+
if (init.latestSummary) {
|
|
1544
|
+
html += '<div class="biz-init-summary">' + esc(init.latestSummary) + '</div>';
|
|
1545
|
+
}
|
|
1546
|
+
html += '<div class="biz-init-meta">'
|
|
1547
|
+
+ '<span class="biz-priority">P' + (init.priorityScore != null ? Number(init.priorityScore).toFixed(1) : '--') + '</span>'
|
|
1548
|
+
+ bizRiskBadge(init.riskLevel)
|
|
1549
|
+
+ '</div></div>';
|
|
1550
|
+
});
|
|
1551
|
+
container.innerHTML = html;
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
function renderBizInterventions(data) {
|
|
1555
|
+
var container = document.getElementById('biz-intv-list');
|
|
1556
|
+
if (!data || !data.interventions || data.interventions.length === 0) {
|
|
1557
|
+
container.innerHTML = '<div class="empty-state">No interventions suggested yet.</div>';
|
|
1558
|
+
document.getElementById('biz-intv-count').textContent = '';
|
|
1559
|
+
return;
|
|
1560
|
+
}
|
|
1561
|
+
document.getElementById('biz-intv-count').textContent = '(' + data.interventions.length + ')';
|
|
1562
|
+
var html = '';
|
|
1563
|
+
data.interventions.forEach(function(intv, idx) {
|
|
1564
|
+
var conf = Math.round((intv.confidence || 0) * 100);
|
|
1565
|
+
html += '<div class="biz-intv-card">'
|
|
1566
|
+
+ '<div class="biz-intv-header">'
|
|
1567
|
+
+ '<span class="biz-intv-rank">#' + (idx + 1) + '</span>'
|
|
1568
|
+
+ '<span class="biz-intv-title">' + esc(intv.title) + '</span>'
|
|
1569
|
+
+ bizStatusBadge(intv.status)
|
|
1570
|
+
+ '</div>';
|
|
1571
|
+
if (intv.description) {
|
|
1572
|
+
html += '<div class="biz-intv-desc">' + esc(intv.description) + '</div>';
|
|
1573
|
+
}
|
|
1574
|
+
html += '<div class="biz-intv-meta">'
|
|
1575
|
+
+ '<div class="biz-intv-confidence">'
|
|
1576
|
+
+ '<span class="confidence-label">Confidence</span>'
|
|
1577
|
+
+ '<div class="confidence-track" style="width:60px;">'
|
|
1578
|
+
+ '<div class="confidence-fill" style="width:' + conf + '%;"></div></div>'
|
|
1579
|
+
+ '<span style="font-family:var(--font-mono);font-size:11px;color:var(--text-secondary);">' + conf + '%</span>'
|
|
1580
|
+
+ '</div>'
|
|
1581
|
+
+ '<span class="biz-priority">P' + (intv.priorityScore != null ? Number(intv.priorityScore).toFixed(1) : '--') + '</span>';
|
|
1582
|
+
if (intv.expectedImpact) {
|
|
1583
|
+
html += '<span class="biz-intv-impact">' + esc(intv.expectedImpact) + '</span>';
|
|
1584
|
+
}
|
|
1585
|
+
html += '</div></div>';
|
|
1586
|
+
});
|
|
1587
|
+
container.innerHTML = html;
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
function renderBizCompetitors(data) {
|
|
1591
|
+
var container = document.getElementById('biz-comp-list');
|
|
1592
|
+
if (!data || !data.competitors || data.competitors.length === 0) {
|
|
1593
|
+
container.innerHTML = '<div class="empty-state">No competitors tracked.</div>';
|
|
1594
|
+
document.getElementById('biz-comp-count').textContent = '';
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1597
|
+
document.getElementById('biz-comp-count').textContent = '(' + data.competitors.length + ')';
|
|
1598
|
+
var html = '';
|
|
1599
|
+
data.competitors.forEach(function(comp) {
|
|
1600
|
+
html += '<div class="biz-comp-card" style="margin-bottom:10px;">'
|
|
1601
|
+
+ '<div class="biz-comp-name">' + esc(comp.name) + '</div>'
|
|
1602
|
+
+ '<div class="biz-comp-desc">' + esc(comp.description) + '</div>'
|
|
1603
|
+
+ '<div class="biz-comp-row">'
|
|
1604
|
+
+ '<span class="biz-comp-label biz-comp-label-threat">Threat</span>'
|
|
1605
|
+
+ '<span class="biz-comp-text">' + esc(comp.threat) + '</span>'
|
|
1606
|
+
+ '</div>'
|
|
1607
|
+
+ '<div class="biz-comp-row">'
|
|
1608
|
+
+ '<span class="biz-comp-label biz-comp-label-opp">Opportunity</span>'
|
|
1609
|
+
+ '<span class="biz-comp-text">' + esc(comp.opportunity) + '</span>'
|
|
1610
|
+
+ '</div>'
|
|
1611
|
+
+ '</div>';
|
|
1612
|
+
});
|
|
1613
|
+
container.innerHTML = html;
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
function renderBizContradictions(data) {
|
|
1617
|
+
var container = document.getElementById('biz-contra-list');
|
|
1618
|
+
if (!data || !data.contradictions || data.contradictions.length === 0) {
|
|
1619
|
+
container.innerHTML = '<div class="empty-state">No contradictions detected.</div>';
|
|
1620
|
+
document.getElementById('biz-contra-count').textContent = '';
|
|
1621
|
+
return;
|
|
1622
|
+
}
|
|
1623
|
+
document.getElementById('biz-contra-count').textContent = '(' + data.contradictions.length + ')';
|
|
1624
|
+
var html = '';
|
|
1625
|
+
data.contradictions.forEach(function(c) {
|
|
1626
|
+
var entities = '';
|
|
1627
|
+
try { entities = JSON.parse(c.affectedEntities || '[]').join(', '); } catch(e) { entities = c.affectedEntities || ''; }
|
|
1628
|
+
html += '<div class="biz-contra-card">'
|
|
1629
|
+
+ '<div class="biz-contra-header">'
|
|
1630
|
+
+ bizSeverityBadge(c.severity)
|
|
1631
|
+
+ '<span class="biz-contra-title">' + esc(c.title) + '</span>'
|
|
1632
|
+
+ bizStatusBadge(c.status)
|
|
1633
|
+
+ '</div>'
|
|
1634
|
+
+ '<div class="biz-contra-desc">' + esc(c.description) + '</div>';
|
|
1635
|
+
if (entities) {
|
|
1636
|
+
html += '<div class="biz-contra-entities">Affects: ' + esc(entities) + '</div>';
|
|
1637
|
+
}
|
|
1638
|
+
html += '</div>';
|
|
1639
|
+
});
|
|
1640
|
+
container.innerHTML = html;
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
function renderBizAgents(data) {
|
|
1644
|
+
var container = document.getElementById('biz-agent-grid');
|
|
1645
|
+
if (!data || !data.agents || data.agents.length === 0) {
|
|
1646
|
+
container.innerHTML = '<div class="empty-state" style="grid-column:1/-1;">No agents registered.</div>';
|
|
1647
|
+
document.getElementById('biz-agent-count').textContent = '';
|
|
1648
|
+
return;
|
|
1649
|
+
}
|
|
1650
|
+
document.getElementById('biz-agent-count').textContent = '(' + data.agents.length + ')';
|
|
1651
|
+
var html = '';
|
|
1652
|
+
data.agents.forEach(function(a) {
|
|
1653
|
+
var dotClass = 'biz-agent-dot-' + (a.status || 'waiting');
|
|
1654
|
+
html += '<div class="biz-agent-card">'
|
|
1655
|
+
+ '<div class="biz-agent-header">'
|
|
1656
|
+
+ '<div class="biz-agent-dot ' + dotClass + '"></div>'
|
|
1657
|
+
+ '<span class="biz-agent-name">' + esc(a.name) + '</span>'
|
|
1658
|
+
+ '</div>'
|
|
1659
|
+
+ '<div class="biz-agent-type">' + esc(a.agentType || 'agent') + '</div>';
|
|
1660
|
+
if (a.currentGoal) {
|
|
1661
|
+
html += '<div class="biz-agent-goal">' + esc(a.currentGoal) + '</div>';
|
|
1662
|
+
}
|
|
1663
|
+
if (a.lastHeartbeatAt) {
|
|
1664
|
+
html += '<div class="biz-agent-heartbeat">Last heartbeat: ' + timeAgo(a.lastHeartbeatAt) + '</div>';
|
|
1665
|
+
}
|
|
1666
|
+
html += '</div>';
|
|
1667
|
+
});
|
|
1668
|
+
container.innerHTML = html;
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
function renderBizDecisions(data) {
|
|
1672
|
+
var container = document.getElementById('biz-dec-list');
|
|
1673
|
+
if (!data || !data.decisions || data.decisions.length === 0) {
|
|
1674
|
+
container.innerHTML = '<div class="empty-state">No decisions recorded.</div>';
|
|
1675
|
+
document.getElementById('biz-dec-count').textContent = '';
|
|
1676
|
+
return;
|
|
1677
|
+
}
|
|
1678
|
+
document.getElementById('biz-dec-count').textContent = '(' + data.decisions.length + ')';
|
|
1679
|
+
var html = '';
|
|
1680
|
+
data.decisions.forEach(function(d) {
|
|
1681
|
+
html += '<div class="biz-decision-row">'
|
|
1682
|
+
+ '<div style="flex-shrink:0;">' + bizStatusBadge(d.status) + '</div>'
|
|
1683
|
+
+ '<div style="flex:1;min-width:0;">'
|
|
1684
|
+
+ '<div class="biz-decision-title">' + esc(d.title) + '</div>'
|
|
1685
|
+
+ '<div class="biz-decision-rationale">' + esc(d.rationale || '') + '</div>'
|
|
1686
|
+
+ '</div>'
|
|
1687
|
+
+ '<div class="event-time">' + timeAgo(d.decidedAt) + '</div>'
|
|
1688
|
+
+ '</div>';
|
|
1689
|
+
});
|
|
1690
|
+
container.innerHTML = html;
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
// ── System Renderers ──────────────────────────────────
|
|
1694
|
+
|
|
1695
|
+
function renderDelta(data) {
|
|
1696
|
+
if (!data) {
|
|
1697
|
+
document.getElementById('delta-ago').textContent = 'No session data yet';
|
|
1698
|
+
document.getElementById('delta-badge').textContent = 'awaiting';
|
|
1699
|
+
return;
|
|
1700
|
+
}
|
|
1701
|
+
document.getElementById('delta-ago').textContent = data.lastSessionAgo || timeAgo(data.lastSessionAt);
|
|
1702
|
+
document.getElementById('delta-badge').textContent = data.status || 'synced';
|
|
1703
|
+
|
|
1704
|
+
var fields = ['strategy','competitors','contradictions','attention'];
|
|
1705
|
+
fields.forEach(function(f) {
|
|
1706
|
+
var val = data[f];
|
|
1707
|
+
var el = document.getElementById('delta-' + f);
|
|
1708
|
+
var delEl = document.getElementById('delta-' + f + '-d');
|
|
1709
|
+
el.textContent = val != null ? val : '--';
|
|
1710
|
+
if (delEl) delEl.innerHTML = deltaStr(data[f + 'Delta']);
|
|
1711
|
+
});
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
function renderTrajectory(data) {
|
|
1715
|
+
if (!data) {
|
|
1716
|
+
document.getElementById('traj-score').textContent = '--';
|
|
1717
|
+
document.getElementById('traj-trend').innerHTML = '';
|
|
1718
|
+
document.getElementById('dim-bars').innerHTML = '<div class="empty-state">No trajectory data yet.<br>Run <code>record_event</code> to start tracking.</div>';
|
|
1719
|
+
return;
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
var score = data.score != null ? data.score : data.composite;
|
|
1723
|
+
document.getElementById('traj-score').textContent = score != null ? Math.round(score) : '--';
|
|
1724
|
+
|
|
1725
|
+
if (data.trend != null) {
|
|
1726
|
+
var cls = data.trend > 0 ? 'trend-up' : data.trend < 0 ? 'trend-down' : 'trend-flat';
|
|
1727
|
+
var arrow = data.trend > 0 ? '\\u2191' : data.trend < 0 ? '\\u2193' : '\\u2192';
|
|
1728
|
+
document.getElementById('traj-trend').innerHTML = '<span class="' + cls + '">' + arrow + ' ' + Math.abs(data.trend).toFixed(1) + '</span>';
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
if (data.sparkline) renderSparkline(data.sparkline);
|
|
1732
|
+
|
|
1733
|
+
var dims = data.dimensions || {};
|
|
1734
|
+
var dimNames = [
|
|
1735
|
+
['identityClarity', 'Identity Clarity'],
|
|
1736
|
+
['executionVelocity', 'Execution Velocity'],
|
|
1737
|
+
['agentAlignment', 'Agent Alignment'],
|
|
1738
|
+
['signalStrength', 'Signal Strength'],
|
|
1739
|
+
['interventionEffectiveness', 'Intervention Effect.'],
|
|
1740
|
+
['contradictionLoad', 'Contradiction Load'],
|
|
1741
|
+
['confidenceTrend', 'Confidence Trend']
|
|
1742
|
+
];
|
|
1743
|
+
var html = '';
|
|
1744
|
+
dimNames.forEach(function(pair) {
|
|
1745
|
+
var key = pair[0], label = pair[1];
|
|
1746
|
+
var val = dims[key] != null ? Math.round(dims[key]) : 0;
|
|
1747
|
+
html += '<div class="dim-bar-row">'
|
|
1748
|
+
+ '<div class="dim-bar-label">' + esc(label) + '</div>'
|
|
1749
|
+
+ '<div class="dim-bar-track"><div class="dim-bar-fill" style="width:' + val + '%;background:' + barColor(val) + ';"></div></div>'
|
|
1750
|
+
+ '<div class="dim-bar-value">' + val + '</div>'
|
|
1751
|
+
+ '</div>';
|
|
1752
|
+
});
|
|
1753
|
+
document.getElementById('dim-bars').innerHTML = html;
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
function renderEvents(data) {
|
|
1757
|
+
var container = document.getElementById('event-list');
|
|
1758
|
+
if (!data || !data.events || data.events.length === 0) {
|
|
1759
|
+
container.innerHTML = '<div class="empty-state">No events recorded yet.<br>Run <code>record_event</code> or <code>record_founder_event</code> to start populating.</div>';
|
|
1760
|
+
document.getElementById('event-count').textContent = '';
|
|
1761
|
+
return;
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
var events = data.events;
|
|
1765
|
+
if (_eventFilter !== 'all') {
|
|
1766
|
+
events = events.filter(function(e) { return e.type === _eventFilter; });
|
|
1767
|
+
}
|
|
1768
|
+
document.getElementById('event-count').textContent = '(' + events.length + ')';
|
|
1769
|
+
|
|
1770
|
+
if (events.length === 0) {
|
|
1771
|
+
container.innerHTML = '<div class="empty-state">No ' + esc(_eventFilter) + ' events found.</div>';
|
|
1772
|
+
return;
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
var html = '';
|
|
1776
|
+
events.slice(0, 20).forEach(function(evt) {
|
|
1777
|
+
html += '<div class="event-row">'
|
|
1778
|
+
+ actorBadge(evt.actor)
|
|
1779
|
+
+ eventTypeBadge(evt.type)
|
|
1780
|
+
+ '<div class="event-summary">' + esc(evt.summary || evt.description || '') + '</div>'
|
|
1781
|
+
+ '<div class="event-time">' + timeAgo(evt.timestamp || evt.createdAt) + '</div>'
|
|
1782
|
+
+ '</div>';
|
|
1783
|
+
});
|
|
1784
|
+
container.innerHTML = html;
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
function renderChanges(data) {
|
|
1788
|
+
var statusBar = document.getElementById('change-status-bar');
|
|
1789
|
+
var container = document.getElementById('change-list');
|
|
1790
|
+
|
|
1791
|
+
if (!data || !data.changes || data.changes.length === 0) {
|
|
1792
|
+
statusBar.innerHTML = '';
|
|
1793
|
+
container.innerHTML = '<div class="empty-state">No important changes detected.<br>Run <code>track_action</code> or <code>record_change</code> to start tracking.</div>';
|
|
1794
|
+
return;
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1797
|
+
// Status summary
|
|
1798
|
+
var counts = data.statusCounts || {};
|
|
1799
|
+
var statuses = [
|
|
1800
|
+
{ key: 'detected', color: 'var(--blue)', label: 'Detected' },
|
|
1801
|
+
{ key: 'acknowledged', color: 'var(--cyan)', label: 'Acknowledged' },
|
|
1802
|
+
{ key: 'investigating', color: 'var(--amber)', label: 'Investigating' },
|
|
1803
|
+
{ key: 'resolved', color: 'var(--emerald)', label: 'Resolved' },
|
|
1804
|
+
{ key: 'dismissed', color: 'var(--text-dim)', label: 'Dismissed' }
|
|
1805
|
+
];
|
|
1806
|
+
var sbHtml = '';
|
|
1807
|
+
statuses.forEach(function(s) {
|
|
1808
|
+
var n = counts[s.key] || 0;
|
|
1809
|
+
if (n > 0 || s.key === 'detected') {
|
|
1810
|
+
sbHtml += '<div class="status-item">'
|
|
1811
|
+
+ '<div class="status-dot" style="background:' + s.color + ';"></div>'
|
|
1812
|
+
+ '<span class="status-num stat">' + n + '</span> ' + s.label
|
|
1813
|
+
+ '</div>';
|
|
1814
|
+
}
|
|
1815
|
+
});
|
|
1816
|
+
statusBar.innerHTML = sbHtml;
|
|
1817
|
+
|
|
1818
|
+
// Change cards
|
|
1819
|
+
var html = '';
|
|
1820
|
+
data.changes.slice(0, 10).forEach(function(ch) {
|
|
1821
|
+
html += '<div class="change-card">'
|
|
1822
|
+
+ '<div class="change-header">'
|
|
1823
|
+
+ impactBadge(ch.impact || ch.priority)
|
|
1824
|
+
+ '<span style="font-size:13px;font-weight:500;color:var(--text-primary);">' + esc(ch.title || ch.category || 'Change') + '</span>'
|
|
1825
|
+
+ '</div>'
|
|
1826
|
+
+ '<div class="change-summary">' + esc(ch.summary || ch.description || '') + '</div>';
|
|
1827
|
+
if (ch.suggestedAction) {
|
|
1828
|
+
html += '<div style="font-size:11px;color:var(--text-muted);margin-bottom:8px;">'
|
|
1829
|
+
+ '<span style="color:var(--accent);font-weight:600;">Suggested:</span> ' + esc(ch.suggestedAction)
|
|
1830
|
+
+ '</div>';
|
|
1831
|
+
}
|
|
1832
|
+
html += '<div class="change-actions">'
|
|
1833
|
+
+ '<button class="btn btn-emerald" onclick="postChangeAction(\\'' + esc(ch.id) + '\\',\\'resolve\\')">Resolve</button>'
|
|
1834
|
+
+ '<button class="btn btn-accent" onclick="postChangeAction(\\'' + esc(ch.id) + '\\',\\'investigate\\')">Investigate</button>'
|
|
1835
|
+
+ '<button class="btn btn-red" onclick="postChangeAction(\\'' + esc(ch.id) + '\\',\\'dismiss\\')">Dismiss</button>'
|
|
1836
|
+
+ '</div></div>';
|
|
1837
|
+
});
|
|
1838
|
+
container.innerHTML = html;
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1841
|
+
function renderPath(data) {
|
|
1842
|
+
var container = document.getElementById('path-replay');
|
|
1843
|
+
if (!data || !data.steps || data.steps.length === 0) {
|
|
1844
|
+
container.innerHTML = '<div class="empty-state" style="width:100%;">No session path recorded.<br>Navigate surfaces to build a path trace.</div>';
|
|
1845
|
+
return;
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
var html = '';
|
|
1849
|
+
data.steps.forEach(function(step, i) {
|
|
1850
|
+
if (i > 0) {
|
|
1851
|
+
html += '<div class="path-arrow">\\u2192</div>';
|
|
1852
|
+
}
|
|
1853
|
+
var color = chipColor(step.surfaceType || step.type);
|
|
1854
|
+
html += '<div class="path-chip" style="border-color:' + color.replace(')', ',0.25)').replace('var(--text-primary)', 'var(--border-subtle)') + ';">'
|
|
1855
|
+
+ '<div class="path-chip-name" style="color:' + color + ';">' + esc(step.name || step.surface || 'view') + '</div>'
|
|
1856
|
+
+ '<div class="path-chip-dur">' + (step.durationMs ? (step.durationMs / 1000).toFixed(1) + 's' : '--') + '</div>'
|
|
1857
|
+
+ '</div>';
|
|
1858
|
+
});
|
|
1859
|
+
container.innerHTML = html;
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
function renderRollups(data) {
|
|
1863
|
+
_rollupData = data;
|
|
1864
|
+
var container = document.getElementById('rollup-grid');
|
|
1865
|
+
|
|
1866
|
+
if (!data || !data.periods) {
|
|
1867
|
+
container.innerHTML = '<div class="empty-state" style="grid-column:1/-1;">No rollup data yet.<br>Rollups accumulate as events are recorded over time.</div>';
|
|
1868
|
+
return;
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1871
|
+
var periodData = data.periods[_rollupPeriod];
|
|
1872
|
+
if (!periodData || !periodData.metrics) {
|
|
1873
|
+
container.innerHTML = '<div class="empty-state" style="grid-column:1/-1;">No data for this period.</div>';
|
|
1874
|
+
return;
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
var html = '';
|
|
1878
|
+
var metrics = periodData.metrics;
|
|
1879
|
+
Object.keys(metrics).forEach(function(key) {
|
|
1880
|
+
var m = metrics[key];
|
|
1881
|
+
var val = m.value != null ? m.value : m;
|
|
1882
|
+
var delta = m.delta;
|
|
1883
|
+
var label = key.replace(/([A-Z])/g, ' $1').replace(/^./, function(c) { return c.toUpperCase(); });
|
|
1884
|
+
html += '<div class="mini-stat">'
|
|
1885
|
+
+ '<div class="mini-stat-label">' + esc(label) + '</div>'
|
|
1886
|
+
+ '<div class="mini-stat-value">' + (typeof val === 'number' ? Math.round(val) : esc(val)) + '</div>'
|
|
1887
|
+
+ '<div class="mini-stat-delta">' + deltaStr(delta) + '</div>'
|
|
1888
|
+
+ '</div>';
|
|
1889
|
+
});
|
|
1890
|
+
container.innerHTML = html;
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
function renderPackets(data) {
|
|
1894
|
+
var container = document.getElementById('packet-list');
|
|
1895
|
+
if (!data || !data.packets || data.packets.length === 0) {
|
|
1896
|
+
container.innerHTML = '<div class="empty-state">No packet data yet.<br>Packets represent pre-built artifact bundles like Decision Memos and Agent Briefs.</div>';
|
|
1897
|
+
return;
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
var html = '';
|
|
1901
|
+
data.packets.forEach(function(pkt) {
|
|
1902
|
+
var staleness = pkt.staleness != null ? Math.min(100, Math.max(0, pkt.staleness)) : 0;
|
|
1903
|
+
html += '<div class="packet-card">'
|
|
1904
|
+
+ '<div class="packet-header">'
|
|
1905
|
+
+ '<div class="packet-name">' + esc(pkt.type || pkt.name) + '</div>'
|
|
1906
|
+
+ '<span class="badge badge-' + (staleness > 60 ? 'red' : staleness > 30 ? 'amber' : 'emerald') + '">'
|
|
1907
|
+
+ staleness + '% stale</span>'
|
|
1908
|
+
+ '</div>';
|
|
1909
|
+
if (pkt.changeCount != null) {
|
|
1910
|
+
html += '<div style="font-size:12px;color:var(--text-muted);">' + pkt.changeCount + ' change' + (pkt.changeCount !== 1 ? 's' : '') + ' since last gen</div>';
|
|
1911
|
+
}
|
|
1912
|
+
if (pkt.reason) {
|
|
1913
|
+
html += '<div style="font-size:11px;color:var(--text-dim);margin-top:4px;">Reason: ' + esc(pkt.reason) + '</div>';
|
|
1914
|
+
}
|
|
1915
|
+
html += '<div class="staleness-track">'
|
|
1916
|
+
+ '<div class="staleness-fill" style="width:' + staleness + '%;background:' + stalenessColor(staleness) + ';"></div>'
|
|
1917
|
+
+ '</div></div>';
|
|
1918
|
+
html += '<div style="height:8px;"></div>';
|
|
1919
|
+
});
|
|
1920
|
+
container.innerHTML = html;
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1923
|
+
function renderActions(data) {
|
|
1924
|
+
var container = document.getElementById('action-list');
|
|
1925
|
+
if (!data || !data.actions || data.actions.length === 0) {
|
|
1926
|
+
container.innerHTML = '<div class="empty-state">No tracked actions yet.<br>Run <code>track_action</code> to record founder decisions and changes.</div>';
|
|
1927
|
+
document.getElementById('action-count').textContent = '';
|
|
1928
|
+
return;
|
|
1929
|
+
}
|
|
1930
|
+
|
|
1931
|
+
document.getElementById('action-count').textContent = '(' + data.actions.length + ')';
|
|
1932
|
+
var html = '';
|
|
1933
|
+
data.actions.slice(0, 10).forEach(function(act) {
|
|
1934
|
+
html += '<div class="action-row">'
|
|
1935
|
+
+ '<div class="action-meta">'
|
|
1936
|
+
+ impactBadge(act.impact || act.category || 'info')
|
|
1937
|
+
+ '</div>'
|
|
1938
|
+
+ '<div class="action-detail">'
|
|
1939
|
+
+ '<div class="action-title">' + esc(act.title || act.action || act.description || '') + '</div>'
|
|
1940
|
+
+ '<div class="action-states">';
|
|
1941
|
+
if (act.before) {
|
|
1942
|
+
html += '<span>' + esc(act.before) + '</span><span class="arrow">\\u2192</span><span>' + esc(act.after || '...') + '</span>';
|
|
1943
|
+
}
|
|
1944
|
+
html += '</div></div>'
|
|
1945
|
+
+ '<div class="event-time">' + timeAgo(act.timestamp || act.createdAt) + '</div>'
|
|
1946
|
+
+ '</div>';
|
|
1947
|
+
});
|
|
1948
|
+
container.innerHTML = html;
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1951
|
+
// ── Change Action Handler ───────────────────────────────
|
|
1952
|
+
window.postChangeAction = async function(id, action) {
|
|
1953
|
+
try {
|
|
1954
|
+
await fetch(API_BASE + '/api/causal/changes', {
|
|
1955
|
+
method: 'POST',
|
|
1956
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1957
|
+
body: JSON.stringify({ id: id, action: action })
|
|
1958
|
+
});
|
|
1959
|
+
refreshAll();
|
|
1960
|
+
} catch (e) {
|
|
1961
|
+
// silent
|
|
1962
|
+
}
|
|
1963
|
+
};
|
|
1964
|
+
|
|
1965
|
+
// ── Clock ───────────────────────────────────────────────
|
|
1966
|
+
function updateClock() {
|
|
1967
|
+
var now = new Date();
|
|
1968
|
+
var h = String(now.getHours()).padStart(2, '0');
|
|
1969
|
+
var m = String(now.getMinutes()).padStart(2, '0');
|
|
1970
|
+
var s = String(now.getSeconds()).padStart(2, '0');
|
|
1971
|
+
document.getElementById('clock').textContent = h + ':' + m + ':' + s;
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
// ── Hash-Diffed Refresh ─────────────────────────────────
|
|
1975
|
+
async function refreshSection(key, endpoint, renderer) {
|
|
1976
|
+
var data = await fetchData(endpoint);
|
|
1977
|
+
var dataStr = JSON.stringify(data);
|
|
1978
|
+
var hash = simpleHash(dataStr);
|
|
1979
|
+
if (_hashes[key] !== hash) {
|
|
1980
|
+
_hashes[key] = hash;
|
|
1981
|
+
renderer(data);
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1985
|
+
async function refreshAll() {
|
|
1986
|
+
var dot = document.getElementById('refreshDot');
|
|
1987
|
+
dot.classList.remove('paused');
|
|
1988
|
+
|
|
1989
|
+
await Promise.allSettled([
|
|
1990
|
+
// Business Intelligence
|
|
1991
|
+
refreshSection('bizCompany', ENDPOINTS.bizCompany, renderBizCompany),
|
|
1992
|
+
refreshSection('bizInitiatives', ENDPOINTS.bizInitiatives, renderBizInitiatives),
|
|
1993
|
+
refreshSection('bizInterventions', ENDPOINTS.bizInterventions, renderBizInterventions),
|
|
1994
|
+
refreshSection('bizCompetitors', ENDPOINTS.bizCompetitors, renderBizCompetitors),
|
|
1995
|
+
refreshSection('bizContradictions', ENDPOINTS.bizContradictions, renderBizContradictions),
|
|
1996
|
+
refreshSection('bizAgents', ENDPOINTS.bizAgents, renderBizAgents),
|
|
1997
|
+
refreshSection('bizDecisions', ENDPOINTS.bizDecisions, renderBizDecisions),
|
|
1998
|
+
// System Intelligence
|
|
1999
|
+
refreshSection('delta', ENDPOINTS.delta, renderDelta),
|
|
2000
|
+
refreshSection('trajectory', ENDPOINTS.trajectory, renderTrajectory),
|
|
2001
|
+
refreshSection('events', ENDPOINTS.events, renderEvents),
|
|
2002
|
+
refreshSection('changes', ENDPOINTS.changes, renderChanges),
|
|
2003
|
+
refreshSection('path', ENDPOINTS.path, renderPath),
|
|
2004
|
+
refreshSection('rollups', ENDPOINTS.rollups, renderRollups),
|
|
2005
|
+
refreshSection('packets', ENDPOINTS.packets, renderPackets),
|
|
2006
|
+
refreshSection('actions', ENDPOINTS.actions, renderActions),
|
|
2007
|
+
]);
|
|
2008
|
+
}
|
|
2009
|
+
|
|
2010
|
+
// ── Event Listeners ─────────────────────────────────────
|
|
2011
|
+
document.getElementById('event-filter').addEventListener('change', function(e) {
|
|
2012
|
+
_eventFilter = e.target.value;
|
|
2013
|
+
_hashes['events'] = null; // force re-render
|
|
2014
|
+
refreshSection('events', ENDPOINTS.events, renderEvents);
|
|
2015
|
+
});
|
|
2016
|
+
|
|
2017
|
+
document.getElementById('rollup-tabs').addEventListener('click', function(e) {
|
|
2018
|
+
if (e.target.classList.contains('tab')) {
|
|
2019
|
+
document.querySelectorAll('#rollup-tabs .tab').forEach(function(t) { t.classList.remove('active'); });
|
|
2020
|
+
e.target.classList.add('active');
|
|
2021
|
+
_rollupPeriod = e.target.dataset.period;
|
|
2022
|
+
if (_rollupData) renderRollups(_rollupData);
|
|
2023
|
+
}
|
|
2024
|
+
});
|
|
2025
|
+
|
|
2026
|
+
// ── Init ────────────────────────────────────────────────
|
|
2027
|
+
updateClock();
|
|
2028
|
+
setInterval(updateClock, 1000);
|
|
2029
|
+
refreshAll();
|
|
2030
|
+
setInterval(refreshAll, REFRESH_INTERVAL);
|
|
2031
|
+
})();
|
|
2032
|
+
</script>
|
|
2033
|
+
</body>
|
|
2034
|
+
</html>`;
|
|
2035
|
+
}
|
|
2036
|
+
//# sourceMappingURL=operatingDashboardHtml.js.map
|