bertrand 0.14.1 → 0.16.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/bertrand.js +322 -139
- package/dist/dashboard/assets/bash-Yzrsuije.js +1 -0
- package/dist/dashboard/assets/css-DPfMkruS.js +1 -0
- package/dist/dashboard/assets/html-GMplVEZG.js +1 -0
- package/dist/dashboard/assets/index-e53khqNa.js +673 -0
- package/dist/dashboard/assets/index-qVHXGTGL.css +1 -0
- package/dist/dashboard/assets/javascript-wDzz0qaB.js +1 -0
- package/dist/dashboard/assets/json-Cp-IABpG.js +1 -0
- package/dist/dashboard/assets/jsx-g9-lgVsj.js +1 -0
- package/dist/dashboard/assets/markdown-Cvjx9yec.js +1 -0
- package/dist/dashboard/assets/python-B6aJPvgy.js +1 -0
- package/dist/dashboard/assets/tsx-COt5Ahok.js +1 -0
- package/dist/dashboard/assets/typescript-BPQ3VLAy.js +1 -0
- package/dist/dashboard/assets/vitesse-black-Bkuqu6BP.js +1 -0
- package/dist/dashboard/assets/vitesse-light-CVO1_9PV.js +1 -0
- package/dist/dashboard/assets/workbox-window.prod.es5-BBnX5xw4.js +2 -0
- package/dist/dashboard/icon.svg +13 -0
- package/dist/dashboard/index.html +28 -0
- package/dist/dashboard/manifest.webmanifest +1 -0
- package/dist/dashboard/sw.js +1 -0
- package/dist/dashboard/workbox-e4022e15.js +1 -0
- package/dist/run-screen.js +357 -285
- package/package.json +1 -1
package/dist/run-screen.js
CHANGED
|
@@ -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 {
|
|
23
|
-
import { Box as
|
|
22
|
+
import { useMemo as useMemo2, useState as useState2 } from "react";
|
|
23
|
+
import { Box as Box4, Text as Text5, useTui } from "@orchetron/storm";
|
|
24
24
|
|
|
25
25
|
// src/tui/components/app-details.tsx
|
|
26
26
|
import { Box, Text } from "@orchetron/storm";
|
|
@@ -74,6 +74,24 @@ import { useMemo, useState } from "react";
|
|
|
74
74
|
import { Box as Box3, Text as Text3, TextInput, useInput } from "@orchetron/storm";
|
|
75
75
|
import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
|
|
76
76
|
var NEW_KEY = "__new__";
|
|
77
|
+
function isSelectable(row) {
|
|
78
|
+
if (row.value === NEW_KEY)
|
|
79
|
+
return true;
|
|
80
|
+
const item = row;
|
|
81
|
+
return item.kind !== "header" && !item.disabled;
|
|
82
|
+
}
|
|
83
|
+
function findNextSelectable(rows, from, direction) {
|
|
84
|
+
let i = from;
|
|
85
|
+
while (i >= 0 && i < rows.length) {
|
|
86
|
+
if (isSelectable(rows[i]))
|
|
87
|
+
return i;
|
|
88
|
+
i += direction;
|
|
89
|
+
}
|
|
90
|
+
return -1;
|
|
91
|
+
}
|
|
92
|
+
function findFirstSelectable(rows) {
|
|
93
|
+
return findNextSelectable(rows, 0, 1);
|
|
94
|
+
}
|
|
77
95
|
function Picker(props) {
|
|
78
96
|
const {
|
|
79
97
|
items,
|
|
@@ -81,7 +99,7 @@ function Picker(props) {
|
|
|
81
99
|
placeholder,
|
|
82
100
|
allowCreate = true,
|
|
83
101
|
emptyHint,
|
|
84
|
-
maxVisible =
|
|
102
|
+
maxVisible = 12
|
|
85
103
|
} = props;
|
|
86
104
|
const [filter, setFilter] = useState("");
|
|
87
105
|
const [cursor, setCursor] = useState(0);
|
|
@@ -89,13 +107,19 @@ function Picker(props) {
|
|
|
89
107
|
const q = filter.trim().toLowerCase();
|
|
90
108
|
if (!q)
|
|
91
109
|
return items;
|
|
92
|
-
return items.filter((it) => it.label.toLowerCase().includes(q));
|
|
110
|
+
return items.filter((it) => it.kind !== "header" && it.label.toLowerCase().includes(q));
|
|
93
111
|
}, [filter, items]);
|
|
94
|
-
const exactMatch = useMemo(() => filtered.some((it) => it.label.toLowerCase() === filter.trim().toLowerCase()), [filter, filtered]);
|
|
112
|
+
const exactMatch = useMemo(() => filtered.some((it) => it.kind !== "header" && it.label.toLowerCase() === filter.trim().toLowerCase()), [filter, filtered]);
|
|
95
113
|
const showCreate = allowCreate && filter.trim().length > 0 && !exactMatch;
|
|
96
114
|
const visibleRows = useMemo(() => showCreate ? [...filtered, { value: NEW_KEY }] : filtered, [filtered, showCreate]);
|
|
97
|
-
if (
|
|
98
|
-
|
|
115
|
+
if (visibleRows.length === 0) {
|
|
116
|
+
if (cursor !== 0)
|
|
117
|
+
setTimeout(() => setCursor(0), 0);
|
|
118
|
+
} else if (cursor >= visibleRows.length || !isSelectable(visibleRows[cursor])) {
|
|
119
|
+
const next = findFirstSelectable(visibleRows);
|
|
120
|
+
if (next !== -1 && next !== cursor) {
|
|
121
|
+
setTimeout(() => setCursor(next), 0);
|
|
122
|
+
}
|
|
99
123
|
}
|
|
100
124
|
useInput((e) => {
|
|
101
125
|
if (!isFocused)
|
|
@@ -108,16 +132,24 @@ function Picker(props) {
|
|
|
108
132
|
if (visibleRows.length === 0)
|
|
109
133
|
return;
|
|
110
134
|
if (e.key === "up") {
|
|
111
|
-
setCursor((c) =>
|
|
135
|
+
setCursor((c) => {
|
|
136
|
+
const next = findNextSelectable(visibleRows, c - 1, -1);
|
|
137
|
+
return next === -1 ? c : next;
|
|
138
|
+
});
|
|
112
139
|
} else if (e.key === "down") {
|
|
113
|
-
setCursor((c) =>
|
|
140
|
+
setCursor((c) => {
|
|
141
|
+
const next = findNextSelectable(visibleRows, c + 1, 1);
|
|
142
|
+
return next === -1 ? c : next;
|
|
143
|
+
});
|
|
114
144
|
} else if (e.key === "tab" && props.mode === "multi") {
|
|
115
145
|
props.onDone();
|
|
116
146
|
}
|
|
117
147
|
}, { isActive: isFocused });
|
|
118
148
|
const submitCursor = () => {
|
|
119
149
|
const row = visibleRows[cursor];
|
|
120
|
-
|
|
150
|
+
if (!row || !isSelectable(row))
|
|
151
|
+
return;
|
|
152
|
+
const value = row.value === NEW_KEY ? filter.trim() : row.value;
|
|
121
153
|
if (!value)
|
|
122
154
|
return;
|
|
123
155
|
if (props.mode === "single") {
|
|
@@ -230,28 +262,46 @@ function Picker(props) {
|
|
|
230
262
|
}, "__new__", true, undefined, this);
|
|
231
263
|
}
|
|
232
264
|
const item = row;
|
|
265
|
+
if (item.kind === "header") {
|
|
266
|
+
return /* @__PURE__ */ jsxDEV3(Box3, {
|
|
267
|
+
flexDirection: "row",
|
|
268
|
+
children: /* @__PURE__ */ jsxDEV3(Text3, {
|
|
269
|
+
dim: true,
|
|
270
|
+
bold: true,
|
|
271
|
+
color: item.color ?? undefined,
|
|
272
|
+
children: item.label
|
|
273
|
+
}, undefined, false, undefined, this)
|
|
274
|
+
}, `h:${item.value}`, false, undefined, this);
|
|
275
|
+
}
|
|
233
276
|
const isSelected = selectedSet.has(item.value);
|
|
234
277
|
const marker = props.mode === "multi" ? isSelected ? "\u2713 " : " " : "";
|
|
278
|
+
const dim = item.dim || item.disabled;
|
|
235
279
|
return /* @__PURE__ */ jsxDEV3(Box3, {
|
|
236
280
|
flexDirection: "row",
|
|
237
281
|
justifyContent: "space-between",
|
|
238
282
|
gap: 1,
|
|
239
283
|
backgroundColor: isCursor ? "green" : undefined,
|
|
240
284
|
children: [
|
|
241
|
-
/* @__PURE__ */ jsxDEV3(
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
children:
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
285
|
+
/* @__PURE__ */ jsxDEV3(Box3, {
|
|
286
|
+
flexDirection: "row",
|
|
287
|
+
gap: 0,
|
|
288
|
+
children: item.display ? item.display : /* @__PURE__ */ jsxDEV3(Text3, {
|
|
289
|
+
color: isCursor ? "black" : item.color ?? undefined,
|
|
290
|
+
bold: isCursor,
|
|
291
|
+
dim: !isCursor && dim,
|
|
292
|
+
children: [
|
|
293
|
+
marker,
|
|
294
|
+
item.label
|
|
295
|
+
]
|
|
296
|
+
}, undefined, true, undefined, this)
|
|
297
|
+
}, undefined, false, undefined, this),
|
|
249
298
|
item.meta && /* @__PURE__ */ jsxDEV3(Box3, {
|
|
250
299
|
paddingX: 1,
|
|
251
300
|
backgroundColor: isCursor ? "black" : "#3a3a3a",
|
|
252
301
|
children: /* @__PURE__ */ jsxDEV3(Text3, {
|
|
253
302
|
color: isCursor ? "green" : "white",
|
|
254
303
|
bold: true,
|
|
304
|
+
dim: !isCursor && dim,
|
|
255
305
|
children: item.meta
|
|
256
306
|
}, undefined, false, undefined, this)
|
|
257
307
|
}, undefined, false, undefined, this)
|
|
@@ -287,12 +337,8 @@ function StatusDot({ status }) {
|
|
|
287
337
|
children: "\u25CF"
|
|
288
338
|
}, undefined, false, undefined, this);
|
|
289
339
|
}
|
|
290
|
-
// src/
|
|
291
|
-
import {
|
|
292
|
-
import { Box as Box4, Text as Text5, TextInput as TextInput2, useInput as useInput2, useTui } from "@orchetron/storm";
|
|
293
|
-
|
|
294
|
-
// src/db/queries/groups.ts
|
|
295
|
-
import { eq, like, or, isNull } from "drizzle-orm";
|
|
340
|
+
// src/db/queries/sessions.ts
|
|
341
|
+
import { eq, and, inArray, sql as sql2 } from "drizzle-orm";
|
|
296
342
|
|
|
297
343
|
// src/db/client.ts
|
|
298
344
|
import { Database } from "bun:sqlite";
|
|
@@ -442,19 +488,13 @@ function getDb() {
|
|
|
442
488
|
// src/lib/id.ts
|
|
443
489
|
import { nanoid } from "nanoid";
|
|
444
490
|
|
|
445
|
-
// src/db/queries/groups.ts
|
|
446
|
-
function getAllGroups() {
|
|
447
|
-
return getDb().select().from(groups).all();
|
|
448
|
-
}
|
|
449
|
-
|
|
450
491
|
// src/db/queries/sessions.ts
|
|
451
|
-
import { eq as eq2, and, inArray, sql as sql2 } from "drizzle-orm";
|
|
452
492
|
function getSession(id) {
|
|
453
|
-
return getDb().select().from(sessions).where(
|
|
493
|
+
return getDb().select().from(sessions).where(eq(sessions.id, id)).get();
|
|
454
494
|
}
|
|
455
495
|
function getAllSessions(opts) {
|
|
456
496
|
const db = getDb();
|
|
457
|
-
const query = db.select({ session: sessions, groupPath: groups.path }).from(sessions).innerJoin(groups,
|
|
497
|
+
const query = db.select({ session: sessions, groupPath: groups.path }).from(sessions).innerJoin(groups, eq(sessions.groupId, groups.id));
|
|
458
498
|
if (opts?.excludeArchived) {
|
|
459
499
|
return query.where(inArray(sessions.status, [
|
|
460
500
|
"active",
|
|
@@ -465,214 +505,6 @@ function getAllSessions(opts) {
|
|
|
465
505
|
return query.all();
|
|
466
506
|
}
|
|
467
507
|
|
|
468
|
-
// src/tui/screens/launch/create-screen.tsx
|
|
469
|
-
import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
|
|
470
|
-
function CreateScreen({
|
|
471
|
-
isFocused,
|
|
472
|
-
onSubmit,
|
|
473
|
-
onQuit
|
|
474
|
-
}) {
|
|
475
|
-
const { clear } = useTui();
|
|
476
|
-
const [step, setStep] = useState2("group");
|
|
477
|
-
const [groupPath, setGroupPath] = useState2(null);
|
|
478
|
-
const [slug, setSlug] = useState2("");
|
|
479
|
-
const [error, setError] = useState2(null);
|
|
480
|
-
const stepRef = useRef(step);
|
|
481
|
-
stepRef.current = step;
|
|
482
|
-
useEffect(() => {
|
|
483
|
-
clear();
|
|
484
|
-
}, [step, clear]);
|
|
485
|
-
const groupItems = useMemo2(() => {
|
|
486
|
-
const activeSessions = getAllSessions({ excludeArchived: true });
|
|
487
|
-
const counts = new Map;
|
|
488
|
-
for (const s of activeSessions) {
|
|
489
|
-
counts.set(s.groupPath, (counts.get(s.groupPath) ?? 0) + 1);
|
|
490
|
-
}
|
|
491
|
-
return getAllGroups().filter((g) => counts.has(g.path)).sort((a, b) => a.path.localeCompare(b.path)).map((g) => ({
|
|
492
|
-
value: g.path,
|
|
493
|
-
label: g.path,
|
|
494
|
-
color: g.color,
|
|
495
|
-
meta: `${counts.get(g.path)}`
|
|
496
|
-
}));
|
|
497
|
-
}, []);
|
|
498
|
-
const handleGroupPicked = useCallback((value) => {
|
|
499
|
-
setGroupPath(value);
|
|
500
|
-
setError(null);
|
|
501
|
-
setStep("slug");
|
|
502
|
-
}, []);
|
|
503
|
-
const handleSlugSubmit = useCallback((value) => {
|
|
504
|
-
if (stepRef.current !== "slug")
|
|
505
|
-
return;
|
|
506
|
-
const trimmed = value.trim();
|
|
507
|
-
if (!trimmed) {
|
|
508
|
-
setError("Name cannot be empty.");
|
|
509
|
-
return;
|
|
510
|
-
}
|
|
511
|
-
if (!/^[a-z0-9][a-z0-9._-]*$/i.test(trimmed)) {
|
|
512
|
-
setError("Name must start alphanumeric; letters, digits, dots, underscores, dashes only.");
|
|
513
|
-
return;
|
|
514
|
-
}
|
|
515
|
-
if (!groupPath)
|
|
516
|
-
return;
|
|
517
|
-
onSubmit({ groupPath, slug: trimmed });
|
|
518
|
-
}, [groupPath, onSubmit]);
|
|
519
|
-
useInput2((e) => {
|
|
520
|
-
if (!isFocused)
|
|
521
|
-
return;
|
|
522
|
-
if (e.key === "c" && e.ctrl) {
|
|
523
|
-
onQuit();
|
|
524
|
-
return;
|
|
525
|
-
}
|
|
526
|
-
if (e.key === "escape" && step === "slug") {
|
|
527
|
-
setStep("group");
|
|
528
|
-
setSlug("");
|
|
529
|
-
setError(null);
|
|
530
|
-
}
|
|
531
|
-
}, { isActive: isFocused });
|
|
532
|
-
return /* @__PURE__ */ jsxDEV5(Box4, {
|
|
533
|
-
flexDirection: "column",
|
|
534
|
-
gap: 1,
|
|
535
|
-
children: [
|
|
536
|
-
/* @__PURE__ */ jsxDEV5(Box4, {
|
|
537
|
-
flexDirection: "row",
|
|
538
|
-
gap: 1,
|
|
539
|
-
children: [
|
|
540
|
-
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
541
|
-
bold: true,
|
|
542
|
-
children: "New session"
|
|
543
|
-
}, undefined, false, undefined, this),
|
|
544
|
-
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
545
|
-
dim: true,
|
|
546
|
-
children: "\xB7"
|
|
547
|
-
}, undefined, false, undefined, this),
|
|
548
|
-
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
549
|
-
dim: true,
|
|
550
|
-
children: stepLabel(step)
|
|
551
|
-
}, undefined, false, undefined, this)
|
|
552
|
-
]
|
|
553
|
-
}, undefined, true, undefined, this),
|
|
554
|
-
groupPath && step === "slug" && /* @__PURE__ */ jsxDEV5(Box4, {
|
|
555
|
-
flexDirection: "row",
|
|
556
|
-
gap: 1,
|
|
557
|
-
children: [
|
|
558
|
-
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
559
|
-
dim: true,
|
|
560
|
-
children: "Group:"
|
|
561
|
-
}, undefined, false, undefined, this),
|
|
562
|
-
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
563
|
-
color: "green",
|
|
564
|
-
children: groupPath
|
|
565
|
-
}, undefined, false, undefined, this)
|
|
566
|
-
]
|
|
567
|
-
}, undefined, true, undefined, this),
|
|
568
|
-
/* @__PURE__ */ jsxDEV5(Box4, {
|
|
569
|
-
flexDirection: "column",
|
|
570
|
-
height: step === "group" ? undefined : 0,
|
|
571
|
-
overflow: "hidden",
|
|
572
|
-
children: /* @__PURE__ */ jsxDEV5(Picker, {
|
|
573
|
-
mode: "single",
|
|
574
|
-
items: groupItems,
|
|
575
|
-
isFocused: isFocused && step === "group",
|
|
576
|
-
placeholder: "Pick or type a new group path\u2026",
|
|
577
|
-
onSubmit: handleGroupPicked,
|
|
578
|
-
emptyHint: "No active groups. Type a name to create one."
|
|
579
|
-
}, undefined, false, undefined, this)
|
|
580
|
-
}, undefined, false, undefined, this),
|
|
581
|
-
/* @__PURE__ */ jsxDEV5(Box4, {
|
|
582
|
-
flexDirection: "column",
|
|
583
|
-
gap: 0,
|
|
584
|
-
height: step === "slug" ? undefined : 0,
|
|
585
|
-
overflow: "hidden",
|
|
586
|
-
children: [
|
|
587
|
-
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
588
|
-
dim: true,
|
|
589
|
-
children: "Name"
|
|
590
|
-
}, undefined, false, undefined, this),
|
|
591
|
-
/* @__PURE__ */ jsxDEV5(Box4, {
|
|
592
|
-
borderStyle: "round",
|
|
593
|
-
borderColor: isFocused ? "green" : undefined,
|
|
594
|
-
borderDimColor: !isFocused,
|
|
595
|
-
paddingX: 1,
|
|
596
|
-
children: /* @__PURE__ */ jsxDEV5(TextInput2, {
|
|
597
|
-
value: slug,
|
|
598
|
-
onChange: (v) => {
|
|
599
|
-
setSlug(v);
|
|
600
|
-
setError(null);
|
|
601
|
-
},
|
|
602
|
-
onSubmit: handleSlugSubmit,
|
|
603
|
-
placeholder: "fix-auth-bug",
|
|
604
|
-
color: "green",
|
|
605
|
-
placeholderColor: "gray",
|
|
606
|
-
isFocused: isFocused && step === "slug"
|
|
607
|
-
}, undefined, false, undefined, this)
|
|
608
|
-
}, undefined, false, undefined, this)
|
|
609
|
-
]
|
|
610
|
-
}, undefined, true, undefined, this),
|
|
611
|
-
error && /* @__PURE__ */ jsxDEV5(Text5, {
|
|
612
|
-
color: "red",
|
|
613
|
-
children: error
|
|
614
|
-
}, undefined, false, undefined, this),
|
|
615
|
-
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
616
|
-
dim: true,
|
|
617
|
-
children: footer(step)
|
|
618
|
-
}, undefined, false, undefined, this)
|
|
619
|
-
]
|
|
620
|
-
}, undefined, true, undefined, this);
|
|
621
|
-
}
|
|
622
|
-
function stepLabel(step) {
|
|
623
|
-
return step === "group" ? "1/2 Group" : "2/2 Name";
|
|
624
|
-
}
|
|
625
|
-
function footer(step) {
|
|
626
|
-
if (step === "group") {
|
|
627
|
-
return "\u2191\u2193 navigate \xB7 enter pick \xB7 ctrl+c quit";
|
|
628
|
-
}
|
|
629
|
-
return "enter create \xB7 esc back \xB7 ctrl+c quit";
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
// src/tui/screens/launch/index.tsx
|
|
633
|
-
import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
|
|
634
|
-
function Launch({ onSelect }) {
|
|
635
|
-
const { exit } = useTui2();
|
|
636
|
-
const select = useCallback2((selection) => {
|
|
637
|
-
onSelect(selection);
|
|
638
|
-
exit();
|
|
639
|
-
}, [onSelect, exit]);
|
|
640
|
-
return /* @__PURE__ */ jsxDEV6(Box5, {
|
|
641
|
-
flexDirection: "column",
|
|
642
|
-
paddingY: 1,
|
|
643
|
-
gap: 1,
|
|
644
|
-
children: [
|
|
645
|
-
/* @__PURE__ */ jsxDEV6(Box5, {
|
|
646
|
-
marginX: 1,
|
|
647
|
-
children: /* @__PURE__ */ jsxDEV6(Logo, {}, undefined, false, undefined, this)
|
|
648
|
-
}, undefined, false, undefined, this),
|
|
649
|
-
/* @__PURE__ */ jsxDEV6(Box5, {
|
|
650
|
-
flexDirection: "column",
|
|
651
|
-
marginX: 2,
|
|
652
|
-
gap: 1,
|
|
653
|
-
children: [
|
|
654
|
-
/* @__PURE__ */ jsxDEV6(AppDetails, {}, undefined, false, undefined, this),
|
|
655
|
-
/* @__PURE__ */ jsxDEV6(CreateScreen, {
|
|
656
|
-
isFocused: true,
|
|
657
|
-
onSubmit: (payload) => select({ type: "create", ...payload }),
|
|
658
|
-
onQuit: () => select({ type: "quit" })
|
|
659
|
-
}, undefined, false, undefined, this)
|
|
660
|
-
]
|
|
661
|
-
}, undefined, true, undefined, this)
|
|
662
|
-
]
|
|
663
|
-
}, undefined, true, undefined, this);
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
// src/tui/screens/Exit.tsx
|
|
667
|
-
import { useState as useState3 } from "react";
|
|
668
|
-
import { Box as Box6, Text as Text6, useInput as useInput3, useTui as useTui3 } from "@orchetron/storm";
|
|
669
|
-
|
|
670
|
-
// src/db/queries/conversations.ts
|
|
671
|
-
import { eq as eq3, and as and2, desc, sql as sql3 } from "drizzle-orm";
|
|
672
|
-
function getConversationsBySession(sessionId) {
|
|
673
|
-
return getDb().select().from(conversations).where(and2(eq3(conversations.sessionId, sessionId), eq3(conversations.discarded, false))).orderBy(desc(conversations.startedAt)).all();
|
|
674
|
-
}
|
|
675
|
-
|
|
676
508
|
// src/lib/format.ts
|
|
677
509
|
var SECOND = 1000;
|
|
678
510
|
var MINUTE = 60 * SECOND;
|
|
@@ -706,8 +538,248 @@ function formatAgo(isoOrDate) {
|
|
|
706
538
|
return date.toLocaleDateString("en-US", { month: "short", day: "numeric" });
|
|
707
539
|
}
|
|
708
540
|
|
|
541
|
+
// src/lib/parse-session-name.ts
|
|
542
|
+
function parseSessionName(input) {
|
|
543
|
+
const trimmed = input.trim().replace(/^\/+|\/+$/g, "");
|
|
544
|
+
if (!trimmed) {
|
|
545
|
+
throw new Error("Session name cannot be empty");
|
|
546
|
+
}
|
|
547
|
+
const segments = trimmed.split("/").filter(Boolean);
|
|
548
|
+
if (segments.length < 2) {
|
|
549
|
+
throw new Error(`Session name must include at least one group: "group/session" (got "${trimmed}")`);
|
|
550
|
+
}
|
|
551
|
+
for (const segment of segments) {
|
|
552
|
+
if (!/^[a-z0-9][a-z0-9._-]*$/i.test(segment)) {
|
|
553
|
+
throw new Error(`Invalid segment "${segment}": must start with alphanumeric and contain only letters, digits, dots, underscores, or dashes`);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
const slug = segments[segments.length - 1];
|
|
557
|
+
const groupPath = segments.slice(0, -1).join("/");
|
|
558
|
+
return { groupPath, slug };
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// src/tui/screens/launch/index.tsx
|
|
562
|
+
import { jsxDEV as jsxDEV5, Fragment } from "react/jsx-dev-runtime";
|
|
563
|
+
var STATUS_COLOR = {
|
|
564
|
+
paused: "gold",
|
|
565
|
+
waiting: "red"
|
|
566
|
+
};
|
|
567
|
+
var STATUS_RANK = {
|
|
568
|
+
paused: 0,
|
|
569
|
+
waiting: 1
|
|
570
|
+
};
|
|
571
|
+
function statusRank(status) {
|
|
572
|
+
return STATUS_RANK[status] ?? 99;
|
|
573
|
+
}
|
|
574
|
+
function recencyKey(s) {
|
|
575
|
+
return s.session.endedAt ?? s.session.startedAt;
|
|
576
|
+
}
|
|
577
|
+
function sessionRow(s) {
|
|
578
|
+
const status = s.session.status;
|
|
579
|
+
const color = STATUS_COLOR[status] ?? "gray";
|
|
580
|
+
const disabled = status === "waiting";
|
|
581
|
+
return {
|
|
582
|
+
value: `${s.groupPath}/${s.session.slug}`,
|
|
583
|
+
label: `${s.groupPath}/${s.session.slug} ${status}`,
|
|
584
|
+
meta: formatAgo(recencyKey(s)),
|
|
585
|
+
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)
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
function groupHeader(groupPath) {
|
|
612
|
+
return {
|
|
613
|
+
value: `__group:${groupPath}`,
|
|
614
|
+
label: groupPath,
|
|
615
|
+
kind: "header"
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
function Launch({ onSelect }) {
|
|
619
|
+
const { exit } = useTui();
|
|
620
|
+
const [error, setError] = useState2(null);
|
|
621
|
+
const allSessions = useMemo2(() => getAllSessions({ excludeArchived: true }), []);
|
|
622
|
+
const visibleSessions = useMemo2(() => {
|
|
623
|
+
return allSessions.filter((s) => s.session.status === "paused" || s.session.status === "waiting").sort((a, b) => {
|
|
624
|
+
const g = a.groupPath.localeCompare(b.groupPath);
|
|
625
|
+
if (g !== 0)
|
|
626
|
+
return g;
|
|
627
|
+
const r = statusRank(a.session.status) - statusRank(b.session.status);
|
|
628
|
+
if (r !== 0)
|
|
629
|
+
return r;
|
|
630
|
+
return recencyKey(b).localeCompare(recencyKey(a));
|
|
631
|
+
});
|
|
632
|
+
}, [allSessions]);
|
|
633
|
+
const items = useMemo2(() => {
|
|
634
|
+
const rows = [];
|
|
635
|
+
let lastGroup = null;
|
|
636
|
+
for (const s of visibleSessions) {
|
|
637
|
+
if (s.groupPath !== lastGroup) {
|
|
638
|
+
rows.push(groupHeader(s.groupPath));
|
|
639
|
+
lastGroup = s.groupPath;
|
|
640
|
+
}
|
|
641
|
+
rows.push(sessionRow(s));
|
|
642
|
+
}
|
|
643
|
+
return rows;
|
|
644
|
+
}, [visibleSessions]);
|
|
645
|
+
const sessionByValue = useMemo2(() => {
|
|
646
|
+
const map = new Map;
|
|
647
|
+
for (const s of allSessions) {
|
|
648
|
+
map.set(`${s.groupPath}/${s.session.slug}`, s);
|
|
649
|
+
}
|
|
650
|
+
return map;
|
|
651
|
+
}, [allSessions]);
|
|
652
|
+
const select = (selection) => {
|
|
653
|
+
onSelect(selection);
|
|
654
|
+
exit();
|
|
655
|
+
};
|
|
656
|
+
const handleSubmit = (value) => {
|
|
657
|
+
const existing = sessionByValue.get(value);
|
|
658
|
+
if (existing) {
|
|
659
|
+
if (existing.session.status === "paused") {
|
|
660
|
+
select({ type: "pick", sessionId: existing.session.id });
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
setError(`${value} is ${existing.session.status} \u2014 can't resume from here.`);
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
try {
|
|
667
|
+
const { groupPath, slug } = parseSessionName(value);
|
|
668
|
+
select({ type: "create", groupPath, slug });
|
|
669
|
+
} catch (e) {
|
|
670
|
+
setError(e instanceof Error ? e.message : "Invalid name");
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
const counts = useMemo2(() => {
|
|
674
|
+
let paused = 0;
|
|
675
|
+
let waiting = 0;
|
|
676
|
+
for (const s of visibleSessions) {
|
|
677
|
+
if (s.session.status === "paused")
|
|
678
|
+
paused++;
|
|
679
|
+
else if (s.session.status === "waiting")
|
|
680
|
+
waiting++;
|
|
681
|
+
}
|
|
682
|
+
return { paused, waiting };
|
|
683
|
+
}, [visibleSessions]);
|
|
684
|
+
return /* @__PURE__ */ jsxDEV5(Box4, {
|
|
685
|
+
flexDirection: "column",
|
|
686
|
+
paddingY: 1,
|
|
687
|
+
gap: 1,
|
|
688
|
+
children: [
|
|
689
|
+
/* @__PURE__ */ jsxDEV5(Box4, {
|
|
690
|
+
marginX: 1,
|
|
691
|
+
children: /* @__PURE__ */ jsxDEV5(Logo, {}, undefined, false, undefined, this)
|
|
692
|
+
}, undefined, false, undefined, this),
|
|
693
|
+
/* @__PURE__ */ jsxDEV5(Box4, {
|
|
694
|
+
flexDirection: "column",
|
|
695
|
+
marginX: 2,
|
|
696
|
+
gap: 1,
|
|
697
|
+
children: [
|
|
698
|
+
/* @__PURE__ */ jsxDEV5(AppDetails, {}, undefined, false, undefined, this),
|
|
699
|
+
/* @__PURE__ */ jsxDEV5(Box4, {
|
|
700
|
+
flexDirection: "column",
|
|
701
|
+
gap: 1,
|
|
702
|
+
children: [
|
|
703
|
+
/* @__PURE__ */ jsxDEV5(Box4, {
|
|
704
|
+
flexDirection: "row",
|
|
705
|
+
gap: 1,
|
|
706
|
+
children: [
|
|
707
|
+
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
708
|
+
bold: true,
|
|
709
|
+
children: "Sessions"
|
|
710
|
+
}, undefined, false, undefined, this),
|
|
711
|
+
visibleSessions.length === 0 ? /* @__PURE__ */ jsxDEV5(Text5, {
|
|
712
|
+
dim: true,
|
|
713
|
+
children: "\xB7 none \u2014 type group/slug to create"
|
|
714
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV5(Fragment, {
|
|
715
|
+
children: [
|
|
716
|
+
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
717
|
+
dim: true,
|
|
718
|
+
children: "\xB7"
|
|
719
|
+
}, undefined, false, undefined, this),
|
|
720
|
+
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
721
|
+
color: "gold",
|
|
722
|
+
children: [
|
|
723
|
+
counts.paused,
|
|
724
|
+
" paused"
|
|
725
|
+
]
|
|
726
|
+
}, undefined, true, undefined, this),
|
|
727
|
+
counts.waiting > 0 && /* @__PURE__ */ jsxDEV5(Fragment, {
|
|
728
|
+
children: [
|
|
729
|
+
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
730
|
+
dim: true,
|
|
731
|
+
children: "\xB7"
|
|
732
|
+
}, undefined, false, undefined, this),
|
|
733
|
+
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
734
|
+
color: "red",
|
|
735
|
+
dim: true,
|
|
736
|
+
children: [
|
|
737
|
+
counts.waiting,
|
|
738
|
+
" waiting"
|
|
739
|
+
]
|
|
740
|
+
}, undefined, true, undefined, this)
|
|
741
|
+
]
|
|
742
|
+
}, undefined, true, undefined, this)
|
|
743
|
+
]
|
|
744
|
+
}, undefined, true, undefined, this)
|
|
745
|
+
]
|
|
746
|
+
}, undefined, true, undefined, this),
|
|
747
|
+
/* @__PURE__ */ jsxDEV5(Picker, {
|
|
748
|
+
mode: "single",
|
|
749
|
+
items,
|
|
750
|
+
isFocused: true,
|
|
751
|
+
placeholder: "Filter or type group/slug to create\u2026",
|
|
752
|
+
emptyHint: "No paused sessions. Type group/slug to create one.",
|
|
753
|
+
onSubmit: handleSubmit
|
|
754
|
+
}, undefined, false, undefined, this),
|
|
755
|
+
error && /* @__PURE__ */ jsxDEV5(Text5, {
|
|
756
|
+
color: "red",
|
|
757
|
+
children: error
|
|
758
|
+
}, undefined, false, undefined, this),
|
|
759
|
+
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
760
|
+
dim: true,
|
|
761
|
+
children: "\u2191\u2193 navigate \xB7 enter continue/create \xB7 esc clear \xB7 ctrl+c quit"
|
|
762
|
+
}, undefined, false, undefined, this)
|
|
763
|
+
]
|
|
764
|
+
}, undefined, true, undefined, this)
|
|
765
|
+
]
|
|
766
|
+
}, undefined, true, undefined, this)
|
|
767
|
+
]
|
|
768
|
+
}, undefined, true, undefined, this);
|
|
769
|
+
}
|
|
770
|
+
|
|
709
771
|
// src/tui/screens/Exit.tsx
|
|
710
|
-
import {
|
|
772
|
+
import { useState as useState3 } from "react";
|
|
773
|
+
import { Box as Box5, Text as Text6, useInput as useInput2, useTui as useTui2 } from "@orchetron/storm";
|
|
774
|
+
|
|
775
|
+
// src/db/queries/conversations.ts
|
|
776
|
+
import { eq as eq2, and as and2, desc, sql as sql3 } from "drizzle-orm";
|
|
777
|
+
function getConversationsBySession(sessionId) {
|
|
778
|
+
return getDb().select().from(conversations).where(and2(eq2(conversations.sessionId, sessionId), eq2(conversations.discarded, false))).orderBy(desc(conversations.startedAt)).all();
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// src/tui/screens/Exit.tsx
|
|
782
|
+
import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
|
|
711
783
|
var OPTIONS = [
|
|
712
784
|
{ action: "save", label: "Save", hint: "Keep session paused for later" },
|
|
713
785
|
{
|
|
@@ -727,11 +799,11 @@ var OPTIONS = [
|
|
|
727
799
|
}
|
|
728
800
|
];
|
|
729
801
|
function Exit({ sessionId, onAction }) {
|
|
730
|
-
const { exit } =
|
|
802
|
+
const { exit } = useTui2();
|
|
731
803
|
const [cursor, setCursor] = useState3(0);
|
|
732
804
|
const session = getSession(sessionId);
|
|
733
805
|
const conversations2 = session ? getConversationsBySession(session.id) : [];
|
|
734
|
-
|
|
806
|
+
useInput2((e) => {
|
|
735
807
|
if (e.key === "c" && e.ctrl)
|
|
736
808
|
exit();
|
|
737
809
|
if (e.key === "up" || e.key === "k") {
|
|
@@ -748,37 +820,37 @@ function Exit({ sessionId, onAction }) {
|
|
|
748
820
|
}
|
|
749
821
|
});
|
|
750
822
|
if (!session) {
|
|
751
|
-
return /* @__PURE__ */
|
|
823
|
+
return /* @__PURE__ */ jsxDEV6(Text6, {
|
|
752
824
|
color: "red",
|
|
753
825
|
children: "Session not found"
|
|
754
826
|
}, undefined, false, undefined, this);
|
|
755
827
|
}
|
|
756
828
|
const duration = session.endedAt && session.startedAt ? formatDuration(new Date(session.endedAt).getTime() - new Date(session.startedAt).getTime()) : null;
|
|
757
|
-
return /* @__PURE__ */
|
|
829
|
+
return /* @__PURE__ */ jsxDEV6(Box5, {
|
|
758
830
|
flexDirection: "column",
|
|
759
831
|
padding: 1,
|
|
760
832
|
gap: 1,
|
|
761
833
|
children: [
|
|
762
|
-
/* @__PURE__ */
|
|
834
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
763
835
|
bold: true,
|
|
764
836
|
color: "#82AAFF",
|
|
765
837
|
children: "Session ended"
|
|
766
838
|
}, undefined, false, undefined, this),
|
|
767
|
-
/* @__PURE__ */
|
|
839
|
+
/* @__PURE__ */ jsxDEV6(Box5, {
|
|
768
840
|
flexDirection: "column",
|
|
769
841
|
children: [
|
|
770
|
-
/* @__PURE__ */
|
|
842
|
+
/* @__PURE__ */ jsxDEV6(Box5, {
|
|
771
843
|
flexDirection: "row",
|
|
772
844
|
gap: 1,
|
|
773
845
|
children: [
|
|
774
|
-
/* @__PURE__ */
|
|
846
|
+
/* @__PURE__ */ jsxDEV6(StatusDot, {
|
|
775
847
|
status: session.status
|
|
776
848
|
}, undefined, false, undefined, this),
|
|
777
|
-
/* @__PURE__ */
|
|
849
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
778
850
|
bold: true,
|
|
779
851
|
children: session.name
|
|
780
852
|
}, undefined, false, undefined, this),
|
|
781
|
-
duration && /* @__PURE__ */
|
|
853
|
+
duration && /* @__PURE__ */ jsxDEV6(Text6, {
|
|
782
854
|
dim: true,
|
|
783
855
|
children: [
|
|
784
856
|
"(",
|
|
@@ -788,7 +860,7 @@ function Exit({ sessionId, onAction }) {
|
|
|
788
860
|
}, undefined, true, undefined, this)
|
|
789
861
|
]
|
|
790
862
|
}, undefined, true, undefined, this),
|
|
791
|
-
/* @__PURE__ */
|
|
863
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
792
864
|
dim: true,
|
|
793
865
|
children: [
|
|
794
866
|
conversations2.length,
|
|
@@ -798,28 +870,28 @@ function Exit({ sessionId, onAction }) {
|
|
|
798
870
|
}, undefined, true, undefined, this)
|
|
799
871
|
]
|
|
800
872
|
}, undefined, true, undefined, this),
|
|
801
|
-
/* @__PURE__ */
|
|
873
|
+
/* @__PURE__ */ jsxDEV6(Box5, {
|
|
802
874
|
flexDirection: "column",
|
|
803
|
-
children: OPTIONS.map((opt, i) => /* @__PURE__ */
|
|
875
|
+
children: OPTIONS.map((opt, i) => /* @__PURE__ */ jsxDEV6(Box5, {
|
|
804
876
|
flexDirection: "row",
|
|
805
877
|
gap: 1,
|
|
806
878
|
children: [
|
|
807
|
-
/* @__PURE__ */
|
|
879
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
808
880
|
children: i === cursor ? "\u276F" : " "
|
|
809
881
|
}, undefined, false, undefined, this),
|
|
810
|
-
/* @__PURE__ */
|
|
882
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
811
883
|
bold: i === cursor,
|
|
812
884
|
children: opt.label
|
|
813
885
|
}, undefined, false, undefined, this),
|
|
814
|
-
/* @__PURE__ */
|
|
886
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
815
887
|
dim: true,
|
|
816
888
|
children: opt.hint
|
|
817
889
|
}, undefined, false, undefined, this)
|
|
818
890
|
]
|
|
819
891
|
}, opt.action, true, undefined, this))
|
|
820
892
|
}, undefined, false, undefined, this),
|
|
821
|
-
/* @__PURE__ */
|
|
822
|
-
children: /* @__PURE__ */
|
|
893
|
+
/* @__PURE__ */ jsxDEV6(Box5, {
|
|
894
|
+
children: /* @__PURE__ */ jsxDEV6(Text6, {
|
|
823
895
|
dim: true,
|
|
824
896
|
children: "\u2191\u2193 navigate \xB7 enter select \xB7 q save & quit"
|
|
825
897
|
}, undefined, false, undefined, this)
|
|
@@ -830,10 +902,10 @@ function Exit({ sessionId, onAction }) {
|
|
|
830
902
|
|
|
831
903
|
// src/tui/screens/Resume.tsx
|
|
832
904
|
import { useState as useState4 } from "react";
|
|
833
|
-
import { Box as
|
|
834
|
-
import { jsxDEV as
|
|
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";
|
|
835
907
|
function Resume({ sessionId, onSelect }) {
|
|
836
|
-
const { exit } =
|
|
908
|
+
const { exit } = useTui3();
|
|
837
909
|
const [cursor, setCursor] = useState4(0);
|
|
838
910
|
const session = getSession(sessionId);
|
|
839
911
|
const conversations2 = getConversationsBySession(sessionId);
|
|
@@ -842,7 +914,7 @@ function Resume({ sessionId, onSelect }) {
|
|
|
842
914
|
onSelect(selection);
|
|
843
915
|
exit();
|
|
844
916
|
};
|
|
845
|
-
|
|
917
|
+
useInput3((e) => {
|
|
846
918
|
if (e.key === "c" && e.ctrl)
|
|
847
919
|
select({ type: "back" });
|
|
848
920
|
if (e.key === "up" || e.key === "k") {
|
|
@@ -861,17 +933,17 @@ function Resume({ sessionId, onSelect }) {
|
|
|
861
933
|
}
|
|
862
934
|
});
|
|
863
935
|
if (!session) {
|
|
864
|
-
return /* @__PURE__ */
|
|
936
|
+
return /* @__PURE__ */ jsxDEV7(Text7, {
|
|
865
937
|
color: "red",
|
|
866
938
|
children: "Session not found"
|
|
867
939
|
}, undefined, false, undefined, this);
|
|
868
940
|
}
|
|
869
|
-
return /* @__PURE__ */
|
|
941
|
+
return /* @__PURE__ */ jsxDEV7(Box6, {
|
|
870
942
|
flexDirection: "column",
|
|
871
943
|
padding: 1,
|
|
872
944
|
gap: 1,
|
|
873
945
|
children: [
|
|
874
|
-
/* @__PURE__ */
|
|
946
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
875
947
|
bold: true,
|
|
876
948
|
color: "#82AAFF",
|
|
877
949
|
children: [
|
|
@@ -879,17 +951,17 @@ function Resume({ sessionId, onSelect }) {
|
|
|
879
951
|
session.name
|
|
880
952
|
]
|
|
881
953
|
}, undefined, true, undefined, this),
|
|
882
|
-
/* @__PURE__ */
|
|
954
|
+
/* @__PURE__ */ jsxDEV7(Box6, {
|
|
883
955
|
flexDirection: "column",
|
|
884
956
|
children: [
|
|
885
|
-
/* @__PURE__ */
|
|
957
|
+
/* @__PURE__ */ jsxDEV7(Box6, {
|
|
886
958
|
flexDirection: "row",
|
|
887
959
|
gap: 1,
|
|
888
960
|
children: [
|
|
889
|
-
/* @__PURE__ */
|
|
961
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
890
962
|
children: cursor === 0 ? "\u276F" : " "
|
|
891
963
|
}, undefined, false, undefined, this),
|
|
892
|
-
/* @__PURE__ */
|
|
964
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
893
965
|
bold: cursor === 0,
|
|
894
966
|
color: "#34D399",
|
|
895
967
|
children: "+ New conversation"
|
|
@@ -901,42 +973,42 @@ function Resume({ sessionId, onSelect }) {
|
|
|
901
973
|
const isSelected = cursor === idx;
|
|
902
974
|
const duration = conv.endedAt ? formatDuration(new Date(conv.endedAt).getTime() - new Date(conv.startedAt).getTime()) : "active";
|
|
903
975
|
const ago = formatAgo(conv.startedAt);
|
|
904
|
-
return /* @__PURE__ */
|
|
976
|
+
return /* @__PURE__ */ jsxDEV7(Box6, {
|
|
905
977
|
flexDirection: "row",
|
|
906
978
|
gap: 1,
|
|
907
979
|
children: [
|
|
908
|
-
/* @__PURE__ */
|
|
980
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
909
981
|
children: isSelected ? "\u276F" : " "
|
|
910
982
|
}, undefined, false, undefined, this),
|
|
911
|
-
/* @__PURE__ */
|
|
983
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
912
984
|
bold: isSelected,
|
|
913
985
|
dim: conv.discarded,
|
|
914
986
|
children: conv.id.slice(0, 8)
|
|
915
987
|
}, undefined, false, undefined, this),
|
|
916
|
-
/* @__PURE__ */
|
|
988
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
917
989
|
dim: true,
|
|
918
990
|
children: ago
|
|
919
991
|
}, undefined, false, undefined, this),
|
|
920
|
-
/* @__PURE__ */
|
|
992
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
921
993
|
dim: true,
|
|
922
994
|
children: "\xB7"
|
|
923
995
|
}, undefined, false, undefined, this),
|
|
924
|
-
/* @__PURE__ */
|
|
996
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
925
997
|
dim: true,
|
|
926
998
|
children: [
|
|
927
999
|
conv.eventCount,
|
|
928
1000
|
" events"
|
|
929
1001
|
]
|
|
930
1002
|
}, undefined, true, undefined, this),
|
|
931
|
-
/* @__PURE__ */
|
|
1003
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
932
1004
|
dim: true,
|
|
933
1005
|
children: "\xB7"
|
|
934
1006
|
}, undefined, false, undefined, this),
|
|
935
|
-
/* @__PURE__ */
|
|
1007
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
936
1008
|
dim: true,
|
|
937
1009
|
children: duration
|
|
938
1010
|
}, undefined, false, undefined, this),
|
|
939
|
-
conv.discarded && /* @__PURE__ */
|
|
1011
|
+
conv.discarded && /* @__PURE__ */ jsxDEV7(Text7, {
|
|
940
1012
|
color: "red",
|
|
941
1013
|
dim: true,
|
|
942
1014
|
children: "(discarded)"
|
|
@@ -946,8 +1018,8 @@ function Resume({ sessionId, onSelect }) {
|
|
|
946
1018
|
})
|
|
947
1019
|
]
|
|
948
1020
|
}, undefined, true, undefined, this),
|
|
949
|
-
/* @__PURE__ */
|
|
950
|
-
children: /* @__PURE__ */
|
|
1021
|
+
/* @__PURE__ */ jsxDEV7(Box6, {
|
|
1022
|
+
children: /* @__PURE__ */ jsxDEV7(Text7, {
|
|
951
1023
|
dim: true,
|
|
952
1024
|
children: "\u2191\u2193 navigate \xB7 enter select \xB7 q back"
|
|
953
1025
|
}, undefined, false, undefined, this)
|
|
@@ -957,7 +1029,7 @@ function Resume({ sessionId, onSelect }) {
|
|
|
957
1029
|
}
|
|
958
1030
|
|
|
959
1031
|
// src/tui/run-screen.tsx
|
|
960
|
-
import { jsxDEV as
|
|
1032
|
+
import { jsxDEV as jsxDEV8 } from "react/jsx-dev-runtime";
|
|
961
1033
|
var [, , screen, outputPath, ...args] = process.argv;
|
|
962
1034
|
if (!screen || !outputPath) {
|
|
963
1035
|
console.error("Usage: run-screen <screen> <outputPath> [args...]");
|
|
@@ -967,7 +1039,7 @@ var result;
|
|
|
967
1039
|
switch (screen) {
|
|
968
1040
|
case "launch": {
|
|
969
1041
|
let selection = { type: "quit" };
|
|
970
|
-
const app = render(/* @__PURE__ */
|
|
1042
|
+
const app = render(/* @__PURE__ */ jsxDEV8(Launch, {
|
|
971
1043
|
onSelect: (s) => {
|
|
972
1044
|
selection = s;
|
|
973
1045
|
}
|
|
@@ -984,7 +1056,7 @@ switch (screen) {
|
|
|
984
1056
|
process.exit(1);
|
|
985
1057
|
}
|
|
986
1058
|
let action = "save";
|
|
987
|
-
const app = render(/* @__PURE__ */
|
|
1059
|
+
const app = render(/* @__PURE__ */ jsxDEV8(Exit, {
|
|
988
1060
|
sessionId,
|
|
989
1061
|
onAction: (a) => {
|
|
990
1062
|
action = a;
|
|
@@ -1002,7 +1074,7 @@ switch (screen) {
|
|
|
1002
1074
|
process.exit(1);
|
|
1003
1075
|
}
|
|
1004
1076
|
let selection = { type: "back" };
|
|
1005
|
-
const app = render(/* @__PURE__ */
|
|
1077
|
+
const app = render(/* @__PURE__ */ jsxDEV8(Resume, {
|
|
1006
1078
|
sessionId,
|
|
1007
1079
|
onSelect: (s) => {
|
|
1008
1080
|
selection = s;
|