bertrand 0.16.0 → 0.18.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.
@@ -19,8 +19,8 @@ import { writeFileSync } from "fs";
19
19
  import { render } from "@orchetron/storm";
20
20
 
21
21
  // src/tui/screens/launch/index.tsx
22
- import { useMemo as useMemo2, useState as useState2 } from "react";
23
- import { Box as Box4, Text as Text5, useTui } from "@orchetron/storm";
22
+ import { useMemo as useMemo2, useState as useState3 } from "react";
23
+ import { Box as Box5, Text as Text6, useTui } from "@orchetron/storm";
24
24
 
25
25
  // src/tui/components/app-details.tsx
26
26
  import { Box, Text } from "@orchetron/storm";
@@ -70,9 +70,178 @@ function Logo() {
70
70
  }, undefined, false, undefined, this);
71
71
  }
72
72
  // src/tui/components/picker.tsx
73
- import { useMemo, useState } from "react";
74
- import { Box as Box3, Text as Text3, TextInput, useInput } from "@orchetron/storm";
73
+ import { useMemo, useState as useState2 } from "react";
74
+ import { Box as Box4, Text as Text4, useGhostText, useInput as useInput2 } from "@orchetron/storm";
75
+
76
+ // src/tui/components/text-field.tsx
77
+ import { useEffect, useRef, useState } from "react";
78
+ import {
79
+ Box as Box3,
80
+ Text as Text3,
81
+ useInput,
82
+ useInterval,
83
+ usePaste
84
+ } from "@orchetron/storm";
75
85
  import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
86
+ var bracketedPasteEnabled = false;
87
+ function ensureBracketedPaste() {
88
+ if (bracketedPasteEnabled)
89
+ return;
90
+ bracketedPasteEnabled = true;
91
+ process.stdout.write("\x1B[?2004h");
92
+ process.once("exit", () => {
93
+ process.stdout.write("\x1B[?2004l");
94
+ });
95
+ }
96
+ function TextField({
97
+ value,
98
+ onChange,
99
+ onSubmit,
100
+ isFocused = true,
101
+ placeholder,
102
+ placeholderColor = "gray",
103
+ color,
104
+ ghost,
105
+ blink = true
106
+ }) {
107
+ const [cursor, setCursor] = useState(value.length);
108
+ useEffect(() => {
109
+ ensureBracketedPaste();
110
+ }, []);
111
+ const lastWrittenRef = useRef(value);
112
+ useEffect(() => {
113
+ if (lastWrittenRef.current !== value) {
114
+ lastWrittenRef.current = value;
115
+ setCursor(value.length);
116
+ }
117
+ }, [value]);
118
+ const [caretOn, setCaretOn] = useState(true);
119
+ useInterval(() => setCaretOn((c) => !c), 500, {
120
+ active: isFocused && blink
121
+ });
122
+ useEffect(() => {
123
+ setCaretOn(true);
124
+ }, [value, cursor]);
125
+ const apply = (newValue, newCursor) => {
126
+ lastWrittenRef.current = newValue;
127
+ onChange(newValue);
128
+ setCursor(newCursor);
129
+ };
130
+ usePaste((text) => {
131
+ if (!isFocused)
132
+ return;
133
+ const sanitized = text.replace(/\r?\n/g, " ");
134
+ if (!sanitized)
135
+ return;
136
+ apply(value.slice(0, cursor) + sanitized + value.slice(cursor), cursor + sanitized.length);
137
+ }, { isActive: isFocused });
138
+ useInput((e) => {
139
+ if (!isFocused)
140
+ return;
141
+ if (e.consumed)
142
+ return;
143
+ if (e.key === "tab" || e.key === "escape" || e.key === "up" || e.key === "down") {
144
+ return;
145
+ }
146
+ if (e.key === "return") {
147
+ onSubmit?.(value);
148
+ e.consumed = true;
149
+ return;
150
+ }
151
+ if (e.key === "backspace") {
152
+ if (cursor > 0) {
153
+ apply(value.slice(0, cursor - 1) + value.slice(cursor), cursor - 1);
154
+ }
155
+ e.consumed = true;
156
+ return;
157
+ }
158
+ if (e.key === "delete") {
159
+ if (cursor < value.length) {
160
+ apply(value.slice(0, cursor) + value.slice(cursor + 1), cursor);
161
+ }
162
+ e.consumed = true;
163
+ return;
164
+ }
165
+ if (e.key === "left") {
166
+ if (cursor > 0) {
167
+ setCursor(cursor - 1);
168
+ e.consumed = true;
169
+ }
170
+ return;
171
+ }
172
+ if (e.key === "right") {
173
+ if (cursor < value.length) {
174
+ setCursor(cursor + 1);
175
+ e.consumed = true;
176
+ }
177
+ return;
178
+ }
179
+ if (e.key === "home") {
180
+ setCursor(0);
181
+ e.consumed = true;
182
+ return;
183
+ }
184
+ if (e.key === "end") {
185
+ setCursor(value.length);
186
+ e.consumed = true;
187
+ return;
188
+ }
189
+ if (e.char && !e.ctrl && !e.meta) {
190
+ apply(value.slice(0, cursor) + e.char + value.slice(cursor), cursor + e.char.length);
191
+ e.consumed = true;
192
+ }
193
+ }, { isActive: isFocused, priority: 1 });
194
+ const showCaret = isFocused && (!blink || caretOn);
195
+ if (value.length === 0) {
196
+ return /* @__PURE__ */ jsxDEV3(Box3, {
197
+ flexDirection: "row",
198
+ children: [
199
+ /* @__PURE__ */ jsxDEV3(Text3, {
200
+ inverse: showCaret,
201
+ color,
202
+ children: " "
203
+ }, undefined, false, undefined, this),
204
+ placeholder && /* @__PURE__ */ jsxDEV3(Text3, {
205
+ color: placeholderColor,
206
+ dim: true,
207
+ children: placeholder
208
+ }, undefined, false, undefined, this),
209
+ ghost && /* @__PURE__ */ jsxDEV3(Text3, {
210
+ dim: true,
211
+ children: ghost
212
+ }, undefined, false, undefined, this)
213
+ ]
214
+ }, undefined, true, undefined, this);
215
+ }
216
+ const before = value.slice(0, cursor);
217
+ const atCursor = value.slice(cursor, cursor + 1) || " ";
218
+ const after = value.slice(cursor + 1);
219
+ return /* @__PURE__ */ jsxDEV3(Box3, {
220
+ flexDirection: "row",
221
+ children: [
222
+ before && /* @__PURE__ */ jsxDEV3(Text3, {
223
+ color,
224
+ children: before
225
+ }, undefined, false, undefined, this),
226
+ /* @__PURE__ */ jsxDEV3(Text3, {
227
+ inverse: showCaret,
228
+ color,
229
+ children: atCursor
230
+ }, undefined, false, undefined, this),
231
+ after && /* @__PURE__ */ jsxDEV3(Text3, {
232
+ color,
233
+ children: after
234
+ }, undefined, false, undefined, this),
235
+ ghost && /* @__PURE__ */ jsxDEV3(Text3, {
236
+ dim: true,
237
+ children: ghost
238
+ }, undefined, false, undefined, this)
239
+ ]
240
+ }, undefined, true, undefined, this);
241
+ }
242
+
243
+ // src/tui/components/picker.tsx
244
+ import { jsxDEV as jsxDEV4 } from "react/jsx-dev-runtime";
76
245
  var NEW_KEY = "__new__";
