@plumpslabs/kuma 2.3.32 → 2.3.34

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.
@@ -4,14 +4,15 @@
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>Kuma Studio</title>
7
- <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>♢</text></svg>">
7
+ <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><circle cx='50' cy='50' r='48' fill='%23D97706'/><circle cx='50' cy='50' r='44' fill='%23F59E0B'/><circle cx='50' cy='45' r='30' fill='%2392400E'/><circle cx='35' cy='38' r='5' fill='%231F2937'/><circle cx='65' cy='38' r='5' fill='%231F2937'/><ellipse cx='50' cy='50' rx='10' ry='6' fill='%23B45309'/><path d='M35 58 Q50 72 65 58' stroke='%231F2937' stroke-width='3.5' fill='none' stroke-linecap='round'/></svg>">
8
8
  <script src="https://unpkg.com/vis-network@9.1.9/dist/vis-network.min.js"></script>
9
9
  <script src="https://unpkg.com/lucide@0.344.0/dist/umd/lucide.min.js"></script>
10
10
  <style>
11
11
  :root {
12
- --bg: #1a1a2e; --surface: #16213e; --border: #2a2a4a;
13
- --text: #e0e0e0; --muted: #888; --accent: #7c3aed; --accent2: #06b6d4;
14
- --green: #22c55e; --yellow: #eab308; --orange: #f97316; --red: #ef4444;
12
+ --bg: #0f172a; --surface: #1e293b; --border: #334155;
13
+ --text: #f1f5f9; --muted: #94a3b8; --accent: #a78bfa; --accent2: #22d3ee;
14
+ --green: #34d399; --yellow: #fbbf24; --orange: #fb923c; --red: #f87171;
15
+ --accent-bright: #c4b5fd; --accent2-bright: #67e8f9;
15
16
  }
16
17
  * { box-sizing: border-box; margin: 0; padding: 0; }
17
18
  html, body {
@@ -20,46 +21,44 @@ html, body {
20
21
  touch-action: none; -webkit-user-select: none; user-select: none;
21
22
  }
22
23
  header {
23
- display: flex; align-items: center; gap: 16px; padding: 12px 24px;
24
+ display: flex; align-items: center; gap: 12px; padding: 8px 16px;
24
25
  background: var(--surface); border-bottom: 1px solid var(--border);
25
26
  -webkit-app-region: drag;
26
27
  }
27
- header {
28
- display: flex; align-items: center; gap: 16px; padding: 12px 24px;
29
- background: var(--surface); border-bottom: 1px solid var(--border);
30
- -webkit-app-region: drag;
31
- }
32
- header h1 { font-size: 18px; font-weight: 700; color: var(--accent); letter-spacing: -0.5px; }
28
+ header h1 { font-size: 15px; font-weight: 700; color: var(--accent-bright); letter-spacing: -0.5px; text-shadow: 0 0 12px rgba(167,139,250,0.3); }
33
29
  header .status { font-size: 12px; color: var(--muted); }
34
30
  header .status .dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 6px; }
35
31
  header .status .dot.on { background: var(--green); }
36
32
  header .status .dot.off { background: var(--red); }
37
33
  .stats-bar {
38
- display: flex; gap: 8px; padding: 8px 24px; background: var(--surface);
39
- border-bottom: 1px solid var(--border); font-size: 12px;
40
- overflow-x: auto;
34
+ display: flex; gap: 6px; padding: 6px 16px; background: var(--surface);
35
+ border-bottom: 1px solid var(--border); font-size: 11px;
36
+ overflow-x: auto; flex-wrap: nowrap;
41
37
  }
42
- .stat { display: flex; align-items: center; gap: 4px; padding: 4px 10px; border-radius: 6px; background: var(--bg); white-space: nowrap; }
43
- .stat .num { font-weight: 700; color: var(--accent2); }
44
- .stat .lbl { color: var(--muted); }
45
- #main { display: flex; height: calc(100vh - 100px); }
38
+ .stat { display: flex; align-items: center; gap: 3px; padding: 3px 8px; border-radius: 5px; background: var(--bg); white-space: nowrap; }
39
+ .stat .num { font-weight: 700; color: var(--accent2); font-size: 12px; }
40
+ .stat .lbl { color: var(--muted); font-size: 10px; }
41
+ #main { display: flex; height: calc(100vh - 80px); }
46
42
  #graph-panel { flex: 1; position: relative; min-width: 0; overflow: hidden; }
47
43
  #graph-container { width: 100%; height: 100%; background: radial-gradient(ellipse at center, #1e1e3a 0%, var(--bg) 70%); }
48
- #sidebar { width: 360px; min-width: 360px; background: var(--surface); border-left: 1px solid var(--border); display: flex; flex-direction: column; }
49
- .tabs { display: flex; border-bottom: 1px solid var(--border); }
44
+ #sidebar { width: 320px; min-width: 320px; background: var(--surface); border-left: 1px solid var(--border); display: flex; flex-direction: column; }
45
+ .tabs { display: flex; gap: 3px; padding: 8px 12px 4px; background: var(--surface); border-bottom: 1px solid var(--border); overflow-x: auto; flex-wrap: nowrap; }
50
46
  .tab {
51
- flex: 1; padding: 10px; text-align: center; cursor: pointer; font-size: 12px;
52
- color: var(--muted); border-bottom: 2px solid transparent; transition: all 0.2s;
47
+ padding: 5px 10px; text-align: center; cursor: pointer; font-size: 11px;
48
+ color: var(--muted); border-radius: 6px; transition: all 0.15s; white-space: nowrap;
49
+ display: flex; align-items: center; gap: 4px; border: 1px solid transparent;
53
50
  }
54
- .tab:hover { color: var(--text); }
55
- .tab.active { color: var(--accent); border-bottom-color: var(--accent); }
56
- .tab-content { flex: 1; overflow-y: auto; padding: 12px; }
51
+ .tab:hover { color: var(--text); background: rgba(167,139,250,0.08); }
52
+ .tab.active { color: var(--accent-bright); background: rgba(167,139,250,0.12); border-color: rgba(167,139,250,0.25); }
53
+ .tab-content { flex: 1; overflow-y: auto; padding: 10px 12px; }
57
54
  .panel { display: none; }
58
- .panel.active { display: block; }
55
+ .panel.active { display: block; animation: fadeIn 0.15s; }
56
+ .tab-label { font-size: 11px; }
57
+ @media (max-width: 400px) { .tab-label { display: none; } .tab { padding: 5px 8px; } }
59
58
 
60
59
  /* Gotcha items */
61
60
  .gotcha-item {
62
- padding: 10px 12px; margin-bottom: 8px; border-radius: 8px;
61
+ padding: 8px 10px; margin-bottom: 6px; border-radius: 6px;
63
62
  background: var(--bg); border-left: 3px solid var(--muted); cursor: pointer;
64
63
  transition: background 0.15s;
65
64
  }
