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.
- package/dist/bertrand.js +887 -136
- package/dist/run-screen.js +509 -147
- package/package.json +2 -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 { useMemo as useMemo2, useState as
|
|
23
|
-
import { Box as
|
|
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
|
|
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] =
|
|
105
|
-
const [cursor, setCursor] =
|
|
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
|
-
|
|
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__ */
|
|
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__ */
|
|
412
|
+
props.mode === "multi" && props.selected.length > 0 && /* @__PURE__ */ jsxDEV4(Box4, {
|
|
185
413
|
flexDirection: "row",
|
|
186
414
|
gap: 1,
|
|
187
415
|
children: [
|
|
188
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
434
|
+
children: /* @__PURE__ */ jsxDEV4(TextField, {
|
|
207
435
|
value: filter,
|
|
208
436
|
onChange: setFilter,
|
|
209
437
|
onSubmit: handleSubmit,
|
|
210
438
|
placeholder,
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
isFocused
|
|
439
|
+
isFocused,
|
|
440
|
+
ghost
|
|
214
441
|
}, undefined, false, undefined, this)
|
|
215
442
|
}, undefined, false, undefined, this),
|
|
216
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
467
|
+
return /* @__PURE__ */ jsxDEV4(Box4, {
|
|
241
468
|
flexDirection: "column",
|
|
242
469
|
children: [
|
|
243
|
-
showDivider && /* @__PURE__ */
|
|
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__ */
|
|
474
|
+
/* @__PURE__ */ jsxDEV4(Box4, {
|
|
248
475
|
flexDirection: "row",
|
|
249
476
|
gap: 1,
|
|
250
|
-
|
|
251
|
-
|
|
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__ */
|
|
493
|
+
return /* @__PURE__ */ jsxDEV4(Box4, {
|
|
267
494
|
flexDirection: "row",
|
|
268
|
-
children: /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
511
|
+
/* @__PURE__ */ jsxDEV4(Box4, {
|
|
286
512
|
flexDirection: "row",
|
|
287
513
|
gap: 0,
|
|
288
|
-
children: item.display ? item.display : /* @__PURE__ */
|
|
289
|
-
color: isCursor ? "
|
|
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__ */
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
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__ */
|
|
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
|
|
326
|
-
import { jsxDEV as
|
|
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__ */
|
|
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
|
|
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
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
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] =
|
|
621
|
-
const
|
|
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) =>
|
|
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__ */
|
|
1005
|
+
return /* @__PURE__ */ jsxDEV6(Box5, {
|
|
685
1006
|
flexDirection: "column",
|
|
686
1007
|
paddingY: 1,
|
|
687
1008
|
gap: 1,
|
|
688
1009
|
children: [
|
|
689
|
-
/* @__PURE__ */
|
|
1010
|
+
/* @__PURE__ */ jsxDEV6(Box5, {
|
|
690
1011
|
marginX: 1,
|
|
691
|
-
children: /* @__PURE__ */
|
|
1012
|
+
children: /* @__PURE__ */ jsxDEV6(Logo, {}, undefined, false, undefined, this)
|
|
692
1013
|
}, undefined, false, undefined, this),
|
|
693
|
-
/* @__PURE__ */
|
|
1014
|
+
/* @__PURE__ */ jsxDEV6(Box5, {
|
|
694
1015
|
flexDirection: "column",
|
|
695
1016
|
marginX: 2,
|
|
696
1017
|
gap: 1,
|
|
697
1018
|
children: [
|
|
698
|
-
/* @__PURE__ */
|
|
699
|
-
/* @__PURE__ */
|
|
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__ */
|
|
1024
|
+
/* @__PURE__ */ jsxDEV6(Box5, {
|
|
704
1025
|
flexDirection: "row",
|
|
705
1026
|
gap: 1,
|
|
706
1027
|
children: [
|
|
707
|
-
/* @__PURE__ */
|
|
1028
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
708
1029
|
bold: true,
|
|
709
1030
|
children: "Sessions"
|
|
710
1031
|
}, undefined, false, undefined, this),
|
|
711
|
-
visibleSessions.length === 0 ? /* @__PURE__ */
|
|
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__ */
|
|
1035
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV6(Fragment, {
|
|
715
1036
|
children: [
|
|
716
|
-
/* @__PURE__ */
|
|
1037
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
717
1038
|
dim: true,
|
|
718
1039
|
children: "\xB7"
|
|
719
1040
|
}, undefined, false, undefined, this),
|
|
720
|
-
/* @__PURE__ */
|
|
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__ */
|
|
1048
|
+
counts.waiting > 0 && /* @__PURE__ */ jsxDEV6(Fragment, {
|
|
728
1049
|
children: [
|
|
729
|
-
/* @__PURE__ */
|
|
1050
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
730
1051
|
dim: true,
|
|
731
1052
|
children: "\xB7"
|
|
732
1053
|
}, undefined, false, undefined, this),
|
|
733
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
1109
|
+
error && /* @__PURE__ */ jsxDEV6(Text6, {
|
|
756
1110
|
color: "red",
|
|
757
1111
|
children: error
|
|
758
1112
|
}, undefined, false, undefined, this),
|
|
759
|
-
/* @__PURE__ */
|
|
1113
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
760
1114
|
dim: true,
|
|
761
|
-
children:
|
|
762
|
-
|
|
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
|
|
773
|
-
import { Box as
|
|
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
|
|
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] =
|
|
1165
|
+
const [cursor, setCursor] = useState4(0);
|
|
804
1166
|
const session = getSession(sessionId);
|
|
805
1167
|
const conversations2 = session ? getConversationsBySession(session.id) : [];
|
|
806
|
-
|
|
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__ */
|
|
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__ */
|
|
1191
|
+
return /* @__PURE__ */ jsxDEV7(Box6, {
|
|
830
1192
|
flexDirection: "column",
|
|
831
1193
|
padding: 1,
|
|
832
1194
|
gap: 1,
|
|
833
1195
|
children: [
|
|
834
|
-
/* @__PURE__ */
|
|
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__ */
|
|
1201
|
+
/* @__PURE__ */ jsxDEV7(Box6, {
|
|
840
1202
|
flexDirection: "column",
|
|
841
1203
|
children: [
|
|
842
|
-
/* @__PURE__ */
|
|
1204
|
+
/* @__PURE__ */ jsxDEV7(Box6, {
|
|
843
1205
|
flexDirection: "row",
|
|
844
1206
|
gap: 1,
|
|
845
1207
|
children: [
|
|
846
|
-
/* @__PURE__ */
|
|
1208
|
+
/* @__PURE__ */ jsxDEV7(StatusDot, {
|
|
847
1209
|
status: session.status
|
|
848
1210
|
}, undefined, false, undefined, this),
|
|
849
|
-
/* @__PURE__ */
|
|
1211
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
850
1212
|
bold: true,
|
|
851
1213
|
children: session.name
|
|
852
1214
|
}, undefined, false, undefined, this),
|
|
853
|
-
duration && /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
1235
|
+
/* @__PURE__ */ jsxDEV7(Box6, {
|
|
874
1236
|
flexDirection: "column",
|
|
875
|
-
children: OPTIONS.map((opt, i) => /* @__PURE__ */
|
|
1237
|
+
children: OPTIONS.map((opt, i) => /* @__PURE__ */ jsxDEV7(Box6, {
|
|
876
1238
|
flexDirection: "row",
|
|
877
1239
|
gap: 1,
|
|
878
1240
|
children: [
|
|
879
|
-
/* @__PURE__ */
|
|
1241
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
880
1242
|
children: i === cursor ? "\u276F" : " "
|
|
881
1243
|
}, undefined, false, undefined, this),
|
|
882
|
-
/* @__PURE__ */
|
|
1244
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
883
1245
|
bold: i === cursor,
|
|
884
1246
|
children: opt.label
|
|
885
1247
|
}, undefined, false, undefined, this),
|
|
886
|
-
/* @__PURE__ */
|
|
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__ */
|
|
894
|
-
children: /* @__PURE__ */
|
|
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
|
|
905
|
-
import { Box as
|
|
906
|
-
import { jsxDEV as
|
|
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] =
|
|
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
|
-
|
|
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__ */
|
|
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__ */
|
|
1303
|
+
return /* @__PURE__ */ jsxDEV8(Box7, {
|
|
942
1304
|
flexDirection: "column",
|
|
943
1305
|
padding: 1,
|
|
944
1306
|
gap: 1,
|
|
945
1307
|
children: [
|
|
946
|
-
/* @__PURE__ */
|
|
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__ */
|
|
1316
|
+
/* @__PURE__ */ jsxDEV8(Box7, {
|
|
955
1317
|
flexDirection: "column",
|
|
956
1318
|
children: [
|
|
957
|
-
/* @__PURE__ */
|
|
1319
|
+
/* @__PURE__ */ jsxDEV8(Box7, {
|
|
958
1320
|
flexDirection: "row",
|
|
959
1321
|
gap: 1,
|
|
960
1322
|
children: [
|
|
961
|
-
/* @__PURE__ */
|
|
1323
|
+
/* @__PURE__ */ jsxDEV8(Text8, {
|
|
962
1324
|
children: cursor === 0 ? "\u276F" : " "
|
|
963
1325
|
}, undefined, false, undefined, this),
|
|
964
|
-
/* @__PURE__ */
|
|
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__ */
|
|
1338
|
+
return /* @__PURE__ */ jsxDEV8(Box7, {
|
|
977
1339
|
flexDirection: "row",
|
|
978
1340
|
gap: 1,
|
|
979
1341
|
children: [
|
|
980
|
-
/* @__PURE__ */
|
|
1342
|
+
/* @__PURE__ */ jsxDEV8(Text8, {
|
|
981
1343
|
children: isSelected ? "\u276F" : " "
|
|
982
1344
|
}, undefined, false, undefined, this),
|
|
983
|
-
/* @__PURE__ */
|
|
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__ */
|
|
1350
|
+
/* @__PURE__ */ jsxDEV8(Text8, {
|
|
989
1351
|
dim: true,
|
|
990
1352
|
children: ago
|
|
991
1353
|
}, undefined, false, undefined, this),
|
|
992
|
-
/* @__PURE__ */
|
|
1354
|
+
/* @__PURE__ */ jsxDEV8(Text8, {
|
|
993
1355
|
dim: true,
|
|
994
1356
|
children: "\xB7"
|
|
995
1357
|
}, undefined, false, undefined, this),
|
|
996
|
-
/* @__PURE__ */
|
|
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__ */
|
|
1365
|
+
/* @__PURE__ */ jsxDEV8(Text8, {
|
|
1004
1366
|
dim: true,
|
|
1005
1367
|
children: "\xB7"
|
|
1006
1368
|
}, undefined, false, undefined, this),
|
|
1007
|
-
/* @__PURE__ */
|
|
1369
|
+
/* @__PURE__ */ jsxDEV8(Text8, {
|
|
1008
1370
|
dim: true,
|
|
1009
1371
|
children: duration
|
|
1010
1372
|
}, undefined, false, undefined, this),
|
|
1011
|
-
conv.discarded && /* @__PURE__ */
|
|
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__ */
|
|
1022
|
-
children: /* @__PURE__ */
|
|
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
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
1439
|
+
const app = render(/* @__PURE__ */ jsxDEV9(Resume, {
|
|
1078
1440
|
sessionId,
|
|
1079
1441
|
onSelect: (s) => {
|
|
1080
1442
|
selection = s;
|