77
246
  function isSelectable(row) {
78
247
  if (row.value === NEW_KEY)
@@ -92,6 +261,38 @@ function findNextSelectable(rows, from, direction) {
92
261
  function findFirstSelectable(rows) {
93
262
  return findNextSelectable(rows, 0, 1);
94
263
  }
264
+ function isHeader(row) {
265
+ return !!row && row.value !== NEW_KEY && row.kind === "header";
266
+ }
267
+ function findCurrentGroupStart(rows, cursor) {
268
+ for (let i = cursor;i >= 0; i--) {
269
+ if (isHeader(rows[i])) {
270
+ return findNextSelectable(rows, i + 1, 1);
271
+ }
272
+ }
273
+ return findFirstSelectable(rows);
274
+ }
275
+ function findNextGroupStart(rows, cursor) {
276
+ for (let i = cursor + 1;i < rows.length; i++) {
277
+ if (isHeader(rows[i])) {
278
+ return findNextSelectable(rows, i + 1, 1);
279
+ }
280
+ }
281
+ return -1;
282
+ }
283
+ function findPrevGroupStart(rows, cursor) {
284
+ let i = cursor;
285
+ for (;i >= 0; i--) {
286
+ if (isHeader(rows[i]))
287
+ break;
288
+ }
289
+ for (i = i - 1;i >= 0; i--) {
290
+ if (isHeader(rows[i])) {
291
+ return findNextSelectable(rows, i + 1, 1);
292
+ }
293
+ }
294
+ return -1;
295
+ }
95
296
  function Picker(props) {
96
297
  const {
97
298
  items,
@@ -101,8 +302,14 @@ function Picker(props) {
101
302
  emptyHint,
102
303
  maxVisible = 12
103
304
  } = props;
104
- const [filter, setFilter] = useState("");
105
- const [cursor, setCursor] = useState(0);
305
+ const [filter, setFilter] = useState2("");
306
+ const [cursor, setCursor] = useState2(0);
307
+ const ghostResult = useGhostText({
308
+ value: filter,
309
+ cursor: filter.length,
310
+ suggest: props.suggest ?? []
311
+ });
312
+ const ghost = props.suggest ? ghostResult.ghost : "";
106
313
  const filtered = useMemo(() => {
107
314
  const q = filter.trim().toLowerCase();
108
315
  if (!q)
@@ -121,7 +328,7 @@ function Picker(props) {
121
328
  setTimeout(() => setCursor(next), 0);
122
329
  }
123
330
  }
124
- useInput((e) => {
331
+ useInput2((e) => {
125
332
  if (!isFocused)
126
333
  return;
127
334
  if (e.key === "escape" && filter.length > 0) {
@@ -141,8 +348,29 @@ function Picker(props) {
141
348
  const next = findNextSelectable(visibleRows, c + 1, 1);
142
349
  return next === -1 ? c : next;
143
350
  });
351
+ } else if (filter.length === 0 && e.key === "left") {
352
+ setCursor((c) => {
353
+ const curr = findCurrentGroupStart(visibleRows, c);
354
+ if (curr !== -1 && curr !== c)
355
+ return curr;
356
+ const prev = findPrevGroupStart(visibleRows, c);
357
+ return prev === -1 ? c : prev;
358
+ });
359
+ } else if (filter.length === 0 && e.key === "right") {
360
+ setCursor((c) => {
361
+ const next = findNextGroupStart(visibleRows, c);
362
+ return next === -1 ? c : next;
363
+ });
364
+ } else if (e.key === "tab" && ghost) {
365
+ const full = ghostResult.accept();
366
+ if (full !== null)
367
+ setFilter(full);
144
368
  } else if (e.key === "tab" && props.mode === "multi") {
145
369
  props.onDone();
370
+ } else if (props.onKey) {
371
+ const row = visibleRows[cursor];
372
+ const cursorItem = row && row.value !== NEW_KEY ? row : null;
373
+ props.onKey(e, cursorItem);
146
374
  }
147
375
  }, { isActive: isFocused });
148
376
  const submitCursor = () => {
@@ -176,50 +404,49 @@ function Picker(props) {
176
404
  const visibleEnd = Math.min(visibleRows.length, visibleStart + maxVisible);
177
405
  const slice = visibleRows.slice(visibleStart, visibleEnd);
178
406
  const selectedSet = props.mode === "multi" ? new Set(props.selected) : new Set;
179
- return /* @__PURE__ */ jsxDEV3(Box3, {
407
+ return /* @__PURE__ */ jsxDEV4(Box4, {
180
408
  flexDirection: "column",
181
409
  gap: 0,
182
410
  width: "100%",
183
411
  children: [
184
- props.mode === "multi" && props.selected.length > 0 && /* @__PURE__ */ jsxDEV3(Box3, {
412
+ props.mode === "multi" && props.selected.length > 0 && /* @__PURE__ */ jsxDEV4(Box4, {
185
413
  flexDirection: "row",
186
414
  gap: 1,
187
415
  children: [
188
- /* @__PURE__ */ jsxDEV3(Text3, {
416
+ /* @__PURE__ */ jsxDEV4(Text4, {
189
417
  dim: true,
190
418
  children: "Selected:"
191
419
  }, undefined, false, undefined, this),
192
420
  props.selected.map((v) => {
193
421
  const item = items.find((i) => i.value === v);
194
- return /* @__PURE__ */ jsxDEV3(Text3, {
422
+ return /* @__PURE__ */ jsxDEV4(Text4, {
195
423
  color: item?.color ?? "cyan",
196
424
  children: item?.label ?? v
197
425
  }, v, false, undefined, this);
198
426
  })
199
427
  ]
200
428
  }, undefined, true, undefined, this),
201
- /* @__PURE__ */ jsxDEV3(Box3, {
429
+ /* @__PURE__ */ jsxDEV4(Box4, {
202
430
  borderStyle: "round",
203
431
  borderColor: isFocused ? "green" : undefined,
204
432
  borderDimColor: !isFocused,
205
433
  paddingX: 1,
206
- children: /* @__PURE__ */ jsxDEV3(TextInput, {
434
+ children: /* @__PURE__ */ jsxDEV4(TextField, {
207
435
  value: filter,
208
436
  onChange: setFilter,
209
437
  onSubmit: handleSubmit,
210
438
  placeholder,
211
- color: "green",
212
- placeholderColor: "gray",
213
- isFocused
439
+ isFocused,
440
+ ghost
214
441
  }, undefined, false, undefined, this)
215
442
  }, undefined, false, undefined, this),
216
- /* @__PURE__ */ jsxDEV3(Box3, {
443
+ /* @__PURE__ */ jsxDEV4(Box4, {
217
444
  flexDirection: "column",
218
445
  borderStyle: "round",
219
446
  borderDimColor: true,
220
447
  paddingX: 1,
221
448
  children: [
222
- visibleStart > 0 && /* @__PURE__ */ jsxDEV3(Text3, {
449
+ visibleStart > 0 && /* @__PURE__ */ jsxDEV4(Text4, {
223
450
  dim: true,
224
451
  children: [
225
452
  "\u25B2 ",
@@ -227,7 +454,7 @@ function Picker(props) {
227
454
  " more above"
228
455
  ]
229
456
  }, undefined, true, undefined, this),
230
- slice.length === 0 && emptyHint && /* @__PURE__ */ jsxDEV3(Text3, {
457
+ slice.length === 0 && emptyHint && /* @__PURE__ */ jsxDEV4(Text4, {
231
458
  dim: true,
232
459
  children: emptyHint
233
460
  }, undefined, false, undefined, this),
@@ -237,21 +464,21 @@ function Picker(props) {
237
464
  const isNew = row.value === NEW_KEY;
238
465
  const showDivider = isNew && filtered.length > 0 && i > 0;
239
466
  if (isNew) {
240
- return /* @__PURE__ */ jsxDEV3(Box3, {
467
+ return /* @__PURE__ */ jsxDEV4(Box4, {
241
468
  flexDirection: "column",
242
469
  children: [
243
- showDivider && /* @__PURE__ */ jsxDEV3(Text3, {
470
+ showDivider && /* @__PURE__ */ jsxDEV4(Text4, {
244
471
  dim: true,
245
472
  children: "\u2500".repeat(Math.min(20, filter.length + 12))
246
473
  }, undefined, false, undefined, this),
247
- /* @__PURE__ */ jsxDEV3(Box3, {
474
+ /* @__PURE__ */ jsxDEV4(Box4, {
248
475
  flexDirection: "row",
249
476
  gap: 1,
250
- backgroundColor: isCursor ? "green" : undefined,
251
- children: /* @__PURE__ */ jsxDEV3(Text3, {
252
- color: isCursor ? "black" : "cyan",
477
+ children: /* @__PURE__ */ jsxDEV4(Text4, {
478
+ color: isCursor ? "green" : "cyan",
253
479
  bold: true,
254
480
  children: [
481
+ isCursor ? "\u276F " : " ",
255
482
  "\u271A create \u201C",
256
483
  filter.trim(),
257
484
  "\u201D"
@@ -263,9 +490,9 @@ function Picker(props) {
263
490
  }
264
491
  const item = row;
265
492
  if (item.kind === "header") {
266
- return /* @__PURE__ */ jsxDEV3(Box3, {
493
+ return /* @__PURE__ */ jsxDEV4(Box4, {
267
494
  flexDirection: "row",
268
- children: /* @__PURE__ */ jsxDEV3(Text3, {
495
+ children: /* @__PURE__ */ jsxDEV4(Text4, {
269
496
  dim: true,
270
497
  bold: true,
271
498
  color: item.color ?? undefined,
@@ -276,39 +503,35 @@ function Picker(props) {
276
503
  const isSelected = selectedSet.has(item.value);
277
504
  const marker = props.mode === "multi" ? isSelected ? "\u2713 " : " " : "";
278
505
  const dim = item.dim || item.disabled;
279
- return /* @__PURE__ */ jsxDEV3(Box3, {
506
+ return /* @__PURE__ */ jsxDEV4(Box4, {
280
507
  flexDirection: "row",
281
508
  justifyContent: "space-between",
282
509
  gap: 1,
283
- backgroundColor: isCursor ? "green" : undefined,
284
510
  children: [
285
- /* @__PURE__ */ jsxDEV3(Box3, {
511
+ /* @__PURE__ */ jsxDEV4(Box4, {
286
512
  flexDirection: "row",
287
513
  gap: 0,
288
- children: item.display ? item.display : /* @__PURE__ */ jsxDEV3(Text3, {
289
- color: isCursor ? "black" : item.color ?? undefined,
514
+ children: item.display ? typeof item.display === "function" ? item.display(isCursor) : item.display : /* @__PURE__ */ jsxDEV4(Text4, {
515
+ color: isCursor ? "green" : item.color ?? undefined,
290
516
  bold: isCursor,
291
517
  dim: !isCursor && dim,
292
518
  children: [
519
+ isCursor ? "\u276F " : " ",
293
520
  marker,
294
521
  item.label
295
522
  ]
296
523
  }, undefined, true, undefined, this)
297
524
  }, undefined, false, undefined, this),
298
- item.meta && /* @__PURE__ */ jsxDEV3(Box3, {
299
- paddingX: 1,
300
- backgroundColor: isCursor ? "black" : "#3a3a3a",
301
- children: /* @__PURE__ */ jsxDEV3(Text3, {
302
- color: isCursor ? "green" : "white",
303
- bold: true,
304
- dim: !isCursor && dim,
305
- children: item.meta
306
- }, undefined, false, undefined, this)
525
+ item.meta && /* @__PURE__ */ jsxDEV4(Text4, {
526
+ color: isCursor ? "green" : undefined,
527
+ bold: isCursor,
528
+ dim: !isCursor,
529
+ children: item.meta
307
530
  }, undefined, false, undefined, this)
308
531
  ]
309
532
  }, item.value, true, undefined, this);
310
533
  }),
311
- visibleEnd < visibleRows.length && /* @__PURE__ */ jsxDEV3(Text3, {
534
+ visibleEnd < visibleRows.length && /* @__PURE__ */ jsxDEV4(Text4, {
312
535
  dim: true,
313
536
  children: [
314
537
  "\u25BC ",
@@ -322,8 +545,8 @@ function Picker(props) {
322
545
  }, undefined, true, undefined, this);
323
546
  }
324
547
  // src/tui/components/status-dot.tsx
325
- import { Text as Text4 } from "@orchetron/storm";
326
- import { jsxDEV as jsxDEV4 } from "react/jsx-dev-runtime";
548
+ import { Text as Text5 } from "@orchetron/storm";
549
+ import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
327
550
  var STATUS_COLORS = {
328
551
  active: "orange",
329
552
  waiting: "red",
@@ -332,7 +555,7 @@ var STATUS_COLORS = {
332
555
  };
333
556
  function StatusDot({ status }) {
334
557
  const color = STATUS_COLORS[status] ?? "#6B7280";
335
- return /* @__PURE__ */ jsxDEV4(Text4, {
558
+ return /* @__PURE__ */ jsxDEV5(Text5, {
336
559
  color,
337
560
  children: "\u25CF"
338
561
  }, undefined, false, undefined, this);
@@ -354,7 +577,8 @@ var paths = {
354
577
  root: join(homedir(), BERTRAND_DIR),
355
578
  db: join(homedir(), BERTRAND_DIR, "bertrand.db"),
356
579
  hooks: join(homedir(), BERTRAND_DIR, "hooks"),
357
- sessions: join(homedir(), BERTRAND_DIR, "sessions")
580
+ sessions: join(homedir(), BERTRAND_DIR, "sessions"),
581
+ syncEnv: join(homedir(), BERTRAND_DIR, "sync.env")
358
582
  };
359
583
 
360
584
  // src/db/schema.ts
@@ -504,6 +728,35 @@ function getAllSessions(opts) {
504
728
  }
505
729
  return query.all();
506
730
  }
731
+ function updateSessionStatus(id, status) {
732
+ return getDb().update(sessions).set({ status, updatedAt: sql2`(datetime('now'))` }).where(eq(sessions.id, id)).returning().get();
733
+ }
734
+
735
+ // src/lib/session-archive.ts
736
+ var ACTIVE_STATUSES = ["active", "waiting"];
737
+ function archiveSession(id) {
738
+ const session = getSession(id);
739
+ if (!session)
740
+ return { ok: false, reason: "not-found" };
741
+ if (ACTIVE_STATUSES.includes(session.status)) {
742
+ return { ok: false, reason: "active" };
743
+ }
744
+ if (session.status === "archived") {
745
+ return { ok: false, reason: "already-archived" };
746
+ }
747
+ const updated = updateSessionStatus(id, "archived");
748
+ return { ok: true, session: updated };
749
+ }
750
+ function unarchiveSession(id) {
751
+ const session = getSession(id);
752
+ if (!session)
753
+ return { ok: false, reason: "not-found" };
754
+ if (session.status !== "archived") {
755
+ return { ok: false, reason: "not-archived" };
756
+ }
757
+ const updated = updateSessionStatus(id, "paused");
758
+ return { ok: true, session: updated };
759
+ }
507
760
 
508
761
  // src/lib/format.ts
509
762
  var SECOND = 1000;
@@ -559,14 +812,16 @@ function parseSessionName(input) {
559
812
  }
560
813
 
561
814
  // src/tui/screens/launch/index.tsx
562
- import { jsxDEV as jsxDEV5, Fragment } from "react/jsx-dev-runtime";
815
+ import { jsxDEV as jsxDEV6, Fragment } from "react/jsx-dev-runtime";
563
816
  var STATUS_COLOR = {
564
817
  paused: "gold",
565
- waiting: "red"
818
+ waiting: "red",
819
+ archived: "purple"
566
820
  };
567
821
  var STATUS_RANK = {
568
822
  paused: 0,
569
- waiting: 1
823
+ waiting: 1,
824
+ archived: 2
570
825
  };
571
826
  function statusRank(status) {
572
827
  return STATUS_RANK[status] ?? 99;
@@ -578,34 +833,53 @@ function sessionRow(s) {
578
833
  const status = s.session.status;
579
834
  const color = STATUS_COLOR[status] ?? "gray";
580
835
  const disabled = status === "waiting";
836
+ const isArchived = status === "archived";
581
837
  return {
582
838
  value: `${s.groupPath}/${s.session.slug}`,
583
839
  label: `${s.groupPath}/${s.session.slug} ${status}`,
584
840
  meta: formatAgo(recencyKey(s)),
585
841
  disabled,
586
- display: /* @__PURE__ */ jsxDEV5(Fragment, {
587
- children: [
588
- /* @__PURE__ */ jsxDEV5(Text5, {
589
- children: " "
590
- }, undefined, false, undefined, this),
591
- /* @__PURE__ */ jsxDEV5(Text5, {
592
- color,
593
- children: "\u25CF "
594
- }, undefined, false, undefined, this),
595
- /* @__PURE__ */ jsxDEV5(Text5, {
596
- color,
597
- dim: disabled,
598
- children: status.padEnd(8)
599
- }, undefined, false, undefined, this),
600
- /* @__PURE__ */ jsxDEV5(Text5, {
601
- children: " "
602
- }, undefined, false, undefined, this),
603
- /* @__PURE__ */ jsxDEV5(Text5, {
604
- dim: disabled,
605
- children: s.session.slug
606
- }, undefined, false, undefined, this)
607
- ]
608
- }, undefined, true, undefined, this)
842
+ dim: isArchived,
843
+ display: (isCursor) => {
844
+ const cursorColor = "green";
845
+ const dotColor = isCursor ? cursorColor : color;
846
+ const textColor = isCursor ? cursorColor : color;
847
+ const slugColor = isCursor ? cursorColor : undefined;
848
+ const dimText = !isCursor && (disabled || isArchived);
849
+ return /* @__PURE__ */ jsxDEV6(Fragment, {
850
+ children: [
851
+ /* @__PURE__ */ jsxDEV6(Text6, {
852
+ color: cursorColor,
853
+ bold: true,
854
+ children: isCursor ? "\u276F " : " "
855
+ }, undefined, false, undefined, this),
856
+ /* @__PURE__ */ jsxDEV6(Text6, {
857
+ color: dotColor,
858
+ bold: isCursor,
859
+ children: [
860
+ "\u25CF",
861
+ " "
862
+ ]
863
+ }, undefined, true, undefined, this),
864
+ /* @__PURE__ */ jsxDEV6(Text6, {
865
+ color: textColor,
866
+ bold: isCursor,
867
+ dim: dimText,
868
+ children: status.padEnd(8)
869
+ }, undefined, false, undefined, this),
870
+ /* @__PURE__ */ jsxDEV6(Text6, {
871
+ color: slugColor,
872
+ children: " "
873
+ }, undefined, false, undefined, this),
874
+ /* @__PURE__ */ jsxDEV6(Text6, {
875
+ color: slugColor,
876
+ bold: isCursor,
877
+ dim: dimText,
878
+ children: s.session.slug
879
+ }, undefined, false, undefined, this)
880
+ ]
881
+ }, undefined, true, undefined, this);
882
+ }
609
883
  };
610
884
  }
611
885
  function groupHeader(groupPath) {
@@ -617,10 +891,20 @@ function groupHeader(groupPath) {
617
891
  }
618
892
  function Launch({ onSelect }) {
619
893
  const { exit } = useTui();
620
- const [error, setError] = useState2(null);
621
- const allSessions = useMemo2(() => getAllSessions({ excludeArchived: true }), []);
894
+ const [error, setError] = useState3(null);
895
+ const [notice, setNotice] = useState3(null);
896
+ const [showArchived, setShowArchived] = useState3(false);
897
+ const [refreshKey, setRefreshKey] = useState3(0);
898
+ const allSessions = useMemo2(() => getAllSessions({ excludeArchived: !showArchived }), [showArchived, refreshKey]);
622
899
  const visibleSessions = useMemo2(() => {
623
- return allSessions.filter((s) => s.session.status === "paused" || s.session.status === "waiting").sort((a, b) => {
900
+ return allSessions.filter((s) => {
901
+ const st = s.session.status;
902
+ if (st === "paused" || st === "waiting")
903
+ return true;
904
+ if (st === "archived")
905
+ return showArchived;
906
+ return false;
907
+ }).sort((a, b) => {
624
908
  const g = a.groupPath.localeCompare(b.groupPath);
625
909
  if (g !== 0)
626
910
  return g;
@@ -629,7 +913,7 @@ function Launch({ onSelect }) {
629
913
  return r;
630
914
  return recencyKey(b).localeCompare(recencyKey(a));
631
915
  });
632
- }, [allSessions]);
916
+ }, [allSessions, showArchived]);
633
917
  const items = useMemo2(() => {
634
918
  const rows = [];
635
919
  let lastGroup = null;
@@ -642,6 +926,15 @@ function Launch({ onSelect }) {
642
926
  }
643
927
  return rows;
644
928
  }, [visibleSessions]);
929
+ const suggestions = useMemo2(() => {
930
+ const groups2 = new Set;
931
+ const names = [];
932
+ for (const s of allSessions) {
933
+ groups2.add(`${s.groupPath}/`);
934
+ names.push(`${s.groupPath}/${s.session.slug}`);
935
+ }
936
+ return [...groups2, ...names];
937
+ }, [allSessions]);
645
938
  const sessionByValue = useMemo2(() => {
646
939
  const map = new Map;
647
940
  for (const s of allSessions) {
@@ -649,6 +942,31 @@ function Launch({ onSelect }) {
649
942
  }
650
943
  return map;
651
944
  }, [allSessions]);
945
+ const handleArchiveKey = (cursorItem) => {
946
+ if (!cursorItem)
947
+ return;
948
+ const existing = sessionByValue.get(cursorItem.value);
949
+ if (!existing)
950
+ return;
951
+ setError(null);
952
+ if (existing.session.status === "archived") {
953
+ const result2 = unarchiveSession(existing.session.id);
954
+ if (result2.ok) {
955
+ setNotice(`Unarchived ${cursorItem.value}`);
956
+ setRefreshKey((k) => k + 1);
957
+ } else {
958
+ setError(`Couldn't unarchive: ${result2.reason}`);
959
+ }
960
+ return;
961
+ }
962
+ const result = archiveSession(existing.session.id);
963
+ if (result.ok) {
964
+ setNotice(`Archived ${cursorItem.value}`);
965
+ setRefreshKey((k) => k + 1);
966
+ } else {
967
+ setError(`Couldn't archive: ${result.reason}`);
968
+ }
969
+ };
652
970
  const select = (selection) => {
653
971
  onSelect(selection);
654
972
  exit();
@@ -673,64 +991,67 @@ function Launch({ onSelect }) {
673
991
  const counts = useMemo2(() => {
674
992
  let paused = 0;
675
993
  let waiting = 0;
994
+ let archived = 0;
676
995
  for (const s of visibleSessions) {
677
996
  if (s.session.status === "paused")
678
997
  paused++;
679
998
  else if (s.session.status === "waiting")
680
999
  waiting++;
1000
+ else if (s.session.status === "archived")
1001
+ archived++;
681
1002
  }
682
- return { paused, waiting };
1003
+ return { paused, waiting, archived };
683
1004
  }, [visibleSessions]);
684
- return /* @__PURE__ */ jsxDEV5(Box4, {
1005
+ return /* @__PURE__ */ jsxDEV6(Box5, {
685
1006
  flexDirection: "column",
686
1007
  paddingY: 1,
687
1008
  gap: 1,
688
1009
  children: [
689
- /* @__PURE__ */ jsxDEV5(Box4, {
1010
+ /* @__PURE__ */ jsxDEV6(Box5, {
690
1011
  marginX: 1,
691
- children: /* @__PURE__ */ jsxDEV5(Logo, {}, undefined, false, undefined, this)
1012
+ children: /* @__PURE__ */ jsxDEV6(Logo, {}, undefined, false, undefined, this)
692
1013
  }, undefined, false, undefined, this),
693
- /* @__PURE__ */ jsxDEV5(Box4, {
1014
+ /* @__PURE__ */ jsxDEV6(Box5, {
694
1015
  flexDirection: "column",
695
1016
  marginX: 2,
696
1017
  gap: 1,
697
1018
  children: [
698
- /* @__PURE__ */ jsxDEV5(AppDetails, {}, undefined, false, undefined, this),
699
- /* @__PURE__ */ jsxDEV5(Box4, {
1019
+ /* @__PURE__ */ jsxDEV6(AppDetails, {}, undefined, false, undefined, this),
1020
+ /* @__PURE__ */ jsxDEV6(Box5, {
700
1021
  flexDirection: "column",
701
1022
  gap: 1,
702
1023
  children: [
703
- /* @__PURE__ */ jsxDEV5(Box4, {
1024
+ /* @__PURE__ */ jsxDEV6(Box5, {
704
1025
  flexDirection: "row",
705
1026
  gap: 1,
706
1027
  children: [
707
- /* @__PURE__ */ jsxDEV5(Text5, {
1028
+ /* @__PURE__ */ jsxDEV6(Text6, {
708
1029
  bold: true,
709
1030
  children: "Sessions"
710
1031
  }, undefined, false, undefined, this),
711
- visibleSessions.length === 0 ? /* @__PURE__ */ jsxDEV5(Text5, {
1032
+ visibleSessions.length === 0 ? /* @__PURE__ */ jsxDEV6(Text6, {
712
1033
  dim: true,
713
1034
  children: "\xB7 none \u2014 type group/slug to create"
714
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV5(Fragment, {
1035
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV6(Fragment, {
715
1036
  children: [
716
- /* @__PURE__ */ jsxDEV5(Text5, {
1037
+ /* @__PURE__ */ jsxDEV6(Text6, {
717
1038
  dim: true,
718
1039
  children: "\xB7"
719
1040
  }, undefined, false, undefined, this),
720
- /* @__PURE__ */ jsxDEV5(Text5, {
1041
+ /* @__PURE__ */ jsxDEV6(Text6, {
721
1042
  color: "gold",
722
1043
  children: [
723
1044
  counts.paused,
724
1045
  " paused"
725
1046
  ]
726
1047
  }, undefined, true, undefined, this),
727
- counts.waiting > 0 && /* @__PURE__ */ jsxDEV5(Fragment, {
1048
+ counts.waiting > 0 && /* @__PURE__ */ jsxDEV6(Fragment, {
728
1049
  children: [
729
- /* @__PURE__ */ jsxDEV5(Text5, {
1050
+ /* @__PURE__ */ jsxDEV6(Text6, {
730
1051
  dim: true,
731
1052
  children: "\xB7"
732
1053
  }, undefined, false, undefined, this),
733
- /* @__PURE__ */ jsxDEV5(Text5, {
1054
+ /* @__PURE__ */ jsxDEV6(Text6, {
734
1055
  color: "red",
735
1056
  dim: true,
736
1057
  children: [
@@ -739,27 +1060,68 @@ function Launch({ onSelect }) {
739
1060
  ]
740
1061
  }, undefined, true, undefined, this)
741
1062
  ]
1063
+ }, undefined, true, undefined, this),
1064
+ showArchived && counts.archived > 0 && /* @__PURE__ */ jsxDEV6(Fragment, {
1065
+ children: [
1066
+ /* @__PURE__ */ jsxDEV6(Text6, {
1067
+ dim: true,
1068
+ children: "\xB7"
1069
+ }, undefined, false, undefined, this),
1070
+ /* @__PURE__ */ jsxDEV6(Text6, {
1071
+ color: "purple",
1072
+ dim: true,
1073
+ children: [
1074
+ counts.archived,
1075
+ " archived"
1076
+ ]
1077
+ }, undefined, true, undefined, this)
1078
+ ]
742
1079
  }, undefined, true, undefined, this)
743
1080
  ]
744
1081
  }, undefined, true, undefined, this)
745
1082
  ]
746
1083
  }, undefined, true, undefined, this),
747
- /* @__PURE__ */ jsxDEV5(Picker, {
1084
+ /* @__PURE__ */ jsxDEV6(Picker, {
748
1085
  mode: "single",
749
1086
  items,
750
1087
  isFocused: true,
1088
+ maxVisible: 24,
1089
+ suggest: suggestions,
751
1090
  placeholder: "Filter or type group/slug to create\u2026",
752
- emptyHint: "No paused sessions. Type group/slug to create one.",
753
- onSubmit: handleSubmit
1091
+ emptyHint: showArchived ? "No sessions. Type group/slug to create one." : "No paused sessions. Type group/slug to create one.",
1092
+ onSubmit: handleSubmit,
1093
+ onKey: (e, cursorItem) => {
1094
+ if (e.key === "c" && e.ctrl) {
1095
+ select({ type: "quit" });
1096
+ } else if (e.key === "a" && e.ctrl) {
1097
+ handleArchiveKey(cursorItem);
1098
+ } else if (e.key === "tab") {
1099
+ setError(null);
1100
+ setNotice(null);
1101
+ setShowArchived((v) => !v);
1102
+ }
1103
+ }
1104
+ }, undefined, false, undefined, this),
1105
+ notice && /* @__PURE__ */ jsxDEV6(Text6, {
1106
+ color: "green",
1107
+ children: notice
754
1108
  }, undefined, false, undefined, this),
755
- error && /* @__PURE__ */ jsxDEV5(Text5, {
1109
+ error && /* @__PURE__ */ jsxDEV6(Text6, {
756
1110
  color: "red",
757
1111
  children: error
758
1112
  }, undefined, false, undefined, this),
759
- /* @__PURE__ */ jsxDEV5(Text5, {
1113
+ /* @__PURE__ */ jsxDEV6(Text6, {
760
1114
  dim: true,
761
- children: "\u2191\u2193 navigate \xB7 enter continue/create \xB7 esc clear \xB7 ctrl+c quit"
762
- }, undefined, false, undefined, this)
1115
+ children: [
1116
+ "\u2191\u2193 navigate \xB7 \u2190\u2192 skip group \xB7 enter continue/create \xB7 ctrl+a",
1117
+ " ",
1118
+ showArchived ? "(un)archive" : "archive",
1119
+ " \xB7 tab",
1120
+ " ",
1121
+ showArchived ? "hide" : "show",
1122
+ " archived \xB7 ctrl+c quit"
1123
+ ]
1124
+ }, undefined, true, undefined, this)
763
1125
  ]
764
1126
  }, undefined, true, undefined, this)
765
1127
  ]
@@ -769,8 +1131,8 @@ function Launch({ onSelect }) {
769
1131
  }
770
1132
 
771
1133
  // src/tui/screens/Exit.tsx
772
- import { useState as useState3 } from "react";
773
- import { Box as Box5, Text as Text6, useInput as useInput2, useTui as useTui2 } from "@orchetron/storm";
1134
+ import { useState as useState4 } from "react";
1135
+ import { Box as Box6, Text as Text7, useInput as useInput3, useTui as useTui2 } from "@orchetron/storm";
774
1136
 
775
1137
  // src/db/queries/conversations.ts
776
1138
  import { eq as eq2, and as and2, desc, sql as sql3 } from "drizzle-orm";
@@ -779,7 +1141,7 @@ function getConversationsBySession(sessionId) {
779
1141
  }
780
1142
 
781
1143
  // src/tui/screens/Exit.tsx
782
- import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
1144
+ import { jsxDEV as jsxDEV7 } from "react/jsx-dev-runtime";
783
1145
  var OPTIONS = [
784
1146
  { action: "save", label: "Save", hint: "Keep session paused for later" },
785
1147
  {
@@ -800,10 +1162,10 @@ var OPTIONS = [
800
1162
  ];
801
1163
  function Exit({ sessionId, onAction }) {
802
1164
  const { exit } = useTui2();
803
- const [cursor, setCursor] = useState3(0);
1165
+ const [cursor, setCursor] = useState4(0);
804
1166
  const session = getSession(sessionId);
805
1167
  const conversations2 = session ? getConversationsBySession(session.id) : [];
806
- useInput2((e) => {
1168
+ useInput3((e) => {
807
1169
  if (e.key === "c" && e.ctrl)
808
1170
  exit();
809
1171
  if (e.key === "up" || e.key === "k") {
@@ -820,37 +1182,37 @@ function Exit({ sessionId, onAction }) {
820
1182
  }
821
1183
  });
822
1184
  if (!session) {
823
- return /* @__PURE__ */ jsxDEV6(Text6, {
1185
+ return /* @__PURE__ */ jsxDEV7(Text7, {
824
1186
  color: "red",
825
1187
  children: "Session not found"
826
1188
  }, undefined, false, undefined, this);
827
1189
  }
828
1190
  const duration = session.endedAt && session.startedAt ? formatDuration(new Date(session.endedAt).getTime() - new Date(session.startedAt).getTime()) : null;
829
- return /* @__PURE__ */ jsxDEV6(Box5, {
1191
+ return /* @__PURE__ */ jsxDEV7(Box6, {
830
1192
  flexDirection: "column",
831
1193
  padding: 1,
832
1194
  gap: 1,
833
1195
  children: [
834
- /* @__PURE__ */ jsxDEV6(Text6, {
1196
+ /* @__PURE__ */ jsxDEV7(Text7, {
835
1197
  bold: true,
836
1198
  color: "#82AAFF",
837
1199
  children: "Session ended"
838
1200
  }, undefined, false, undefined, this),
839
- /* @__PURE__ */ jsxDEV6(Box5, {
1201
+ /* @__PURE__ */ jsxDEV7(Box6, {
840
1202
  flexDirection: "column",
841
1203
  children: [
842
- /* @__PURE__ */ jsxDEV6(Box5, {
1204
+ /* @__PURE__ */ jsxDEV7(Box6, {
843
1205
  flexDirection: "row",
844
1206
  gap: 1,
845
1207
  children: [
846
- /* @__PURE__ */ jsxDEV6(StatusDot, {
1208
+ /* @__PURE__ */ jsxDEV7(StatusDot, {
847
1209
  status: session.status
848
1210
  }, undefined, false, undefined, this),
849
- /* @__PURE__ */ jsxDEV6(Text6, {
1211
+ /* @__PURE__ */ jsxDEV7(Text7, {
850
1212
  bold: true,
851
1213
  children: session.name
852
1214
  }, undefined, false, undefined, this),
853
- duration && /* @__PURE__ */ jsxDEV6(Text6, {
1215
+ duration && /* @__PURE__ */ jsxDEV7(Text7, {
854
1216
  dim: true,
855
1217
  children: [
856
1218
  "(",
@@ -860,7 +1222,7 @@ function Exit({ sessionId, onAction }) {
860
1222
  }, undefined, true, undefined, this)
861
1223
  ]
862
1224
  }, undefined, true, undefined, this),
863
- /* @__PURE__ */ jsxDEV6(Text6, {
1225
+ /* @__PURE__ */ jsxDEV7(Text7, {
864
1226
  dim: true,
865
1227
  children: [
866
1228
  conversations2.length,
@@ -870,28 +1232,28 @@ function Exit({ sessionId, onAction }) {
870
1232
  }, undefined, true, undefined, this)
871
1233
  ]
872
1234
  }, undefined, true, undefined, this),
873
- /* @__PURE__ */ jsxDEV6(Box5, {
1235
+ /* @__PURE__ */ jsxDEV7(Box6, {
874
1236
  flexDirection: "column",
875
- children: OPTIONS.map((opt, i) => /* @__PURE__ */ jsxDEV6(Box5, {
1237
+ children: OPTIONS.map((opt, i) => /* @__PURE__ */ jsxDEV7(Box6, {
876
1238
  flexDirection: "row",
877
1239
  gap: 1,
878
1240
  children: [
879
- /* @__PURE__ */ jsxDEV6(Text6, {
1241
+ /* @__PURE__ */ jsxDEV7(Text7, {
880
1242
  children: i === cursor ? "\u276F" : " "
881
1243
  }, undefined, false, undefined, this),
882
- /* @__PURE__ */ jsxDEV6(Text6, {
1244
+ /* @__PURE__ */ jsxDEV7(Text7, {
883
1245
  bold: i === cursor,
884
1246
  children: opt.label
885
1247
  }, undefined, false, undefined, this),
886
- /* @__PURE__ */ jsxDEV6(Text6, {
1248
+ /* @__PURE__ */ jsxDEV7(Text7, {
887
1249
  dim: true,
888
1250
  children: opt.hint
889
1251
  }, undefined, false, undefined, this)
890
1252
  ]
891
1253
  }, opt.action, true, undefined, this))
892
1254
  }, undefined, false, undefined, this),
893
- /* @__PURE__ */ jsxDEV6(Box5, {
894
- children: /* @__PURE__ */ jsxDEV6(Text6, {
1255
+ /* @__PURE__ */ jsxDEV7(Box6, {
1256
+ children: /* @__PURE__ */ jsxDEV7(Text7, {
895
1257
  dim: true,
896
1258
  children: "\u2191\u2193 navigate \xB7 enter select \xB7 q save & quit"
897
1259
  }, undefined, false, undefined, this)
@@ -901,12 +1263,12 @@ function Exit({ sessionId, onAction }) {
901
1263
  }
902
1264
 
903
1265
  // src/tui/screens/Resume.tsx
904
- import { useState as useState4 } from "react";
905
- import { Box as Box6, Text as Text7, useInput as useInput3, useTui as useTui3 } from "@orchetron/storm";
906
- import { jsxDEV as jsxDEV7 } from "react/jsx-dev-runtime";
1266
+ import { useState as useState5 } from "react";
1267
+ import { Box as Box7, Text as Text8, useInput as useInput4, useTui as useTui3 } from "@orchetron/storm";
1268
+ import { jsxDEV as jsxDEV8 } from "react/jsx-dev-runtime";
907
1269
  function Resume({ sessionId, onSelect }) {
908
1270
  const { exit } = useTui3();
909
- const [cursor, setCursor] = useState4(0);
1271
+ const [cursor, setCursor] = useState5(0);
910
1272
  const session = getSession(sessionId);
911
1273
  const conversations2 = getConversationsBySession(sessionId);
912
1274
  const totalOptions = conversations2.length + 1;
@@ -914,7 +1276,7 @@ function Resume({ sessionId, onSelect }) {
914
1276
  onSelect(selection);
915
1277
  exit();
916
1278
  };
917
- useInput3((e) => {
1279
+ useInput4((e) => {
918
1280
  if (e.key === "c" && e.ctrl)
919
1281
  select({ type: "back" });
920
1282
  if (e.key === "up" || e.key === "k") {
@@ -933,17 +1295,17 @@ function Resume({ sessionId, onSelect }) {
933
1295
  }
934
1296
  });
935
1297
  if (!session) {
936
- return /* @__PURE__ */ jsxDEV7(Text7, {
1298
+ return /* @__PURE__ */ jsxDEV8(Text8, {
937
1299
  color: "red",
938
1300
  children: "Session not found"
939
1301
  }, undefined, false, undefined, this);
940
1302
  }
941
- return /* @__PURE__ */ jsxDEV7(Box6, {
1303
+ return /* @__PURE__ */ jsxDEV8(Box7, {
942
1304
  flexDirection: "column",
943
1305
  padding: 1,
944
1306
  gap: 1,
945
1307
  children: [
946
- /* @__PURE__ */ jsxDEV7(Text7, {
1308
+ /* @__PURE__ */ jsxDEV8(Text8, {
947
1309
  bold: true,
948
1310
  color: "#82AAFF",
949
1311
  children: [
@@ -951,17 +1313,17 @@ function Resume({ sessionId, onSelect }) {
951
1313
  session.name
952
1314
  ]
953
1315
  }, undefined, true, undefined, this),
954
- /* @__PURE__ */ jsxDEV7(Box6, {
1316
+ /* @__PURE__ */ jsxDEV8(Box7, {
955
1317
  flexDirection: "column",
956
1318
  children: [
957
- /* @__PURE__ */ jsxDEV7(Box6, {
1319
+ /* @__PURE__ */ jsxDEV8(Box7, {
958
1320
  flexDirection: "row",
959
1321
  gap: 1,
960
1322
  children: [
961
- /* @__PURE__ */ jsxDEV7(Text7, {
1323
+ /* @__PURE__ */ jsxDEV8(Text8, {
962
1324
  children: cursor === 0 ? "\u276F" : " "
963
1325
  }, undefined, false, undefined, this),
964
- /* @__PURE__ */ jsxDEV7(Text7, {
1326
+ /* @__PURE__ */ jsxDEV8(Text8, {
965
1327
  bold: cursor === 0,
966
1328
  color: "#34D399",
967
1329
  children: "+ New conversation"
@@ -973,42 +1335,42 @@ function Resume({ sessionId, onSelect }) {
973
1335
  const isSelected = cursor === idx;
974
1336
  const duration = conv.endedAt ? formatDuration(new Date(conv.endedAt).getTime() - new Date(conv.startedAt).getTime()) : "active";
975
1337
  const ago = formatAgo(conv.startedAt);
976
- return /* @__PURE__ */ jsxDEV7(Box6, {
1338
+ return /* @__PURE__ */ jsxDEV8(Box7, {
977
1339
  flexDirection: "row",
978
1340
  gap: 1,
979
1341
  children: [
980
- /* @__PURE__ */ jsxDEV7(Text7, {
1342
+ /* @__PURE__ */ jsxDEV8(Text8, {
981
1343
  children: isSelected ? "\u276F" : " "
982
1344
  }, undefined, false, undefined, this),
983
- /* @__PURE__ */ jsxDEV7(Text7, {
1345
+ /* @__PURE__ */ jsxDEV8(Text8, {
984
1346
  bold: isSelected,
985
1347
  dim: conv.discarded,
986
1348
  children: conv.id.slice(0, 8)
987
1349
  }, undefined, false, undefined, this),
988
- /* @__PURE__ */ jsxDEV7(Text7, {
1350
+ /* @__PURE__ */ jsxDEV8(Text8, {
989
1351
  dim: true,
990
1352
  children: ago
991
1353
  }, undefined, false, undefined, this),
992
- /* @__PURE__ */ jsxDEV7(Text7, {
1354
+ /* @__PURE__ */ jsxDEV8(Text8, {
993
1355
  dim: true,
994
1356
  children: "\xB7"
995
1357
  }, undefined, false, undefined, this),
996
- /* @__PURE__ */ jsxDEV7(Text7, {
1358
+ /* @__PURE__ */ jsxDEV8(Text8, {
997
1359
  dim: true,
998
1360
  children: [
999
1361
  conv.eventCount,
1000
1362
  " events"
1001
1363
  ]
1002
1364
  }, undefined, true, undefined, this),
1003
- /* @__PURE__ */ jsxDEV7(Text7, {
1365
+ /* @__PURE__ */ jsxDEV8(Text8, {
1004
1366
  dim: true,
1005
1367
  children: "\xB7"
1006
1368
  }, undefined, false, undefined, this),
1007
- /* @__PURE__ */ jsxDEV7(Text7, {
1369
+ /* @__PURE__ */ jsxDEV8(Text8, {
1008
1370
  dim: true,
1009
1371
  children: duration
1010
1372
  }, undefined, false, undefined, this),
1011
- conv.discarded && /* @__PURE__ */ jsxDEV7(Text7, {
1373
+ conv.discarded && /* @__PURE__ */ jsxDEV8(Text8, {
1012
1374
  color: "red",
1013
1375
  dim: true,
1014
1376
  children: "(discarded)"
@@ -1018,8 +1380,8 @@ function Resume({ sessionId, onSelect }) {
1018
1380
  })
1019
1381
  ]
1020
1382
  }, undefined, true, undefined, this),
1021
- /* @__PURE__ */ jsxDEV7(Box6, {
1022
- children: /* @__PURE__ */ jsxDEV7(Text7, {
1383
+ /* @__PURE__ */ jsxDEV8(Box7, {
1384
+ children: /* @__PURE__ */ jsxDEV8(Text8, {
1023
1385
  dim: true,
1024
1386
  children: "\u2191\u2193 navigate \xB7 enter select \xB7 q back"
1025
1387
  }, undefined, false, undefined, this)
@@ -1029,7 +1391,7 @@ function Resume({ sessionId, onSelect }) {
1029
1391
  }
1030
1392
 
1031
1393
  // src/tui/run-screen.tsx
1032
- import { jsxDEV as jsxDEV8 } from "react/jsx-dev-runtime";
1394
+ import { jsxDEV as jsxDEV9 } from "react/jsx-dev-runtime";
1033
1395
  var [, , screen, outputPath, ...args] = process.argv;
1034
1396
  if (!screen || !outputPath) {
1035
1397
  console.error("Usage: run-screen <screen> <outputPath> [args...]");
@@ -1039,7 +1401,7 @@ var result;
1039
1401
  switch (screen) {
1040
1402
  case "launch": {
1041
1403
  let selection = { type: "quit" };
1042
- const app = render(/* @__PURE__ */ jsxDEV8(Launch, {
1404
+ const app = render(/* @__PURE__ */ jsxDEV9(Launch, {
1043
1405
  onSelect: (s) => {
1044
1406
  selection = s;
1045
1407
  }
@@ -1056,7 +1418,7 @@ switch (screen) {
1056
1418
  process.exit(1);
1057
1419
  }
1058
1420
  let action = "save";
1059
- const app = render(/* @__PURE__ */ jsxDEV8(Exit, {
1421
+ const app = render(/* @__PURE__ */ jsxDEV9(Exit, {
1060
1422
  sessionId,
1061
1423
  onAction: (a) => {
1062
1424
  action = a;
@@ -1074,7 +1436,7 @@ switch (screen) {
1074
1436
  process.exit(1);
1075
1437
  }
1076
1438
  let selection = { type: "back" };
1077
- const app = render(/* @__PURE__ */ jsxDEV8(Resume, {
1439
+ const app = render(/* @__PURE__ */ jsxDEV9(Resume, {
1078
1440
  sessionId,
1079
1441
  onSelect: (s) => {
1080
1442
  selection = s;