agenttop 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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
- } from "./chunk-H2JOTO54.js";
18
+ startMcpServer,
19
+ unarchiveSession
20
+ } from "./chunk-6N34AD6O.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 React10 from "react";
26
+ import React14 from "react";
22
27
  import { render } from "ink";
23
28
 
24
29
  // src/ui/App.tsx
25
- import { useState as useState8, useEffect as useEffect5, useCallback as useCallback3 } from "react";
26
- import { Box as Box9, Text as Text9, useApp, useInput as useInput3, useStdout as useStdout2 } from "ink";
30
+ import { useState as useState11, useEffect as useEffect8, useCallback as useCallback4 } from "react";
31
+ import { Box as Box13, Text as Text12, 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(({ sessions, selectedIndex, focused, filter }) => {
290
- return /* @__PURE__ */ jsxs2(
291
- Box2,
292
- {
293
- flexDirection: "column",
294
- width: SIDEBAR_WIDTH,
295
- borderStyle: "single",
296
- borderColor: focused ? colors.primary : colors.border,
297
- overflow: "hidden",
298
- children: [
299
- /* @__PURE__ */ jsxs2(Box2, { paddingX: 1, children: [
300
- /* @__PURE__ */ jsx2(Text2, { color: colors.header, bold: true, children: "SESSIONS" }),
301
- filter && /* @__PURE__ */ jsxs2(Text2, { color: colors.muted, children: [
302
- " [",
303
- truncate(filter, 10),
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
- ] }, session.sessionId);
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(({ keybindings, updateStatus }) => /* @__PURE__ */ jsxs7(Box7, { paddingX: 1, children: [
673
- /* @__PURE__ */ jsx7(Box7, { marginRight: 2, children: /* @__PURE__ */ jsxs7(Text7, { color: "#5C6370", children: [
674
- label(keybindings.quit),
675
- ":quit"
676
- ] }) }),
677
- /* @__PURE__ */ jsx7(Box7, { marginRight: 2, children: /* @__PURE__ */ jsxs7(Text7, { color: "#5C6370", children: [
678
- label(keybindings.navDown),
679
- "/",
680
- label(keybindings.navUp),
681
- ":nav"
682
- ] }) }),
683
- /* @__PURE__ */ jsx7(Box7, { marginRight: 2, children: /* @__PURE__ */ jsxs7(Text7, { color: "#5C6370", children: [
684
- label(keybindings.panelNext),
685
- ":panel"
686
- ] }) }),
687
- /* @__PURE__ */ jsx7(Box7, { marginRight: 2, children: /* @__PURE__ */ jsxs7(Text7, { color: "#5C6370", children: [
688
- label(keybindings.filter),
689
- ":filter"
690
- ] }) }),
691
- /* @__PURE__ */ jsx7(Box7, { marginRight: 2, children: /* @__PURE__ */ jsxs7(Text7, { color: "#5C6370", children: [
692
- label(keybindings.nickname),
693
- ":name"
694
- ] }) }),
695
- /* @__PURE__ */ jsx7(Box7, { marginRight: 2, children: /* @__PURE__ */ jsxs7(Text7, { color: "#5C6370", children: [
696
- label(keybindings.detail),
697
- ":detail"
698
- ] }) }),
699
- /* @__PURE__ */ jsx7(Box7, { marginRight: 2, children: /* @__PURE__ */ jsxs7(Text7, { color: "#5C6370", children: [
700
- label(keybindings.settings),
701
- ":settings"
702
- ] }) }),
703
- updateStatus && /* @__PURE__ */ jsx7(Box7, { marginRight: 2, children: /* @__PURE__ */ jsx7(Text7, { color: colors.secondary, children: updateStatus }) })
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 sessions",
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
- const next = SEVERITY_OPTIONS[(idx + 1) % SEVERITY_OPTIONS.length];
812
- return { ...cfg, notifications: { ...cfg.notifications, minSeverity: next } };
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
- if (item.apply) {
843
- setLocalConfig((c) => item.apply(c, newKey));
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.type === "keybind") {
863
- setRebinding(true);
864
- } else if (item.apply) {
865
- setLocalConfig((c) => item.apply(c));
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__ */ jsx8(Text8, { color: colors.muted, children: "esc to save & close" })
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,460 @@ 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 }) })
915
1367
  ]
916
1368
  }
917
1369
  ) });
918
1370
  });
919
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 }) })
1712
+ ]
1713
+ }
1714
+ ) });
1715
+ });
1716
+
1717
+ // src/ui/components/ConfirmModal.tsx
1718
+ import React11 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 ConfirmModal = React11.memo(({ title, message, onConfirm, onCancel }) => {
1722
+ useInput5((input, key) => {
1723
+ if (input === "y" || input === "Y") {
1724
+ onConfirm();
1725
+ } else if (input === "n" || input === "N" || key.escape) {
1726
+ onCancel();
1727
+ }
1728
+ });
1729
+ return /* @__PURE__ */ jsxs11(
1730
+ Box11,
1731
+ {
1732
+ borderStyle: "round",
1733
+ borderColor: colors.warning,
1734
+ flexDirection: "column",
1735
+ paddingX: 2,
1736
+ paddingY: 1,
1737
+ alignSelf: "center",
1738
+ children: [
1739
+ /* @__PURE__ */ jsx11(Text11, { color: colors.warning, bold: true, children: title }),
1740
+ /* @__PURE__ */ jsx11(Text11, { color: colors.text, children: message }),
1741
+ /* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx11(Text11, { color: colors.muted, children: "[y] confirm [n/esc] cancel" }) })
1742
+ ]
1743
+ }
1744
+ );
1745
+ });
1746
+
1747
+ // src/ui/components/SplitPanel.tsx
1748
+ import React12 from "react";
1749
+ import { Box as Box12 } from "ink";
1750
+ import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
1751
+ var SplitPanel = React12.memo(
1752
+ ({
1753
+ activePanel,
1754
+ leftSession,
1755
+ rightSession,
1756
+ leftEvents,
1757
+ rightEvents,
1758
+ leftScroll,
1759
+ rightScroll,
1760
+ leftFilter,
1761
+ rightFilter,
1762
+ leftShowDetail,
1763
+ rightShowDetail,
1764
+ height
1765
+ }) => {
1766
+ const left = leftShowDetail && leftSession ? /* @__PURE__ */ jsx12(SessionDetail, { session: leftSession, focused: activePanel === "left", height }) : /* @__PURE__ */ jsx12(
1767
+ ActivityFeed,
1768
+ {
1769
+ events: leftEvents,
1770
+ sessionSlug: leftSession?.slug ?? null,
1771
+ focused: activePanel === "left",
1772
+ height,
1773
+ scrollOffset: leftScroll,
1774
+ filter: leftFilter || void 0
1775
+ }
1776
+ );
1777
+ const right = rightShowDetail && rightSession ? /* @__PURE__ */ jsx12(SessionDetail, { session: rightSession, focused: activePanel === "right", height }) : /* @__PURE__ */ jsx12(
1778
+ ActivityFeed,
1779
+ {
1780
+ events: rightEvents,
1781
+ sessionSlug: rightSession?.slug ?? null,
1782
+ focused: activePanel === "right",
1783
+ height,
1784
+ scrollOffset: rightScroll,
1785
+ filter: rightFilter || void 0
1786
+ }
1787
+ );
1788
+ return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "row", flexGrow: 1, children: [
1789
+ left,
1790
+ right
1791
+ ] });
1792
+ }
1793
+ );
1794
+
920
1795
  // src/ui/hooks/useSessions.ts
921
- import { useState as useState4, useEffect as useEffect2, useCallback, useRef } from "react";
1796
+ import { useState as useState6, useEffect as useEffect4, useCallback as useCallback2, useRef as useRef3 } from "react";
922
1797
  var ACTIVE_POLL_MS = 1e4;
923
1798
  var IDLE_POLL_MS = 3e4;
924
- var useSessions = (allUsers, filter) => {
925
- const [sessions, setSessions] = useState4([]);
926
- const [selectedIndex, setSelectedIndex] = useState4(0);
927
- const usageOverrides = useRef(/* @__PURE__ */ new Map());
928
- const refresh = useCallback(() => {
1799
+ var useSessions = (allUsers, filter, archivedIds, viewingArchive) => {
1800
+ const [sessions, setSessions] = useState6([]);
1801
+ const [selectedIndex, setSelectedIndex] = useState6(0);
1802
+ const usageOverrides = useRef3(/* @__PURE__ */ new Map());
1803
+ const refresh = useCallback2(() => {
929
1804
  const found = discoverSessions(allUsers);
930
1805
  const nicknames = getNicknames();
931
1806
  const enriched = found.map((s) => {
@@ -942,31 +1817,40 @@ var useSessions = (allUsers, filter) => {
942
1817
  };
943
1818
  });
944
1819
  let filtered = enriched;
1820
+ if (archivedIds && archivedIds.size > 0) {
1821
+ if (viewingArchive) {
1822
+ filtered = filtered.filter((s) => archivedIds.has(s.sessionId));
1823
+ } else {
1824
+ filtered = filtered.filter((s) => !archivedIds.has(s.sessionId));
1825
+ }
1826
+ } else if (viewingArchive) {
1827
+ filtered = [];
1828
+ }
945
1829
  if (filter) {
946
1830
  const lower = filter.toLowerCase();
947
- filtered = enriched.filter(
1831
+ filtered = filtered.filter(
948
1832
  (s) => s.slug.toLowerCase().includes(lower) || s.nickname?.toLowerCase().includes(lower) || s.project.toLowerCase().includes(lower) || s.model.toLowerCase().includes(lower)
949
1833
  );
950
1834
  }
951
1835
  setSessions(filtered);
952
- }, [allUsers, filter]);
953
- useEffect2(() => {
1836
+ }, [allUsers, filter, archivedIds, viewingArchive]);
1837
+ useEffect4(() => {
954
1838
  refresh();
955
1839
  const pollMs = sessions.length > 0 ? ACTIVE_POLL_MS : IDLE_POLL_MS;
956
1840
  const interval = setInterval(refresh, pollMs);
957
1841
  return () => clearInterval(interval);
958
1842
  }, [refresh, sessions.length > 0]);
959
1843
  const selectedSession = sessions[selectedIndex] ?? null;
960
- const selectNext = useCallback(() => {
1844
+ const selectNext = useCallback2(() => {
961
1845
  setSelectedIndex((i) => Math.min(i + 1, Math.max(0, sessions.length - 1)));
962
1846
  }, [sessions.length]);
963
- const selectPrev = useCallback(() => {
1847
+ const selectPrev = useCallback2(() => {
964
1848
  setSelectedIndex((i) => Math.max(i - 1, 0));
965
1849
  }, []);
966
- const selectIndex = useCallback((i) => {
1850
+ const selectIndex = useCallback2((i) => {
967
1851
  setSelectedIndex(i);
968
1852
  }, []);
969
- const addUsage = useCallback((sessionId, usage) => {
1853
+ const addUsage = useCallback2((sessionId, usage) => {
970
1854
  const existing = usageOverrides.current.get(sessionId);
971
1855
  if (existing) {
972
1856
  usageOverrides.current.set(sessionId, {
@@ -983,12 +1867,12 @@ var useSessions = (allUsers, filter) => {
983
1867
  };
984
1868
 
985
1869
  // src/ui/hooks/useActivityStream.ts
986
- import { useState as useState5, useEffect as useEffect3, useRef as useRef2 } from "react";
1870
+ import { useState as useState7, useEffect as useEffect5, useRef as useRef4 } from "react";
987
1871
  var MAX_EVENTS = 200;
988
1872
  var useActivityStream = (session, allUsers) => {
989
- const [events, setEvents] = useState5([]);
990
- const watcherRef = useRef2(null);
991
- useEffect3(() => {
1873
+ const [events, setEvents] = useState7([]);
1874
+ const watcherRef = useRef4(null);
1875
+ useEffect5(() => {
992
1876
  setEvents([]);
993
1877
  if (!session) return;
994
1878
  const existingCalls = [];
@@ -1014,8 +1898,19 @@ var useActivityStream = (session, allUsers) => {
1014
1898
  return events;
1015
1899
  };
1016
1900
 
1901
+ // src/ui/hooks/useFilteredEvents.ts
1902
+ import { useMemo as useMemo2 } from "react";
1903
+ var applyFilter = (events, filter) => {
1904
+ if (!filter) return events;
1905
+ const lower = filter.toLowerCase();
1906
+ return events.filter(
1907
+ (e) => e.toolName.toLowerCase().includes(lower) || JSON.stringify(e.toolInput).toLowerCase().includes(lower)
1908
+ );
1909
+ };
1910
+ var useFilteredEvents = (rawEvents, filter) => useMemo2(() => applyFilter(rawEvents, filter), [rawEvents, filter]);
1911
+
1017
1912
  // src/ui/hooks/useAlerts.ts
1018
- import { useState as useState6, useEffect as useEffect4, useRef as useRef3 } from "react";
1913
+ import { useState as useState8, useEffect as useEffect6, useRef as useRef5 } from "react";
1019
1914
 
1020
1915
  // src/notifications.ts
1021
1916
  import { exec as exec2 } from "child_process";
@@ -1083,11 +1978,11 @@ var AlertLogger = class {
1083
1978
  // src/ui/hooks/useAlerts.ts
1084
1979
  var MAX_ALERTS = 100;
1085
1980
  var useAlerts = (enabled, alertLevel, allUsers, config) => {
1086
- const [alerts, setAlerts] = useState6([]);
1087
- const engineRef = useRef3(new SecurityEngine(alertLevel));
1088
- const watcherRef = useRef3(null);
1089
- const loggerRef = useRef3(null);
1090
- useEffect4(() => {
1981
+ const [alerts, setAlerts] = useState8([]);
1982
+ const engineRef = useRef5(new SecurityEngine(alertLevel));
1983
+ const watcherRef = useRef5(null);
1984
+ const loggerRef = useRef5(null);
1985
+ useEffect6(() => {
1091
1986
  if (!enabled) return;
1092
1987
  engineRef.current = new SecurityEngine(alertLevel);
1093
1988
  if (config?.alerts.enabled) {
@@ -1123,27 +2018,27 @@ var useAlerts = (enabled, alertLevel, allUsers, config) => {
1123
2018
  };
1124
2019
 
1125
2020
  // src/ui/hooks/useTextInput.ts
1126
- import { useState as useState7, useCallback as useCallback2 } from "react";
2021
+ import { useState as useState9, useCallback as useCallback3 } from "react";
1127
2022
  var useTextInput = (onConfirm, onCancel) => {
1128
- const [value, setValue] = useState7("");
1129
- const [isActive, setIsActive] = useState7(false);
1130
- const start = useCallback2((initial = "") => {
2023
+ const [value, setValue] = useState9("");
2024
+ const [isActive, setIsActive] = useState9(false);
2025
+ const start = useCallback3((initial = "") => {
1131
2026
  setValue(initial);
1132
2027
  setIsActive(true);
1133
2028
  }, []);
1134
- const cancel = useCallback2(() => {
2029
+ const cancel = useCallback3(() => {
1135
2030
  setValue("");
1136
2031
  setIsActive(false);
1137
2032
  onCancel?.();
1138
2033
  }, [onCancel]);
1139
- const confirm = useCallback2(() => {
2034
+ const confirm = useCallback3(() => {
1140
2035
  const result = value;
1141
2036
  setIsActive(false);
1142
2037
  setValue("");
1143
2038
  onConfirm?.(result);
1144
2039
  return result;
1145
2040
  }, [value, onConfirm]);
1146
- const handleInput = useCallback2(
2041
+ const handleInput = useCallback3(
1147
2042
  (input, key) => {
1148
2043
  if (!isActive) return false;
1149
2044
  if (key.escape) {
@@ -1175,34 +2070,297 @@ var useTextInput = (onConfirm, onCancel) => {
1175
2070
  return { value, isActive, start, cancel, confirm, handleInput };
1176
2071
  };
1177
2072
 
1178
- // src/ui/App.tsx
1179
- import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
2073
+ // src/ui/hooks/useKeyHandler.ts
2074
+ import { useInput as useInput6 } from "ink";
1180
2075
  var matchKey = (binding, input, key) => {
1181
2076
  if (binding === "tab") return Boolean(key.tab);
1182
2077
  if (binding === "shift+tab") return Boolean(key.shift && key.tab);
1183
2078
  if (binding === "enter") return Boolean(key.return);
1184
2079
  return input === binding;
1185
2080
  };
2081
+ var useKeyHandler = (deps) => {
2082
+ const d = deps;
2083
+ useInput6((input, key) => {
2084
+ if (d.showSetup || d.showSettings || d.confirmAction) return;
2085
+ if (d.inputMode === "nickname") {
2086
+ d.nicknameInput.handleInput(input, key);
2087
+ return;
2088
+ }
2089
+ if (d.inputMode === "filter") {
2090
+ if (key.escape) {
2091
+ if (d.activePanel === "sessions") d.setFilter("");
2092
+ else if (d.activePanel === "left") d.setLeftFilter("");
2093
+ else if (d.activePanel === "right") d.setRightFilter("");
2094
+ else d.setActivityFilter("");
2095
+ d.setInputMode("normal");
2096
+ d.filterInput.cancel();
2097
+ return;
2098
+ }
2099
+ d.filterInput.handleInput(input, key);
2100
+ return;
2101
+ }
2102
+ if (matchKey(d.kb.quit, input, key)) {
2103
+ d.exit();
2104
+ return;
2105
+ }
2106
+ if (d.showDetail && !d.splitMode) {
2107
+ if (key.escape || key.return || key.leftArrow) d.setShowDetail(false);
2108
+ return;
2109
+ }
2110
+ if (d.splitMode && (d.leftShowDetail || d.rightShowDetail)) {
2111
+ if (key.escape || key.return) {
2112
+ if (d.activePanel === "left") d.setLeftShowDetail(false);
2113
+ else if (d.activePanel === "right") d.setRightShowDetail(false);
2114
+ else {
2115
+ d.setLeftShowDetail(false);
2116
+ d.setRightShowDetail(false);
2117
+ }
2118
+ return;
2119
+ }
2120
+ if (key.leftArrow) {
2121
+ if (d.activePanel === "left") d.setLeftShowDetail(false);
2122
+ else if (d.activePanel === "right") d.setRightShowDetail(false);
2123
+ return;
2124
+ }
2125
+ }
2126
+ if (matchKey(d.kb.split, input, key)) {
2127
+ if (d.splitMode) {
2128
+ d.clearSplitState();
2129
+ } else {
2130
+ d.setSplitMode(true);
2131
+ d.setLeftSession(d.selectedSession);
2132
+ d.setActivePanel("left");
2133
+ }
2134
+ return;
2135
+ }
2136
+ if (d.splitMode && d.activePanel === "sessions" && d.selectedSession) {
2137
+ if (matchKey(d.kb.pinLeft, input, key)) {
2138
+ d.resetPanel("left");
2139
+ d.setLeftSession(d.selectedSession);
2140
+ return;
2141
+ }
2142
+ if (matchKey(d.kb.pinRight, input, key)) {
2143
+ d.resetPanel("right");
2144
+ d.setRightSession(d.selectedSession);
2145
+ return;
2146
+ }
2147
+ }
2148
+ if (d.splitMode && matchKey(d.kb.swapPanels, input, key)) {
2149
+ const tmpLs = d.leftSession;
2150
+ const tmpRs = d.rightSession;
2151
+ d.setLeftSession(tmpRs);
2152
+ d.setRightSession(tmpLs);
2153
+ const tmpScroll = d.leftScroll;
2154
+ d.setLeftScroll(d.rightScroll);
2155
+ d.setRightScroll(tmpScroll);
2156
+ const tmpFilt = d.leftFilter;
2157
+ d.setLeftFilter(d.rightFilter);
2158
+ d.setRightFilter(tmpFilt);
2159
+ const tmpDetail = d.leftShowDetail;
2160
+ d.setLeftShowDetail(d.rightShowDetail);
2161
+ d.setRightShowDetail(tmpDetail);
2162
+ return;
2163
+ }
2164
+ if (d.splitMode && matchKey(d.kb.closePanel, input, key)) {
2165
+ if (d.activePanel === "left") {
2166
+ d.resetPanel("left");
2167
+ if (!d.rightSession) d.clearSplitState();
2168
+ } else if (d.activePanel === "right") {
2169
+ d.resetPanel("right");
2170
+ if (!d.leftSession) d.clearSplitState();
2171
+ }
2172
+ return;
2173
+ }
2174
+ if (matchKey(d.kb.detail, input, key) && d.selectedSession) {
2175
+ if (d.splitMode) {
2176
+ if (d.activePanel === "left") {
2177
+ d.setLeftShowDetail((v) => !v);
2178
+ return;
2179
+ }
2180
+ if (d.activePanel === "right") {
2181
+ d.setRightShowDetail((v) => !v);
2182
+ return;
2183
+ }
2184
+ if (d.activePanel === "sessions") {
2185
+ if (!d.leftSession) {
2186
+ d.setLeftSession(d.selectedSession);
2187
+ d.setLeftShowDetail(true);
2188
+ d.setActivePanel("left");
2189
+ } else if (!d.rightSession) {
2190
+ d.setRightSession(d.selectedSession);
2191
+ d.setRightShowDetail(true);
2192
+ d.setActivePanel("right");
2193
+ }
2194
+ return;
2195
+ }
2196
+ } else if (d.activePanel === "sessions") {
2197
+ d.setShowDetail(true);
2198
+ return;
2199
+ }
2200
+ }
2201
+ if (matchKey(d.kb.panelNext, input, key) || key.rightArrow) {
2202
+ d.switchPanel("next");
2203
+ return;
2204
+ }
2205
+ if (matchKey(d.kb.panelPrev, input, key) || key.leftArrow) {
2206
+ d.switchPanel("prev");
2207
+ return;
2208
+ }
2209
+ if (matchKey(d.kb.nickname, input, key) && d.selectedSession) {
2210
+ d.setInputMode("nickname");
2211
+ d.nicknameInput.start(d.selectedSession.nickname || "");
2212
+ return;
2213
+ }
2214
+ if (matchKey(d.kb.clearNickname, input, key) && d.selectedSession) {
2215
+ d.onClearNickname(d.selectedSession.sessionId);
2216
+ return;
2217
+ }
2218
+ if (matchKey(d.kb.filter, input, key)) {
2219
+ d.setInputMode("filter");
2220
+ d.filterInput.start(d.getActiveFilter());
2221
+ return;
2222
+ }
2223
+ if (key.escape) {
2224
+ if (d.activePanel === "sessions" && d.filter) {
2225
+ d.setFilter("");
2226
+ return;
2227
+ }
2228
+ if (d.activePanel === "activity" && d.activityFilter) {
2229
+ d.setActivityFilter("");
2230
+ return;
2231
+ }
2232
+ if (d.activePanel === "left" && d.leftFilter) {
2233
+ d.setLeftFilter("");
2234
+ return;
2235
+ }
2236
+ if (d.activePanel === "right" && d.rightFilter) {
2237
+ d.setRightFilter("");
2238
+ return;
2239
+ }
2240
+ return;
2241
+ }
2242
+ if (matchKey(d.kb.settings, input, key)) {
2243
+ d.setShowSettings(true);
2244
+ return;
2245
+ }
2246
+ if (matchKey(d.kb.viewArchive, input, key)) {
2247
+ d.setViewingArchive((v) => !v);
2248
+ return;
2249
+ }
2250
+ if (matchKey(d.kb.archive, input, key) && d.selectedSession) {
2251
+ if (d.viewingArchive) d.onUnarchive(d.selectedSession.sessionId);
2252
+ else d.onArchive(d.selectedSession.sessionId);
2253
+ return;
2254
+ }
2255
+ if (matchKey(d.kb.delete, input, key) && d.selectedSession) {
2256
+ d.onDelete(d.selectedSession);
2257
+ return;
2258
+ }
2259
+ if (matchKey(d.kb.update, input, key) && d.updateInfo?.available) {
2260
+ d.onUpdate();
2261
+ return;
2262
+ }
2263
+ if (d.activePanel === "sessions") {
2264
+ if (matchKey(d.kb.navDown, input, key) || key.downArrow) d.selectNext();
2265
+ if (matchKey(d.kb.navUp, input, key) || key.upArrow) d.selectPrev();
2266
+ }
2267
+ if (d.activePanel === "activity") {
2268
+ if (matchKey(d.kb.navUp, input, key) || key.upArrow) d.setActivityScroll((s) => Math.min(s + 1, d.maxScroll));
2269
+ if (matchKey(d.kb.navDown, input, key) || key.downArrow) d.setActivityScroll((s) => Math.max(s - 1, 0));
2270
+ if (matchKey(d.kb.scrollBottom, input, key)) d.setActivityScroll(0);
2271
+ if (matchKey(d.kb.scrollTop, input, key)) d.setActivityScroll(d.maxScroll);
2272
+ }
2273
+ if (d.activePanel === "left") {
2274
+ if (matchKey(d.kb.navUp, input, key) || key.upArrow) d.setLeftScroll((s) => Math.min(s + 1, d.leftMaxScroll));
2275
+ if (matchKey(d.kb.navDown, input, key) || key.downArrow) d.setLeftScroll((s) => Math.max(s - 1, 0));
2276
+ if (matchKey(d.kb.scrollBottom, input, key)) d.setLeftScroll(0);
2277
+ if (matchKey(d.kb.scrollTop, input, key)) d.setLeftScroll(d.leftMaxScroll);
2278
+ }
2279
+ if (d.activePanel === "right") {
2280
+ if (matchKey(d.kb.navUp, input, key) || key.upArrow) d.setRightScroll((s) => Math.min(s + 1, d.rightMaxScroll));
2281
+ if (matchKey(d.kb.navDown, input, key) || key.downArrow) d.setRightScroll((s) => Math.max(s - 1, 0));
2282
+ if (matchKey(d.kb.scrollBottom, input, key)) d.setRightScroll(0);
2283
+ if (matchKey(d.kb.scrollTop, input, key)) d.setRightScroll(d.rightMaxScroll);
2284
+ }
2285
+ });
2286
+ };
2287
+
2288
+ // src/ui/hooks/useUpdateChecker.ts
2289
+ import { useState as useState10, useEffect as useEffect7 } from "react";
2290
+ var useUpdateChecker = (disabled, checkOnLaunch, checkInterval) => {
2291
+ const [updateInfo, setUpdateInfo] = useState10(null);
2292
+ useEffect7(() => {
2293
+ if (disabled || !checkOnLaunch) return;
2294
+ try {
2295
+ const i = checkForUpdate();
2296
+ if (i.available) setUpdateInfo(i);
2297
+ } catch {
2298
+ }
2299
+ const iv = setInterval(() => {
2300
+ try {
2301
+ const i = checkForUpdate();
2302
+ if (i.available) setUpdateInfo(i);
2303
+ } catch {
2304
+ }
2305
+ }, checkInterval);
2306
+ return () => clearInterval(iv);
2307
+ }, []);
2308
+ return updateInfo;
2309
+ };
2310
+
2311
+ // src/ui/App.tsx
2312
+ import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
1186
2313
  var App = ({ options, config: initialConfig, version, firstRun }) => {
1187
2314
  const { exit } = useApp();
1188
- const { stdout } = useStdout2();
2315
+ const { stdout } = useStdout3();
1189
2316
  const termHeight = stdout?.rows ?? 40;
1190
- const [liveConfig, setLiveConfig] = useState8(initialConfig);
2317
+ const [liveConfig, setLiveConfig] = useState11(initialConfig);
1191
2318
  const kb = liveConfig.keybindings;
1192
- const [activePanel, setActivePanel] = useState8("sessions");
1193
- const [activityScroll, setActivityScroll] = useState8(0);
1194
- const [inputMode, setInputMode] = useState8("normal");
1195
- const [showSetup, setShowSetup] = useState8(firstRun);
1196
- const [filter, setFilter] = useState8("");
1197
- const [updateInfo, setUpdateInfo] = useState8(null);
1198
- const [updateStatus, setUpdateStatus] = useState8("");
1199
- const [showDetail, setShowDetail] = useState8(false);
1200
- const [showSettings, setShowSettings] = useState8(false);
2319
+ const [activePanel, setActivePanel] = useState11("sessions");
2320
+ const [activityScroll, setActivityScroll] = useState11(0);
2321
+ const [inputMode, setInputMode] = useState11("normal");
2322
+ const [showSetup, setShowSetup] = useState11(firstRun);
2323
+ const [filter, setFilter] = useState11("");
2324
+ const [activityFilter, setActivityFilter] = useState11("");
2325
+ const [updateStatus, setUpdateStatus] = useState11("");
2326
+ const [showDetail, setShowDetail] = useState11(false);
2327
+ const [showSettings, setShowSettings] = useState11(false);
2328
+ const [showThemeMenu, setShowThemeMenu] = useState11(false);
2329
+ const [viewingArchive, setViewingArchive] = useState11(false);
2330
+ const [confirmAction, setConfirmAction] = useState11(
2331
+ null
2332
+ );
2333
+ const [archivedIds, setArchivedIds] = useState11(() => new Set(Object.keys(getArchived())));
2334
+ const [splitMode, setSplitMode] = useState11(false);
2335
+ const [leftSession, setLeftSession] = useState11(null);
2336
+ const [rightSession, setRightSession] = useState11(null);
2337
+ const [leftScroll, setLeftScroll] = useState11(0);
2338
+ const [rightScroll, setRightScroll] = useState11(0);
2339
+ const [leftFilter, setLeftFilter] = useState11("");
2340
+ const [rightFilter, setRightFilter] = useState11("");
2341
+ const [leftShowDetail, setLeftShowDetail] = useState11(false);
2342
+ const [rightShowDetail, setRightShowDetail] = useState11(false);
2343
+ const refreshArchived = useCallback4(() => setArchivedIds(new Set(Object.keys(getArchived()))), []);
2344
+ const updateInfo = useUpdateChecker(
2345
+ options.noUpdates,
2346
+ liveConfig.updates.checkOnLaunch,
2347
+ liveConfig.updates.checkInterval
2348
+ );
2349
+ useEffect8(() => {
2350
+ applyTheme(resolveTheme(liveConfig.theme, liveConfig.customThemes));
2351
+ }, [liveConfig.theme, liveConfig.customThemes]);
1201
2352
  const { sessions, selectedSession, selectedIndex, selectNext, selectPrev, refresh } = useSessions(
1202
2353
  options.allUsers,
1203
- filter || void 0
2354
+ filter || void 0,
2355
+ archivedIds,
2356
+ viewingArchive
1204
2357
  );
1205
- const events = useActivityStream(selectedSession, options.allUsers);
2358
+ const rawEvents = useActivityStream(splitMode ? null : selectedSession, options.allUsers);
2359
+ const leftRawEvents = useActivityStream(splitMode ? leftSession : null, options.allUsers);
2360
+ const rightRawEvents = useActivityStream(splitMode ? rightSession : null, options.allUsers);
2361
+ const events = useFilteredEvents(rawEvents, activityFilter);
2362
+ const leftEvents = useFilteredEvents(leftRawEvents, leftFilter);
2363
+ const rightEvents = useFilteredEvents(rightRawEvents, rightFilter);
1206
2364
  const { alerts } = useAlerts(!options.noSecurity, options.alertLevel, options.allUsers, liveConfig);
1207
2365
  const nicknameInput = useTextInput(
1208
2366
  (value) => {
@@ -1216,43 +2374,43 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
1216
2374
  );
1217
2375
  const filterInput = useTextInput(
1218
2376
  (value) => {
1219
- setFilter(value);
2377
+ if (activePanel === "sessions") setFilter(value);
2378
+ else if (activePanel === "left") setLeftFilter(value);
2379
+ else if (activePanel === "right") setRightFilter(value);
2380
+ else setActivityFilter(value);
1220
2381
  setInputMode("normal");
1221
2382
  },
1222
2383
  () => {
1223
- setFilter("");
2384
+ if (activePanel === "sessions") setFilter("");
2385
+ else if (activePanel === "left") setLeftFilter("");
2386
+ else if (activePanel === "right") setRightFilter("");
2387
+ else setActivityFilter("");
1224
2388
  setInputMode("normal");
1225
2389
  }
1226
2390
  );
1227
- useEffect5(() => {
1228
- if (options.noUpdates || !liveConfig.updates.checkOnLaunch) return;
1229
- try {
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);
2391
+ useEffect8(() => {
2392
+ purgeExpiredArchives();
2393
+ refreshArchived();
1242
2394
  }, []);
2395
+ useEffect8(() => {
2396
+ setActivityScroll(0);
2397
+ }, [selectedSession?.sessionId]);
1243
2398
  const alertHeight = options.noSecurity ? 0 : 6;
1244
2399
  const mainHeight = termHeight - 3 - alertHeight - 1 - (inputMode !== "normal" ? 1 : 0);
1245
2400
  const viewportRows = mainHeight - 2;
1246
- const maxScroll = Math.max(0, events.length - viewportRows);
1247
- useEffect5(() => {
1248
- setActivityScroll(0);
1249
- }, [selectedSession?.sessionId]);
1250
- const handleSettingsClose = useCallback3((updatedConfig) => {
1251
- setLiveConfig(updatedConfig);
1252
- saveConfig(updatedConfig);
2401
+ const handleSettingsClose = useCallback4((c) => {
2402
+ setLiveConfig(c);
2403
+ saveConfig(c);
1253
2404
  setShowSettings(false);
1254
2405
  }, []);
1255
- const handleSetupComplete = useCallback3(
2406
+ const handleThemeMenuClose = useCallback4((c) => {
2407
+ setLiveConfig(c);
2408
+ saveConfig(c);
2409
+ setShowThemeMenu(false);
2410
+ setShowSettings(true);
2411
+ }, []);
2412
+ const handleOpenThemeMenu = useCallback4(() => setShowThemeMenu(true), []);
2413
+ const handleSetupComplete = useCallback4(
1256
2414
  (results) => {
1257
2415
  const nc = { ...liveConfig };
1258
2416
  const [hc, mc] = results;
@@ -1275,143 +2433,235 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
1275
2433
  },
1276
2434
  [liveConfig]
1277
2435
  );
1278
- const switchPanel = useCallback3((_dir) => {
1279
- setActivePanel((p) => p === "sessions" ? "activity" : "sessions");
1280
- }, []);
1281
- useInput3((input, key) => {
1282
- if (showSetup || showSettings) return;
1283
- if (inputMode === "nickname") {
1284
- nicknameInput.handleInput(input, key);
1285
- return;
1286
- }
1287
- if (inputMode === "filter") {
1288
- if (key.escape) {
1289
- setFilter("");
1290
- setInputMode("normal");
1291
- filterInput.cancel();
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);
2436
+ const switchPanel = useCallback4(
2437
+ (dir) => {
2438
+ if (splitMode) {
2439
+ const order = ["sessions", "left", "right"];
2440
+ setActivePanel((p) => {
2441
+ const idx = order.indexOf(p);
2442
+ if (idx === -1) return "sessions";
2443
+ return dir === "next" ? order[(idx + 1) % order.length] : order[(idx - 1 + order.length) % order.length];
2444
+ });
2445
+ } else {
2446
+ setActivePanel((p) => p === "sessions" ? "activity" : "sessions");
1304
2447
  }
1305
- return;
1306
- }
1307
- if (matchKey(kb.detail, input, key) && selectedSession && activePanel === "sessions") {
1308
- setShowDetail(true);
1309
- return;
1310
- }
1311
- if (matchKey(kb.panelNext, input, key) || key.rightArrow) {
1312
- switchPanel("next");
1313
- return;
1314
- }
1315
- if (matchKey(kb.panelPrev, input, key) || key.leftArrow) {
1316
- switchPanel("prev");
1317
- return;
1318
- }
1319
- if (matchKey(kb.nickname, input, key) && selectedSession) {
1320
- setInputMode("nickname");
1321
- nicknameInput.start(selectedSession.nickname || "");
1322
- return;
2448
+ },
2449
+ [splitMode]
2450
+ );
2451
+ const getActiveFilter = useCallback4(() => {
2452
+ if (activePanel === "sessions") return filter;
2453
+ if (activePanel === "left") return leftFilter;
2454
+ if (activePanel === "right") return rightFilter;
2455
+ return activityFilter;
2456
+ }, [activePanel, filter, leftFilter, rightFilter, activityFilter]);
2457
+ const clearSplitState = useCallback4(() => {
2458
+ setSplitMode(false);
2459
+ setLeftSession(null);
2460
+ setRightSession(null);
2461
+ setLeftScroll(0);
2462
+ setRightScroll(0);
2463
+ setLeftFilter("");
2464
+ setRightFilter("");
2465
+ setLeftShowDetail(false);
2466
+ setRightShowDetail(false);
2467
+ setActivePanel("sessions");
2468
+ }, []);
2469
+ const resetPanel = useCallback4((side) => {
2470
+ if (side === "left") {
2471
+ setLeftSession(null);
2472
+ setLeftScroll(0);
2473
+ setLeftFilter("");
2474
+ setLeftShowDetail(false);
2475
+ } else {
2476
+ setRightSession(null);
2477
+ setRightScroll(0);
2478
+ setRightFilter("");
2479
+ setRightShowDetail(false);
1323
2480
  }
1324
- if (matchKey(kb.clearNickname, input, key) && selectedSession) {
1325
- clearNickname(selectedSession.sessionId);
2481
+ }, []);
2482
+ useKeyHandler({
2483
+ kb,
2484
+ activePanel,
2485
+ splitMode,
2486
+ inputMode,
2487
+ showSetup,
2488
+ showSettings: showSettings || showThemeMenu,
2489
+ showDetail,
2490
+ leftShowDetail,
2491
+ rightShowDetail,
2492
+ confirmAction,
2493
+ selectedSession,
2494
+ leftSession,
2495
+ rightSession,
2496
+ leftScroll,
2497
+ rightScroll,
2498
+ leftFilter,
2499
+ rightFilter,
2500
+ filter,
2501
+ activityFilter,
2502
+ viewingArchive,
2503
+ archivedIds,
2504
+ updateInfo,
2505
+ maxScroll: Math.max(0, events.length - viewportRows),
2506
+ leftMaxScroll: Math.max(0, leftEvents.length - viewportRows),
2507
+ rightMaxScroll: Math.max(0, rightEvents.length - viewportRows),
2508
+ exit,
2509
+ selectNext,
2510
+ selectPrev,
2511
+ refresh,
2512
+ switchPanel,
2513
+ clearSplitState,
2514
+ resetPanel,
2515
+ getActiveFilter,
2516
+ setActivePanel,
2517
+ setInputMode,
2518
+ setFilter,
2519
+ setActivityFilter,
2520
+ setLeftFilter,
2521
+ setRightFilter,
2522
+ setShowDetail,
2523
+ setLeftShowDetail,
2524
+ setRightShowDetail,
2525
+ setShowSettings,
2526
+ setViewingArchive,
2527
+ setSplitMode,
2528
+ setLeftSession,
2529
+ setRightSession,
2530
+ setLeftScroll,
2531
+ setRightScroll,
2532
+ setActivityScroll,
2533
+ setConfirmAction,
2534
+ setUpdateStatus,
2535
+ nicknameInput,
2536
+ filterInput,
2537
+ onNickname: (id) => {
2538
+ clearNickname(id);
1326
2539
  refresh();
1327
- return;
1328
- }
1329
- if (matchKey(kb.filter, input, key)) {
1330
- setInputMode("filter");
1331
- filterInput.start(filter);
1332
- return;
1333
- }
1334
- if (key.escape && filter) {
1335
- setFilter("");
1336
- return;
1337
- }
1338
- if (matchKey(kb.settings, input, key)) {
1339
- setShowSettings(true);
1340
- return;
1341
- }
1342
- if (matchKey(kb.update, input, key) && updateInfo?.available) {
2540
+ },
2541
+ onClearNickname: (id) => {
2542
+ clearNickname(id);
2543
+ refresh();
2544
+ },
2545
+ onArchive: (id) => {
2546
+ archiveSession(id);
2547
+ refreshArchived();
2548
+ refresh();
2549
+ },
2550
+ onUnarchive: (id) => {
2551
+ unarchiveSession(id);
2552
+ refreshArchived();
2553
+ refresh();
2554
+ },
2555
+ onDelete: (sess) => setConfirmAction({
2556
+ title: "Delete session?",
2557
+ message: `Delete ${sess.nickname || sess.slug}? Output files will be removed.`,
2558
+ onConfirm: () => {
2559
+ deleteSessionFiles(sess.outputFiles);
2560
+ clearNickname(sess.sessionId);
2561
+ if (archivedIds.has(sess.sessionId)) {
2562
+ unarchiveSession(sess.sessionId);
2563
+ refreshArchived();
2564
+ }
2565
+ refresh();
2566
+ setConfirmAction(null);
2567
+ }
2568
+ }),
2569
+ onUpdate: () => {
1343
2570
  setUpdateStatus("updating...");
1344
- installUpdate().then(() => setUpdateStatus(`updated to v${updateInfo.latest} \u2014 restart to apply`)).catch(() => setUpdateStatus("update failed"));
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);
2571
+ installUpdate().then(() => setUpdateStatus(`updated to v${updateInfo?.latest} \u2014 restart to apply`)).catch(() => setUpdateStatus("update failed"));
1356
2572
  }
1357
2573
  });
1358
2574
  if (showSetup) {
1359
- const steps = [];
1360
- if (liveConfig.prompts.hook === "pending")
1361
- steps.push({
1362
- title: "Install Claude Code hook?",
1363
- description: "Adds a PostToolUse hook that blocks prompt injection attempts in real-time."
1364
- });
1365
- if (liveConfig.prompts.mcp === "pending")
1366
- steps.push({
1367
- title: "Install MCP server?",
1368
- description: "Registers agenttop as an MCP server so Claude Code can query session status and alerts."
1369
- });
2575
+ const steps = [
2576
+ ...liveConfig.prompts.hook === "pending" ? [
2577
+ {
2578
+ title: "Install Claude Code hook?",
2579
+ description: "Adds a PostToolUse hook that blocks prompt injection attempts in real-time."
2580
+ }
2581
+ ] : [],
2582
+ ...liveConfig.prompts.mcp === "pending" ? [
2583
+ {
2584
+ title: "Install MCP server?",
2585
+ description: "Registers agenttop as an MCP server so Claude Code can query session status and alerts."
2586
+ }
2587
+ ] : []
2588
+ ];
1370
2589
  if (steps.length === 0) {
1371
2590
  setShowSetup(false);
1372
2591
  return null;
1373
2592
  }
1374
- return /* @__PURE__ */ jsx9(SetupModal, { steps, onComplete: handleSetupComplete });
2593
+ return /* @__PURE__ */ jsx13(SetupModal, { steps, onComplete: handleSetupComplete });
1375
2594
  }
1376
- if (showSettings) {
1377
- return /* @__PURE__ */ jsx9(SettingsMenu, { config: liveConfig, onClose: handleSettingsClose });
2595
+ if (showThemeMenu) return /* @__PURE__ */ jsx13(ThemeMenu, { config: liveConfig, onClose: handleThemeMenuClose });
2596
+ if (showSettings)
2597
+ return /* @__PURE__ */ jsx13(SettingsMenu, { config: liveConfig, onClose: handleSettingsClose, onOpenThemeMenu: handleOpenThemeMenu });
2598
+ if (confirmAction) {
2599
+ return /* @__PURE__ */ jsx13(Box13, { flexDirection: "column", height: termHeight, justifyContent: "center", alignItems: "center", children: /* @__PURE__ */ jsx13(
2600
+ ConfirmModal,
2601
+ {
2602
+ title: confirmAction.title,
2603
+ message: confirmAction.message,
2604
+ onConfirm: confirmAction.onConfirm,
2605
+ onCancel: () => setConfirmAction(null)
2606
+ }
2607
+ ) });
1378
2608
  }
1379
- const rightPanel = showDetail && selectedSession ? /* @__PURE__ */ jsx9(SessionDetail, { session: selectedSession, focused: activePanel === "activity", height: mainHeight }) : /* @__PURE__ */ jsx9(
2609
+ const filterLabel = activePanel === "sessions" ? "sessions" : activePanel === "left" ? "left" : activePanel === "right" ? "right" : "activity";
2610
+ const rightPanel = splitMode ? /* @__PURE__ */ jsx13(
2611
+ SplitPanel,
2612
+ {
2613
+ activePanel,
2614
+ leftSession,
2615
+ rightSession,
2616
+ leftEvents,
2617
+ rightEvents,
2618
+ leftScroll,
2619
+ rightScroll,
2620
+ leftFilter,
2621
+ rightFilter,
2622
+ leftShowDetail,
2623
+ rightShowDetail,
2624
+ height: mainHeight
2625
+ }
2626
+ ) : showDetail && selectedSession ? /* @__PURE__ */ jsx13(SessionDetail, { session: selectedSession, focused: activePanel === "activity", height: mainHeight }) : /* @__PURE__ */ jsx13(
1380
2627
  ActivityFeed,
1381
2628
  {
1382
2629
  events,
1383
2630
  sessionSlug: selectedSession?.slug ?? null,
1384
2631
  focused: activePanel === "activity",
1385
2632
  height: mainHeight,
1386
- scrollOffset: activityScroll
2633
+ scrollOffset: activityScroll,
2634
+ filter: activityFilter || void 0
1387
2635
  }
1388
2636
  );
1389
- return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", height: termHeight, children: [
1390
- /* @__PURE__ */ jsx9(StatusBar, { sessionCount: sessions.length, alertCount: alerts.length, version, updateInfo }),
1391
- /* @__PURE__ */ jsxs9(Box9, { flexGrow: 1, height: mainHeight, children: [
1392
- /* @__PURE__ */ jsx9(
2637
+ return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", height: termHeight, children: [
2638
+ /* @__PURE__ */ jsx13(StatusBar, { sessionCount: sessions.length, alertCount: alerts.length, version, updateInfo }),
2639
+ /* @__PURE__ */ jsxs13(Box13, { flexGrow: 1, height: mainHeight, children: [
2640
+ /* @__PURE__ */ jsx13(
1393
2641
  SessionList,
1394
2642
  {
1395
2643
  sessions,
1396
2644
  selectedIndex,
1397
2645
  focused: activePanel === "sessions",
1398
- filter: filter || void 0
2646
+ filter: filter || void 0,
2647
+ viewingArchive
1399
2648
  }
1400
2649
  ),
1401
2650
  rightPanel
1402
2651
  ] }),
1403
- !options.noSecurity && /* @__PURE__ */ jsx9(AlertBar, { alerts }),
1404
- inputMode === "nickname" && /* @__PURE__ */ jsxs9(Box9, { paddingX: 1, children: [
1405
- /* @__PURE__ */ jsx9(Text9, { color: colors.primary, children: "nickname: " }),
1406
- /* @__PURE__ */ jsx9(Text9, { color: colors.bright, children: nicknameInput.value }),
1407
- /* @__PURE__ */ jsx9(Text9, { color: colors.muted, children: "_" })
2652
+ !options.noSecurity && /* @__PURE__ */ jsx13(AlertBar, { alerts }),
2653
+ inputMode === "nickname" && /* @__PURE__ */ jsxs13(Box13, { paddingX: 1, children: [
2654
+ /* @__PURE__ */ jsx13(Text12, { color: colors.primary, children: "nickname: " }),
2655
+ /* @__PURE__ */ jsx13(Text12, { color: colors.bright, children: nicknameInput.value }),
2656
+ /* @__PURE__ */ jsx13(Text12, { color: colors.muted, children: "_" })
1408
2657
  ] }),
1409
- inputMode === "filter" && /* @__PURE__ */ jsxs9(Box9, { paddingX: 1, children: [
1410
- /* @__PURE__ */ jsx9(Text9, { color: colors.primary, children: "/" }),
1411
- /* @__PURE__ */ jsx9(Text9, { color: colors.bright, children: filterInput.value }),
1412
- /* @__PURE__ */ jsx9(Text9, { color: colors.muted, children: "_" })
2658
+ inputMode === "filter" && /* @__PURE__ */ jsxs13(Box13, { paddingX: 1, children: [
2659
+ /* @__PURE__ */ jsx13(Text12, { color: colors.muted, children: filterLabel }),
2660
+ /* @__PURE__ */ jsx13(Text12, { color: colors.primary, children: "/" }),
2661
+ /* @__PURE__ */ jsx13(Text12, { color: colors.bright, children: filterInput.value }),
2662
+ /* @__PURE__ */ jsx13(Text12, { color: colors.muted, children: "_" })
1413
2663
  ] }),
1414
- inputMode === "normal" && /* @__PURE__ */ jsx9(FooterBar, { keybindings: kb, updateStatus })
2664
+ inputMode === "normal" && /* @__PURE__ */ jsx13(FooterBar, { keybindings: kb, updateStatus, viewingArchive, splitMode })
1415
2665
  ] });
1416
2666
  };
1417
2667
 
@@ -1648,7 +2898,7 @@ var main = () => {
1648
2898
  if (options.noUpdates) config.updates.checkOnLaunch = false;
1649
2899
  if (options.noSecurity) config.security.enabled = false;
1650
2900
  if (firstRun) saveConfig(config);
1651
- render(React10.createElement(App, { options, config, version: VERSION, firstRun }));
2901
+ render(React14.createElement(App, { options, config, version: VERSION, firstRun }));
1652
2902
  };
1653
2903
  main();
1654
2904
  //# sourceMappingURL=index.js.map