agenttop 0.4.0 → 0.7.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 +89 -7
- package/dist/{chunk-H2JOTO54.js → chunk-FCCWHOJO.js} +58 -5
- package/dist/chunk-FCCWHOJO.js.map +1 -0
- package/dist/index.js +1742 -326
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-H2JOTO54.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -2,28 +2,350 @@
|
|
|
2
2
|
import {
|
|
3
3
|
SecurityEngine,
|
|
4
4
|
Watcher,
|
|
5
|
+
archiveSession,
|
|
5
6
|
clearNickname,
|
|
7
|
+
deleteSessionFiles,
|
|
6
8
|
discoverSessions,
|
|
9
|
+
getArchived,
|
|
7
10
|
getNicknames,
|
|
8
11
|
isFirstRun,
|
|
9
12
|
loadConfig,
|
|
13
|
+
purgeExpiredArchives,
|
|
10
14
|
resolveAlertLogPath,
|
|
11
15
|
rotateLogFile,
|
|
12
16
|
saveConfig,
|
|
13
17
|
setNickname,
|
|
14
|
-
startMcpServer
|
|
15
|
-
|
|
18
|
+
startMcpServer,
|
|
19
|
+
unarchiveSession
|
|
20
|
+
} from "./chunk-FCCWHOJO.js";
|
|
16
21
|
|
|
17
22
|
// src/index.tsx
|
|
18
23
|
import { readFileSync as readFileSync4 } from "fs";
|
|
19
24
|
import { join as join4, dirname as dirname4 } from "path";
|
|
20
25
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
21
|
-
import
|
|
26
|
+
import React16 from "react";
|
|
22
27
|
import { render } from "ink";
|
|
23
28
|
|
|
24
29
|
// src/ui/App.tsx
|
|
25
|
-
import { useState as
|
|
26
|
-
import { Box as
|
|
30
|
+
import { useState as useState13, useEffect as useEffect9, useCallback as useCallback4 } from "react";
|
|
31
|
+
import { Box as Box15, Text as Text14, useApp, useStdout as useStdout3 } from "ink";
|
|
32
|
+
|
|
33
|
+
// src/config/themes.ts
|
|
34
|
+
var COLOR_KEYS = [
|
|
35
|
+
"primary",
|
|
36
|
+
"secondary",
|
|
37
|
+
"accent",
|
|
38
|
+
"warning",
|
|
39
|
+
"error",
|
|
40
|
+
"critical",
|
|
41
|
+
"muted",
|
|
42
|
+
"text",
|
|
43
|
+
"bright",
|
|
44
|
+
"border",
|
|
45
|
+
"selected",
|
|
46
|
+
"header"
|
|
47
|
+
];
|
|
48
|
+
var TOOL_COLOR_KEYS = [
|
|
49
|
+
"Bash",
|
|
50
|
+
"Read",
|
|
51
|
+
"Write",
|
|
52
|
+
"Edit",
|
|
53
|
+
"Grep",
|
|
54
|
+
"Glob",
|
|
55
|
+
"Task",
|
|
56
|
+
"WebFetch",
|
|
57
|
+
"WebSearch"
|
|
58
|
+
];
|
|
59
|
+
var deriveToolColors = (c) => ({
|
|
60
|
+
Bash: c.error,
|
|
61
|
+
Read: c.secondary,
|
|
62
|
+
Write: c.accent,
|
|
63
|
+
Edit: c.accent,
|
|
64
|
+
Grep: c.primary,
|
|
65
|
+
Glob: c.primary,
|
|
66
|
+
Task: c.warning,
|
|
67
|
+
WebFetch: c.warning,
|
|
68
|
+
WebSearch: c.warning
|
|
69
|
+
});
|
|
70
|
+
var fromTuple = ([
|
|
71
|
+
name,
|
|
72
|
+
primary,
|
|
73
|
+
secondary,
|
|
74
|
+
accent,
|
|
75
|
+
warning,
|
|
76
|
+
error,
|
|
77
|
+
critical,
|
|
78
|
+
muted,
|
|
79
|
+
text,
|
|
80
|
+
bright,
|
|
81
|
+
border,
|
|
82
|
+
selected,
|
|
83
|
+
header
|
|
84
|
+
]) => ({
|
|
85
|
+
name,
|
|
86
|
+
builtin: true,
|
|
87
|
+
colors: { primary, secondary, accent, warning, error, critical, muted, text, bright, border, selected, header },
|
|
88
|
+
toolColors: deriveToolColors({
|
|
89
|
+
primary,
|
|
90
|
+
secondary,
|
|
91
|
+
accent,
|
|
92
|
+
warning,
|
|
93
|
+
error,
|
|
94
|
+
critical,
|
|
95
|
+
muted,
|
|
96
|
+
text,
|
|
97
|
+
bright,
|
|
98
|
+
border,
|
|
99
|
+
selected,
|
|
100
|
+
header
|
|
101
|
+
})
|
|
102
|
+
});
|
|
103
|
+
var PRESETS = [
|
|
104
|
+
[
|
|
105
|
+
"one-dark",
|
|
106
|
+
"#61AFEF",
|
|
107
|
+
"#98C379",
|
|
108
|
+
"#C678DD",
|
|
109
|
+
"#E5C07B",
|
|
110
|
+
"#E06C75",
|
|
111
|
+
"#FF0000",
|
|
112
|
+
"#5C6370",
|
|
113
|
+
"#ABB2BF",
|
|
114
|
+
"#FFFFFF",
|
|
115
|
+
"#3E4451",
|
|
116
|
+
"#2C313A",
|
|
117
|
+
"#61AFEF"
|
|
118
|
+
],
|
|
119
|
+
[
|
|
120
|
+
"dracula",
|
|
121
|
+
"#BD93F9",
|
|
122
|
+
"#50FA7B",
|
|
123
|
+
"#FF79C6",
|
|
124
|
+
"#F1FA8C",
|
|
125
|
+
"#FF5555",
|
|
126
|
+
"#FF0000",
|
|
127
|
+
"#6272A4",
|
|
128
|
+
"#F8F8F2",
|
|
129
|
+
"#FFFFFF",
|
|
130
|
+
"#44475A",
|
|
131
|
+
"#383A59",
|
|
132
|
+
"#BD93F9"
|
|
133
|
+
],
|
|
134
|
+
[
|
|
135
|
+
"monokai-pro",
|
|
136
|
+
"#78DCE8",
|
|
137
|
+
"#A9DC76",
|
|
138
|
+
"#AB9DF2",
|
|
139
|
+
"#FFD866",
|
|
140
|
+
"#FF6188",
|
|
141
|
+
"#FF0000",
|
|
142
|
+
"#727072",
|
|
143
|
+
"#FCFCFA",
|
|
144
|
+
"#FFFFFF",
|
|
145
|
+
"#403E41",
|
|
146
|
+
"#2D2A2E",
|
|
147
|
+
"#78DCE8"
|
|
148
|
+
],
|
|
149
|
+
[
|
|
150
|
+
"solarized-dark",
|
|
151
|
+
"#268BD2",
|
|
152
|
+
"#859900",
|
|
153
|
+
"#D33682",
|
|
154
|
+
"#B58900",
|
|
155
|
+
"#DC322F",
|
|
156
|
+
"#FF0000",
|
|
157
|
+
"#586E75",
|
|
158
|
+
"#839496",
|
|
159
|
+
"#FDF6E3",
|
|
160
|
+
"#073642",
|
|
161
|
+
"#002B36",
|
|
162
|
+
"#268BD2"
|
|
163
|
+
],
|
|
164
|
+
[
|
|
165
|
+
"solarized-light",
|
|
166
|
+
"#268BD2",
|
|
167
|
+
"#859900",
|
|
168
|
+
"#D33682",
|
|
169
|
+
"#B58900",
|
|
170
|
+
"#DC322F",
|
|
171
|
+
"#FF0000",
|
|
172
|
+
"#93A1A1",
|
|
173
|
+
"#657B83",
|
|
174
|
+
"#002B36",
|
|
175
|
+
"#EEE8D5",
|
|
176
|
+
"#FDF6E3",
|
|
177
|
+
"#268BD2"
|
|
178
|
+
],
|
|
179
|
+
[
|
|
180
|
+
"nord",
|
|
181
|
+
"#88C0D0",
|
|
182
|
+
"#A3BE8C",
|
|
183
|
+
"#B48EAD",
|
|
184
|
+
"#EBCB8B",
|
|
185
|
+
"#BF616A",
|
|
186
|
+
"#FF0000",
|
|
187
|
+
"#4C566A",
|
|
188
|
+
"#D8DEE9",
|
|
189
|
+
"#ECEFF4",
|
|
190
|
+
"#3B4252",
|
|
191
|
+
"#2E3440",
|
|
192
|
+
"#88C0D0"
|
|
193
|
+
],
|
|
194
|
+
[
|
|
195
|
+
"gruvbox-dark",
|
|
196
|
+
"#83A598",
|
|
197
|
+
"#B8BB26",
|
|
198
|
+
"#D3869B",
|
|
199
|
+
"#FABD2F",
|
|
200
|
+
"#FB4934",
|
|
201
|
+
"#FF0000",
|
|
202
|
+
"#928374",
|
|
203
|
+
"#EBDBB2",
|
|
204
|
+
"#FBF1C7",
|
|
205
|
+
"#3C3836",
|
|
206
|
+
"#282828",
|
|
207
|
+
"#83A598"
|
|
208
|
+
],
|
|
209
|
+
[
|
|
210
|
+
"tokyo-night",
|
|
211
|
+
"#7AA2F7",
|
|
212
|
+
"#9ECE6A",
|
|
213
|
+
"#BB9AF7",
|
|
214
|
+
"#E0AF68",
|
|
215
|
+
"#F7768E",
|
|
216
|
+
"#FF0000",
|
|
217
|
+
"#565F89",
|
|
218
|
+
"#A9B1D6",
|
|
219
|
+
"#C0CAF5",
|
|
220
|
+
"#292E42",
|
|
221
|
+
"#1A1B26",
|
|
222
|
+
"#7AA2F7"
|
|
223
|
+
],
|
|
224
|
+
[
|
|
225
|
+
"catppuccin-mocha",
|
|
226
|
+
"#89B4FA",
|
|
227
|
+
"#A6E3A1",
|
|
228
|
+
"#CBA6F7",
|
|
229
|
+
"#F9E2AF",
|
|
230
|
+
"#F38BA8",
|
|
231
|
+
"#FF0000",
|
|
232
|
+
"#6C7086",
|
|
233
|
+
"#CDD6F4",
|
|
234
|
+
"#FFFFFF",
|
|
235
|
+
"#313244",
|
|
236
|
+
"#1E1E2E",
|
|
237
|
+
"#89B4FA"
|
|
238
|
+
],
|
|
239
|
+
[
|
|
240
|
+
"catppuccin-latte",
|
|
241
|
+
"#1E66F5",
|
|
242
|
+
"#40A02B",
|
|
243
|
+
"#8839EF",
|
|
244
|
+
"#DF8E1D",
|
|
245
|
+
"#D20F39",
|
|
246
|
+
"#FF0000",
|
|
247
|
+
"#9CA0B0",
|
|
248
|
+
"#4C4F69",
|
|
249
|
+
"#11111B",
|
|
250
|
+
"#E6E9EF",
|
|
251
|
+
"#EFF1F5",
|
|
252
|
+
"#1E66F5"
|
|
253
|
+
],
|
|
254
|
+
[
|
|
255
|
+
"rose-pine",
|
|
256
|
+
"#9CCFD8",
|
|
257
|
+
"#31748F",
|
|
258
|
+
"#C4A7E7",
|
|
259
|
+
"#F6C177",
|
|
260
|
+
"#EB6F92",
|
|
261
|
+
"#FF0000",
|
|
262
|
+
"#6E6A86",
|
|
263
|
+
"#E0DEF4",
|
|
264
|
+
"#E0DEF4",
|
|
265
|
+
"#26233A",
|
|
266
|
+
"#191724",
|
|
267
|
+
"#9CCFD8"
|
|
268
|
+
],
|
|
269
|
+
[
|
|
270
|
+
"rose-pine-moon",
|
|
271
|
+
"#9CCFD8",
|
|
272
|
+
"#3E8FB0",
|
|
273
|
+
"#C4A7E7",
|
|
274
|
+
"#F6C177",
|
|
275
|
+
"#EB6F92",
|
|
276
|
+
"#FF0000",
|
|
277
|
+
"#6E6A86",
|
|
278
|
+
"#E0DEF4",
|
|
279
|
+
"#E0DEF4",
|
|
280
|
+
"#2A273F",
|
|
281
|
+
"#232136",
|
|
282
|
+
"#9CCFD8"
|
|
283
|
+
],
|
|
284
|
+
[
|
|
285
|
+
"pastel-dark",
|
|
286
|
+
"#89CFF0",
|
|
287
|
+
"#77DD77",
|
|
288
|
+
"#FDCFE8",
|
|
289
|
+
"#FFD580",
|
|
290
|
+
"#FF6961",
|
|
291
|
+
"#FF0000",
|
|
292
|
+
"#7B8794",
|
|
293
|
+
"#D4D4D4",
|
|
294
|
+
"#FFFFFF",
|
|
295
|
+
"#3A3A4A",
|
|
296
|
+
"#2B2B3A",
|
|
297
|
+
"#89CFF0"
|
|
298
|
+
],
|
|
299
|
+
[
|
|
300
|
+
"kanagawa",
|
|
301
|
+
"#7E9CD8",
|
|
302
|
+
"#76946A",
|
|
303
|
+
"#957FB8",
|
|
304
|
+
"#E6C384",
|
|
305
|
+
"#C34043",
|
|
306
|
+
"#FF0000",
|
|
307
|
+
"#727169",
|
|
308
|
+
"#DCD7BA",
|
|
309
|
+
"#FFFFFF",
|
|
310
|
+
"#2A2A37",
|
|
311
|
+
"#1F1F28",
|
|
312
|
+
"#7E9CD8"
|
|
313
|
+
],
|
|
314
|
+
[
|
|
315
|
+
"everforest",
|
|
316
|
+
"#7FBBB3",
|
|
317
|
+
"#A7C080",
|
|
318
|
+
"#D699B6",
|
|
319
|
+
"#DBBC7F",
|
|
320
|
+
"#E67E80",
|
|
321
|
+
"#FF0000",
|
|
322
|
+
"#859289",
|
|
323
|
+
"#D3C6AA",
|
|
324
|
+
"#FFFFFF",
|
|
325
|
+
"#374145",
|
|
326
|
+
"#2D353B",
|
|
327
|
+
"#7FBBB3"
|
|
328
|
+
]
|
|
329
|
+
];
|
|
330
|
+
var BUILTIN_THEMES = PRESETS.map(fromTuple);
|
|
331
|
+
var getDefaultTheme = () => BUILTIN_THEMES[0];
|
|
332
|
+
var getAllThemes = (customThemes) => [
|
|
333
|
+
...BUILTIN_THEMES,
|
|
334
|
+
...Object.values(customThemes).map((t) => ({ ...t, builtin: false }))
|
|
335
|
+
];
|
|
336
|
+
var resolveTheme = (name, customThemes) => {
|
|
337
|
+
const builtin = BUILTIN_THEMES.find((t) => t.name === name);
|
|
338
|
+
if (builtin) return builtin;
|
|
339
|
+
const custom = customThemes[name];
|
|
340
|
+
if (custom) return { ...custom, builtin: false };
|
|
341
|
+
return getDefaultTheme();
|
|
342
|
+
};
|
|
343
|
+
var deriveSeverityColors = (c) => ({
|
|
344
|
+
info: c.muted,
|
|
345
|
+
warn: c.warning,
|
|
346
|
+
high: c.error,
|
|
347
|
+
critical: c.critical
|
|
348
|
+
});
|
|
27
349
|
|
|
28
350
|
// src/hooks/installer.ts
|
|
29
351
|
import { existsSync, readFileSync, writeFileSync, copyFileSync, mkdirSync, chmodSync } from "fs";
|
|
@@ -229,6 +551,11 @@ var toolColors = {
|
|
|
229
551
|
WebSearch: colors.warning
|
|
230
552
|
};
|
|
231
553
|
var getToolColor = (toolName) => toolColors[toolName] || colors.text;
|
|
554
|
+
var applyTheme = (theme) => {
|
|
555
|
+
Object.assign(colors, theme.colors);
|
|
556
|
+
Object.assign(toolColors, theme.toolColors);
|
|
557
|
+
Object.assign(severityColors, deriveSeverityColors(theme.colors));
|
|
558
|
+
};
|
|
232
559
|
|
|
233
560
|
// src/ui/components/StatusBar.tsx
|
|
234
561
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -286,76 +613,82 @@ var formatTokens = (n) => {
|
|
|
286
613
|
var truncate = (s, max) => s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
|
|
287
614
|
var SIDEBAR_WIDTH = 28;
|
|
288
615
|
var INNER_WIDTH = SIDEBAR_WIDTH - 4;
|
|
289
|
-
var SessionList = React2.memo(
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
"
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
sessions.length === 0 && /* @__PURE__ */ jsx2(Box2, { paddingX: 1, paddingY: 1, children: /* @__PURE__ */ jsx2(Text2, { color: colors.muted, italic: true, children: filter ? "No matches" : "No sessions" }) }),
|
|
308
|
-
sessions.map((session, i) => {
|
|
309
|
-
const isSelected = i === selectedIndex;
|
|
310
|
-
const indicator = isSelected ? ">" : " ";
|
|
311
|
-
const nameMaxLen = INNER_WIDTH - 2;
|
|
312
|
-
const displayName = truncate(session.nickname || session.slug, nameMaxLen);
|
|
313
|
-
const totalIn = session.usage.inputTokens + session.usage.cacheReadTokens;
|
|
314
|
-
const proj = formatProject(session.project, 12);
|
|
315
|
-
const model = formatModel(session.model);
|
|
316
|
-
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, paddingY: 0, children: [
|
|
317
|
-
/* @__PURE__ */ jsxs2(
|
|
318
|
-
Text2,
|
|
319
|
-
{
|
|
320
|
-
color: isSelected ? colors.bright : colors.text,
|
|
321
|
-
bold: isSelected,
|
|
322
|
-
backgroundColor: isSelected ? colors.selected : void 0,
|
|
323
|
-
wrap: "truncate",
|
|
324
|
-
children: [
|
|
325
|
-
indicator,
|
|
326
|
-
" ",
|
|
327
|
-
displayName
|
|
328
|
-
]
|
|
329
|
-
}
|
|
330
|
-
),
|
|
331
|
-
session.nickname && /* @__PURE__ */ jsxs2(Text2, { color: colors.muted, wrap: "truncate", children: [
|
|
332
|
-
" ",
|
|
333
|
-
truncate(session.slug, nameMaxLen)
|
|
334
|
-
] }),
|
|
335
|
-
/* @__PURE__ */ jsxs2(Text2, { color: colors.muted, wrap: "truncate", children: [
|
|
336
|
-
" ",
|
|
337
|
-
proj,
|
|
338
|
-
" ",
|
|
339
|
-
model,
|
|
340
|
-
" ",
|
|
341
|
-
session.agentCount,
|
|
342
|
-
"ag"
|
|
343
|
-
] }),
|
|
344
|
-
/* @__PURE__ */ jsxs2(Text2, { color: colors.muted, wrap: "truncate", children: [
|
|
345
|
-
" ",
|
|
346
|
-
formatTokens(totalIn),
|
|
347
|
-
"in ",
|
|
348
|
-
formatTokens(session.usage.outputTokens),
|
|
349
|
-
"out ",
|
|
350
|
-
session.cpu,
|
|
351
|
-
"%"
|
|
616
|
+
var SessionList = React2.memo(
|
|
617
|
+
({ sessions, selectedIndex, focused, filter, viewingArchive }) => {
|
|
618
|
+
const divider = "-".repeat(INNER_WIDTH);
|
|
619
|
+
return /* @__PURE__ */ jsxs2(
|
|
620
|
+
Box2,
|
|
621
|
+
{
|
|
622
|
+
flexDirection: "column",
|
|
623
|
+
width: SIDEBAR_WIDTH,
|
|
624
|
+
borderStyle: "single",
|
|
625
|
+
borderColor: focused ? colors.primary : colors.border,
|
|
626
|
+
overflow: "hidden",
|
|
627
|
+
children: [
|
|
628
|
+
/* @__PURE__ */ jsxs2(Box2, { paddingX: 1, children: [
|
|
629
|
+
/* @__PURE__ */ jsx2(Text2, { color: viewingArchive ? colors.warning : colors.header, bold: true, children: viewingArchive ? "ARCHIVE" : "SESSIONS" }),
|
|
630
|
+
filter && /* @__PURE__ */ jsxs2(Text2, { color: colors.muted, children: [
|
|
631
|
+
" [",
|
|
632
|
+
truncate(filter, 10),
|
|
633
|
+
"]"
|
|
352
634
|
] })
|
|
353
|
-
] },
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
635
|
+
] }),
|
|
636
|
+
sessions.length === 0 && /* @__PURE__ */ jsx2(Box2, { paddingX: 1, paddingY: 1, children: /* @__PURE__ */ jsx2(Text2, { color: colors.muted, italic: true, children: filter ? "No matches" : viewingArchive ? "No archived sessions" : "No sessions" }) }),
|
|
637
|
+
sessions.map((session, i) => {
|
|
638
|
+
const isSelected = i === selectedIndex;
|
|
639
|
+
const indicator = isSelected ? ">" : " ";
|
|
640
|
+
const nameMaxLen = INNER_WIDTH - 2;
|
|
641
|
+
const displayName = truncate(session.nickname || session.slug, nameMaxLen);
|
|
642
|
+
const totalIn = session.usage.inputTokens + session.usage.cacheReadTokens;
|
|
643
|
+
const proj = formatProject(session.project, 12);
|
|
644
|
+
const model = formatModel(session.model);
|
|
645
|
+
const isActive = session.pid !== null;
|
|
646
|
+
const nameColor = isSelected ? colors.bright : isActive ? colors.secondary : colors.error;
|
|
647
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, paddingY: 0, children: [
|
|
648
|
+
i > 0 && /* @__PURE__ */ jsx2(Text2, { color: colors.border, wrap: "truncate", children: divider }),
|
|
649
|
+
/* @__PURE__ */ jsxs2(
|
|
650
|
+
Text2,
|
|
651
|
+
{
|
|
652
|
+
color: nameColor,
|
|
653
|
+
bold: isSelected,
|
|
654
|
+
backgroundColor: isSelected ? colors.selected : void 0,
|
|
655
|
+
wrap: "truncate",
|
|
656
|
+
children: [
|
|
657
|
+
indicator,
|
|
658
|
+
" ",
|
|
659
|
+
displayName
|
|
660
|
+
]
|
|
661
|
+
}
|
|
662
|
+
),
|
|
663
|
+
session.nickname && /* @__PURE__ */ jsxs2(Text2, { color: colors.muted, wrap: "truncate", children: [
|
|
664
|
+
" ",
|
|
665
|
+
truncate(session.slug, nameMaxLen)
|
|
666
|
+
] }),
|
|
667
|
+
/* @__PURE__ */ jsxs2(Text2, { color: colors.muted, wrap: "truncate", children: [
|
|
668
|
+
" ",
|
|
669
|
+
proj,
|
|
670
|
+
" ",
|
|
671
|
+
model,
|
|
672
|
+
" ",
|
|
673
|
+
session.agentCount,
|
|
674
|
+
"ag"
|
|
675
|
+
] }),
|
|
676
|
+
/* @__PURE__ */ jsxs2(Text2, { color: colors.muted, wrap: "truncate", children: [
|
|
677
|
+
" ",
|
|
678
|
+
formatTokens(totalIn),
|
|
679
|
+
"in ",
|
|
680
|
+
formatTokens(session.usage.outputTokens),
|
|
681
|
+
"out ",
|
|
682
|
+
session.cpu,
|
|
683
|
+
"%"
|
|
684
|
+
] })
|
|
685
|
+
] }, session.sessionId);
|
|
686
|
+
})
|
|
687
|
+
]
|
|
688
|
+
}
|
|
689
|
+
);
|
|
690
|
+
}
|
|
691
|
+
);
|
|
359
692
|
|
|
360
693
|
// src/ui/components/ActivityFeed.tsx
|
|
361
694
|
import React3 from "react";
|
|
@@ -388,7 +721,7 @@ var summarizeInput = (call) => {
|
|
|
388
721
|
}
|
|
389
722
|
};
|
|
390
723
|
var ActivityFeed = React3.memo(
|
|
391
|
-
({ events, sessionSlug, focused, height, scrollOffset }) => {
|
|
724
|
+
({ events, sessionSlug, focused, height, scrollOffset, filter }) => {
|
|
392
725
|
const viewportRows = height - 2;
|
|
393
726
|
const totalEvents = events.length;
|
|
394
727
|
const start = Math.max(0, totalEvents - viewportRows - scrollOffset);
|
|
@@ -412,6 +745,11 @@ var ActivityFeed = React3.memo(
|
|
|
412
745
|
" (",
|
|
413
746
|
sessionSlug,
|
|
414
747
|
")"
|
|
748
|
+
] }),
|
|
749
|
+
filter && /* @__PURE__ */ jsxs3(Text3, { color: colors.muted, children: [
|
|
750
|
+
" [",
|
|
751
|
+
filter.length > 10 ? filter.slice(0, 9) + "\u2026" : filter,
|
|
752
|
+
"]"
|
|
415
753
|
] })
|
|
416
754
|
] }),
|
|
417
755
|
focused && canScroll && !isAtBottom && /* @__PURE__ */ jsxs3(Text3, { color: colors.muted, children: [
|
|
@@ -662,49 +1000,81 @@ var SetupModal = React6.memo(({ steps, onComplete }) => {
|
|
|
662
1000
|
// src/ui/components/FooterBar.tsx
|
|
663
1001
|
import React7 from "react";
|
|
664
1002
|
import { Box as Box7, Text as Text7 } from "ink";
|
|
665
|
-
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1003
|
+
import { Fragment, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
666
1004
|
var label = (key) => {
|
|
667
1005
|
if (key === "tab") return "tab";
|
|
668
1006
|
if (key === "shift+tab") return "S-tab";
|
|
669
1007
|
if (key === "enter") return "enter";
|
|
670
1008
|
return key;
|
|
671
1009
|
};
|
|
672
|
-
var FooterBar = React7.memo(
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
] }))
|
|
1010
|
+
var FooterBar = React7.memo(
|
|
1011
|
+
({ keybindings, updateStatus, viewingArchive, splitMode }) => /* @__PURE__ */ jsxs7(Box7, { paddingX: 1, children: [
|
|
1012
|
+
/* @__PURE__ */ jsx7(Box7, { marginRight: 2, children: /* @__PURE__ */ jsxs7(Text7, { color: colors.muted, children: [
|
|
1013
|
+
label(keybindings.quit),
|
|
1014
|
+
":quit"
|
|
1015
|
+
] }) }),
|
|
1016
|
+
/* @__PURE__ */ jsx7(Box7, { marginRight: 2, children: /* @__PURE__ */ jsxs7(Text7, { color: colors.muted, children: [
|
|
1017
|
+
label(keybindings.navDown),
|
|
1018
|
+
"/",
|
|
1019
|
+
label(keybindings.navUp),
|
|
1020
|
+
":nav"
|
|
1021
|
+
] }) }),
|
|
1022
|
+
/* @__PURE__ */ jsx7(Box7, { marginRight: 2, children: /* @__PURE__ */ jsxs7(Text7, { color: colors.muted, children: [
|
|
1023
|
+
label(keybindings.panelNext),
|
|
1024
|
+
":panel"
|
|
1025
|
+
] }) }),
|
|
1026
|
+
/* @__PURE__ */ jsx7(Box7, { marginRight: 2, children: /* @__PURE__ */ jsxs7(Text7, { color: colors.muted, children: [
|
|
1027
|
+
label(keybindings.filter),
|
|
1028
|
+
":filter"
|
|
1029
|
+
] }) }),
|
|
1030
|
+
/* @__PURE__ */ jsx7(Box7, { marginRight: 2, children: /* @__PURE__ */ jsxs7(Text7, { color: colors.muted, children: [
|
|
1031
|
+
label(keybindings.nickname),
|
|
1032
|
+
":name"
|
|
1033
|
+
] }) }),
|
|
1034
|
+
/* @__PURE__ */ jsx7(Box7, { marginRight: 2, children: /* @__PURE__ */ jsxs7(Text7, { color: colors.muted, children: [
|
|
1035
|
+
label(keybindings.detail),
|
|
1036
|
+
":detail"
|
|
1037
|
+
] }) }),
|
|
1038
|
+
/* @__PURE__ */ jsx7(Box7, { marginRight: 2, children: /* @__PURE__ */ jsxs7(Text7, { color: colors.muted, children: [
|
|
1039
|
+
label(keybindings.split),
|
|
1040
|
+
":",
|
|
1041
|
+
splitMode ? "unsplit" : "split"
|
|
1042
|
+
] }) }),
|
|
1043
|
+
splitMode && /* @__PURE__ */ jsxs7(Fragment, { children: [
|
|
1044
|
+
/* @__PURE__ */ jsx7(Box7, { marginRight: 2, children: /* @__PURE__ */ jsxs7(Text7, { color: colors.muted, children: [
|
|
1045
|
+
label(keybindings.pinLeft),
|
|
1046
|
+
"/",
|
|
1047
|
+
label(keybindings.pinRight),
|
|
1048
|
+
":pin"
|
|
1049
|
+
] }) }),
|
|
1050
|
+
/* @__PURE__ */ jsx7(Box7, { marginRight: 2, children: /* @__PURE__ */ jsxs7(Text7, { color: colors.muted, children: [
|
|
1051
|
+
label(keybindings.swapPanels),
|
|
1052
|
+
":swap"
|
|
1053
|
+
] }) })
|
|
1054
|
+
] }),
|
|
1055
|
+
/* @__PURE__ */ jsx7(Box7, { marginRight: 2, children: /* @__PURE__ */ jsxs7(Text7, { color: colors.muted, children: [
|
|
1056
|
+
label(keybindings.archive),
|
|
1057
|
+
":",
|
|
1058
|
+
viewingArchive ? "restore" : "archive"
|
|
1059
|
+
] }) }),
|
|
1060
|
+
/* @__PURE__ */ jsx7(Box7, { marginRight: 2, children: /* @__PURE__ */ jsxs7(Text7, { color: colors.muted, children: [
|
|
1061
|
+
label(keybindings.delete),
|
|
1062
|
+
":delete"
|
|
1063
|
+
] }) }),
|
|
1064
|
+
/* @__PURE__ */ jsx7(Box7, { marginRight: 2, children: /* @__PURE__ */ jsxs7(Text7, { color: colors.muted, children: [
|
|
1065
|
+
label(keybindings.viewArchive),
|
|
1066
|
+
":archived"
|
|
1067
|
+
] }) }),
|
|
1068
|
+
/* @__PURE__ */ jsx7(Box7, { marginRight: 2, children: /* @__PURE__ */ jsxs7(Text7, { color: colors.muted, children: [
|
|
1069
|
+
label(keybindings.settings),
|
|
1070
|
+
":settings"
|
|
1071
|
+
] }) }),
|
|
1072
|
+
updateStatus && /* @__PURE__ */ jsx7(Box7, { marginRight: 2, children: /* @__PURE__ */ jsx7(Text7, { color: colors.secondary, children: updateStatus }) })
|
|
1073
|
+
] })
|
|
1074
|
+
);
|
|
705
1075
|
|
|
706
1076
|
// src/ui/components/SettingsMenu.tsx
|
|
707
|
-
import React8, { useState as useState3, useMemo } from "react";
|
|
1077
|
+
import React8, { useState as useState3, useMemo, useEffect as useEffect2, useRef } from "react";
|
|
708
1078
|
import { Box as Box8, Text as Text8, useInput as useInput2, useStdout } from "ink";
|
|
709
1079
|
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
710
1080
|
var KEYBIND_LABELS = {
|
|
@@ -715,12 +1085,20 @@ var KEYBIND_LABELS = {
|
|
|
715
1085
|
panelPrev: "Previous panel",
|
|
716
1086
|
scrollTop: "Scroll to top",
|
|
717
1087
|
scrollBottom: "Scroll to bottom",
|
|
718
|
-
filter: "Filter
|
|
1088
|
+
filter: "Filter",
|
|
719
1089
|
nickname: "Set nickname",
|
|
720
1090
|
clearNickname: "Clear nickname",
|
|
721
1091
|
detail: "Detail view",
|
|
722
1092
|
update: "Install update",
|
|
723
|
-
settings: "Settings"
|
|
1093
|
+
settings: "Settings",
|
|
1094
|
+
archive: "Archive session",
|
|
1095
|
+
delete: "Delete session",
|
|
1096
|
+
viewArchive: "View archive",
|
|
1097
|
+
split: "Toggle split view",
|
|
1098
|
+
pinLeft: "Pin to left panel",
|
|
1099
|
+
pinRight: "Pin to right panel",
|
|
1100
|
+
swapPanels: "Swap panels",
|
|
1101
|
+
closePanel: "Close panel"
|
|
724
1102
|
};
|
|
725
1103
|
var RULE_LABELS = {
|
|
726
1104
|
network: "Network detection",
|
|
@@ -729,7 +1107,32 @@ var RULE_LABELS = {
|
|
|
729
1107
|
shellEscape: "Shell escape",
|
|
730
1108
|
injection: "Prompt injection"
|
|
731
1109
|
};
|
|
1110
|
+
var DEFAULT_KEYBINDINGS = {
|
|
1111
|
+
quit: "q",
|
|
1112
|
+
navUp: "k",
|
|
1113
|
+
navDown: "j",
|
|
1114
|
+
panelNext: "tab",
|
|
1115
|
+
panelPrev: "shift+tab",
|
|
1116
|
+
scrollTop: "g",
|
|
1117
|
+
scrollBottom: "G",
|
|
1118
|
+
filter: "/",
|
|
1119
|
+
nickname: "n",
|
|
1120
|
+
clearNickname: "N",
|
|
1121
|
+
detail: "enter",
|
|
1122
|
+
update: "u",
|
|
1123
|
+
settings: "s",
|
|
1124
|
+
archive: "a",
|
|
1125
|
+
delete: "d",
|
|
1126
|
+
viewArchive: "A",
|
|
1127
|
+
split: "x",
|
|
1128
|
+
pinLeft: "1",
|
|
1129
|
+
pinRight: "2",
|
|
1130
|
+
swapPanels: "S",
|
|
1131
|
+
closePanel: "X"
|
|
1132
|
+
};
|
|
732
1133
|
var SEVERITY_OPTIONS = ["info", "warn", "high", "critical"];
|
|
1134
|
+
var ARCHIVE_EXPIRY_OPTIONS = [0, 7, 14, 30, 60, 90];
|
|
1135
|
+
var formatExpiry = (days) => days === 0 ? "never" : `${days}d`;
|
|
733
1136
|
var displayKey = (key) => {
|
|
734
1137
|
if (key === "tab") return "tab";
|
|
735
1138
|
if (key === "shift+tab") return "S-tab";
|
|
@@ -738,6 +1141,27 @@ var displayKey = (key) => {
|
|
|
738
1141
|
};
|
|
739
1142
|
var buildMenuItems = () => {
|
|
740
1143
|
const items = [];
|
|
1144
|
+
items.push({ type: "header", label: "THEMES", section: "themes", getValue: () => "", key: void 0 });
|
|
1145
|
+
items.push({
|
|
1146
|
+
type: "action",
|
|
1147
|
+
label: "Manage themes...",
|
|
1148
|
+
section: "themes",
|
|
1149
|
+
key: "manageThemes",
|
|
1150
|
+
getValue: (cfg) => cfg.theme,
|
|
1151
|
+
apply: void 0
|
|
1152
|
+
});
|
|
1153
|
+
items.push({ type: "header", label: "GENERAL", section: "general", getValue: () => "", key: void 0 });
|
|
1154
|
+
items.push({
|
|
1155
|
+
type: "cycle",
|
|
1156
|
+
label: "Archive expiry",
|
|
1157
|
+
section: "general",
|
|
1158
|
+
key: "archiveExpiryDays",
|
|
1159
|
+
getValue: (cfg) => formatExpiry(cfg.archiveExpiryDays),
|
|
1160
|
+
apply: (cfg) => {
|
|
1161
|
+
const idx = ARCHIVE_EXPIRY_OPTIONS.indexOf(cfg.archiveExpiryDays);
|
|
1162
|
+
return { ...cfg, archiveExpiryDays: ARCHIVE_EXPIRY_OPTIONS[(idx + 1) % ARCHIVE_EXPIRY_OPTIONS.length] };
|
|
1163
|
+
}
|
|
1164
|
+
});
|
|
741
1165
|
items.push({ type: "header", label: "KEYBINDINGS", section: "keybindings", getValue: () => "", key: void 0 });
|
|
742
1166
|
for (const [k, label2] of Object.entries(KEYBIND_LABELS)) {
|
|
743
1167
|
const kbKey = k;
|
|
@@ -747,12 +1171,17 @@ var buildMenuItems = () => {
|
|
|
747
1171
|
section: "keybindings",
|
|
748
1172
|
key: kbKey,
|
|
749
1173
|
getValue: (cfg) => displayKey(cfg.keybindings[kbKey]),
|
|
750
|
-
apply: (cfg, newValue) => ({
|
|
751
|
-
...cfg,
|
|
752
|
-
keybindings: { ...cfg.keybindings, [kbKey]: newValue }
|
|
753
|
-
})
|
|
1174
|
+
apply: (cfg, newValue) => ({ ...cfg, keybindings: { ...cfg.keybindings, [kbKey]: newValue } })
|
|
754
1175
|
});
|
|
755
1176
|
}
|
|
1177
|
+
items.push({
|
|
1178
|
+
type: "action",
|
|
1179
|
+
label: "Reset all keybindings",
|
|
1180
|
+
section: "keybindings",
|
|
1181
|
+
key: "resetAllKeybinds",
|
|
1182
|
+
getValue: () => "",
|
|
1183
|
+
apply: (cfg) => ({ ...cfg, keybindings: { ...DEFAULT_KEYBINDINGS } })
|
|
1184
|
+
});
|
|
756
1185
|
items.push({ type: "header", label: "SECURITY RULES", section: "security", getValue: () => "", key: void 0 });
|
|
757
1186
|
for (const [k, label2] of Object.entries(RULE_LABELS)) {
|
|
758
1187
|
const ruleKey = k;
|
|
@@ -764,30 +1193,18 @@ var buildMenuItems = () => {
|
|
|
764
1193
|
getValue: (cfg) => cfg.security.rules[ruleKey] ? "ON" : "OFF",
|
|
765
1194
|
apply: (cfg) => ({
|
|
766
1195
|
...cfg,
|
|
767
|
-
security: {
|
|
768
|
-
...cfg.security,
|
|
769
|
-
rules: { ...cfg.security.rules, [ruleKey]: !cfg.security.rules[ruleKey] }
|
|
770
|
-
}
|
|
1196
|
+
security: { ...cfg.security, rules: { ...cfg.security.rules, [ruleKey]: !cfg.security.rules[ruleKey] } }
|
|
771
1197
|
})
|
|
772
1198
|
});
|
|
773
1199
|
}
|
|
774
|
-
items.push({
|
|
775
|
-
type: "header",
|
|
776
|
-
label: "NOTIFICATIONS",
|
|
777
|
-
section: "notifications",
|
|
778
|
-
getValue: () => "",
|
|
779
|
-
key: void 0
|
|
780
|
-
});
|
|
1200
|
+
items.push({ type: "header", label: "NOTIFICATIONS", section: "notifications", getValue: () => "", key: void 0 });
|
|
781
1201
|
items.push({
|
|
782
1202
|
type: "toggle",
|
|
783
1203
|
label: "Terminal bell",
|
|
784
1204
|
section: "notifications",
|
|
785
1205
|
key: "bell",
|
|
786
1206
|
getValue: (cfg) => cfg.notifications.bell ? "ON" : "OFF",
|
|
787
|
-
apply: (cfg) => ({
|
|
788
|
-
...cfg,
|
|
789
|
-
notifications: { ...cfg.notifications, bell: !cfg.notifications.bell }
|
|
790
|
-
})
|
|
1207
|
+
apply: (cfg) => ({ ...cfg, notifications: { ...cfg.notifications, bell: !cfg.notifications.bell } })
|
|
791
1208
|
});
|
|
792
1209
|
items.push({
|
|
793
1210
|
type: "toggle",
|
|
@@ -795,10 +1212,7 @@ var buildMenuItems = () => {
|
|
|
795
1212
|
section: "notifications",
|
|
796
1213
|
key: "desktop",
|
|
797
1214
|
getValue: (cfg) => cfg.notifications.desktop ? "ON" : "OFF",
|
|
798
|
-
apply: (cfg) => ({
|
|
799
|
-
...cfg,
|
|
800
|
-
notifications: { ...cfg.notifications, desktop: !cfg.notifications.desktop }
|
|
801
|
-
})
|
|
1215
|
+
apply: (cfg) => ({ ...cfg, notifications: { ...cfg.notifications, desktop: !cfg.notifications.desktop } })
|
|
802
1216
|
});
|
|
803
1217
|
items.push({
|
|
804
1218
|
type: "cycle",
|
|
@@ -808,20 +1222,35 @@ var buildMenuItems = () => {
|
|
|
808
1222
|
getValue: (cfg) => cfg.notifications.minSeverity,
|
|
809
1223
|
apply: (cfg) => {
|
|
810
1224
|
const idx = SEVERITY_OPTIONS.indexOf(cfg.notifications.minSeverity);
|
|
811
|
-
|
|
812
|
-
|
|
1225
|
+
return {
|
|
1226
|
+
...cfg,
|
|
1227
|
+
notifications: { ...cfg.notifications, minSeverity: SEVERITY_OPTIONS[(idx + 1) % SEVERITY_OPTIONS.length] }
|
|
1228
|
+
};
|
|
813
1229
|
}
|
|
814
1230
|
});
|
|
815
1231
|
return items;
|
|
816
1232
|
};
|
|
817
1233
|
var MENU_ITEMS = buildMenuItems();
|
|
818
1234
|
var SELECTABLE_INDICES = MENU_ITEMS.map((item, i) => item.type !== "header" ? i : -1).filter((i) => i >= 0);
|
|
819
|
-
var SettingsMenu = React8.memo(({ config, onClose }) => {
|
|
1235
|
+
var SettingsMenu = React8.memo(({ config, onClose, onOpenThemeMenu }) => {
|
|
820
1236
|
const { stdout } = useStdout();
|
|
821
1237
|
const termHeight = stdout?.rows ?? 40;
|
|
822
1238
|
const [localConfig, setLocalConfig] = useState3(() => JSON.parse(JSON.stringify(config)));
|
|
823
1239
|
const [selectablePos, setSelectablePos] = useState3(0);
|
|
824
1240
|
const [rebinding, setRebinding] = useState3(false);
|
|
1241
|
+
const [toast, setToast] = useState3("");
|
|
1242
|
+
const toastTimer = useRef(null);
|
|
1243
|
+
const showToast = (msg) => {
|
|
1244
|
+
if (toastTimer.current) clearTimeout(toastTimer.current);
|
|
1245
|
+
setToast(msg);
|
|
1246
|
+
toastTimer.current = setTimeout(() => setToast(""), 2500);
|
|
1247
|
+
};
|
|
1248
|
+
useEffect2(
|
|
1249
|
+
() => () => {
|
|
1250
|
+
if (toastTimer.current) clearTimeout(toastTimer.current);
|
|
1251
|
+
},
|
|
1252
|
+
[]
|
|
1253
|
+
);
|
|
825
1254
|
const selectedIndex = SELECTABLE_INDICES[selectablePos];
|
|
826
1255
|
const maxLabelLen = useMemo(
|
|
827
1256
|
() => Math.max(...MENU_ITEMS.filter((i) => i.type !== "header").map((i) => i.label.length)),
|
|
@@ -837,11 +1266,24 @@ var SettingsMenu = React8.memo(({ config, onClose }) => {
|
|
|
837
1266
|
else if (key.escape) {
|
|
838
1267
|
setRebinding(false);
|
|
839
1268
|
return;
|
|
1269
|
+
} else if (key.backspace || key.delete) {
|
|
1270
|
+
const kbKey2 = item.key;
|
|
1271
|
+
const defaultVal = DEFAULT_KEYBINDINGS[kbKey2];
|
|
1272
|
+
if (item.apply) setLocalConfig((c) => item.apply(c, defaultVal));
|
|
1273
|
+
showToast(`Reset to default: ${displayKey(defaultVal)}`);
|
|
1274
|
+
setRebinding(false);
|
|
1275
|
+
return;
|
|
840
1276
|
} else if (input && input.length === 1) newKey = input;
|
|
841
1277
|
else return;
|
|
842
|
-
|
|
843
|
-
|
|
1278
|
+
const kbKey = item.key;
|
|
1279
|
+
const conflict = Object.entries(localConfig.keybindings).find(([k, v]) => k !== kbKey && v === newKey);
|
|
1280
|
+
if (conflict) {
|
|
1281
|
+
const conflictLabel = KEYBIND_LABELS[conflict[0]] || conflict[0];
|
|
1282
|
+
showToast(`'${displayKey(newKey)}' is already assigned to '${conflictLabel}'`);
|
|
1283
|
+
setRebinding(false);
|
|
1284
|
+
return;
|
|
844
1285
|
}
|
|
1286
|
+
if (item.apply) setLocalConfig((c) => item.apply(c, newKey));
|
|
845
1287
|
setRebinding(false);
|
|
846
1288
|
return;
|
|
847
1289
|
}
|
|
@@ -859,11 +1301,17 @@ var SettingsMenu = React8.memo(({ config, onClose }) => {
|
|
|
859
1301
|
}
|
|
860
1302
|
if (key.return) {
|
|
861
1303
|
const item = MENU_ITEMS[selectedIndex];
|
|
862
|
-
if (item.
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
1304
|
+
if (item.key === "manageThemes") {
|
|
1305
|
+
onOpenThemeMenu();
|
|
1306
|
+
return;
|
|
1307
|
+
}
|
|
1308
|
+
if (item.key === "resetAllKeybinds") {
|
|
1309
|
+
if (item.apply) setLocalConfig((c) => item.apply(c));
|
|
1310
|
+
showToast("All keybindings reset to defaults");
|
|
1311
|
+
return;
|
|
866
1312
|
}
|
|
1313
|
+
if (item.type === "keybind") setRebinding(true);
|
|
1314
|
+
else if (item.apply) setLocalConfig((c) => item.apply(c));
|
|
867
1315
|
}
|
|
868
1316
|
});
|
|
869
1317
|
const contentHeight = termHeight - 6;
|
|
@@ -884,7 +1332,10 @@ var SettingsMenu = React8.memo(({ config, onClose }) => {
|
|
|
884
1332
|
children: [
|
|
885
1333
|
/* @__PURE__ */ jsxs8(Box8, { justifyContent: "space-between", marginBottom: 1, children: [
|
|
886
1334
|
/* @__PURE__ */ jsx8(Text8, { color: colors.header, bold: true, children: "SETTINGS" }),
|
|
887
|
-
/* @__PURE__ */
|
|
1335
|
+
/* @__PURE__ */ jsxs8(Text8, { color: colors.muted, children: [
|
|
1336
|
+
"esc to save & close ",
|
|
1337
|
+
rebinding ? "| backspace to reset" : ""
|
|
1338
|
+
] })
|
|
888
1339
|
] }),
|
|
889
1340
|
visibleItems.map((item, vi) => {
|
|
890
1341
|
const realIndex = visibleStartIndex + vi;
|
|
@@ -896,36 +1347,579 @@ var SettingsMenu = React8.memo(({ config, onClose }) => {
|
|
|
896
1347
|
] }) }, `h-${item.label}`);
|
|
897
1348
|
}
|
|
898
1349
|
const value = item.getValue(localConfig);
|
|
899
|
-
const dots = ".".repeat(Math.max(2, maxLabelLen - item.label.length + 4));
|
|
900
1350
|
const isRebindingThis = rebinding && isSelected;
|
|
901
1351
|
const displayValue = isRebindingThis ? "[press key...]" : value;
|
|
902
1352
|
const valueColor = isRebindingThis ? colors.warning : value === "ON" ? colors.secondary : value === "OFF" ? colors.error : colors.bright;
|
|
1353
|
+
const isReset = item.key === "resetAllKeybinds";
|
|
1354
|
+
const dots = isReset ? "" : ".".repeat(Math.max(2, maxLabelLen - item.label.length + 4)) + " ";
|
|
903
1355
|
return /* @__PURE__ */ jsxs8(Box8, { children: [
|
|
904
1356
|
/* @__PURE__ */ jsxs8(Text8, { color: isSelected ? colors.primary : colors.text, children: [
|
|
905
1357
|
isSelected ? "> " : " ",
|
|
906
|
-
"
|
|
1358
|
+
" ",
|
|
907
1359
|
item.label,
|
|
908
1360
|
" ",
|
|
909
|
-
dots
|
|
910
|
-
" "
|
|
1361
|
+
dots
|
|
911
1362
|
] }),
|
|
912
|
-
/* @__PURE__ */ jsx8(Text8, { color: valueColor, children: displayValue })
|
|
1363
|
+
!isReset && /* @__PURE__ */ jsx8(Text8, { color: valueColor, children: displayValue })
|
|
913
1364
|
] }, `${item.section}-${item.key}`);
|
|
914
|
-
})
|
|
1365
|
+
}),
|
|
1366
|
+
toast && /* @__PURE__ */ jsx8(Box8, { marginTop: 1, paddingX: 2, children: /* @__PURE__ */ jsx8(Text8, { color: colors.warning, children: toast }) })
|
|
1367
|
+
]
|
|
1368
|
+
}
|
|
1369
|
+
) });
|
|
1370
|
+
});
|
|
1371
|
+
|
|
1372
|
+
// src/ui/components/ThemeMenu.tsx
|
|
1373
|
+
import React10, { useState as useState5, useCallback, useRef as useRef2, useEffect as useEffect3 } from "react";
|
|
1374
|
+
import { Box as Box10, Text as Text10, useInput as useInput4, useStdout as useStdout2 } from "ink";
|
|
1375
|
+
|
|
1376
|
+
// src/ui/components/ThemeEditor.tsx
|
|
1377
|
+
import React9, { useState as useState4 } from "react";
|
|
1378
|
+
import { Box as Box9, Text as Text9, useInput as useInput3 } from "ink";
|
|
1379
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1380
|
+
var ALL_KEYS = [
|
|
1381
|
+
...COLOR_KEYS.map((key) => ({ group: "colors", key })),
|
|
1382
|
+
...TOOL_COLOR_KEYS.map((key) => ({ group: "toolColors", key }))
|
|
1383
|
+
];
|
|
1384
|
+
var isValidHex = (v) => /^#[0-9A-Fa-f]{6}$/.test(v);
|
|
1385
|
+
var ThemeEditor = React9.memo(({ theme, onSave, onCancel }) => {
|
|
1386
|
+
const [editColors, setEditColors] = useState4({ ...theme.colors });
|
|
1387
|
+
const [editToolColors, setEditToolColors] = useState4({ ...theme.toolColors });
|
|
1388
|
+
const [selectedIdx, setSelectedIdx] = useState4(0);
|
|
1389
|
+
const [editing, setEditing] = useState4(false);
|
|
1390
|
+
const [inputValue, setInputValue] = useState4("");
|
|
1391
|
+
const currentTarget = ALL_KEYS[selectedIdx];
|
|
1392
|
+
const getCurrentValue = (t) => t.group === "colors" ? editColors[t.key] : editToolColors[t.key];
|
|
1393
|
+
useInput3((input, key) => {
|
|
1394
|
+
if (editing) {
|
|
1395
|
+
if (key.escape) {
|
|
1396
|
+
setEditing(false);
|
|
1397
|
+
setInputValue("");
|
|
1398
|
+
return;
|
|
1399
|
+
}
|
|
1400
|
+
if (key.return) {
|
|
1401
|
+
const val = inputValue.startsWith("#") ? inputValue : `#${inputValue}`;
|
|
1402
|
+
if (isValidHex(val)) {
|
|
1403
|
+
if (currentTarget.group === "colors") {
|
|
1404
|
+
setEditColors((c) => ({ ...c, [currentTarget.key]: val }));
|
|
1405
|
+
} else {
|
|
1406
|
+
setEditToolColors((c) => ({ ...c, [currentTarget.key]: val }));
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
setEditing(false);
|
|
1410
|
+
setInputValue("");
|
|
1411
|
+
return;
|
|
1412
|
+
}
|
|
1413
|
+
if (key.backspace || key.delete) {
|
|
1414
|
+
setInputValue((v) => v.slice(0, -1));
|
|
1415
|
+
return;
|
|
1416
|
+
}
|
|
1417
|
+
if (input && input.length === 1 && /[0-9A-Fa-f#]/.test(input)) {
|
|
1418
|
+
setInputValue((v) => v + input);
|
|
1419
|
+
}
|
|
1420
|
+
return;
|
|
1421
|
+
}
|
|
1422
|
+
if (key.escape) {
|
|
1423
|
+
onCancel();
|
|
1424
|
+
return;
|
|
1425
|
+
}
|
|
1426
|
+
if (key.upArrow) {
|
|
1427
|
+
setSelectedIdx((i) => Math.max(0, i - 1));
|
|
1428
|
+
return;
|
|
1429
|
+
}
|
|
1430
|
+
if (key.downArrow) {
|
|
1431
|
+
setSelectedIdx((i) => Math.min(ALL_KEYS.length - 1, i + 1));
|
|
1432
|
+
return;
|
|
1433
|
+
}
|
|
1434
|
+
if (key.return) {
|
|
1435
|
+
setEditing(true);
|
|
1436
|
+
setInputValue(getCurrentValue(currentTarget));
|
|
1437
|
+
return;
|
|
1438
|
+
}
|
|
1439
|
+
if (input === "s" || input === "S") {
|
|
1440
|
+
onSave({ ...theme, colors: editColors, toolColors: editToolColors });
|
|
1441
|
+
}
|
|
1442
|
+
});
|
|
1443
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
|
|
1444
|
+
/* @__PURE__ */ jsxs9(Box9, { justifyContent: "space-between", marginBottom: 1, children: [
|
|
1445
|
+
/* @__PURE__ */ jsxs9(Text9, { color: colors.header, bold: true, children: [
|
|
1446
|
+
"EDIT THEME: ",
|
|
1447
|
+
theme.name
|
|
1448
|
+
] }),
|
|
1449
|
+
/* @__PURE__ */ jsx9(Text9, { color: colors.muted, children: "enter:edit s:save esc:cancel" })
|
|
1450
|
+
] }),
|
|
1451
|
+
/* @__PURE__ */ jsx9(Box9, { marginBottom: 1, children: /* @__PURE__ */ jsxs9(Text9, { color: colors.accent, bold: true, children: [
|
|
1452
|
+
" ",
|
|
1453
|
+
"COLORS"
|
|
1454
|
+
] }) }),
|
|
1455
|
+
COLOR_KEYS.map((k, i) => {
|
|
1456
|
+
const isSelected = selectedIdx === i;
|
|
1457
|
+
const value = editColors[k];
|
|
1458
|
+
const isEditingThis = editing && isSelected;
|
|
1459
|
+
return /* @__PURE__ */ jsxs9(Box9, { children: [
|
|
1460
|
+
/* @__PURE__ */ jsxs9(Text9, { color: isSelected ? colors.primary : colors.text, children: [
|
|
1461
|
+
isSelected ? "> " : " ",
|
|
1462
|
+
" ",
|
|
1463
|
+
k.padEnd(12)
|
|
1464
|
+
] }),
|
|
1465
|
+
/* @__PURE__ */ jsx9(Text9, { color: value, children: isEditingThis ? inputValue + "_" : value }),
|
|
1466
|
+
/* @__PURE__ */ jsxs9(Text9, { color: value, children: [
|
|
1467
|
+
" ",
|
|
1468
|
+
"\u2588\u2588"
|
|
1469
|
+
] })
|
|
1470
|
+
] }, k);
|
|
1471
|
+
}),
|
|
1472
|
+
/* @__PURE__ */ jsx9(Box9, { marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ jsxs9(Text9, { color: colors.accent, bold: true, children: [
|
|
1473
|
+
" ",
|
|
1474
|
+
"TOOL COLORS"
|
|
1475
|
+
] }) }),
|
|
1476
|
+
TOOL_COLOR_KEYS.map((k, i) => {
|
|
1477
|
+
const realIdx = COLOR_KEYS.length + i;
|
|
1478
|
+
const isSelected = selectedIdx === realIdx;
|
|
1479
|
+
const value = editToolColors[k];
|
|
1480
|
+
const isEditingThis = editing && isSelected;
|
|
1481
|
+
return /* @__PURE__ */ jsxs9(Box9, { children: [
|
|
1482
|
+
/* @__PURE__ */ jsxs9(Text9, { color: isSelected ? colors.primary : colors.text, children: [
|
|
1483
|
+
isSelected ? "> " : " ",
|
|
1484
|
+
" ",
|
|
1485
|
+
k.padEnd(12)
|
|
1486
|
+
] }),
|
|
1487
|
+
/* @__PURE__ */ jsx9(Text9, { color: value, children: isEditingThis ? inputValue + "_" : value }),
|
|
1488
|
+
/* @__PURE__ */ jsxs9(Text9, { color: value, children: [
|
|
1489
|
+
" ",
|
|
1490
|
+
"\u2588\u2588"
|
|
1491
|
+
] })
|
|
1492
|
+
] }, k);
|
|
1493
|
+
})
|
|
1494
|
+
] });
|
|
1495
|
+
});
|
|
1496
|
+
|
|
1497
|
+
// src/ui/components/ThemeMenu.tsx
|
|
1498
|
+
import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1499
|
+
var ThemeMenu = React10.memo(({ config, onClose }) => {
|
|
1500
|
+
const { stdout } = useStdout2();
|
|
1501
|
+
const termHeight = stdout?.rows ?? 40;
|
|
1502
|
+
const [localConfig, setLocalConfig] = useState5(() => JSON.parse(JSON.stringify(config)));
|
|
1503
|
+
const [selectedIdx, setSelectedIdx] = useState5(0);
|
|
1504
|
+
const [view, setView] = useState5("list");
|
|
1505
|
+
const [editingTheme, setEditingTheme] = useState5(null);
|
|
1506
|
+
const [nameInput, setNameInput] = useState5("");
|
|
1507
|
+
const [namingAction, setNamingAction] = useState5("copy");
|
|
1508
|
+
const [toast, setToast] = useState5("");
|
|
1509
|
+
const toastTimer = useRef2(null);
|
|
1510
|
+
const showToast = (msg) => {
|
|
1511
|
+
if (toastTimer.current) clearTimeout(toastTimer.current);
|
|
1512
|
+
setToast(msg);
|
|
1513
|
+
toastTimer.current = setTimeout(() => setToast(""), 2500);
|
|
1514
|
+
};
|
|
1515
|
+
useEffect3(
|
|
1516
|
+
() => () => {
|
|
1517
|
+
if (toastTimer.current) clearTimeout(toastTimer.current);
|
|
1518
|
+
},
|
|
1519
|
+
[]
|
|
1520
|
+
);
|
|
1521
|
+
const themes = getAllThemes(localConfig.customThemes);
|
|
1522
|
+
const previewTheme = useCallback(
|
|
1523
|
+
(idx) => {
|
|
1524
|
+
const t = themes[idx];
|
|
1525
|
+
if (t) applyTheme(t);
|
|
1526
|
+
},
|
|
1527
|
+
[themes]
|
|
1528
|
+
);
|
|
1529
|
+
const updateSelection = useCallback(
|
|
1530
|
+
(newIdx) => {
|
|
1531
|
+
setSelectedIdx(newIdx);
|
|
1532
|
+
previewTheme(newIdx);
|
|
1533
|
+
},
|
|
1534
|
+
[previewTheme]
|
|
1535
|
+
);
|
|
1536
|
+
useInput4((input, key) => {
|
|
1537
|
+
if (view === "naming") {
|
|
1538
|
+
if (key.escape) {
|
|
1539
|
+
setView("list");
|
|
1540
|
+
setNameInput("");
|
|
1541
|
+
return;
|
|
1542
|
+
}
|
|
1543
|
+
if (key.return && nameInput.trim()) {
|
|
1544
|
+
const name = nameInput.trim();
|
|
1545
|
+
if (themes.some((t) => t.name === name)) {
|
|
1546
|
+
showToast(`Theme '${name}' already exists`);
|
|
1547
|
+
return;
|
|
1548
|
+
}
|
|
1549
|
+
if (namingAction === "copy") {
|
|
1550
|
+
const source = themes[selectedIdx];
|
|
1551
|
+
const newTheme = {
|
|
1552
|
+
name,
|
|
1553
|
+
colors: { ...source.colors },
|
|
1554
|
+
toolColors: { ...source.toolColors }
|
|
1555
|
+
};
|
|
1556
|
+
setLocalConfig((c) => ({
|
|
1557
|
+
...c,
|
|
1558
|
+
theme: name,
|
|
1559
|
+
customThemes: {
|
|
1560
|
+
...c.customThemes,
|
|
1561
|
+
[name]: { name, colors: newTheme.colors, toolColors: newTheme.toolColors }
|
|
1562
|
+
}
|
|
1563
|
+
}));
|
|
1564
|
+
applyTheme(newTheme);
|
|
1565
|
+
showToast(`Created '${name}'`);
|
|
1566
|
+
} else {
|
|
1567
|
+
const old = themes[selectedIdx];
|
|
1568
|
+
const updated = { ...localConfig.customThemes };
|
|
1569
|
+
delete updated[old.name];
|
|
1570
|
+
updated[name] = { name, colors: old.colors, toolColors: old.toolColors };
|
|
1571
|
+
const newActive = localConfig.theme === old.name ? name : localConfig.theme;
|
|
1572
|
+
setLocalConfig((c) => ({ ...c, theme: newActive, customThemes: updated }));
|
|
1573
|
+
showToast(`Renamed to '${name}'`);
|
|
1574
|
+
}
|
|
1575
|
+
setView("list");
|
|
1576
|
+
setNameInput("");
|
|
1577
|
+
return;
|
|
1578
|
+
}
|
|
1579
|
+
if (key.backspace || key.delete) {
|
|
1580
|
+
setNameInput((v) => v.slice(0, -1));
|
|
1581
|
+
return;
|
|
1582
|
+
}
|
|
1583
|
+
if (input && input.length === 1) setNameInput((v) => v + input);
|
|
1584
|
+
return;
|
|
1585
|
+
}
|
|
1586
|
+
if (view === "editor" && editingTheme) return;
|
|
1587
|
+
if (key.escape) {
|
|
1588
|
+
const restoreTheme = resolveTheme(localConfig.theme, localConfig.customThemes);
|
|
1589
|
+
applyTheme(restoreTheme);
|
|
1590
|
+
onClose(localConfig);
|
|
1591
|
+
return;
|
|
1592
|
+
}
|
|
1593
|
+
if (key.upArrow) {
|
|
1594
|
+
updateSelection(Math.max(0, selectedIdx - 1));
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1597
|
+
if (key.downArrow) {
|
|
1598
|
+
updateSelection(Math.min(themes.length - 1, selectedIdx + 1));
|
|
1599
|
+
return;
|
|
1600
|
+
}
|
|
1601
|
+
if (key.return) {
|
|
1602
|
+
setLocalConfig((c) => ({ ...c, theme: themes[selectedIdx].name }));
|
|
1603
|
+
applyTheme(themes[selectedIdx]);
|
|
1604
|
+
onClose({ ...localConfig, theme: themes[selectedIdx].name });
|
|
1605
|
+
return;
|
|
1606
|
+
}
|
|
1607
|
+
if (input === "c") {
|
|
1608
|
+
setNamingAction("copy");
|
|
1609
|
+
setNameInput(themes[selectedIdx].name + "-custom");
|
|
1610
|
+
setView("naming");
|
|
1611
|
+
return;
|
|
1612
|
+
}
|
|
1613
|
+
const selected = themes[selectedIdx];
|
|
1614
|
+
if (input === "e" && !selected.builtin) {
|
|
1615
|
+
setEditingTheme(selected);
|
|
1616
|
+
setView("editor");
|
|
1617
|
+
return;
|
|
1618
|
+
}
|
|
1619
|
+
if (input === "r" && !selected.builtin) {
|
|
1620
|
+
setNamingAction("rename");
|
|
1621
|
+
setNameInput(selected.name);
|
|
1622
|
+
setView("naming");
|
|
1623
|
+
return;
|
|
1624
|
+
}
|
|
1625
|
+
if (input === "d" && !selected.builtin) {
|
|
1626
|
+
const updated = { ...localConfig.customThemes };
|
|
1627
|
+
delete updated[selected.name];
|
|
1628
|
+
const newTheme = localConfig.theme === selected.name ? "one-dark" : localConfig.theme;
|
|
1629
|
+
setLocalConfig((c) => ({ ...c, theme: newTheme, customThemes: updated }));
|
|
1630
|
+
if (localConfig.theme === selected.name) applyTheme(getDefaultTheme());
|
|
1631
|
+
setSelectedIdx((i) => Math.min(i, themes.length - 2));
|
|
1632
|
+
showToast(`Deleted '${selected.name}'`);
|
|
1633
|
+
return;
|
|
1634
|
+
}
|
|
1635
|
+
if (key.backspace || key.delete) {
|
|
1636
|
+
setLocalConfig((c) => ({ ...c, theme: "one-dark" }));
|
|
1637
|
+
applyTheme(getDefaultTheme());
|
|
1638
|
+
showToast("Restored default theme");
|
|
1639
|
+
}
|
|
1640
|
+
});
|
|
1641
|
+
const handleEditorSave = useCallback((updated) => {
|
|
1642
|
+
setLocalConfig((c) => ({
|
|
1643
|
+
...c,
|
|
1644
|
+
customThemes: {
|
|
1645
|
+
...c.customThemes,
|
|
1646
|
+
[updated.name]: { name: updated.name, colors: updated.colors, toolColors: updated.toolColors }
|
|
1647
|
+
}
|
|
1648
|
+
}));
|
|
1649
|
+
applyTheme(updated);
|
|
1650
|
+
setView("list");
|
|
1651
|
+
setEditingTheme(null);
|
|
1652
|
+
showToast(`Saved '${updated.name}'`);
|
|
1653
|
+
}, []);
|
|
1654
|
+
const handleEditorCancel = useCallback(() => {
|
|
1655
|
+
const restoreTheme = resolveTheme(localConfig.theme, localConfig.customThemes);
|
|
1656
|
+
applyTheme(restoreTheme);
|
|
1657
|
+
setView("list");
|
|
1658
|
+
setEditingTheme(null);
|
|
1659
|
+
}, [localConfig]);
|
|
1660
|
+
if (view === "editor" && editingTheme) {
|
|
1661
|
+
return /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", height: termHeight, children: /* @__PURE__ */ jsx10(Box10, { borderStyle: "round", borderColor: colors.primary, flexDirection: "column", height: termHeight, children: /* @__PURE__ */ jsx10(ThemeEditor, { theme: editingTheme, onSave: handleEditorSave, onCancel: handleEditorCancel }) }) });
|
|
1662
|
+
}
|
|
1663
|
+
const contentHeight = termHeight - 8;
|
|
1664
|
+
const halfView = Math.floor(contentHeight / 2);
|
|
1665
|
+
const scrollStart = Math.max(0, Math.min(selectedIdx - halfView, themes.length - contentHeight));
|
|
1666
|
+
const visibleThemes = themes.slice(Math.max(0, scrollStart), Math.max(0, scrollStart) + contentHeight);
|
|
1667
|
+
const visibleStartIdx = Math.max(0, scrollStart);
|
|
1668
|
+
return /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", height: termHeight, children: /* @__PURE__ */ jsxs10(
|
|
1669
|
+
Box10,
|
|
1670
|
+
{
|
|
1671
|
+
borderStyle: "round",
|
|
1672
|
+
borderColor: colors.primary,
|
|
1673
|
+
flexDirection: "column",
|
|
1674
|
+
paddingX: 2,
|
|
1675
|
+
paddingY: 1,
|
|
1676
|
+
height: termHeight,
|
|
1677
|
+
children: [
|
|
1678
|
+
/* @__PURE__ */ jsxs10(Box10, { justifyContent: "space-between", marginBottom: 1, children: [
|
|
1679
|
+
/* @__PURE__ */ jsx10(Text10, { color: colors.header, bold: true, children: "THEMES" }),
|
|
1680
|
+
/* @__PURE__ */ jsx10(Text10, { color: colors.muted, children: "enter:apply c:copy e:edit r:rename d:delete esc:back" })
|
|
1681
|
+
] }),
|
|
1682
|
+
view === "naming" && /* @__PURE__ */ jsxs10(Box10, { marginBottom: 1, children: [
|
|
1683
|
+
/* @__PURE__ */ jsxs10(Text10, { color: colors.warning, children: [
|
|
1684
|
+
namingAction === "copy" ? "New name" : "Rename to",
|
|
1685
|
+
": "
|
|
1686
|
+
] }),
|
|
1687
|
+
/* @__PURE__ */ jsxs10(Text10, { color: colors.bright, children: [
|
|
1688
|
+
nameInput,
|
|
1689
|
+
"_"
|
|
1690
|
+
] })
|
|
1691
|
+
] }),
|
|
1692
|
+
visibleThemes.map((theme, vi) => {
|
|
1693
|
+
const realIdx = visibleStartIdx + vi;
|
|
1694
|
+
const isSelected = realIdx === selectedIdx;
|
|
1695
|
+
const isActive = theme.name === localConfig.theme;
|
|
1696
|
+
return /* @__PURE__ */ jsxs10(Box10, { children: [
|
|
1697
|
+
/* @__PURE__ */ jsxs10(Text10, { color: isSelected ? colors.primary : colors.text, children: [
|
|
1698
|
+
isSelected ? "> " : " ",
|
|
1699
|
+
isActive ? "* " : " ",
|
|
1700
|
+
theme.name
|
|
1701
|
+
] }),
|
|
1702
|
+
/* @__PURE__ */ jsx10(Text10, { color: colors.muted, children: theme.builtin ? " (built-in)" : " (custom)" }),
|
|
1703
|
+
/* @__PURE__ */ jsx10(Text10, { children: " " }),
|
|
1704
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.colors.primary, children: "\u2588" }),
|
|
1705
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.colors.secondary, children: "\u2588" }),
|
|
1706
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.colors.accent, children: "\u2588" }),
|
|
1707
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.colors.warning, children: "\u2588" }),
|
|
1708
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.colors.error, children: "\u2588" })
|
|
1709
|
+
] }, theme.name);
|
|
1710
|
+
}),
|
|
1711
|
+
toast && /* @__PURE__ */ jsx10(Box10, { marginTop: 1, paddingX: 2, children: /* @__PURE__ */ jsx10(Text10, { color: colors.warning, children: toast }) })
|
|
915
1712
|
]
|
|
916
1713
|
}
|
|
917
1714
|
) });
|
|
918
1715
|
});
|
|
919
1716
|
|
|
1717
|
+
// src/ui/components/ThemePickerModal.tsx
|
|
1718
|
+
import React11, { useState as useState6, useEffect as useEffect4 } from "react";
|
|
1719
|
+
import { Box as Box11, Text as Text11, useInput as useInput5 } from "ink";
|
|
1720
|
+
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1721
|
+
var ThemePickerModal = React11.memo(({ onSelect, onSkip, onDismiss }) => {
|
|
1722
|
+
const [selectedIndex, setSelectedIndex] = useState6(0);
|
|
1723
|
+
const themes = BUILTIN_THEMES;
|
|
1724
|
+
useEffect4(() => {
|
|
1725
|
+
applyTheme(themes[selectedIndex]);
|
|
1726
|
+
}, [selectedIndex]);
|
|
1727
|
+
useInput5((input, key) => {
|
|
1728
|
+
if (key.upArrow) setSelectedIndex((i) => Math.max(0, i - 1));
|
|
1729
|
+
if (key.downArrow) setSelectedIndex((i) => Math.min(themes.length - 1, i + 1));
|
|
1730
|
+
if (key.return) onSelect(themes[selectedIndex].name);
|
|
1731
|
+
if (input === "n") onSkip();
|
|
1732
|
+
if (input === "d") onDismiss();
|
|
1733
|
+
});
|
|
1734
|
+
const renderSwatch = (theme) => {
|
|
1735
|
+
const c = theme.colors;
|
|
1736
|
+
return /* @__PURE__ */ jsxs11(Text11, { children: [
|
|
1737
|
+
/* @__PURE__ */ jsx11(Text11, { color: c.primary, children: "\u2588" }),
|
|
1738
|
+
/* @__PURE__ */ jsx11(Text11, { color: c.secondary, children: "\u2588" }),
|
|
1739
|
+
/* @__PURE__ */ jsx11(Text11, { color: c.accent, children: "\u2588" }),
|
|
1740
|
+
/* @__PURE__ */ jsx11(Text11, { color: c.warning, children: "\u2588" }),
|
|
1741
|
+
/* @__PURE__ */ jsx11(Text11, { color: c.error, children: "\u2588" })
|
|
1742
|
+
] });
|
|
1743
|
+
};
|
|
1744
|
+
return /* @__PURE__ */ jsx11(Box11, { flexDirection: "column", paddingX: 4, paddingY: 1, children: /* @__PURE__ */ jsxs11(Box11, { borderStyle: "round", borderColor: colors.primary, flexDirection: "column", paddingX: 3, paddingY: 1, children: [
|
|
1745
|
+
/* @__PURE__ */ jsx11(Box11, { marginBottom: 1, children: /* @__PURE__ */ jsx11(Text11, { color: colors.header, bold: true, children: "Choose a theme" }) }),
|
|
1746
|
+
/* @__PURE__ */ jsx11(Box11, { marginBottom: 1, children: /* @__PURE__ */ jsx11(Text11, { color: colors.text, children: "Select a theme to get started. You can change this later in settings (s)." }) }),
|
|
1747
|
+
themes.map((theme, i) => /* @__PURE__ */ jsxs11(Box11, { children: [
|
|
1748
|
+
/* @__PURE__ */ jsx11(Text11, { color: i === selectedIndex ? colors.primary : colors.muted, children: i === selectedIndex ? "> " : " " }),
|
|
1749
|
+
renderSwatch(theme),
|
|
1750
|
+
/* @__PURE__ */ jsxs11(Text11, { color: i === selectedIndex ? colors.bright : colors.text, children: [
|
|
1751
|
+
" ",
|
|
1752
|
+
theme.name
|
|
1753
|
+
] })
|
|
1754
|
+
] }, theme.name)),
|
|
1755
|
+
/* @__PURE__ */ jsx11(Box11, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx11(Text11, { color: colors.muted, children: "Enter = select | n = not now | d = don't ask again" }) })
|
|
1756
|
+
] }) });
|
|
1757
|
+
});
|
|
1758
|
+
|
|
1759
|
+
// src/ui/components/GuidedTour.tsx
|
|
1760
|
+
import React12, { useState as useState7 } from "react";
|
|
1761
|
+
import { Box as Box12, Text as Text12, useInput as useInput6 } from "ink";
|
|
1762
|
+
import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1763
|
+
var STEPS = [
|
|
1764
|
+
{
|
|
1765
|
+
title: "Session list",
|
|
1766
|
+
body: "The left panel shows all active Claude Code sessions. Use j/k to navigate and Enter to view details."
|
|
1767
|
+
},
|
|
1768
|
+
{
|
|
1769
|
+
title: "Activity feed",
|
|
1770
|
+
body: "The right panel streams tool calls in real-time. Press Tab to switch between panels."
|
|
1771
|
+
},
|
|
1772
|
+
{
|
|
1773
|
+
title: "Split view",
|
|
1774
|
+
body: "Press x to split the activity panel. Pin sessions to left (1) or right (2) panels. Press S to swap."
|
|
1775
|
+
},
|
|
1776
|
+
{
|
|
1777
|
+
title: "Filtering",
|
|
1778
|
+
body: "Press / to filter sessions or activity by keyword. Works in any panel."
|
|
1779
|
+
},
|
|
1780
|
+
{
|
|
1781
|
+
title: "Session management",
|
|
1782
|
+
body: "Press n to nickname a session, a to archive it, d to delete it. Press A to view archived sessions."
|
|
1783
|
+
},
|
|
1784
|
+
{
|
|
1785
|
+
title: "Security alerts",
|
|
1786
|
+
body: "The alert bar highlights suspicious tool calls like network access or sensitive file reads."
|
|
1787
|
+
},
|
|
1788
|
+
{
|
|
1789
|
+
title: "Themes & settings",
|
|
1790
|
+
body: "Press s to open settings. Customise keybindings, manage themes, and configure updates."
|
|
1791
|
+
}
|
|
1792
|
+
];
|
|
1793
|
+
var GuidedTour = React12.memo(({ onComplete, onSkip }) => {
|
|
1794
|
+
const [stepIndex, setStepIndex] = useState7(0);
|
|
1795
|
+
const step = STEPS[stepIndex];
|
|
1796
|
+
const isLast = stepIndex === STEPS.length - 1;
|
|
1797
|
+
useInput6((input, key) => {
|
|
1798
|
+
if (key.return || key.rightArrow) {
|
|
1799
|
+
if (isLast) onComplete();
|
|
1800
|
+
else setStepIndex((i) => i + 1);
|
|
1801
|
+
}
|
|
1802
|
+
if (key.leftArrow && stepIndex > 0) setStepIndex((i) => i - 1);
|
|
1803
|
+
if (input === "q" || key.escape) onSkip();
|
|
1804
|
+
});
|
|
1805
|
+
return /* @__PURE__ */ jsx12(Box12, { flexDirection: "column", paddingX: 4, paddingY: 1, children: /* @__PURE__ */ jsxs12(Box12, { borderStyle: "round", borderColor: colors.primary, flexDirection: "column", paddingX: 3, paddingY: 1, children: [
|
|
1806
|
+
/* @__PURE__ */ jsx12(Box12, { marginBottom: 1, children: /* @__PURE__ */ jsxs12(Text12, { color: colors.header, bold: true, children: [
|
|
1807
|
+
"Quick tour (",
|
|
1808
|
+
stepIndex + 1,
|
|
1809
|
+
"/",
|
|
1810
|
+
STEPS.length,
|
|
1811
|
+
")"
|
|
1812
|
+
] }) }),
|
|
1813
|
+
/* @__PURE__ */ jsx12(Box12, { marginBottom: 1, children: /* @__PURE__ */ jsx12(Text12, { color: colors.bright, bold: true, children: step.title }) }),
|
|
1814
|
+
/* @__PURE__ */ jsx12(Box12, { marginBottom: 1, children: /* @__PURE__ */ jsx12(Text12, { color: colors.text, children: step.body }) }),
|
|
1815
|
+
/* @__PURE__ */ jsxs12(Text12, { color: colors.muted, children: [
|
|
1816
|
+
isLast ? "Enter = finish" : "Enter/\u2192 = next",
|
|
1817
|
+
" | ",
|
|
1818
|
+
stepIndex > 0 ? "\u2190 = back | " : "",
|
|
1819
|
+
"q = skip tour"
|
|
1820
|
+
] })
|
|
1821
|
+
] }) });
|
|
1822
|
+
});
|
|
1823
|
+
|
|
1824
|
+
// src/ui/components/ConfirmModal.tsx
|
|
1825
|
+
import React13 from "react";
|
|
1826
|
+
import { Box as Box13, Text as Text13, useInput as useInput7 } from "ink";
|
|
1827
|
+
import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1828
|
+
var ConfirmModal = React13.memo(({ title, message, onConfirm, onCancel }) => {
|
|
1829
|
+
useInput7((input, key) => {
|
|
1830
|
+
if (input === "y" || input === "Y") {
|
|
1831
|
+
onConfirm();
|
|
1832
|
+
} else if (input === "n" || input === "N" || key.escape) {
|
|
1833
|
+
onCancel();
|
|
1834
|
+
}
|
|
1835
|
+
});
|
|
1836
|
+
return /* @__PURE__ */ jsxs13(
|
|
1837
|
+
Box13,
|
|
1838
|
+
{
|
|
1839
|
+
borderStyle: "round",
|
|
1840
|
+
borderColor: colors.warning,
|
|
1841
|
+
flexDirection: "column",
|
|
1842
|
+
paddingX: 2,
|
|
1843
|
+
paddingY: 1,
|
|
1844
|
+
alignSelf: "center",
|
|
1845
|
+
children: [
|
|
1846
|
+
/* @__PURE__ */ jsx13(Text13, { color: colors.warning, bold: true, children: title }),
|
|
1847
|
+
/* @__PURE__ */ jsx13(Text13, { color: colors.text, children: message }),
|
|
1848
|
+
/* @__PURE__ */ jsx13(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx13(Text13, { color: colors.muted, children: "[y] confirm [n/esc] cancel" }) })
|
|
1849
|
+
]
|
|
1850
|
+
}
|
|
1851
|
+
);
|
|
1852
|
+
});
|
|
1853
|
+
|
|
1854
|
+
// src/ui/components/SplitPanel.tsx
|
|
1855
|
+
import React14 from "react";
|
|
1856
|
+
import { Box as Box14 } from "ink";
|
|
1857
|
+
import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1858
|
+
var SplitPanel = React14.memo(
|
|
1859
|
+
({
|
|
1860
|
+
activePanel,
|
|
1861
|
+
leftSession,
|
|
1862
|
+
rightSession,
|
|
1863
|
+
leftEvents,
|
|
1864
|
+
rightEvents,
|
|
1865
|
+
leftScroll,
|
|
1866
|
+
rightScroll,
|
|
1867
|
+
leftFilter,
|
|
1868
|
+
rightFilter,
|
|
1869
|
+
leftShowDetail,
|
|
1870
|
+
rightShowDetail,
|
|
1871
|
+
height
|
|
1872
|
+
}) => {
|
|
1873
|
+
const left = leftShowDetail && leftSession ? /* @__PURE__ */ jsx14(SessionDetail, { session: leftSession, focused: activePanel === "left", height }) : /* @__PURE__ */ jsx14(
|
|
1874
|
+
ActivityFeed,
|
|
1875
|
+
{
|
|
1876
|
+
events: leftEvents,
|
|
1877
|
+
sessionSlug: leftSession?.slug ?? null,
|
|
1878
|
+
focused: activePanel === "left",
|
|
1879
|
+
height,
|
|
1880
|
+
scrollOffset: leftScroll,
|
|
1881
|
+
filter: leftFilter || void 0
|
|
1882
|
+
}
|
|
1883
|
+
);
|
|
1884
|
+
const right = rightShowDetail && rightSession ? /* @__PURE__ */ jsx14(SessionDetail, { session: rightSession, focused: activePanel === "right", height }) : /* @__PURE__ */ jsx14(
|
|
1885
|
+
ActivityFeed,
|
|
1886
|
+
{
|
|
1887
|
+
events: rightEvents,
|
|
1888
|
+
sessionSlug: rightSession?.slug ?? null,
|
|
1889
|
+
focused: activePanel === "right",
|
|
1890
|
+
height,
|
|
1891
|
+
scrollOffset: rightScroll,
|
|
1892
|
+
filter: rightFilter || void 0
|
|
1893
|
+
}
|
|
1894
|
+
);
|
|
1895
|
+
return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "row", flexGrow: 1, children: [
|
|
1896
|
+
/* @__PURE__ */ jsx14(
|
|
1897
|
+
Box14,
|
|
1898
|
+
{
|
|
1899
|
+
flexGrow: 1,
|
|
1900
|
+
borderStyle: "single",
|
|
1901
|
+
borderRight: true,
|
|
1902
|
+
borderTop: false,
|
|
1903
|
+
borderBottom: false,
|
|
1904
|
+
borderLeft: false,
|
|
1905
|
+
borderColor: colors.border,
|
|
1906
|
+
children: left
|
|
1907
|
+
}
|
|
1908
|
+
),
|
|
1909
|
+
right
|
|
1910
|
+
] });
|
|
1911
|
+
}
|
|
1912
|
+
);
|
|
1913
|
+
|
|
920
1914
|
// src/ui/hooks/useSessions.ts
|
|
921
|
-
import { useState as
|
|
1915
|
+
import { useState as useState8, useEffect as useEffect5, useCallback as useCallback2, useRef as useRef3 } from "react";
|
|
922
1916
|
var ACTIVE_POLL_MS = 1e4;
|
|
923
1917
|
var IDLE_POLL_MS = 3e4;
|
|
924
|
-
var useSessions = (allUsers, filter) => {
|
|
925
|
-
const [sessions, setSessions] =
|
|
926
|
-
const [selectedIndex, setSelectedIndex] =
|
|
927
|
-
const usageOverrides =
|
|
928
|
-
const refresh =
|
|
1918
|
+
var useSessions = (allUsers, filter, archivedIds, viewingArchive) => {
|
|
1919
|
+
const [sessions, setSessions] = useState8([]);
|
|
1920
|
+
const [selectedIndex, setSelectedIndex] = useState8(0);
|
|
1921
|
+
const usageOverrides = useRef3(/* @__PURE__ */ new Map());
|
|
1922
|
+
const refresh = useCallback2(() => {
|
|
929
1923
|
const found = discoverSessions(allUsers);
|
|
930
1924
|
const nicknames = getNicknames();
|
|
931
1925
|
const enriched = found.map((s) => {
|
|
@@ -942,31 +1936,40 @@ var useSessions = (allUsers, filter) => {
|
|
|
942
1936
|
};
|
|
943
1937
|
});
|
|
944
1938
|
let filtered = enriched;
|
|
1939
|
+
if (archivedIds && archivedIds.size > 0) {
|
|
1940
|
+
if (viewingArchive) {
|
|
1941
|
+
filtered = filtered.filter((s) => archivedIds.has(s.sessionId));
|
|
1942
|
+
} else {
|
|
1943
|
+
filtered = filtered.filter((s) => !archivedIds.has(s.sessionId));
|
|
1944
|
+
}
|
|
1945
|
+
} else if (viewingArchive) {
|
|
1946
|
+
filtered = [];
|
|
1947
|
+
}
|
|
945
1948
|
if (filter) {
|
|
946
1949
|
const lower = filter.toLowerCase();
|
|
947
|
-
filtered =
|
|
1950
|
+
filtered = filtered.filter(
|
|
948
1951
|
(s) => s.slug.toLowerCase().includes(lower) || s.nickname?.toLowerCase().includes(lower) || s.project.toLowerCase().includes(lower) || s.model.toLowerCase().includes(lower)
|
|
949
1952
|
);
|
|
950
1953
|
}
|
|
951
1954
|
setSessions(filtered);
|
|
952
|
-
}, [allUsers, filter]);
|
|
953
|
-
|
|
1955
|
+
}, [allUsers, filter, archivedIds, viewingArchive]);
|
|
1956
|
+
useEffect5(() => {
|
|
954
1957
|
refresh();
|
|
955
1958
|
const pollMs = sessions.length > 0 ? ACTIVE_POLL_MS : IDLE_POLL_MS;
|
|
956
1959
|
const interval = setInterval(refresh, pollMs);
|
|
957
1960
|
return () => clearInterval(interval);
|
|
958
1961
|
}, [refresh, sessions.length > 0]);
|
|
959
1962
|
const selectedSession = sessions[selectedIndex] ?? null;
|
|
960
|
-
const selectNext =
|
|
1963
|
+
const selectNext = useCallback2(() => {
|
|
961
1964
|
setSelectedIndex((i) => Math.min(i + 1, Math.max(0, sessions.length - 1)));
|
|
962
1965
|
}, [sessions.length]);
|
|
963
|
-
const selectPrev =
|
|
1966
|
+
const selectPrev = useCallback2(() => {
|
|
964
1967
|
setSelectedIndex((i) => Math.max(i - 1, 0));
|
|
965
1968
|
}, []);
|
|
966
|
-
const selectIndex =
|
|
1969
|
+
const selectIndex = useCallback2((i) => {
|
|
967
1970
|
setSelectedIndex(i);
|
|
968
1971
|
}, []);
|
|
969
|
-
const addUsage =
|
|
1972
|
+
const addUsage = useCallback2((sessionId, usage) => {
|
|
970
1973
|
const existing = usageOverrides.current.get(sessionId);
|
|
971
1974
|
if (existing) {
|
|
972
1975
|
usageOverrides.current.set(sessionId, {
|
|
@@ -983,12 +1986,12 @@ var useSessions = (allUsers, filter) => {
|
|
|
983
1986
|
};
|
|
984
1987
|
|
|
985
1988
|
// src/ui/hooks/useActivityStream.ts
|
|
986
|
-
import { useState as
|
|
1989
|
+
import { useState as useState9, useEffect as useEffect6, useRef as useRef4 } from "react";
|
|
987
1990
|
var MAX_EVENTS = 200;
|
|
988
1991
|
var useActivityStream = (session, allUsers) => {
|
|
989
|
-
const [events, setEvents] =
|
|
990
|
-
const watcherRef =
|
|
991
|
-
|
|
1992
|
+
const [events, setEvents] = useState9([]);
|
|
1993
|
+
const watcherRef = useRef4(null);
|
|
1994
|
+
useEffect6(() => {
|
|
992
1995
|
setEvents([]);
|
|
993
1996
|
if (!session) return;
|
|
994
1997
|
const existingCalls = [];
|
|
@@ -1014,8 +2017,19 @@ var useActivityStream = (session, allUsers) => {
|
|
|
1014
2017
|
return events;
|
|
1015
2018
|
};
|
|
1016
2019
|
|
|
2020
|
+
// src/ui/hooks/useFilteredEvents.ts
|
|
2021
|
+
import { useMemo as useMemo2 } from "react";
|
|
2022
|
+
var applyFilter = (events, filter) => {
|
|
2023
|
+
if (!filter) return events;
|
|
2024
|
+
const lower = filter.toLowerCase();
|
|
2025
|
+
return events.filter(
|
|
2026
|
+
(e) => e.toolName.toLowerCase().includes(lower) || JSON.stringify(e.toolInput).toLowerCase().includes(lower)
|
|
2027
|
+
);
|
|
2028
|
+
};
|
|
2029
|
+
var useFilteredEvents = (rawEvents, filter) => useMemo2(() => applyFilter(rawEvents, filter), [rawEvents, filter]);
|
|
2030
|
+
|
|
1017
2031
|
// src/ui/hooks/useAlerts.ts
|
|
1018
|
-
import { useState as
|
|
2032
|
+
import { useState as useState10, useEffect as useEffect7, useRef as useRef5 } from "react";
|
|
1019
2033
|
|
|
1020
2034
|
// src/notifications.ts
|
|
1021
2035
|
import { exec as exec2 } from "child_process";
|
|
@@ -1083,11 +2097,11 @@ var AlertLogger = class {
|
|
|
1083
2097
|
// src/ui/hooks/useAlerts.ts
|
|
1084
2098
|
var MAX_ALERTS = 100;
|
|
1085
2099
|
var useAlerts = (enabled, alertLevel, allUsers, config) => {
|
|
1086
|
-
const [alerts, setAlerts] =
|
|
1087
|
-
const engineRef =
|
|
1088
|
-
const watcherRef =
|
|
1089
|
-
const loggerRef =
|
|
1090
|
-
|
|
2100
|
+
const [alerts, setAlerts] = useState10([]);
|
|
2101
|
+
const engineRef = useRef5(new SecurityEngine(alertLevel));
|
|
2102
|
+
const watcherRef = useRef5(null);
|
|
2103
|
+
const loggerRef = useRef5(null);
|
|
2104
|
+
useEffect7(() => {
|
|
1091
2105
|
if (!enabled) return;
|
|
1092
2106
|
engineRef.current = new SecurityEngine(alertLevel);
|
|
1093
2107
|
if (config?.alerts.enabled) {
|
|
@@ -1123,27 +2137,27 @@ var useAlerts = (enabled, alertLevel, allUsers, config) => {
|
|
|
1123
2137
|
};
|
|
1124
2138
|
|
|
1125
2139
|
// src/ui/hooks/useTextInput.ts
|
|
1126
|
-
import { useState as
|
|
2140
|
+
import { useState as useState11, useCallback as useCallback3 } from "react";
|
|
1127
2141
|
var useTextInput = (onConfirm, onCancel) => {
|
|
1128
|
-
const [value, setValue] =
|
|
1129
|
-
const [isActive, setIsActive] =
|
|
1130
|
-
const start =
|
|
2142
|
+
const [value, setValue] = useState11("");
|
|
2143
|
+
const [isActive, setIsActive] = useState11(false);
|
|
2144
|
+
const start = useCallback3((initial = "") => {
|
|
1131
2145
|
setValue(initial);
|
|
1132
2146
|
setIsActive(true);
|
|
1133
2147
|
}, []);
|
|
1134
|
-
const cancel =
|
|
2148
|
+
const cancel = useCallback3(() => {
|
|
1135
2149
|
setValue("");
|
|
1136
2150
|
setIsActive(false);
|
|
1137
2151
|
onCancel?.();
|
|
1138
2152
|
}, [onCancel]);
|
|
1139
|
-
const confirm =
|
|
2153
|
+
const confirm = useCallback3(() => {
|
|
1140
2154
|
const result = value;
|
|
1141
2155
|
setIsActive(false);
|
|
1142
2156
|
setValue("");
|
|
1143
2157
|
onConfirm?.(result);
|
|
1144
2158
|
return result;
|
|
1145
2159
|
}, [value, onConfirm]);
|
|
1146
|
-
const handleInput =
|
|
2160
|
+
const handleInput = useCallback3(
|
|
1147
2161
|
(input, key) => {
|
|
1148
2162
|
if (!isActive) return false;
|
|
1149
2163
|
if (key.escape) {
|
|
@@ -1175,34 +2189,299 @@ var useTextInput = (onConfirm, onCancel) => {
|
|
|
1175
2189
|
return { value, isActive, start, cancel, confirm, handleInput };
|
|
1176
2190
|
};
|
|
1177
2191
|
|
|
1178
|
-
// src/ui/
|
|
1179
|
-
import {
|
|
2192
|
+
// src/ui/hooks/useKeyHandler.ts
|
|
2193
|
+
import { useInput as useInput8 } from "ink";
|
|
1180
2194
|
var matchKey = (binding, input, key) => {
|
|
1181
2195
|
if (binding === "tab") return Boolean(key.tab);
|
|
1182
2196
|
if (binding === "shift+tab") return Boolean(key.shift && key.tab);
|
|
1183
2197
|
if (binding === "enter") return Boolean(key.return);
|
|
1184
2198
|
return input === binding;
|
|
1185
2199
|
};
|
|
2200
|
+
var useKeyHandler = (deps) => {
|
|
2201
|
+
const d = deps;
|
|
2202
|
+
useInput8((input, key) => {
|
|
2203
|
+
if (d.showSetup || d.showSettings || d.confirmAction) return;
|
|
2204
|
+
if (d.inputMode === "nickname") {
|
|
2205
|
+
d.nicknameInput.handleInput(input, key);
|
|
2206
|
+
return;
|
|
2207
|
+
}
|
|
2208
|
+
if (d.inputMode === "filter") {
|
|
2209
|
+
if (key.escape) {
|
|
2210
|
+
if (d.activePanel === "sessions") d.setFilter("");
|
|
2211
|
+
else if (d.activePanel === "left") d.setLeftFilter("");
|
|
2212
|
+
else if (d.activePanel === "right") d.setRightFilter("");
|
|
2213
|
+
else d.setActivityFilter("");
|
|
2214
|
+
d.setInputMode("normal");
|
|
2215
|
+
d.filterInput.cancel();
|
|
2216
|
+
return;
|
|
2217
|
+
}
|
|
2218
|
+
d.filterInput.handleInput(input, key);
|
|
2219
|
+
return;
|
|
2220
|
+
}
|
|
2221
|
+
if (matchKey(d.kb.quit, input, key)) {
|
|
2222
|
+
d.exit();
|
|
2223
|
+
return;
|
|
2224
|
+
}
|
|
2225
|
+
if (d.showDetail && !d.splitMode) {
|
|
2226
|
+
if (key.escape || key.return || key.leftArrow) d.setShowDetail(false);
|
|
2227
|
+
return;
|
|
2228
|
+
}
|
|
2229
|
+
if (d.splitMode && (d.leftShowDetail || d.rightShowDetail)) {
|
|
2230
|
+
if (key.escape || key.return) {
|
|
2231
|
+
if (d.activePanel === "left") d.setLeftShowDetail(false);
|
|
2232
|
+
else if (d.activePanel === "right") d.setRightShowDetail(false);
|
|
2233
|
+
else {
|
|
2234
|
+
d.setLeftShowDetail(false);
|
|
2235
|
+
d.setRightShowDetail(false);
|
|
2236
|
+
}
|
|
2237
|
+
return;
|
|
2238
|
+
}
|
|
2239
|
+
if (key.leftArrow) {
|
|
2240
|
+
if (d.activePanel === "left") d.setLeftShowDetail(false);
|
|
2241
|
+
else if (d.activePanel === "right") d.setRightShowDetail(false);
|
|
2242
|
+
return;
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
if (matchKey(d.kb.split, input, key)) {
|
|
2246
|
+
if (d.splitMode) {
|
|
2247
|
+
d.clearSplitState();
|
|
2248
|
+
} else {
|
|
2249
|
+
d.setSplitMode(true);
|
|
2250
|
+
d.setLeftSession(d.selectedSession);
|
|
2251
|
+
d.setActivePanel("left");
|
|
2252
|
+
}
|
|
2253
|
+
return;
|
|
2254
|
+
}
|
|
2255
|
+
if (d.splitMode && d.activePanel === "sessions" && d.selectedSession) {
|
|
2256
|
+
if (matchKey(d.kb.pinLeft, input, key)) {
|
|
2257
|
+
d.resetPanel("left");
|
|
2258
|
+
d.setLeftSession(d.selectedSession);
|
|
2259
|
+
return;
|
|
2260
|
+
}
|
|
2261
|
+
if (matchKey(d.kb.pinRight, input, key)) {
|
|
2262
|
+
d.resetPanel("right");
|
|
2263
|
+
d.setRightSession(d.selectedSession);
|
|
2264
|
+
return;
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
if (d.splitMode && matchKey(d.kb.swapPanels, input, key)) {
|
|
2268
|
+
const tmpLs = d.leftSession;
|
|
2269
|
+
const tmpRs = d.rightSession;
|
|
2270
|
+
d.setLeftSession(tmpRs);
|
|
2271
|
+
d.setRightSession(tmpLs);
|
|
2272
|
+
const tmpScroll = d.leftScroll;
|
|
2273
|
+
d.setLeftScroll(d.rightScroll);
|
|
2274
|
+
d.setRightScroll(tmpScroll);
|
|
2275
|
+
const tmpFilt = d.leftFilter;
|
|
2276
|
+
d.setLeftFilter(d.rightFilter);
|
|
2277
|
+
d.setRightFilter(tmpFilt);
|
|
2278
|
+
const tmpDetail = d.leftShowDetail;
|
|
2279
|
+
d.setLeftShowDetail(d.rightShowDetail);
|
|
2280
|
+
d.setRightShowDetail(tmpDetail);
|
|
2281
|
+
return;
|
|
2282
|
+
}
|
|
2283
|
+
if (d.splitMode && matchKey(d.kb.closePanel, input, key)) {
|
|
2284
|
+
if (d.activePanel === "left") {
|
|
2285
|
+
d.resetPanel("left");
|
|
2286
|
+
if (!d.rightSession) d.clearSplitState();
|
|
2287
|
+
} else if (d.activePanel === "right") {
|
|
2288
|
+
d.resetPanel("right");
|
|
2289
|
+
if (!d.leftSession) d.clearSplitState();
|
|
2290
|
+
}
|
|
2291
|
+
return;
|
|
2292
|
+
}
|
|
2293
|
+
if (matchKey(d.kb.detail, input, key) && d.selectedSession) {
|
|
2294
|
+
if (d.splitMode) {
|
|
2295
|
+
if (d.activePanel === "left") {
|
|
2296
|
+
d.setLeftShowDetail((v) => !v);
|
|
2297
|
+
return;
|
|
2298
|
+
}
|
|
2299
|
+
if (d.activePanel === "right") {
|
|
2300
|
+
d.setRightShowDetail((v) => !v);
|
|
2301
|
+
return;
|
|
2302
|
+
}
|
|
2303
|
+
if (d.activePanel === "sessions") {
|
|
2304
|
+
if (!d.leftSession) {
|
|
2305
|
+
d.setLeftSession(d.selectedSession);
|
|
2306
|
+
d.setLeftShowDetail(true);
|
|
2307
|
+
d.setActivePanel("left");
|
|
2308
|
+
} else if (!d.rightSession) {
|
|
2309
|
+
d.setRightSession(d.selectedSession);
|
|
2310
|
+
d.setRightShowDetail(true);
|
|
2311
|
+
d.setActivePanel("right");
|
|
2312
|
+
}
|
|
2313
|
+
return;
|
|
2314
|
+
}
|
|
2315
|
+
} else if (d.activePanel === "sessions") {
|
|
2316
|
+
d.setShowDetail(true);
|
|
2317
|
+
return;
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2320
|
+
if (matchKey(d.kb.panelNext, input, key) || key.rightArrow) {
|
|
2321
|
+
d.switchPanel("next");
|
|
2322
|
+
return;
|
|
2323
|
+
}
|
|
2324
|
+
if (matchKey(d.kb.panelPrev, input, key) || key.leftArrow) {
|
|
2325
|
+
d.switchPanel("prev");
|
|
2326
|
+
return;
|
|
2327
|
+
}
|
|
2328
|
+
if (matchKey(d.kb.nickname, input, key) && d.selectedSession) {
|
|
2329
|
+
d.setInputMode("nickname");
|
|
2330
|
+
d.nicknameInput.start(d.selectedSession.nickname || "");
|
|
2331
|
+
return;
|
|
2332
|
+
}
|
|
2333
|
+
if (matchKey(d.kb.clearNickname, input, key) && d.selectedSession) {
|
|
2334
|
+
d.onClearNickname(d.selectedSession.sessionId);
|
|
2335
|
+
return;
|
|
2336
|
+
}
|
|
2337
|
+
if (matchKey(d.kb.filter, input, key)) {
|
|
2338
|
+
d.setInputMode("filter");
|
|
2339
|
+
d.filterInput.start(d.getActiveFilter());
|
|
2340
|
+
return;
|
|
2341
|
+
}
|
|
2342
|
+
if (key.escape) {
|
|
2343
|
+
if (d.activePanel === "sessions" && d.filter) {
|
|
2344
|
+
d.setFilter("");
|
|
2345
|
+
return;
|
|
2346
|
+
}
|
|
2347
|
+
if (d.activePanel === "activity" && d.activityFilter) {
|
|
2348
|
+
d.setActivityFilter("");
|
|
2349
|
+
return;
|
|
2350
|
+
}
|
|
2351
|
+
if (d.activePanel === "left" && d.leftFilter) {
|
|
2352
|
+
d.setLeftFilter("");
|
|
2353
|
+
return;
|
|
2354
|
+
}
|
|
2355
|
+
if (d.activePanel === "right" && d.rightFilter) {
|
|
2356
|
+
d.setRightFilter("");
|
|
2357
|
+
return;
|
|
2358
|
+
}
|
|
2359
|
+
return;
|
|
2360
|
+
}
|
|
2361
|
+
if (matchKey(d.kb.settings, input, key)) {
|
|
2362
|
+
d.setShowSettings(true);
|
|
2363
|
+
return;
|
|
2364
|
+
}
|
|
2365
|
+
if (matchKey(d.kb.viewArchive, input, key)) {
|
|
2366
|
+
d.setViewingArchive((v) => !v);
|
|
2367
|
+
return;
|
|
2368
|
+
}
|
|
2369
|
+
if (matchKey(d.kb.archive, input, key) && d.selectedSession) {
|
|
2370
|
+
if (d.viewingArchive) d.onUnarchive(d.selectedSession.sessionId);
|
|
2371
|
+
else d.onArchive(d.selectedSession.sessionId);
|
|
2372
|
+
return;
|
|
2373
|
+
}
|
|
2374
|
+
if (matchKey(d.kb.delete, input, key) && d.selectedSession) {
|
|
2375
|
+
d.onDelete(d.selectedSession);
|
|
2376
|
+
return;
|
|
2377
|
+
}
|
|
2378
|
+
if (matchKey(d.kb.update, input, key) && d.updateInfo?.available) {
|
|
2379
|
+
d.onUpdate();
|
|
2380
|
+
return;
|
|
2381
|
+
}
|
|
2382
|
+
if (d.activePanel === "sessions") {
|
|
2383
|
+
if (matchKey(d.kb.navDown, input, key) || key.downArrow) d.selectNext();
|
|
2384
|
+
if (matchKey(d.kb.navUp, input, key) || key.upArrow) d.selectPrev();
|
|
2385
|
+
}
|
|
2386
|
+
if (d.activePanel === "activity") {
|
|
2387
|
+
if (matchKey(d.kb.navUp, input, key) || key.upArrow) d.setActivityScroll((s) => Math.min(s + 1, d.maxScroll));
|
|
2388
|
+
if (matchKey(d.kb.navDown, input, key) || key.downArrow) d.setActivityScroll((s) => Math.max(s - 1, 0));
|
|
2389
|
+
if (matchKey(d.kb.scrollBottom, input, key)) d.setActivityScroll(0);
|
|
2390
|
+
if (matchKey(d.kb.scrollTop, input, key)) d.setActivityScroll(d.maxScroll);
|
|
2391
|
+
}
|
|
2392
|
+
if (d.activePanel === "left") {
|
|
2393
|
+
if (matchKey(d.kb.navUp, input, key) || key.upArrow) d.setLeftScroll((s) => Math.min(s + 1, d.leftMaxScroll));
|
|
2394
|
+
if (matchKey(d.kb.navDown, input, key) || key.downArrow) d.setLeftScroll((s) => Math.max(s - 1, 0));
|
|
2395
|
+
if (matchKey(d.kb.scrollBottom, input, key)) d.setLeftScroll(0);
|
|
2396
|
+
if (matchKey(d.kb.scrollTop, input, key)) d.setLeftScroll(d.leftMaxScroll);
|
|
2397
|
+
}
|
|
2398
|
+
if (d.activePanel === "right") {
|
|
2399
|
+
if (matchKey(d.kb.navUp, input, key) || key.upArrow) d.setRightScroll((s) => Math.min(s + 1, d.rightMaxScroll));
|
|
2400
|
+
if (matchKey(d.kb.navDown, input, key) || key.downArrow) d.setRightScroll((s) => Math.max(s - 1, 0));
|
|
2401
|
+
if (matchKey(d.kb.scrollBottom, input, key)) d.setRightScroll(0);
|
|
2402
|
+
if (matchKey(d.kb.scrollTop, input, key)) d.setRightScroll(d.rightMaxScroll);
|
|
2403
|
+
}
|
|
2404
|
+
});
|
|
2405
|
+
};
|
|
2406
|
+
|
|
2407
|
+
// src/ui/hooks/useUpdateChecker.ts
|
|
2408
|
+
import { useState as useState12, useEffect as useEffect8 } from "react";
|
|
2409
|
+
var useUpdateChecker = (disabled, checkOnLaunch, checkInterval) => {
|
|
2410
|
+
const [updateInfo, setUpdateInfo] = useState12(null);
|
|
2411
|
+
useEffect8(() => {
|
|
2412
|
+
if (disabled || !checkOnLaunch) return;
|
|
2413
|
+
try {
|
|
2414
|
+
const i = checkForUpdate();
|
|
2415
|
+
if (i.available) setUpdateInfo(i);
|
|
2416
|
+
} catch {
|
|
2417
|
+
}
|
|
2418
|
+
const iv = setInterval(() => {
|
|
2419
|
+
try {
|
|
2420
|
+
const i = checkForUpdate();
|
|
2421
|
+
if (i.available) setUpdateInfo(i);
|
|
2422
|
+
} catch {
|
|
2423
|
+
}
|
|
2424
|
+
}, checkInterval);
|
|
2425
|
+
return () => clearInterval(iv);
|
|
2426
|
+
}, []);
|
|
2427
|
+
return updateInfo;
|
|
2428
|
+
};
|
|
2429
|
+
|
|
2430
|
+
// src/ui/App.tsx
|
|
2431
|
+
import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1186
2432
|
var App = ({ options, config: initialConfig, version, firstRun }) => {
|
|
1187
2433
|
const { exit } = useApp();
|
|
1188
|
-
const { stdout } =
|
|
2434
|
+
const { stdout } = useStdout3();
|
|
1189
2435
|
const termHeight = stdout?.rows ?? 40;
|
|
1190
|
-
const [liveConfig, setLiveConfig] =
|
|
2436
|
+
const [liveConfig, setLiveConfig] = useState13(initialConfig);
|
|
1191
2437
|
const kb = liveConfig.keybindings;
|
|
1192
|
-
const [activePanel, setActivePanel] =
|
|
1193
|
-
const [activityScroll, setActivityScroll] =
|
|
1194
|
-
const [inputMode, setInputMode] =
|
|
1195
|
-
const [showSetup, setShowSetup] =
|
|
1196
|
-
const [
|
|
1197
|
-
const [
|
|
1198
|
-
const [
|
|
1199
|
-
const [
|
|
1200
|
-
const [
|
|
2438
|
+
const [activePanel, setActivePanel] = useState13("sessions");
|
|
2439
|
+
const [activityScroll, setActivityScroll] = useState13(0);
|
|
2440
|
+
const [inputMode, setInputMode] = useState13("normal");
|
|
2441
|
+
const [showSetup, setShowSetup] = useState13(firstRun);
|
|
2442
|
+
const [showThemePicker, setShowThemePicker] = useState13(false);
|
|
2443
|
+
const [showTour, setShowTour] = useState13(false);
|
|
2444
|
+
const [filter, setFilter] = useState13("");
|
|
2445
|
+
const [activityFilter, setActivityFilter] = useState13("");
|
|
2446
|
+
const [updateStatus, setUpdateStatus] = useState13("");
|
|
2447
|
+
const [showDetail, setShowDetail] = useState13(false);
|
|
2448
|
+
const [showSettings, setShowSettings] = useState13(false);
|
|
2449
|
+
const [showThemeMenu, setShowThemeMenu] = useState13(false);
|
|
2450
|
+
const [viewingArchive, setViewingArchive] = useState13(false);
|
|
2451
|
+
const [confirmAction, setConfirmAction] = useState13(
|
|
2452
|
+
null
|
|
2453
|
+
);
|
|
2454
|
+
const [archivedIds, setArchivedIds] = useState13(() => new Set(Object.keys(getArchived())));
|
|
2455
|
+
const [splitMode, setSplitMode] = useState13(false);
|
|
2456
|
+
const [leftSession, setLeftSession] = useState13(null);
|
|
2457
|
+
const [rightSession, setRightSession] = useState13(null);
|
|
2458
|
+
const [leftScroll, setLeftScroll] = useState13(0);
|
|
2459
|
+
const [rightScroll, setRightScroll] = useState13(0);
|
|
2460
|
+
const [leftFilter, setLeftFilter] = useState13("");
|
|
2461
|
+
const [rightFilter, setRightFilter] = useState13("");
|
|
2462
|
+
const [leftShowDetail, setLeftShowDetail] = useState13(false);
|
|
2463
|
+
const [rightShowDetail, setRightShowDetail] = useState13(false);
|
|
2464
|
+
const refreshArchived = useCallback4(() => setArchivedIds(new Set(Object.keys(getArchived()))), []);
|
|
2465
|
+
const updateInfo = useUpdateChecker(
|
|
2466
|
+
options.noUpdates,
|
|
2467
|
+
liveConfig.updates.checkOnLaunch,
|
|
2468
|
+
liveConfig.updates.checkInterval
|
|
2469
|
+
);
|
|
2470
|
+
useEffect9(() => {
|
|
2471
|
+
applyTheme(resolveTheme(liveConfig.theme, liveConfig.customThemes));
|
|
2472
|
+
}, [liveConfig.theme, liveConfig.customThemes]);
|
|
1201
2473
|
const { sessions, selectedSession, selectedIndex, selectNext, selectPrev, refresh } = useSessions(
|
|
1202
2474
|
options.allUsers,
|
|
1203
|
-
filter || void 0
|
|
2475
|
+
filter || void 0,
|
|
2476
|
+
archivedIds,
|
|
2477
|
+
viewingArchive
|
|
1204
2478
|
);
|
|
1205
|
-
const
|
|
2479
|
+
const rawEvents = useActivityStream(splitMode ? null : selectedSession, options.allUsers);
|
|
2480
|
+
const leftRawEvents = useActivityStream(splitMode ? leftSession : null, options.allUsers);
|
|
2481
|
+
const rightRawEvents = useActivityStream(splitMode ? rightSession : null, options.allUsers);
|
|
2482
|
+
const events = useFilteredEvents(rawEvents, activityFilter);
|
|
2483
|
+
const leftEvents = useFilteredEvents(leftRawEvents, leftFilter);
|
|
2484
|
+
const rightEvents = useFilteredEvents(rightRawEvents, rightFilter);
|
|
1206
2485
|
const { alerts } = useAlerts(!options.noSecurity, options.alertLevel, options.allUsers, liveConfig);
|
|
1207
2486
|
const nicknameInput = useTextInput(
|
|
1208
2487
|
(value) => {
|
|
@@ -1216,43 +2495,43 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
|
|
|
1216
2495
|
);
|
|
1217
2496
|
const filterInput = useTextInput(
|
|
1218
2497
|
(value) => {
|
|
1219
|
-
setFilter(value);
|
|
2498
|
+
if (activePanel === "sessions") setFilter(value);
|
|
2499
|
+
else if (activePanel === "left") setLeftFilter(value);
|
|
2500
|
+
else if (activePanel === "right") setRightFilter(value);
|
|
2501
|
+
else setActivityFilter(value);
|
|
1220
2502
|
setInputMode("normal");
|
|
1221
2503
|
},
|
|
1222
2504
|
() => {
|
|
1223
|
-
setFilter("");
|
|
2505
|
+
if (activePanel === "sessions") setFilter("");
|
|
2506
|
+
else if (activePanel === "left") setLeftFilter("");
|
|
2507
|
+
else if (activePanel === "right") setRightFilter("");
|
|
2508
|
+
else setActivityFilter("");
|
|
1224
2509
|
setInputMode("normal");
|
|
1225
2510
|
}
|
|
1226
2511
|
);
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
const i = checkForUpdate();
|
|
1231
|
-
if (i.available) setUpdateInfo(i);
|
|
1232
|
-
} catch {
|
|
1233
|
-
}
|
|
1234
|
-
const iv = setInterval(() => {
|
|
1235
|
-
try {
|
|
1236
|
-
const i = checkForUpdate();
|
|
1237
|
-
if (i.available) setUpdateInfo(i);
|
|
1238
|
-
} catch {
|
|
1239
|
-
}
|
|
1240
|
-
}, liveConfig.updates.checkInterval);
|
|
1241
|
-
return () => clearInterval(iv);
|
|
2512
|
+
useEffect9(() => {
|
|
2513
|
+
purgeExpiredArchives();
|
|
2514
|
+
refreshArchived();
|
|
1242
2515
|
}, []);
|
|
2516
|
+
useEffect9(() => {
|
|
2517
|
+
setActivityScroll(0);
|
|
2518
|
+
}, [selectedSession?.sessionId]);
|
|
1243
2519
|
const alertHeight = options.noSecurity ? 0 : 6;
|
|
1244
2520
|
const mainHeight = termHeight - 3 - alertHeight - 1 - (inputMode !== "normal" ? 1 : 0);
|
|
1245
2521
|
const viewportRows = mainHeight - 2;
|
|
1246
|
-
const
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
}, [selectedSession?.sessionId]);
|
|
1250
|
-
const handleSettingsClose = useCallback3((updatedConfig) => {
|
|
1251
|
-
setLiveConfig(updatedConfig);
|
|
1252
|
-
saveConfig(updatedConfig);
|
|
2522
|
+
const handleSettingsClose = useCallback4((c) => {
|
|
2523
|
+
setLiveConfig(c);
|
|
2524
|
+
saveConfig(c);
|
|
1253
2525
|
setShowSettings(false);
|
|
1254
2526
|
}, []);
|
|
1255
|
-
const
|
|
2527
|
+
const handleThemeMenuClose = useCallback4((c) => {
|
|
2528
|
+
setLiveConfig(c);
|
|
2529
|
+
saveConfig(c);
|
|
2530
|
+
setShowThemeMenu(false);
|
|
2531
|
+
setShowSettings(true);
|
|
2532
|
+
}, []);
|
|
2533
|
+
const handleOpenThemeMenu = useCallback4(() => setShowThemeMenu(true), []);
|
|
2534
|
+
const handleSetupComplete = useCallback4(
|
|
1256
2535
|
(results) => {
|
|
1257
2536
|
const nc = { ...liveConfig };
|
|
1258
2537
|
const [hc, mc] = results;
|
|
@@ -1272,146 +2551,283 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
|
|
|
1272
2551
|
} else if (mc === "dismiss") nc.prompts.mcp = "dismissed";
|
|
1273
2552
|
saveConfig(nc);
|
|
1274
2553
|
setShowSetup(false);
|
|
2554
|
+
if (nc.prompts.theme === "pending") setShowThemePicker(true);
|
|
2555
|
+
else if (nc.prompts.tour === "pending") setShowTour(true);
|
|
2556
|
+
},
|
|
2557
|
+
[liveConfig]
|
|
2558
|
+
);
|
|
2559
|
+
const handleThemePickerSelect = useCallback4(
|
|
2560
|
+
(themeName) => {
|
|
2561
|
+
const nc = { ...liveConfig, theme: themeName, prompts: { ...liveConfig.prompts, theme: "done" } };
|
|
2562
|
+
setLiveConfig(nc);
|
|
2563
|
+
saveConfig(nc);
|
|
2564
|
+
setShowThemePicker(false);
|
|
2565
|
+
if (nc.prompts.tour === "pending") setShowTour(true);
|
|
1275
2566
|
},
|
|
1276
2567
|
[liveConfig]
|
|
1277
2568
|
);
|
|
1278
|
-
const
|
|
1279
|
-
|
|
2569
|
+
const handleThemePickerSkip = useCallback4(() => {
|
|
2570
|
+
setShowThemePicker(false);
|
|
2571
|
+
if (liveConfig.prompts.tour === "pending") setShowTour(true);
|
|
2572
|
+
}, [liveConfig]);
|
|
2573
|
+
const handleThemePickerDismiss = useCallback4(() => {
|
|
2574
|
+
const nc = { ...liveConfig, prompts: { ...liveConfig.prompts, theme: "dismissed" } };
|
|
2575
|
+
setLiveConfig(nc);
|
|
2576
|
+
saveConfig(nc);
|
|
2577
|
+
setShowThemePicker(false);
|
|
2578
|
+
if (nc.prompts.tour === "pending") setShowTour(true);
|
|
2579
|
+
}, [liveConfig]);
|
|
2580
|
+
const handleTourComplete = useCallback4(() => {
|
|
2581
|
+
const nc = { ...liveConfig, prompts: { ...liveConfig.prompts, tour: "done" } };
|
|
2582
|
+
setLiveConfig(nc);
|
|
2583
|
+
saveConfig(nc);
|
|
2584
|
+
setShowTour(false);
|
|
2585
|
+
}, [liveConfig]);
|
|
2586
|
+
const handleTourSkip = useCallback4(() => {
|
|
2587
|
+
setShowTour(false);
|
|
1280
2588
|
}, []);
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
return;
|
|
1293
|
-
}
|
|
1294
|
-
filterInput.handleInput(input, key);
|
|
1295
|
-
return;
|
|
1296
|
-
}
|
|
1297
|
-
if (matchKey(kb.quit, input, key)) {
|
|
1298
|
-
exit();
|
|
1299
|
-
return;
|
|
1300
|
-
}
|
|
1301
|
-
if (showDetail) {
|
|
1302
|
-
if (key.escape || key.return || key.leftArrow) {
|
|
1303
|
-
setShowDetail(false);
|
|
2589
|
+
const switchPanel = useCallback4(
|
|
2590
|
+
(dir) => {
|
|
2591
|
+
if (splitMode) {
|
|
2592
|
+
const order = ["sessions", "left", "right"];
|
|
2593
|
+
setActivePanel((p) => {
|
|
2594
|
+
const idx = order.indexOf(p);
|
|
2595
|
+
if (idx === -1) return "sessions";
|
|
2596
|
+
return dir === "next" ? order[(idx + 1) % order.length] : order[(idx - 1 + order.length) % order.length];
|
|
2597
|
+
});
|
|
2598
|
+
} else {
|
|
2599
|
+
setActivePanel((p) => p === "sessions" ? "activity" : "sessions");
|
|
1304
2600
|
}
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
if (
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
2601
|
+
},
|
|
2602
|
+
[splitMode]
|
|
2603
|
+
);
|
|
2604
|
+
const getActiveFilter = useCallback4(() => {
|
|
2605
|
+
if (activePanel === "sessions") return filter;
|
|
2606
|
+
if (activePanel === "left") return leftFilter;
|
|
2607
|
+
if (activePanel === "right") return rightFilter;
|
|
2608
|
+
return activityFilter;
|
|
2609
|
+
}, [activePanel, filter, leftFilter, rightFilter, activityFilter]);
|
|
2610
|
+
const clearSplitState = useCallback4(() => {
|
|
2611
|
+
setSplitMode(false);
|
|
2612
|
+
setLeftSession(null);
|
|
2613
|
+
setRightSession(null);
|
|
2614
|
+
setLeftScroll(0);
|
|
2615
|
+
setRightScroll(0);
|
|
2616
|
+
setLeftFilter("");
|
|
2617
|
+
setRightFilter("");
|
|
2618
|
+
setLeftShowDetail(false);
|
|
2619
|
+
setRightShowDetail(false);
|
|
2620
|
+
setActivePanel("sessions");
|
|
2621
|
+
}, []);
|
|
2622
|
+
const resetPanel = useCallback4((side) => {
|
|
2623
|
+
if (side === "left") {
|
|
2624
|
+
setLeftSession(null);
|
|
2625
|
+
setLeftScroll(0);
|
|
2626
|
+
setLeftFilter("");
|
|
2627
|
+
setLeftShowDetail(false);
|
|
2628
|
+
} else {
|
|
2629
|
+
setRightSession(null);
|
|
2630
|
+
setRightScroll(0);
|
|
2631
|
+
setRightFilter("");
|
|
2632
|
+
setRightShowDetail(false);
|
|
1323
2633
|
}
|
|
1324
|
-
|
|
1325
|
-
|
|
2634
|
+
}, []);
|
|
2635
|
+
useKeyHandler({
|
|
2636
|
+
kb,
|
|
2637
|
+
activePanel,
|
|
2638
|
+
splitMode,
|
|
2639
|
+
inputMode,
|
|
2640
|
+
showSetup,
|
|
2641
|
+
showSettings: showSettings || showThemeMenu || showThemePicker || showTour,
|
|
2642
|
+
showDetail,
|
|
2643
|
+
leftShowDetail,
|
|
2644
|
+
rightShowDetail,
|
|
2645
|
+
confirmAction,
|
|
2646
|
+
selectedSession,
|
|
2647
|
+
leftSession,
|
|
2648
|
+
rightSession,
|
|
2649
|
+
leftScroll,
|
|
2650
|
+
rightScroll,
|
|
2651
|
+
leftFilter,
|
|
2652
|
+
rightFilter,
|
|
2653
|
+
filter,
|
|
2654
|
+
activityFilter,
|
|
2655
|
+
viewingArchive,
|
|
2656
|
+
archivedIds,
|
|
2657
|
+
updateInfo,
|
|
2658
|
+
maxScroll: Math.max(0, events.length - viewportRows),
|
|
2659
|
+
leftMaxScroll: Math.max(0, leftEvents.length - viewportRows),
|
|
2660
|
+
rightMaxScroll: Math.max(0, rightEvents.length - viewportRows),
|
|
2661
|
+
exit,
|
|
2662
|
+
selectNext,
|
|
2663
|
+
selectPrev,
|
|
2664
|
+
refresh,
|
|
2665
|
+
switchPanel,
|
|
2666
|
+
clearSplitState,
|
|
2667
|
+
resetPanel,
|
|
2668
|
+
getActiveFilter,
|
|
2669
|
+
setActivePanel,
|
|
2670
|
+
setInputMode,
|
|
2671
|
+
setFilter,
|
|
2672
|
+
setActivityFilter,
|
|
2673
|
+
setLeftFilter,
|
|
2674
|
+
setRightFilter,
|
|
2675
|
+
setShowDetail,
|
|
2676
|
+
setLeftShowDetail,
|
|
2677
|
+
setRightShowDetail,
|
|
2678
|
+
setShowSettings,
|
|
2679
|
+
setViewingArchive,
|
|
2680
|
+
setSplitMode,
|
|
2681
|
+
setLeftSession,
|
|
2682
|
+
setRightSession,
|
|
2683
|
+
setLeftScroll,
|
|
2684
|
+
setRightScroll,
|
|
2685
|
+
setActivityScroll,
|
|
2686
|
+
setConfirmAction,
|
|
2687
|
+
setUpdateStatus,
|
|
2688
|
+
nicknameInput,
|
|
2689
|
+
filterInput,
|
|
2690
|
+
onNickname: (id) => {
|
|
2691
|
+
clearNickname(id);
|
|
1326
2692
|
refresh();
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
}
|
|
1342
|
-
|
|
2693
|
+
},
|
|
2694
|
+
onClearNickname: (id) => {
|
|
2695
|
+
clearNickname(id);
|
|
2696
|
+
refresh();
|
|
2697
|
+
},
|
|
2698
|
+
onArchive: (id) => {
|
|
2699
|
+
archiveSession(id);
|
|
2700
|
+
refreshArchived();
|
|
2701
|
+
refresh();
|
|
2702
|
+
},
|
|
2703
|
+
onUnarchive: (id) => {
|
|
2704
|
+
unarchiveSession(id);
|
|
2705
|
+
refreshArchived();
|
|
2706
|
+
refresh();
|
|
2707
|
+
},
|
|
2708
|
+
onDelete: (sess) => setConfirmAction({
|
|
2709
|
+
title: "Delete session?",
|
|
2710
|
+
message: `Delete ${sess.nickname || sess.slug}? Output files will be removed.`,
|
|
2711
|
+
onConfirm: () => {
|
|
2712
|
+
deleteSessionFiles(sess.outputFiles);
|
|
2713
|
+
clearNickname(sess.sessionId);
|
|
2714
|
+
if (archivedIds.has(sess.sessionId)) {
|
|
2715
|
+
unarchiveSession(sess.sessionId);
|
|
2716
|
+
refreshArchived();
|
|
2717
|
+
}
|
|
2718
|
+
refresh();
|
|
2719
|
+
setConfirmAction(null);
|
|
2720
|
+
}
|
|
2721
|
+
}),
|
|
2722
|
+
onUpdate: () => {
|
|
1343
2723
|
setUpdateStatus("updating...");
|
|
1344
|
-
installUpdate().then(() => setUpdateStatus(`updated to v${updateInfo
|
|
1345
|
-
return;
|
|
1346
|
-
}
|
|
1347
|
-
if (activePanel === "sessions") {
|
|
1348
|
-
if (matchKey(kb.navDown, input, key) || key.downArrow) selectNext();
|
|
1349
|
-
if (matchKey(kb.navUp, input, key) || key.upArrow) selectPrev();
|
|
1350
|
-
}
|
|
1351
|
-
if (activePanel === "activity") {
|
|
1352
|
-
if (matchKey(kb.navUp, input, key) || key.upArrow) setActivityScroll((s) => Math.min(s + 1, maxScroll));
|
|
1353
|
-
if (matchKey(kb.navDown, input, key) || key.downArrow) setActivityScroll((s) => Math.max(s - 1, 0));
|
|
1354
|
-
if (matchKey(kb.scrollBottom, input, key) || key.end) setActivityScroll(0);
|
|
1355
|
-
if (matchKey(kb.scrollTop, input, key) || key.home) setActivityScroll(maxScroll);
|
|
2724
|
+
installUpdate().then(() => setUpdateStatus(`updated to v${updateInfo?.latest} \u2014 restart to apply`)).catch(() => setUpdateStatus("update failed"));
|
|
1356
2725
|
}
|
|
1357
2726
|
});
|
|
1358
2727
|
if (showSetup) {
|
|
1359
|
-
const steps = [
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
2728
|
+
const steps = [
|
|
2729
|
+
...liveConfig.prompts.hook === "pending" ? [
|
|
2730
|
+
{
|
|
2731
|
+
title: "Install Claude Code hook?",
|
|
2732
|
+
description: "Adds a PostToolUse hook that blocks prompt injection attempts in real-time."
|
|
2733
|
+
}
|
|
2734
|
+
] : [],
|
|
2735
|
+
...liveConfig.prompts.mcp === "pending" ? [
|
|
2736
|
+
{
|
|
2737
|
+
title: "Install MCP server?",
|
|
2738
|
+
description: "Registers agenttop as an MCP server so Claude Code can query session status and alerts."
|
|
2739
|
+
}
|
|
2740
|
+
] : []
|
|
2741
|
+
];
|
|
1370
2742
|
if (steps.length === 0) {
|
|
1371
2743
|
setShowSetup(false);
|
|
2744
|
+
if (liveConfig.prompts.theme === "pending") setShowThemePicker(true);
|
|
2745
|
+
else if (liveConfig.prompts.tour === "pending") setShowTour(true);
|
|
1372
2746
|
return null;
|
|
1373
2747
|
}
|
|
1374
|
-
return /* @__PURE__ */
|
|
2748
|
+
return /* @__PURE__ */ jsx15(SetupModal, { steps, onComplete: handleSetupComplete });
|
|
1375
2749
|
}
|
|
1376
|
-
if (
|
|
1377
|
-
return /* @__PURE__ */
|
|
2750
|
+
if (showThemePicker) {
|
|
2751
|
+
return /* @__PURE__ */ jsx15(
|
|
2752
|
+
ThemePickerModal,
|
|
2753
|
+
{
|
|
2754
|
+
onSelect: handleThemePickerSelect,
|
|
2755
|
+
onSkip: handleThemePickerSkip,
|
|
2756
|
+
onDismiss: handleThemePickerDismiss
|
|
2757
|
+
}
|
|
2758
|
+
);
|
|
2759
|
+
}
|
|
2760
|
+
if (showTour) return /* @__PURE__ */ jsx15(GuidedTour, { onComplete: handleTourComplete, onSkip: handleTourSkip });
|
|
2761
|
+
if (showThemeMenu) return /* @__PURE__ */ jsx15(ThemeMenu, { config: liveConfig, onClose: handleThemeMenuClose });
|
|
2762
|
+
if (showSettings)
|
|
2763
|
+
return /* @__PURE__ */ jsx15(SettingsMenu, { config: liveConfig, onClose: handleSettingsClose, onOpenThemeMenu: handleOpenThemeMenu });
|
|
2764
|
+
if (confirmAction) {
|
|
2765
|
+
return /* @__PURE__ */ jsx15(Box15, { flexDirection: "column", height: termHeight, justifyContent: "center", alignItems: "center", children: /* @__PURE__ */ jsx15(
|
|
2766
|
+
ConfirmModal,
|
|
2767
|
+
{
|
|
2768
|
+
title: confirmAction.title,
|
|
2769
|
+
message: confirmAction.message,
|
|
2770
|
+
onConfirm: confirmAction.onConfirm,
|
|
2771
|
+
onCancel: () => setConfirmAction(null)
|
|
2772
|
+
}
|
|
2773
|
+
) });
|
|
1378
2774
|
}
|
|
1379
|
-
const
|
|
2775
|
+
const filterLabel = activePanel === "sessions" ? "sessions" : activePanel === "left" ? "left" : activePanel === "right" ? "right" : "activity";
|
|
2776
|
+
const rightPanel = splitMode ? /* @__PURE__ */ jsx15(
|
|
2777
|
+
SplitPanel,
|
|
2778
|
+
{
|
|
2779
|
+
activePanel,
|
|
2780
|
+
leftSession,
|
|
2781
|
+
rightSession,
|
|
2782
|
+
leftEvents,
|
|
2783
|
+
rightEvents,
|
|
2784
|
+
leftScroll,
|
|
2785
|
+
rightScroll,
|
|
2786
|
+
leftFilter,
|
|
2787
|
+
rightFilter,
|
|
2788
|
+
leftShowDetail,
|
|
2789
|
+
rightShowDetail,
|
|
2790
|
+
height: mainHeight
|
|
2791
|
+
}
|
|
2792
|
+
) : showDetail && selectedSession ? /* @__PURE__ */ jsx15(SessionDetail, { session: selectedSession, focused: activePanel === "activity", height: mainHeight }) : /* @__PURE__ */ jsx15(
|
|
1380
2793
|
ActivityFeed,
|
|
1381
2794
|
{
|
|
1382
2795
|
events,
|
|
1383
2796
|
sessionSlug: selectedSession?.slug ?? null,
|
|
1384
2797
|
focused: activePanel === "activity",
|
|
1385
2798
|
height: mainHeight,
|
|
1386
|
-
scrollOffset: activityScroll
|
|
2799
|
+
scrollOffset: activityScroll,
|
|
2800
|
+
filter: activityFilter || void 0
|
|
1387
2801
|
}
|
|
1388
2802
|
);
|
|
1389
|
-
return /* @__PURE__ */
|
|
1390
|
-
/* @__PURE__ */
|
|
1391
|
-
/* @__PURE__ */
|
|
1392
|
-
/* @__PURE__ */
|
|
2803
|
+
return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", height: termHeight, children: [
|
|
2804
|
+
/* @__PURE__ */ jsx15(StatusBar, { sessionCount: sessions.length, alertCount: alerts.length, version, updateInfo }),
|
|
2805
|
+
/* @__PURE__ */ jsxs15(Box15, { flexGrow: 1, height: mainHeight, children: [
|
|
2806
|
+
/* @__PURE__ */ jsx15(
|
|
1393
2807
|
SessionList,
|
|
1394
2808
|
{
|
|
1395
2809
|
sessions,
|
|
1396
2810
|
selectedIndex,
|
|
1397
2811
|
focused: activePanel === "sessions",
|
|
1398
|
-
filter: filter || void 0
|
|
2812
|
+
filter: filter || void 0,
|
|
2813
|
+
viewingArchive
|
|
1399
2814
|
}
|
|
1400
2815
|
),
|
|
1401
2816
|
rightPanel
|
|
1402
2817
|
] }),
|
|
1403
|
-
!options.noSecurity && /* @__PURE__ */
|
|
1404
|
-
inputMode === "nickname" && /* @__PURE__ */
|
|
1405
|
-
/* @__PURE__ */
|
|
1406
|
-
/* @__PURE__ */
|
|
1407
|
-
/* @__PURE__ */
|
|
2818
|
+
!options.noSecurity && /* @__PURE__ */ jsx15(AlertBar, { alerts }),
|
|
2819
|
+
inputMode === "nickname" && /* @__PURE__ */ jsxs15(Box15, { paddingX: 1, children: [
|
|
2820
|
+
/* @__PURE__ */ jsx15(Text14, { color: colors.primary, children: "nickname: " }),
|
|
2821
|
+
/* @__PURE__ */ jsx15(Text14, { color: colors.bright, children: nicknameInput.value }),
|
|
2822
|
+
/* @__PURE__ */ jsx15(Text14, { color: colors.muted, children: "_" })
|
|
1408
2823
|
] }),
|
|
1409
|
-
inputMode === "filter" && /* @__PURE__ */
|
|
1410
|
-
/* @__PURE__ */
|
|
1411
|
-
/* @__PURE__ */
|
|
1412
|
-
/* @__PURE__ */
|
|
2824
|
+
inputMode === "filter" && /* @__PURE__ */ jsxs15(Box15, { paddingX: 1, children: [
|
|
2825
|
+
/* @__PURE__ */ jsx15(Text14, { color: colors.muted, children: filterLabel }),
|
|
2826
|
+
/* @__PURE__ */ jsx15(Text14, { color: colors.primary, children: "/" }),
|
|
2827
|
+
/* @__PURE__ */ jsx15(Text14, { color: colors.bright, children: filterInput.value }),
|
|
2828
|
+
/* @__PURE__ */ jsx15(Text14, { color: colors.muted, children: "_" })
|
|
1413
2829
|
] }),
|
|
1414
|
-
inputMode === "normal" && /* @__PURE__ */
|
|
2830
|
+
inputMode === "normal" && /* @__PURE__ */ jsx15(FooterBar, { keybindings: kb, updateStatus, viewingArchive, splitMode })
|
|
1415
2831
|
] });
|
|
1416
2832
|
};
|
|
1417
2833
|
|
|
@@ -1648,7 +3064,7 @@ var main = () => {
|
|
|
1648
3064
|
if (options.noUpdates) config.updates.checkOnLaunch = false;
|
|
1649
3065
|
if (options.noSecurity) config.security.enabled = false;
|
|
1650
3066
|
if (firstRun) saveConfig(config);
|
|
1651
|
-
render(
|
|
3067
|
+
render(React16.createElement(App, { options, config, version: VERSION, firstRun }));
|
|
1652
3068
|
};
|
|
1653
3069
|
main();
|
|
1654
3070
|
//# sourceMappingURL=index.js.map
|