@kylindc/ccxray 1.2.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.
@@ -0,0 +1,67 @@
1
+ // ── Topbar Quota Ticker ───────────────────────────────────────────────
2
+ let quotaTickerInterval = null;
3
+
4
+ async function updateQuotaTicker() {
5
+ try {
6
+ const [block, monthly] = await Promise.all([
7
+ fetch('/_api/costs/current-block').then(r => r.json()).catch(() => ({ active: false })),
8
+ fetch('/_api/costs/monthly').then(r => r.json()).catch(() => ({ currentMonth: { costUSD: 0 } })),
9
+ ]);
10
+
11
+ // Progress bar
12
+ const barWrap = document.getElementById('qt-bar-wrap');
13
+ const barFill = document.getElementById('qt-bar-fill');
14
+ const barPct = document.getElementById('qt-bar-pct');
15
+ const barTime = document.getElementById('qt-bar-time');
16
+ if (block.active) {
17
+ const pct = block.percentUsed || 0;
18
+ const timePct = block.timePct || 0;
19
+ const color = pct < 60 ? 'var(--green)' : pct < 85 ? 'var(--yellow)' : 'var(--red)';
20
+ barFill.style.width = Math.min(pct, 100) + '%';
21
+ barFill.style.background = color;
22
+ const minR = block.minutesRemaining || 0;
23
+ const timeStr = minR > 60 ? Math.floor(minR/60) + 'h' + (minR%60) + 'm' : minR + 'min';
24
+ // Show pace warning if token% significantly exceeds time%
25
+ const paceRatio = timePct > 0 ? pct / timePct : 0;
26
+ const paceWarn = paceRatio > 1.3 ? ' ⚡' : '';
27
+ barPct.textContent = pct.toFixed(1) + '%' + paceWarn;
28
+ barTime.textContent = timeStr + ' left';
29
+ barWrap.style.display = 'flex';
30
+ } else {
31
+ barWrap.style.display = 'none';
32
+ }
33
+
34
+ // ROI badge
35
+ const roiEl = document.getElementById('qt-roi');
36
+ const currentCost = monthly.currentMonth?.costUSD || 0;
37
+ const roi = (currentCost / 200).toFixed(1);
38
+ roiEl.textContent = 'ROI ' + roi + 'x';
39
+ roiEl.style.display = 'inline';
40
+
41
+ // Recommendation chip
42
+ const chipEl = document.getElementById('qt-chip');
43
+ if (block.active && block.burnRate) {
44
+ const br = block.burnRate.tokensPerMinute || 0;
45
+ const capacity = 220000 / 300; // tokens per min at full speed
46
+ const ratio = br / capacity;
47
+ if (ratio < 0.3) {
48
+ chipEl.textContent = '💡 Can parallelize';
49
+ chipEl.style.display = 'inline';
50
+ } else if (block.projection && block.projection.totalTokens > 220000 * 0.9) {
51
+ chipEl.textContent = '⚠️ Slow down';
52
+ chipEl.style.display = 'inline';
53
+ } else {
54
+ chipEl.style.display = 'none';
55
+ }
56
+ } else {
57
+ chipEl.style.display = 'none';
58
+ }
59
+ } catch (e) {
60
+ // silent fail — ticker is ambient, not critical
61
+ }
62
+ }
63
+
64
+ function startQuotaTicker() {
65
+ updateQuotaTicker();
66
+ quotaTickerInterval = setInterval(updateQuotaTicker, 30000);
67
+ }
@@ -0,0 +1,431 @@
1
+ :root {
2
+ --bg: #0d1117; --surface: #161b22; --surface-hover: #1c2129; --surface-active: #1a2535; --border: #30363d;
3
+ --text: #e6edf3; --dim: #8b949e; --accent: #58a6ff;
4
+ --green: #3fb950; --yellow: #d29922; --red: #f85149;
5
+ --purple: #bc8cff; --orange: #f0883e;
6
+ /* Heatmap */
7
+ --heatmap-empty: #161b22;
8
+ --heatmap-0: #2a1508; --heatmap-1: #4d2510; --heatmap-2: #7a3a18;
9
+ --heatmap-3: #b05628; --heatmap-4: #d4784a; --heatmap-5: #f0a070;
10
+ /* Context bar segments */
11
+ --color-cache-read: #4dd0e1; --color-cache-write: #ff8a65; --color-input: #ce93d8;
12
+ /* Section categories */
13
+ --color-system: #1f6feb; --color-system-deep: #1158c7; --color-system-mid: #388bfd;
14
+ --color-system-light: #58a6ff; --color-system-pale: #79c0ff; --color-system-muted: #3d5a8a;
15
+ --color-system-soft: #5d80c8; --color-system-faint: #a5c8ff;
16
+ --color-tools: #2ea043; --color-mcp-tools: #3fb950; --color-messages: #e3b341;
17
+ /* Content types */
18
+ --color-thinking: #ce93d8; --color-tool-use: #ff8a65; --color-tool-result: #4dd0e1;
19
+ /* Diff */
20
+ --color-diff-add: #56d364; --color-diff-del: #f85149;
21
+ }
22
+ [data-theme="light"] {
23
+ --bg: #ffffff; --surface: #f6f8fa; --surface-hover: #eaeef2; --surface-active: #ddf4ff; --border: #d0d7de;
24
+ --text: #1f2328; --dim: #656d76; --accent: #0969da;
25
+ --green: #1a7f37; --yellow: #9a6700; --red: #d1242f;
26
+ --purple: #8250df; --orange: #bc4c00;
27
+ --heatmap-empty: #ebedf0;
28
+ --heatmap-0: #9be9a8; --heatmap-1: #6fdc8c; --heatmap-2: #40c463;
29
+ --heatmap-3: #30a14e; --heatmap-4: #216e39; --heatmap-5: #0e4429;
30
+ --color-cache-read: #0891b2; --color-cache-write: #c2410c; --color-input: #9333ea;
31
+ --color-system: #0969da; --color-system-deep: #0550ae; --color-system-mid: #0969da;
32
+ --color-system-light: #218bff; --color-system-pale: #54aeff; --color-system-muted: #3d5a8a;
33
+ --color-system-soft: #3b82f6; --color-system-faint: #85b8ff;
34
+ --color-tools: #1a7f37; --color-mcp-tools: #2da44e; --color-messages: #9a6700;
35
+ --color-thinking: #8250df; --color-tool-use: #bc4c00; --color-tool-result: #0891b2;
36
+ --color-diff-add: #1a7f37; --color-diff-del: #d1242f;
37
+ }
38
+ * { margin: 0; padding: 0; box-sizing: border-box; }
39
+ body {
40
+ font-family: 'SF Mono', 'Cascadia Code', 'Fira Code', monospace;
41
+ font-size: 13px; background: var(--bg); color: var(--text);
42
+ height: 100vh; display: flex; flex-direction: column; overflow: hidden;
43
+ }
44
+
45
+ /* ── Miller Columns Layout ── */
46
+ #app { display: flex; flex-direction: column; height: 100vh; overflow: hidden; }
47
+ #topbar { border-bottom: 1px solid var(--border); flex-shrink: 0; }
48
+ #topbar-row1 { display: flex; align-items: center; gap: 12px; padding: 6px 16px; }
49
+ #topbar-row1 h1 { font-size: 15px; color: var(--accent); display: flex; align-items: center; gap: 8px; white-space: nowrap; margin: 0; }
50
+ h1 .dot { width: 8px; height: 8px; background: var(--green); border-radius: 50%; animation: pulse 2s infinite; }
51
+ @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } }
52
+ #topbar-tabs { display: flex; gap: 2px; margin-left: 4px; }
53
+ .topbar-tab { background: none; border: none; border-bottom: 2px solid transparent; color: var(--dim); padding: 4px 10px; font-size: 12px; font-family: inherit; cursor: pointer; white-space: nowrap; display: flex; align-items: center; transition: color 0.15s, border-color 0.15s; outline: none; }
54
+ .topbar-tab:hover { color: var(--text); }
55
+ .topbar-tab.active { color: var(--accent); border-bottom-color: var(--accent); }
56
+ #status-cluster { margin-left: auto; display: flex; align-items: center; gap: 8px; font-size: 11px; color: var(--dim); flex-shrink: 0; }
57
+ #topbar-row1 #theme-toggle { background: none; border: 1px solid var(--border); color: var(--dim); padding: 3px 8px; font-size: 11px; border-radius: 3px; cursor: pointer; flex-shrink: 0; }
58
+ #topbar-row1 #theme-toggle:hover { background: var(--surface-hover); }
59
+ #topbar-row2 { display: flex; align-items: center; padding: 3px 16px 5px; font-size: 12px; border-top: 1px solid var(--border); min-height: 26px; }
60
+ #row2-dashboard, #row2-usage, #row2-sysprompt { display: flex; align-items: center; gap: 8px; width: 100%; }
61
+ #breadcrumb { color: var(--dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 120px; flex: 1; }
62
+ #breadcrumb .bc-seg { cursor: pointer; transition: color 0.1s; }
63
+ #breadcrumb .bc-seg:hover { color: var(--accent); }
64
+ #breadcrumb .bc-sep { color: var(--border); margin: 0 2px; cursor: default; }
65
+ #copy-link-btn { background: none; border: none; color: var(--dim); cursor: pointer; font-size: 12px; padding: 2px 4px; flex-shrink: 0; }
66
+ #copy-link-btn:hover { color: var(--text); }
67
+ #quota-ticker { display: flex; align-items: center; gap: 8px; margin-left: auto; flex-shrink: 0; }
68
+ #qt-bar-wrap { display: flex; align-items: center; gap: 5px; }
69
+ .qt-bar-track { width: 80px; height: 7px; background: var(--border); border-radius: 4px; overflow: hidden; }
70
+ #qt-bar-fill { height: 100%; width: 0%; background: var(--green); border-radius: 4px; transition: width 0.5s, background 0.5s; }
71
+ #qt-bar-pct, #qt-bar-time { font-size: 10px; color: var(--dim); }
72
+ #qt-roi { font-size: 10px; color: var(--green); cursor: pointer; padding: 2px 6px; border: 1px solid var(--green); border-radius: 10px; }
73
+ #qt-roi:hover { background: rgba(63,185,80,0.1); }
74
+ #qt-chip { font-size: 10px; color: var(--dim); }
75
+ #row2-usage span, #row2-sysprompt span { font-size: 11px; color: var(--dim); }
76
+
77
+ #columns { display: flex; flex: 1; overflow: hidden; }
78
+ .col { overflow-y: auto; overflow-x: hidden; border-right: 1px solid var(--border); flex-shrink: 0; min-width: 0; border-top: 2px solid transparent; transition: border-top-color 0.15s; scrollbar-width: thin; scrollbar-color: var(--border) transparent; }
79
+ .col::-webkit-scrollbar { width: 4px; }
80
+ .col::-webkit-scrollbar-track { background: transparent; }
81
+ .col::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
82
+ .col::-webkit-scrollbar-thumb:hover { background: var(--dim); }
83
+ .col.col-focused { border-top-color: var(--accent); }
84
+ .col:last-child { border-right: none; flex: 1; }
85
+
86
+ /* Focused mode: collapse all columns except detail */
87
+ #columns.focused #col-projects,
88
+ #columns.focused #col-sessions,
89
+ #columns.focused #col-turns,
90
+ #columns.focused #col-sections { display: none; }
91
+ #columns.focused #col-detail { flex: 1; }
92
+
93
+ /* Expand button in detail header */
94
+ .expand-btn { cursor: pointer; color: var(--dim); font-size: 14px; padding: 2px 4px; border-radius: 3px; transition: color 0.1s, background 0.1s; }
95
+ .expand-btn:hover { color: var(--text); background: var(--surface-hover); }
96
+
97
+ /* Timeline accordion in detail */
98
+ .tl-step { border-bottom: none; }
99
+ .tl-step-summary { cursor: pointer; padding: 4px 8px; transition: background 0.1s; }
100
+ .tl-step-summary:hover { background: var(--surface-hover); }
101
+ .tl-step-summary.active { background: var(--surface-active); box-shadow: inset 3px 0 0 0 var(--accent); }
102
+ .tool-call-error { background: rgba(248,81,73,0.12); border-left: 2px solid var(--red); }
103
+ .tl-step-detail { padding: 8px 12px; border-top: 1px solid var(--border); background: var(--bg); }
104
+
105
+ /* Timeline split pane in focused mode */
106
+ .tl-split { display: flex; flex: 1; overflow: hidden; }
107
+ .tl-split-list { width: 280px; min-width: 200px; max-width: 400px; flex-shrink: 0; overflow-y: auto; border-right: 1px solid var(--border); scrollbar-width: thin; scrollbar-color: var(--border) transparent; }
108
+ .tl-split-detail { flex: 1; overflow-y: auto; scrollbar-width: thin; scrollbar-color: var(--border) transparent; }
109
+
110
+ /* Token minimap — vertical color strip beside timeline */
111
+ .tl-with-minimap { display: flex; flex: 1; overflow: hidden; }
112
+ .tl-scroll-area { flex: 1; overflow-y: auto; scrollbar-width: thin; scrollbar-color: var(--border) transparent; scroll-padding-block: 40px; }
113
+ .minimap { width: 36px; flex-shrink: 0; position: relative; border-right: 1px solid var(--border); background: var(--bg); overflow: hidden; cursor: pointer; }
114
+ .minimap-cache-bar { display: flex; height: 4px; width: 100%; flex-shrink: 0; }
115
+ .minimap-cache-bar > div { min-width: 1px; }
116
+ .minimap-blocks { width: 100%; opacity: 0.5; transition: opacity 0.25s ease; }
117
+ .minimap:hover .minimap-blocks { opacity: 1; }
118
+ .minimap-block { width: 100%; transition: opacity 0.15s; position: relative; }
119
+ .minimap-block.mm-active { opacity: 1 !important; filter: brightness(1.8); outline: 1px solid var(--text); z-index: 1; }
120
+ .minimap-block.mm-highlight { filter: brightness(2); z-index: 1; box-shadow: 0 0 0 1px var(--text); }
121
+ .minimap-empty { width: 100%; }
122
+ .minimap-usage { position: absolute; bottom: 2px; left: 0; right: 0; text-align: center; font-size: 9px; color: var(--dim); opacity: 0; transition: opacity 0.25s; pointer-events: none; }
123
+ .minimap:hover .minimap-usage { opacity: 1; }
124
+ .tl-step-summary.mm-hover { background: var(--surface-hover); outline: 1px solid var(--border); outline-offset: -1px; }
125
+ [data-theme="light"] .minimap-cache-bar > div { opacity: 0.8; }
126
+ #col-projects { width: 220px; }
127
+ #col-sessions { width: 220px; }
128
+
129
+ .project-item { padding: 7px 10px; cursor: pointer; border-left: 3px solid transparent; border-bottom: 1px solid var(--border); }
130
+ .project-item:hover { background: var(--surface-hover); }
131
+ .project-item.selected { border-left-color: var(--accent); background: var(--surface-active); }
132
+ .project-item .pi-name { font-size: 12px; color: var(--text); font-weight: bold; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
133
+ .project-item .pi-name.all { color: var(--dim); font-weight: normal; }
134
+ .project-item .pi-meta { font-size: 11px; color: var(--dim); margin-top: 2px; }
135
+ .project-item .pi-cost { color: var(--yellow); }
136
+ .project-item .pi-range { font-size: 10px; color: var(--dim); margin-top: 1px; }
137
+ #col-turns { width: 220px; position: relative; }
138
+ #col-sections { width: 220px; }
139
+
140
+ .col-empty { color: var(--dim); text-align: center; padding: 40px 12px; font-size: 12px; }
141
+ .col-title { padding: 10px 12px; font-size: 10px; font-weight: 600; color: var(--dim); letter-spacing: 0.08em; text-transform: uppercase; border-bottom: 2px solid var(--border); position: sticky; top: 0; background: var(--surface); z-index: 1; }
142
+
143
+ /* ── Status dots ── */
144
+ .sdot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-right: 5px; flex-shrink: 0; vertical-align: middle; border: none; background: none; padding: 0; outline: none; cursor: default; }
145
+ .sdot-stream { background: var(--green); }
146
+ .sdot-idle { background: var(--yellow); }
147
+ .sdot-off { background: transparent; border: 1px solid var(--border); }
148
+ .sdot-armed { box-shadow: 0 0 0 3px rgba(210, 153, 34, 0.5); cursor: pointer; }
149
+ .sdot-armed:hover { box-shadow: 0 0 0 3px rgba(210, 153, 34, 0.9); }
150
+ .sdot-stream:not(.sdot-armed):hover, .sdot-idle:not(.sdot-armed):hover { box-shadow: 0 0 0 3px rgba(210, 153, 34, 0.25); cursor: pointer; }
151
+
152
+ /* ── Session items ── */
153
+ .session-item { padding: 8px 10px; cursor: pointer; border-left: 3px solid transparent; border-bottom: 1px solid var(--border); }
154
+ .session-item:hover { background: var(--surface-hover); }
155
+ .session-item.selected { border-left-color: var(--purple); background: var(--surface-active); }
156
+ .session-item .si-row1 { display: flex; justify-content: space-between; align-items: center; margin-bottom: 3px; }
157
+ .session-item .sid { color: var(--purple); font-size: 12px; font-weight: bold; }
158
+ .session-item .si-row2 { font-size: 11px; color: var(--text); margin-bottom: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
159
+ .session-item .si-row3 { display: flex; justify-content: space-between; font-size: 11px; color: var(--dim); }
160
+ .session-item .si-cost { color: var(--yellow); }
161
+ .ctx-alert { font-size: 10px; font-weight: bold; }
162
+ .ctx-alert-yellow { color: var(--yellow); }
163
+ .ctx-alert-red { color: var(--red); }
164
+ .launch-btn { background: none; border: 1px solid var(--border); color: var(--green); width: 18px; height: 18px; border-radius: 50%; cursor: pointer; font-size: 10px; display: inline-flex; align-items: center; justify-content: center; padding: 0; flex-shrink: 0; }
165
+ .launch-btn:hover { background: var(--green); color: #000; border-color: var(--green); }
166
+ .pin-btn { background: none; border: none; color: var(--dim); cursor: pointer; font-size: 11px; padding: 0 2px; opacity: 0.3; transition: opacity 0.15s, color 0.15s; margin-left: auto; flex-shrink: 0; }
167
+ .pin-btn:hover { opacity: 1; color: var(--yellow); }
168
+ .pin-btn.pinned { opacity: 1; color: var(--yellow); }
169
+
170
+ /* ── Turn items ── */
171
+ .turn-item { padding: 7px 10px; cursor: pointer; border-left: 3px solid transparent; border-bottom: 1px solid var(--border); }
172
+ .turn-item:hover { background: var(--surface-hover); }
173
+ .turn-item.selected { border-left-color: var(--accent); background: var(--surface-hover); }
174
+ .turn-item .turn-line1 { display: flex; gap: 6px; align-items: center; font-size: 12px; }
175
+ .turn-item .turn-num { color: var(--dim); }
176
+ .turn-item .turn-model { color: var(--purple); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
177
+ .turn-item .turn-line2 { display: flex; gap: 6px; font-size: 11px; color: var(--dim); margin-top: 3px; flex-wrap: wrap; }
178
+ .turn-item .status-ok { color: var(--green); }
179
+ .turn-item .status-err { color: var(--red); }
180
+ .turn-item .turn-cost { color: var(--yellow); }
181
+ .turn-item .turn-line3 { display: flex; flex-wrap: wrap; gap: 3px; margin-top: 2px; }
182
+ .tool-chip { font-size: 9px; color: var(--dim); background: var(--bg2, var(--surface-hover)); border-radius: 3px; padding: 0 3px; }
183
+ .turn-item .turn-title { font-size: 11px; color: var(--text); margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
184
+ .compact-badge { font-size: 9px; color: var(--red); border: 1px solid var(--red); border-radius: 2px; padding: 0 3px; margin-left: 4px; opacity: 0.85; flex-shrink: 0; }
185
+ .turn-sub { opacity: 0.7; border-left-color: transparent !important; }
186
+ .turn-sub:hover { opacity: 1; }
187
+ .turn-sub.selected { opacity: 1; border-left-color: var(--orange) !important; }
188
+ .sub-indent { color: var(--dim); margin-right: 2px; user-select: none; }
189
+ .tool-chip.chip-agent { color: var(--orange); }
190
+ .si-tools { font-size: 10px; color: var(--dim); margin: 2px 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
191
+ .col-sticky-header { position: sticky; top: 0; z-index: 2; background: var(--surface); }
192
+ #scroll-toggle { user-select: none; }
193
+ #scroll-toggle .scroll-on, #scroll-toggle .scroll-off { padding: 1px 4px; border-radius: 3px; color: var(--dim); }
194
+ #scroll-toggle .scroll-on.active { color: var(--green); background: rgba(63,185,80,0.15); }
195
+ #scroll-toggle .scroll-off.active { color: var(--red); background: rgba(248,81,73,0.15); }
196
+ #session-tool-bar { padding: 4px 10px; font-size: 10px; color: var(--dim); border-bottom: 1px solid var(--border); display: flex; flex-wrap: nowrap; gap: 4px; align-items: center; overflow: hidden; white-space: nowrap; }
197
+ .turn-ctx-bar { margin-top: 3px; }
198
+ .turn-ctx-bar-bg { height: 3px; border-radius: 1.5px; background: var(--border); overflow: hidden; display: flex; }
199
+ .turn-ctx-pct { font-size: 9px; color: var(--dim); margin-top: 1px; text-align: right; line-height: 1; }
200
+ #ctx-legend { padding: 3px 10px 4px; border-bottom: 1px solid var(--border); display: flex; gap: 8px; align-items: center; font-size: 9px; color: var(--dim); }
201
+ .ctx-legend-dot { display: inline-block; width: 7px; height: 7px; border-radius: 1px; margin-right: 2px; vertical-align: middle; }
202
+ #session-sparkline { padding: 6px 10px; border-bottom: 1px solid var(--border); display: none; }
203
+ #session-sparkline svg { width: 100%; height: 56px; }
204
+
205
+ /* ── Scorecard hover card ── */
206
+ .scorecard-tooltip { position: absolute; right: 100%; top: 0; margin-right: 4px; background: var(--surface); border: 1px solid var(--border); border-radius: 6px; padding: 8px 10px; font-size: 10px; z-index: 10; width: 180px; box-shadow: 0 4px 12px rgba(0,0,0,0.4); pointer-events: none; }
207
+ .scorecard-tooltip .sc-row { display: flex; justify-content: space-between; margin-bottom: 3px; }
208
+ .scorecard-tooltip .sc-label { color: var(--dim); }
209
+ .scorecard-tooltip .sc-value { color: var(--text); font-weight: bold; }
210
+ .scorecard-tooltip .sc-bar { height: 4px; border-radius: 2px; background: var(--border); margin-bottom: 4px; }
211
+ .scorecard-tooltip .sc-bar-fill { height: 100%; border-radius: 2px; }
212
+
213
+ /* ── Sections column ── */
214
+ .col-header { padding: 10px 12px; border-bottom: 2px solid var(--border); background: var(--surface); position: sticky; top: 0; z-index: 1; }
215
+ .col-header .ch-line1 { font-size: 12px; margin-bottom: 4px; }
216
+ .col-header .ch-line2 { font-size: 11px; color: var(--dim); }
217
+ .section-item { padding: 8px 12px; cursor: pointer; border-left: 3px solid transparent; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 6px; }
218
+ .section-item:hover { background: var(--surface-hover); }
219
+ .section-item.selected { border-left-color: var(--accent); background: var(--surface-hover); }
220
+ .section-item .si-name { font-size: 12px; flex: 1; }
221
+ .section-item .si-badge { font-size: 11px; color: var(--dim); background: var(--border); padding: 1px 5px; border-radius: 3px; white-space: nowrap; }
222
+ .section-item .si-arrow { color: var(--dim); font-size: 11px; flex-shrink: 0; }
223
+ .section-group-title { padding: 6px 12px 2px; font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: var(--dim); border-top: 1px solid var(--border); }
224
+ .section-group-title:first-child { border-top: none; }
225
+
226
+ /* ── Detail column ── */
227
+ #col-detail { display: flex; flex-direction: column; overflow: hidden; transition: opacity 0.15s ease; }
228
+ .new-turn-pill {
229
+ position: sticky;
230
+ bottom: 12px;
231
+ align-self: center;
232
+ margin: 0 auto;
233
+ display: block;
234
+ width: fit-content;
235
+ background: var(--accent);
236
+ color: var(--bg);
237
+ padding: 3px 12px;
238
+ border-radius: 12px;
239
+ font-size: 11px;
240
+ cursor: pointer;
241
+ box-shadow: 0 2px 8px rgba(0,0,0,0.3);
242
+ z-index: 10;
243
+ animation: slideUp 0.3s ease;
244
+ }
245
+ @keyframes slideUp {
246
+ from { transform: translateX(-50%) translateY(10px); opacity: 0; }
247
+ to { transform: translateX(-50%) translateY(0); opacity: 1; }
248
+ }
249
+ .ctx-sticky { position: sticky; top: 0; background: var(--surface); border-bottom: 2px solid var(--border); padding: 10px 12px; z-index: 2; flex-shrink: 0; }
250
+ .ctx-sticky-title { font-size: 11px; color: var(--dim); margin-bottom: 6px; }
251
+ .detail-scroll { flex: 1; overflow-y: auto; scrollbar-width: thin; scrollbar-color: var(--border) transparent; }
252
+ .detail-scroll::-webkit-scrollbar { width: 4px; }
253
+ .detail-scroll::-webkit-scrollbar-track { background: transparent; }
254
+ .detail-scroll::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
255
+ .detail-scroll::-webkit-scrollbar-thumb:hover { background: var(--dim); }
256
+ .detail-content { padding: 12px; }
257
+
258
+ /* ── Shared content styles ── */
259
+ .section { border-bottom: 1px solid var(--border); }
260
+ .section:last-child { border-bottom: none; }
261
+ .section-header { padding: 8px 14px; cursor: pointer; color: var(--accent); font-size: 12px; user-select: none; display: flex; align-items: center; gap: 6px; }
262
+ .section-header:hover { background: var(--surface-hover); }
263
+ .section-header .arrow { color: var(--dim); font-size: 10px; transition: transform 0.15s; }
264
+ .section-header.open .arrow { transform: rotate(90deg); }
265
+ .section-header .badge { background: var(--border); color: var(--dim); padding: 1px 6px; border-radius: 4px; font-size: 11px; }
266
+ .section-content { display: none; padding: 8px 14px; overflow: auto; }
267
+ .section-content.open { display: block; }
268
+ pre { white-space: pre-wrap; word-break: break-all; font-size: 12px; line-height: 1.5; }
269
+ .msg { margin-bottom: 8px; padding: 8px; border-radius: 4px; border: 1px solid var(--border); }
270
+ .msg-role { font-size: 11px; font-weight: bold; margin-bottom: 4px; }
271
+ .msg-role.user { color: var(--accent); }
272
+ .msg-role.assistant { color: var(--green); }
273
+ .tool-grid { display: flex; flex-wrap: wrap; gap: 4px; }
274
+ .tool-tag { background: var(--border); padding: 2px 8px; border-radius: 4px; font-size: 11px; color: var(--dim); }
275
+ .content-block { margin-bottom: 6px; padding: 6px 8px; background: var(--bg); border-radius: 4px; }
276
+ .content-block .type { font-size: 11px; color: var(--yellow); margin-bottom: 2px; }
277
+ .msg-list-row { display: flex; align-items: baseline; gap: 4px; font-size: 11px; line-height: 1.4; }
278
+ .msg-list-idx { flex-shrink: 0; color: var(--dim); }
279
+ .msg-list-preview { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; min-width: 0; }
280
+ .msg-list-tok { flex-shrink: 0; font-size: 10px; color: var(--dim); margin-left: auto; }
281
+ .msg-thinking-line { font-size: 10px; color: var(--purple); opacity: 0.75; margin-top: 3px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; line-height: 1.5; }
282
+ .section-item.has-thinking { flex-direction: column; align-items: stretch; }
283
+ .msg-badge { font-size: 9px; font-weight: bold; padding: 0 4px; border-radius: 3px; flex-shrink: 0; letter-spacing: 0.03em; }
284
+ .msg-badge-human { background: var(--accent); color: var(--bg); }
285
+ .msg-badge-tool_results { background: var(--border); color: var(--dim); }
286
+ .msg-badge-system { background: var(--yellow); color: var(--bg); }
287
+ .msg-badge-call { background: var(--green); color: var(--bg); }
288
+ .msg-badge-think { background: var(--purple); color: var(--bg); }
289
+
290
+ /* ── Sysprompt Diff UI ── */
291
+ .sysprompt-badge { display: inline-flex; align-items: center; gap: 4px; font-size: 10px; color: var(--accent); cursor: pointer; padding: 2px 5px; border: 1px solid var(--accent); border-radius: 3px; margin-bottom: 4px; }
292
+ .sysprompt-badge:hover { background: rgba(77,208,225,0.1); }
293
+ .version-banner { background: var(--surface-hover); border-bottom: 1px solid var(--accent); padding: 6px 12px; font-size: 11px; color: var(--text); display: flex; align-items: center; gap: 8px; }
294
+ .version-banner .vb-dot { color: var(--accent); }
295
+ .version-banner .vb-ver { color: var(--purple); font-weight: bold; }
296
+ .version-banner .vb-delta { color: var(--green); }
297
+ .version-banner .vb-view { color: var(--accent); cursor: pointer; text-decoration: underline; }
298
+ .version-banner .vb-close { margin-left: auto; cursor: pointer; color: var(--dim); }
299
+
300
+ /* ── Intercept UI ── */
301
+ .held-badge { font-size: 9px; font-weight: bold; color: #fff; background: var(--red); padding: 0 5px; border-radius: 3px; margin-left: auto; flex-shrink: 0; cursor: pointer; animation: held-pulse 1.5s ease-in-out infinite; }
302
+ @keyframes held-pulse { 50% { opacity: 0.7; } }
303
+ #topbar-held { font-size: 10px; color: var(--bg); background: var(--yellow); cursor: pointer; padding: 1px 8px; border-radius: 10px; font-weight: 600; display: none; animation: held-pulse 1.5s ease-in-out infinite; }
304
+ #topbar-held:hover { opacity: 0.85; }
305
+
306
+ .intercept-overlay { position: absolute; inset: 0; z-index: 100; background: var(--surface); display: flex; flex-direction: column; border-left: 2px solid var(--yellow); }
307
+ .intercept-header { padding: 10px 14px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 8px; }
308
+ .intercept-header .ih-title { font-size: 12px; color: var(--yellow); font-weight: bold; flex: 1; }
309
+ .intercept-header .ih-session { font-size: 11px; color: var(--dim); }
310
+ .intercept-tabs { display: flex; border-bottom: 1px solid var(--border); padding: 0 10px; }
311
+ .intercept-tab { padding: 6px 12px; font-size: 11px; color: var(--dim); cursor: pointer; border-bottom: 2px solid transparent; }
312
+ .intercept-tab:hover { color: var(--text); }
313
+ .intercept-tab.active { color: var(--yellow); border-bottom-color: var(--yellow); }
314
+ .intercept-editor { flex: 1; overflow: auto; padding: 10px 14px; scrollbar-width: thin; scrollbar-color: var(--border) transparent; }
315
+ .intercept-editor::-webkit-scrollbar { width: 4px; }
316
+ .intercept-editor::-webkit-scrollbar-track { background: transparent; }
317
+ .intercept-editor::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
318
+ .intercept-editor textarea { width: 100%; min-height: 120px; background: var(--bg); color: var(--text); border: 1px solid var(--border); border-radius: 4px; padding: 8px; font-family: inherit; font-size: 12px; resize: vertical; }
319
+ .intercept-editor textarea:focus { outline: none; border-color: var(--yellow); }
320
+ .intercept-editor select { background: var(--bg); color: var(--text); border: 1px solid var(--border); border-radius: 4px; padding: 4px 8px; font-family: inherit; font-size: 12px; }
321
+ .intercept-summary { padding: 6px 14px; font-size: 11px; color: var(--dim); border-top: 1px solid var(--border); }
322
+ .intercept-countdown { height: 4px; background: var(--bg); flex-shrink: 0; }
323
+ .intercept-countdown-bar { height: 100%; transition: width 1s linear, background-color 0.5s; border-radius: 2px; }
324
+ .intercept-actions { padding: 10px 14px; border-top: 1px solid var(--border); display: flex; gap: 8px; justify-content: flex-end; }
325
+ .intercept-actions button { padding: 5px 16px; border-radius: 4px; cursor: pointer; font-family: inherit; font-size: 12px; border: 1px solid var(--border); }
326
+ .intercept-actions .btn-reject { background: transparent; color: var(--red); border-color: var(--red); }
327
+ .intercept-actions .btn-reject:hover { background: rgba(248, 81, 73, 0.15); }
328
+ .intercept-actions .btn-approve { background: var(--green); color: #000; border-color: var(--green); font-weight: bold; }
329
+ .intercept-actions .btn-approve:hover { opacity: 0.9; }
330
+ .ie-msg { padding: 6px 8px; border: 1px solid var(--border); border-radius: 4px; margin-bottom: 6px; cursor: pointer; }
331
+ .ie-msg:hover { background: var(--surface-hover); }
332
+ .ie-msg .ie-msg-role { font-size: 11px; font-weight: bold; margin-bottom: 2px; }
333
+ .ie-msg .ie-msg-role.user { color: var(--accent); }
334
+ .ie-msg .ie-msg-role.assistant { color: var(--green); }
335
+ .ie-msg .ie-msg-preview { font-size: 11px; color: var(--dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
336
+ .ie-msg.expanded textarea { margin-top: 6px; }
337
+ .ie-tool-item { display: flex; align-items: center; gap: 6px; padding: 4px 0; font-size: 12px; }
338
+ .ie-tool-item label { cursor: pointer; flex: 1; }
339
+ .ie-tool-item input[type=checkbox] { accent-color: var(--yellow); }
340
+
341
+ /* ── Fullscreen Page Shell ── */
342
+ .fullscreen-page { display: none; flex: 1; background: var(--bg); flex-direction: column; overflow: hidden; }
343
+ .fullscreen-page.open { display: flex; }
344
+ .fullscreen-page > .fp-header { display: none; }
345
+ .fp-back, .fp-close { background: none; border: none; color: var(--dim); cursor: pointer; font-size: 14px; padding: 4px 8px; flex-shrink: 0; }
346
+ .fp-back:hover, .fp-close:hover { color: var(--text); }
347
+ .fp-close { margin-left: auto; }
348
+ .fp-title { font-size: 14px; font-weight: 600; color: var(--text); }
349
+ .fp-actions { display: flex; align-items: center; gap: 8px; margin-left: auto; }
350
+ .fp-actions + .fp-close { margin-left: 0; }
351
+ .fp-toolbar { padding: 8px 16px; border-bottom: 1px solid var(--border); flex-shrink: 0; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
352
+ .fp-content { flex: 1; overflow-y: auto; padding: 20px; }
353
+
354
+ /* ── Fullscreen Page: Cost-specific responsive ── */
355
+ .cost-layout { display: flex; gap: 16px; align-items: flex-start; max-width: 960px; margin: 0 auto; }
356
+ .cost-left { flex: 1; min-width: 280px; max-width: 360px; display: flex; flex-direction: column; gap: 12px; }
357
+ .cost-right { flex: 2; min-width: 320px; }
358
+ .cost-card { background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 14px; }
359
+ .cost-card-label { font-size: 10px; color: var(--dim); margin-bottom: 8px; text-transform: uppercase; letter-spacing: 0.05em; }
360
+
361
+ /* ── Fullscreen Page: Changelog-specific ── */
362
+ .sp-changelog-body { display: flex; flex: 1; overflow: hidden; }
363
+ .sp-version-list { width: 240px; flex-shrink: 0; border-right: 1px solid var(--border); overflow-y: auto; padding: 8px 0; }
364
+ .sp-version-list-title { font-size: 10px; color: var(--dim); text-transform: uppercase; letter-spacing: 0.05em; padding: 4px 12px 8px; }
365
+ .sp-version-item { padding: 6px 12px; font-size: 11px; color: var(--dim); cursor: pointer; display: grid; grid-template-columns: 36px 1fr auto auto; gap: 4px; align-items: center; border-left: 3px solid transparent; white-space: nowrap; }
366
+ .sp-version-item:hover { background: var(--surface-hover); color: var(--text); }
367
+ .sp-version-item.active { border-left-color: var(--accent); color: var(--text); background: var(--surface-active); }
368
+ .sp-content-area { flex: 1; overflow-y: auto; padding: 12px 16px; }
369
+ .sp-status-bar { padding: 6px 16px; font-size: 10px; color: var(--dim); border-top: 1px solid var(--border); flex-shrink: 0; }
370
+
371
+ @media (max-width: 1023px) {
372
+ .cost-layout { flex-direction: column; }
373
+ .cost-left { max-width: none; flex-direction: row; gap: 12px; flex-wrap: wrap; }
374
+ .cost-left > * { flex: 1; min-width: 240px; }
375
+ .cost-right { min-width: 0; }
376
+ }
377
+
378
+ /* ── iPad (768–1024px): narrower version list ── */
379
+ @media (max-width: 1024px) and (min-width: 768px) {
380
+ .sp-version-list { width: 160px; }
381
+ .sp-version-item { grid-template-columns: 36px 1fr; }
382
+ .sp-version-item .sp-size-col,
383
+ .sp-version-item .sp-delta-col { display: none; }
384
+ }
385
+
386
+ /* ── iPhone (<768px): push/pop navigation ── */
387
+ @media (max-width: 767px) {
388
+ .sp-changelog-body { flex-direction: column; position: relative; }
389
+ .sp-version-list { width: 100%; border-right: none; border-bottom: 1px solid var(--border); }
390
+ .sp-version-item { grid-template-columns: 36px 1fr auto auto; }
391
+ .sp-content-area { width: 100%; display: none; }
392
+ .sp-changelog-body.sp-mobile-detail .sp-version-list { display: none; }
393
+ .sp-changelog-body.sp-mobile-detail .sp-content-area { display: block; flex: 1; }
394
+ }
395
+ @media (max-width: 599px) {
396
+ .fp-content { padding: 12px; }
397
+ .cost-left { flex-direction: column; }
398
+ .cost-left > * { min-width: 0; }
399
+ .cost-card { border: none; border-radius: 0; border-bottom: 1px solid var(--border); }
400
+ .cost-right .cost-card { overflow-x: auto; }
401
+ }
402
+
403
+ /* ── Toast Notifications ── */
404
+ #toast-container { position: fixed; bottom: 20px; right: 20px; z-index: 200; display: flex; flex-direction: column; gap: 8px; pointer-events: none; }
405
+ .toast { pointer-events: auto; background: var(--surface); border: 1px solid var(--border); border-left: 3px solid var(--yellow); color: var(--text); padding: 10px 14px; border-radius: 4px; font-size: 12px; max-width: 400px; cursor: pointer; animation: toast-in 0.3s ease-out; line-height: 1.5; }
406
+ .toast.fade-out { animation: toast-out 0.3s ease-in forwards; }
407
+ @keyframes toast-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
408
+ @keyframes toast-out { from { opacity: 1; } to { opacity: 0; transform: translateY(10px); } }
409
+
410
+ /* ── Detail Panel: Tool-Specific Rendering ── */
411
+ .detail-tool-header { display: flex; align-items: center; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid var(--border); margin-bottom: 8px; }
412
+ .detail-tool-title { font-size: 13px; font-weight: bold; display: flex; align-items: center; gap: 6px; }
413
+ .detail-meta { padding: 4px 0; font-size: 12px; color: var(--dim); }
414
+ .detail-meta code { color: var(--accent); background: var(--bg); padding: 1px 4px; border-radius: 2px; font-size: 11px; }
415
+ .detail-meta-line { font-size: 11px; color: var(--dim); margin-top: 2px; }
416
+ .detail-tag { font-size: 9px; background: var(--border); color: var(--dim); padding: 1px 4px; border-radius: 2px; }
417
+ .detail-cmd-block { background: var(--bg); border: 1px solid var(--border); padding: 8px; border-radius: 4px; }
418
+ .detail-collapse-bar { text-align: center; padding: 6px; margin: 4px 0; background: var(--surface); border: 1px dashed var(--border); border-radius: 4px; color: var(--dim); font-size: 11px; cursor: pointer; }
419
+ .detail-collapse-bar:hover { background: var(--surface-hover); color: var(--accent); }
420
+ .detail-diff { border: 1px solid var(--border); border-radius: 4px; overflow: hidden; margin-bottom: 6px; }
421
+ .detail-diff-section { padding: 4px 0; }
422
+ .detail-diff-label { font-size: 10px; color: var(--dim); padding: 2px 8px; font-weight: 600; letter-spacing: 0.05em; }
423
+ .detail-diff-old { background: rgba(248, 81, 73, 0.06); border-bottom: 1px solid var(--border); }
424
+ .detail-diff-new { background: rgba(63, 185, 80, 0.06); }
425
+ .diff-line-del { color: var(--red); }
426
+ .diff-line-add { color: var(--green); }
427
+ .detail-thinking .think-para { margin-bottom: 12px; line-height: 1.6; white-space: pre-wrap; word-break: break-word; font-size: 12px; }
428
+ .detail-copy-btn { background: none; border: 1px solid var(--border); border-radius: 3px; color: var(--dim); cursor: pointer; padding: 2px 6px; font-size: 11px; flex-shrink: 0; }
429
+ .detail-copy-btn:hover { color: var(--text); background: var(--surface); }
430
+ .detail-input-details summary { cursor: pointer; font-size: 11px; color: var(--yellow); margin-bottom: 2px; }
431
+ .detail-input-details { margin-bottom: 6px; padding: 6px 8px; background: var(--bg); border-radius: 4px; }