@@ -74,19 +73,19 @@ header .status .dot.off { background: var(--red); }
74
73
 
75
74
  /* Feature items */
76
75
  .feature-item {
77
- padding: 10px 12px; margin-bottom: 8px; border-radius: 8px;
76
+ padding: 8px 10px; margin-bottom: 6px; border-radius: 6px;
78
77
  background: var(--bg); border-left: 3px solid var(--accent); cursor: pointer;
79
78
  transition: background 0.15s;
80
79
  }
81
80
  .feature-item:hover { background: #1e293b; }
82
- .feature-item .name { font-size: 14px; font-weight: 600; color: var(--accent); margin-bottom: 4px; }
81
+ .feature-item .name { font-size: 13px; font-weight: 600; color: var(--accent); margin-bottom: 2px; }
83
82
  .feature-item .meta { font-size: 11px; color: var(--muted); }
84
83
  .feature-item .tags { display: flex; gap: 4px; flex-wrap: wrap; margin-top: 4px; }
85
- .feature-item .tag { font-size: 10px; padding: 1px 6px; border-radius: 4px; background: rgba(124,58,237,0.15); color: var(--accent); }
84
+ .feature-item .tag { font-size: 10px; padding: 1px 6px; border-radius: 4px; background: rgba(167,139,250,0.15); color: var(--accent-bright); }
86
85
 
87
86
  /* Health items */
88
- .health-item { padding: 8px 12px; margin-bottom: 6px; background: var(--bg); border-radius: 6px; }
89
- .health-item .score { font-size: 20px; font-weight: 700; }
87
+ .health-item { padding: 6px 10px; margin-bottom: 5px; background: var(--bg); border-radius: 5px; }
88
+ .health-item .score { font-size: 18px; font-weight: 700; }
90
89
  .health-item .score.good { color: var(--green); }
91
90
  .health-item .score.ok { color: var(--yellow); }
92
91
  .health-item .score.bad { color: var(--red); }
@@ -96,7 +95,7 @@ header .status .dot.off { background: var(--red); }
96
95
 
97
96
  /* Legend */
98
97
  #legend {
99
- position: fixed; bottom: 16px; left: 16px; background: rgba(22,33,62,0.95);
98
+ position: fixed; bottom: 16px; left: 16px; background: rgba(15,23,42,0.95);
100
99
  border: 1px solid var(--border); border-radius: 8px; padding: 10px 14px;
101
100
  font-size: 11px; z-index: 10; max-height: 200px; overflow-y: auto;
102
101
  backdrop-filter: blur(8px); color: #f8fafc;
@@ -130,45 +129,45 @@ header .status .dot.off { background: var(--red); }
130
129
  border-radius: 6px; cursor: pointer; font-size: 12px; color: var(--text);
131
130
  transition: background 0.1s;
132
131
  }
133
- #ctx-menu .ctx-item:hover { background: rgba(124,58,237,0.15); color: var(--accent); }
132
+ #ctx-menu .ctx-item:hover { background: rgba(167,139,250,0.15); color: var(--accent-bright); }
134
133
  #ctx-menu .ctx-item i { width: 14px; height: 14px; flex-shrink: 0; }
135
134
  #ctx-menu .ctx-sep { height: 1px; background: var(--border); margin: 4px 8px; }
136
135
  #search-box input {
137
- background: rgba(22,33,62,0.92); border: 1px solid var(--border);
136
+ background: rgba(15,23,42,0.92); border: 1px solid var(--border);
138
137
  border-radius: 8px; padding: 8px 12px; color: var(--text); font-size: 12px;
139
138
  width: 200px; outline: none; backdrop-filter: blur(8px);
140
139
  transition: border-color 0.2s;
141
140
  }
142
- #search-box input:focus { border-color: var(--accent); }
141
+ #search-box input:focus { border-color: var(--accent-bright); box-shadow: 0 0 8px rgba(167,139,250,0.3); }
143
142
  #search-box input::placeholder { color: var(--muted); }
144
143
 
145
144
  /* Graph controls */
146
145
  #graph-controls {
147
- position: fixed; bottom: 16px; right: 376px; z-index: 10;
146
+ position: fixed; bottom: 16px; right: 336px; z-index: 10;
148
147
  display: flex; gap: 6px;
149
148
  }
150
149
  #graph-controls button {
151
- background: rgba(22,33,62,0.92); border: 1px solid var(--border);
150
+ background: rgba(15,23,42,0.92); border: 1px solid var(--border);
152
151
  border-radius: 6px; color: var(--text); padding: 6px 10px;
153
152
  font-size: 11px; cursor: pointer; backdrop-filter: blur(8px);
154
153
  transition: background 0.2s;
155
154
  }
156
- #graph-controls button:hover { background: var(--accent); border-color: var(--accent); }
155
+ #graph-controls button:hover { background: var(--accent); border-color: var(--accent-bright); }
157
156
  #graph-controls button.active {
158
- background: var(--accent) !important; border-color: var(--accent) !important;
159
- color: #ffffff !important; box-shadow: 0 0 10px rgba(124, 58, 237, 0.4);
157
+ background: var(--accent) !important; border-color: var(--accent-bright) !important;
158
+ color: #ffffff !important; box-shadow: 0 0 12px rgba(167,139,250,0.5);
160
159
  }
161
160
  #graph-controls .ctrl-sep { width: 1px; background: var(--border); margin: 0 2px; }
162
161
  .depth-btn { min-width: 28px !important; padding: 4px 6px !important; font-size: 11px !important; font-weight: 600; }
163
- .depth-btn.active { background: rgba(124,58,237,0.3) !important; border-color: var(--accent) !important; color: var(--accent) !important; }
162
+ .depth-btn.active { background: rgba(167,139,250,0.25) !important; border-color: var(--accent-bright) !important; color: var(--accent-bright) !important; }
164
163
 
165
164
  /* Loading */
166
165
  #loading {
167
166
  position: fixed; inset: 0; background: var(--bg); display: flex;
168
167
  align-items: center; justify-content: center; z-index: 100;
169
- font-size: 18px; color: var(--accent); flex-direction: column; gap: 16px;
168
+ font-size: 18px; color: var(--accent-bright); flex-direction: column; gap: 16px;
170
169
  }
171
- #loading .spinner { width: 32px; height: 32px; border: 3px solid var(--border); border-top-color: var(--accent); border-radius: 50%; animation: spin 0.8s linear infinite; }
170
+ #loading .spinner { width: 32px; height: 32px; border: 3px solid var(--border); border-top-color: var(--accent-bright); border-radius: 50%; animation: spin 0.8s linear infinite; }
172
171
  @keyframes spin { to { transform: rotate(360deg); } }
