@buzzie-ai/jannal 0.3.0 → 0.4.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/README.md +13 -11
- package/package.json +2 -1
- package/profiles.json +72 -0
- package/public/assets/index-Bah0aqc_.css +1 -0
- package/public/assets/index-eeA0j5Sg.js +23 -0
- package/public/index.html +5 -2
- package/server.js +42 -0
- package/public/assets/index-B8dfyj9-.css +0 -1
- package/public/assets/index-CzXZ1AkJ.js +0 -23
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Jannal
|
|
2
2
|
|
|
3
|
+
**/jun-nul/** — Tamil for "window".
|
|
4
|
+
|
|
3
5
|
**See what's eating your context window. Then fix it.**
|
|
4
6
|
|
|
5
7
|
Jannal sits between your AI tools and the Anthropic API. It intercepts every request, visualizes how your context window is being used, and lets you filter out tools you don't need — saving tokens and money.
|
|
@@ -8,16 +10,6 @@ Works with Claude Code and any tool that speaks the Anthropic Messages API. [Cur
|
|
|
8
10
|
|
|
9
11
|

|
|
10
12
|
|
|
11
|
-

|
|
12
|
-
|
|
13
|
-
## What it does
|
|
14
|
-
|
|
15
|
-
**Inspect** — Watch every API request in real time. See exactly how many tokens go to the system prompt, tool definitions, conversation history, and tool results. The context bar shows you at a glance where your tokens are going.
|
|
16
|
-
|
|
17
|
-
**Cost tracking** — See the cost of every turn, with per-model pricing (Opus, Sonnet, Haiku). Session cost accumulates in the header so you always know what you're spending. Uses the official `count_tokens` API for accurate counts before the response even finishes.
|
|
18
|
-
|
|
19
|
-
**Filter tools** — The killer feature. If you're running Claude Code with 40+ MCP tools defined, half of them are probably irrelevant to what you're doing right now. Jannal strips them from the request before it hits the API. Create named profiles ("Coding Only", "Browser Automation") and switch between them from the UI.
|
|
20
|
-
|
|
21
13
|
## Quick start
|
|
22
14
|
|
|
23
15
|
```bash
|
|
@@ -46,6 +38,16 @@ ANTHROPIC_BASE_URL=http://localhost:4455 your-tool
|
|
|
46
38
|
|
|
47
39
|
Open `http://localhost:4455` in your browser to see the Inspector.
|
|
48
40
|
|
|
41
|
+
[](https://youtu.be/AI1q4yKAxV8)
|
|
42
|
+
|
|
43
|
+
## What it does
|
|
44
|
+
|
|
45
|
+
**Inspect** — Watch every API request in real time. See exactly how many tokens go to the system prompt, tool definitions, conversation history, and tool results. The context bar shows you at a glance where your tokens are going.
|
|
46
|
+
|
|
47
|
+
**Cost tracking** — See the cost of every turn, with per-model pricing (Opus, Sonnet, Haiku). Session cost accumulates in the header so you always know what you're spending. Uses the official `count_tokens` API for accurate counts before the response even finishes.
|
|
48
|
+
|
|
49
|
+
**Filter tools** — The killer feature. If you're running Claude Code with 40+ MCP tools defined, half of them are probably irrelevant to what you're doing right now. Jannal strips them from the request before it hits the API. Create named profiles ("Coding Only", "Browser Automation") and switch between them from the UI.
|
|
50
|
+
|
|
49
51
|
## How it works
|
|
50
52
|
|
|
51
53
|
```
|
|
@@ -151,7 +153,7 @@ Open `http://localhost:5173` for the dev UI (auto-proxies API calls to the serve
|
|
|
151
153
|
```
|
|
152
154
|
jannal/
|
|
153
155
|
├── server.js # Proxy server, createServer() factory, plugin hooks
|
|
154
|
-
├── bin/jannal.js # CLI entry point (npx jannal)
|
|
156
|
+
├── bin/jannal.js # CLI entry point (npx @buzzie-ai/jannal)
|
|
155
157
|
├── lib/
|
|
156
158
|
│ ├── plugins.js # Plugin host (lifecycle hooks, route handling)
|
|
157
159
|
│ └── tokens.js # Token estimation, budget inference
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@buzzie-ai/jannal",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Intercept, visualize, and optimize LLM context windows. Proxy that sits between your AI tools and the Anthropic API.",
|
|
5
5
|
"main": "server.js",
|
|
6
6
|
"exports": {
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"profiles.json"
|
|
18
18
|
],
|
|
19
19
|
"scripts": {
|
|
20
|
+
"dev": "vite",
|
|
20
21
|
"dev:server": "node server.js",
|
|
21
22
|
"dev:ui": "vite",
|
|
22
23
|
"build": "vite build",
|
package/profiles.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"profiles": {
|
|
3
|
+
"All Tools": {
|
|
4
|
+
"name": "All Tools",
|
|
5
|
+
"mode": "allowlist",
|
|
6
|
+
"tools": []
|
|
7
|
+
},
|
|
8
|
+
"Simple": {
|
|
9
|
+
"name": "Simple",
|
|
10
|
+
"mode": "allowlist",
|
|
11
|
+
"tools": [
|
|
12
|
+
"Bash",
|
|
13
|
+
"Agent",
|
|
14
|
+
"AskUserQuestion",
|
|
15
|
+
"EnterPlanMode",
|
|
16
|
+
"TaskUpdate",
|
|
17
|
+
"Grep",
|
|
18
|
+
"CronCreate",
|
|
19
|
+
"TaskCreate",
|
|
20
|
+
"ExitPlanMode",
|
|
21
|
+
"Read",
|
|
22
|
+
"ExitWorktree",
|
|
23
|
+
"WebSearch",
|
|
24
|
+
"WebFetch",
|
|
25
|
+
"Edit",
|
|
26
|
+
"Skill",
|
|
27
|
+
"EnterWorktree",
|
|
28
|
+
"NotebookEdit",
|
|
29
|
+
"TaskList",
|
|
30
|
+
"Glob",
|
|
31
|
+
"Write",
|
|
32
|
+
"TaskGet",
|
|
33
|
+
"TaskOutput",
|
|
34
|
+
"TaskStop",
|
|
35
|
+
"CronDelete",
|
|
36
|
+
"CronList"
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
"No tools": {
|
|
40
|
+
"name": "No tools",
|
|
41
|
+
"mode": "allowlist",
|
|
42
|
+
"tools": [
|
|
43
|
+
"Bash",
|
|
44
|
+
"Agent",
|
|
45
|
+
"AskUserQuestion",
|
|
46
|
+
"EnterPlanMode",
|
|
47
|
+
"TaskUpdate",
|
|
48
|
+
"Grep",
|
|
49
|
+
"CronCreate",
|
|
50
|
+
"TaskCreate",
|
|
51
|
+
"ExitPlanMode",
|
|
52
|
+
"Read",
|
|
53
|
+
"ExitWorktree",
|
|
54
|
+
"WebSearch",
|
|
55
|
+
"WebFetch",
|
|
56
|
+
"Edit",
|
|
57
|
+
"Skill",
|
|
58
|
+
"EnterWorktree",
|
|
59
|
+
"NotebookEdit",
|
|
60
|
+
"TaskList",
|
|
61
|
+
"Glob",
|
|
62
|
+
"Write",
|
|
63
|
+
"TaskGet",
|
|
64
|
+
"TaskOutput",
|
|
65
|
+
"TaskStop",
|
|
66
|
+
"CronDelete",
|
|
67
|
+
"CronList"
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"activeProfile": "No tools"
|
|
72
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
*,:before,:after{box-sizing:border-box;margin:0;padding:0}:root{--bg:#09090b;--bg2:#111113;--bg3:#18181b;--bg4:#27272a;--border:#ffffff0f;--text:#fafafa;--text2:#a1a1aa;--text3:#52525b;--blue:#60a5fa;--purple:#a78bfa;--green:#34d399;--orange:#fb923c;--cyan:#22d3ee;--yellow:#fbbf24;--red:#f87171;--amber:#f59e0b;--seg-system:#60a5fa;--seg-tools:#fb923c;--seg-message:#22d3ee;--seg-assistant:#34d399;--seg-tool-result:#fbbf24;--seg-tool-use:#a78bfa;--overlay-1:#ffffff03;--overlay-2:#ffffff05;--overlay-3:#ffffff08;--overlay-4:#ffffff0a;--overlay-5:#ffffff0d;--overlay-6:#ffffff0f;--overlay-8:#ffffff14;--overlay-10:#ffffff1a;--overlay-12:#ffffff1f;--overlay-15:#ffffff26;--scrollbar-thumb:#ffffff0f;--scrollbar-thumb-hover:#ffffff1f;--modal-backdrop:#000000b3;--noise-opacity:.015;--text-hover:white;--bar-seg-text:white;--bar-seg-shadow:0 1px 2px #00000080;--line-num-color:#ffffff1f;--shadow-inset:inset 0 1px 3px #0000004d;--font-ui:"Instrument Sans", -apple-system, system-ui, sans-serif;--font-mono:"JetBrains Mono", "SF Mono", "Fira Code", monospace;--radius-sm:6px;--radius-md:10px;--radius-lg:14px;--radius-xl:20px;--shadow-sm:0 1px 2px #0000004d;--shadow-md:0 4px 12px #0006;--shadow-lg:0 12px 40px #00000080;--shadow-xl:0 24px 80px #0009;--ease-out:cubic-bezier(.16, 1, .3, 1);--ease-spring:cubic-bezier(.34, 1.56, .64, 1);--duration-fast:.15s;--duration-normal:.25s;--duration-slow:.4s}html[data-theme=light]{--bg:#fafafa;--bg2:#f4f4f5;--bg3:#e4e4e7;--bg4:#d4d4d8;--border:#00000014;--text:#18181b;--text2:#52525b;--text3:#a1a1aa;--blue:#2563eb;--purple:#7c3aed;--green:#059669;--orange:#ea580c;--cyan:#0891b2;--yellow:#d97706;--red:#dc2626;--amber:#b45309;--seg-system:#2563eb;--seg-tools:#ea580c;--seg-message:#0891b2;--seg-assistant:#059669;--seg-tool-result:#d97706;--seg-tool-use:#7c3aed;--overlay-1:#00000003;--overlay-2:#00000005;--overlay-3:#00000008;--overlay-4:#0000000a;--overlay-5:#0000000d;--overlay-6:#0000000f;--overlay-8:#0000000f;--overlay-10:#00000014;--overlay-12:#0000001a;--overlay-15:#0000001f;--scrollbar-thumb:#0000001a;--scrollbar-thumb-hover:#0003;--modal-backdrop:#0000004d;--noise-opacity:0;--text-hover:var(--text);--bar-seg-text:white;--bar-seg-shadow:0 1px 2px #0000004d;--line-num-color:#0003;--shadow-inset:inset 0 1px 3px #00000014;--shadow-sm:0 1px 2px #0000000f;--shadow-md:0 4px 12px #00000014;--shadow-lg:0 12px 40px #0000001a;--shadow-xl:0 24px 80px #0000001f}html.theme-transitioning,html.theme-transitioning *,html.theme-transitioning :before,html.theme-transitioning :after{transition:background-color .3s,color .3s,border-color .3s,box-shadow .3s!important}body{background:var(--bg);color:var(--text);font-family:var(--font-ui);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;flex-direction:column;height:100vh;display:flex;overflow:hidden}::-webkit-scrollbar{width:6px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background:var(--scrollbar-thumb);transition:background var(--duration-fast);border-radius:99px}::-webkit-scrollbar-thumb:hover{background:var(--scrollbar-thumb-hover)}.noise-overlay{z-index:9999;pointer-events:none;opacity:var(--noise-opacity);background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='300'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='1'/%3E%3C/svg%3E");position:fixed;inset:0}.header{z-index:100;background:var(--bg2);justify-content:space-between;align-items:center;padding:10px 20px;display:flex;position:relative}.header:after{content:"";background:var(--border);height:1px;position:absolute;bottom:0;left:0;right:0}.header-left{align-items:center;gap:10px;display:flex}.header-brand{flex-direction:column;gap:1px;display:flex}.logo{object-fit:cover;border-radius:7px;width:28px;height:28px}.header h1{letter-spacing:-.02em;font-size:15px;font-weight:800;line-height:1}.header-brand .status{font-size:10px}.header-right{align-items:center;gap:8px;display:flex}.hdr-sep{background:var(--border);flex-shrink:0;width:1px;height:18px}.hdr-metrics{align-items:center;gap:6px;display:flex}.theme-toggle{border-radius:var(--radius-sm);border:1px solid var(--overlay-8);background:var(--overlay-3);width:26px;height:26px;color:var(--text3);cursor:pointer;transition:all var(--duration-fast);flex-shrink:0;justify-content:center;align-items:center;display:flex}.theme-toggle:hover{background:var(--overlay-8);color:var(--text);border-color:var(--overlay-12)}.theme-toggle:active{transform:scale(.95)}.status{align-items:center;gap:5px;font-size:11px;font-weight:600;display:flex}.status-dot{border-radius:50%;flex-shrink:0;width:6px;height:6px}.status-dot.connected{background:var(--green);animation:2s ease-in-out infinite statusPulse;box-shadow:0 0 6px #34d39980}.status-dot.disconnected{background:var(--red)}@keyframes statusPulse{0%,to{box-shadow:0 0 6px #34d39980}50%{box-shadow:0 0 10px #34d399b3}}.req-badge{border-radius:var(--radius-sm);background:var(--overlay-3);height:26px;color:var(--text3);font-size:11px;font-weight:600;font-family:var(--font-mono);font-variant-numeric:tabular-nums;align-items:center;padding:4px 8px;display:flex}.daily-saved{border-radius:var(--radius-sm);height:26px;color:var(--text3);font-size:11px;font-weight:700;font-family:var(--font-mono);font-variant-numeric:tabular-nums;background:#22c55e0f;border:1px solid #22c55e14;align-items:center;padding:4px 8px;display:flex}.daily-saved.has-savings{color:var(--green);background:#22c55e1a;border-color:#22c55e26}.daily-cost{border-radius:var(--radius-sm);height:26px;color:var(--amber);font-size:11px;font-weight:700;font-family:var(--font-mono);font-variant-numeric:tabular-nums;background:#f59e0b14;border:1px solid #f59e0b1f;align-items:center;padding:4px 8px;display:flex}.profile-select{border-radius:var(--radius-sm);border:1px solid var(--overlay-8);background:var(--overlay-3);height:26px;color:var(--text);font-size:11px;font-weight:600;font-family:var(--font-ui);cursor:pointer;max-width:140px;transition:all var(--duration-fast);outline:none;padding:0 8px}.profile-select:hover{background:var(--overlay-6);border-color:var(--overlay-12)}.profile-select.filtering{color:var(--orange);background:#fb923c0f;border-color:#fb923c4d}.filter-badge{border-radius:var(--radius-sm);height:26px;color:var(--orange);letter-spacing:.06em;background:#fb923c1f;border:1px solid #fb923c33;align-items:center;padding:0 6px;font-size:9px;font-weight:800;line-height:1;display:flex}.router-badge-wrapper{position:relative}.router-badge{border-radius:var(--radius-sm);letter-spacing:.03em;cursor:pointer;-webkit-user-select:none;user-select:none;height:26px;transition:all var(--duration-fast);align-items:center;padding:0 8px;font-size:9px;font-weight:700;display:flex}.router-badge:hover{filter:brightness(1.2)}.router-badge--off{background:var(--overlay-3);color:var(--text3);border:1px solid var(--overlay-8)}.router-badge--shadow{color:var(--purple);background:#a78bfa1a;border:1px solid #a78bfa33}.router-badge--auto{color:var(--cyan);background:#22d3ee1a;border:1px solid #22d3ee33}.router-popover{background:var(--bg3);border:1px solid var(--border);border-radius:var(--radius-md);z-index:300;min-width:210px;padding:6px 0;display:none;position:absolute;top:calc(100% + 8px);right:0;box-shadow:0 8px 30px #00000059}.router-popover.open{display:block}.router-popover-title{color:var(--text3);text-transform:uppercase;letter-spacing:.1em;border-bottom:1px solid var(--border);margin-bottom:2px;padding:6px 12px 8px;font-size:8px;font-weight:700}.router-popover-opt{width:100%;color:var(--text);font-size:11px;font-weight:500;font-family:var(--font-ui);cursor:pointer;text-align:left;transition:background var(--duration-fast);background:0 0;border:none;align-items:center;gap:8px;padding:7px 12px;display:flex}.router-popover-opt:hover{background:var(--overlay-6)}.router-popover-opt.active{background:var(--overlay-4);font-weight:700}.router-opt-dot{border-radius:50%;flex-shrink:0;width:7px;height:7px}.router-opt-dot--off{background:var(--text3)}.router-opt-dot--shadow{background:var(--purple)}.router-opt-dot--auto{background:var(--cyan)}.router-opt-desc{color:var(--text3);margin-left:auto;font-size:9px;font-weight:400}.global-search-wrapper{position:relative}.global-search{border-radius:var(--radius-sm);border:1px solid var(--overlay-8);background:var(--overlay-3);height:26px;color:var(--text);font-size:10px;font-family:var(--font-ui);width:170px;transition:all var(--duration-fast);outline:none;padding:0 10px}.global-search::placeholder{color:var(--text3)}.global-search:focus{border-color:#60a5fa4d;width:220px;box-shadow:0 0 0 2px #60a5fa14}.global-search-results{background:var(--bg3);border:1px solid var(--border);border-radius:var(--radius-md);z-index:200;max-height:320px;box-shadow:var(--shadow-lg);min-width:360px;margin-top:4px;display:none;position:absolute;top:100%;left:0;right:0;overflow-y:auto}.global-search-results.open{display:block}.search-result-item{border-bottom:1px solid var(--overlay-4);cursor:pointer;transition:background var(--duration-fast);padding:8px 12px}.search-result-item:hover{background:var(--overlay-6)}.search-result-item:last-child{border-bottom:none}.search-result-turn{color:var(--blue);margin-bottom:2px;font-size:10px;font-weight:700}.search-result-snippet{color:var(--text2);font-size:11px;font-family:var(--font-mono);white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.search-result-snippet mark{color:var(--text);background:#fbbf244d;border-radius:2px}.search-no-results{color:var(--text3);text-align:center;padding:12px;font-size:11px}.bar-container{padding:14px 24px 10px}.bar-outer{background:var(--bg3);border:1px solid var(--border);height:52px;box-shadow:var(--shadow-inset);transition:box-shadow var(--duration-slow), border-color var(--duration-slow);border-radius:12px;position:relative;overflow:hidden}.bar-outer:before{content:"";z-index:0;pointer-events:none;background:radial-gradient(ellipse at 50% 100%, var(--overlay-2) 0%, transparent 70%);position:absolute;inset:0}.bar-outer.pressure-high{box-shadow:var(--shadow-inset), 0 0 24px #fb923c26;border-color:#fb923c40}.bar-outer.pressure-critical{box-shadow:var(--shadow-inset), 0 0 30px #f8717133;border-color:#f871714d}.bar-inner{z-index:1;height:100%;transition:all var(--duration-slow) var(--ease-out);display:flex;position:relative}.bar-segment{cursor:pointer;justify-content:center;align-items:center;height:100%;transition:all .35s;display:flex;overflow:hidden}.bar-segment:hover{filter:brightness(1.2)}.bar-segment span{color:var(--bar-seg-text);text-shadow:var(--bar-seg-shadow);white-space:nowrap;padding:0 3px;font-size:10px;font-weight:600}.bar-empty{flex-grow:1;justify-content:center;align-items:center;display:flex}.bar-empty span{color:var(--text3);font-size:11px}.bar-break{pointer-events:none;background:repeating-linear-gradient(-60deg, transparent, transparent 3px, var(--overlay-10) 3px, var(--overlay-10) 5px);width:12px;min-width:12px;height:100%;transition:opacity .35s;position:relative}.bar-break:before,.bar-break:after{content:"";background:var(--overlay-10);width:1px;position:absolute;top:0;bottom:0}.bar-break:before{left:0}.bar-break:after{right:0}.bar-marker{border-left:1px dashed var(--overlay-10);pointer-events:none;z-index:2;position:absolute;top:0;bottom:0}.bar-marker span{color:var(--text3);font-size:7px;position:absolute;top:1px;left:3px}.bar-stats{justify-content:space-between;align-items:center;margin-top:6px;padding:0 2px;display:flex}.bar-legend{flex-wrap:wrap;gap:16px;display:flex}.legend-item{color:var(--text3);cursor:pointer;align-items:center;gap:4px;font-size:11px;display:flex}.legend-dot{border-radius:2px;width:7px;height:7px}.bar-total{font-size:13px;font-weight:700;font-family:var(--font-mono);font-variant-numeric:tabular-nums}.bar-pct{background:var(--overlay-4);font-size:10px;font-weight:600;font-family:var(--font-mono);border-radius:99px;padding:1px 6px}.token-chart-container{border-radius:var(--radius-sm);background:var(--overlay-2);border:1px solid var(--border);margin-top:8px;padding:8px 12px}.token-chart-label{color:var(--text3);text-transform:uppercase;letter-spacing:.05em;margin-bottom:4px;font-size:9px;font-weight:700}.token-chart{align-items:center;gap:8px;display:flex}.token-chart-svg{width:100%;max-width:240px;height:36px}.token-chart-hint{color:var(--text3);white-space:nowrap;font-size:10px;font-family:var(--font-mono)}.main{flex:1;display:flex;overflow:hidden}.panel{flex-direction:column;display:flex;overflow:hidden}.panel-header{border-bottom:1px solid var(--border);color:var(--text2);text-transform:uppercase;letter-spacing:.06em;flex-shrink:0;justify-content:space-between;align-items:center;padding:10px 16px;font-size:12px;font-weight:800;display:flex}.panel-actions{align-items:center;gap:6px;display:flex}.panel-btn{background:var(--overlay-4);border:1px solid var(--overlay-8);color:var(--text2);cursor:pointer;font-size:11px;font-family:var(--font-ui);border-radius:var(--radius-sm);transition:all var(--duration-fast);padding:4px 10px;font-weight:600}.panel-btn:hover:not(:disabled){color:var(--text);background:var(--overlay-6)}.panel-btn:active:not(:disabled){transform:scale(.97)}.panel-btn:disabled{opacity:.5;cursor:not-allowed}.export-dropdown{position:relative}.export-menu{background:var(--bg3);border:1px solid var(--border);border-radius:var(--radius-sm);z-index:100;min-width:90px;box-shadow:var(--shadow-md);margin-top:4px;padding:4px;display:none;position:absolute;top:100%;right:0}.export-menu.open{flex-direction:column;gap:2px;display:flex}.export-option{color:var(--text);cursor:pointer;font-size:11px;font-family:var(--font-ui);text-align:left;transition:background var(--duration-fast);background:0 0;border:none;border-radius:4px;padding:6px 10px}.export-option:hover{background:var(--overlay-8)}.panel-body{flex:1;overflow-y:auto}.reqs-panel{border-right:1px solid var(--border);background:var(--bg2);width:320px}.req-card{border-bottom:1px solid var(--border);cursor:pointer;transition:background var(--duration-fast), transform var(--duration-fast);padding:6px 12px}.req-card:hover{background:var(--overlay-2)}.req-card:active{transform:scale(.99)}.req-card.selected{border-left:3px solid var(--amber);background:#f59e0b0d;padding-left:9px}.req-card-head{justify-content:space-between;align-items:center;display:flex}.req-label{color:var(--text);font-size:12px;font-weight:500}.req-tokens{font-size:11px;font-weight:600;font-family:var(--font-mono);font-variant-numeric:tabular-nums}.req-mini-bar{background:var(--bg);border-radius:99px;height:3px;margin-top:2px;overflow:hidden}.req-mini-fill{height:100%;transition:width .3s var(--ease-out);box-shadow:0 0 4px var(--overlay-5);border-radius:99px}.req-meta{color:var(--text3);font-size:10px;font-family:var(--font-mono);gap:6px;margin-top:2px;display:flex}.req-io{color:var(--green);font-weight:600}.req-cost-inline{color:var(--amber);font-weight:600}.view-toggle{transition:all var(--duration-fast);font-weight:600!important}.view-toggle.active{background:#f59e0b1a;color:var(--amber)!important}.group-card{margin-bottom:2px}.group-header{cursor:pointer;transition:background var(--duration-fast);border-bottom:1px solid var(--border);background:linear-gradient(180deg, var(--overlay-2) 0%, transparent 100%);align-items:center;gap:8px;padding:10px 14px;display:flex}.group-header:hover{background:var(--overlay-3)}.group-chevron{color:var(--text3);transition:transform var(--duration-normal) var(--ease-spring);text-align:center;flex-shrink:0;width:14px;font-size:10px}.group-chevron.expanded{transform:rotate(90deg)}.group-title{color:var(--text);flex:1;min-width:0;font-size:13px;font-weight:600}.group-summary{font-size:11px;font-family:var(--font-mono);font-variant-numeric:tabular-nums;align-items:center;gap:10px;display:flex}.group-req-count{background:var(--overlay-6);color:var(--text2);border-radius:4px;padding:2px 8px;font-weight:600}.group-cost{color:var(--amber);font-weight:600}.group-tokens{color:var(--text3)}.group-children{overflow:hidden}.group-children.collapsed{display:none}.group-session-label{color:var(--text3);text-transform:uppercase;letter-spacing:.05em;align-items:center;gap:6px;margin-left:14px;padding:4px 14px 2px 26px;font-size:9px;font-weight:700;display:flex}.session-pill{letter-spacing:.03em;text-transform:capitalize;border-radius:4px;padding:2px 8px;font-size:9px;font-weight:700;display:inline-block}.session-pill.main{color:var(--purple);background:#a78bfa26}.session-pill.subagent{color:var(--cyan);background:#22d3ee26}.group-children .req-card{border-left:2px solid var(--overlay-6);margin-left:14px;padding-left:26px}.group-children .req-card.selected{border-left:2px solid var(--amber);padding-left:26px}.group-time{color:var(--text3);font-size:9px;font-family:var(--font-mono);padding:2px 14px 8px}.detail-panel{background:var(--bg);flex:1}.segment-row{border-bottom:1px solid var(--overlay-3);transition:background var(--duration-fast);cursor:pointer;align-items:center;gap:10px;padding:12px 18px;display:flex}.segment-row:hover{background:var(--overlay-3)}.seg-color{border-radius:3px;flex-shrink:0;width:5px;height:36px}.seg-info{flex:1;min-width:0}.seg-name{font-size:13px;font-weight:600}.seg-sub{color:var(--text3);white-space:nowrap;text-overflow:ellipsis;max-width:350px;margin-top:1px;font-size:11px;overflow:hidden}.seg-tokens{font-size:13px;font-weight:700;font-family:var(--font-mono);font-variant-numeric:tabular-nums;text-align:right;min-width:60px}.seg-pct{color:var(--text3);text-align:right;min-width:40px;font-size:11px;font-family:var(--font-mono)}.seg-bar{background:var(--bg3);border-radius:99px;flex-shrink:0;width:80px;height:4px;overflow:hidden}.seg-bar-fill{border-radius:99px;height:100%}.seg-expand-hint{color:var(--text3);background:var(--overlay-3);white-space:nowrap;border-radius:4px;padding:2px 6px;font-size:9px}.empty{height:100%;color:var(--text3);flex-direction:column;justify-content:center;align-items:center;display:flex}.empty-icon{opacity:.3;margin-bottom:12px;font-size:40px}.empty h2{color:var(--text2);margin-bottom:4px;font-size:15px;font-weight:700}.empty p{text-align:center;max-width:300px;font-size:13px;line-height:1.5}.copy-command-btn{border:1px solid var(--overlay-12);background:var(--overlay-4);color:var(--cyan);cursor:pointer;font-size:10px;font-family:var(--font-ui);border-radius:4px;margin-left:6px;padding:2px 8px}.copy-command-btn:hover{background:#22d3ee1f;border-color:#22d3ee40}.stats-grid{grid-template-columns:repeat(3,1fr);gap:8px;padding:12px 16px;display:grid}.stats-grid:empty{display:none}.usage-box{border-radius:var(--radius-md);background:var(--bg2);border:1px solid var(--border);padding:10px 12px}.usage-row{justify-content:space-between;align-items:center;padding:2px 0;font-size:11px;display:flex}.usage-label{color:var(--text2)}.usage-value{font-weight:700;font-family:var(--font-mono);font-variant-numeric:tabular-nums}.usage-value.actual{color:var(--green)}.usage-value.estimated{color:var(--text2)}.filter-box{border-radius:var(--radius-md);background:#fb923c0f;border:1px solid #fb923c26;padding:10px 12px}.filter-box-title{color:var(--orange);margin-bottom:6px;font-size:11px;font-weight:700}.warning-box{border-radius:var(--radius-md);background:#fbbf240f;border:1px solid #fbbf2426;padding:10px 12px}.warning-box-title{color:var(--yellow);margin-bottom:6px;font-size:11px;font-weight:700}.router-box{border-radius:var(--radius-md);background:#a78bfa0d;border:1px solid #a78bfa26;padding:10px 12px}.router-box-title{color:var(--purple);margin-bottom:6px;font-size:11px;font-weight:700}.router-mode-shadow{color:var(--purple)}.router-mode-auto{color:var(--cyan)}.router-mode-off{color:var(--text3)}.router-shadow-note{color:var(--text3);border-top:1px solid #a78bfa1f;margin-top:6px;padding-top:6px;font-size:9px;font-style:italic}.premium-locked{opacity:.5;cursor:not-allowed}.premium-locked-msg{color:var(--text3);text-align:center;padding:8px 0;font-size:10px;line-height:1.5}.router-box.premium-locked{border-style:dashed}.router-popover-opt.premium-locked{pointer-events:none}.daily-saved.premium-locked{opacity:.5;font-size:10px}.modal-overlay{z-index:1000;background:var(--modal-backdrop);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);justify-content:center;align-items:center;padding:32px;display:none;position:fixed;inset:0}.modal-overlay.open{display:flex}.modal{background:var(--bg2);border:1px solid var(--overlay-8);border-radius:var(--radius-xl);width:100%;max-width:900px;height:85vh;box-shadow:var(--shadow-xl);animation:modal-in var(--duration-normal) var(--ease-spring);flex-direction:column;display:flex;overflow:hidden}.modal-header{border-bottom:1px solid var(--border);background:var(--overlay-2);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);flex-shrink:0;align-items:center;gap:12px;padding:16px 20px;display:flex}.modal-color-bar{border-radius:2px;width:4px;height:28px}.modal-title{flex:1}.modal-title h2{font-size:16px;font-weight:700}.modal-title .modal-meta{color:var(--text3);margin-top:2px;font-size:12px}.modal-stats{align-items:center;gap:12px;display:flex}.modal-stat{text-align:center}.modal-stat-value{font-size:15px;font-weight:800;font-family:var(--font-mono);font-variant-numeric:tabular-nums}.modal-stat-label{color:var(--text3);text-transform:uppercase;letter-spacing:.05em;font-size:10px}.modal-close{border-radius:var(--radius-sm);cursor:pointer;background:var(--overlay-6);width:32px;height:32px;color:var(--text2);transition:all var(--duration-fast);border:none;justify-content:center;align-items:center;font-size:18px;display:flex}.modal-close:hover{background:var(--overlay-12);color:var(--text-hover)}.modal-close:active{transform:scale(.97)}.modal-toolbar{border-bottom:1px solid var(--border);flex-shrink:0;align-items:center;gap:6px;padding:10px 20px;display:flex}.modal-toolbar button{cursor:pointer;font-size:11px;font-weight:600;font-family:var(--font-ui);color:var(--text2);transition:all var(--duration-fast);background:0 0;border:none;border-radius:99px;padding:5px 14px}.modal-toolbar button:hover{background:var(--overlay-6);color:var(--text-hover)}.modal-toolbar button:active{transform:scale(.97)}.modal-toolbar button.active{color:var(--blue);background:#60a5fa1f;box-shadow:inset 0 0 0 1px #60a5fa33}.modal-toolbar .spacer{flex:1}.modal-toolbar .search-box{border:1px solid var(--overlay-8);background:var(--overlay-3);color:var(--text);width:180px;font-size:11px;font-family:var(--font-ui);transition:all var(--duration-fast);border-radius:99px;outline:none;padding:5px 12px}.modal-toolbar .search-box:focus{border-color:#60a5fa66;box-shadow:0 0 0 3px #60a5fa1a}.modal-body{flex:1;min-height:0;padding:0;overflow:auto}.modal-content{font-family:var(--font-mono);color:var(--text);white-space:pre-wrap;word-break:break-word;tab-size:2;padding:12px 20px;font-size:12px;line-height:1.3}.modal-content .line{border-left:1px solid var(--overlay-4);padding:1px 0 1px 48px;display:block;position:relative}.modal-content .line:nth-child(2n){background:var(--overlay-1)}.modal-content .line:hover{background:var(--overlay-3)}.modal-content .line-num{text-align:right;width:40px;color:var(--line-num-color);-webkit-user-select:none;user-select:none;padding-right:8px;font-size:10px;position:absolute;left:0}.modal-content .highlight{background:#fbbf2433;border-radius:2px}.modal-tools{padding:16px 20px}.modal-tools-header{border-radius:var(--radius-md);background:var(--overlay-2);justify-content:space-between;align-items:center;margin-bottom:12px;padding:8px 12px;display:flex}.modal-tools-header-left{color:var(--text2);font-size:11px}.modal-tools-header-left strong{color:var(--text)}.tool-section-header{color:var(--text3);text-transform:uppercase;letter-spacing:.06em;margin-top:4px;padding:12px 4px 6px;font-size:10px;font-weight:800}.tool-group{border:1px solid var(--overlay-6);border-radius:var(--radius-md);margin-bottom:10px;overflow:hidden}.tool-group-header{background:var(--overlay-3);cursor:pointer;transition:background var(--duration-fast);align-items:center;gap:8px;padding:8px 12px;display:flex}.tool-group-header:hover{background:var(--overlay-5)}.tool-group-checkbox,.tool-card input[type=checkbox]{appearance:none;border:1.5px solid var(--text3);cursor:pointer;width:16px;height:16px;transition:all var(--duration-fast);background:0 0;border-radius:4px;flex-shrink:0;position:relative}.tool-group-checkbox:checked,.tool-card input[type=checkbox]:checked{background:var(--blue);border-color:var(--blue)}.tool-group-checkbox:checked:after,.tool-card input[type=checkbox]:checked:after{content:"";border:2px solid #fff;border-width:0 2px 2px 0;width:5px;height:9px;position:absolute;top:1px;left:4px;transform:rotate(45deg)}.tool-group-chevron{color:var(--text3);transition:transform var(--duration-normal) var(--ease-spring);-webkit-user-select:none;user-select:none;flex-shrink:0;font-size:9px}.tool-group-chevron.expanded{transform:rotate(90deg)}.tool-group-title{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.tool-group-name{color:var(--purple);font-size:13px;font-weight:700}.tool-group-meta{color:var(--text3);font-size:11px}.tool-group-actions{flex-shrink:0;gap:4px;display:flex}.tool-group-body{padding:8px}.tool-group-body.collapsed{display:none}.tool-card{border-radius:var(--radius-sm);background:var(--overlay-2);border:1px solid var(--overlay-4);transition:all var(--duration-fast);align-items:center;gap:10px;margin-bottom:4px;padding:8px 12px;display:flex}.tool-card:hover{background:var(--overlay-5);box-shadow:var(--shadow-sm)}.tool-card-info{flex:1;min-width:0}.tool-card-name{color:var(--orange);font-size:13px;font-weight:700}.tool-card-desc{color:var(--text3);white-space:nowrap;text-overflow:ellipsis;margin-top:1px;font-size:11px;overflow:hidden}.tool-card-tokens{color:var(--text3);font-size:11px;font-weight:600;font-family:var(--font-mono);font-variant-numeric:tabular-nums;white-space:nowrap}.never-used-tag{color:var(--text3);background:var(--overlay-4);vertical-align:middle;border-radius:3px;margin-left:6px;padding:1px 5px;font-size:8px;font-weight:600}.save-profile-bar{border-radius:var(--radius-md);background:var(--overlay-2);border:1px dashed var(--overlay-10);align-items:center;gap:8px;margin-top:12px;padding:10px 12px;display:flex}.save-profile-bar input{border-radius:var(--radius-sm);border:1px solid var(--overlay-10);background:var(--overlay-4);color:var(--text);font-size:11px;font-family:var(--font-ui);transition:border-color var(--duration-fast);outline:none;flex:1;padding:6px 10px}.save-profile-bar input:focus{border-color:#60a5fa66;box-shadow:0 0 0 3px #60a5fa1a}.save-profile-bar button{border-radius:var(--radius-sm);cursor:pointer;font-size:11px;font-weight:700;font-family:var(--font-ui);transition:all var(--duration-fast);border:none;padding:6px 14px}.save-profile-bar button:active{transform:scale(.97)}.btn-primary{color:var(--blue);background:#60a5fa33}.btn-primary:hover{background:#60a5fa4d}.btn-secondary{background:var(--overlay-6);color:var(--text2)}.btn-secondary:hover{background:var(--overlay-10);color:var(--text-hover)}.savings-banner{border-radius:var(--radius-sm);color:var(--green);background:#34d39914;border:1px solid #34d39926;margin-top:8px;padding:8px 12px;font-size:11px;font-weight:600}.session-tabs{border-bottom:1px solid var(--border);background:var(--bg2);scrollbar-width:none;animation:fadeInUp var(--duration-normal) var(--ease-out) 30ms both;gap:0;padding:0 24px;display:none;overflow-x:auto}.session-tabs::-webkit-scrollbar{display:none}.session-tabs.visible{display:flex}.session-tab{color:var(--text3);cursor:pointer;white-space:nowrap;transition:color var(--duration-fast) var(--ease-out), border-color var(--duration-fast) var(--ease-out);border-bottom:2px solid #0000;flex-shrink:0;padding:8px 18px 8px 14px;font-size:12px;font-weight:600;position:relative}.session-tab:hover{color:var(--text2)}.session-tab.active{color:var(--cyan);border-bottom-color:var(--cyan);font-weight:700}.session-tab-close{color:var(--text3);cursor:pointer;font-size:12px;line-height:1;display:none;position:absolute;top:4px;right:2px}.session-tab:hover .session-tab-close{display:block}.session-tab-close:hover{color:var(--red)}.session-tab-label{display:block}.session-tab-path{color:var(--text3);font-size:9px;font-weight:400;font-family:var(--font-mono);text-overflow:ellipsis;white-space:nowrap;text-align:left;direction:rtl;max-width:160px;display:block;overflow:hidden}@keyframes pulse{0%,to{opacity:1}50%{opacity:.4}}.waiting{animation:2s ease-in-out infinite pulse}@keyframes modal-in{0%{opacity:0;transform:scale(.96)translateY(8px)}to{opacity:1;transform:scale(1)translateY(0)}}@keyframes fadeInUp{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.header{animation:fadeInUp var(--duration-normal) var(--ease-out) both}.bar-container{animation:fadeInUp var(--duration-normal) var(--ease-out) 80ms both}.reqs-panel{animation:fadeInUp var(--duration-normal) var(--ease-out) .14s both}.detail-panel{animation:fadeInUp var(--duration-normal) var(--ease-out) .2s both}button:active{transform:scale(.97)}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var e={connected:!1,reqs:[],selectedReq:null,profiles:{},activeProfile:`All Tools`,premium:!1,routerMode:`off`,toolsUsed:new Set,groups:{},groupView:!0,expandedGroups:{},sessions:{},activeSessionTab:null},t={segment:null,segIndex:null,view:`formatted`,fullContent:``,parsedTools:null,loading:!1},n={system:`#60A5FA`,tools:`#FB923C`,message:`#22D3EE`,assistant:`#34D399`,tool_result:`#FBBF24`,tool_use:`#A78BFA`},r={system:`--seg-system`,tools:`--seg-tools`,message:`--seg-message`,assistant:`--seg-assistant`,tool_result:`--seg-tool-result`,tool_use:`--seg-tool-use`};function i(e){let t=r[e];if(t){let e=getComputedStyle(document.documentElement).getPropertyValue(t).trim();if(e)return e}return n[e]||`#64748B`}var a=3.8;function o(e){if(!e)return 0;let t=typeof e==`string`?e:JSON.stringify(e);return Math.ceil(t.length/a)}function s(e){return e.type===`message`&&e.role===`assistant`?i(`assistant`):e.type===`message`?i(`message`):i(e.type)}function c(e){return e.type===`message`?e.role===`user`?`User Message`:`Assistant Message`:e.type===`tool_result`?`Tool Result`:e.type===`tool_use`?`Tool Use`:e.type===`system`?`System Prompt`:e.type===`tools`?`Tool Definitions`:e.type}function l(e){return e>=1e6?(e/1e6).toFixed(1)+`M`:e>=1e3?(e/1e3).toFixed(1)+`k`:e.toString()}function u(e){return e>=1?`$`+e.toFixed(2):e>=.01?`$`+e.toFixed(3):`$`+e.toFixed(4)}function d(e){return e?e.includes(`opus-4-6`)||e.includes(`opus-4.6`)||e.includes(`opus-4-5`)||e.includes(`opus-4.5`)?5:e.includes(`opus`)?15:e.includes(`haiku-4`)?1:e.includes(`haiku`)?.8:3:3}function f(e){let t=document.createElement(`div`);return t.textContent=e,t.innerHTML}function p(e,t,n){return n||!t||!t.tools||t.tools.length===0?!0:t.mode===`blocklist`?!t.tools.includes(e):t.tools.includes(e)}function m(e){let t=e?.name||``;if(t.startsWith(`mcp__`)){let e=t.split(`__`);if(e.length>=3){let t=e[1].split(`_`);return t[t.length-1].toLowerCase()}}let n=t.match(/^([a-zA-Z0-9]+)[_\/]/);return n?n[1].toLowerCase():`other`}function h(e){let t=new Map;for(let n of e){let e=m(n);t.has(e)||t.set(e,[]),t.get(e).push(n)}let n=new Map,r=[...t.keys()].sort((e,t)=>e===`other`?1:t===`other`?-1:e.localeCompare(t));for(let e of r)n.set(e,t.get(e));return n}var g=`jannal_session`,_=`jannal_daily_costs`,v=`jannal_daily_savings`,ee=500,y=null;function b(e){y&&clearTimeout(y),y=setTimeout(()=>{try{let t={reqs:e.reqs,selectedReq:e.selectedReq,groupView:e.groupView,sessions:e.sessions,activeSessionTab:e.activeSessionTab,savedAt:Date.now()};localStorage.setItem(g,JSON.stringify(t))}catch(e){console.warn(`Failed to persist session:`,e.message)}y=null},ee)}function te(e){try{let t=localStorage.getItem(g);if(!t)return!1;let n=JSON.parse(t),r=n.reqs||n.turns,i=n.selectedReq??n.selectedTurn;if(r&&Array.isArray(r)&&r.length>0){e.reqs=r,e.selectedReq=i!=null&&i<e.reqs.length?i:e.reqs.length-1,n.groupView!=null&&(e.groupView=n.groupView),n.sessions&&(e.sessions=n.sessions),n.activeSessionTab!==void 0&&(e.activeSessionTab=n.activeSessionTab);for(let t of e.reqs)t.sessionId&&!t.tabKey&&(t.tabKey=t.sessionPath||t.sessionId);if(e.toolsUsed){e.toolsUsed.clear();for(let t of e.reqs)t.toolsUsed?.length&&t.toolsUsed.forEach(t=>e.toolsUsed.add(t))}return!0}}catch(e){console.warn(`Failed to restore session:`,e.message)}return!1}function ne(e){let t=0,n=e.reqs.map(e=>{let n=e.actualCost?.totalCost??e.estimatedCost?.totalCost??0;return t+=n,{request:e.turn,model:e.model,timestamp:e.timestamp,inputTokens:e.actualUsage?.input_tokens??e.totalEstimatedTokens,outputTokens:e.actualUsage?.output_tokens??0,cost:n,segments:e.segments?.map(e=>({name:e.name,type:e.type,tokens:e.tokens}))??[]}}),r={exportedAt:new Date().toISOString(),requestCount:n.length,totalCost:t,requests:n};return JSON.stringify(r,null,2)}function x(e){let t=[`Request`,`Model`,`Timestamp`,`Input Tokens`,`Output Tokens`,`Cost ($)`],n=e.reqs.map(e=>[e.turn,e.model,new Date(e.timestamp).toISOString(),e.actualUsage?.input_tokens??e.totalEstimatedTokens??``,e.actualUsage?.output_tokens??0,(e.actualCost?.totalCost??e.estimatedCost?.totalCost??0).toFixed(4)]);return[t.join(`,`),...n.map(e=>e.map((e,t)=>t===5?e:`"${String(e)}"`).join(`,`))].join(`
|
|
2
|
+
`)}function re(e){if(!(!e||e<=0))try{let t=new Date().toISOString().slice(0,10),n=JSON.parse(localStorage.getItem(_)||`{}`);n[t]=(n[t]||0)+e,localStorage.setItem(_,JSON.stringify(n))}catch{}}function ie(){try{let e=new Date().toISOString().slice(0,10);return JSON.parse(localStorage.getItem(_)||`{}`)[e]||0}catch{return 0}}function ae(e,t){if(!((!e||e<=0)&&(!t||t<=0)))try{let n=new Date().toISOString().slice(0,10),r=JSON.parse(localStorage.getItem(v)||`{}`);r[n]||(r[n]={cost:0,tokens:0}),typeof r[n]==`number`&&(r[n]={cost:r[n],tokens:0}),r[n].cost+=e||0,r[n].tokens+=t||0,localStorage.setItem(v,JSON.stringify(r))}catch{}}function oe(){try{let e=new Date().toISOString().slice(0,10),t=JSON.parse(localStorage.getItem(v)||`{}`)[e];return t?typeof t==`number`?{cost:t,tokens:0}:{cost:t.cost||0,tokens:t.tokens||0}:{cost:0,tokens:0}}catch{return{cost:0,tokens:0}}}function S(e,t){let n=new Blob([e],{type:`application/octet-stream`}),r=URL.createObjectURL(n),i=document.createElement(`a`);i.href=r,i.download=t,i.click(),URL.revokeObjectURL(r)}function C(e={}){T(),E(),le(),ce(),O(),e.skipDetail||j(),se()}function se(){let t=document.getElementById(`exportBtn`);t&&(t.disabled=e.reqs.length===0,t.title=e.reqs.length===0?`No data to export`:`Export session as JSON or CSV`)}function w(){let t=[];for(let n=0;n<e.reqs.length;n++){let r=e.reqs[n];(e.activeSessionTab===null||r.tabKey===e.activeSessionTab)&&t.push({originalIndex:n,req:r})}return t}function ce(){let t=document.getElementById(`sessionTabs`);if(!t)return;let n=Object.keys(e.sessions);if(n.length===0){t.classList.remove(`visible`);return}t.classList.add(`visible`);let r=`<div class="session-tab${e.activeSessionTab===null?` active`:``}" data-tab="">All</div>`;for(let t of n){let n=e.sessions[t],i=e.activeSessionTab===t;r+=`<div class="session-tab${i?` active`:``}" data-tab="${f(t)}">`,r+=`<span class="session-tab-label">${f(n.label)}</span>`,n.path&&(r+=`<span class="session-tab-path">${f(n.path)}</span>`),r+=`<span class="session-tab-close" data-tab-close="${f(t)}">×</span>`,r+=`</div>`}t.innerHTML=r,t.onclick=e=>{let t=e.target.closest(`[data-tab-close]`);if(t){e.stopPropagation(),window.dismissSessionTab(t.dataset.tabClose);return}let n=e.target.closest(`[data-tab]`);n&&window.selectSessionTab(n.dataset.tab||null)}}function T(){document.getElementById(`statusDot`).className=`status-dot ${e.connected?`connected`:`disconnected`}`;let t=document.getElementById(`statusText`);t.textContent=e.connected?`Connected`:`Disconnected`,t.style.color=e.connected?`var(--green)`:`var(--red)`;let n=document.getElementById(`reqBadge`);n&&(n.textContent=`Req ${e.reqs.length}`);let r=ie(),i=document.getElementById(`dailyCost`);i&&(i.textContent=`Cost: ${u(r)}`);let a=document.getElementById(`dailySaved`);if(a)if(!e.premium)a.textContent=`Saved: Pro`,a.className=`daily-saved premium-locked`,a.title=`Savings intelligence requires Pro`;else{let{cost:e,tokens:t}=oe(),n=t>0?` (${l(t)})`:``;a.textContent=`Saved: ${u(e)}${n}`,a.className=`daily-saved`,a.classList.toggle(`has-savings`,e>0),a.title=`Estimated daily savings from router intelligence`}let o=document.getElementById(`routerBadge`);if(o)if(e.premium){let t=e.routerMode||`off`;o.textContent={off:`Router Off`,shadow:`Router Shadow`,auto:`Router Auto`}[t]||`Router`,o.className=`router-badge router-badge--${t}`;let n=document.getElementById(`routerPopover`);if(n)for(let e of n.querySelectorAll(`.router-popover-opt`))e.classList.toggle(`active`,e.dataset.mode===t),e.classList.remove(`premium-locked`)}else{o.textContent=`Router Pro`,o.className=`router-badge premium-locked`;let e=document.getElementById(`routerPopover`);if(e)for(let t of e.querySelectorAll(`.router-popover-opt`))t.classList.add(`premium-locked`),t.classList.remove(`active`)}}function E(){let t=e.selectedReq===null?null:e.reqs[e.selectedReq],n=document.getElementById(`barInner`),r=document.getElementById(`barOuter`);if(!t){n.innerHTML=`<div class="bar-empty"><span>No data yet</span></div>`,r.className=`bar-outer`,document.getElementById(`barLegend`).innerHTML=``,document.getElementById(`barTotal`).textContent=`0 / 0`,document.getElementById(`barPct`).textContent=`0%`;return}let i=t.budget,a=t.actualUsage?t.actualUsage.input_tokens:t.totalEstimatedTokens,o=a/i*100;r.className=`bar-outer`+(o>95?` pressure-critical`:o>80?` pressure-high`:``);let u=1,d=0;if(o>0&&o<30){let e=(1-o/30)**2;u=(o+(65-o)*e)/o,d=e}let f=u>1,p=[];for(let e=0;e<t.segments.length;e++){let n=t.segments[e],r=s(n),a=n.tokens/i*100*u,o=p[p.length-1];o&&o.color===r&&a<.3?(o.tokens+=n.tokens,o.count++,o.endIndex=e):p.push({color:r,tokens:n.tokens,name:n.name,count:1,startIndex:e,endIndex:e})}let m=``;for(let e of p){let t=e.tokens/i*100*u;if(t<.1)continue;let n=e.count>1?`${e.name} (×${e.count})`:e.name;m+=`<div class="bar-segment" style="width:${t}%;background:linear-gradient(180deg,${e.color}cc,${e.color}88);border-right:1.5px solid var(--bg3)" title="${n}: ${l(e.tokens)} tokens" onclick="openModal(${e.startIndex})">`,t>5&&(m+=`<span>${t>15?n:l(e.tokens)}</span>`),m+=`</div>`}f&&(m+=`<div class="bar-break" style="opacity:${d}"></div>`),o<100&&(m+=`<div class="bar-empty"><span>${l(i-a)} free</span></div>`),n.innerHTML=m,r.querySelectorAll(`.bar-marker`).forEach(e=>{e.style.display=f?`none`:``});let h=new Map;for(let e of t.segments){let t=c(e),n=s(e);h.has(t)||h.set(t,n)}document.getElementById(`barLegend`).innerHTML=Array.from(h.entries()).map(([e,t])=>`<div class="legend-item"><div class="legend-dot" style="background:${t}"></div>${e}</div>`).join(``);let g=t.actualUsage?l(t.actualUsage.input_tokens):t.tokenCountSource===`count_tokens`?l(t.totalEstimatedTokens):`~${l(t.totalEstimatedTokens)}`,_=document.getElementById(`barTotal`),v=document.getElementById(`barPct`);_.textContent=`${g} / ${l(i)}`,_.style.color=o>95?`var(--red)`:o>80?`var(--orange)`:`var(--text)`,v.textContent=`${o.toFixed(1)}%`,v.style.color=o>95?`var(--red)`:o>80?`var(--orange)`:`var(--text3)`}function le(){let e=document.getElementById(`tokenChartContainer`),t=document.getElementById(`tokenChart`);if(!e||!t)return;let n=w();if(n.length<2){e.style.display=`none`;return}e.style.display=`block`;let r=n.map(e=>e.req.actualUsage?.input_tokens??e.req.totalEstimatedTokens??0),i=Math.max(...r),a=Math.min(...r),o=i-a||1;t.innerHTML=`
|
|
3
|
+
<svg viewBox="0 0 200 36" preserveAspectRatio="none" class="token-chart-svg">
|
|
4
|
+
<polyline
|
|
5
|
+
fill="none"
|
|
6
|
+
stroke="var(--cyan)"
|
|
7
|
+
stroke-width="2"
|
|
8
|
+
stroke-linecap="round"
|
|
9
|
+
stroke-linejoin="round"
|
|
10
|
+
points="${r.map((e,t)=>`${t/(r.length-1)*200},${36-(e-a)/o*32-2}`).join(` `)}"
|
|
11
|
+
/>
|
|
12
|
+
</svg>
|
|
13
|
+
<div class="token-chart-hint">${r.length} reqs · ${l(a)} → ${l(i)} tokens</div>
|
|
14
|
+
`}function D(){return`ANTHROPIC_BASE_URL=http://localhost:${location.port===`5173`?`4455`:location.port||`4455`} claude`}function ue(){navigator.clipboard.writeText(D()).then(()=>{let e=document.getElementById(`copyCommandBtn`);if(e){let t=e.textContent;e.textContent=`Copied!`,e.style.color=`var(--green)`,setTimeout(()=>{e.textContent=t,e.style.color=``},1500)}})}function O(){let t=document.getElementById(`reqList`),n=w();if(n.length===0){e.reqs.length===0?t.innerHTML=`<div class="empty"><div class="empty-icon waiting">🔍</div><h2>Waiting for requests...</h2><p>Start Claude Code with:<br><code style="color:var(--cyan);font-size:11px">${D()}</code> <button id="copyCommandBtn" class="copy-command-btn" onclick="copyClaudeCommand()" title="Copy to clipboard">Copy</button></p></div>`:t.innerHTML=`<div class="empty"><div class="empty-icon">🔍</div><h2>No requests in this session</h2></div>`;return}let r=document.getElementById(`viewToggleBtn`);r&&(r.textContent=e.groupView?`Grouped`:`Flat`,r.classList.toggle(`active`,e.groupView));let i=t.scrollTop;e.groupView&&Object.keys(e.groups).length>0?de(t,n):k(t,n),t.scrollTop=i}function k(e,t){let n=``;for(let e=t.length-1;e>=0;e--)n+=A(t[e].originalIndex,e+1);e.innerHTML=n}function A(t,n){let r=e.reqs[t],i=r.actualUsage?r.actualUsage.input_tokens:r.totalEstimatedTokens,a=Math.min(i/r.budget*100,100),o=a>95?`var(--red)`:a>80?`var(--orange)`:`var(--green)`,s=r.model.replace(`claude-`,``).replace(/-\d{8,}$/,``),c=r.actualUsage?l(r.actualUsage.input_tokens):r.tokenCountSource===`count_tokens`?l(r.totalEstimatedTokens):`~`+l(r.totalEstimatedTokens),d=``;r.actualUsage&&(d=`${l(r.actualUsage.input_tokens)} in / ${l(r.actualUsage.output_tokens)} out`);let f=r.actualCost?u(r.actualCost.totalCost):r.estimatedCost?`~`+u(r.estimatedCost.totalCost):``,p=`<div class="req-card${t===e.selectedReq?` selected`:``}" onclick="selectReq(${t})">`,m=n??r.turn;return p+=`<div class="req-card-head"><span class="req-label">Req ${m}</span><span class="req-tokens" style="color:${o}">${c}</span></div>`,p+=`<div class="req-mini-bar"><div class="req-mini-fill" style="width:${a}%;background:${o}"></div></div>`,p+=`<div class="req-meta"><span>${s}</span>`,d&&(p+=`<span class="req-io">${d}</span>`),f&&(p+=`<span class="req-cost-inline">${f}</span>`),p+=`</div>`,p+=`</div>`,p}function de(t,n){let r=new Set(n.map(e=>e.originalIndex)),i=new Map,a=0;for(let e of n)i.set(e.originalIndex,++a);let o=Object.keys(e.groups).map(Number).sort((e,t)=>t-e),s=``;for(let t of o){let n=e.groups[t],a=n.reqIndices.filter(e=>r.has(e));if(a.length===0)continue;let o=e.expandedGroups[t]!==!1,c=0,d=0;for(let t of a){let n=e.reqs[t];n&&(n.actualCost?c+=n.actualCost.totalCost:n.estimatedCost&&(c+=n.estimatedCost.totalCost),d+=n.actualUsage?.input_tokens??n.totalEstimatedTokens??0)}let f=a.length,p=t+1;s+=`<div class="group-card">`,s+=`<div class="group-header" onclick="toggleGroup(${t})">`,s+=`<span class="group-chevron ${o?`expanded`:``}">▶</span>`,s+=`<span class="group-title">Turn ${p}</span>`,s+=`<div class="group-summary">`,s+=`<span class="group-tokens">${l(d)}</span>`,s+=`<span class="group-cost">${u(c)}</span>`,s+=`<span class="group-req-count">${f} req${f===1?``:`s`}</span>`,s+=`</div></div>`,s+=`<div class="group-children ${o?``:`collapsed`}">`;let m={};for(let t of a){let n=e.reqs[t],r=n?.sessionHash||`unknown`;m[r]||(m[r]={reqIndices:[],model:n?.model||`unknown`}),m[r].reqIndices.push(t)}let h=Object.keys(m);if(h.length>1){let e=h.sort((e,t)=>m[t].reqIndices.length-m[e].reqIndices.length),t=0;for(let n of e){let e=m[n],r=e.model||`unknown`,a=t===0,o=a?`Main`:`Subagent`,c=a?`main`:`subagent`;s+=`<div class="group-session-label">`,s+=`<span class="session-pill ${c}">${o}</span>`,s+=`<span>${r} · ${e.reqIndices.length} req${e.reqIndices.length===1?``:`s`}</span>`,s+=`</div>`;for(let t=e.reqIndices.length-1;t>=0;t--)s+=A(e.reqIndices[t],i.get(e.reqIndices[t]));t++}}else for(let e=a.length-1;e>=0;e--)s+=A(a[e],i.get(a[e]));let g=new Date(n.startTime).toLocaleTimeString(),_=new Date(n.endTime).toLocaleTimeString(),v=g===_?g:`${g} – ${_}`;s+=`<div class="group-time">${v}</div>`,s+=`</div></div>`}t.innerHTML=s}function j(){let t=document.getElementById(`detailBody`),n=document.getElementById(`detailTitle`),r=document.getElementById(`detailMeta`);if(e.selectedReq===null||!e.reqs[e.selectedReq]){n.textContent=`Segment Breakdown`,r.textContent=``,t.innerHTML=`<div class="empty"><div class="empty-icon">📊</div><h2>No request selected</h2><p>Click a request on the left to see its context breakdown.</p></div>`;return}let i=e.reqs[e.selectedReq];n.textContent=`Req ${i.turn} — Segment Breakdown`,r.textContent=`${i.model} | ${i.segments.length} segments | ${i.messageCount} messages`;let a=``;a+=`<div class="stats-grid">`;let o=i.segments?.find(e=>e.type===`system`);if(o&&i.budget){let e=o.tokens/i.budget*100;e>15&&(a+=`<div class="warning-box">`,a+=`<div class="warning-box-title">System prompt is large</div>`,a+=`<div class="usage-row"><span class="usage-label">System prompt</span><span class="usage-value" style="color:var(--orange)">${l(o.tokens)} tokens (${e.toFixed(1)}% of context)</span></div>`,a+=`<div style="margin-top:6px;font-size:10px;color:var(--text3)">Consider trimming to free context for conversation.</div>`,a+=`</div>`)}if(i.filteringActive&&i.removedTools&&i.removedTools.length>0&&(a+=`<div class="filter-box">`,a+=`<div class="filter-box-title">Filtering Active</div>`,a+=`<div class="usage-row"><span class="usage-label">Original tools</span><span class="usage-value" style="color:var(--text2)">${i.originalToolCount}</span></div>`,a+=`<div class="usage-row"><span class="usage-label">After filtering</span><span class="usage-value" style="color:var(--green)">${i.filteredToolCount}</span></div>`,a+=`<div class="usage-row"><span class="usage-label">Removed</span><span class="usage-value" style="color:var(--orange)">${i.removedTools.length} tools</span></div>`,i.tokensSaved&&(a+=`<div class="usage-row"><span class="usage-label">Tokens saved</span><span class="usage-value" style="color:var(--green)">~${l(i.tokensSaved)}</span></div>`),a+=`</div>`),!e.premium)a+=`<div class="router-box premium-locked">`,a+=`<div class="router-box-title">Router Intelligence</div>`,a+=`<div class="premium-locked-msg">Intelligent routing, savings analysis, and auto-filtering.<br>Available in Pro.</div>`,a+=`</div>`;else if(i.router){let e=i.router,t=e.mode===`shadow`?`Shadow (observe only)`:e.mode===`auto`?`Auto`:e.mode||`off`;if(a+=`<div class="router-box">`,a+=`<div class="router-box-title">Router Decision</div>`,a+=`<div class="usage-row"><span class="usage-label">Mode</span><span class="usage-value router-mode-${e.mode}">${t}</span></div>`,e.eligible){let t=e.mode===`shadow`;if(a+=`<div class="usage-row"><span class="usage-label">Matched by</span><span class="usage-value" style="color:var(--cyan)">${f(e.matched_by||`—`)}</span></div>`,e.confidence!=null){let t=e.confidence>=.9?`var(--green)`:e.confidence>=.7?`var(--amber)`:`var(--orange)`;a+=`<div class="usage-row"><span class="usage-label">Confidence</span><span class="usage-value" style="color:${t}">${(e.confidence*100).toFixed(0)}%</span></div>`}if(e.selected_groups&&e.selected_groups.length>0){let n=e.selected_groups.filter(e=>e!==`core`).join(`, `)||`—`;a+=`<div class="usage-row"><span class="usage-label">${t?`Would keep`:`Selected groups`}</span><span class="usage-value" style="color:var(--text2);font-size:10px">${f(n)}</span></div>`}if(e.stripped_groups&&e.stripped_groups.length>0&&(a+=`<div class="usage-row"><span class="usage-label">${t?`Would strip`:`Stripped groups`}</span><span class="usage-value" style="color:var(--text3);font-size:10px">${f(e.stripped_groups.join(`, `))}</span></div>`),e.estimated_tokens_saved>0){let n=i.totalEstimatedTokens>0?(e.estimated_tokens_saved/i.totalEstimatedTokens*100).toFixed(1):`?`;a+=`<div class="usage-row"><span class="usage-label">${t?`Potential savings`:`Est. savings`}</span><span class="usage-value" style="color:var(--green)">~${l(e.estimated_tokens_saved)} tokens (${n}%)</span></div>`}e.sticky_reused&&(a+=`<div style="margin-top:4px;font-size:9px;color:var(--purple)">Sticky route reused</div>`)}else{let t=e.skip_reason===`router_off`?`Router is off`:e.skip_reason===`below_threshold`?`Below threshold`:e.skip_reason===`no_request_data`?`No request data`:e.skip_reason||`Skipped`;a+=`<div class="usage-row"><span class="usage-label">Status</span><span class="usage-value" style="color:var(--text3)">${f(t)}</span></div>`}e.mode===`shadow`&&(a+=`<div class="router-shadow-note">All tools forwarded — shadow mode</div>`),a+=`</div>`}if(i.actualUsage){let e=i.actualUsage,t=e.cache_read_input_tokens||0,n=e.cache_creation_input_tokens||0,r=t>0||n>0,o=e.input_tokens-i.totalEstimatedTokens,s=e.input_tokens?(o/e.input_tokens*100).toFixed(1):`0.0`;if(a+=`<div class="usage-box">`,a+=`<div class="usage-row"><span class="usage-label">Estimated input</span><span class="usage-value estimated">~${i.totalEstimatedTokens.toLocaleString()}</span></div>`,a+=`<div class="usage-row"><span class="usage-label">Actual input</span><span class="usage-value actual">${e.input_tokens.toLocaleString()}</span></div>`,r){a+=`<div class="usage-row"><span class="usage-label" style="padding-left:12px">Cache read</span><span class="usage-value" style="color:var(--green)">${t.toLocaleString()}</span></div>`,a+=`<div class="usage-row"><span class="usage-label" style="padding-left:12px">Cache write</span><span class="usage-value" style="color:var(--cyan,var(--blue))">${n.toLocaleString()}</span></div>`;let r=Math.max(0,e.input_tokens-t-n);a+=`<div class="usage-row"><span class="usage-label" style="padding-left:12px">Uncached</span><span class="usage-value">${r.toLocaleString()}</span></div>`}a+=`<div class="usage-row"><span class="usage-label">Estimation error</span><span class="usage-value" style="color:${Math.abs(parseFloat(s))<15?`var(--green)`:`var(--orange)`}">${o>0?`+`:``}${o.toLocaleString()} (${s}%)</span></div>`,a+=`<div class="usage-row"><span class="usage-label">Output tokens</span><span class="usage-value">${e.output_tokens.toLocaleString()}</span></div>`,i.actualCost&&(a+=`<div style="border-top:1px solid var(--border);margin-top:6px;padding-top:6px">`,a+=`<div class="usage-row"><span class="usage-label">Input cost</span><span class="usage-value" style="color:var(--amber)">${u(i.actualCost.inputCost)}</span></div>`,a+=`<div class="usage-row"><span class="usage-label">Output cost</span><span class="usage-value" style="color:var(--amber)">${u(i.actualCost.outputCost)}</span></div>`,a+=`<div class="usage-row"><span class="usage-label">Total cost</span><span class="usage-value" style="color:var(--amber);font-size:13px">${u(i.actualCost.totalCost)}</span></div>`,a+=`</div>`),a+=`</div>`}else if(i.estimatedCost){let e=i.tokenCountSource===`count_tokens`;a+=`<div class="usage-box">`,a+=`<div class="usage-row"><span class="usage-label">Input tokens ${e?`(exact)`:`(est.)`}</span><span class="usage-value" style="color:${e?`var(--green)`:`var(--text2)`}">${e?``:`~`}${i.totalEstimatedTokens.toLocaleString()}</span></div>`,a+=`<div class="usage-row"><span class="usage-label">Input cost ${e?``:`(est.)`}</span><span class="usage-value" style="color:${e?`var(--amber)`:`var(--text3)`}">${e?``:`~`}${u(i.estimatedCost.totalCost)}</span></div>`,e&&(a+=`<div style="margin-top:4px;font-size:9px;color:var(--text3)">via count_tokens API</div>`),a+=`</div>`}if(i.toolsUsed&&i.toolsUsed.length>0){a+=`<div class="usage-box">`,a+=`<div style="font-size:10px;font-weight:700;color:var(--cyan);margin-bottom:4px">Tools Used (${i.toolsUsed.length})</div>`;for(let e of i.toolsUsed)a+=`<div style="font-size:10px;color:var(--text2);padding:1px 0">${f(e)}</div>`;a+=`</div>`}a+=`</div>`;for(let e=0;e<i.segments.length;e++){let t=i.segments[e],n=s(t),r=(t.tokens/i.totalEstimatedTokens*100).toFixed(1),o=Math.min(r,100),c=t.preview?t.preview.slice(0,80).replace(/\n/g,` `):``,u=t.index===void 0?`#${e}`:`msg #${t.index}`;a+=`<div class="segment-row" onclick="openModal(${e})">`,a+=`<div class="seg-color" style="background:${n}"></div>`,a+=`<div class="seg-info">`,a+=`<div class="seg-name" style="color:${n}">${t.name} <span style="color:var(--text3);font-size:10px;font-weight:400">${u}</span></div>`,a+=`<div class="seg-sub">${f(c)}${t.charLength>80?`...`:``}</div>`,a+=`</div>`,a+=`<div class="seg-bar"><div class="seg-bar-fill" style="width:${o}%;background:${n}"></div></div>`,a+=`<div class="seg-pct">${r}%</div>`,a+=`<div class="seg-tokens">${l(t.tokens)}</div>`,a+=`<span class="seg-expand-hint">View</span>`,a+=`</div>`}t.innerHTML=a}async function fe(e,t){return(await fetch(`/api/content/${e}/${t}`)).json()}async function pe(e,t,n){return(await fetch(`/api/profiles`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({name:e,mode:t,tools:n})})).json()}function M(e){R({type:`set_active_profile`,profile:e})}async function N(t,n,r){try{let i=await pe(t,n,r);return i.success&&(e.profiles[t]=i.profile,P(),M(t)),i}catch(e){return console.error(`Failed to save profile:`,e),{error:e.message}}}function P(){let t=document.getElementById(`profileSelect`),n=document.getElementById(`filterBadge`),r=e.activeProfile;t.innerHTML=``;for(let n of Object.keys(e.profiles)){let e=document.createElement(`option`);e.value=n,e.textContent=n,n===r&&(e.selected=!0),t.appendChild(e)}let i=r!==`All Tools`;t.className=`profile-select`+(i?` filtering`:``),n.style.display=i?`flex`:`none`}function F(t,n){let r=n.groupId;if(r==null)return;if(!e.groups[r]){e.groups[r]={id:r,reqIndices:[],sessions:{},startTime:n.timestamp,endTime:n.timestamp};for(let t of Object.keys(e.expandedGroups))e.expandedGroups[t]=!1;e.expandedGroups[r]=!0}let i=e.groups[r];i.reqIndices.push(t),i.endTime=Math.max(i.endTime,n.timestamp);let a=n.sessionHash||`unknown`;i.sessions[a]||(i.sessions[a]={reqIndices:[],model:n.model}),i.sessions[a].reqIndices.push(t)}function I(){e.groups={},e.expandedGroups={};for(let t=0;t<e.reqs.length;t++)F(t,e.reqs[t]);let t=Object.keys(e.groups).map(Number);if(t.length>0){let n=Math.max(...t);for(let r of t)e.expandedGroups[r]=r===n}}var L;function R(e){L&&L.readyState===1&&L.send(JSON.stringify(e))}function z(){let t=location.protocol===`https:`?`wss:`:`ws:`,n=location.port===`5173`?`localhost:4455`:location.host;L=new WebSocket(`${t}//${n}`),L.onopen=()=>{e.connected=!0,T()},L.onclose=()=>{e.connected=!1,T(),setTimeout(z,2e3)},L.onmessage=t=>{let n=JSON.parse(t.data);if(n.type===`connected`){e.premium=!!n.premium,n.profiles&&(e.profiles=n.profiles),n.activeProfile&&(e.activeProfile=n.activeProfile),n.routerMode!=null&&(e.routerMode=n.routerMode),P(),C();return}if(n.type===`request`){if(n.toolsUsed&&n.toolsUsed.length&&n.toolsUsed.forEach(t=>e.toolsUsed.add(t)),e.reqs.push(n),n.sessionId&&n.sessionPath){let t=n.sessionPath;e.sessions[t]?e.sessions[t].sessionIds.includes(n.sessionId)||e.sessions[t].sessionIds.push(n.sessionId):e.sessions[t]={id:t,label:n.sessionLabel||n.sessionId,path:n.sessionPath||null,sessionIds:[n.sessionId],firstSeen:n.timestamp},n.tabKey=t}e.reqs.length>50?(e.reqs.splice(0,e.reqs.length-50),I()):F(e.reqs.length-1,n);let t=e.selectedReq!==null;t||(e.selectedReq=e.reqs.length-1),C({skipDetail:t}),b(e)}if(n.type===`token_count_update`){let t=e.reqs.find(e=>e.turn===n.turn);t&&(t.exactInputTokens=n.exactInputTokens,t.segments=n.segments,t.totalEstimatedTokens=n.exactInputTokens,t.estimatedCost=n.estimatedCost,t.tokenCountSource=`count_tokens`,C({skipDetail:!(e.selectedReq!==null&&e.reqs[e.selectedReq]?.turn===n.turn)}),b(e))}if(n.type===`response_complete`){let t=n.turn==null?e.reqs[e.reqs.length-1]:e.reqs.find(e=>e.turn===n.turn);t&&(t.actualUsage=n.usage,t.stopReason=n.stopReason,n.cost&&(t.actualCost=n.cost,re(n.cost.totalCost)),n.toolsUsed&&(t.toolsUsed=n.toolsUsed,n.toolsUsed.forEach(t=>e.toolsUsed.add(t))),C({skipDetail:!(e.selectedReq!==null&&e.reqs[e.selectedReq]?.turn===n.turn)}),b(e))}if(n.type===`router_decision`){let t=e.reqs.find(e=>e.turn===n.turn);if(t){let r=!t.router;if(t.router={mode:n.mode,eligible:n.eligible,skip_reason:n.skip_reason,matched_by:n.matched_by,confidence:n.confidence,selected_groups:n.selected_groups,stripped_groups:n.stripped_groups,estimated_tokens_saved:n.estimated_tokens_saved,sticky_reused:n.sticky_reused},r&&n.estimated_tokens_saved>0){let e=d(t.model);ae(n.estimated_tokens_saved/1e6*e*.1,n.estimated_tokens_saved)}e.selectedReq!==null&&e.reqs[e.selectedReq]?.turn===n.turn&&j(),b(e)}}n.type===`router_mode_changed`&&(e.routerMode=n.mode,T()),n.type===`profiles_updated`&&(e.profiles=n.profiles||{},e.activeProfile=n.active||`All Tools`,P()),n.type===`active_profile_changed`&&(e.activeProfile=n.active||`All Tools`,P())}}async function B(n){let r=e.reqs[e.selectedReq];if(!r)return;let i=r.segments[n];if(!i)return;t.segment=i,t.segIndex=n,t.view=i.type===`tools`?`tools`:`formatted`,t.fullContent=``,t.loading=!0,t.parsedTools=null;let a=s(i);document.getElementById(`modalColorBar`).style.background=a,document.getElementById(`modalTitle`).textContent=i.name,document.getElementById(`modalMeta`).textContent=`${c(i)}${i.role?` (`+i.role+`)`:``}${i.count?` — `+i.count+` tools`:``}`;let o=`
|
|
15
|
+
<div class="modal-stat"><div class="modal-stat-value" style="color:${a}">${l(i.tokens)}</div><div class="modal-stat-label">Tokens</div></div>
|
|
16
|
+
<div class="modal-stat"><div class="modal-stat-value">${(i.charLength||0).toLocaleString()}</div><div class="modal-stat-label">Chars</div></div>
|
|
17
|
+
<div class="modal-stat"><div class="modal-stat-value">${(i.tokens/r.totalEstimatedTokens*100).toFixed(1)}%</div><div class="modal-stat-label">Of total</div></div>
|
|
18
|
+
`;document.getElementById(`modalStats`).innerHTML=o;let u=document.getElementById(`toolsViewBtn`);u.style.display=i.type===`tools`?`inline`:`none`,document.querySelectorAll(`.modal-toolbar button`).forEach(e=>e.classList.remove(`active`)),i.type===`tools`?u.classList.add(`active`):document.getElementById(`formattedBtn`).classList.add(`active`),document.getElementById(`modalSearch`).value=``,document.getElementById(`modalBody`).innerHTML=`<div style="display:flex;align-items:center;justify-content:center;height:200px;color:var(--text3);font-size:13px"><div style="text-align:center"><div style="font-size:24px;margin-bottom:8px;animation:pulse 1s ease-in-out infinite">⏳</div>Loading full content...</div></div>`,document.getElementById(`modalOverlay`).classList.add(`open`);try{let e=await fe(r.turn,n);if(e.content){t.fullContent=e.content;let n=document.querySelector(`.modal-stat:nth-child(2) .modal-stat-value`);if(n&&(n.textContent=e.content.length.toLocaleString()),i.type===`tools`)try{t.parsedTools=JSON.parse(e.content)}catch{t.parsedTools=null}}else t.fullContent=i.preview||`(content no longer available — request may have been evicted)`}catch(e){console.error(`Failed to fetch segment content:`,e),t.fullContent=i.preview||`(failed to load content)`}t.loading=!1,U()}function V(){document.getElementById(`modalOverlay`).classList.remove(`open`),t.segment=null,t.parsedTools=null}function H(e,n){t.view=e,document.querySelectorAll(`.modal-toolbar button`).forEach(e=>e.classList.remove(`active`)),n&&n.classList.add(`active`),U()}function U(){let e=document.getElementById(`modalBody`),n=t.fullContent;if(t.loading)return;if(t.view===`tools`&&t.parsedTools){W(e);return}if(t.view===`raw`){e.innerHTML=`<div class="modal-content" style="white-space:pre-wrap;word-break:break-all">${f(n)}</div>`;return}let r=n;try{let e=JSON.parse(n);r=JSON.stringify(e,null,2)}catch{}let i=r.split(`
|
|
19
|
+
`),a=`<div class="modal-content">`;for(let e=0;e<i.length;e++)a+=`<span class="line"><span class="line-num">${e+1}</span>${f(i[e])}</span>`;a+=`</div>`,e.innerHTML=a}function W(n){let r=t.parsedTools;if(!r||!r.length){n.innerHTML=`<div style="padding:20px;color:var(--text3)">No tools found</div>`;return}let i=e.profiles[e.activeProfile],a=e.activeProfile===`All Tools`,s=h(r),c=`<div class="modal-tools">`,u=r.filter(e=>p(e.name,i,a)).length;c+=`<div class="modal-tools-header">`,c+=`<div class="modal-tools-header-left"><strong>${u}</strong> of <strong>${r.length}</strong> tools enabled</div>`,c+=`<div style="display:flex;gap:6px">`,c+=`<button class="btn-secondary" onclick="toggleAllTools(true)" style="padding:3px 8px;border-radius:4px;font-size:10px">All</button>`,c+=`<button class="btn-secondary" onclick="toggleAllTools(false)" style="padding:3px 8px;border-radius:4px;font-size:10px">None</button>`,c+=`</div></div>`;let d=[],m=[];for(let[e,t]of s)e===`other`?m.push([e,t]):d.push([e,t]);function g(t){for(let[n,r]of t){let t=[...r].sort((e,t)=>o(t)-o(e)),s=n===`other`?`Other`:n.charAt(0).toUpperCase()+n.slice(1),u=t.reduce((e,t)=>e+o(t),0),d=t.filter(e=>p(e.name,i,a)).length,m=d===t.length;c+=`<div class="tool-group" data-server="${f(n)}">`,c+=`<div class="tool-group-header">`,c+=`<input type="checkbox" class="tool-group-checkbox" data-server="${f(n)}" ${m?`checked`:``} onclick="event.stopPropagation(); toggleGroupCheckbox('${f(n)}', this.checked)">`,c+=`<span class="tool-group-chevron" onclick="toggleGroupAccordion('${f(n)}')">▶</span>`,c+=`<div class="tool-group-title" onclick="toggleGroupAccordion('${f(n)}')">`,c+=`<span class="tool-group-name">${f(s)}</span>`,c+=`<span class="tool-group-meta">${d}/${t.length} tools · ~${l(u)} tok</span>`,c+=`</div>`,c+=`<div class="tool-group-actions">`,c+=`<button class="btn-secondary" onclick="event.stopPropagation(); toggleGroupTools('${f(n)}', true)" style="padding:2px 6px;font-size:9px">All</button>`,c+=`<button class="btn-secondary" onclick="event.stopPropagation(); toggleGroupTools('${f(n)}', false)" style="padding:2px 6px;font-size:9px">None</button>`,c+=`</div></div>`,c+=`<div class="tool-group-body collapsed">`;for(let r of t){let t=p(r.name,i,a),s=o(r),u=(r.description||``).slice(0,120),d=!e.toolsUsed?.has(r.name);c+=`<div class="tool-card" data-tool-name="${f(r.name)}">`,c+=`<input type="checkbox" data-tool="${f(r.name)}" data-server="${f(n)}" ${t?`checked`:``} onchange="onToolToggle()">`,c+=`<div class="tool-card-info">`,c+=`<div class="tool-card-name">${f(r.name)}${d?` <span class="never-used-tag" title="Not used this session">never used</span>`:``}</div>`,u&&(c+=`<div class="tool-card-desc">${f(u)}</div>`),c+=`</div>`,c+=`<div class="tool-card-tokens">~${l(s)} tok</div>`,c+=`</div>`}c+=`</div></div>`}}d.length>0&&(c+=`<div class="tool-section-header">MCP Servers</div>`,g(d)),m.length>0&&(c+=`<div class="tool-section-header">Other Tools</div>`,g(m)),c+=`<div id="toolsSavings"></div>`;let _=e.reqs[e.selectedReq]?.toolsUsed||[];c+=`<div class="save-profile-bar">`,c+=`<input type="text" id="profileNameInput" placeholder="Profile name..." value="">`,c+=`<button class="btn-primary" onclick="saveCurrentAsProfile()">Save as Profile</button>`,_.length>0&&(c+=`<button class="btn-secondary" onclick="createProfileFromThisTurn()" title="Create profile from tools used in this request">From this turn</button>`),c+=`</div>`,c+=`</div>`,n.innerHTML=c,G()}function me(e){let t=document.querySelector(`.tool-group[data-server="${e}"]`);if(!t)return;let n=t.querySelector(`.tool-group-body`),r=t.querySelector(`.tool-group-chevron`);n&&n.classList.toggle(`collapsed`),r&&r.classList.toggle(`expanded`)}function he(e,t){document.querySelectorAll(`.modal-tools input[type="checkbox"][data-server="${e}"]:not(.tool-group-checkbox)`).forEach(e=>{e.checked=t}),G()}function ge(){document.querySelectorAll(`.tool-group-checkbox`).forEach(e=>{let t=e.dataset.server,n=document.querySelectorAll(`.modal-tools input[type="checkbox"][data-server="${t}"]:not(.tool-group-checkbox)`),r=n.length,i=0;n.forEach(e=>{e.checked&&i++}),e.checked=i===r,e.indeterminate=i>0&&i<r;let a=e.closest(`.tool-group`)?.querySelector(`.tool-group-meta`);if(a){let e=a.textContent.match(/·.*$/);a.textContent=`${i}/${r} tools ${e?e[0]:``}`}})}function _e(e,t){document.querySelectorAll(`.modal-tools input[data-server="${e}"]:not(.tool-group-checkbox)`).forEach(e=>{e.checked=t}),G()}function ve(e){document.querySelectorAll(`.modal-tools input[type="checkbox"]:not(.tool-group-checkbox)`).forEach(t=>{t.checked=e}),G()}function G(){let e=document.querySelectorAll(`.modal-tools input[type="checkbox"]:not(.tool-group-checkbox)`),n=t.parsedTools;if(!n||!e.length)return;let r=0,i=0,a=0;e.forEach(e=>{let t=n.find(t=>t.name===e.dataset.tool);if(!t)return;let s=o(t);e.checked?(r+=s,a++):i+=s}),ge();let s=document.getElementById(`toolsSavings`);s&&i>0?s.innerHTML=`<div class="savings-banner">Disabling ${n.length-a} tools saves ~${l(i)} tokens per request</div>`:s&&(s.innerHTML=``);let c=document.querySelector(`.modal-tools-header-left`);c&&(c.innerHTML=`<strong>${a}</strong> of <strong>${n.length}</strong> tools enabled`)}async function ye(){let t=e.reqs[e.selectedReq],n=t?.toolsUsed||[];if(n.length!==0&&(await N(`Req ${t?.turn??0} tools`,`allowlist`,n)).success){let e=document.querySelector(`.save-profile-bar`);if(e){let t=e.style.background;e.style.background=`rgba(52,211,153,0.1)`,e.style.borderColor=`rgba(52,211,153,0.3)`,setTimeout(()=>{e.style.background=t,e.style.borderColor=``},1500)}}}async function be(){let e=document.getElementById(`profileNameInput`),t=(e?.value||``).trim();if(!t){e.style.borderColor=`var(--red)`,e.focus(),setTimeout(()=>e.style.borderColor=``,1500);return}let n=document.querySelectorAll(`.modal-tools input[type="checkbox"]:not(.tool-group-checkbox)`),r=[];if(n.forEach(e=>{e.checked&&r.push(e.dataset.tool)}),(await N(t,`allowlist`,r)).success){e.value=``;let t=document.querySelector(`.save-profile-bar`);if(t){let e=t.style.background;t.style.background=`rgba(16,185,129,0.1)`,t.style.borderColor=`rgba(16,185,129,0.3)`,setTimeout(()=>{t.style.background=e,t.style.borderColor=``},1500)}}}function K(){let e=document.getElementById(`modalSearch`).value.trim().toLowerCase(),n=document.getElementById(`modalBody`);if(!e){U();return}if(t.view===`tools`){document.querySelectorAll(`.tool-group`).forEach(t=>{let n=0;t.querySelectorAll(`.tool-card`).forEach(t=>{let r=t.querySelector(`.tool-card-name`)?.textContent?.toLowerCase()||``,i=t.querySelector(`.tool-card-desc`)?.textContent?.toLowerCase()||``,a=r.includes(e)||i.includes(e);t.style.display=a?`flex`:`none`,a&&n++}),t.style.display=n>0?`block`:`none`;let r=t.querySelector(`.tool-group-body`),i=t.querySelector(`.tool-group-chevron`);n>0?(r?.classList.remove(`collapsed`),i?.classList.add(`expanded`)):(r?.classList.add(`collapsed`),i?.classList.remove(`expanded`))});return}let r=t.fullContent;try{r=JSON.stringify(JSON.parse(r),null,2)}catch{}let i=r.split(`
|
|
20
|
+
`),a=`<div class="modal-content">`;for(let t=0;t<i.length;t++){let n=i[t],r=n.toLowerCase().indexOf(e);if(r!==-1){let i=f(n.slice(0,r)),o=f(n.slice(r,r+e.length)),s=f(n.slice(r+e.length));a+=`<span class="line"><span class="line-num">${t+1}</span>${i}<span class="highlight">${o}</span>${s}</span>`}else a+=`<span class="line" style="opacity:0.25"><span class="line-num">${t+1}</span>${f(n)}</span>`}a+=`</div>`,n.innerHTML=a}function q(){navigator.clipboard.writeText(t.fullContent).then(()=>{let e=document.getElementById(`copyBtn`),t=e.textContent;e.textContent=`Copied!`,e.style.color=`var(--green)`,setTimeout(()=>{e.textContent=t,e.style.color=``},1500)})}var J=`jannal_theme`;function Y(){return localStorage.getItem(J)||`dark`}function X(e){e===`light`?document.documentElement.setAttribute(`data-theme`,`light`):document.documentElement.removeAttribute(`data-theme`);let t=document.getElementById(`themeIconMoon`),n=document.getElementById(`themeIconSun`);t&&n&&(t.style.display=e===`dark`?`block`:`none`,n.style.display=e===`light`?`block`:`none`)}function xe(){let e=Y()===`dark`?`light`:`dark`;return localStorage.setItem(J,e),document.documentElement.classList.add(`theme-transitioning`),X(e),setTimeout(()=>document.documentElement.classList.remove(`theme-transitioning`),350),e}function Se(){X(Y())}function Z(t){e.selectedReq=t,E(),O(),j(),b(e)}function Q(){e.reqs=[],e.selectedReq=null,e.groups={},e.expandedGroups={},e.sessions={},e.activeSessionTab=null,C(),b(e)}function Ce(t){e.activeSessionTab=t;let n=null;for(let r=e.reqs.length-1;r>=0;r--)if(t===null||e.reqs[r].tabKey===t){n=r;break}e.selectedReq=n,C(),b(e)}function we(t){delete e.sessions[t],e.activeSessionTab===t&&(e.activeSessionTab=null,e.selectedReq=e.reqs.length>0?e.reqs.length-1:null),C(),b(e)}function Te(){e.groupView=!e.groupView,O(),b(e)}function Ee(t){e.expandedGroups[t]=!e.expandedGroups[t],O()}function De(){e.reqs.length!==0&&document.getElementById(`exportMenu`)?.classList.toggle(`open`)}function Oe(){e.reqs.length!==0&&(S(ne(e),`jannal-session-${new Date().toISOString().slice(0,19).replace(/[:-]/g,``)}.json`),document.getElementById(`exportMenu`)?.classList.remove(`open`))}function ke(){e.reqs.length!==0&&(S(x(e),`jannal-session-${new Date().toISOString().slice(0,19).replace(/[:-]/g,``)}.csv`),document.getElementById(`exportMenu`)?.classList.remove(`open`))}window.openModal=B,window.closeModal=V,window.setModalView=H,window.selectReq=Z,window.clearReqs=Q,window.toggleGroup=Ee,window.onProfileChange=M,window.toggleAllTools=ve,window.toggleGroupTools=_e,window.toggleGroupAccordion=me,window.toggleGroupCheckbox=he,window.onToolToggle=G,window.saveCurrentAsProfile=be,window.createProfileFromThisTurn=ye,window.copyClaudeCommand=ue,window.selectSessionTab=Ce,window.dismissSessionTab=we,window.filterModalContent=K,window.copyModalContent=q;var $=null;function Ae(t){let n=document.getElementById(`globalSearchResults`);if(!t||t.length<2){n.classList.remove(`open`),n.innerHTML=``;return}clearTimeout($),$=setTimeout(async()=>{try{let r=await(await fetch(`/api/search?q=${encodeURIComponent(t)}`)).json();if(r.results.length===0){n.innerHTML=`<div class="search-no-results">No matches found</div>`,n.classList.add(`open`);return}let i=t.toLowerCase();n.innerHTML=r.results.map(t=>{let n=e.reqs.findIndex(e=>e.turn===t.turnId),r=n>=0?`Req ${e.reqs[n].turn}`:`Req ${t.turnId}`,a=t.snippet.replace(/</g,`<`).replace(/>/g,`>`).replace(RegExp(`(${i.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)})`,`gi`),`<mark>$1</mark>`);return`<div class="search-result-item" data-turn="${n}" data-seg="${t.segIndex}">
|
|
21
|
+
<div class="search-result-turn">${r} · segment ${t.segIndex}</div>
|
|
22
|
+
<div class="search-result-snippet">${a}</div>
|
|
23
|
+
</div>`}).join(``),n.classList.add(`open`)}catch(e){console.error(`Search error:`,e)}},250)}document.getElementById(`themeToggle`).addEventListener(`click`,()=>{xe(),C()}),document.getElementById(`profileSelect`).addEventListener(`change`,e=>{M(e.target.value)}),document.getElementById(`clearBtn`).addEventListener(`click`,Q),document.getElementById(`viewToggleBtn`).addEventListener(`click`,Te),document.getElementById(`exportBtn`).addEventListener(`click`,De),document.getElementById(`exportMenu`)?.addEventListener(`click`,e=>{let t=e.target.closest(`.export-option`);t&&(t.dataset.format===`json`?Oe():t.dataset.format===`csv`&&ke())}),document.addEventListener(`click`,e=>{let t=document.getElementById(`exportMenu`),n=document.querySelector(`.export-dropdown`);t?.classList.contains(`open`)&&n&&!n.contains(e.target)&&t.classList.remove(`open`);let r=document.querySelector(`.router-badge-wrapper`),i=document.getElementById(`routerPopover`);i?.classList.contains(`open`)&&r&&!r.contains(e.target)&&i.classList.remove(`open`)}),document.getElementById(`routerBadge`)?.addEventListener(`click`,e=>{e.stopPropagation(),document.getElementById(`routerPopover`)?.classList.toggle(`open`)}),document.getElementById(`routerPopover`)?.addEventListener(`click`,async t=>{let n=t.target.closest(`.router-popover-opt`);if(!n||!e.premium)return;let r=n.dataset.mode;if(r===e.routerMode){document.getElementById(`routerPopover`)?.classList.remove(`open`);return}try{(await fetch(`/api/router/mode`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({mode:r})})).ok&&(e.routerMode=r,T())}catch(e){console.error(`Failed to set router mode:`,e)}document.getElementById(`routerPopover`)?.classList.remove(`open`)}),document.getElementById(`modalOverlay`).addEventListener(`click`,e=>{e.target===e.currentTarget&&V()}),document.getElementById(`modalCloseBtn`).addEventListener(`click`,V),document.getElementById(`formattedBtn`).addEventListener(`click`,e=>{H(`formatted`,e.target)}),document.getElementById(`rawBtn`).addEventListener(`click`,e=>{H(`raw`,e.target)}),document.getElementById(`toolsViewBtn`).addEventListener(`click`,e=>{H(`tools`,e.target)}),document.getElementById(`modalSearch`).addEventListener(`input`,K),document.getElementById(`copyBtn`).addEventListener(`click`,q),document.getElementById(`globalSearch`).addEventListener(`input`,e=>{Ae(e.target.value.trim())}),document.getElementById(`globalSearchResults`).addEventListener(`click`,e=>{let t=e.target.closest(`.search-result-item`);if(!t)return;let n=parseInt(t.dataset.turn),r=parseInt(t.dataset.seg);n>=0&&(Z(n),B(r)),document.getElementById(`globalSearchResults`).classList.remove(`open`),document.getElementById(`globalSearch`).value=``}),document.addEventListener(`click`,e=>{let t=document.querySelector(`.global-search-wrapper`);t&&!t.contains(e.target)&&document.getElementById(`globalSearchResults`).classList.remove(`open`)}),document.addEventListener(`keydown`,e=>{e.key===`Escape`&&(document.getElementById(`globalSearchResults`).classList.remove(`open`),V())}),Se(),te(e),I(),z(),C();
|
package/public/index.html
CHANGED
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
10
10
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Sans:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
11
11
|
<script>(function(){var t=localStorage.getItem('jannal_theme');if(t==='light')document.documentElement.setAttribute('data-theme','light')})()</script>
|
|
12
|
-
<script type="module" crossorigin src="/assets/index-
|
|
13
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
12
|
+
<script type="module" crossorigin src="/assets/index-eeA0j5Sg.js"></script>
|
|
13
|
+
<link rel="stylesheet" crossorigin href="/assets/index-Bah0aqc_.css">
|
|
14
14
|
</head>
|
|
15
15
|
<body>
|
|
16
16
|
<div class="noise-overlay" aria-hidden="true"></div>
|
|
@@ -56,6 +56,9 @@
|
|
|
56
56
|
</div>
|
|
57
57
|
</div>
|
|
58
58
|
|
|
59
|
+
<!-- Session Tabs -->
|
|
60
|
+
<div class="session-tabs" id="sessionTabs"></div>
|
|
61
|
+
|
|
59
62
|
<!-- Context Bar -->
|
|
60
63
|
<div class="bar-container">
|
|
61
64
|
<div class="bar-outer" id="barOuter">
|
package/server.js
CHANGED
|
@@ -328,6 +328,41 @@ function createServer(opts = {}) {
|
|
|
328
328
|
return simpleHash(model + "|" + text.slice(0, 5000));
|
|
329
329
|
}
|
|
330
330
|
|
|
331
|
+
/**
|
|
332
|
+
* Extract Claude Code session identity from the request body.
|
|
333
|
+
* - sessionId: the per-session hash from the billing header (cch=...)
|
|
334
|
+
* - sessionLabel: last path component of the working directory
|
|
335
|
+
* - sessionPath: full working directory path
|
|
336
|
+
*/
|
|
337
|
+
function extractSessionInfo(body) {
|
|
338
|
+
if (!body.system) return { sessionId: null, sessionLabel: null, sessionPath: null }
|
|
339
|
+
let text
|
|
340
|
+
if (typeof body.system === 'string') {
|
|
341
|
+
text = body.system
|
|
342
|
+
} else if (Array.isArray(body.system)) {
|
|
343
|
+
text = body.system
|
|
344
|
+
.filter(b => b.type === 'text' && b.text)
|
|
345
|
+
.map(b => b.text)
|
|
346
|
+
.join('\n')
|
|
347
|
+
} else {
|
|
348
|
+
return { sessionId: null, sessionLabel: null, sessionPath: null }
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const cchMatch = text.match(/cch=([a-zA-Z0-9]+)/)
|
|
352
|
+
const sessionId = cchMatch ? cchMatch[1] : null
|
|
353
|
+
|
|
354
|
+
const dirMatch = text.match(/Primary working directory:\s*(.+)/)
|
|
355
|
+
let sessionLabel = null
|
|
356
|
+
let sessionPath = null
|
|
357
|
+
if (dirMatch) {
|
|
358
|
+
sessionPath = dirMatch[1].trim()
|
|
359
|
+
const parts = sessionPath.split('/')
|
|
360
|
+
sessionLabel = parts[parts.length - 1] || parts[parts.length - 2] || sessionPath
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return { sessionId, sessionLabel, sessionPath }
|
|
364
|
+
}
|
|
365
|
+
|
|
331
366
|
/**
|
|
332
367
|
* Walk body.messages backwards to find the most recent human-authored text.
|
|
333
368
|
*/
|
|
@@ -580,6 +615,7 @@ function createServer(opts = {}) {
|
|
|
580
615
|
const model = body.model || "unknown";
|
|
581
616
|
const sessionHash = getSessionHash(body);
|
|
582
617
|
const groupId = assignGroup(body);
|
|
618
|
+
const { sessionId, sessionLabel, sessionPath } = extractSessionInfo(body);
|
|
583
619
|
|
|
584
620
|
const toolsSeg = segments.find((s) => s.type === "tools");
|
|
585
621
|
const userMessages = (body.messages || [])
|
|
@@ -611,6 +647,9 @@ function createServer(opts = {}) {
|
|
|
611
647
|
toolCount: toolsSeg?.count || 0,
|
|
612
648
|
estimatedToolTokens: toolsSeg?.tokens || 0,
|
|
613
649
|
userMessages,
|
|
650
|
+
sessionId,
|
|
651
|
+
sessionLabel,
|
|
652
|
+
sessionPath,
|
|
614
653
|
});
|
|
615
654
|
|
|
616
655
|
// Evict old requests if over limit
|
|
@@ -637,6 +676,9 @@ function createServer(opts = {}) {
|
|
|
637
676
|
toolsUsed: [...toolsUsed],
|
|
638
677
|
groupId,
|
|
639
678
|
sessionHash,
|
|
679
|
+
sessionId,
|
|
680
|
+
sessionLabel,
|
|
681
|
+
sessionPath,
|
|
640
682
|
};
|
|
641
683
|
}
|
|
642
684
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
*,:before,:after{box-sizing:border-box;margin:0;padding:0}:root{--bg:#09090b;--bg2:#111113;--bg3:#18181b;--bg4:#27272a;--border:#ffffff0f;--text:#fafafa;--text2:#a1a1aa;--text3:#52525b;--blue:#60a5fa;--purple:#a78bfa;--green:#34d399;--orange:#fb923c;--cyan:#22d3ee;--yellow:#fbbf24;--red:#f87171;--amber:#f59e0b;--seg-system:#60a5fa;--seg-tools:#fb923c;--seg-message:#22d3ee;--seg-assistant:#34d399;--seg-tool-result:#fbbf24;--seg-tool-use:#a78bfa;--overlay-1:#ffffff03;--overlay-2:#ffffff05;--overlay-3:#ffffff08;--overlay-4:#ffffff0a;--overlay-5:#ffffff0d;--overlay-6:#ffffff0f;--overlay-8:#ffffff14;--overlay-10:#ffffff1a;--overlay-12:#ffffff1f;--overlay-15:#ffffff26;--scrollbar-thumb:#ffffff0f;--scrollbar-thumb-hover:#ffffff1f;--modal-backdrop:#000000b3;--noise-opacity:.015;--text-hover:white;--bar-seg-text:white;--bar-seg-shadow:0 1px 2px #00000080;--line-num-color:#ffffff1f;--shadow-inset:inset 0 1px 3px #0000004d;--font-ui:"Instrument Sans", -apple-system, system-ui, sans-serif;--font-mono:"JetBrains Mono", "SF Mono", "Fira Code", monospace;--radius-sm:6px;--radius-md:10px;--radius-lg:14px;--radius-xl:20px;--shadow-sm:0 1px 2px #0000004d;--shadow-md:0 4px 12px #0006;--shadow-lg:0 12px 40px #00000080;--shadow-xl:0 24px 80px #0009;--ease-out:cubic-bezier(.16, 1, .3, 1);--ease-spring:cubic-bezier(.34, 1.56, .64, 1);--duration-fast:.15s;--duration-normal:.25s;--duration-slow:.4s}html[data-theme=light]{--bg:#fafafa;--bg2:#f4f4f5;--bg3:#e4e4e7;--bg4:#d4d4d8;--border:#00000014;--text:#18181b;--text2:#52525b;--text3:#a1a1aa;--blue:#2563eb;--purple:#7c3aed;--green:#059669;--orange:#ea580c;--cyan:#0891b2;--yellow:#d97706;--red:#dc2626;--amber:#b45309;--seg-system:#2563eb;--seg-tools:#ea580c;--seg-message:#0891b2;--seg-assistant:#059669;--seg-tool-result:#d97706;--seg-tool-use:#7c3aed;--overlay-1:#00000003;--overlay-2:#00000005;--overlay-3:#00000008;--overlay-4:#0000000a;--overlay-5:#0000000d;--overlay-6:#0000000f;--overlay-8:#0000000f;--overlay-10:#00000014;--overlay-12:#0000001a;--overlay-15:#0000001f;--scrollbar-thumb:#0000001a;--scrollbar-thumb-hover:#0003;--modal-backdrop:#0000004d;--noise-opacity:0;--text-hover:var(--text);--bar-seg-text:white;--bar-seg-shadow:0 1px 2px #0000004d;--line-num-color:#0003;--shadow-inset:inset 0 1px 3px #00000014;--shadow-sm:0 1px 2px #0000000f;--shadow-md:0 4px 12px #00000014;--shadow-lg:0 12px 40px #0000001a;--shadow-xl:0 24px 80px #0000001f}html.theme-transitioning,html.theme-transitioning *,html.theme-transitioning :before,html.theme-transitioning :after{transition:background-color .3s,color .3s,border-color .3s,box-shadow .3s!important}body{background:var(--bg);color:var(--text);font-family:var(--font-ui);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;flex-direction:column;height:100vh;display:flex;overflow:hidden}::-webkit-scrollbar{width:6px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background:var(--scrollbar-thumb);transition:background var(--duration-fast);border-radius:99px}::-webkit-scrollbar-thumb:hover{background:var(--scrollbar-thumb-hover)}.noise-overlay{z-index:9999;pointer-events:none;opacity:var(--noise-opacity);background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='300'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='1'/%3E%3C/svg%3E");position:fixed;inset:0}.header{z-index:100;background:var(--bg2);justify-content:space-between;align-items:center;padding:10px 20px;display:flex;position:relative}.header:after{content:"";background:var(--border);height:1px;position:absolute;bottom:0;left:0;right:0}.header-left{align-items:center;gap:10px;display:flex}.header-brand{flex-direction:column;gap:1px;display:flex}.logo{object-fit:cover;border-radius:7px;width:28px;height:28px}.header h1{letter-spacing:-.02em;font-size:14px;font-weight:800;line-height:1}.header-brand .status{font-size:9px}.header-right{align-items:center;gap:8px;display:flex}.hdr-sep{background:var(--border);flex-shrink:0;width:1px;height:18px}.hdr-metrics{align-items:center;gap:6px;display:flex}.theme-toggle{border-radius:var(--radius-sm);border:1px solid var(--overlay-8);background:var(--overlay-3);width:26px;height:26px;color:var(--text3);cursor:pointer;transition:all var(--duration-fast);flex-shrink:0;justify-content:center;align-items:center;display:flex}.theme-toggle:hover{background:var(--overlay-8);color:var(--text);border-color:var(--overlay-12)}.theme-toggle:active{transform:scale(.95)}.status{align-items:center;gap:5px;font-size:10px;font-weight:600;display:flex}.status-dot{border-radius:50%;flex-shrink:0;width:6px;height:6px}.status-dot.connected{background:var(--green);animation:2s ease-in-out infinite statusPulse;box-shadow:0 0 6px #34d39980}.status-dot.disconnected{background:var(--red)}@keyframes statusPulse{0%,to{box-shadow:0 0 6px #34d39980}50%{box-shadow:0 0 10px #34d399b3}}.req-badge{border-radius:var(--radius-sm);background:var(--overlay-3);height:26px;color:var(--text3);font-size:10px;font-weight:600;font-family:var(--font-mono);font-variant-numeric:tabular-nums;align-items:center;padding:4px 8px;display:flex}.daily-saved{border-radius:var(--radius-sm);height:26px;color:var(--text3);font-size:10px;font-weight:700;font-family:var(--font-mono);font-variant-numeric:tabular-nums;background:#22c55e0f;border:1px solid #22c55e14;align-items:center;padding:4px 8px;display:flex}.daily-saved.has-savings{color:var(--green);background:#22c55e1a;border-color:#22c55e26}.daily-cost{border-radius:var(--radius-sm);height:26px;color:var(--amber);font-size:10px;font-weight:700;font-family:var(--font-mono);font-variant-numeric:tabular-nums;background:#f59e0b14;border:1px solid #f59e0b1f;align-items:center;padding:4px 8px;display:flex}.profile-select{border-radius:var(--radius-sm);border:1px solid var(--overlay-8);background:var(--overlay-3);height:26px;color:var(--text);font-size:10px;font-weight:600;font-family:var(--font-ui);cursor:pointer;max-width:140px;transition:all var(--duration-fast);outline:none;padding:0 8px}.profile-select:hover{background:var(--overlay-6);border-color:var(--overlay-12)}.profile-select.filtering{color:var(--orange);background:#fb923c0f;border-color:#fb923c4d}.filter-badge{border-radius:var(--radius-sm);height:26px;color:var(--orange);letter-spacing:.06em;background:#fb923c1f;border:1px solid #fb923c33;align-items:center;padding:3px 6px;font-size:8px;font-weight:800;display:flex}.router-badge-wrapper{position:relative}.router-badge{border-radius:var(--radius-sm);letter-spacing:.03em;cursor:pointer;-webkit-user-select:none;user-select:none;height:26px;transition:all var(--duration-fast);align-items:center;padding:0 8px;font-size:9px;font-weight:700;display:flex}.router-badge:hover{filter:brightness(1.2)}.router-badge--off{background:var(--overlay-3);color:var(--text3);border:1px solid var(--overlay-8)}.router-badge--shadow{color:var(--purple);background:#a78bfa1a;border:1px solid #a78bfa33}.router-badge--auto{color:var(--cyan);background:#22d3ee1a;border:1px solid #22d3ee33}.router-popover{background:var(--bg3);border:1px solid var(--border);border-radius:var(--radius-md);z-index:300;min-width:210px;padding:6px 0;display:none;position:absolute;top:calc(100% + 8px);right:0;box-shadow:0 8px 30px #00000059}.router-popover.open{display:block}.router-popover-title{color:var(--text3);text-transform:uppercase;letter-spacing:.1em;border-bottom:1px solid var(--border);margin-bottom:2px;padding:6px 12px 8px;font-size:8px;font-weight:700}.router-popover-opt{width:100%;color:var(--text);font-size:11px;font-weight:500;font-family:var(--font-ui);cursor:pointer;text-align:left;transition:background var(--duration-fast);background:0 0;border:none;align-items:center;gap:8px;padding:7px 12px;display:flex}.router-popover-opt:hover{background:var(--overlay-6)}.router-popover-opt.active{background:var(--overlay-4);font-weight:700}.router-opt-dot{border-radius:50%;flex-shrink:0;width:7px;height:7px}.router-opt-dot--off{background:var(--text3)}.router-opt-dot--shadow{background:var(--purple)}.router-opt-dot--auto{background:var(--cyan)}.router-opt-desc{color:var(--text3);margin-left:auto;font-size:9px;font-weight:400}.global-search-wrapper{position:relative}.global-search{border-radius:var(--radius-sm);border:1px solid var(--overlay-8);background:var(--overlay-3);height:26px;color:var(--text);font-size:10px;font-family:var(--font-ui);width:170px;transition:all var(--duration-fast);outline:none;padding:0 10px}.global-search::placeholder{color:var(--text3)}.global-search:focus{border-color:#60a5fa4d;width:220px;box-shadow:0 0 0 2px #60a5fa14}.global-search-results{background:var(--bg3);border:1px solid var(--border);border-radius:var(--radius-md);z-index:200;max-height:320px;box-shadow:var(--shadow-lg);min-width:360px;margin-top:4px;display:none;position:absolute;top:100%;left:0;right:0;overflow-y:auto}.global-search-results.open{display:block}.search-result-item{border-bottom:1px solid var(--overlay-4);cursor:pointer;transition:background var(--duration-fast);padding:8px 12px}.search-result-item:hover{background:var(--overlay-6)}.search-result-item:last-child{border-bottom:none}.search-result-turn{color:var(--blue);margin-bottom:2px;font-size:10px;font-weight:700}.search-result-snippet{color:var(--text2);font-size:11px;font-family:var(--font-mono);white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.search-result-snippet mark{color:var(--text);background:#fbbf244d;border-radius:2px}.search-no-results{color:var(--text3);text-align:center;padding:12px;font-size:11px}.bar-container{padding:14px 24px 10px}.bar-outer{background:var(--bg3);border:1px solid var(--border);height:52px;box-shadow:var(--shadow-inset);transition:box-shadow var(--duration-slow), border-color var(--duration-slow);border-radius:12px;position:relative;overflow:hidden}.bar-outer:before{content:"";z-index:0;pointer-events:none;background:radial-gradient(ellipse at 50% 100%, var(--overlay-2) 0%, transparent 70%);position:absolute;inset:0}.bar-outer.pressure-high{box-shadow:var(--shadow-inset), 0 0 24px #fb923c26;border-color:#fb923c40}.bar-outer.pressure-critical{box-shadow:var(--shadow-inset), 0 0 30px #f8717133;border-color:#f871714d}.bar-inner{z-index:1;height:100%;transition:all var(--duration-slow) var(--ease-out);display:flex;position:relative}.bar-segment{cursor:pointer;justify-content:center;align-items:center;height:100%;transition:all .35s;display:flex;overflow:hidden}.bar-segment:hover{filter:brightness(1.2)}.bar-segment span{color:var(--bar-seg-text);text-shadow:var(--bar-seg-shadow);white-space:nowrap;padding:0 3px;font-size:9px;font-weight:600}.bar-empty{flex-grow:1;justify-content:center;align-items:center;display:flex}.bar-empty span{color:var(--text3);font-size:10px}.bar-marker{border-left:1px dashed var(--overlay-10);pointer-events:none;z-index:2;position:absolute;top:0;bottom:0}.bar-marker span{color:var(--text3);font-size:7px;position:absolute;top:1px;left:3px}.bar-stats{justify-content:space-between;align-items:center;margin-top:6px;padding:0 2px;display:flex}.bar-legend{flex-wrap:wrap;gap:16px;display:flex}.legend-item{color:var(--text3);cursor:pointer;align-items:center;gap:4px;font-size:10px;display:flex}.legend-dot{border-radius:2px;width:7px;height:7px}.bar-total{font-size:12px;font-weight:700;font-family:var(--font-mono);font-variant-numeric:tabular-nums}.bar-pct{background:var(--overlay-4);font-size:10px;font-weight:600;font-family:var(--font-mono);border-radius:99px;padding:1px 6px}.token-chart-container{border-radius:var(--radius-sm);background:var(--overlay-2);border:1px solid var(--border);margin-top:8px;padding:8px 12px}.token-chart-label{color:var(--text3);text-transform:uppercase;letter-spacing:.05em;margin-bottom:4px;font-size:9px;font-weight:700}.token-chart{align-items:center;gap:8px;display:flex}.token-chart-svg{width:100%;max-width:240px;height:36px}.token-chart-hint{color:var(--text3);white-space:nowrap;font-size:10px;font-family:var(--font-mono)}.main{flex:1;display:flex;overflow:hidden}.panel{flex-direction:column;display:flex;overflow:hidden}.panel-header{border-bottom:1px solid var(--border);color:var(--text2);text-transform:uppercase;letter-spacing:.06em;flex-shrink:0;justify-content:space-between;align-items:center;padding:10px 16px;font-size:11px;font-weight:800;display:flex}.panel-actions{align-items:center;gap:6px;display:flex}.panel-btn{color:var(--text3);cursor:pointer;font-size:10px;font-family:var(--font-ui);border-radius:var(--radius-sm);transition:all var(--duration-fast);background:0 0;border:none;padding:3px 8px}.panel-btn:hover:not(:disabled){color:var(--text);background:var(--overlay-6)}.panel-btn:active:not(:disabled){transform:scale(.97)}.panel-btn:disabled{opacity:.5;cursor:not-allowed}.export-dropdown{position:relative}.export-menu{background:var(--bg3);border:1px solid var(--border);border-radius:var(--radius-sm);z-index:100;min-width:90px;box-shadow:var(--shadow-md);margin-top:4px;padding:4px;display:none;position:absolute;top:100%;right:0}.export-menu.open{flex-direction:column;gap:2px;display:flex}.export-option{color:var(--text);cursor:pointer;font-size:11px;font-family:var(--font-ui);text-align:left;transition:background var(--duration-fast);background:0 0;border:none;border-radius:4px;padding:6px 10px}.export-option:hover{background:var(--overlay-8)}.panel-body{flex:1;overflow-y:auto}.reqs-panel{border-right:1px solid var(--border);background:var(--bg2);width:320px}.req-card{border-bottom:1px solid var(--border);cursor:pointer;transition:background var(--duration-fast), transform var(--duration-fast);padding:6px 12px}.req-card:hover{background:var(--overlay-2)}.req-card:active{transform:scale(.99)}.req-card.selected{border-left:3px solid var(--amber);background:#f59e0b0d;padding-left:9px}.req-card-head{justify-content:space-between;align-items:center;display:flex}.req-label{color:var(--text);font-size:11px;font-weight:700}.req-tokens{font-size:10px;font-weight:600;font-family:var(--font-mono);font-variant-numeric:tabular-nums}.req-mini-bar{background:var(--bg);border-radius:99px;height:3px;margin-top:2px;overflow:hidden}.req-mini-fill{height:100%;transition:width .3s var(--ease-out);box-shadow:0 0 4px var(--overlay-5);border-radius:99px}.req-meta{color:var(--text3);font-size:9px;font-family:var(--font-mono);gap:6px;margin-top:2px;display:flex}.req-io{color:var(--green);font-weight:600}.req-cost-inline{color:var(--amber);font-weight:600}.view-toggle{transition:all var(--duration-fast);font-weight:600!important}.view-toggle.active{background:#f59e0b1a;color:var(--amber)!important}.group-card{margin-bottom:2px}.group-header{cursor:pointer;transition:background var(--duration-fast);border-bottom:1px solid var(--border);background:linear-gradient(180deg, var(--overlay-2) 0%, transparent 100%);align-items:center;gap:8px;padding:10px 14px;display:flex}.group-header:hover{background:var(--overlay-3)}.group-chevron{color:var(--text3);transition:transform var(--duration-normal) var(--ease-spring);text-align:center;flex-shrink:0;width:14px;font-size:10px}.group-chevron.expanded{transform:rotate(90deg)}.group-title{color:var(--text);flex:1;min-width:0;font-size:12px;font-weight:700}.group-summary{font-size:10px;font-family:var(--font-mono);font-variant-numeric:tabular-nums;align-items:center;gap:10px;display:flex}.group-req-count{background:var(--overlay-6);color:var(--text2);border-radius:4px;padding:2px 8px;font-weight:600}.group-cost{color:var(--amber);font-weight:600}.group-tokens{color:var(--text3)}.group-children{overflow:hidden}.group-children.collapsed{display:none}.group-session-label{color:var(--text3);text-transform:uppercase;letter-spacing:.05em;align-items:center;gap:6px;margin-left:14px;padding:4px 14px 2px 26px;font-size:9px;font-weight:700;display:flex}.session-pill{letter-spacing:.03em;text-transform:capitalize;border-radius:4px;padding:2px 8px;font-size:9px;font-weight:700;display:inline-block}.session-pill.main{color:var(--purple);background:#a78bfa26}.session-pill.subagent{color:var(--cyan);background:#22d3ee26}.group-children .req-card{border-left:2px solid var(--overlay-6);margin-left:14px;padding-left:26px}.group-children .req-card.selected{border-left:2px solid var(--amber);padding-left:26px}.group-time{color:var(--text3);font-size:9px;font-family:var(--font-mono);padding:2px 14px 8px}.detail-panel{background:var(--bg);flex:1}.segment-row{border-bottom:1px solid var(--overlay-3);transition:background var(--duration-fast);cursor:pointer;align-items:center;gap:10px;padding:12px 18px;display:flex}.segment-row:hover{background:var(--overlay-3)}.seg-color{border-radius:3px;flex-shrink:0;width:5px;height:36px}.seg-info{flex:1;min-width:0}.seg-name{font-size:12px;font-weight:600}.seg-sub{color:var(--text3);white-space:nowrap;text-overflow:ellipsis;max-width:350px;margin-top:1px;font-size:10px;overflow:hidden}.seg-tokens{font-size:12px;font-weight:700;font-family:var(--font-mono);font-variant-numeric:tabular-nums;text-align:right;min-width:60px}.seg-pct{color:var(--text3);text-align:right;min-width:40px;font-size:10px;font-family:var(--font-mono)}.seg-bar{background:var(--bg3);border-radius:99px;flex-shrink:0;width:80px;height:4px;overflow:hidden}.seg-bar-fill{border-radius:99px;height:100%}.seg-expand-hint{color:var(--text3);background:var(--overlay-3);white-space:nowrap;border-radius:4px;padding:2px 6px;font-size:9px}.empty{height:100%;color:var(--text3);flex-direction:column;justify-content:center;align-items:center;display:flex}.empty-icon{opacity:.3;margin-bottom:12px;font-size:40px}.empty h2{color:var(--text2);margin-bottom:4px;font-size:14px;font-weight:700}.empty p{text-align:center;max-width:300px;font-size:12px;line-height:1.5}.copy-command-btn{border:1px solid var(--overlay-12);background:var(--overlay-4);color:var(--cyan);cursor:pointer;font-size:10px;font-family:var(--font-ui);border-radius:4px;margin-left:6px;padding:2px 8px}.copy-command-btn:hover{background:#22d3ee1f;border-color:#22d3ee40}.stats-grid{grid-template-columns:repeat(3,1fr);gap:8px;padding:12px 16px;display:grid}.stats-grid:empty{display:none}.usage-box{border-radius:var(--radius-md);background:var(--bg2);border:1px solid var(--border);padding:10px 12px}.usage-row{justify-content:space-between;align-items:center;padding:2px 0;font-size:11px;display:flex}.usage-label{color:var(--text2)}.usage-value{font-weight:700;font-family:var(--font-mono);font-variant-numeric:tabular-nums}.usage-value.actual{color:var(--green)}.usage-value.estimated{color:var(--text2)}.filter-box{border-radius:var(--radius-md);background:#fb923c0f;border:1px solid #fb923c26;padding:10px 12px}.filter-box-title{color:var(--orange);margin-bottom:6px;font-size:11px;font-weight:700}.warning-box{border-radius:var(--radius-md);background:#fbbf240f;border:1px solid #fbbf2426;padding:10px 12px}.warning-box-title{color:var(--yellow);margin-bottom:6px;font-size:11px;font-weight:700}.router-box{border-radius:var(--radius-md);background:#a78bfa0d;border:1px solid #a78bfa26;padding:10px 12px}.router-box-title{color:var(--purple);margin-bottom:6px;font-size:11px;font-weight:700}.router-mode-shadow{color:var(--purple)}.router-mode-auto{color:var(--cyan)}.router-mode-off{color:var(--text3)}.router-shadow-note{color:var(--text3);border-top:1px solid #a78bfa1f;margin-top:6px;padding-top:6px;font-size:9px;font-style:italic}.premium-locked{opacity:.5;cursor:not-allowed}.premium-locked-msg{color:var(--text3);text-align:center;padding:8px 0;font-size:10px;line-height:1.5}.router-box.premium-locked{border-style:dashed}.router-popover-opt.premium-locked{pointer-events:none}.daily-saved.premium-locked{opacity:.5;font-size:10px}.modal-overlay{z-index:1000;background:var(--modal-backdrop);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);justify-content:center;align-items:center;padding:32px;display:none;position:fixed;inset:0}.modal-overlay.open{display:flex}.modal{background:var(--bg2);border:1px solid var(--overlay-8);border-radius:var(--radius-xl);width:100%;max-width:900px;height:85vh;box-shadow:var(--shadow-xl);animation:modal-in var(--duration-normal) var(--ease-spring);flex-direction:column;display:flex;overflow:hidden}.modal-header{border-bottom:1px solid var(--border);background:var(--overlay-2);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);flex-shrink:0;align-items:center;gap:12px;padding:16px 20px;display:flex}.modal-color-bar{border-radius:2px;width:4px;height:28px}.modal-title{flex:1}.modal-title h2{font-size:15px;font-weight:700}.modal-title .modal-meta{color:var(--text3);margin-top:2px;font-size:11px}.modal-stats{align-items:center;gap:12px;display:flex}.modal-stat{text-align:center}.modal-stat-value{font-size:14px;font-weight:800;font-family:var(--font-mono);font-variant-numeric:tabular-nums}.modal-stat-label{color:var(--text3);text-transform:uppercase;letter-spacing:.05em;font-size:9px}.modal-close{border-radius:var(--radius-sm);cursor:pointer;background:var(--overlay-6);width:32px;height:32px;color:var(--text2);transition:all var(--duration-fast);border:none;justify-content:center;align-items:center;font-size:18px;display:flex}.modal-close:hover{background:var(--overlay-12);color:var(--text-hover)}.modal-close:active{transform:scale(.97)}.modal-toolbar{border-bottom:1px solid var(--border);flex-shrink:0;align-items:center;gap:6px;padding:10px 20px;display:flex}.modal-toolbar button{cursor:pointer;font-size:11px;font-weight:600;font-family:var(--font-ui);color:var(--text2);transition:all var(--duration-fast);background:0 0;border:none;border-radius:99px;padding:5px 14px}.modal-toolbar button:hover{background:var(--overlay-6);color:var(--text-hover)}.modal-toolbar button:active{transform:scale(.97)}.modal-toolbar button.active{color:var(--blue);background:#60a5fa1f;box-shadow:inset 0 0 0 1px #60a5fa33}.modal-toolbar .spacer{flex:1}.modal-toolbar .search-box{border:1px solid var(--overlay-8);background:var(--overlay-3);color:var(--text);width:180px;font-size:11px;font-family:var(--font-ui);transition:all var(--duration-fast);border-radius:99px;outline:none;padding:5px 12px}.modal-toolbar .search-box:focus{border-color:#60a5fa66;box-shadow:0 0 0 3px #60a5fa1a}.modal-body{flex:1;min-height:0;padding:0;overflow:auto}.modal-content{font-family:var(--font-mono);color:var(--text);white-space:pre-wrap;word-break:break-word;tab-size:2;padding:12px 20px;font-size:11px;line-height:1.3}.modal-content .line{border-left:1px solid var(--overlay-4);padding:1px 0 1px 48px;display:block;position:relative}.modal-content .line:nth-child(2n){background:var(--overlay-1)}.modal-content .line:hover{background:var(--overlay-3)}.modal-content .line-num{text-align:right;width:40px;color:var(--line-num-color);-webkit-user-select:none;user-select:none;padding-right:8px;font-size:10px;position:absolute;left:0}.modal-content .highlight{background:#fbbf2433;border-radius:2px}.modal-tools{padding:16px 20px}.modal-tools-header{border-radius:var(--radius-md);background:var(--overlay-2);justify-content:space-between;align-items:center;margin-bottom:12px;padding:8px 12px;display:flex}.modal-tools-header-left{color:var(--text2);font-size:11px}.modal-tools-header-left strong{color:var(--text)}.tool-section-header{color:var(--text3);text-transform:uppercase;letter-spacing:.06em;margin-top:4px;padding:12px 4px 6px;font-size:10px;font-weight:800}.tool-group{border:1px solid var(--overlay-6);border-radius:var(--radius-md);margin-bottom:10px;overflow:hidden}.tool-group-header{background:var(--overlay-3);cursor:pointer;transition:background var(--duration-fast);align-items:center;gap:8px;padding:8px 12px;display:flex}.tool-group-header:hover{background:var(--overlay-5)}.tool-group-checkbox,.tool-card input[type=checkbox]{appearance:none;border:1.5px solid var(--text3);cursor:pointer;width:16px;height:16px;transition:all var(--duration-fast);background:0 0;border-radius:4px;flex-shrink:0;position:relative}.tool-group-checkbox:checked,.tool-card input[type=checkbox]:checked{background:var(--blue);border-color:var(--blue)}.tool-group-checkbox:checked:after,.tool-card input[type=checkbox]:checked:after{content:"";border:2px solid #fff;border-width:0 2px 2px 0;width:5px;height:9px;position:absolute;top:1px;left:4px;transform:rotate(45deg)}.tool-group-chevron{color:var(--text3);transition:transform var(--duration-normal) var(--ease-spring);-webkit-user-select:none;user-select:none;flex-shrink:0;font-size:9px}.tool-group-chevron.expanded{transform:rotate(90deg)}.tool-group-title{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.tool-group-name{color:var(--purple);font-size:12px;font-weight:700}.tool-group-meta{color:var(--text3);font-size:10px}.tool-group-actions{flex-shrink:0;gap:4px;display:flex}.tool-group-body{padding:8px}.tool-group-body.collapsed{display:none}.tool-card{border-radius:var(--radius-sm);background:var(--overlay-2);border:1px solid var(--overlay-4);transition:all var(--duration-fast);align-items:center;gap:10px;margin-bottom:4px;padding:8px 12px;display:flex}.tool-card:hover{background:var(--overlay-5);box-shadow:var(--shadow-sm)}.tool-card-info{flex:1;min-width:0}.tool-card-name{color:var(--orange);font-size:12px;font-weight:700}.tool-card-desc{color:var(--text3);white-space:nowrap;text-overflow:ellipsis;margin-top:1px;font-size:10px;overflow:hidden}.tool-card-tokens{color:var(--text3);font-size:10px;font-weight:600;font-family:var(--font-mono);font-variant-numeric:tabular-nums;white-space:nowrap}.never-used-tag{color:var(--text3);background:var(--overlay-4);vertical-align:middle;border-radius:3px;margin-left:6px;padding:1px 5px;font-size:8px;font-weight:600}.save-profile-bar{border-radius:var(--radius-md);background:var(--overlay-2);border:1px dashed var(--overlay-10);align-items:center;gap:8px;margin-top:12px;padding:10px 12px;display:flex}.save-profile-bar input{border-radius:var(--radius-sm);border:1px solid var(--overlay-10);background:var(--overlay-4);color:var(--text);font-size:11px;font-family:var(--font-ui);transition:border-color var(--duration-fast);outline:none;flex:1;padding:6px 10px}.save-profile-bar input:focus{border-color:#60a5fa66;box-shadow:0 0 0 3px #60a5fa1a}.save-profile-bar button{border-radius:var(--radius-sm);cursor:pointer;font-size:11px;font-weight:700;font-family:var(--font-ui);transition:all var(--duration-fast);border:none;padding:6px 14px}.save-profile-bar button:active{transform:scale(.97)}.btn-primary{color:var(--blue);background:#60a5fa33}.btn-primary:hover{background:#60a5fa4d}.btn-secondary{background:var(--overlay-6);color:var(--text2)}.btn-secondary:hover{background:var(--overlay-10);color:var(--text-hover)}.savings-banner{border-radius:var(--radius-sm);color:var(--green);background:#34d39914;border:1px solid #34d39926;margin-top:8px;padding:8px 12px;font-size:11px;font-weight:600}@keyframes pulse{0%,to{opacity:1}50%{opacity:.4}}.waiting{animation:2s ease-in-out infinite pulse}@keyframes modal-in{0%{opacity:0;transform:scale(.96)translateY(8px)}to{opacity:1;transform:scale(1)translateY(0)}}@keyframes fadeInUp{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.header{animation:fadeInUp var(--duration-normal) var(--ease-out) both}.bar-container{animation:fadeInUp var(--duration-normal) var(--ease-out) 60ms both}.reqs-panel{animation:fadeInUp var(--duration-normal) var(--ease-out) .12s both}.detail-panel{animation:fadeInUp var(--duration-normal) var(--ease-out) .18s both}button:active{transform:scale(.97)}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
var e=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var t={connected:!1,reqs:[],selectedReq:null,profiles:{},activeProfile:`All Tools`,premium:!1,routerMode:`off`,toolsUsed:new Set,groups:{},groupView:!0,expandedGroups:{}},n={segment:null,segIndex:null,view:`formatted`,fullContent:``,parsedTools:null,loading:!1},r={system:`#60A5FA`,tools:`#FB923C`,message:`#22D3EE`,assistant:`#34D399`,tool_result:`#FBBF24`,tool_use:`#A78BFA`},i={system:`--seg-system`,tools:`--seg-tools`,message:`--seg-message`,assistant:`--seg-assistant`,tool_result:`--seg-tool-result`,tool_use:`--seg-tool-use`};function a(e){let t=i[e];if(t){let e=getComputedStyle(document.documentElement).getPropertyValue(t).trim();if(e)return e}return r[e]||`#64748B`}var o=e(((e,t)=>{var n=3.8;function r(e){if(!e)return 0;let t=typeof e==`string`?e:JSON.stringify(e);return Math.ceil(t.length/n)}function i(e){return r(e)}var a={"gpt-4o":128e3,"gpt-4-turbo":128e3,"gpt-3.5":16385,gemini:1e6},o=[128e3,2e5,1e6];function s(e){if(!e)return 2e5;let t=e.toLowerCase();if(t.includes(`1m`)||t.includes(`opus-4-5`)||t.includes(`opus-4-6`)||t.includes(`opus-4.5`)||t.includes(`opus-4.6`))return 1e6;if(t.includes(`claude`))return 2e5;for(let[e,n]of Object.entries(a))if(t.includes(e))return n;return 2e5}function c(e,t){let n=s(e);if(t<=n)return n;for(let e of o)if(t<=e)return e;return Math.max(n,t)}function l(e){let t=[],n=e.model||`unknown`;if(e.system){let n=typeof e.system==`string`?e.system:JSON.stringify(e.system,null,2);t.push({type:`system`,name:`System Prompt`,tokens:r(n),charLength:n.length})}if(e.tools&&e.tools.length>0){let n=JSON.stringify(e.tools);t.push({type:`tools`,name:`Tools (${e.tools.length})`,tokens:r(n),count:e.tools.length})}if(e.messages)for(let n=0;n<e.messages.length;n++){let i=e.messages[n],a=typeof i.content==`string`?i.content:JSON.stringify(i.content),o=Array.isArray(i.content)&&i.content.some(e=>e.type===`tool_result`),s=Array.isArray(i.content)&&i.content.some(e=>e.type===`tool_use`),c=`message`,l=`${i.role} message`;o?(c=`tool_result`,l=`Tool Result`):s&&(c=`tool_use`,l=`Tool Use (assistant)`),t.push({type:c,role:i.role,name:l,tokens:r(a),charLength:a.length,index:n})}let i=t.reduce((e,t)=>e+t.tokens,0);return{segments:t,totalEstimatedTokens:i,budget:c(n,i)}}t.exports={CHARS_PER_TOKEN:n,estimateTokens:r,estimateToolTokens:i,getBudget:s,inferBudget:c,MODEL_BUDGETS:a,CONTEXT_TIERS:o,analyzeSegments:l}}))();function s(e){return e.type===`message`&&e.role===`assistant`?a(`assistant`):e.type===`message`?a(`message`):a(e.type)}function c(e){return e.type===`message`?e.role===`user`?`User Message`:`Assistant Message`:e.type===`tool_result`?`Tool Result`:e.type===`tool_use`?`Tool Use`:e.type===`system`?`System Prompt`:e.type===`tools`?`Tool Definitions`:e.type}function l(e){return e>=1e6?(e/1e6).toFixed(1)+`M`:e>=1e3?(e/1e3).toFixed(1)+`k`:e.toString()}function u(e){return e>=1?`$`+e.toFixed(2):e>=.01?`$`+e.toFixed(3):`$`+e.toFixed(4)}function d(e){return e?e.includes(`opus-4-6`)||e.includes(`opus-4.6`)||e.includes(`opus-4-5`)||e.includes(`opus-4.5`)?5:e.includes(`opus`)?15:e.includes(`haiku-4`)?1:e.includes(`haiku`)?.8:3:3}function f(e){let t=document.createElement(`div`);return t.textContent=e,t.innerHTML}function p(e,t,n){return n||!t||!t.tools||t.tools.length===0?!0:t.mode===`blocklist`?!t.tools.includes(e):t.tools.includes(e)}function m(e){let t=e?.name||``;if(t.startsWith(`mcp__`)){let e=t.split(`__`);if(e.length>=3){let t=e[1].split(`_`);return t[t.length-1].toLowerCase()}}let n=t.match(/^([a-zA-Z0-9]+)[_\/]/);return n?n[1].toLowerCase():`other`}function h(e){let t=new Map;for(let n of e){let e=m(n);t.has(e)||t.set(e,[]),t.get(e).push(n)}let n=new Map,r=[...t.keys()].sort((e,t)=>e===`other`?1:t===`other`?-1:e.localeCompare(t));for(let e of r)n.set(e,t.get(e));return n}var g=`jannal_session`,_=`jannal_daily_costs`,v=`jannal_daily_savings`,y=500,b=null;function x(e){b&&clearTimeout(b),b=setTimeout(()=>{try{let t={reqs:e.reqs,selectedReq:e.selectedReq,groupView:e.groupView,savedAt:Date.now()};localStorage.setItem(g,JSON.stringify(t))}catch(e){console.warn(`Failed to persist session:`,e.message)}b=null},y)}function ee(e){try{let t=localStorage.getItem(g);if(!t)return!1;let n=JSON.parse(t),r=n.reqs||n.turns,i=n.selectedReq??n.selectedTurn;if(r&&Array.isArray(r)&&r.length>0){if(e.reqs=r,e.selectedReq=i!=null&&i<e.reqs.length?i:e.reqs.length-1,n.groupView!=null&&(e.groupView=n.groupView),e.toolsUsed){e.toolsUsed.clear();for(let t of e.reqs)t.toolsUsed?.length&&t.toolsUsed.forEach(t=>e.toolsUsed.add(t))}return!0}}catch(e){console.warn(`Failed to restore session:`,e.message)}return!1}function te(e){let t=0,n=e.reqs.map(e=>{let n=e.actualCost?.totalCost??e.estimatedCost?.totalCost??0;return t+=n,{request:e.turn,model:e.model,timestamp:e.timestamp,inputTokens:e.actualUsage?.input_tokens??e.totalEstimatedTokens,outputTokens:e.actualUsage?.output_tokens??0,cost:n,segments:e.segments?.map(e=>({name:e.name,type:e.type,tokens:e.tokens}))??[]}}),r={exportedAt:new Date().toISOString(),requestCount:n.length,totalCost:t,requests:n};return JSON.stringify(r,null,2)}function S(e){let t=[`Request`,`Model`,`Timestamp`,`Input Tokens`,`Output Tokens`,`Cost ($)`],n=e.reqs.map(e=>[e.turn,e.model,new Date(e.timestamp).toISOString(),e.actualUsage?.input_tokens??e.totalEstimatedTokens??``,e.actualUsage?.output_tokens??0,(e.actualCost?.totalCost??e.estimatedCost?.totalCost??0).toFixed(4)]);return[t.join(`,`),...n.map(e=>e.map((e,t)=>t===5?e:`"${String(e)}"`).join(`,`))].join(`
|
|
2
|
-
`)}function ne(e){if(!(!e||e<=0))try{let t=new Date().toISOString().slice(0,10),n=JSON.parse(localStorage.getItem(_)||`{}`);n[t]=(n[t]||0)+e,localStorage.setItem(_,JSON.stringify(n))}catch{}}function re(){try{let e=new Date().toISOString().slice(0,10);return JSON.parse(localStorage.getItem(_)||`{}`)[e]||0}catch{return 0}}function ie(e,t){if(!((!e||e<=0)&&(!t||t<=0)))try{let n=new Date().toISOString().slice(0,10),r=JSON.parse(localStorage.getItem(v)||`{}`);r[n]||(r[n]={cost:0,tokens:0}),typeof r[n]==`number`&&(r[n]={cost:r[n],tokens:0}),r[n].cost+=e||0,r[n].tokens+=t||0,localStorage.setItem(v,JSON.stringify(r))}catch{}}function ae(){try{let e=new Date().toISOString().slice(0,10),t=JSON.parse(localStorage.getItem(v)||`{}`)[e];return t?typeof t==`number`?{cost:t,tokens:0}:{cost:t.cost||0,tokens:t.tokens||0}:{cost:0,tokens:0}}catch{return{cost:0,tokens:0}}}function C(e,t){let n=new Blob([e],{type:`application/octet-stream`}),r=URL.createObjectURL(n),i=document.createElement(`a`);i.href=r,i.download=t,i.click(),URL.revokeObjectURL(r)}function w(e={}){E(),D(),oe(),k(),e.skipDetail||M(),T()}function T(){let e=document.getElementById(`exportBtn`);e&&(e.disabled=t.reqs.length===0,e.title=t.reqs.length===0?`No data to export`:`Export session as JSON or CSV`)}function E(){document.getElementById(`statusDot`).className=`status-dot ${t.connected?`connected`:`disconnected`}`;let e=document.getElementById(`statusText`);e.textContent=t.connected?`Connected`:`Disconnected`,e.style.color=t.connected?`var(--green)`:`var(--red)`;let n=document.getElementById(`reqBadge`);n&&(n.textContent=`Req ${t.reqs.length}`);let r=re(),i=document.getElementById(`dailyCost`);i&&(i.textContent=`Cost: ${u(r)}`);let a=document.getElementById(`dailySaved`);if(a)if(!t.premium)a.textContent=`Saved: Pro`,a.className=`daily-saved premium-locked`,a.title=`Savings intelligence requires Pro`;else{let{cost:e,tokens:t}=ae(),n=t>0?` (${l(t)})`:``;a.textContent=`Saved: ${u(e)}${n}`,a.className=`daily-saved`,a.classList.toggle(`has-savings`,e>0),a.title=`Estimated daily savings from router intelligence`}let o=document.getElementById(`routerBadge`);if(o)if(t.premium){let e=t.routerMode||`off`;o.textContent={off:`Router Off`,shadow:`Router Shadow`,auto:`Router Auto`}[e]||`Router`,o.className=`router-badge router-badge--${e}`;let n=document.getElementById(`routerPopover`);if(n)for(let t of n.querySelectorAll(`.router-popover-opt`))t.classList.toggle(`active`,t.dataset.mode===e),t.classList.remove(`premium-locked`)}else{o.textContent=`Router Pro`,o.className=`router-badge premium-locked`;let e=document.getElementById(`routerPopover`);if(e)for(let t of e.querySelectorAll(`.router-popover-opt`))t.classList.add(`premium-locked`),t.classList.remove(`active`)}}function D(){let e=t.selectedReq===null?null:t.reqs[t.selectedReq],n=document.getElementById(`barInner`),r=document.getElementById(`barOuter`);if(!e){n.innerHTML=`<div class="bar-empty"><span>No data yet</span></div>`,r.className=`bar-outer`,document.getElementById(`barLegend`).innerHTML=``,document.getElementById(`barTotal`).textContent=`0 / 0`,document.getElementById(`barPct`).textContent=`0%`;return}let i=e.budget,a=e.actualUsage?e.actualUsage.input_tokens:e.totalEstimatedTokens,o=a/i*100;r.className=`bar-outer`+(o>95?` pressure-critical`:o>80?` pressure-high`:``);let u=[];for(let t=0;t<e.segments.length;t++){let n=e.segments[t],r=s(n),i=u[u.length-1];i&&i.color===r?(i.tokens+=n.tokens,i.count++,i.endIndex=t):u.push({color:r,tokens:n.tokens,name:n.name,count:1,startIndex:t,endIndex:t})}let d=``;for(let e of u){let t=e.tokens/i*100;if(t<.1)continue;let n=e.count>1?`${e.name} (×${e.count})`:e.name;d+=`<div class="bar-segment" style="width:${t}%;background:linear-gradient(180deg,${e.color}cc,${e.color}88);border-right:1.5px solid var(--bg3)" title="${n}: ${l(e.tokens)} tokens" onclick="openModal(${e.startIndex})">`,t>5&&(d+=`<span>${t>15?n:l(e.tokens)}</span>`),d+=`</div>`}o<100&&(d+=`<div class="bar-empty"><span>${l(i-a)} free</span></div>`),n.innerHTML=d;let f=new Map;for(let t of e.segments){let e=c(t),n=s(t);f.has(e)||f.set(e,n)}document.getElementById(`barLegend`).innerHTML=Array.from(f.entries()).map(([e,t])=>`<div class="legend-item"><div class="legend-dot" style="background:${t}"></div>${e}</div>`).join(``);let p=e.actualUsage?l(e.actualUsage.input_tokens):e.tokenCountSource===`count_tokens`?l(e.totalEstimatedTokens):`~${l(e.totalEstimatedTokens)}`,m=document.getElementById(`barTotal`),h=document.getElementById(`barPct`);m.textContent=`${p} / ${l(i)}`,m.style.color=o>95?`var(--red)`:o>80?`var(--orange)`:`var(--text)`,h.textContent=`${o.toFixed(1)}%`,h.style.color=o>95?`var(--red)`:o>80?`var(--orange)`:`var(--text3)`}function oe(){let e=document.getElementById(`tokenChartContainer`),n=document.getElementById(`tokenChart`);if(!e||!n)return;if(t.reqs.length<2){e.style.display=`none`;return}e.style.display=`block`;let r=t.reqs.map(e=>e.actualUsage?.input_tokens??e.totalEstimatedTokens??0),i=Math.max(...r),a=Math.min(...r),o=i-a||1;n.innerHTML=`
|
|
3
|
-
<svg viewBox="0 0 200 36" preserveAspectRatio="none" class="token-chart-svg">
|
|
4
|
-
<polyline
|
|
5
|
-
fill="none"
|
|
6
|
-
stroke="var(--cyan)"
|
|
7
|
-
stroke-width="2"
|
|
8
|
-
stroke-linecap="round"
|
|
9
|
-
stroke-linejoin="round"
|
|
10
|
-
points="${r.map((e,t)=>`${t/(r.length-1)*200},${36-(e-a)/o*32-2}`).join(` `)}"
|
|
11
|
-
/>
|
|
12
|
-
</svg>
|
|
13
|
-
<div class="token-chart-hint">${r.length} reqs · ${l(a)} → ${l(i)} tokens</div>
|
|
14
|
-
`}function O(){return`ANTHROPIC_BASE_URL=http://localhost:${location.port===`5173`?`4455`:location.port||`4455`} claude`}function se(){navigator.clipboard.writeText(O()).then(()=>{let e=document.getElementById(`copyCommandBtn`);if(e){let t=e.textContent;e.textContent=`Copied!`,e.style.color=`var(--green)`,setTimeout(()=>{e.textContent=t,e.style.color=``},1500)}})}function k(){let e=document.getElementById(`reqList`);if(t.reqs.length===0){e.innerHTML=`<div class="empty"><div class="empty-icon waiting">🔍</div><h2>Waiting for requests...</h2><p>Start Claude Code with:<br><code style="color:var(--cyan);font-size:11px">${O()}</code> <button id="copyCommandBtn" class="copy-command-btn" onclick="copyClaudeCommand()" title="Copy to clipboard">Copy</button></p></div>`;return}let n=document.getElementById(`viewToggleBtn`);n&&(n.textContent=t.groupView?`Grouped`:`Flat`,n.classList.toggle(`active`,t.groupView));let r=e.scrollTop;t.groupView&&Object.keys(t.groups).length>0?j(e):ce(e),e.scrollTop=r}function ce(e){let n=``;for(let e=t.reqs.length-1;e>=0;e--)n+=A(e);e.innerHTML=n}function A(e){let n=t.reqs[e],r=n.actualUsage?n.actualUsage.input_tokens:n.totalEstimatedTokens,i=Math.min(r/n.budget*100,100),a=i>95?`var(--red)`:i>80?`var(--orange)`:`var(--green)`,o=n.model.replace(`claude-`,``).replace(/-\d{8,}$/,``),s=n.actualUsage?l(n.actualUsage.input_tokens):n.tokenCountSource===`count_tokens`?l(n.totalEstimatedTokens):`~`+l(n.totalEstimatedTokens),c=``;n.actualUsage&&(c=`${l(n.actualUsage.input_tokens)} in / ${l(n.actualUsage.output_tokens)} out`);let d=n.actualCost?u(n.actualCost.totalCost):n.estimatedCost?`~`+u(n.estimatedCost.totalCost):``,f=`<div class="req-card${e===t.selectedReq?` selected`:``}" onclick="selectReq(${e})">`;return f+=`<div class="req-card-head"><span class="req-label">Req ${n.turn}</span><span class="req-tokens" style="color:${a}">${s}</span></div>`,f+=`<div class="req-mini-bar"><div class="req-mini-fill" style="width:${i}%;background:${a}"></div></div>`,f+=`<div class="req-meta"><span>${o}</span>`,c&&(f+=`<span class="req-io">${c}</span>`),d&&(f+=`<span class="req-cost-inline">${d}</span>`),f+=`</div>`,f+=`</div>`,f}function j(e){let n=Object.keys(t.groups).map(Number).sort((e,t)=>t-e),r=``;for(let e of n){let n=t.groups[e],i=t.expandedGroups[e]!==!1,a=0,o=0;for(let e of n.reqIndices){let n=t.reqs[e];n&&(n.actualCost?a+=n.actualCost.totalCost:n.estimatedCost&&(a+=n.estimatedCost.totalCost),o+=n.actualUsage?.input_tokens??n.totalEstimatedTokens??0)}let s=n.reqIndices.length,c=Object.keys(n.sessions),d=c.length>1,f=e+1;if(r+=`<div class="group-card">`,r+=`<div class="group-header" onclick="toggleGroup(${e})">`,r+=`<span class="group-chevron ${i?`expanded`:``}">▶</span>`,r+=`<span class="group-title">Turn ${f}</span>`,r+=`<div class="group-summary">`,r+=`<span class="group-tokens">${l(o)}</span>`,r+=`<span class="group-cost">${u(a)}</span>`,r+=`<span class="group-req-count">${s} req${s===1?``:`s`}</span>`,r+=`</div></div>`,r+=`<div class="group-children ${i?``:`collapsed`}">`,d){let e=c.sort((e,t)=>{let r=n.sessions[e].reqIndices.length;return n.sessions[t].reqIndices.length-r}),t=0;for(let i of e){let e=n.sessions[i],a=e.model||`unknown`,o=t===0,s=o?`Main`:`Subagent`,c=o?`main`:`subagent`;r+=`<div class="group-session-label">`,r+=`<span class="session-pill ${c}">${s}</span>`,r+=`<span>${a} · ${e.reqIndices.length} req${e.reqIndices.length===1?``:`s`}</span>`,r+=`</div>`;for(let t=e.reqIndices.length-1;t>=0;t--)r+=A(e.reqIndices[t]);t++}}else for(let e=n.reqIndices.length-1;e>=0;e--)r+=A(n.reqIndices[e]);let p=new Date(n.startTime).toLocaleTimeString(),m=new Date(n.endTime).toLocaleTimeString(),h=p===m?p:`${p} – ${m}`;r+=`<div class="group-time">${h}</div>`,r+=`</div></div>`}e.innerHTML=r}function M(){let e=document.getElementById(`detailBody`),n=document.getElementById(`detailTitle`),r=document.getElementById(`detailMeta`);if(t.selectedReq===null||!t.reqs[t.selectedReq]){n.textContent=`Segment Breakdown`,r.textContent=``,e.innerHTML=`<div class="empty"><div class="empty-icon">📊</div><h2>No request selected</h2><p>Click a request on the left to see its context breakdown.</p></div>`;return}let i=t.reqs[t.selectedReq];n.textContent=`Req ${i.turn} — Segment Breakdown`,r.textContent=`${i.model} | ${i.segments.length} segments | ${i.messageCount} messages`;let a=``;a+=`<div class="stats-grid">`;let o=i.segments?.find(e=>e.type===`system`);if(o&&i.budget){let e=o.tokens/i.budget*100;e>15&&(a+=`<div class="warning-box">`,a+=`<div class="warning-box-title">System prompt is large</div>`,a+=`<div class="usage-row"><span class="usage-label">System prompt</span><span class="usage-value" style="color:var(--orange)">${l(o.tokens)} tokens (${e.toFixed(1)}% of context)</span></div>`,a+=`<div style="margin-top:6px;font-size:10px;color:var(--text3)">Consider trimming to free context for conversation.</div>`,a+=`</div>`)}if(i.filteringActive&&i.removedTools&&i.removedTools.length>0&&(a+=`<div class="filter-box">`,a+=`<div class="filter-box-title">Filtering Active</div>`,a+=`<div class="usage-row"><span class="usage-label">Original tools</span><span class="usage-value" style="color:var(--text2)">${i.originalToolCount}</span></div>`,a+=`<div class="usage-row"><span class="usage-label">After filtering</span><span class="usage-value" style="color:var(--green)">${i.filteredToolCount}</span></div>`,a+=`<div class="usage-row"><span class="usage-label">Removed</span><span class="usage-value" style="color:var(--orange)">${i.removedTools.length} tools</span></div>`,i.tokensSaved&&(a+=`<div class="usage-row"><span class="usage-label">Tokens saved</span><span class="usage-value" style="color:var(--green)">~${l(i.tokensSaved)}</span></div>`),a+=`</div>`),!t.premium)a+=`<div class="router-box premium-locked">`,a+=`<div class="router-box-title">Router Intelligence</div>`,a+=`<div class="premium-locked-msg">Intelligent routing, savings analysis, and auto-filtering.<br>Available in Pro.</div>`,a+=`</div>`;else if(i.router){let e=i.router,t=e.mode===`shadow`?`Shadow (observe only)`:e.mode===`auto`?`Auto`:e.mode||`off`;if(a+=`<div class="router-box">`,a+=`<div class="router-box-title">Router Decision</div>`,a+=`<div class="usage-row"><span class="usage-label">Mode</span><span class="usage-value router-mode-${e.mode}">${t}</span></div>`,e.eligible){let t=e.mode===`shadow`;if(a+=`<div class="usage-row"><span class="usage-label">Matched by</span><span class="usage-value" style="color:var(--cyan)">${f(e.matched_by||`—`)}</span></div>`,e.confidence!=null){let t=e.confidence>=.9?`var(--green)`:e.confidence>=.7?`var(--amber)`:`var(--orange)`;a+=`<div class="usage-row"><span class="usage-label">Confidence</span><span class="usage-value" style="color:${t}">${(e.confidence*100).toFixed(0)}%</span></div>`}if(e.selected_groups&&e.selected_groups.length>0){let n=e.selected_groups.filter(e=>e!==`core`).join(`, `)||`—`;a+=`<div class="usage-row"><span class="usage-label">${t?`Would keep`:`Selected groups`}</span><span class="usage-value" style="color:var(--text2);font-size:10px">${f(n)}</span></div>`}if(e.stripped_groups&&e.stripped_groups.length>0&&(a+=`<div class="usage-row"><span class="usage-label">${t?`Would strip`:`Stripped groups`}</span><span class="usage-value" style="color:var(--text3);font-size:10px">${f(e.stripped_groups.join(`, `))}</span></div>`),e.estimated_tokens_saved>0){let n=i.totalEstimatedTokens>0?(e.estimated_tokens_saved/i.totalEstimatedTokens*100).toFixed(1):`?`;a+=`<div class="usage-row"><span class="usage-label">${t?`Potential savings`:`Est. savings`}</span><span class="usage-value" style="color:var(--green)">~${l(e.estimated_tokens_saved)} tokens (${n}%)</span></div>`}e.sticky_reused&&(a+=`<div style="margin-top:4px;font-size:9px;color:var(--purple)">Sticky route reused</div>`)}else{let t=e.skip_reason===`router_off`?`Router is off`:e.skip_reason===`below_threshold`?`Below threshold`:e.skip_reason===`no_request_data`?`No request data`:e.skip_reason||`Skipped`;a+=`<div class="usage-row"><span class="usage-label">Status</span><span class="usage-value" style="color:var(--text3)">${f(t)}</span></div>`}e.mode===`shadow`&&(a+=`<div class="router-shadow-note">All tools forwarded — shadow mode</div>`),a+=`</div>`}if(i.actualUsage){let e=i.actualUsage,t=e.cache_read_input_tokens||0,n=e.cache_creation_input_tokens||0,r=t>0||n>0,o=e.input_tokens-i.totalEstimatedTokens,s=e.input_tokens?(o/e.input_tokens*100).toFixed(1):`0.0`;if(a+=`<div class="usage-box">`,a+=`<div class="usage-row"><span class="usage-label">Estimated input</span><span class="usage-value estimated">~${i.totalEstimatedTokens.toLocaleString()}</span></div>`,a+=`<div class="usage-row"><span class="usage-label">Actual input</span><span class="usage-value actual">${e.input_tokens.toLocaleString()}</span></div>`,r){a+=`<div class="usage-row"><span class="usage-label" style="padding-left:12px">Cache read</span><span class="usage-value" style="color:var(--green)">${t.toLocaleString()}</span></div>`,a+=`<div class="usage-row"><span class="usage-label" style="padding-left:12px">Cache write</span><span class="usage-value" style="color:var(--cyan,var(--blue))">${n.toLocaleString()}</span></div>`;let r=Math.max(0,e.input_tokens-t-n);a+=`<div class="usage-row"><span class="usage-label" style="padding-left:12px">Uncached</span><span class="usage-value">${r.toLocaleString()}</span></div>`}a+=`<div class="usage-row"><span class="usage-label">Estimation error</span><span class="usage-value" style="color:${Math.abs(parseFloat(s))<15?`var(--green)`:`var(--orange)`}">${o>0?`+`:``}${o.toLocaleString()} (${s}%)</span></div>`,a+=`<div class="usage-row"><span class="usage-label">Output tokens</span><span class="usage-value">${e.output_tokens.toLocaleString()}</span></div>`,i.actualCost&&(a+=`<div style="border-top:1px solid var(--border);margin-top:6px;padding-top:6px">`,a+=`<div class="usage-row"><span class="usage-label">Input cost</span><span class="usage-value" style="color:var(--amber)">${u(i.actualCost.inputCost)}</span></div>`,a+=`<div class="usage-row"><span class="usage-label">Output cost</span><span class="usage-value" style="color:var(--amber)">${u(i.actualCost.outputCost)}</span></div>`,a+=`<div class="usage-row"><span class="usage-label">Total cost</span><span class="usage-value" style="color:var(--amber);font-size:13px">${u(i.actualCost.totalCost)}</span></div>`,a+=`</div>`),a+=`</div>`}else if(i.estimatedCost){let e=i.tokenCountSource===`count_tokens`;a+=`<div class="usage-box">`,a+=`<div class="usage-row"><span class="usage-label">Input tokens ${e?`(exact)`:`(est.)`}</span><span class="usage-value" style="color:${e?`var(--green)`:`var(--text2)`}">${e?``:`~`}${i.totalEstimatedTokens.toLocaleString()}</span></div>`,a+=`<div class="usage-row"><span class="usage-label">Input cost ${e?``:`(est.)`}</span><span class="usage-value" style="color:${e?`var(--amber)`:`var(--text3)`}">${e?``:`~`}${u(i.estimatedCost.totalCost)}</span></div>`,e&&(a+=`<div style="margin-top:4px;font-size:9px;color:var(--text3)">via count_tokens API</div>`),a+=`</div>`}if(i.toolsUsed&&i.toolsUsed.length>0){a+=`<div class="usage-box">`,a+=`<div style="font-size:10px;font-weight:700;color:var(--cyan);margin-bottom:4px">Tools Used (${i.toolsUsed.length})</div>`;for(let e of i.toolsUsed)a+=`<div style="font-size:10px;color:var(--text2);padding:1px 0">${f(e)}</div>`;a+=`</div>`}a+=`</div>`;for(let e=0;e<i.segments.length;e++){let t=i.segments[e],n=s(t),r=(t.tokens/i.totalEstimatedTokens*100).toFixed(1),o=Math.min(r,100),c=t.preview?t.preview.slice(0,80).replace(/\n/g,` `):``,u=t.index===void 0?`#${e}`:`msg #${t.index}`;a+=`<div class="segment-row" onclick="openModal(${e})">`,a+=`<div class="seg-color" style="background:${n}"></div>`,a+=`<div class="seg-info">`,a+=`<div class="seg-name" style="color:${n}">${t.name} <span style="color:var(--text3);font-size:10px;font-weight:400">${u}</span></div>`,a+=`<div class="seg-sub">${f(c)}${t.charLength>80?`...`:``}</div>`,a+=`</div>`,a+=`<div class="seg-bar"><div class="seg-bar-fill" style="width:${o}%;background:${n}"></div></div>`,a+=`<div class="seg-pct">${r}%</div>`,a+=`<div class="seg-tokens">${l(t.tokens)}</div>`,a+=`<span class="seg-expand-hint">View</span>`,a+=`</div>`}e.innerHTML=a}async function le(e,t){return(await fetch(`/api/content/${e}/${t}`)).json()}async function ue(e,t,n){return(await fetch(`/api/profiles`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({name:e,mode:t,tools:n})})).json()}function N(e){z({type:`set_active_profile`,profile:e})}async function P(e,n,r){try{let i=await ue(e,n,r);return i.success&&(t.profiles[e]=i.profile,F(),N(e)),i}catch(e){return console.error(`Failed to save profile:`,e),{error:e.message}}}function F(){let e=document.getElementById(`profileSelect`),n=document.getElementById(`filterBadge`),r=t.activeProfile;e.innerHTML=``;for(let n of Object.keys(t.profiles)){let t=document.createElement(`option`);t.value=n,t.textContent=n,n===r&&(t.selected=!0),e.appendChild(t)}let i=r!==`All Tools`;e.className=`profile-select`+(i?` filtering`:``),n.style.display=i?`inline`:`none`}function I(e,n){let r=n.groupId;if(r==null)return;if(!t.groups[r]){t.groups[r]={id:r,reqIndices:[],sessions:{},startTime:n.timestamp,endTime:n.timestamp};for(let e of Object.keys(t.expandedGroups))t.expandedGroups[e]=!1;t.expandedGroups[r]=!0}let i=t.groups[r];i.reqIndices.push(e),i.endTime=Math.max(i.endTime,n.timestamp);let a=n.sessionHash||`unknown`;i.sessions[a]||(i.sessions[a]={reqIndices:[],model:n.model}),i.sessions[a].reqIndices.push(e)}function L(){t.groups={},t.expandedGroups={};for(let e=0;e<t.reqs.length;e++)I(e,t.reqs[e]);let e=Object.keys(t.groups).map(Number);if(e.length>0){let n=Math.max(...e);for(let r of e)t.expandedGroups[r]=r===n}}var R;function z(e){R&&R.readyState===1&&R.send(JSON.stringify(e))}function B(){let e=location.protocol===`https:`?`wss:`:`ws:`,n=location.port===`5173`?`localhost:4455`:location.host;R=new WebSocket(`${e}//${n}`),R.onopen=()=>{t.connected=!0,E()},R.onclose=()=>{t.connected=!1,E(),setTimeout(B,2e3)},R.onmessage=e=>{let n=JSON.parse(e.data);if(n.type===`connected`){t.premium=!!n.premium,n.profiles&&(t.profiles=n.profiles),n.activeProfile&&(t.activeProfile=n.activeProfile),n.routerMode!=null&&(t.routerMode=n.routerMode),F(),w();return}if(n.type===`request`){n.toolsUsed&&n.toolsUsed.length&&n.toolsUsed.forEach(e=>t.toolsUsed.add(e)),t.reqs.push(n),t.reqs.length>50?(t.reqs.splice(0,t.reqs.length-50),L()):I(t.reqs.length-1,n);let e=t.selectedReq!==null;e||(t.selectedReq=t.reqs.length-1),w({skipDetail:e}),x(t)}if(n.type===`token_count_update`){let e=t.reqs.find(e=>e.turn===n.turn);e&&(e.exactInputTokens=n.exactInputTokens,e.segments=n.segments,e.totalEstimatedTokens=n.exactInputTokens,e.estimatedCost=n.estimatedCost,e.tokenCountSource=`count_tokens`,w({skipDetail:!(t.selectedReq!==null&&t.reqs[t.selectedReq]?.turn===n.turn)}),x(t))}if(n.type===`response_complete`){let e=n.turn==null?t.reqs[t.reqs.length-1]:t.reqs.find(e=>e.turn===n.turn);e&&(e.actualUsage=n.usage,e.stopReason=n.stopReason,n.cost&&(e.actualCost=n.cost,ne(n.cost.totalCost)),n.toolsUsed&&(e.toolsUsed=n.toolsUsed,n.toolsUsed.forEach(e=>t.toolsUsed.add(e))),w({skipDetail:!(t.selectedReq!==null&&t.reqs[t.selectedReq]?.turn===n.turn)}),x(t))}if(n.type===`router_decision`){let e=t.reqs.find(e=>e.turn===n.turn);if(e){let r=!e.router;if(e.router={mode:n.mode,eligible:n.eligible,skip_reason:n.skip_reason,matched_by:n.matched_by,confidence:n.confidence,selected_groups:n.selected_groups,stripped_groups:n.stripped_groups,estimated_tokens_saved:n.estimated_tokens_saved,sticky_reused:n.sticky_reused},r&&n.estimated_tokens_saved>0){let t=d(e.model);ie(n.estimated_tokens_saved/1e6*t*.1,n.estimated_tokens_saved)}t.selectedReq!==null&&t.reqs[t.selectedReq]?.turn===n.turn&&M(),x(t)}}n.type===`router_mode_changed`&&(t.routerMode=n.mode,E()),n.type===`profiles_updated`&&(t.profiles=n.profiles||{},t.activeProfile=n.active||`All Tools`,F()),n.type===`active_profile_changed`&&(t.activeProfile=n.active||`All Tools`,F())}}async function V(e){let r=t.reqs[t.selectedReq];if(!r)return;let i=r.segments[e];if(!i)return;n.segment=i,n.segIndex=e,n.view=i.type===`tools`?`tools`:`formatted`,n.fullContent=``,n.loading=!0,n.parsedTools=null;let a=s(i);document.getElementById(`modalColorBar`).style.background=a,document.getElementById(`modalTitle`).textContent=i.name,document.getElementById(`modalMeta`).textContent=`${c(i)}${i.role?` (`+i.role+`)`:``}${i.count?` — `+i.count+` tools`:``}`;let o=`
|
|
15
|
-
<div class="modal-stat"><div class="modal-stat-value" style="color:${a}">${l(i.tokens)}</div><div class="modal-stat-label">Tokens</div></div>
|
|
16
|
-
<div class="modal-stat"><div class="modal-stat-value">${(i.charLength||0).toLocaleString()}</div><div class="modal-stat-label">Chars</div></div>
|
|
17
|
-
<div class="modal-stat"><div class="modal-stat-value">${(i.tokens/r.totalEstimatedTokens*100).toFixed(1)}%</div><div class="modal-stat-label">Of total</div></div>
|
|
18
|
-
`;document.getElementById(`modalStats`).innerHTML=o;let u=document.getElementById(`toolsViewBtn`);u.style.display=i.type===`tools`?`inline`:`none`,document.querySelectorAll(`.modal-toolbar button`).forEach(e=>e.classList.remove(`active`)),i.type===`tools`?u.classList.add(`active`):document.getElementById(`formattedBtn`).classList.add(`active`),document.getElementById(`modalSearch`).value=``,document.getElementById(`modalBody`).innerHTML=`<div style="display:flex;align-items:center;justify-content:center;height:200px;color:var(--text3);font-size:13px"><div style="text-align:center"><div style="font-size:24px;margin-bottom:8px;animation:pulse 1s ease-in-out infinite">⏳</div>Loading full content...</div></div>`,document.getElementById(`modalOverlay`).classList.add(`open`);try{let t=await le(r.turn,e);if(t.content){n.fullContent=t.content;let e=document.querySelector(`.modal-stat:nth-child(2) .modal-stat-value`);if(e&&(e.textContent=t.content.length.toLocaleString()),i.type===`tools`)try{n.parsedTools=JSON.parse(t.content)}catch{n.parsedTools=null}}else n.fullContent=i.preview||`(content no longer available — request may have been evicted)`}catch(e){console.error(`Failed to fetch segment content:`,e),n.fullContent=i.preview||`(failed to load content)`}n.loading=!1,W()}function H(){document.getElementById(`modalOverlay`).classList.remove(`open`),n.segment=null,n.parsedTools=null}function U(e,t){n.view=e,document.querySelectorAll(`.modal-toolbar button`).forEach(e=>e.classList.remove(`active`)),t&&t.classList.add(`active`),W()}function W(){let e=document.getElementById(`modalBody`),t=n.fullContent;if(n.loading)return;if(n.view===`tools`&&n.parsedTools){de(e);return}if(n.view===`raw`){e.innerHTML=`<div class="modal-content" style="white-space:pre-wrap;word-break:break-all">${f(t)}</div>`;return}let r=t;try{let e=JSON.parse(t);r=JSON.stringify(e,null,2)}catch{}let i=r.split(`
|
|
19
|
-
`),a=`<div class="modal-content">`;for(let e=0;e<i.length;e++)a+=`<span class="line"><span class="line-num">${e+1}</span>${f(i[e])}</span>`;a+=`</div>`,e.innerHTML=a}function de(e){let r=n.parsedTools;if(!r||!r.length){e.innerHTML=`<div style="padding:20px;color:var(--text3)">No tools found</div>`;return}let i=t.profiles[t.activeProfile],a=t.activeProfile===`All Tools`,s=h(r),c=`<div class="modal-tools">`,u=r.filter(e=>p(e.name,i,a)).length;c+=`<div class="modal-tools-header">`,c+=`<div class="modal-tools-header-left"><strong>${u}</strong> of <strong>${r.length}</strong> tools enabled</div>`,c+=`<div style="display:flex;gap:6px">`,c+=`<button class="btn-secondary" onclick="toggleAllTools(true)" style="padding:3px 8px;border-radius:4px;font-size:10px">All</button>`,c+=`<button class="btn-secondary" onclick="toggleAllTools(false)" style="padding:3px 8px;border-radius:4px;font-size:10px">None</button>`,c+=`</div></div>`;let d=[],m=[];for(let[e,t]of s)e===`other`?m.push([e,t]):d.push([e,t]);function g(e){for(let[n,r]of e){let e=[...r].sort((e,t)=>(0,o.estimateToolTokens)(t)-(0,o.estimateToolTokens)(e)),s=n===`other`?`Other`:n.charAt(0).toUpperCase()+n.slice(1),u=e.reduce((e,t)=>e+(0,o.estimateToolTokens)(t),0),d=e.filter(e=>p(e.name,i,a)).length,m=d===e.length;c+=`<div class="tool-group" data-server="${f(n)}">`,c+=`<div class="tool-group-header">`,c+=`<input type="checkbox" class="tool-group-checkbox" data-server="${f(n)}" ${m?`checked`:``} onclick="event.stopPropagation(); toggleGroupCheckbox('${f(n)}', this.checked)">`,c+=`<span class="tool-group-chevron" onclick="toggleGroupAccordion('${f(n)}')">▶</span>`,c+=`<div class="tool-group-title" onclick="toggleGroupAccordion('${f(n)}')">`,c+=`<span class="tool-group-name">${f(s)}</span>`,c+=`<span class="tool-group-meta">${d}/${e.length} tools · ~${l(u)} tok</span>`,c+=`</div>`,c+=`<div class="tool-group-actions">`,c+=`<button class="btn-secondary" onclick="event.stopPropagation(); toggleGroupTools('${f(n)}', true)" style="padding:2px 6px;font-size:9px">All</button>`,c+=`<button class="btn-secondary" onclick="event.stopPropagation(); toggleGroupTools('${f(n)}', false)" style="padding:2px 6px;font-size:9px">None</button>`,c+=`</div></div>`,c+=`<div class="tool-group-body collapsed">`;for(let r of e){let e=p(r.name,i,a),s=(0,o.estimateToolTokens)(r),u=(r.description||``).slice(0,120),d=!t.toolsUsed?.has(r.name);c+=`<div class="tool-card" data-tool-name="${f(r.name)}">`,c+=`<input type="checkbox" data-tool="${f(r.name)}" data-server="${f(n)}" ${e?`checked`:``} onchange="onToolToggle()">`,c+=`<div class="tool-card-info">`,c+=`<div class="tool-card-name">${f(r.name)}${d?` <span class="never-used-tag" title="Not used this session">never used</span>`:``}</div>`,u&&(c+=`<div class="tool-card-desc">${f(u)}</div>`),c+=`</div>`,c+=`<div class="tool-card-tokens">~${l(s)} tok</div>`,c+=`</div>`}c+=`</div></div>`}}d.length>0&&(c+=`<div class="tool-section-header">MCP Servers</div>`,g(d)),m.length>0&&(c+=`<div class="tool-section-header">Other Tools</div>`,g(m)),c+=`<div id="toolsSavings"></div>`;let _=t.reqs[t.selectedReq]?.toolsUsed||[];c+=`<div class="save-profile-bar">`,c+=`<input type="text" id="profileNameInput" placeholder="Profile name..." value="">`,c+=`<button class="btn-primary" onclick="saveCurrentAsProfile()">Save as Profile</button>`,_.length>0&&(c+=`<button class="btn-secondary" onclick="createProfileFromThisTurn()" title="Create profile from tools used in this request">From this turn</button>`),c+=`</div>`,c+=`</div>`,e.innerHTML=c,G()}function fe(e){let t=document.querySelector(`.tool-group[data-server="${e}"]`);if(!t)return;let n=t.querySelector(`.tool-group-body`),r=t.querySelector(`.tool-group-chevron`);n&&n.classList.toggle(`collapsed`),r&&r.classList.toggle(`expanded`)}function pe(e,t){document.querySelectorAll(`.modal-tools input[type="checkbox"][data-server="${e}"]:not(.tool-group-checkbox)`).forEach(e=>{e.checked=t}),G()}function me(){document.querySelectorAll(`.tool-group-checkbox`).forEach(e=>{let t=e.dataset.server,n=document.querySelectorAll(`.modal-tools input[type="checkbox"][data-server="${t}"]:not(.tool-group-checkbox)`),r=n.length,i=0;n.forEach(e=>{e.checked&&i++}),e.checked=i===r,e.indeterminate=i>0&&i<r;let a=e.closest(`.tool-group`)?.querySelector(`.tool-group-meta`);if(a){let e=a.textContent.match(/·.*$/);a.textContent=`${i}/${r} tools ${e?e[0]:``}`}})}function he(e,t){document.querySelectorAll(`.modal-tools input[data-server="${e}"]:not(.tool-group-checkbox)`).forEach(e=>{e.checked=t}),G()}function ge(e){document.querySelectorAll(`.modal-tools input[type="checkbox"]:not(.tool-group-checkbox)`).forEach(t=>{t.checked=e}),G()}function G(){let e=document.querySelectorAll(`.modal-tools input[type="checkbox"]:not(.tool-group-checkbox)`),t=n.parsedTools;if(!t||!e.length)return;let r=0,i=0,a=0;e.forEach(e=>{let n=t.find(t=>t.name===e.dataset.tool);if(!n)return;let s=(0,o.estimateToolTokens)(n);e.checked?(r+=s,a++):i+=s}),me();let s=document.getElementById(`toolsSavings`);s&&i>0?s.innerHTML=`<div class="savings-banner">Disabling ${t.length-a} tools saves ~${l(i)} tokens per request</div>`:s&&(s.innerHTML=``);let c=document.querySelector(`.modal-tools-header-left`);c&&(c.innerHTML=`<strong>${a}</strong> of <strong>${t.length}</strong> tools enabled`)}async function _e(){let e=t.reqs[t.selectedReq],n=e?.toolsUsed||[];if(n.length!==0&&(await P(`Req ${e?.turn??0} tools`,`allowlist`,n)).success){let e=document.querySelector(`.save-profile-bar`);if(e){let t=e.style.background;e.style.background=`rgba(52,211,153,0.1)`,e.style.borderColor=`rgba(52,211,153,0.3)`,setTimeout(()=>{e.style.background=t,e.style.borderColor=``},1500)}}}async function ve(){let e=document.getElementById(`profileNameInput`),t=(e?.value||``).trim();if(!t){e.style.borderColor=`var(--red)`,e.focus(),setTimeout(()=>e.style.borderColor=``,1500);return}let n=document.querySelectorAll(`.modal-tools input[type="checkbox"]:not(.tool-group-checkbox)`),r=[];if(n.forEach(e=>{e.checked&&r.push(e.dataset.tool)}),(await P(t,`allowlist`,r)).success){e.value=``;let t=document.querySelector(`.save-profile-bar`);if(t){let e=t.style.background;t.style.background=`rgba(16,185,129,0.1)`,t.style.borderColor=`rgba(16,185,129,0.3)`,setTimeout(()=>{t.style.background=e,t.style.borderColor=``},1500)}}}function K(){let e=document.getElementById(`modalSearch`).value.trim().toLowerCase(),t=document.getElementById(`modalBody`);if(!e){W();return}if(n.view===`tools`){document.querySelectorAll(`.tool-group`).forEach(t=>{let n=0;t.querySelectorAll(`.tool-card`).forEach(t=>{let r=t.querySelector(`.tool-card-name`)?.textContent?.toLowerCase()||``,i=t.querySelector(`.tool-card-desc`)?.textContent?.toLowerCase()||``,a=r.includes(e)||i.includes(e);t.style.display=a?`flex`:`none`,a&&n++}),t.style.display=n>0?`block`:`none`;let r=t.querySelector(`.tool-group-body`),i=t.querySelector(`.tool-group-chevron`);n>0?(r?.classList.remove(`collapsed`),i?.classList.add(`expanded`)):(r?.classList.add(`collapsed`),i?.classList.remove(`expanded`))});return}let r=n.fullContent;try{r=JSON.stringify(JSON.parse(r),null,2)}catch{}let i=r.split(`
|
|
20
|
-
`),a=`<div class="modal-content">`;for(let t=0;t<i.length;t++){let n=i[t],r=n.toLowerCase().indexOf(e);if(r!==-1){let i=f(n.slice(0,r)),o=f(n.slice(r,r+e.length)),s=f(n.slice(r+e.length));a+=`<span class="line"><span class="line-num">${t+1}</span>${i}<span class="highlight">${o}</span>${s}</span>`}else a+=`<span class="line" style="opacity:0.25"><span class="line-num">${t+1}</span>${f(n)}</span>`}a+=`</div>`,t.innerHTML=a}function q(){navigator.clipboard.writeText(n.fullContent).then(()=>{let e=document.getElementById(`copyBtn`),t=e.textContent;e.textContent=`Copied!`,e.style.color=`var(--green)`,setTimeout(()=>{e.textContent=t,e.style.color=``},1500)})}var J=`jannal_theme`;function Y(){return localStorage.getItem(J)||`dark`}function X(e){e===`light`?document.documentElement.setAttribute(`data-theme`,`light`):document.documentElement.removeAttribute(`data-theme`);let t=document.getElementById(`themeIconMoon`),n=document.getElementById(`themeIconSun`);t&&n&&(t.style.display=e===`dark`?`block`:`none`,n.style.display=e===`light`?`block`:`none`)}function ye(){let e=Y()===`dark`?`light`:`dark`;return localStorage.setItem(J,e),document.documentElement.classList.add(`theme-transitioning`),X(e),setTimeout(()=>document.documentElement.classList.remove(`theme-transitioning`),350),e}function be(){X(Y())}function Z(e){t.selectedReq=e,D(),k(),M(),x(t)}function Q(){t.reqs=[],t.selectedReq=null,t.groups={},t.expandedGroups={},w(),x(t)}function xe(){t.groupView=!t.groupView,k(),x(t)}function Se(e){t.expandedGroups[e]=!t.expandedGroups[e],k()}function Ce(){t.reqs.length!==0&&document.getElementById(`exportMenu`)?.classList.toggle(`open`)}function we(){t.reqs.length!==0&&(C(te(t),`jannal-session-${new Date().toISOString().slice(0,19).replace(/[:-]/g,``)}.json`),document.getElementById(`exportMenu`)?.classList.remove(`open`))}function Te(){t.reqs.length!==0&&(C(S(t),`jannal-session-${new Date().toISOString().slice(0,19).replace(/[:-]/g,``)}.csv`),document.getElementById(`exportMenu`)?.classList.remove(`open`))}window.openModal=V,window.closeModal=H,window.setModalView=U,window.selectReq=Z,window.clearReqs=Q,window.toggleGroup=Se,window.onProfileChange=N,window.toggleAllTools=ge,window.toggleGroupTools=he,window.toggleGroupAccordion=fe,window.toggleGroupCheckbox=pe,window.onToolToggle=G,window.saveCurrentAsProfile=ve,window.createProfileFromThisTurn=_e,window.copyClaudeCommand=se,window.filterModalContent=K,window.copyModalContent=q;var $=null;function Ee(e){let n=document.getElementById(`globalSearchResults`);if(!e||e.length<2){n.classList.remove(`open`),n.innerHTML=``;return}clearTimeout($),$=setTimeout(async()=>{try{let r=await(await fetch(`/api/search?q=${encodeURIComponent(e)}`)).json();if(r.results.length===0){n.innerHTML=`<div class="search-no-results">No matches found</div>`,n.classList.add(`open`);return}let i=e.toLowerCase();n.innerHTML=r.results.map(e=>{let n=t.reqs.findIndex(t=>t.turn===e.turnId),r=n>=0?`Req ${t.reqs[n].turn}`:`Req ${e.turnId}`,a=e.snippet.replace(/</g,`<`).replace(/>/g,`>`).replace(RegExp(`(${i.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)})`,`gi`),`<mark>$1</mark>`);return`<div class="search-result-item" data-turn="${n}" data-seg="${e.segIndex}">
|
|
21
|
-
<div class="search-result-turn">${r} · segment ${e.segIndex}</div>
|
|
22
|
-
<div class="search-result-snippet">${a}</div>
|
|
23
|
-
</div>`}).join(``),n.classList.add(`open`)}catch(e){console.error(`Search error:`,e)}},250)}document.getElementById(`themeToggle`).addEventListener(`click`,()=>{ye(),w()}),document.getElementById(`profileSelect`).addEventListener(`change`,e=>{N(e.target.value)}),document.getElementById(`clearBtn`).addEventListener(`click`,Q),document.getElementById(`viewToggleBtn`).addEventListener(`click`,xe),document.getElementById(`exportBtn`).addEventListener(`click`,Ce),document.getElementById(`exportMenu`)?.addEventListener(`click`,e=>{let t=e.target.closest(`.export-option`);t&&(t.dataset.format===`json`?we():t.dataset.format===`csv`&&Te())}),document.addEventListener(`click`,e=>{let t=document.getElementById(`exportMenu`),n=document.querySelector(`.export-dropdown`);t?.classList.contains(`open`)&&n&&!n.contains(e.target)&&t.classList.remove(`open`);let r=document.querySelector(`.router-badge-wrapper`),i=document.getElementById(`routerPopover`);i?.classList.contains(`open`)&&r&&!r.contains(e.target)&&i.classList.remove(`open`)}),document.getElementById(`routerBadge`)?.addEventListener(`click`,e=>{e.stopPropagation(),document.getElementById(`routerPopover`)?.classList.toggle(`open`)}),document.getElementById(`routerPopover`)?.addEventListener(`click`,async e=>{let n=e.target.closest(`.router-popover-opt`);if(!n||!t.premium)return;let r=n.dataset.mode;if(r===t.routerMode){document.getElementById(`routerPopover`)?.classList.remove(`open`);return}try{(await fetch(`/api/router/mode`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({mode:r})})).ok&&(t.routerMode=r,E())}catch(e){console.error(`Failed to set router mode:`,e)}document.getElementById(`routerPopover`)?.classList.remove(`open`)}),document.getElementById(`modalOverlay`).addEventListener(`click`,e=>{e.target===e.currentTarget&&H()}),document.getElementById(`modalCloseBtn`).addEventListener(`click`,H),document.getElementById(`formattedBtn`).addEventListener(`click`,e=>{U(`formatted`,e.target)}),document.getElementById(`rawBtn`).addEventListener(`click`,e=>{U(`raw`,e.target)}),document.getElementById(`toolsViewBtn`).addEventListener(`click`,e=>{U(`tools`,e.target)}),document.getElementById(`modalSearch`).addEventListener(`input`,K),document.getElementById(`copyBtn`).addEventListener(`click`,q),document.getElementById(`globalSearch`).addEventListener(`input`,e=>{Ee(e.target.value.trim())}),document.getElementById(`globalSearchResults`).addEventListener(`click`,e=>{let t=e.target.closest(`.search-result-item`);if(!t)return;let n=parseInt(t.dataset.turn),r=parseInt(t.dataset.seg);n>=0&&(Z(n),V(r)),document.getElementById(`globalSearchResults`).classList.remove(`open`),document.getElementById(`globalSearch`).value=``}),document.addEventListener(`click`,e=>{let t=document.querySelector(`.global-search-wrapper`);t&&!t.contains(e.target)&&document.getElementById(`globalSearchResults`).classList.remove(`open`)}),document.addEventListener(`keydown`,e=>{e.key===`Escape`&&(document.getElementById(`globalSearchResults`).classList.remove(`open`),H())}),be(),ee(t),L(),B(),w();
|