173
172
 
174
173
  /* Node Detail Modal */
@@ -179,31 +178,31 @@ header .status .dot.off { background: var(--red); }
179
178
  }
180
179
  @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
181
180
  .modal {
182
- background: var(--surface); border: 1px solid var(--border); border-radius: 12px;
183
- width: 560px; max-width: 90vw; max-height: 85vh; overflow: hidden; box-shadow: 0 16px 64px rgba(0,0,0,0.5);
181
+ background: var(--surface); border: 1px solid var(--border); border-radius: 10px;
182
+ width: 520px; max-width: 90vw; max-height: 85vh; overflow: hidden; box-shadow: 0 16px 64px rgba(0,0,0,0.5);
184
183
  display: flex; flex-direction: column; animation: slideUp 0.2s;
185
184
  }
186
185
  @keyframes slideUp { from { transform: translateY(20px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
187
186
  .modal-header {
188
187
  display: flex; align-items: center; justify-content: space-between;
189
- padding: 14px 20px; border-bottom: 1px solid var(--border); flex-shrink: 0;
188
+ padding: 10px 16px; border-bottom: 1px solid var(--border); flex-shrink: 0;
190
189
  }
191
- .modal-header h2 { font-size: 14px; font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
190
+ .modal-header h2 { font-size: 13px; font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
192
191
  .modal-header .close {
193
192
  background: none; border: none; color: var(--muted); font-size: 18px;
194
193
  cursor: pointer; width: 28px; height: 28px; display: flex; align-items: center; justify-content: center;
195
194
  border-radius: 6px; transition: all 0.15s; flex-shrink: 0;
196
195
  }
197
196
  .modal-header .close:hover { background: rgba(255,255,255,0.1); color: var(--text); }
198
- .modal-body { padding: 16px 20px; overflow-y: auto; flex: 1; min-height: 0; }
199
- .modal-section { margin-bottom: 14px; }
200
- .modal-section h3 { font-size: 11px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 6px; }
201
- .modal-kv { display: flex; gap: 8px; margin-bottom: 5px; font-size: 12px; line-height: 1.4; }
202
- .modal-kv .key { color: var(--muted); min-width: 72px; flex-shrink: 0; }
197
+ .modal-body { padding: 12px 16px; overflow-y: auto; flex: 1; min-height: 0; }
198
+ .modal-section { margin-bottom: 10px; }
199
+ .modal-section h3 { font-size: 10px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 4px; }
200
+ .modal-kv { display: flex; gap: 6px; margin-bottom: 3px; font-size: 11px; line-height: 1.3; }
201
+ .modal-kv .key { color: var(--muted); min-width: 64px; flex-shrink: 0; }
203
202
  .modal-kv .val { color: var(--text); word-break: break-word; overflow-wrap: anywhere; min-width: 0; }
204
203
  .relation-item {
205
- display: flex; align-items: center; gap: 6px; padding: 5px 8px;
206
- margin-bottom: 3px; border-radius: 6px; background: var(--bg);
204
+ display: flex; align-items: center; gap: 5px; padding: 4px 7px;
205
+ margin-bottom: 2px; border-radius: 5px; background: var(--bg);
207
206
  font-size: 11px; cursor: pointer; transition: background 0.15s;
208
207
  }
209
208
  .relation-item:hover { background: #1e293b; }
@@ -212,20 +211,20 @@ header .status .dot.off { background: var(--red); }
212
211
  .relation-item .type { color: var(--muted); font-size: 10px; flex-shrink: 0; max-width: 80px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
213
212
  .relation-item .weight { color: var(--accent2); font-size: 10px; flex-shrink: 0; }
214
213
 
215
- ::-webkit-scrollbar { width: 6px; }
214
+ ::-webkit-scrollbar { width: 4px; height: 4px; }
216
215
  ::-webkit-scrollbar-track { background: transparent; }
217
- ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
216
+ ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
218
217
  </style>
219
218
  </head>
220
219
  <body>
221
220
  <div id="loading"><div class="spinner"></div>Connecting to Kuma...</div>
222
221
  <header>
223
- <h1><i data-lucide="brain" style="width:20px;height:20px;vertical-align:middle;margin-right:6px"></i> Kuma Studio</h1>
222
+ <h1><svg width="22" height="22" viewBox="0 0 100 100" style="vertical-align:middle;margin-right:6px"><circle cx="50" cy="50" r="48" fill="#D97706"/><circle cx="50" cy="50" r="44" fill="#F59E0B"/><circle cx="50" cy="45" r="30" fill="#92400E"/><circle cx="35" cy="38" r="5" fill="#1F2937"/><circle cx="65" cy="38" r="5" fill="#1F2937"/><ellipse cx="50" cy="50" rx="10" ry="6" fill="#B45309"/><path d="M35 58 Q50 72 65 58" stroke="#1F2937" stroke-width="3.5" fill="none" stroke-linecap="round"/></svg> Kuma Studio</h1>
224
223
  <div class="status"><span class="dot" id="status-dot"></span><span id="status-text">connecting...</span></div>
225
224
  <div style="margin-left:auto;display:flex;gap:8px;-webkit-app-region:no-drag">
226
- <button onclick="refetch()" id="refetch-btn" style="background:rgba(124,58,237,0.15);border:1px solid rgba(124,58,237,0.3);border-radius:6px;color:var(--accent);padding:5px 12px;font-size:11px;cursor:pointer;transition:all 0.2s"><i data-lucide="refresh-cw" style="width:13px;height:13px;vertical-align:middle;margin-right:3px"></i> Refetch</button>
227
- <button onclick="resetKumaDb()" style="background:rgba(239,68,68,0.15);border:1px solid rgba(239,68,68,0.3);border-radius:6px;color:var(--red);padding:5px 12px;font-size:11px;cursor:pointer;transition:all 0.2s"><i data-lucide="trash-2" style="width:13px;height:13px;vertical-align:middle;margin-right:3px"></i> Reset DB</button>
228
- <button onclick="stopKumaStudio()" style="background:rgba(148,163,184,0.15);border:1px solid rgba(148,163,184,0.3);border-radius:6px;color:#94a3b8;padding:5px 12px;font-size:11px;cursor:pointer;transition:all 0.2s"><i data-lucide="square" style="width:13px;height:13px;vertical-align:middle;margin-right:3px"></i> Stop</button>
225
+ <button onclick="refetch()" id="refetch-btn" style="background:rgba(167,139,250,0.12);border:1px solid rgba(167,139,250,0.25);border-radius:5px;color:var(--accent-bright);padding:4px 10px;font-size:10px;cursor:pointer;transition:all 0.2s;display:flex;align-items:center;gap:3px"><i data-lucide="refresh-cw" style="width:11px;height:11px"></i> Refetch</button>
226
+ <button onclick="resetKumaDb()" style="background:rgba(239,68,68,0.12);border:1px solid rgba(239,68,68,0.25);border-radius:5px;color:var(--red);padding:4px 10px;font-size:10px;cursor:pointer;transition:all 0.2s;display:flex;align-items:center;gap:3px"><i data-lucide="trash-2" style="width:11px;height:11px"></i> Reset</button>
227
+ <button onclick="stopKumaStudio()" style="background:rgba(148,163,184,0.12);border:1px solid rgba(148,163,184,0.25);border-radius:5px;color:#94a3b8;padding:4px 10px;font-size:10px;cursor:pointer;transition:all 0.2s;display:flex;align-items:center;gap:3px"><i data-lucide="square" style="width:11px;height:11px"></i> Stop</button>
229
228
  </div>
230
229
  </header>
231
230
  <div class="stats-bar" id="stats-bar"></div>
@@ -249,11 +248,12 @@ header .status .dot.off { background: var(--red); }
249
248
  </div>
250
249
  <div id="sidebar">
251
250
  <div class="tabs">
252
- <div class="tab active" data-tab="features"><i data-lucide="star" style="width:13px;height:13px;vertical-align:middle;margin-right:3px"></i> Features</div>
253
- <div class="tab" data-tab="gotchas"><i data-lucide="alert-triangle" style="width:13px;height:13px;vertical-align:middle;margin-right:3px"></i> Gotchas</div>
254
- <div class="tab" data-tab="health"><i data-lucide="heart-pulse" style="width:13px;height:13px;vertical-align:middle;margin-right:3px"></i> Health</div>
255
- <div class="tab" data-tab="efficiency"><i data-lucide="zap" style="width:13px;height:13px;vertical-align:middle;margin-right:3px"></i> Efficiency</div>
256
- <div class="tab" data-tab="staleness"><i data-lucide="activity" style="width:13px;height:13px;vertical-align:middle;margin-right:3px"></i> Staleness</div>
251
+ <div class="tab active" data-tab="features" title="Features"><i data-lucide="star" style="width:12px;height:12px"></i><span class="tab-label">Features</span></div>
252
+ <div class="tab" data-tab="gotchas" title="Gotchas"><i data-lucide="alert-triangle" style="width:12px;height:12px"></i><span class="tab-label">Gotchas</span></div>
253
+ <div class="tab" data-tab="injections" title="Shadow Memory"><i data-lucide="zap" style="width:12px;height:12px"></i><span class="tab-label">Injections</span></div>
254
+ <div class="tab" data-tab="efficiency" title="Efficiency"><i data-lucide="zap" style="width:12px;height:12px"></i><span class="tab-label">Efficiency</span></div>
255
+ <div class="tab" data-tab="staleness" title="Staleness"><i data-lucide="activity" style="width:12px;height:12px"></i><span class="tab-label">Staleness</span></div>
256
+ <div class="tab" data-tab="activity" title="Activity"><i data-lucide="bar-chart-3" style="width:12px;height:12px"></i><span class="tab-label">Activity</span></div>
257
257
  </div>
258
258
  <div class="tab-content" id="tab-content"></div>
259
259
  </div>
@@ -337,14 +337,17 @@ async function init() {
337
337
  renderGraph(data);
338
338
  renderFeatures(data);
339
339
  renderGotchas(data);
340
- renderHealth(data);
340
+ renderInjections(data);
341
341
  renderEfficiency(data);
342
342
  renderStaleness(data);
343
+ renderActivity(data);
343
344
  setupSearch();
344
345
  lucide.createIcons();
345
346
  }
346
347
  init();
347
348
 
349
+
350
+
348
351
  function showError(msg) {
349
352
  document.getElementById("graph-container").innerHTML = '<div class="empty"><i data-lucide="alert-circle" style="width:18px;height:18px;vertical-align:middle;margin-right:4px"></i> '+msg+'</div>';
350
353
  lucide.createIcons();
@@ -352,15 +355,15 @@ function showError(msg) {
352
355
 
353
356
  function renderStats(data) {
354
357
  var s = data.stats || {};
355
- var hc = s.health_score;
356
- var hcColor = hc >= 80 ? "var(--green)" : hc >= 50 ? "var(--yellow)" : "var(--red)";
358
+ var inj = data.injections || {};
359
+ var savedMin = inj.savedFormatted || '0 min';
357
360
  document.getElementById("stats-bar").innerHTML =
358
361
  '<div class="stat"><span class="num">'+(s.node_count||0)+'</span><span class="lbl">nodes</span></div>'+
359
362
  '<div class="stat"><span class="num">'+(s.edge_count||0)+'</span><span class="lbl">edges</span></div>'+
360
- '<div class="stat"><span class="num" style="color:var(--accent)">'+(s.feature_count||0)+'</span><span class="lbl">features</span></div>'+
361
363
  '<div class="stat"><span class="num">'+(s.gotcha_count||0)+'</span><span class="lbl">gotchas</span></div>'+
362
364
  '<div class="stat"><span class="num" style="color:var(--accent)">'+currentDbSize+'</span><span class="lbl">kuma.db</span></div>'+
363
- '<div class="stat"><span class="num" style="color:'+hcColor+'">'+(hc||0)+'</span><span class="lbl">health</span></div>';
365
+ '<div class="stat"><span class="num" style="color:var(--blue)">'+inj.count+'</span><span class="lbl">injections</span></div>'+
366
+ '<div class="stat"><span class="num" style="font-size:13px;color:var(--green)">'+savedMin+'</span><span class="lbl">time saved</span></div>';
364
367
  }
365
368
 
366
369
  function truncateLabel(name, maxLen) {
@@ -439,7 +442,7 @@ function renderGraph(data) {
439
442
  var color = isGotcha ? '#f97316' : (colors[n.type] || '#64748b');
440
443
  return {
441
444
  id: n.id, label: label,
442
- color: { background: color, border: color, highlight: { background: color, border: '#38bdf8' }, hover: { background: color, border: '#a855f7' } },
445
+ color: { background: color, border: color, highlight: { background: color, border: '#67e8f9' }, hover: { background: color, border: '#c4b5fd' } },
443
446
  shape: shape,
444
447
  font: { color: '#ffffff', size: Math.min(13, Math.max(10, size * 0.55)), face: 'sans-serif', multi: false, bold: true },
445
448
  borderWidth: 0, size: size, mass: 1 + (size / 40),
@@ -469,7 +472,7 @@ function renderGraph(data) {
469
472
  id: 'e'+idx, from: e.source, to: e.target,
470
473
  label: e.relation || '',
471
474
  arrows: { to: { enabled: true, scaleFactor: 0.7, type: 'arrow' } },
472
- color: { color: edgeColor, highlight: '#a855f7', hover: '#38bdf8', opacity: 0.85 },
475
+ color: { color: edgeColor, highlight: '#c4b5fd', hover: '#67e8f9', opacity: 0.85 },
473
476
  font: { size: 10, color: '#94a3b8', strokeWidth: 0, background: 'transparent' },
474
477
  dashes: dashes,
475
478
  smooth: { type: 'continuous', roundness: 0.3 },
@@ -486,7 +489,7 @@ function renderGraph(data) {
486
489
  },
487
490
  interaction: { hover: true, tooltipDelay: 300, zoomView: true, dragView: true, dragNodes: true, keyboard: true, multiselect: true, selectable: true, selectConnectedEdges: false },
488
491
  nodes: { borderWidth: 0, font: { color: '#ffffff', size: 11, face: 'sans-serif' }, margin: { top: 6, bottom: 6, left: 10, right: 10 }, shapeProperties: { borderRadius: 6 } },
489
- edges: { smooth: { type: 'continuous', roundness: 0.2 }, width: 1, color: { color: '#4a4a6a', highlight: '#7c3aed' } },
492
+ edges: { smooth: { type: 'continuous', roundness: 0.2 }, width: 1, color: { color: '#4a4a6a', highlight: '#a78bfa' } },
490
493
  configure: { enabled: false },
491
494
  });
492
495
 
@@ -610,7 +613,7 @@ function resetHighlight() {
610
613
  network.body.data.nodes.update(nUp);
611
614
  network.body.data.edges.forEach(function(e) {
612
615
  var def = edgeDefaults[e.id];
613
- eUp.push({ id: e.id, color: { color: def ? def.color : '#64748b', highlight: '#a855f7', hover: '#38bdf8', opacity: 0.85 }, width: def ? def.width : 1 });
616
+ eUp.push({ id: e.id, color: { color: def ? def.color : '#64748b', highlight: '#c4b5fd', hover: '#67e8f9', opacity: 0.85 }, width: def ? def.width : 1 });
614
617
  });
615
618
  network.body.data.edges.update(eUp);
616
619
  document.getElementById("graph-tooltip").style.display = 'none';
@@ -788,7 +791,7 @@ async function refetch() {
788
791
  if (status.ok) { dot.className = 'dot on'; txt.textContent = status.project || 'connected'; } else { dot.className = 'dot off'; txt.textContent = 'no kuma.db found'; }
789
792
  var data = await fetch(API+'/dashboard').then(function(r){return r.json()});
790
793
  if (data.error) { showError(data.error); return; }
791
- renderStats(data); renderGraph(data); renderFeatures(data); renderGotchas(data); renderHealth(data); renderEfficiency(data); renderStaleness(data);
794
+ renderStats(data); renderGraph(data); renderGotchas(data); renderEfficiency(data); renderStaleness(data); renderInjections(data);
792
795
  lucide.createIcons();
793
796
  } catch(e) { showError('Refetch failed: ' + e.message); }
794
797
  btn.innerHTML = '<i data-lucide="refresh-cw" style="width:13px;height:13px;vertical-align:middle;margin-right:3px"></i> Refetch';
@@ -806,9 +809,10 @@ function setupTabs(){
806
809
  content.innerHTML =
807
810
  '<div class="panel active" id="panel-features"><div class="empty">Loading...</div></div>' +
808
811
  '<div class="panel" id="panel-gotchas"><div class="empty">Loading...</div></div>' +
809
- '<div class="panel" id="panel-health"><div class="empty">Loading...</div></div>' +
812
+ '<div class="panel" id="panel-injections"><div class="empty">Loading...</div></div>' +
810
813
  '<div class="panel" id="panel-efficiency"><div class="empty">Loading...</div></div>' +
811
- '<div class="panel" id="panel-staleness"><div class="empty">Loading...</div></div>';
814
+ '<div class="panel" id="panel-staleness"><div class="empty">Loading...</div></div>' +
815
+ '<div class="panel" id="panel-activity"><div class="empty">Loading...</div></div>';
812
816
  document.querySelectorAll('.tab').forEach(function(tab){
813
817
  tab.onclick = function(){
814
818
  document.querySelectorAll('.tab').forEach(function(t){t.classList.remove('active')});
@@ -860,17 +864,22 @@ function renderGotchas(data){
860
864
  lucide.createIcons();
861
865
  }
862
866
 
863
- function renderHealth(data){
864
- var items = data.health || [];
865
- var el = document.getElementById("panel-health");
867
+ function renderInjections(data) {
868
+ var inj = data.injections || {};
869
+ var el = document.getElementById("panel-injections");
866
870
  if(!el) return;
867
- if(!items.length){el.innerHTML='<div class="empty">No health data yet</div>';return;}
868
- el.innerHTML = items.map(function(h){
869
- var cls = h.score>=80?'good':h.score>=50?'ok':'bad';
870
- var summary = h.summary||h.risk_level||'-';
871
- var date = h.created_at ? new Date(h.created_at*1000).toLocaleDateString() : '-';
872
- return '<div class="health-item"><div class="score '+cls+'">'+h.score+'</div><div class="meta">'+escapeHtml(summary)+' &middot; '+date+'</div></div>';
873
- }).join('');
871
+ if(!inj.count){el.innerHTML='<div class="empty">No shadow memory injections yet. Enable hooks via `kuma init --claude`.</div>';return;}
872
+ el.innerHTML = '<div class="section-title">Shadow Memory (24h)</div>'+
873
+ '<div class="efficiency-grid">'+
874
+ '<div class="eff-item"><span class="eff-num">'+inj.count+'</span><span class="eff-lbl">Injections</span></div>'+
875
+ '<div class="eff-item"><span class="eff-num" style="color:var(--green)">'+inj.savedFormatted+'</span><span class="eff-lbl">Time Saved</span></div>'+
876
+ '<div class="eff-item"><span class="eff-num" style="color:var(--blue)">~'+Math.round(inj.savedMs/5000)+'x</span><span class="eff-lbl">Multiplier</span></div>'+
877
+ '</div>'+
878
+ '<div class="section-title" style="margin-top:16px">Auto-Inject Hooks</div>'+
879
+ '<div class="gotcha-item">Pre-Edit: <code>kuma hook pre-edit</code> — injects gotchas + decisions + history</div>'+
880
+ '<div class="gotcha-item">Pre-Bash: <code>kuma hook pre-bash</code> — injects command-triggered gotchas</div>'+
881
+ '<div class="gotcha-item">Dedupe: files not re-injected within 15 min (I5)</div>'+
882
+ '<div class="gotcha-item">Loop Capture: 4+ edits within 30 min auto-records a gotcha (I3)</div>';
874
883
  }
875
884
 
876
885
  // ============ EFFICIENCY PANEL (GAP 1) ============
@@ -892,7 +901,7 @@ function renderEfficiency(data){
892
901
  var r = eff.recordings || {};
893
902
  var savedSec = m.researchTimeSaved ? Math.round(m.researchTimeSaved/1000) : 0;
894
903
  var passRate = eff.verificationPassRate;
895
- var passColor = passRate == null ? 'var(--muted)' : passRate >= 80 ? 'var(--green)' : passRate >= 50 ? 'var(--yellow)' : 'var(--red)';
904
+ var passColor = passRate >= 80 ? 'var(--green)' : passRate >= 50 ? 'var(--yellow)' : 'var(--red)';
896
905
 
897
906
  var stat = function(num, lbl, color){
898
907
  return '<div style="flex:1;min-width:110px;padding:10px 12px;background:var(--bg);border-radius:8px;border:1px solid var(--border)"><div style="font-size:20px;font-weight:700;color:'+(color||'var(--accent2)')+'">'+num+'</div><div style="font-size:11px;color:var(--muted);margin-top:2px">'+lbl+'</div></div>';
@@ -902,7 +911,7 @@ function renderEfficiency(data){
902
911
  html += '<div style="display:flex;flex-wrap:wrap;gap:8px;margin-bottom:12px">' +
903
912
  stat(eff.sessions||0, 'sessions') +
904
913
  stat(eff.toolCalls||0, 'tool calls') +
905
- stat(eff.totalEdits||0, 'file edits') +
914
+ stat(eff.gotchas||0, 'gotchas', 'var(--orange)') +
906
915
  stat(eff.researchCacheScopes||0, 'research cached', 'var(--green)') +
907
916
  stat(eff.decisions||0, 'decisions', 'var(--accent)') +
908
917
  stat(eff.verifications||0, 'verifications', passRate==null?'var(--muted)':'var(--green)') +
@@ -1057,6 +1066,277 @@ function setupSearch() {
1057
1066
  }, 150);
1058
1067
  };
1059
1068
  }
1069
+
1070
+ // ============ ACTIVITY PANEL — Agent Usage Intensity ============
1071
+ // Shows how intensely agents use Kuma: tool calls per day, sessions frequency,
1072
+ // and activity heatmap to understand adoption patterns.
1073
+
1074
+ function renderActivity(data) {
1075
+ var el = document.getElementById("panel-activity");
1076
+ if (!el) return;
1077
+ var eff = data.efficiency || {};
1078
+ var sess = eff.recentSessions || [];
1079
+ var totalSessions = eff.sessions || 0;
1080
+ var totalToolCalls = eff.toolCalls || 0;
1081
+ var totalGotchas = eff.gotchas || 0;
1082
+ var totalArchFlows = eff.archFlows || 0;
1083
+ var totalDecisions = eff.decisions || 0;
1084
+ var totalEdits = eff.metrics ? (eff.metrics.filesEdited || 0) : 0;
1085
+
1086
+ var html = '';
1087
+
1088
+ // === COPY REPORT BUTTON ===
1089
+ html += '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px">';
1090
+ html += '<div style="font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:0.5px">🤖 Agent Usage Intensity</div>';
1091
+ html += '<button onclick="copyActivityReport()" id="copy-report-btn" style="background:rgba(34,211,238,0.12);border:1px solid rgba(34,211,238,0.25);border-radius:6px;color:var(--accent2);padding:4px 10px;font-size:10px;cursor:pointer;transition:all 0.2s;display:flex;align-items:center;gap:4px"><i data-lucide="copy" style="width:11px;height:11px"></i> Copy Report</button>';
1092
+ html += '</div>';
1093
+
1094
+ // Intensity score: tool_calls per session
1095
+ var avgCallsPerSession = totalSessions > 0 ? (totalToolCalls / totalSessions).toFixed(1) : '0';
1096
+ var intensityLevel = totalToolCalls > 100 ? 'High' : totalToolCalls > 30 ? 'Medium' : totalToolCalls > 0 ? 'Low' : 'None';
1097
+ var intensityColor = totalToolCalls > 100 ? 'var(--green)' : totalToolCalls > 30 ? 'var(--yellow)' : totalToolCalls > 0 ? 'var(--orange)' : 'var(--muted)';
1098
+ var intensityIcon = totalToolCalls > 100 ? '🔥' : totalToolCalls > 30 ? '⚡' : totalToolCalls > 0 ? '💤' : '⚫';
1099
+
1100
+ html += '<div class="health-item" style="margin-bottom:12px;border-left:3px solid ' + intensityColor + '">';
1101
+ html += '<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px">';
1102
+ html += '<span style="font-size:24px">' + intensityIcon + '</span>';
1103
+ html += '<div><div style="font-size:16px;font-weight:700;color:' + intensityColor + '">' + intensityLevel + '</div>';
1104
+ html += '<div style="font-size:11px;color:var(--muted)">' + avgCallsPerSession + ' calls/session average</div></div>';
1105
+ html += '</div>';
1106
+ html += '<div style="display:flex;flex-wrap:wrap;gap:6px;margin-top:6px">';
1107
+ html += '<span style="font-size:10px;padding:2px 8px;border-radius:4px;background:rgba(34,211,238,0.15);color:var(--accent2)">' + totalToolCalls + ' total calls</span>';
1108
+ html += '<span style="font-size:10px;padding:2px 8px;border-radius:4px;background:rgba(52,211,153,0.15);color:var(--green)">' + totalSessions + ' sessions</span>';
1109
+ html += '<span style="font-size:10px;padding:2px 8px;border-radius:4px;background:rgba(251,191,36,0.15);color:var(--yellow)">' + totalEdits + ' edits</span>';
1110
+ html += '</div>';
1111
+ html += '</div>';
1112
+
1113
+ // === HEALTH SCORE TREND ===
1114
+ html += '<div style="font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:0.5px;margin:10px 0 6px">📈 Activity Summary</div>';
1115
+ html += '<div style="display:flex;flex-wrap:wrap;gap:8px;margin-bottom:12px">';
1116
+
1117
+ var summaryCard = function(val, label, color) {
1118
+ return '<div style="flex:1;min-width:90px;padding:10px;background:var(--bg);border-radius:8px;border:1px solid var(--border);text-align:center">' +
1119
+ '<div style="font-size:20px;font-weight:700;color:' + (color || 'var(--accent2)') + '">' + val + '</div>' +
1120
+ '<div style="font-size:10px;color:var(--muted);margin-top:2px">' + label + '</div></div>';
1121
+ };
1122
+
1123
+ html += summaryCard(totalSessions, 'Sessions', 'var(--accent2)');
1124
+ html += summaryCard(totalToolCalls, 'Tool Calls', 'var(--accent)');
1125
+ html += summaryCard(totalGotchas, 'Gotchas', 'var(--orange)');
1126
+ html += summaryCard(totalArchFlows, 'Arch Flows', 'var(--accent)');
1127
+ html += summaryCard(totalDecisions, 'Decisions', 'var(--accent2)');
1128
+ html += '</div>';
1129
+
1130
+ // === RECENT SESSION ACTIVITY (last 8) ===
1131
+ if (sess.length) {
1132
+ html += '<div style="font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:0.5px;margin:10px 0 6px">🕐 Recent Activity</div>';
1133
+ html += '<div style="display:flex;flex-direction:column;gap:4px;margin-bottom:12px">';
1134
+
1135
+ var maxCalls = Math.max.apply(null, sess.map(function(s) { return s.toolCalls || 0; }).concat([1]));
1136
+
1137
+ sess.forEach(function(s) {
1138
+ var calls = s.toolCalls || 0;
1139
+ var edits = s.edits || 0;
1140
+ var rollbacks = s.rollbacks || 0;
1141
+ var failures = s.failures || 0;
1142
+ var barWidth = Math.round((calls / maxCalls) * 100);
1143
+ var barColor = failures > 0 ? 'var(--red)' : rollbacks > 0 ? 'var(--yellow)' : 'var(--accent)';
1144
+ var goal = s.goal || 'No goal';
1145
+ var date = s.startedAt ? new Date(s.startedAt * 1000) : null;
1146
+ var dateStr = date ? date.toLocaleDateString() + ' ' + date.toLocaleTimeString([], {hour: '2-digit', minute: '2-digit'}) : '-';
1147
+
1148
+ html += '<div style="background:var(--bg);border-radius:6px;padding:8px 10px;border:1px solid var(--border)">';
1149
+ html += '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:4px">';
1150
+ html += '<span style="font-size:11px;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:180px" title="' + escapeHtml(goal) + '">' + escapeHtml(goal) + '</span>';
1151
+ html += '<span style="font-size:10px;color:var(--muted);flex-shrink:0;margin-left:8px">' + dateStr + '</span>';
1152
+ html += '</div>';
1153
+ // Activity bar
1154
+ html += '<div style="height:6px;background:var(--border);border-radius:3px;overflow:hidden;margin-bottom:4px">';
1155
+ html += '<div style="height:100%;width:' + barWidth + '%;background:' + barColor + ';border-radius:3px;transition:width 0.3s"></div>';
1156
+ html += '</div>';
1157
+ // Metrics row
1158
+ html += '<div style="display:flex;gap:8px;font-size:10px;color:var(--muted)">';
1159
+ html += '<span>🔧 ' + calls + ' calls</span>';
1160
+ if (edits > 0) html += '<span>📝 ' + edits + ' edits</span>';
1161
+ if (rollbacks > 0) html += '<span style="color:var(--yellow)">↩ ' + rollbacks + '</span>';
1162
+ if (failures > 0) html += '<span style="color:var(--red)">❌ ' + failures + '</span>';
1163
+ if (s.safetyScore != null) html += '<span style="color:var(--green)">🛡 ' + s.safetyScore + '</span>';
1164
+ html += '</div>';
1165
+ html += '</div>';
1166
+ });
1167
+ html += '</div>';
1168
+ }
1169
+
1170
+ // === EFFICIENCY TRENDS ===
1171
+ var successRate = eff.verificationPassRate || 100;
1172
+ var passColor = successRate >= 90 ? 'var(--green)' : successRate >= 70 ? 'var(--yellow)' : 'var(--red)';
1173
+ var recordingRate = totalToolCalls > 0 ? Math.round(((totalGotchas + totalArchFlows + totalDecisions) / totalToolCalls) * 100) : 0;
1174
+ // Rollback rate from recent sessions (rollbacks / calls, %)
1175
+ var totalRollbacks = sess.reduce(function(a, s) { return a + (s.rollbacks || 0); }, 0);
1176
+ var totalCalls = sess.reduce(function(a, s) { return a + (s.toolCalls || 0); }, 0);
1177
+ var rollbackRate = totalCalls > 0 ? Math.round((totalRollbacks / totalCalls) * 100) : 0;
1178
+
1179
+ html += '<div style="font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:0.5px;margin:10px 0 6px">📊 Efficiency Trends</div>';
1180
+ html += '<div class="health-item" style="margin-bottom:8px">';
1181
+ html += '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px">';
1182
+ html += '<span style="font-size:12px;color:var(--text)">Verification Pass Rate</span>';
1183
+ html += '<span style="font-size:14px;font-weight:700;color:' + passColor + '">' + (successRate || '—') + '%</span>';
1184
+ html += '</div>';
1185
+ html += '<div style="height:8px;background:var(--border);border-radius:4px;overflow:hidden">';
1186
+ html += '<div style="height:100%;width:' + (successRate || 0) + '%;background:' + passColor + ';border-radius:4px"></div>';
1187
+ html += '</div>';
1188
+ html += '</div>';
1189
+
1190
+ html += '<div class="health-item" style="margin-bottom:8px">';
1191
+ html += '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px">';
1192
+ html += '<span style="font-size:12px;color:var(--text)">Recording Rate</span>';
1193
+ html += '<span style="font-size:14px;font-weight:700;color:' + (recordingRate >= 10 ? 'var(--green)' : recordingRate >= 5 ? 'var(--yellow)' : 'var(--orange)') + '">' + recordingRate + '%</span>';
1194
+ html += '</div>';
1195
+ html += '<div style="height:8px;background:var(--border);border-radius:4px;overflow:hidden">';
1196
+ html += '<div style="height:100%;width:' + Math.min(recordingRate, 100) + '%;background:' + (recordingRate >= 10 ? 'var(--green)' : recordingRate >= 5 ? 'var(--yellow)' : 'var(--orange)') + ';border-radius:4px"></div>';
1197
+ html += '</div>';
1198
+ html += '</div>';
1199
+
1200
+ // === INSIGHT ===
1201
+ html += '<div style="margin-top:12px;padding:10px 12px;background:rgba(167,139,250,0.1);border:1px solid rgba(167,139,250,0.2);border-radius:8px;font-size:11px;color:var(--accent-bright)">';
1202
+ html += '💡 <b>Activity Insight:</b> ';
1203
+ if (totalSessions === 0) {
1204
+ html += 'No sessions recorded yet. Start using Kuma tools to track activity!';
1205
+ } else if (totalToolCalls < 10) {
1206
+ html += 'Getting started! Kuma learns more with each tool call.';
1207
+ } else if (successRate >= 90) {
1208
+ html += 'Excellent! High success rate shows Kuma is well-calibrated for your workflow.';
1209
+ } else if (rollbackRate > 10) {
1210
+ html += 'High rollback rate — Kuma gotchas may help avoid repeated mistakes.';
1211
+ } else {
1212
+ html += 'Healthy usage pattern. The more you use Kuma, the smarter it gets!';
1213
+ }
1214
+ html += '</div>';
1215
+
1216
+ el.innerHTML = html;
1217
+ lucide.createIcons();
1218
+ }
1219
+
1220
+ // ============ COPY ACTIVITY REPORT ============
1221
+ function copyActivityReport() {
1222
+ var btn = document.getElementById('copy-report-btn');
1223
+ if (!btn) return;
1224
+
1225
+ // Find the data from the last render
1226
+ var panel = document.getElementById('panel-activity');
1227
+ if (!panel) return;
1228
+
1229
+ // Build a clean text report
1230
+ var report = [];
1231
+ report.push('=== KUMA STUDIO — ACTIVITY REPORT ===');
1232
+ report.push('Generated: ' + new Date().toLocaleString());
1233
+ report.push('');
1234
+
1235
+ // Get all stat cards
1236
+ var stats = panel.querySelectorAll('[style*="font-size:20px"]');
1237
+ var labels = panel.querySelectorAll('[style*="font-size:10px;color:var(--muted)"]');
1238
+
1239
+ // Extract intensity info
1240
+ var intensityEl = panel.querySelector('[style*="font-size:16px;font-weight:700"]');
1241
+ var intensity = intensityEl ? intensityEl.textContent : 'Unknown';
1242
+ var avgCallsEl = panel.querySelector('[style*="font-size:11px;color:var(--muted)"]');
1243
+ var avgCalls = avgCallsEl ? avgCallsEl.textContent : '';
1244
+
1245
+ report.push('INTENSITY: ' + intensity);
1246
+ if (avgCalls) report.push('Average: ' + avgCalls);
1247
+ report.push('');
1248
+
1249
+ // Get all section headers and content
1250
+ var sections = panel.querySelectorAll('[style*="text-transform:uppercase"]');
1251
+ sections.forEach(function(section) {
1252
+ var title = section.textContent.trim();
1253
+ report.push('--- ' + title + ' ---');
1254
+
1255
+ // Get next sibling content
1256
+ var next = section.nextElementSibling;
1257
+ if (next) {
1258
+ // Extract text content, cleaning up HTML
1259
+ var text = next.textContent.trim()
1260
+ .replace(/\s+/g, ' ')
1261
+ .replace(/([0-9]+) sessions/g, '$1 sessions')
1262
+ .replace(/([0-9]+) tool calls/g, '$1 tool calls')
1263
+ .replace(/([0-9]+) file edits/g, '$1 file edits')
1264
+ .replace(/([0-9]+) rollbacks/g, '$1 rollbacks')
1265
+ .replace(/([0-9]+) failures/g, '$1 failures')
1266
+ .replace(/([0-9]+) total calls/g, '$1 total calls');
1267
+ report.push(text);
1268
+ }
1269
+ report.push('');
1270
+ });
1271
+
1272
+ // Get recent sessions
1273
+ var sessionItems = panel.querySelectorAll('[style*="background:var(--bg);border-radius:6px"]');
1274
+ if (sessionItems.length > 0) {
1275
+ report.push('--- RECENT SESSIONS ---');
1276
+ sessionItems.forEach(function(item) {
1277
+ var goal = item.querySelector('[style*="font-size:11px"]');
1278
+ var metrics = item.querySelector('[style*="font-size:10px"]');
1279
+ if (goal) report.push('• ' + goal.textContent.trim());
1280
+ if (metrics) report.push(' ' + metrics.textContent.trim());
1281
+ });
1282
+ report.push('');
1283
+ }
1284
+
1285
+ // Get efficiency trends
1286
+ var trends = panel.querySelectorAll('.health-item');
1287
+ if (trends.length > 0) {
1288
+ report.push('--- EFFICIENCY TRENDS ---');
1289
+ trends.forEach(function(trend) {
1290
+ var label = trend.querySelector('[style*="font-size:12px"]');
1291
+ var value = trend.querySelector('[style*="font-size:14px"]');
1292
+ if (label && value) {
1293
+ report.push(label.textContent.trim() + ': ' + value.textContent.trim());
1294
+ }
1295
+ });
1296
+ report.push('');
1297
+ }
1298
+
1299
+ // Get insight
1300
+ var insight = panel.querySelector('[style*="rgba(167,139,250,0.1)"]');
1301
+ if (insight) {
1302
+ report.push('--- INSIGHT ---');
1303
+ report.push(insight.textContent.trim());
1304
+ }
1305
+
1306
+ var reportText = report.join('\n');
1307
+
1308
+ // Copy to clipboard
1309
+ navigator.clipboard.writeText(reportText).then(function() {
1310
+ btn.innerHTML = '<i data-lucide="check" style="width:11px;height:11px"></i> Copied!';
1311
+ btn.style.background = 'rgba(52,211,153,0.15)';
1312
+ btn.style.borderColor = 'rgba(52,211,153,0.3)';
1313
+ btn.style.color = 'var(--green)';
1314
+ lucide.createIcons();
1315
+ setTimeout(function() {
1316
+ btn.innerHTML = '<i data-lucide="copy" style="width:11px;height:11px"></i> Copy Report';
1317
+ btn.style.background = 'rgba(34,211,238,0.12)';
1318
+ btn.style.borderColor = 'rgba(34,211,238,0.25)';
1319
+ btn.style.color = 'var(--accent2)';
1320
+ lucide.createIcons();
1321
+ }, 2000);
1322
+ }).catch(function() {
1323
+ // Fallback: select text for manual copy
1324
+ var textarea = document.createElement('textarea');
1325
+ textarea.value = reportText;
1326
+ textarea.style.position = 'fixed';
1327
+ textarea.style.left = '-9999px';
1328
+ document.body.appendChild(textarea);
1329
+ textarea.select();
1330
+ document.execCommand('copy');
1331
+ document.body.removeChild(textarea);
1332
+ btn.innerHTML = '<i data-lucide="check" style="width:11px;height:11px"></i> Copied!';
1333
+ lucide.createIcons();
1334
+ setTimeout(function() {
1335
+ btn.innerHTML = '<i data-lucide="copy" style="width:11px;height:11px"></i> Copy Report';
1336
+ lucide.createIcons();
1337
+ }, 2000);
1338
+ });
1339
+ }
1060
1340
  </script>
1061
1341
  </body>
1062
1342
  </html>