@sealant/mend 0.27.5 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -13
- package/dist/{chunk-A5I5EW2Y.js → chunk-F6AJNFVE.js} +238 -8
- package/dist/{dashboard-G52NHJCZ.js → dashboard-ZKCPU2QJ.js} +1388 -602
- package/dist/main.js +1473 -663
- package/man/mend-accounts.1 +1 -1
- package/man/mend-adopt.1 +8 -2
- package/man/mend-attach.1 +1 -1
- package/man/mend-codex.1 +1 -1
- package/man/mend-completions.1 +1 -1
- package/man/mend-connect.1 +9 -4
- package/man/mend-continue.1 +1 -1
- package/man/mend-doctor.1 +1 -1
- package/man/mend-dotfiles-sync.1 +1 -1
- package/man/mend-dotfiles.1 +1 -1
- package/man/mend-env-cluster.1 +1 -1
- package/man/mend-env-load.1 +1 -1
- package/man/mend-env-show.1 +1 -1
- package/man/mend-folder-create.1 +11 -0
- package/man/mend-folder-list.1 +11 -0
- package/man/mend-folder-push.1 +18 -0
- package/man/mend-folder-rm.1 +11 -0
- package/man/mend-help.1 +1 -1
- package/man/mend-invite.1 +26 -0
- package/man/mend-keys-init.1 +1 -1
- package/man/mend-keys-mode.1 +1 -1
- package/man/mend-keys-share.1 +1 -1
- package/man/mend-keys-show.1 +1 -1
- package/man/mend-login.1 +1 -1
- package/man/mend-logout.1 +1 -1
- package/man/mend-man.1 +1 -1
- package/man/mend-members.1 +11 -0
- package/man/mend-operator-exposure.1 +11 -0
- package/man/mend-operator-gate.1 +11 -0
- package/man/mend-operator-grant-owner.1 +11 -0
- package/man/mend-operator-org-create.1 +11 -0
- package/man/mend-operator-org-invite-owner.1 +15 -0
- package/man/mend-operator-org-list.1 +11 -0
- package/man/mend-operator-org-rename.1 +11 -0
- package/man/mend-operator-reset-link.1 +11 -0
- package/man/mend-pair.1 +1 -1
- package/man/mend-projects.1 +1 -1
- package/man/mend-refresh.1 +1 -1
- package/man/mend-rejoin.1 +1 -1
- package/man/mend-resume.1 +1 -1
- package/man/mend-run.1 +1 -1
- package/man/mend-server-logs.1 +1 -1
- package/man/mend-server-restart.1 +1 -1
- package/man/mend-server-setup.1 +2 -2
- package/man/mend-server-start.1 +1 -1
- package/man/mend-server-status.1 +1 -1
- package/man/mend-server-stop.1 +1 -1
- package/man/mend-server-upgrade.1 +1 -1
- package/man/mend-server.1 +1 -1
- package/man/mend-service-add.1 +1 -1
- package/man/mend-service-connect.1 +1 -1
- package/man/mend-service-init.1 +1 -1
- package/man/mend-service-list.1 +1 -1
- package/man/mend-service-logs.1 +1 -1
- package/man/mend-service-restart.1 +1 -1
- package/man/mend-service-run.1 +1 -1
- package/man/mend-service-stop.1 +1 -1
- package/man/mend-session-share.1 +14 -0
- package/man/mend-sessions.1 +1 -1
- package/man/mend-shell.1 +1 -1
- package/man/mend-skills-push.1 +1 -1
- package/man/mend-skills.1 +1 -1
- package/man/mend-ssh-setup.1 +1 -1
- package/man/mend-ssh.1 +1 -1
- package/man/mend-stop.1 +1 -1
- package/man/mend-ui.1 +6 -2
- package/man/mend-uninstall.1 +1 -1
- package/man/mend-version.1 +1 -1
- package/man/mend-worktrees.1 +1 -1
- package/man/mend.1 +48 -2
- package/package.json +1 -1
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
normalizeProjectName,
|
|
9
9
|
pendingId,
|
|
10
10
|
repositoryCloneUrlIssue
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-F6AJNFVE.js";
|
|
12
12
|
|
|
13
13
|
// src/dashboard.tsx
|
|
14
14
|
import { CliRenderEvents, createCliRenderer } from "@opentui/core";
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
useQuery as useQuery2,
|
|
21
21
|
useQueryClient as useQueryClient2
|
|
22
22
|
} from "@tanstack/react-query";
|
|
23
|
-
import { useEffect as useEffect2, useRef as useRef2, useState as useState2, useSyncExternalStore } from "react";
|
|
23
|
+
import { useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2, useSyncExternalStore } from "react";
|
|
24
24
|
|
|
25
25
|
// src/dashboard-adoption.ts
|
|
26
26
|
var deriveAdoptOffer = (facts) => {
|
|
@@ -143,7 +143,9 @@ var deriveWorktrees = (data, projectId) => {
|
|
|
143
143
|
const item = toSessionItem(data, detail);
|
|
144
144
|
const sorted = detail.sessions.filter((session) => !isDeadEnd(session)).toSorted(bySessionRecency);
|
|
145
145
|
const groups = [];
|
|
146
|
-
if (detail.worktrees
|
|
146
|
+
if (detail.worktrees === void 0) {
|
|
147
|
+
for (const session of sorted) groups.push(pseudoGroup(session, item));
|
|
148
|
+
} else {
|
|
147
149
|
const claimed = /* @__PURE__ */ new Set();
|
|
148
150
|
for (const worktree of detail.worktrees) {
|
|
149
151
|
const members = sorted.filter((session) => session.worktreeId === worktree.id);
|
|
@@ -164,8 +166,6 @@ var deriveWorktrees = (data, projectId) => {
|
|
|
164
166
|
if (claimed.has(session.id)) continue;
|
|
165
167
|
groups.push(pseudoGroup(session, item));
|
|
166
168
|
}
|
|
167
|
-
} else {
|
|
168
|
-
for (const session of sorted) groups.push(pseudoGroup(session, item));
|
|
169
169
|
}
|
|
170
170
|
return groups.toSorted((a, b) => {
|
|
171
171
|
if (a.live > 0 !== b.live > 0) return a.live > 0 ? -1 : 1;
|
|
@@ -185,13 +185,6 @@ var pseudoGroup = (session, item) => ({
|
|
|
185
185
|
live: LIVE_STATUSES.has(session.status) ? 1 : 0,
|
|
186
186
|
annotation: item(session).annotation
|
|
187
187
|
});
|
|
188
|
-
var deriveRows = (groups) => groups.flatMap(
|
|
189
|
-
(group) => [
|
|
190
|
-
{ kind: "worktree", group },
|
|
191
|
-
...group.sessions.map((item) => ({ kind: "session", group, item }))
|
|
192
|
-
]
|
|
193
|
-
);
|
|
194
|
-
var rowKeyOf = (row) => row.kind === "worktree" ? `wt:${row.group.key}` : `s:${row.item.session.id}`;
|
|
195
188
|
var deriveHarnesses = (resuming) => {
|
|
196
189
|
if (resuming !== null) {
|
|
197
190
|
const others = Object.keys(HARNESS_COMMANDS).filter((h) => h !== resuming.harness);
|
|
@@ -218,6 +211,20 @@ var deriveHarnesses = (resuming) => {
|
|
|
218
211
|
})
|
|
219
212
|
);
|
|
220
213
|
};
|
|
214
|
+
var sessionDisplayName = (session) => {
|
|
215
|
+
if (session.label !== null && session.label !== "") return session.label;
|
|
216
|
+
if (isPendingId(session.id)) return `${session.harness} \xB7 starting`;
|
|
217
|
+
return `${session.harness} ${session.id.slice(0, 8)}`;
|
|
218
|
+
};
|
|
219
|
+
var groupBaseLabel = (group) => {
|
|
220
|
+
if (group.baseRef !== null && group.baseRef !== "") return group.baseRef;
|
|
221
|
+
const sha = group.sessions[0]?.session.baseSha ?? "";
|
|
222
|
+
return sha === "" ? "base unknown" : sha.slice(0, 7);
|
|
223
|
+
};
|
|
224
|
+
var groupActivityAt = (group) => group.sessions.reduce(
|
|
225
|
+
(newest, item) => item.session.createdAt > newest ? item.session.createdAt : newest,
|
|
226
|
+
group.createdAt
|
|
227
|
+
);
|
|
221
228
|
var foldGroupStatus = (group) => {
|
|
222
229
|
const statuses = group.sessions.map((item) => item.session.status);
|
|
223
230
|
if (statuses.includes("waiting")) return "waiting";
|
|
@@ -227,12 +234,33 @@ var foldGroupStatus = (group) => {
|
|
|
227
234
|
};
|
|
228
235
|
var liveShellOf = (processes) => processes.findLast((process2) => process2.kind === "shell" && process2.exitedAt === null) ?? null;
|
|
229
236
|
var liveProtocolOf = (processes) => processes.find((process2) => process2.kind === "agent-protocol" && process2.exitedAt === null) ?? null;
|
|
230
|
-
var
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
if (
|
|
234
|
-
|
|
235
|
-
return
|
|
237
|
+
var planAttach = (session) => {
|
|
238
|
+
if (session === null) return { kind: "none" };
|
|
239
|
+
if (isPendingId(session.id)) return { kind: "pending", session };
|
|
240
|
+
if (!LIVE_STATUSES.has(session.status)) return { kind: "settled", session };
|
|
241
|
+
if (session.status === "starting") return { kind: "starting", session };
|
|
242
|
+
return { kind: "attach", session };
|
|
243
|
+
};
|
|
244
|
+
var planResume = (session) => {
|
|
245
|
+
if (session === null) return { kind: "none" };
|
|
246
|
+
if (isPendingId(session.id)) return { kind: "pending", session };
|
|
247
|
+
if (LIVE_STATUSES.has(session.status)) return { kind: "live", session };
|
|
248
|
+
return { kind: "resume", session };
|
|
249
|
+
};
|
|
250
|
+
var createLaunchGate = () => {
|
|
251
|
+
const taken = /* @__PURE__ */ new Set();
|
|
252
|
+
return {
|
|
253
|
+
take: (key) => {
|
|
254
|
+
if (taken.has(key)) return false;
|
|
255
|
+
taken.add(key);
|
|
256
|
+
return true;
|
|
257
|
+
},
|
|
258
|
+
release: (key) => {
|
|
259
|
+
taken.delete(key);
|
|
260
|
+
},
|
|
261
|
+
held: (key) => taken.has(key),
|
|
262
|
+
count: () => taken.size
|
|
263
|
+
};
|
|
236
264
|
};
|
|
237
265
|
var markSessionStopped = (data, sessionId) => {
|
|
238
266
|
const processesBySession = new Map(data.processesBySession);
|
|
@@ -290,6 +318,17 @@ var filterBranches = (branches, query) => {
|
|
|
290
318
|
return b.branch.committedAt.localeCompare(a.branch.committedAt);
|
|
291
319
|
}).map((entry) => entry.branch);
|
|
292
320
|
};
|
|
321
|
+
var baseStepNotice = (state) => {
|
|
322
|
+
if (state.branchError !== null) {
|
|
323
|
+
return `branches unreadable \u2014 ${state.branchError} \xB7 enter uses the default`;
|
|
324
|
+
}
|
|
325
|
+
if (state.branches === null) return "reading branches\u2026";
|
|
326
|
+
if (state.branches.length === 0) return "no branches read \u2014 enter uses the default branch";
|
|
327
|
+
if (filterBranches(state.branches, state.query).length === 0) {
|
|
328
|
+
return "no branch matches \u2014 enter uses the default branch";
|
|
329
|
+
}
|
|
330
|
+
return null;
|
|
331
|
+
};
|
|
293
332
|
var advanceFromBase = (current) => {
|
|
294
333
|
const chosen = filterBranches(current.branches ?? [], current.query)[current.baseIndex];
|
|
295
334
|
return {
|
|
@@ -298,6 +337,366 @@ var advanceFromBase = (current) => {
|
|
|
298
337
|
step: "harness"
|
|
299
338
|
};
|
|
300
339
|
};
|
|
340
|
+
var COLUMNS = ["projects", "worktrees", "sessions", "detail"];
|
|
341
|
+
var NAV_SECTIONS = ["projects", "worktrees", "sessions"];
|
|
342
|
+
var isNavSection = (column) => column !== "detail";
|
|
343
|
+
var expandedSection = (focus, lastNav) => isNavSection(focus) ? focus : lastNav;
|
|
344
|
+
var SIDEBAR_SHARE = 0.25;
|
|
345
|
+
var MIN_SIDEBAR_WIDTH = 24;
|
|
346
|
+
var MIN_DETAIL_WIDTH = 48;
|
|
347
|
+
var SPLIT_MIN_WIDTH = MIN_SIDEBAR_WIDTH + MIN_DETAIL_WIDTH;
|
|
348
|
+
var MIN_SECTION_HEIGHT = 3;
|
|
349
|
+
var CHROME_ROWS = 3;
|
|
350
|
+
var stackSections = (body, open) => {
|
|
351
|
+
const room = body - (NAV_SECTIONS.length - 1) * MIN_SECTION_HEIGHT;
|
|
352
|
+
if (room < MIN_SECTION_HEIGHT) {
|
|
353
|
+
const height = Math.max(MIN_SECTION_HEIGHT, body);
|
|
354
|
+
return [{ section: open, expanded: true, height, rows: height - 2 }];
|
|
355
|
+
}
|
|
356
|
+
return NAV_SECTIONS.map(
|
|
357
|
+
(section) => section === open ? { section, expanded: true, height: room, rows: room - 2 } : { section, expanded: false, height: MIN_SECTION_HEIGHT, rows: 1 }
|
|
358
|
+
);
|
|
359
|
+
};
|
|
360
|
+
var planLayout = (width, height, focus, lastNav) => {
|
|
361
|
+
const split = width >= SPLIT_MIN_WIDTH;
|
|
362
|
+
const navVisible = split || isNavSection(focus);
|
|
363
|
+
const sidebarOuter = split ? Math.max(
|
|
364
|
+
MIN_SIDEBAR_WIDTH,
|
|
365
|
+
Math.min(width - MIN_DETAIL_WIDTH, Math.round(width * SIDEBAR_SHARE))
|
|
366
|
+
) : navVisible ? Math.max(4, width) : 0;
|
|
367
|
+
const detailOuter = split ? width - sidebarOuter : navVisible ? 0 : Math.max(4, width);
|
|
368
|
+
const build = (body2, breadcrumb) => {
|
|
369
|
+
const sections = navVisible ? stackSections(body2, expandedSection(focus, lastNav)) : [];
|
|
370
|
+
const shown = new Set(sections.map((section) => section.section));
|
|
371
|
+
if (detailOuter > 0) shown.add("detail");
|
|
372
|
+
return {
|
|
373
|
+
split,
|
|
374
|
+
sidebarWidth: navVisible ? sidebarOuter : 0,
|
|
375
|
+
sections,
|
|
376
|
+
detailWidth: detailOuter === 0 ? 0 : Math.max(1, detailOuter - 2),
|
|
377
|
+
detailRows: detailOuter === 0 ? 0 : Math.max(1, body2 - 2),
|
|
378
|
+
offscreen: COLUMNS.filter((column) => !shown.has(column)),
|
|
379
|
+
breadcrumb
|
|
380
|
+
};
|
|
381
|
+
};
|
|
382
|
+
const body = Math.max(MIN_SECTION_HEIGHT, height - CHROME_ROWS);
|
|
383
|
+
const full = build(body, false);
|
|
384
|
+
if (full.offscreen.length === 0) return full;
|
|
385
|
+
return body - 1 >= MIN_SECTION_HEIGHT ? build(body - 1, true) : full;
|
|
386
|
+
};
|
|
387
|
+
var stepColumn = (focus, delta) => {
|
|
388
|
+
const at = COLUMNS.indexOf(focus);
|
|
389
|
+
if (at === -1) return "sessions";
|
|
390
|
+
return COLUMNS[Math.max(0, Math.min(COLUMNS.length - 1, at + delta))] ?? focus;
|
|
391
|
+
};
|
|
392
|
+
var fitHints = (hints, width) => {
|
|
393
|
+
const kept = [];
|
|
394
|
+
for (const hint of hints) {
|
|
395
|
+
const candidate = [...kept, hint].join(" \xB7 ");
|
|
396
|
+
const wouldTruncate = kept.length + 1 < hints.length;
|
|
397
|
+
if (candidate.length + (wouldTruncate ? 2 : 0) > width) break;
|
|
398
|
+
kept.push(hint);
|
|
399
|
+
}
|
|
400
|
+
if (kept.length === hints.length) return kept.join(" \xB7 ");
|
|
401
|
+
return kept.length === 0 ? "\u2026" : `${kept.join(" \xB7 ")} \u2026`;
|
|
402
|
+
};
|
|
403
|
+
var clampIndex = (length, index) => length === 0 ? 0 : Math.max(0, Math.min(length - 1, index));
|
|
404
|
+
var KEY_BINDINGS = [
|
|
405
|
+
{
|
|
406
|
+
verb: "moveUp",
|
|
407
|
+
keys: ["up", "k"],
|
|
408
|
+
shift: false,
|
|
409
|
+
hints: {
|
|
410
|
+
projects: "\u2191\u2193 move",
|
|
411
|
+
worktrees: "\u2191\u2193 move",
|
|
412
|
+
sessions: "\u2191\u2193 move",
|
|
413
|
+
detail: "\u2191\u2193 scroll"
|
|
414
|
+
}
|
|
415
|
+
},
|
|
416
|
+
{ verb: "moveDown", keys: ["down", "j"], shift: false, hints: {} },
|
|
417
|
+
{ verb: "pageUp", keys: ["pageup"], shift: false, hints: {} },
|
|
418
|
+
{ verb: "pageDown", keys: ["pagedown"], shift: false, hints: {} },
|
|
419
|
+
{
|
|
420
|
+
verb: "columnRight",
|
|
421
|
+
keys: ["return", "linefeed", "l", "right", "tab"],
|
|
422
|
+
shift: false,
|
|
423
|
+
hints: { projects: "\u2192 worktrees", worktrees: "\u2190\u2192 panes", sessions: "\u2190\u2192 panes" }
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
verb: "columnLeft",
|
|
427
|
+
keys: ["left", "h", "-", "backspace"],
|
|
428
|
+
shift: false,
|
|
429
|
+
hints: { detail: "\u2190 sessions" }
|
|
430
|
+
},
|
|
431
|
+
{ verb: "columnLeft", keys: ["backtab"], shift: "any", hints: {} },
|
|
432
|
+
{ verb: "columnLeft", keys: ["tab"], shift: true, hints: {} },
|
|
433
|
+
{
|
|
434
|
+
verb: "attach",
|
|
435
|
+
keys: ["a"],
|
|
436
|
+
shift: false,
|
|
437
|
+
hints: { worktrees: "a attach", sessions: "a attach", detail: "a attach" }
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
verb: "resume",
|
|
441
|
+
keys: ["r"],
|
|
442
|
+
shift: false,
|
|
443
|
+
hints: { worktrees: "r resume", sessions: "r resume", detail: "r resume" }
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
verb: "newSession",
|
|
447
|
+
keys: ["n"],
|
|
448
|
+
shift: false,
|
|
449
|
+
hints: {
|
|
450
|
+
projects: "n/w new worktree",
|
|
451
|
+
worktrees: "n new session",
|
|
452
|
+
sessions: "n new session"
|
|
453
|
+
}
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
verb: "newWorktree",
|
|
457
|
+
keys: ["w"],
|
|
458
|
+
shift: false,
|
|
459
|
+
hints: { worktrees: "w new worktree", sessions: "w new worktree" }
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
verb: "stop",
|
|
463
|
+
keys: ["k"],
|
|
464
|
+
shift: true,
|
|
465
|
+
hints: { worktrees: "\u21E7K stop all", sessions: "\u21E7K stop" }
|
|
466
|
+
},
|
|
467
|
+
{ verb: "stop", keys: ["x"], shift: false, hints: {} },
|
|
468
|
+
{
|
|
469
|
+
verb: "remove",
|
|
470
|
+
keys: ["d"],
|
|
471
|
+
shift: true,
|
|
472
|
+
hints: { worktrees: "\u21E7D remove worktree", sessions: "\u21E7D remove" }
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
verb: "review",
|
|
476
|
+
keys: ["v"],
|
|
477
|
+
shift: false,
|
|
478
|
+
hints: { worktrees: "v review", sessions: "v review", detail: "v review" }
|
|
479
|
+
},
|
|
480
|
+
{
|
|
481
|
+
verb: "rename",
|
|
482
|
+
keys: ["e"],
|
|
483
|
+
shift: false,
|
|
484
|
+
hints: { sessions: "e rename", detail: "e rename" }
|
|
485
|
+
},
|
|
486
|
+
{ verb: "openWeb", keys: ["o"], shift: false, hints: { sessions: "o web", detail: "o web" } },
|
|
487
|
+
{
|
|
488
|
+
verb: "refresh",
|
|
489
|
+
keys: ["r"],
|
|
490
|
+
shift: true,
|
|
491
|
+
hints: { projects: "\u21E7R refresh", worktrees: "\u21E7R refresh", detail: "\u21E7R refresh" }
|
|
492
|
+
},
|
|
493
|
+
{
|
|
494
|
+
verb: "quit",
|
|
495
|
+
keys: ["q"],
|
|
496
|
+
shift: false,
|
|
497
|
+
hints: { projects: "q quit", worktrees: "q quit", sessions: "q quit", detail: "q quit" }
|
|
498
|
+
}
|
|
499
|
+
];
|
|
500
|
+
var verbForKey = (name, shift) => KEY_BINDINGS.find(
|
|
501
|
+
(binding) => binding.keys.includes(name) && (binding.shift === "any" || binding.shift === shift)
|
|
502
|
+
)?.verb ?? null;
|
|
503
|
+
var verbHints = (focus) => KEY_BINDINGS.flatMap((binding) => {
|
|
504
|
+
const hint = binding.hints[focus];
|
|
505
|
+
return hint === void 0 ? [] : [hint];
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
// src/dashboard-preview.ts
|
|
509
|
+
import { stripVTControlCharacters } from "node:util";
|
|
510
|
+
var BLOCK_LINES = {
|
|
511
|
+
user: 6,
|
|
512
|
+
assistant: 14,
|
|
513
|
+
reasoning: 4,
|
|
514
|
+
command: 3,
|
|
515
|
+
output: 5,
|
|
516
|
+
meta: 1,
|
|
517
|
+
blank: 1
|
|
518
|
+
};
|
|
519
|
+
var CONTROL_STRING = (
|
|
520
|
+
// eslint-disable-next-line no-control-regex -- matching control bytes is the point
|
|
521
|
+
/(?:\u001b[\]P^_X]|[\u0090\u0098\u009d\u009e\u009f])[\s\S]*?(?:\u0007|\u001b\\|\u009c|$)/g
|
|
522
|
+
);
|
|
523
|
+
var BELL = "\x07";
|
|
524
|
+
var UNSAFE_CONTROLS = /[\u0000-\u0008\u000b-\u001f\u007f-\u009f]/g;
|
|
525
|
+
var lastWrite = (line) => line.includes("\r") ? line.split("\r").filter((part) => part !== "").at(-1) ?? "" : line;
|
|
526
|
+
var plainText = (text) => stripVTControlCharacters(text.replaceAll(CONTROL_STRING, "").replaceAll(BELL, "")).replaceAll("\r\n", "\n").split("\n").map(lastWrite).join("\n").replaceAll(UNSAFE_CONTROLS, "");
|
|
527
|
+
var GRAPHEMES = new Intl.Segmenter("en", { granularity: "grapheme" });
|
|
528
|
+
var NON_ASCII = /[^\u0020-\u007e\t]/;
|
|
529
|
+
var ZERO_WIDTH = /^[\p{Mn}\p{Me}\p{Cf}]/u;
|
|
530
|
+
var EMOJI_PRESENTATION = "\uFE0F";
|
|
531
|
+
var WIDE_RANGES = [
|
|
532
|
+
[4352, 4447],
|
|
533
|
+
// Hangul Jamo
|
|
534
|
+
[11904, 12350],
|
|
535
|
+
// CJK radicals, Kangxi, CJK symbols and punctuation
|
|
536
|
+
[12353, 42191],
|
|
537
|
+
// Kana, Bopomofo, CJK unified ideographs, Yi
|
|
538
|
+
[43360, 43391],
|
|
539
|
+
// Hangul Jamo Extended-A
|
|
540
|
+
[44032, 55203],
|
|
541
|
+
// Hangul syllables
|
|
542
|
+
[63744, 64255],
|
|
543
|
+
// CJK compatibility ideographs
|
|
544
|
+
[65040, 65049],
|
|
545
|
+
// Vertical forms
|
|
546
|
+
[65072, 65135],
|
|
547
|
+
// CJK compatibility forms, small form variants
|
|
548
|
+
[65280, 65376],
|
|
549
|
+
// Fullwidth forms
|
|
550
|
+
[65504, 65510],
|
|
551
|
+
// Fullwidth signs
|
|
552
|
+
[94176, 94180],
|
|
553
|
+
// Tangut and Nushu marks
|
|
554
|
+
[94208, 101589],
|
|
555
|
+
// Tangut
|
|
556
|
+
[110592, 111359],
|
|
557
|
+
// Kana supplement and extensions
|
|
558
|
+
[127488, 127743],
|
|
559
|
+
// Enclosed ideographic supplement
|
|
560
|
+
[127744, 129791],
|
|
561
|
+
// Emoji planes
|
|
562
|
+
[131072, 262141]
|
|
563
|
+
// CJK unified ideographs, extension B onwards
|
|
564
|
+
];
|
|
565
|
+
var isWide = (code) => WIDE_RANGES.some(([low, high]) => code >= low && code <= high);
|
|
566
|
+
var graphemeWidth = (cluster) => {
|
|
567
|
+
if (ZERO_WIDTH.test(cluster)) return 0;
|
|
568
|
+
if (cluster.includes(EMOJI_PRESENTATION)) return 2;
|
|
569
|
+
return isWide(cluster.codePointAt(0) ?? 0) ? 2 : 1;
|
|
570
|
+
};
|
|
571
|
+
var displayWidth = (text) => {
|
|
572
|
+
if (!NON_ASCII.test(text)) return text.length;
|
|
573
|
+
let width = 0;
|
|
574
|
+
for (const { segment } of GRAPHEMES.segment(text)) width += graphemeWidth(segment);
|
|
575
|
+
return width;
|
|
576
|
+
};
|
|
577
|
+
var cutToWidth = (text, limit) => {
|
|
578
|
+
if (!NON_ASCII.test(text)) return [text.slice(0, limit), text.slice(limit)];
|
|
579
|
+
let width = 0;
|
|
580
|
+
for (const { segment, index } of GRAPHEMES.segment(text)) {
|
|
581
|
+
const next = width + graphemeWidth(segment);
|
|
582
|
+
if (next > limit) {
|
|
583
|
+
const at = index === 0 ? segment.length : index;
|
|
584
|
+
return [text.slice(0, at), text.slice(at)];
|
|
585
|
+
}
|
|
586
|
+
width = next;
|
|
587
|
+
}
|
|
588
|
+
return [text, ""];
|
|
589
|
+
};
|
|
590
|
+
var wrapText = (text, width) => {
|
|
591
|
+
const limit = Math.max(8, width);
|
|
592
|
+
const out = [];
|
|
593
|
+
for (const paragraph of plainText(text).replaceAll(" ", " ").split("\n")) {
|
|
594
|
+
const trimmed = paragraph.trimEnd();
|
|
595
|
+
if (trimmed === "") {
|
|
596
|
+
out.push("");
|
|
597
|
+
continue;
|
|
598
|
+
}
|
|
599
|
+
let line = "";
|
|
600
|
+
let lineWidth = 0;
|
|
601
|
+
for (const word of trimmed.split(/ +/)) {
|
|
602
|
+
const wordWidth = displayWidth(word);
|
|
603
|
+
if (line === "") {
|
|
604
|
+
line = word;
|
|
605
|
+
lineWidth = wordWidth;
|
|
606
|
+
} else if (lineWidth + 1 + wordWidth <= limit) {
|
|
607
|
+
line = `${line} ${word}`;
|
|
608
|
+
lineWidth += 1 + wordWidth;
|
|
609
|
+
} else {
|
|
610
|
+
out.push(line);
|
|
611
|
+
line = word;
|
|
612
|
+
lineWidth = wordWidth;
|
|
613
|
+
}
|
|
614
|
+
while (lineWidth > limit) {
|
|
615
|
+
const [head, rest] = cutToWidth(line, limit);
|
|
616
|
+
out.push(head);
|
|
617
|
+
line = rest;
|
|
618
|
+
lineWidth = displayWidth(rest);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
if (line !== "") out.push(line);
|
|
622
|
+
}
|
|
623
|
+
return out.length === 0 ? [""] : out;
|
|
624
|
+
};
|
|
625
|
+
var isBlank = (line) => line?.kind === "blank";
|
|
626
|
+
var previewLines = (transcript, options) => {
|
|
627
|
+
const width = Math.max(8, options.width);
|
|
628
|
+
const lines = [];
|
|
629
|
+
const push = (kind, text) => {
|
|
630
|
+
const wrapped = wrapText(text, width);
|
|
631
|
+
const budget = BLOCK_LINES[kind];
|
|
632
|
+
for (const [index, line] of wrapped.slice(0, budget).entries()) {
|
|
633
|
+
lines.push({ kind, text: line, lead: index === 0 });
|
|
634
|
+
}
|
|
635
|
+
if (wrapped.length > budget) {
|
|
636
|
+
lines.push({ kind: "meta", text: `\u2026 ${wrapped.length - budget} more lines`, lead: false });
|
|
637
|
+
}
|
|
638
|
+
};
|
|
639
|
+
const separate = () => {
|
|
640
|
+
if (lines.length > 0 && !isBlank(lines.at(-1))) {
|
|
641
|
+
lines.push({ kind: "blank", text: "", lead: false });
|
|
642
|
+
}
|
|
643
|
+
};
|
|
644
|
+
for (const event of transcript.events) {
|
|
645
|
+
const text = plainText(event.text ?? "").trim();
|
|
646
|
+
switch (event.kind) {
|
|
647
|
+
case "user":
|
|
648
|
+
if (text === "") break;
|
|
649
|
+
separate();
|
|
650
|
+
push("user", text);
|
|
651
|
+
break;
|
|
652
|
+
case "assistant":
|
|
653
|
+
if (text === "") break;
|
|
654
|
+
separate();
|
|
655
|
+
push("assistant", text);
|
|
656
|
+
break;
|
|
657
|
+
case "reasoning":
|
|
658
|
+
if (text === "") break;
|
|
659
|
+
push("reasoning", text);
|
|
660
|
+
break;
|
|
661
|
+
case "tool": {
|
|
662
|
+
const command = plainText(event.command ?? "").trim();
|
|
663
|
+
push("command", command === "" ? event.name ?? "tool" : command);
|
|
664
|
+
const output = plainText(event.output ?? "").trimEnd();
|
|
665
|
+
if (output !== "") push("output", output);
|
|
666
|
+
break;
|
|
667
|
+
}
|
|
668
|
+
default:
|
|
669
|
+
break;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
while (lines.length > 0 && isBlank(lines[0])) lines.shift();
|
|
673
|
+
while (lines.length > 0 && isBlank(lines.at(-1))) lines.pop();
|
|
674
|
+
const max = Math.max(1, options.maxLines);
|
|
675
|
+
if (lines.length <= max) return lines;
|
|
676
|
+
const kept = lines.slice(lines.length - (max - 1));
|
|
677
|
+
return [
|
|
678
|
+
{ kind: "meta", text: `\u2026 ${lines.length - kept.length} earlier lines`, lead: true },
|
|
679
|
+
...kept
|
|
680
|
+
];
|
|
681
|
+
};
|
|
682
|
+
var previewWindow = (lines, rows, offset) => {
|
|
683
|
+
const height = Math.max(1, rows);
|
|
684
|
+
const furthest = Math.max(0, lines.length - height);
|
|
685
|
+
const clamped = Math.max(0, Math.min(furthest, Math.trunc(offset)));
|
|
686
|
+
const end = lines.length - clamped;
|
|
687
|
+
const start = Math.max(0, end - height);
|
|
688
|
+
return {
|
|
689
|
+
lines: lines.slice(start, end),
|
|
690
|
+
offset: clamped,
|
|
691
|
+
earlier: start,
|
|
692
|
+
later: lines.length - end
|
|
693
|
+
};
|
|
694
|
+
};
|
|
695
|
+
var TRANSCRIPT_KEY = (sessionId) => [
|
|
696
|
+
"transcript",
|
|
697
|
+
sessionId
|
|
698
|
+
];
|
|
699
|
+
var fetchTranscript = (api, sessionId) => api("GET", `/sessions/${sessionId}/transcript`);
|
|
301
700
|
|
|
302
701
|
// src/review-workflow.ts
|
|
303
702
|
var reviewTargetForSession = (session, annotation, projectName) => annotation?.changeId === null || annotation?.changeId === void 0 ? null : { session, changeId: annotation.changeId, projectName };
|
|
@@ -508,22 +907,25 @@ var openUrl = (target) => {
|
|
|
508
907
|
};
|
|
509
908
|
|
|
510
909
|
// src/tui-theme.ts
|
|
511
|
-
var
|
|
512
|
-
var
|
|
513
|
-
var
|
|
514
|
-
var
|
|
515
|
-
var
|
|
516
|
-
var
|
|
517
|
-
var
|
|
518
|
-
var
|
|
519
|
-
var
|
|
520
|
-
var
|
|
521
|
-
var
|
|
522
|
-
var
|
|
523
|
-
var
|
|
524
|
-
var
|
|
525
|
-
var
|
|
526
|
-
var
|
|
910
|
+
var CANVAS = "#1f2430";
|
|
911
|
+
var PANEL = "#242936";
|
|
912
|
+
var SURFACE = "#282e3b";
|
|
913
|
+
var INK = "#e6e1cf";
|
|
914
|
+
var INK_2 = "#d1d4dc";
|
|
915
|
+
var MUTED = "#c2cad7";
|
|
916
|
+
var FAINT = "#b0bbcc";
|
|
917
|
+
var RULE = "#78859a";
|
|
918
|
+
var ACCENT = "#ffcc66";
|
|
919
|
+
var WASH = "#2d3445";
|
|
920
|
+
var GREEN = "#87d96c";
|
|
921
|
+
var RED = "#f27983";
|
|
922
|
+
var ERROR = "#ff6666";
|
|
923
|
+
var ADD_WASH = "#384c41";
|
|
924
|
+
var DELETE_WASH = "#4d3945";
|
|
925
|
+
var SYNTAX_KEYWORD = "#ffa659";
|
|
926
|
+
var SYNTAX_STRING = "#d5ff80";
|
|
927
|
+
var SYNTAX_NUMBER = "#dfbfff";
|
|
928
|
+
var SYNTAX_FUNCTION = "#ffcd66";
|
|
527
929
|
|
|
528
930
|
// src/review.tsx
|
|
529
931
|
import { Fragment, jsx, jsxs } from "@opentui/react/jsx-runtime";
|
|
@@ -592,7 +994,7 @@ var anchorOf = (comment) => comment.file === null ? "change" : `${comment.file}$
|
|
|
592
994
|
var passFact = (pass) => {
|
|
593
995
|
const label = pass.kind === "suggest" ? "suggestions" : pass.kind;
|
|
594
996
|
if (pass.status === "running") return { text: `${label} running`, color: INK_2 };
|
|
595
|
-
if (pass.status === "failed") return { text: `${label} failed`, color:
|
|
997
|
+
if (pass.status === "failed") return { text: `${label} failed`, color: ERROR };
|
|
596
998
|
return {
|
|
597
999
|
text: `${label} observed \xB7 ${pass.findings ?? 0} ${pass.kind === "tour" ? "stops" : "drafts"}`,
|
|
598
1000
|
color: MUTED
|
|
@@ -600,7 +1002,7 @@ var passFact = (pass) => {
|
|
|
600
1002
|
};
|
|
601
1003
|
var FileRow = ({ file, selected }) => /* @__PURE__ */ jsxs("box", { height: 2, flexShrink: 0, backgroundColor: selected ? WASH : "transparent", children: [
|
|
602
1004
|
/* @__PURE__ */ jsxs("text", { height: 1, bg: "transparent", children: [
|
|
603
|
-
/* @__PURE__ */ jsx("span", { fg: selected ?
|
|
1005
|
+
/* @__PURE__ */ jsx("span", { fg: selected ? ACCENT : FAINT, children: selected ? "\u258C " : " " }),
|
|
604
1006
|
/* @__PURE__ */ jsx("span", { fg: INK, children: file.path }),
|
|
605
1007
|
file.binary ? /* @__PURE__ */ jsx("span", { fg: MUTED, children: " \xB7 binary" }) : null,
|
|
606
1008
|
file.likelyGenerated ? /* @__PURE__ */ jsx("span", { fg: FAINT, children: " \xB7 inferred \xB7 likely generated" }) : null
|
|
@@ -631,7 +1033,7 @@ var CommentRow = ({
|
|
|
631
1033
|
selected
|
|
632
1034
|
}) => /* @__PURE__ */ jsxs("box", { height: 2, flexShrink: 0, backgroundColor: selected ? WASH : "transparent", children: [
|
|
633
1035
|
/* @__PURE__ */ jsxs("text", { height: 1, bg: "transparent", children: [
|
|
634
|
-
/* @__PURE__ */ jsx("span", { fg: selected ?
|
|
1036
|
+
/* @__PURE__ */ jsx("span", { fg: selected ? ACCENT : FAINT, children: selected ? "\u258C " : " " }),
|
|
635
1037
|
/* @__PURE__ */ jsx("span", { fg: stateColor(comment.state), children: comment.state }),
|
|
636
1038
|
/* @__PURE__ */ jsxs("span", { fg: FAINT, children: [
|
|
637
1039
|
" \xB7 ",
|
|
@@ -661,6 +1063,7 @@ var Description = ({
|
|
|
661
1063
|
border: true,
|
|
662
1064
|
borderStyle: "rounded",
|
|
663
1065
|
borderColor: RULE,
|
|
1066
|
+
titleColor: INK,
|
|
664
1067
|
title: " change description ",
|
|
665
1068
|
titleAlignment: "left",
|
|
666
1069
|
backgroundColor: "transparent",
|
|
@@ -726,6 +1129,7 @@ var CompactTourEvidence = ({
|
|
|
726
1129
|
border: true,
|
|
727
1130
|
borderStyle: "rounded",
|
|
728
1131
|
borderColor: RULE,
|
|
1132
|
+
titleColor: INK,
|
|
729
1133
|
title: ` tour stop ${index + 1}/${total} \xB7 ${stop.grounded ? "direct record" : "inferred reading"} `,
|
|
730
1134
|
titleAlignment: "left",
|
|
731
1135
|
backgroundColor: "transparent",
|
|
@@ -756,6 +1160,7 @@ var EvidenceCard = ({
|
|
|
756
1160
|
border: true,
|
|
757
1161
|
borderStyle: "rounded",
|
|
758
1162
|
borderColor: RULE,
|
|
1163
|
+
titleColor: INK,
|
|
759
1164
|
title: ` ${anchorOf(comment)} \xB7 ${comment.authorKind === "mend" ? "Mend" : "You"} `,
|
|
760
1165
|
titleAlignment: "left",
|
|
761
1166
|
backgroundColor: "transparent",
|
|
@@ -791,7 +1196,7 @@ var EditorPanel = ({
|
|
|
791
1196
|
{
|
|
792
1197
|
border: true,
|
|
793
1198
|
borderStyle: "rounded",
|
|
794
|
-
borderColor:
|
|
1199
|
+
borderColor: ACCENT,
|
|
795
1200
|
title,
|
|
796
1201
|
titleAlignment: "left",
|
|
797
1202
|
backgroundColor: "transparent",
|
|
@@ -972,7 +1377,7 @@ function ReviewScreen({
|
|
|
972
1377
|
(anchor) => anchor.newLine !== null && anchor.newLine >= Math.min(start, selectedLine) && anchor.newLine <= Math.max(start, selectedLine)
|
|
973
1378
|
);
|
|
974
1379
|
for (const anchor of highlighted) {
|
|
975
|
-
diffRef.current.setLineColor(anchor.unifiedRow, { gutter:
|
|
1380
|
+
diffRef.current.setLineColor(anchor.unifiedRow, { gutter: ACCENT, content: WASH });
|
|
976
1381
|
}
|
|
977
1382
|
return () => {
|
|
978
1383
|
if (diffRef.current === null) return;
|
|
@@ -1307,7 +1712,7 @@ function ReviewScreen({
|
|
|
1307
1712
|
if (key.name === "z") return setShowWhitespace((current) => !current);
|
|
1308
1713
|
});
|
|
1309
1714
|
if (data === void 0) {
|
|
1310
|
-
return /* @__PURE__ */ jsx("box", { flexGrow: 1, backgroundColor:
|
|
1715
|
+
return /* @__PURE__ */ jsx("box", { flexGrow: 1, backgroundColor: CANVAS, alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ jsx("text", { bg: "transparent", children: /* @__PURE__ */ jsx("span", { fg: failureReason === null ? INK_2 : MUTED, children: failureReason === null ? "Loading the live change\u2026" : `${errorMessage(failureReason)} \xB7 retrying` }) }) });
|
|
1311
1716
|
}
|
|
1312
1717
|
const additions = data.wire.files.reduce((sum, file) => sum + file.additions, 0);
|
|
1313
1718
|
const deletions = data.wire.files.reduce((sum, file) => sum + file.deletions, 0);
|
|
@@ -1322,7 +1727,8 @@ function ReviewScreen({
|
|
|
1322
1727
|
{
|
|
1323
1728
|
border: true,
|
|
1324
1729
|
borderStyle: "rounded",
|
|
1325
|
-
borderColor: focus === "files" ?
|
|
1730
|
+
borderColor: focus === "files" ? ACCENT : RULE,
|
|
1731
|
+
titleColor: focus === "files" ? ACCENT : INK,
|
|
1326
1732
|
title: ` files \xB7 ${files.length} `,
|
|
1327
1733
|
titleAlignment: "left",
|
|
1328
1734
|
backgroundColor: "transparent",
|
|
@@ -1354,7 +1760,8 @@ function ReviewScreen({
|
|
|
1354
1760
|
{
|
|
1355
1761
|
border: true,
|
|
1356
1762
|
borderStyle: "rounded",
|
|
1357
|
-
borderColor: focus === "comments" ?
|
|
1763
|
+
borderColor: focus === "comments" ? ACCENT : RULE,
|
|
1764
|
+
titleColor: focus === "comments" ? ACCENT : INK,
|
|
1358
1765
|
title: ` comments \xB7 ${openCount} open${draftCount > 0 ? ` \xB7 ${draftCount} draft` : ""} `,
|
|
1359
1766
|
titleAlignment: "left",
|
|
1360
1767
|
backgroundColor: "transparent",
|
|
@@ -1379,160 +1786,166 @@ function ReviewScreen({
|
|
|
1379
1786
|
}
|
|
1380
1787
|
)
|
|
1381
1788
|
] }) : null;
|
|
1382
|
-
return
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
/* @__PURE__ */
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
"
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
" ",
|
|
1403
|
-
data.wire.change.branch.replace(/^mend\/session\//, "session ")
|
|
1404
|
-
] }),
|
|
1405
|
-
/* @__PURE__ */ jsxs("span", { fg: FAINT, children: [
|
|
1406
|
-
" \xB7 worktree vs ",
|
|
1407
|
-
data.wire.change.baseSha.slice(0, 12),
|
|
1408
|
-
" \xB7 "
|
|
1409
|
-
] }),
|
|
1410
|
-
/* @__PURE__ */ jsxs("span", { fg: MUTED, children: [
|
|
1411
|
-
files.length,
|
|
1412
|
-
" files"
|
|
1413
|
-
] }),
|
|
1414
|
-
/* @__PURE__ */ jsxs("span", { fg: GREEN, children: [
|
|
1415
|
-
" +",
|
|
1416
|
-
additions
|
|
1417
|
-
] }),
|
|
1418
|
-
/* @__PURE__ */ jsxs("span", { fg: RED, children: [
|
|
1419
|
-
" \u2212",
|
|
1420
|
-
deletions
|
|
1789
|
+
return (
|
|
1790
|
+
// The review screen replaces the dashboard root rather than nesting inside
|
|
1791
|
+
// it, so it paints its own canvas; without this the terminal's own
|
|
1792
|
+
// background shows through and the screen stops matching the dashboard.
|
|
1793
|
+
/* @__PURE__ */ jsxs("box", { flexGrow: 1, flexDirection: "column", backgroundColor: CANVAS, children: [
|
|
1794
|
+
/* @__PURE__ */ jsxs("box", { height: 2, flexShrink: 0, flexDirection: "column", backgroundColor: "transparent", children: [
|
|
1795
|
+
/* @__PURE__ */ jsxs("text", { height: 1, bg: "transparent", children: [
|
|
1796
|
+
/* @__PURE__ */ jsx("span", { fg: INK, children: " mend" }),
|
|
1797
|
+
/* @__PURE__ */ jsx("span", { fg: FAINT, children: " / " }),
|
|
1798
|
+
/* @__PURE__ */ jsx("span", { fg: MUTED, children: projectName }),
|
|
1799
|
+
/* @__PURE__ */ jsx("span", { fg: FAINT, children: " / " }),
|
|
1800
|
+
/* @__PURE__ */ jsx("span", { fg: INK, children: "review" }),
|
|
1801
|
+
/* @__PURE__ */ jsxs("span", { fg: FAINT, children: [
|
|
1802
|
+
" ",
|
|
1803
|
+
"\xB7 ",
|
|
1804
|
+
session.harness,
|
|
1805
|
+
" ",
|
|
1806
|
+
session.id.slice(0, 8)
|
|
1807
|
+
] }),
|
|
1808
|
+
isFetching ? /* @__PURE__ */ jsx("span", { fg: FAINT, children: " \xB7 syncing" }) : null,
|
|
1809
|
+
/* @__PURE__ */ jsx("span", { fg: checkpointState === "failed" ? MUTED : FAINT, children: checkpointState === "recording" ? " \xB7 checkpointing" : checkpointState === "failed" ? " \xB7 checkpoint incomplete" : " \xB7 checkpoint recorded" })
|
|
1421
1810
|
] }),
|
|
1422
|
-
/* @__PURE__ */ jsxs("
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1811
|
+
/* @__PURE__ */ jsxs("text", { height: 1, bg: "transparent", children: [
|
|
1812
|
+
/* @__PURE__ */ jsxs("span", { fg: FAINT, children: [
|
|
1813
|
+
" ",
|
|
1814
|
+
data.wire.change.branch.replace(/^mend\/session\//, "session ")
|
|
1815
|
+
] }),
|
|
1816
|
+
/* @__PURE__ */ jsxs("span", { fg: FAINT, children: [
|
|
1817
|
+
" \xB7 worktree vs ",
|
|
1818
|
+
data.wire.change.baseSha.slice(0, 12),
|
|
1819
|
+
" \xB7 "
|
|
1820
|
+
] }),
|
|
1821
|
+
/* @__PURE__ */ jsxs("span", { fg: MUTED, children: [
|
|
1822
|
+
files.length,
|
|
1823
|
+
" files"
|
|
1824
|
+
] }),
|
|
1825
|
+
/* @__PURE__ */ jsxs("span", { fg: GREEN, children: [
|
|
1826
|
+
" +",
|
|
1827
|
+
additions
|
|
1828
|
+
] }),
|
|
1829
|
+
/* @__PURE__ */ jsxs("span", { fg: RED, children: [
|
|
1830
|
+
" \u2212",
|
|
1831
|
+
deletions
|
|
1832
|
+
] }),
|
|
1833
|
+
/* @__PURE__ */ jsxs("span", { fg: MUTED, children: [
|
|
1834
|
+
" \xB7 ",
|
|
1835
|
+
openCount,
|
|
1836
|
+
" open"
|
|
1837
|
+
] })
|
|
1426
1838
|
] })
|
|
1427
|
-
] })
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
Description,
|
|
1431
|
-
{
|
|
1432
|
-
data,
|
|
1433
|
-
tourIndex: activeTourIndex,
|
|
1434
|
-
stale: tourStale,
|
|
1435
|
-
width,
|
|
1436
|
-
height: descriptionHeight
|
|
1437
|
-
}
|
|
1438
|
-
),
|
|
1439
|
-
/* @__PURE__ */ jsxs("box", { flexGrow: 1, minHeight: 0, flexDirection: "row", backgroundColor: "transparent", children: [
|
|
1440
|
-
sidebar,
|
|
1441
|
-
/* @__PURE__ */ jsxs(
|
|
1442
|
-
"box",
|
|
1839
|
+
] }),
|
|
1840
|
+
/* @__PURE__ */ jsx(
|
|
1841
|
+
Description,
|
|
1443
1842
|
{
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
lineNumberFg: FAINT,
|
|
1488
|
-
lineNumberBg: "transparent",
|
|
1489
|
-
contextBg: "transparent",
|
|
1490
|
-
contextContentBg: "transparent",
|
|
1491
|
-
addedBg: ADD_WASH,
|
|
1492
|
-
addedContentBg: ADD_WASH,
|
|
1493
|
-
removedBg: DELETE_WASH,
|
|
1494
|
-
removedContentBg: DELETE_WASH,
|
|
1495
|
-
addedSignColor: GREEN,
|
|
1496
|
-
removedSignColor: RED,
|
|
1497
|
-
addedLineNumberBg: "transparent",
|
|
1498
|
-
removedLineNumberBg: "transparent",
|
|
1499
|
-
selectionBg: WASH,
|
|
1500
|
-
selectionFg: INK,
|
|
1501
|
-
width: "100%",
|
|
1502
|
-
height: displayRows,
|
|
1503
|
-
flexShrink: 0
|
|
1843
|
+
data,
|
|
1844
|
+
tourIndex: activeTourIndex,
|
|
1845
|
+
stale: tourStale,
|
|
1846
|
+
width,
|
|
1847
|
+
height: descriptionHeight
|
|
1848
|
+
}
|
|
1849
|
+
),
|
|
1850
|
+
/* @__PURE__ */ jsxs("box", { flexGrow: 1, minHeight: 0, flexDirection: "row", backgroundColor: "transparent", children: [
|
|
1851
|
+
sidebar,
|
|
1852
|
+
/* @__PURE__ */ jsxs(
|
|
1853
|
+
"box",
|
|
1854
|
+
{
|
|
1855
|
+
flexGrow: 1,
|
|
1856
|
+
minWidth: 0,
|
|
1857
|
+
minHeight: 0,
|
|
1858
|
+
flexDirection: "column",
|
|
1859
|
+
backgroundColor: "transparent",
|
|
1860
|
+
children: [
|
|
1861
|
+
/* @__PURE__ */ jsx(
|
|
1862
|
+
"box",
|
|
1863
|
+
{
|
|
1864
|
+
border: true,
|
|
1865
|
+
borderStyle: "rounded",
|
|
1866
|
+
borderColor: focus === "diff" ? ACCENT : RULE,
|
|
1867
|
+
titleColor: focus === "diff" ? ACCENT : INK,
|
|
1868
|
+
title: selectedFile === null ? " diff " : ` ${selectedFile.path} \xB7 ${view}${selectedFile.likelyGenerated ? " \xB7 inferred likely generated" : ""}${showWhitespace ? " \xB7 whitespace" : ""}${wrap ? " \xB7 wrapped" : ""} `,
|
|
1869
|
+
titleAlignment: "left",
|
|
1870
|
+
backgroundColor: "transparent",
|
|
1871
|
+
flexGrow: 1,
|
|
1872
|
+
minHeight: 0,
|
|
1873
|
+
children: selectedFile === null || selectedFile.patch === "" || selectedFile.binary ? /* @__PURE__ */ jsx("box", { flexGrow: 1, alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ jsx("text", { fg: FAINT, bg: "transparent", children: files.length === 0 ? "The worktree matches its base \u2014 nothing to review yet." : selectedFile?.binary === true ? "Binary change \u2014 text diff unavailable; statistics remain reviewable." : "This file has no renderable text patch." }) }) : /* @__PURE__ */ jsx(
|
|
1874
|
+
"scrollbox",
|
|
1875
|
+
{
|
|
1876
|
+
ref: diffScrollRef,
|
|
1877
|
+
focused: focus === "diff",
|
|
1878
|
+
flexGrow: 1,
|
|
1879
|
+
minHeight: 0,
|
|
1880
|
+
scrollX: true,
|
|
1881
|
+
style: {
|
|
1882
|
+
rootOptions: { backgroundColor: "transparent", border: false },
|
|
1883
|
+
wrapperOptions: { backgroundColor: "transparent" },
|
|
1884
|
+
viewportOptions: { backgroundColor: "transparent" },
|
|
1885
|
+
contentOptions: { backgroundColor: "transparent" }
|
|
1504
1886
|
},
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1887
|
+
children: /* @__PURE__ */ jsx(
|
|
1888
|
+
"diff",
|
|
1889
|
+
{
|
|
1890
|
+
ref: diffRef,
|
|
1891
|
+
diff: displayPatch,
|
|
1892
|
+
view,
|
|
1893
|
+
syncScroll: true,
|
|
1894
|
+
...selectedFile.filetype === void 0 ? {} : { filetype: selectedFile.filetype },
|
|
1895
|
+
syntaxStyle,
|
|
1896
|
+
wrapMode: wrap ? "word" : "none",
|
|
1897
|
+
showLineNumbers: true,
|
|
1898
|
+
fg: INK,
|
|
1899
|
+
lineNumberFg: FAINT,
|
|
1900
|
+
lineNumberBg: "transparent",
|
|
1901
|
+
contextBg: "transparent",
|
|
1902
|
+
contextContentBg: "transparent",
|
|
1903
|
+
addedBg: ADD_WASH,
|
|
1904
|
+
addedContentBg: ADD_WASH,
|
|
1905
|
+
removedBg: DELETE_WASH,
|
|
1906
|
+
removedContentBg: DELETE_WASH,
|
|
1907
|
+
addedSignColor: GREEN,
|
|
1908
|
+
removedSignColor: RED,
|
|
1909
|
+
addedLineNumberBg: "transparent",
|
|
1910
|
+
removedLineNumberBg: "transparent",
|
|
1911
|
+
selectionBg: WASH,
|
|
1912
|
+
selectionFg: INK,
|
|
1913
|
+
width: "100%",
|
|
1914
|
+
height: displayRows,
|
|
1915
|
+
flexShrink: 0
|
|
1916
|
+
},
|
|
1917
|
+
`${selectedFile.path}-${view}-${wrap}-${showWhitespace}`
|
|
1918
|
+
)
|
|
1919
|
+
}
|
|
1920
|
+
)
|
|
1921
|
+
}
|
|
1922
|
+
),
|
|
1923
|
+
focus === "comments" && detailComment !== null ? /* @__PURE__ */ jsx(EvidenceCard, { comment: detailComment, width: wide ? width - 38 : width }) : height < 30 && activeTourStop !== null ? /* @__PURE__ */ jsx(
|
|
1924
|
+
CompactTourEvidence,
|
|
1925
|
+
{
|
|
1926
|
+
stop: activeTourStop,
|
|
1927
|
+
index: activeTourIndex,
|
|
1928
|
+
total: tourStops.length,
|
|
1929
|
+
width
|
|
1930
|
+
}
|
|
1931
|
+
) : detailComment === null ? null : /* @__PURE__ */ jsx(EvidenceCard, { comment: detailComment, width: wide ? width - 38 : width })
|
|
1932
|
+
]
|
|
1933
|
+
}
|
|
1934
|
+
)
|
|
1935
|
+
] }),
|
|
1936
|
+
editor === null ? null : /* @__PURE__ */ jsx(
|
|
1937
|
+
EditorPanel,
|
|
1938
|
+
{
|
|
1939
|
+
editor,
|
|
1940
|
+
refValue: editorRef,
|
|
1941
|
+
busy,
|
|
1942
|
+
onSubmit: () => void submitEditor()
|
|
1521
1943
|
}
|
|
1522
|
-
)
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
editor,
|
|
1528
|
-
refValue: editorRef,
|
|
1529
|
-
busy,
|
|
1530
|
-
onSubmit: () => void submitEditor()
|
|
1531
|
-
}
|
|
1532
|
-
),
|
|
1533
|
-
/* @__PURE__ */ jsx("text", { height: 1, fg: status === "" ? FAINT : INK_2, bg: "transparent", children: status === "" ? ` m read \xB7 g suggest \xB7 t tour \xB7 ,/. tour stops \xB7 s draft review${data.followUp?.status === "pending" ? " \xB7 y deliver & relaunch" : ""} \xB7 o web \xB7 r refresh` : ` ${status}` }),
|
|
1534
|
-
/* @__PURE__ */ jsx("text", { height: 1, fg: FAINT, bg: "transparent", children: footer })
|
|
1535
|
-
] });
|
|
1944
|
+
),
|
|
1945
|
+
/* @__PURE__ */ jsx("text", { height: 1, fg: status === "" ? FAINT : INK_2, bg: "transparent", children: status === "" ? ` m read \xB7 g suggest \xB7 t tour \xB7 ,/. tour stops \xB7 s draft review${data.followUp?.status === "pending" ? " \xB7 y deliver & relaunch" : ""} \xB7 o web \xB7 r refresh` : ` ${status}` }),
|
|
1946
|
+
/* @__PURE__ */ jsx("text", { height: 1, fg: FAINT, bg: "transparent", children: footer })
|
|
1947
|
+
] })
|
|
1948
|
+
);
|
|
1536
1949
|
}
|
|
1537
1950
|
|
|
1538
1951
|
// src/workbench-events.ts
|
|
@@ -1567,10 +1980,13 @@ var eventFamilies = (payload) => {
|
|
|
1567
1980
|
case "worktree":
|
|
1568
1981
|
case "session-process":
|
|
1569
1982
|
return ["workbench"];
|
|
1570
|
-
//
|
|
1571
|
-
//
|
|
1983
|
+
// Settlement refreshes the final transcript immediately. Conversation
|
|
1984
|
+
// items can arrive many times per second; the live preview's bounded
|
|
1985
|
+
// polling reads those instead of repeatedly fetching the whole record.
|
|
1572
1986
|
case "session":
|
|
1987
|
+
return ["workbench", "review", "transcript"];
|
|
1573
1988
|
case "agent-conversation":
|
|
1989
|
+
return ["workbench", "review"];
|
|
1574
1990
|
// A comment moves the review AND the dashboard's open-comment counts.
|
|
1575
1991
|
case "session-change":
|
|
1576
1992
|
case "review-comment":
|
|
@@ -1588,9 +2004,10 @@ var STATUS_COLOR = {
|
|
|
1588
2004
|
waiting: INK_2,
|
|
1589
2005
|
idle: INK_2,
|
|
1590
2006
|
completed: FAINT,
|
|
1591
|
-
failed:
|
|
2007
|
+
failed: ERROR,
|
|
1592
2008
|
stopped: FAINT
|
|
1593
2009
|
};
|
|
2010
|
+
var AGENT_LIVE_STATUSES = /* @__PURE__ */ new Set(["starting", "running", "waiting"]);
|
|
1594
2011
|
var timeAgo = (iso) => {
|
|
1595
2012
|
const ms = Date.now() - new Date(iso).getTime();
|
|
1596
2013
|
if (!Number.isFinite(ms) || ms < 0) return "";
|
|
@@ -1601,8 +2018,17 @@ var timeAgo = (iso) => {
|
|
|
1601
2018
|
if (hours < 24) return `${hours}h ago`;
|
|
1602
2019
|
return `${Math.floor(hours / 24)}d ago`;
|
|
1603
2020
|
};
|
|
1604
|
-
var
|
|
1605
|
-
var
|
|
2021
|
+
var shortAge = (iso) => timeAgo(iso).replace(" ago", "");
|
|
2022
|
+
var fit = (text, width) => text.length <= width ? text : `${text.slice(0, Math.max(0, width - 1))}\u2026`;
|
|
2023
|
+
var COLUMN_TITLE = {
|
|
2024
|
+
projects: "projects",
|
|
2025
|
+
worktrees: "worktrees",
|
|
2026
|
+
sessions: "sessions",
|
|
2027
|
+
detail: "session"
|
|
2028
|
+
};
|
|
2029
|
+
var WORKTREE_ROW_HEIGHT = 2;
|
|
2030
|
+
var SESSION_ROW_HEIGHT = 2;
|
|
2031
|
+
var Gutter = ({ selected }) => /* @__PURE__ */ jsx2("span", { fg: selected ? ACCENT : FAINT, children: selected ? "\u258C " : " " });
|
|
1606
2032
|
var Pane = ({
|
|
1607
2033
|
title,
|
|
1608
2034
|
focused,
|
|
@@ -1615,10 +2041,12 @@ var Pane = ({
|
|
|
1615
2041
|
{
|
|
1616
2042
|
border: true,
|
|
1617
2043
|
borderStyle: "rounded",
|
|
1618
|
-
borderColor: focused ?
|
|
2044
|
+
borderColor: focused ? ACCENT : RULE,
|
|
2045
|
+
titleColor: focused ? ACCENT : INK,
|
|
1619
2046
|
title: ` ${title} `,
|
|
1620
2047
|
titleAlignment: "left",
|
|
1621
|
-
backgroundColor:
|
|
2048
|
+
backgroundColor: PANEL,
|
|
2049
|
+
overflow: "hidden",
|
|
1622
2050
|
...width === void 0 ? {} : { width, flexShrink: 0, minHeight: 0 },
|
|
1623
2051
|
...height === void 0 ? {} : { height, flexShrink: 0 },
|
|
1624
2052
|
...grow === true ? { flexGrow: 1, flexShrink: 1, minHeight: 0, minWidth: 0 } : {},
|
|
@@ -1626,135 +2054,182 @@ var Pane = ({
|
|
|
1626
2054
|
children
|
|
1627
2055
|
}
|
|
1628
2056
|
);
|
|
2057
|
+
var SummaryRow = ({
|
|
2058
|
+
name,
|
|
2059
|
+
fact,
|
|
2060
|
+
width,
|
|
2061
|
+
empty
|
|
2062
|
+
}) => {
|
|
2063
|
+
const factWidth = fact === "" ? 0 : Math.min(fact.length, Math.max(0, width - 8));
|
|
2064
|
+
const nameWidth = Math.max(4, width - factWidth - 3);
|
|
2065
|
+
return /* @__PURE__ */ jsxs2("text", { height: 1, bg: "transparent", children: [
|
|
2066
|
+
/* @__PURE__ */ jsx2("span", { fg: FAINT, children: " " }),
|
|
2067
|
+
/* @__PURE__ */ jsx2("span", { fg: empty ? FAINT : INK_2, children: fit(name, nameWidth - 1).padEnd(nameWidth) }),
|
|
2068
|
+
factWidth === 0 ? null : /* @__PURE__ */ jsx2("span", { fg: FAINT, children: fit(fact, factWidth) })
|
|
2069
|
+
] });
|
|
2070
|
+
};
|
|
1629
2071
|
var paneScrollStyle = {
|
|
1630
2072
|
rootOptions: { backgroundColor: "transparent", border: false },
|
|
1631
2073
|
wrapperOptions: { backgroundColor: "transparent" },
|
|
1632
2074
|
viewportOptions: { backgroundColor: "transparent" },
|
|
1633
2075
|
contentOptions: { backgroundColor: "transparent" }
|
|
1634
2076
|
};
|
|
2077
|
+
var EmptyNote = ({ text }) => /* @__PURE__ */ jsx2("text", { height: 1, fg: FAINT, bg: "transparent", children: ` ${text}` });
|
|
1635
2078
|
var ProjectRow = ({
|
|
1636
2079
|
item,
|
|
1637
2080
|
selected,
|
|
1638
|
-
|
|
1639
|
-
}) => /* @__PURE__ */ jsx2("box", { height: 1, flexShrink: 0, backgroundColor: selected ? WASH : "transparent", children: /* @__PURE__ */ jsxs2("text", { height: 1, bg: "transparent", children: [
|
|
1640
|
-
/* @__PURE__ */ jsx2(Gutter, { selected }),
|
|
1641
|
-
/* @__PURE__ */ jsx2("span", { fg: INK, children: item.project.name.padEnd(nameWidth) }),
|
|
1642
|
-
/* @__PURE__ */ jsx2("span", { fg: item.total === 0 ? FAINT : MUTED, children: ` ${item.total}` }),
|
|
1643
|
-
item.live > 0 ? /* @__PURE__ */ jsx2("span", { fg: MUTED, children: ` \xB7 ${item.live} live` }) : null,
|
|
1644
|
-
item.open > 0 ? /* @__PURE__ */ jsx2("span", { fg: MUTED, children: ` \xB7 ${item.open}` }) : null
|
|
1645
|
-
] }) });
|
|
1646
|
-
var ProcessLines = ({
|
|
1647
|
-
processes,
|
|
1648
|
-
services,
|
|
1649
|
-
indent
|
|
1650
|
-
}) => /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
1651
|
-
processes.map((process2) => /* @__PURE__ */ jsx2("box", { height: 1, flexShrink: 0, backgroundColor: "transparent", children: /* @__PURE__ */ jsxs2("text", { height: 1, bg: "transparent", children: [
|
|
1652
|
-
/* @__PURE__ */ jsx2("span", { fg: FAINT, children: indent }),
|
|
1653
|
-
/* @__PURE__ */ jsx2("span", { fg: INK_2, children: process2.kind === "shell" ? process2.label ?? "shell" : process2.harness ?? process2.kind }),
|
|
1654
|
-
/* @__PURE__ */ jsxs2("span", { fg: FAINT, children: [
|
|
1655
|
-
process2.kind === "agent-protocol" ? " \xB7 protocol" : process2.kind === "agent-external" ? " \xB7 external" : "",
|
|
1656
|
-
` ${process2.status}`
|
|
1657
|
-
] })
|
|
1658
|
-
] }) }, process2.id)),
|
|
1659
|
-
services.map((service) => /* @__PURE__ */ jsx2("box", { height: 1, flexShrink: 0, backgroundColor: "transparent", children: /* @__PURE__ */ jsxs2("text", { height: 1, bg: "transparent", children: [
|
|
1660
|
-
/* @__PURE__ */ jsx2("span", { fg: FAINT, children: indent }),
|
|
1661
|
-
/* @__PURE__ */ jsx2("span", { fg: service.status === "reachable" ? INK_2 : MUTED, children: `${service.label ?? service.id.slice(0, 6)} :${service.workspacePort ?? "?"}${service.protocol === "udp" ? "u" : ""}\u2192${service.hostPort ?? "?"}` }),
|
|
1662
|
-
/* @__PURE__ */ jsx2("span", { fg: FAINT, children: ` ${service.status}` })
|
|
1663
|
-
] }) }, service.id))
|
|
1664
|
-
] });
|
|
1665
|
-
var WorktreeHeaderRow = ({
|
|
1666
|
-
group,
|
|
1667
|
-
selected
|
|
2081
|
+
width
|
|
1668
2082
|
}) => {
|
|
1669
|
-
const
|
|
1670
|
-
const color = group.live > 0 ? STATUS_COLOR[folded] ?? MUTED : FAINT;
|
|
1671
|
-
const age = timeAgo(group.sessions.at(-1)?.session.createdAt ?? group.createdAt).replace(
|
|
1672
|
-
" ago",
|
|
1673
|
-
""
|
|
1674
|
-
);
|
|
1675
|
-
const open = group.annotation?.openComments ?? 0;
|
|
2083
|
+
const nameWidth = Math.max(6, width - 9);
|
|
1676
2084
|
return /* @__PURE__ */ jsx2("box", { height: 1, flexShrink: 0, backgroundColor: selected ? WASH : "transparent", children: /* @__PURE__ */ jsxs2("text", { height: 1, bg: "transparent", children: [
|
|
1677
2085
|
/* @__PURE__ */ jsx2(Gutter, { selected }),
|
|
1678
|
-
/* @__PURE__ */ jsx2("span", { fg: INK, children:
|
|
1679
|
-
/* @__PURE__ */ jsx2("span", { fg:
|
|
1680
|
-
/* @__PURE__ */ jsx2("span", { fg: FAINT, children: age.padEnd(9) }),
|
|
1681
|
-
/* @__PURE__ */ jsx2("span", { fg: MUTED, children: `${group.sessions.length} session${group.sessions.length === 1 ? "" : "s"}` }),
|
|
1682
|
-
open > 0 ? /* @__PURE__ */ jsx2("span", { fg: MUTED, children: ` \xB7 ${open} open` }) : null
|
|
2086
|
+
/* @__PURE__ */ jsx2("span", { fg: INK, children: fit(item.project.name, nameWidth).padEnd(nameWidth + 1) }),
|
|
2087
|
+
/* @__PURE__ */ jsx2("span", { fg: item.live > 0 ? MUTED : FAINT, children: item.live > 0 ? `${item.live}/${item.total}` : String(item.total) })
|
|
1683
2088
|
] }) });
|
|
1684
2089
|
};
|
|
1685
|
-
var
|
|
2090
|
+
var WorktreeRow = ({
|
|
2091
|
+
group,
|
|
2092
|
+
selected,
|
|
2093
|
+
width
|
|
2094
|
+
}) => {
|
|
2095
|
+
const folded = foldGroupStatus(group);
|
|
2096
|
+
const live = group.live > 0;
|
|
2097
|
+
const status = live ? folded : "settled";
|
|
2098
|
+
const color = live ? STATUS_COLOR[folded] ?? MUTED : FAINT;
|
|
2099
|
+
const open = group.annotation?.openComments ?? 0;
|
|
2100
|
+
const sessions = group.sessions.length;
|
|
2101
|
+
const nameWidth = Math.max(6, width - 5 - status.length);
|
|
2102
|
+
const factWidth = Math.max(8, width - 3);
|
|
2103
|
+
const facts = fitHints(
|
|
2104
|
+
[
|
|
2105
|
+
fit(groupBaseLabel(group), factWidth),
|
|
2106
|
+
shortAge(groupActivityAt(group)),
|
|
2107
|
+
...open > 0 ? [`${open} open`] : [],
|
|
2108
|
+
...sessions === 0 ? ["empty"] : sessions === 1 ? [] : [`${sessions} sessions`]
|
|
2109
|
+
],
|
|
2110
|
+
factWidth
|
|
2111
|
+
);
|
|
2112
|
+
return /* @__PURE__ */ jsxs2("box", { flexShrink: 0, flexDirection: "column", backgroundColor: selected ? WASH : "transparent", children: [
|
|
2113
|
+
/* @__PURE__ */ jsx2("box", { height: 1, flexShrink: 0, backgroundColor: "transparent", children: /* @__PURE__ */ jsxs2("text", { height: 1, bg: "transparent", children: [
|
|
2114
|
+
/* @__PURE__ */ jsx2(Gutter, { selected }),
|
|
2115
|
+
/* @__PURE__ */ jsx2("span", { fg: INK, children: fit(group.name, nameWidth - 1).padEnd(nameWidth) }),
|
|
2116
|
+
/* @__PURE__ */ jsx2("span", { fg: color, children: status })
|
|
2117
|
+
] }) }),
|
|
2118
|
+
/* @__PURE__ */ jsx2("box", { height: 1, flexShrink: 0, backgroundColor: "transparent", children: /* @__PURE__ */ jsx2("text", { height: 1, bg: "transparent", fg: FAINT, children: ` ${facts}` }) })
|
|
2119
|
+
] });
|
|
2120
|
+
};
|
|
2121
|
+
var SessionRow = ({
|
|
1686
2122
|
item,
|
|
1687
|
-
selected
|
|
2123
|
+
selected,
|
|
2124
|
+
width
|
|
1688
2125
|
}) => {
|
|
1689
|
-
const { session,
|
|
1690
|
-
const
|
|
1691
|
-
const
|
|
1692
|
-
const
|
|
1693
|
-
|
|
1694
|
-
|
|
2126
|
+
const { session, processes, services } = item;
|
|
2127
|
+
const status = session.status;
|
|
2128
|
+
const color = STATUS_COLOR[status] ?? MUTED;
|
|
2129
|
+
const agents = processes.filter((process2) => process2.kind !== "shell").length;
|
|
2130
|
+
const shells = processes.filter((process2) => process2.kind === "shell").length;
|
|
2131
|
+
const nameWidth = Math.max(6, width - 5 - status.length);
|
|
2132
|
+
const factWidth = Math.max(8, width - 3);
|
|
2133
|
+
const facts = fitHints(
|
|
2134
|
+
[
|
|
2135
|
+
session.harness,
|
|
2136
|
+
shortAge(session.createdAt),
|
|
2137
|
+
...shells > 0 ? [`${shells} shell`] : [],
|
|
2138
|
+
...agents > 0 ? [`${agents} agent`] : [],
|
|
2139
|
+
...services.length > 0 ? [`${services.length} service`] : []
|
|
2140
|
+
],
|
|
2141
|
+
factWidth
|
|
2142
|
+
);
|
|
2143
|
+
return /* @__PURE__ */ jsxs2("box", { flexShrink: 0, flexDirection: "column", backgroundColor: selected ? WASH : "transparent", children: [
|
|
2144
|
+
/* @__PURE__ */ jsx2("box", { height: 1, flexShrink: 0, backgroundColor: "transparent", children: /* @__PURE__ */ jsxs2("text", { height: 1, bg: "transparent", children: [
|
|
1695
2145
|
/* @__PURE__ */ jsx2(Gutter, { selected }),
|
|
1696
|
-
/* @__PURE__ */ jsx2("span", { fg:
|
|
1697
|
-
/* @__PURE__ */ jsx2("span", { fg:
|
|
1698
|
-
/* @__PURE__ */ jsx2("span", { fg: color, children: session.status.padEnd(10) }),
|
|
1699
|
-
/* @__PURE__ */ jsx2("span", { fg: FAINT, children: age.padEnd(9) }),
|
|
1700
|
-
/* @__PURE__ */ jsx2("span", { fg: MUTED, children: session.harness.padEnd(10) })
|
|
2146
|
+
/* @__PURE__ */ jsx2("span", { fg: INK, children: fit(sessionDisplayName(session), nameWidth - 1).padEnd(nameWidth) }),
|
|
2147
|
+
/* @__PURE__ */ jsx2("span", { fg: color, children: status })
|
|
1701
2148
|
] }) }),
|
|
1702
|
-
/* @__PURE__ */ jsx2(
|
|
2149
|
+
/* @__PURE__ */ jsx2("box", { height: 1, flexShrink: 0, backgroundColor: "transparent", children: /* @__PURE__ */ jsx2("text", { height: 1, bg: "transparent", fg: FAINT, children: ` ${facts}` }) })
|
|
1703
2150
|
] });
|
|
1704
2151
|
};
|
|
1705
2152
|
var HarnessRow = ({
|
|
1706
2153
|
item,
|
|
1707
|
-
selected
|
|
1708
|
-
|
|
2154
|
+
selected,
|
|
2155
|
+
background
|
|
2156
|
+
}) => /* @__PURE__ */ jsx2("box", { height: 1, flexShrink: 0, backgroundColor: selected ? WASH : background, children: /* @__PURE__ */ jsxs2("text", { height: 1, bg: selected ? WASH : background, children: [
|
|
1709
2157
|
/* @__PURE__ */ jsx2(Gutter, { selected }),
|
|
1710
2158
|
/* @__PURE__ */ jsx2("span", { fg: INK, children: item.label.padEnd(12) }),
|
|
1711
2159
|
/* @__PURE__ */ jsx2("span", { fg: FAINT, children: item.hint })
|
|
1712
2160
|
] }) });
|
|
1713
|
-
var
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
2161
|
+
var PREVIEW_GLYPH = {
|
|
2162
|
+
user: "\u203A ",
|
|
2163
|
+
assistant: " ",
|
|
2164
|
+
reasoning: "\xB7 ",
|
|
2165
|
+
command: "$ ",
|
|
2166
|
+
output: " ",
|
|
2167
|
+
meta: " ",
|
|
2168
|
+
blank: " "
|
|
2169
|
+
};
|
|
2170
|
+
var PREVIEW_COLOR = {
|
|
2171
|
+
user: INK,
|
|
2172
|
+
assistant: INK_2,
|
|
2173
|
+
reasoning: FAINT,
|
|
2174
|
+
command: MUTED,
|
|
2175
|
+
output: FAINT,
|
|
2176
|
+
meta: FAINT,
|
|
2177
|
+
blank: FAINT
|
|
2178
|
+
};
|
|
2179
|
+
var PreviewRow = ({ line }) => /* @__PURE__ */ jsx2("box", { height: 1, flexShrink: 0, backgroundColor: "transparent", children: /* @__PURE__ */ jsxs2("text", { height: 1, bg: "transparent", children: [
|
|
2180
|
+
/* @__PURE__ */ jsx2("span", { fg: FAINT, children: line.lead ? PREVIEW_GLYPH[line.kind] : " " }),
|
|
2181
|
+
/* @__PURE__ */ jsx2("span", { fg: PREVIEW_COLOR[line.kind], children: line.text })
|
|
2182
|
+
] }) });
|
|
2183
|
+
var SessionFacts = ({
|
|
2184
|
+
group,
|
|
2185
|
+
item,
|
|
2186
|
+
rows
|
|
2187
|
+
}) => {
|
|
1717
2188
|
const { session, annotation, services } = item;
|
|
1718
2189
|
const color = STATUS_COLOR[session.status] ?? MUTED;
|
|
1719
2190
|
const summary = session.summary?.split("\n")[0] ?? null;
|
|
1720
|
-
|
|
2191
|
+
const change = annotation ?? group?.annotation;
|
|
2192
|
+
const lines = [
|
|
2193
|
+
/* @__PURE__ */ jsxs2("text", { height: 1, bg: "transparent", children: [
|
|
2194
|
+
/* @__PURE__ */ jsx2("span", { children: " " }),
|
|
2195
|
+
/* @__PURE__ */ jsx2("span", { fg: INK, children: sessionDisplayName(session) }),
|
|
2196
|
+
/* @__PURE__ */ jsx2("span", { fg: FAINT, children: ` \xB7 ${session.harness}` })
|
|
2197
|
+
] }, "name"),
|
|
1721
2198
|
/* @__PURE__ */ jsxs2("text", { height: 1, bg: "transparent", children: [
|
|
1722
2199
|
/* @__PURE__ */ jsx2("span", { children: " " }),
|
|
1723
2200
|
/* @__PURE__ */ jsx2("span", { fg: color, children: session.status }),
|
|
1724
2201
|
/* @__PURE__ */ jsx2("span", { fg: FAINT, children: " \xB7 " }),
|
|
1725
2202
|
/* @__PURE__ */ jsx2("span", { fg: MUTED, children: session.branch }),
|
|
1726
|
-
/* @__PURE__ */
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
timeAgo(session.createdAt)
|
|
1730
|
-
] })
|
|
1731
|
-
] }),
|
|
2203
|
+
/* @__PURE__ */ jsx2("span", { fg: FAINT, children: session.baseSha === "" ? "" : ` vs ${session.baseRef ?? session.baseSha.slice(0, 12)}` })
|
|
2204
|
+
] }, "status"),
|
|
2205
|
+
/* @__PURE__ */ jsx2("text", { height: 1, bg: "transparent", fg: FAINT, children: ` started ${timeAgo(session.createdAt)} \xB7 ${isPendingId(session.id) ? "provisioning" : session.id.slice(0, 8)}` }, "started"),
|
|
1732
2206
|
/* @__PURE__ */ jsxs2("text", { height: 1, bg: "transparent", children: [
|
|
1733
2207
|
/* @__PURE__ */ jsx2("span", { children: " " }),
|
|
1734
|
-
services.length === 0 ? /* @__PURE__ */ jsx2("span", { fg: FAINT, children: "no services running" }) : services.slice(0,
|
|
2208
|
+
services.length === 0 ? /* @__PURE__ */ jsx2("span", { fg: FAINT, children: "no services running" }) : services.slice(0, 2).map((service, index) => /* @__PURE__ */ jsxs2("span", { children: [
|
|
1735
2209
|
index > 0 ? /* @__PURE__ */ jsx2("span", { fg: FAINT, children: " \xB7 " }) : null,
|
|
1736
2210
|
/* @__PURE__ */ jsx2("span", { fg: service.status === "reachable" ? INK_2 : MUTED, children: `${service.label ?? service.id.slice(0, 6)} :${service.workspacePort ?? "?"}${service.protocol === "udp" ? "u" : ""}\u2192${service.hostPort ?? "?"} ${service.status}` })
|
|
1737
2211
|
] }, service.id)),
|
|
1738
|
-
services.length >
|
|
1739
|
-
] }),
|
|
2212
|
+
services.length > 2 ? /* @__PURE__ */ jsx2("span", { fg: FAINT, children: ` \xB7 +${services.length - 2} more` }) : null
|
|
2213
|
+
] }, "services"),
|
|
1740
2214
|
/* @__PURE__ */ jsxs2("text", { height: 1, bg: "transparent", children: [
|
|
1741
2215
|
/* @__PURE__ */ jsx2("span", { children: " " }),
|
|
1742
|
-
|
|
1743
|
-
|
|
2216
|
+
change === void 0 || change.openComments === 0 ? /* @__PURE__ */ jsx2("span", { fg: FAINT, children: "no open review comments" }) : /* @__PURE__ */ jsxs2("span", { fg: INK_2, children: [
|
|
2217
|
+
change.openComments,
|
|
1744
2218
|
" open comment",
|
|
1745
|
-
|
|
2219
|
+
change.openComments === 1 ? "" : "s"
|
|
1746
2220
|
] }),
|
|
1747
|
-
|
|
2221
|
+
change?.pendingFollowUp === true ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
1748
2222
|
/* @__PURE__ */ jsx2("span", { fg: FAINT, children: " \xB7 " }),
|
|
1749
2223
|
/* @__PURE__ */ jsx2("span", { fg: INK_2, children: "follow-up pending" })
|
|
1750
2224
|
] }) : null,
|
|
1751
|
-
|
|
2225
|
+
change?.changeId == null ? null : /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
1752
2226
|
/* @__PURE__ */ jsx2("span", { fg: FAINT, children: " \xB7 " }),
|
|
1753
2227
|
/* @__PURE__ */ jsx2("span", { fg: MUTED, children: "v reviews the change" })
|
|
1754
|
-
] })
|
|
1755
|
-
] }),
|
|
1756
|
-
/* @__PURE__ */ jsx2("text", { height: 1, bg: "transparent", fg: summary === null ? FAINT : MUTED, children: ` ${summary ?? "no summary yet"}` })
|
|
1757
|
-
]
|
|
2228
|
+
] })
|
|
2229
|
+
] }, "comments"),
|
|
2230
|
+
/* @__PURE__ */ jsx2("text", { height: 1, bg: "transparent", fg: summary === null ? FAINT : MUTED, children: ` ${fit(summary ?? "no summary yet", 200)}` }, "summary")
|
|
2231
|
+
];
|
|
2232
|
+
return /* @__PURE__ */ jsx2("box", { flexShrink: 0, flexDirection: "column", backgroundColor: "transparent", children: lines.slice(0, Math.max(0, rows)) });
|
|
1758
2233
|
};
|
|
1759
2234
|
var StatusLine = ({
|
|
1760
2235
|
busy,
|
|
@@ -1773,18 +2248,18 @@ var StatusLine = ({
|
|
|
1773
2248
|
}
|
|
1774
2249
|
return void 0;
|
|
1775
2250
|
}, [busy, status]);
|
|
1776
|
-
const text = busy
|
|
2251
|
+
const text = busy === null ? status !== null && Date.now() - status.at < 5e3 ? ` ${status.text}` : "" : ` ${busy} ${Math.max(0, Math.round((now - busyStarted) / 1e3))}s`;
|
|
1777
2252
|
return /* @__PURE__ */ jsx2("text", { height: 1, fg: INK_2, bg: "transparent", children: text });
|
|
1778
2253
|
};
|
|
1779
2254
|
var errorText = (error) => error instanceof Error ? error.message : String(error);
|
|
1780
|
-
var keepSpanVisible = (scroll, top, height) => {
|
|
1781
|
-
if (scroll === null) return;
|
|
1782
|
-
const viewH = Math.max(1,
|
|
1783
|
-
if (top < scroll.scrollTop) scroll.scrollTo(top);
|
|
2255
|
+
var keepSpanVisible = (scroll, top, height, viewRows) => {
|
|
2256
|
+
if (scroll === null || viewRows <= 0) return;
|
|
2257
|
+
const viewH = Math.max(1, viewRows);
|
|
2258
|
+
if (top < scroll.scrollTop || height > viewH) scroll.scrollTo(top);
|
|
1784
2259
|
else if (top + height > scroll.scrollTop + viewH) scroll.scrollTo(top + height - viewH);
|
|
1785
2260
|
};
|
|
1786
|
-
var
|
|
1787
|
-
var
|
|
2261
|
+
var PREVIEW_MAX_LINES = 300;
|
|
2262
|
+
var SESSION_FACT_ROWS = 6;
|
|
1788
2263
|
var noShare = () => "off";
|
|
1789
2264
|
var App = ({ ctx, onQuit }) => {
|
|
1790
2265
|
const renderer = useRenderer();
|
|
@@ -1796,8 +2271,10 @@ var App = ({ ctx, onQuit }) => {
|
|
|
1796
2271
|
retry: true,
|
|
1797
2272
|
retryDelay: 1e3
|
|
1798
2273
|
});
|
|
1799
|
-
const [
|
|
2274
|
+
const [focus, setFocusState] = useState2("sessions");
|
|
2275
|
+
const [lastNav, setLastNav] = useState2("sessions");
|
|
1800
2276
|
const [projectKey, setProjectKey] = useState2(null);
|
|
2277
|
+
const [worktreeKey, setWorktreeKey] = useState2(null);
|
|
1801
2278
|
const [sessionKey, setSessionKey] = useState2(null);
|
|
1802
2279
|
const [picker, setPicker] = useState2(null);
|
|
1803
2280
|
const [pickerIndex, setPickerIndex] = useState2(0);
|
|
@@ -1811,34 +2288,73 @@ var App = ({ ctx, onQuit }) => {
|
|
|
1811
2288
|
);
|
|
1812
2289
|
const [editing, setEditing] = useState2(null);
|
|
1813
2290
|
const [stopArmed, setStopArmed] = useState2(null);
|
|
2291
|
+
const [removeArmed, setRemoveArmed] = useState2(null);
|
|
1814
2292
|
const [creating, setCreating] = useState2(null);
|
|
1815
2293
|
const [adoptOffer, setAdoptOffer] = useState2(null);
|
|
1816
2294
|
const adoptOfferDecided = useRef2(false);
|
|
1817
2295
|
const [reviewing, setReviewing] = useState2(null);
|
|
1818
2296
|
const lockRef = useRef2(false);
|
|
1819
|
-
const
|
|
1820
|
-
|
|
2297
|
+
const [gate] = useState2(createLaunchGate);
|
|
2298
|
+
const creatingRef = useRef2(creating);
|
|
2299
|
+
creatingRef.current = creating;
|
|
2300
|
+
const branchRequest = useRef2(0);
|
|
1821
2301
|
const { width: terminalCols, height: terminalRows } = useTerminalDimensions2();
|
|
1822
|
-
const showProjectsPane = terminalCols >= 72;
|
|
1823
|
-
const showDetail = terminalRows >= 18;
|
|
1824
|
-
const focus = showProjectsPane ? focusState : "sessions";
|
|
1825
2302
|
const projectItems = deriveProjects(data);
|
|
1826
2303
|
const homeProject = data === void 0 ? void 0 : matchProjectByCwd(data.projects, cwdFacts(ctx.cwd));
|
|
1827
2304
|
const projectIndexRaw = projectKey === null ? -1 : projectItems.findIndex((p) => p.project.id === projectKey);
|
|
1828
|
-
const projectIndex = projectIndexRaw
|
|
2305
|
+
const projectIndex = projectIndexRaw === -1 ? Math.max(
|
|
1829
2306
|
0,
|
|
1830
2307
|
homeProject === void 0 ? 0 : projectItems.findIndex((p) => p.project.id === homeProject.id)
|
|
1831
|
-
);
|
|
2308
|
+
) : projectIndexRaw;
|
|
1832
2309
|
const selectedProject = projectItems[projectIndex] ?? null;
|
|
1833
2310
|
const worktreeGroups = deriveWorktrees(data, selectedProject?.project.id ?? null);
|
|
1834
|
-
const
|
|
1835
|
-
const
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
const
|
|
1839
|
-
const
|
|
2311
|
+
const worktreeIndexRaw = worktreeKey === null ? -1 : worktreeGroups.findIndex((group) => group.key === worktreeKey);
|
|
2312
|
+
const sessionWorktreeIndex = worktreeGroups.findIndex(
|
|
2313
|
+
(group) => group.sessions.some((item) => item.session.id === sessionKey)
|
|
2314
|
+
);
|
|
2315
|
+
const worktreeIndex = worktreeIndexRaw === -1 ? Math.max(0, sessionWorktreeIndex) : worktreeIndexRaw;
|
|
2316
|
+
const selectedGroup = worktreeGroups[worktreeIndex] ?? null;
|
|
2317
|
+
const sessionItems = selectedGroup?.sessions ?? [];
|
|
2318
|
+
const sessionIndexRaw = sessionKey === null ? -1 : sessionItems.findIndex((item) => item.session.id === sessionKey);
|
|
2319
|
+
const sessionIndex = sessionIndexRaw === -1 ? 0 : sessionIndexRaw;
|
|
2320
|
+
const selectedItem = sessionItems[sessionIndex] ?? null;
|
|
2321
|
+
const selectedSession = selectedItem?.session ?? null;
|
|
1840
2322
|
const pickerItems = picker === null ? [] : deriveHarnesses(picker.session);
|
|
1841
|
-
const
|
|
2323
|
+
const layout = planLayout(terminalCols, terminalRows, focus, lastNav);
|
|
2324
|
+
const detailWidth = layout.detailWidth;
|
|
2325
|
+
const sectionRows = (section) => layout.sections.find((entry) => entry.section === section)?.rows ?? 0;
|
|
2326
|
+
const previewSessionId = selectedSession !== null && !isPendingId(selectedSession.id) ? selectedSession.id : null;
|
|
2327
|
+
const previewLive = selectedSession !== null && LIVE_STATUSES.has(selectedSession.status);
|
|
2328
|
+
const transcript = useQuery2({
|
|
2329
|
+
queryKey: TRANSCRIPT_KEY(previewSessionId ?? "none"),
|
|
2330
|
+
queryFn: () => fetchTranscript(ctx.api, previewSessionId ?? ""),
|
|
2331
|
+
enabled: previewSessionId !== null && selectedSession?.harness !== "shell" && detailWidth > 0,
|
|
2332
|
+
staleTime: 3e3,
|
|
2333
|
+
// A live agent writes while you watch; the record is a read, not an
|
|
2334
|
+
// attach, so following it costs one GET while that session is selected.
|
|
2335
|
+
refetchInterval: previewLive ? 8e3 : false,
|
|
2336
|
+
retry: 1
|
|
2337
|
+
});
|
|
2338
|
+
const preview = useMemo2(
|
|
2339
|
+
() => transcript.data === void 0 ? [] : previewLines(transcript.data, {
|
|
2340
|
+
width: Math.max(16, detailWidth - 6),
|
|
2341
|
+
maxLines: PREVIEW_MAX_LINES
|
|
2342
|
+
}),
|
|
2343
|
+
[transcript.data, detailWidth]
|
|
2344
|
+
);
|
|
2345
|
+
const [previewOffset, setPreviewOffset] = useState2(0);
|
|
2346
|
+
const factRows = Math.max(0, Math.min(SESSION_FACT_ROWS, layout.detailRows - 2));
|
|
2347
|
+
const showFactRule = factRows > 0 && layout.detailRows - factRows > 1;
|
|
2348
|
+
const previewRows = Math.max(1, layout.detailRows - factRows - (showFactRule ? 1 : 0));
|
|
2349
|
+
const previewView = previewWindow(preview, previewRows, previewOffset);
|
|
2350
|
+
const setFocus = (column) => {
|
|
2351
|
+
setFocusState(column);
|
|
2352
|
+
if (isNavSection(column)) setLastNav(column);
|
|
2353
|
+
};
|
|
2354
|
+
const selectSession = (id) => {
|
|
2355
|
+
setSessionKey(id);
|
|
2356
|
+
setPreviewOffset(0);
|
|
2357
|
+
};
|
|
1842
2358
|
const say = (text) => setStatus({ text, at: Date.now() });
|
|
1843
2359
|
const refetch = () => void queryClient.invalidateQueries({ queryKey: WORKBENCH_KEY });
|
|
1844
2360
|
useEffect2(() => {
|
|
@@ -1859,19 +2375,30 @@ var App = ({ ctx, onQuit }) => {
|
|
|
1859
2375
|
setAdoptOffer(deriveAdoptOffer(cwdFacts(ctx.cwd)));
|
|
1860
2376
|
}, [data, homeProject]);
|
|
1861
2377
|
const projectScrollRef = useRef2(null);
|
|
2378
|
+
const worktreeScrollRef = useRef2(null);
|
|
1862
2379
|
const sessionScrollRef = useRef2(null);
|
|
2380
|
+
const projectRows = sectionRows("projects");
|
|
2381
|
+
const worktreeRows = sectionRows("worktrees");
|
|
2382
|
+
const sessionRows = sectionRows("sessions");
|
|
2383
|
+
useEffect2(() => {
|
|
2384
|
+
keepSpanVisible(projectScrollRef.current, projectIndex, 1, projectRows);
|
|
2385
|
+
}, [projectIndex, projectRows]);
|
|
1863
2386
|
useEffect2(() => {
|
|
1864
|
-
|
|
1865
|
-
|
|
2387
|
+
keepSpanVisible(
|
|
2388
|
+
worktreeScrollRef.current,
|
|
2389
|
+
worktreeIndex * WORKTREE_ROW_HEIGHT,
|
|
2390
|
+
WORKTREE_ROW_HEIGHT,
|
|
2391
|
+
worktreeRows
|
|
2392
|
+
);
|
|
2393
|
+
}, [worktreeIndex, worktreeRows]);
|
|
1866
2394
|
useEffect2(() => {
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
}, [rowIndex, rows]);
|
|
2395
|
+
keepSpanVisible(
|
|
2396
|
+
sessionScrollRef.current,
|
|
2397
|
+
sessionIndex * SESSION_ROW_HEIGHT,
|
|
2398
|
+
SESSION_ROW_HEIGHT,
|
|
2399
|
+
sessionRows
|
|
2400
|
+
);
|
|
2401
|
+
}, [sessionIndex, sessionRows]);
|
|
1875
2402
|
const attachFlow = async (session) => {
|
|
1876
2403
|
const short = session.id.slice(0, 8);
|
|
1877
2404
|
lockRef.current = true;
|
|
@@ -1896,12 +2423,7 @@ attached \xB7 ${session.harness} \xB7 ${short} \xB7 detach: Ctrl+]
|
|
|
1896
2423
|
`);
|
|
1897
2424
|
await ctx.api("POST", `/sessions/${session.id}/handoff`, { to: "pty" });
|
|
1898
2425
|
outcome = await ctx.attachTty(session.id, session.harness);
|
|
1899
|
-
} else if (existing
|
|
1900
|
-
process.stdout.write(`no live terminal \u2014 rejoining the open shell
|
|
1901
|
-
|
|
1902
|
-
`);
|
|
1903
|
-
outcome = await ctx.attachTty(session.id, "shell", existing.id);
|
|
1904
|
-
} else {
|
|
2426
|
+
} else if (existing === null) {
|
|
1905
2427
|
const shell = await ctx.api(
|
|
1906
2428
|
"POST",
|
|
1907
2429
|
`/sessions/${session.id}/shell`
|
|
@@ -1910,17 +2432,22 @@ attached \xB7 ${session.harness} \xB7 ${short} \xB7 detach: Ctrl+]
|
|
|
1910
2432
|
|
|
1911
2433
|
`);
|
|
1912
2434
|
outcome = await ctx.attachTty(session.id, "shell", shell.id);
|
|
2435
|
+
} else {
|
|
2436
|
+
process.stdout.write(`no live terminal \u2014 rejoining the open shell
|
|
2437
|
+
|
|
2438
|
+
`);
|
|
2439
|
+
outcome = await ctx.attachTty(session.id, "shell", existing.id);
|
|
1913
2440
|
}
|
|
1914
2441
|
}
|
|
1915
2442
|
} catch (error) {
|
|
1916
|
-
say(
|
|
2443
|
+
say(errorText(error));
|
|
1917
2444
|
return;
|
|
1918
2445
|
} finally {
|
|
1919
2446
|
renderer.resume();
|
|
1920
2447
|
lockRef.current = false;
|
|
1921
2448
|
}
|
|
1922
2449
|
say(
|
|
1923
|
-
outcome === "unavailable" ? "attach unavailable \u2014 could not connect" : outcome === "detached" || outcome === "interrupted" ? `detached \u2014 ${short} keeps running` : outcome === "dropped" ? `disconnected \
|
|
2450
|
+
outcome === "unavailable" ? "attach unavailable \u2014 could not connect" : outcome === "detached" || outcome === "interrupted" ? `detached \u2014 ${short} keeps running` : outcome === "dropped" ? `disconnected \xB7 ${short} \u2014 refreshing session status` : `terminal ended \xB7 ${short} \u2014 refreshing session status`
|
|
1924
2451
|
);
|
|
1925
2452
|
refetch();
|
|
1926
2453
|
};
|
|
@@ -1932,13 +2459,6 @@ attached \xB7 ${session.harness} \xB7 ${short} \xB7 detach: Ctrl+]
|
|
|
1932
2459
|
const current = workbench();
|
|
1933
2460
|
if (current !== void 0) queryClient.setQueryData(WORKBENCH_KEY, f(current));
|
|
1934
2461
|
};
|
|
1935
|
-
const attachIfIdle = async (session) => {
|
|
1936
|
-
if (lockRef.current || modalRef.current) {
|
|
1937
|
-
say(`session ready \xB7 ${session.id.slice(0, 8)} \u2014 enter attaches`);
|
|
1938
|
-
return;
|
|
1939
|
-
}
|
|
1940
|
-
await attachFlow(session);
|
|
1941
|
-
};
|
|
1942
2462
|
const launchMutation = useMutation2({
|
|
1943
2463
|
mutationFn: async (vars) => {
|
|
1944
2464
|
const argv = HARNESS_COMMANDS[vars.harness];
|
|
@@ -1967,6 +2487,7 @@ attached \xB7 ${session.harness} \xB7 ${short} \xB7 detach: Ctrl+]
|
|
|
1967
2487
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1968
2488
|
})
|
|
1969
2489
|
);
|
|
2490
|
+
setWorktreeKey(vars.pendingKey);
|
|
1970
2491
|
selectSession(vars.pendingKey);
|
|
1971
2492
|
setBusy(`provisioning ${vars.harness} workspace \u2014 a first launch builds the harness image \xB7`);
|
|
1972
2493
|
setBusyStarted(Date.now());
|
|
@@ -1976,15 +2497,21 @@ attached \xB7 ${session.harness} \xB7 ${short} \xB7 detach: Ctrl+]
|
|
|
1976
2497
|
setBusy(null);
|
|
1977
2498
|
say(errorText(error));
|
|
1978
2499
|
},
|
|
1979
|
-
onSuccess:
|
|
2500
|
+
onSuccess: (session, vars) => {
|
|
1980
2501
|
patchWorkbench(
|
|
1981
2502
|
(current) => replaceSession(current, vars.projectId, vars.pendingKey, session)
|
|
1982
2503
|
);
|
|
1983
|
-
|
|
2504
|
+
setWorktreeKey(
|
|
2505
|
+
(current) => current === vars.pendingKey ? session.worktreeId ?? session.id : current
|
|
2506
|
+
);
|
|
2507
|
+
setSessionKey((current) => current === vars.pendingKey ? session.id : current);
|
|
1984
2508
|
setBusy(null);
|
|
1985
|
-
|
|
2509
|
+
say(`started \xB7 ${sessionDisplayName(session)} \u2014 a attaches`);
|
|
1986
2510
|
},
|
|
1987
|
-
onSettled:
|
|
2511
|
+
onSettled: (_data, _error, vars) => {
|
|
2512
|
+
gate.release(vars.gateKey);
|
|
2513
|
+
settleRefetch();
|
|
2514
|
+
}
|
|
1988
2515
|
});
|
|
1989
2516
|
const resumeMutation = useMutation2({
|
|
1990
2517
|
mutationFn: (vars) => ctx.api("POST", `/sessions/${vars.session.id}/resume`, {
|
|
@@ -2000,7 +2527,7 @@ attached \xB7 ${session.harness} \xB7 ${short} \xB7 detach: Ctrl+]
|
|
|
2000
2527
|
);
|
|
2001
2528
|
selectSession(vars.session.id);
|
|
2002
2529
|
setBusy(
|
|
2003
|
-
`resuming ${vars.session
|
|
2530
|
+
`resuming ${sessionDisplayName(vars.session)} \u2014 a fresh workspace restores the saved state \xB7`
|
|
2004
2531
|
);
|
|
2005
2532
|
setBusyStarted(Date.now());
|
|
2006
2533
|
},
|
|
@@ -2009,15 +2536,17 @@ attached \xB7 ${session.harness} \xB7 ${short} \xB7 detach: Ctrl+]
|
|
|
2009
2536
|
say(errorText(error));
|
|
2010
2537
|
refetch();
|
|
2011
2538
|
},
|
|
2012
|
-
onSuccess:
|
|
2539
|
+
onSuccess: (resumed, vars) => {
|
|
2013
2540
|
patchWorkbench(
|
|
2014
2541
|
(current) => replaceSession(current, vars.projectId, vars.session.id, resumed)
|
|
2015
2542
|
);
|
|
2016
|
-
selectSession(resumed.id);
|
|
2017
2543
|
setBusy(null);
|
|
2018
|
-
|
|
2544
|
+
say(`resumed \xB7 ${sessionDisplayName(resumed)} \u2014 a attaches`);
|
|
2019
2545
|
},
|
|
2020
|
-
onSettled:
|
|
2546
|
+
onSettled: (_data, _error, vars) => {
|
|
2547
|
+
gate.release(vars.gateKey);
|
|
2548
|
+
settleRefetch();
|
|
2549
|
+
}
|
|
2021
2550
|
});
|
|
2022
2551
|
const ADOPT_AUTH_MODES = [
|
|
2023
2552
|
{
|
|
@@ -2058,8 +2587,9 @@ attached \xB7 ${session.harness} \xB7 ${short} \xB7 detach: Ctrl+]
|
|
|
2058
2587
|
return;
|
|
2059
2588
|
}
|
|
2060
2589
|
const project = result.project;
|
|
2061
|
-
say(`adopted \xB7 ${project.name} \u2014
|
|
2590
|
+
say(`adopted \xB7 ${project.name} \u2014 w starts a worktree`);
|
|
2062
2591
|
setProjectKey(project.id);
|
|
2592
|
+
setWorktreeKey(null);
|
|
2063
2593
|
setSessionKey(null);
|
|
2064
2594
|
},
|
|
2065
2595
|
onSettled: settleRefetch
|
|
@@ -2089,7 +2619,7 @@ attached \xB7 ${session.harness} \xB7 ${short} \xB7 detach: Ctrl+]
|
|
|
2089
2619
|
onMutate: async (session) => {
|
|
2090
2620
|
await queryClient.cancelQueries({ queryKey: WORKBENCH_KEY });
|
|
2091
2621
|
patchWorkbench((current) => markSessionStopped(current, session.id));
|
|
2092
|
-
say(`stopped \xB7 ${
|
|
2622
|
+
say(`stopped \xB7 ${sessionDisplayName(session)} \u2014 the record and review remain`);
|
|
2093
2623
|
},
|
|
2094
2624
|
onError: (error) => {
|
|
2095
2625
|
say(errorText(error));
|
|
@@ -2133,24 +2663,29 @@ attached \xB7 ${session.harness} \xB7 ${short} \xB7 detach: Ctrl+]
|
|
|
2133
2663
|
setBusy(null);
|
|
2134
2664
|
say(errorText(error));
|
|
2135
2665
|
},
|
|
2136
|
-
onSuccess:
|
|
2666
|
+
onSuccess: (session, vars) => {
|
|
2137
2667
|
patchWorkbench(
|
|
2138
2668
|
(current) => replaceSession(current, vars.projectId, vars.pendingKey, session)
|
|
2139
2669
|
);
|
|
2140
|
-
|
|
2670
|
+
setSessionKey((current) => current === vars.pendingKey ? session.id : current);
|
|
2141
2671
|
setBusy(null);
|
|
2142
|
-
|
|
2672
|
+
say(`started \xB7 ${sessionDisplayName(session)} \u2014 a attaches`);
|
|
2143
2673
|
},
|
|
2144
|
-
onSettled:
|
|
2674
|
+
onSettled: (_data, _error, vars) => {
|
|
2675
|
+
gate.release(vars.gateKey);
|
|
2676
|
+
settleRefetch();
|
|
2677
|
+
}
|
|
2145
2678
|
});
|
|
2146
2679
|
const removeWorktreeMutation = useMutation2({
|
|
2147
|
-
mutationFn: (group) => group.id
|
|
2680
|
+
mutationFn: (group) => group.id === null ? ctx.api("DELETE", `/sessions/${group.sessions[0]?.session.id ?? ""}`) : ctx.api("DELETE", `/worktrees/${group.id}`),
|
|
2148
2681
|
onMutate: async (group) => {
|
|
2149
2682
|
await queryClient.cancelQueries({ queryKey: WORKBENCH_KEY });
|
|
2150
2683
|
const projectId = selectedProject?.project.id;
|
|
2151
2684
|
if (projectId !== void 0) {
|
|
2152
2685
|
patchWorkbench((current) => removeWorktreeGroup(current, projectId, group));
|
|
2153
2686
|
}
|
|
2687
|
+
setWorktreeKey(null);
|
|
2688
|
+
setSessionKey(null);
|
|
2154
2689
|
say(`removing worktree \xB7 ${group.name}`);
|
|
2155
2690
|
},
|
|
2156
2691
|
onError: (error) => {
|
|
@@ -2171,121 +2706,136 @@ attached \xB7 ${session.harness} \xB7 ${short} \xB7 detach: Ctrl+]
|
|
|
2171
2706
|
if (projectId !== void 0) {
|
|
2172
2707
|
patchWorkbench((current) => removeSession(current, projectId, session.id));
|
|
2173
2708
|
}
|
|
2174
|
-
|
|
2709
|
+
setSessionKey(null);
|
|
2710
|
+
say(`removing session \xB7 ${sessionDisplayName(session)}`);
|
|
2175
2711
|
},
|
|
2176
2712
|
onError: (error) => {
|
|
2177
2713
|
say(errorText(error));
|
|
2178
2714
|
refetch();
|
|
2179
2715
|
},
|
|
2180
2716
|
onSuccess: (_result, session) => {
|
|
2181
|
-
say(`removed \xB7 ${
|
|
2717
|
+
say(`removed \xB7 ${sessionDisplayName(session)} \u2014 the worktree remains`);
|
|
2182
2718
|
refetch();
|
|
2183
2719
|
},
|
|
2184
2720
|
onSettled: settleRefetch
|
|
2185
2721
|
});
|
|
2186
|
-
const
|
|
2722
|
+
const confirmationVisible = (prefix) => busy === null && status !== null && Date.now() - status.at < 5e3 && status.text.startsWith(prefix);
|
|
2187
2723
|
const armRemove = () => {
|
|
2188
|
-
if (
|
|
2189
|
-
const
|
|
2190
|
-
if (isPendingId(session.id)) return;
|
|
2191
|
-
if (
|
|
2192
|
-
say(
|
|
2724
|
+
if (focus === "worktrees") {
|
|
2725
|
+
const group = selectedGroup;
|
|
2726
|
+
if (group === null || group.sessions.some((item) => isPendingId(item.session.id))) return;
|
|
2727
|
+
if (group.live > 0) {
|
|
2728
|
+
say(
|
|
2729
|
+
`${group.live} session${group.live === 1 ? "" : "s"} live \u2014 stop them first (\u21E7K) \xB7 ${group.name}`
|
|
2730
|
+
);
|
|
2193
2731
|
return;
|
|
2194
2732
|
}
|
|
2195
|
-
if (removeArmed ===
|
|
2733
|
+
if (removeArmed === `wt:${group.key}` && confirmationVisible("press \u21E7D again")) {
|
|
2196
2734
|
setRemoveArmed(null);
|
|
2197
|
-
|
|
2735
|
+
removeWorktreeMutation.mutate(group);
|
|
2198
2736
|
return;
|
|
2199
2737
|
}
|
|
2200
|
-
setRemoveArmed(
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
);
|
|
2738
|
+
setRemoveArmed(`wt:${group.key}`);
|
|
2739
|
+
const facts = group.sessions.length === 1 ? "its session and change go with it" : `${group.sessions.length} sessions and the change go with it`;
|
|
2740
|
+
say(`press \u21E7D again to remove worktree \xB7 ${group.name} \u2014 ${facts}`);
|
|
2204
2741
|
return;
|
|
2205
2742
|
}
|
|
2206
|
-
const
|
|
2207
|
-
if (
|
|
2208
|
-
if (
|
|
2209
|
-
say(
|
|
2210
|
-
`${group.live} session${group.live === 1 ? "" : "s"} live \u2014 stop them first \xB7 ${group.name}`
|
|
2211
|
-
);
|
|
2743
|
+
const session = selectedSession;
|
|
2744
|
+
if (session === null || isPendingId(session.id)) return;
|
|
2745
|
+
if (AGENT_LIVE_STATUSES.has(session.status)) {
|
|
2746
|
+
say(`the agent is still working \u2014 stop it first (\u21E7K) \xB7 ${sessionDisplayName(session)}`);
|
|
2212
2747
|
return;
|
|
2213
2748
|
}
|
|
2214
|
-
if (removeArmed ===
|
|
2749
|
+
if (removeArmed === session.id && confirmationVisible("press \u21E7D again")) {
|
|
2215
2750
|
setRemoveArmed(null);
|
|
2216
|
-
|
|
2751
|
+
removeSessionMutation.mutate(session);
|
|
2217
2752
|
return;
|
|
2218
2753
|
}
|
|
2219
|
-
setRemoveArmed(
|
|
2220
|
-
|
|
2221
|
-
|
|
2754
|
+
setRemoveArmed(session.id);
|
|
2755
|
+
say(
|
|
2756
|
+
`press \u21E7D again to remove session \xB7 ${sessionDisplayName(session)} \u2014 its record goes, the worktree stays`
|
|
2757
|
+
);
|
|
2222
2758
|
};
|
|
2223
2759
|
const armStop = () => {
|
|
2224
|
-
if (
|
|
2225
|
-
const group =
|
|
2226
|
-
|
|
2760
|
+
if (focus === "worktrees") {
|
|
2761
|
+
const group = selectedGroup;
|
|
2762
|
+
if (group === null) return;
|
|
2763
|
+
const live = group.sessions.filter((item) => LIVE_STATUSES.has(item.session.status));
|
|
2227
2764
|
if (live.length === 0) {
|
|
2228
2765
|
say("nothing to stop \u2014 the worktree is settled");
|
|
2229
2766
|
return;
|
|
2230
2767
|
}
|
|
2231
|
-
if (stopArmed === `wt:${group.key}`) {
|
|
2768
|
+
if (stopArmed === `wt:${group.key}` && confirmationVisible("press \u21E7K again")) {
|
|
2232
2769
|
setStopArmed(null);
|
|
2233
|
-
for (const
|
|
2770
|
+
for (const item of live) stopMutation.mutate(item.session);
|
|
2234
2771
|
return;
|
|
2235
2772
|
}
|
|
2236
2773
|
setStopArmed(`wt:${group.key}`);
|
|
2237
2774
|
say(
|
|
2238
|
-
`press again to stop ${live.length} live session${live.length === 1 ? "" : "s"} \xB7 ${group.name}`
|
|
2775
|
+
`press \u21E7K again to stop ${live.length} live session${live.length === 1 ? "" : "s"} \xB7 ${group.name}`
|
|
2239
2776
|
);
|
|
2240
2777
|
return;
|
|
2241
2778
|
}
|
|
2242
|
-
const
|
|
2243
|
-
if (
|
|
2244
|
-
if (!LIVE_STATUSES.has(
|
|
2779
|
+
const session = selectedSession;
|
|
2780
|
+
if (session === null || isPendingId(session.id)) return;
|
|
2781
|
+
if (!LIVE_STATUSES.has(session.status)) {
|
|
2245
2782
|
say("nothing to stop \u2014 the session is settled");
|
|
2246
2783
|
return;
|
|
2247
2784
|
}
|
|
2248
|
-
if (stopArmed ===
|
|
2785
|
+
if (stopArmed === session.id && confirmationVisible("press \u21E7K again")) {
|
|
2249
2786
|
setStopArmed(null);
|
|
2250
|
-
stopMutation.mutate(
|
|
2787
|
+
stopMutation.mutate(session);
|
|
2251
2788
|
return;
|
|
2252
2789
|
}
|
|
2253
|
-
setStopArmed(
|
|
2254
|
-
say(`press again to stop \xB7 ${
|
|
2790
|
+
setStopArmed(session.id);
|
|
2791
|
+
say(`press \u21E7K again to stop \xB7 ${sessionDisplayName(session)}`);
|
|
2255
2792
|
};
|
|
2256
2793
|
const openCreateModal = (projectId) => {
|
|
2257
|
-
|
|
2794
|
+
const request = ++branchRequest.current;
|
|
2795
|
+
const draft = {
|
|
2258
2796
|
projectId,
|
|
2259
2797
|
step: "name",
|
|
2260
2798
|
name: "",
|
|
2261
2799
|
branches: null,
|
|
2800
|
+
branchError: null,
|
|
2262
2801
|
query: "",
|
|
2263
2802
|
baseIndex: 0,
|
|
2264
2803
|
base: null,
|
|
2265
2804
|
joins: false,
|
|
2266
2805
|
harnessIndex: 0
|
|
2267
|
-
}
|
|
2806
|
+
};
|
|
2807
|
+
creatingRef.current = draft;
|
|
2808
|
+
setCreating(draft);
|
|
2268
2809
|
const prefill = projectId === homeProject?.id ? ctx.cwdBranch : null;
|
|
2269
2810
|
void ctx.api("GET", `/projects/${projectId}/branches`).then(
|
|
2270
2811
|
(branches) => setCreating((current) => {
|
|
2271
|
-
if (current
|
|
2812
|
+
if (current?.projectId !== projectId || request !== branchRequest.current) return current;
|
|
2272
2813
|
const match = prefill === null ? void 0 : branches.find((candidate) => candidate.name === prefill);
|
|
2273
2814
|
const ordered = filterBranches(branches, "");
|
|
2274
2815
|
return {
|
|
2275
2816
|
...current,
|
|
2276
2817
|
branches,
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2818
|
+
branchError: null,
|
|
2819
|
+
// A late response may populate the list, but not change a base
|
|
2820
|
+
// the user has already selected or a filter they are navigating.
|
|
2821
|
+
...current.step === "name" || current.step === "base" && current.query === "" && current.baseIndex === 0 ? {
|
|
2822
|
+
base: match === void 0 || match.isDefault ? null : match.name,
|
|
2823
|
+
baseIndex: match === void 0 ? 0 : Math.max(
|
|
2824
|
+
0,
|
|
2825
|
+
ordered.findIndex((candidate) => candidate.name === match.name)
|
|
2826
|
+
)
|
|
2827
|
+
} : {}
|
|
2282
2828
|
};
|
|
2283
2829
|
}),
|
|
2284
|
-
() =>
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2830
|
+
(error) => {
|
|
2831
|
+
if (creatingRef.current?.projectId !== projectId || request !== branchRequest.current)
|
|
2832
|
+
return;
|
|
2833
|
+
const message = errorText(error);
|
|
2834
|
+
say(`could not read branches \u2014 ${message}`);
|
|
2835
|
+
setCreating(
|
|
2836
|
+
(current) => current?.projectId === projectId ? { ...current, branches: [], branchError: message } : current
|
|
2837
|
+
);
|
|
2838
|
+
}
|
|
2289
2839
|
);
|
|
2290
2840
|
};
|
|
2291
2841
|
const advanceFromName = (current, raw) => {
|
|
@@ -2313,6 +2863,12 @@ attached \xB7 ${session.harness} \xB7 ${short} \xB7 detach: Ctrl+]
|
|
|
2313
2863
|
if (current === null) return;
|
|
2314
2864
|
const choice = deriveHarnesses(null)[current.harnessIndex];
|
|
2315
2865
|
if (choice?.harness == null) return;
|
|
2866
|
+
const gateKey = `launch:${current.projectId}:${current.name}`;
|
|
2867
|
+
if (!gate.take(gateKey)) {
|
|
2868
|
+
setCreating(null);
|
|
2869
|
+
say("that worktree is already starting \u2014 wait for it to finish before starting another");
|
|
2870
|
+
return;
|
|
2871
|
+
}
|
|
2316
2872
|
setCreating(null);
|
|
2317
2873
|
setFocus("sessions");
|
|
2318
2874
|
launchMutation.mutate({
|
|
@@ -2320,13 +2876,10 @@ attached \xB7 ${session.harness} \xB7 ${short} \xB7 detach: Ctrl+]
|
|
|
2320
2876
|
harness: choice.harness,
|
|
2321
2877
|
name: current.name === "" ? null : current.name,
|
|
2322
2878
|
base: current.base,
|
|
2323
|
-
pendingKey: pendingId()
|
|
2879
|
+
pendingKey: pendingId(),
|
|
2880
|
+
gateKey
|
|
2324
2881
|
});
|
|
2325
2882
|
};
|
|
2326
|
-
const resumeSession = (projectId, session, harness) => {
|
|
2327
|
-
setFocus("sessions");
|
|
2328
|
-
resumeMutation.mutate({ projectId, session, harness });
|
|
2329
|
-
};
|
|
2330
2883
|
const submitRename = (session, value) => {
|
|
2331
2884
|
setEditing(null);
|
|
2332
2885
|
const label = value.trim() === "" ? null : value.trim();
|
|
@@ -2336,86 +2889,142 @@ attached \xB7 ${session.harness} \xB7 ${short} \xB7 detach: Ctrl+]
|
|
|
2336
2889
|
setPicker(worktree === void 0 ? { session } : { session, worktree });
|
|
2337
2890
|
setPickerIndex(0);
|
|
2338
2891
|
};
|
|
2339
|
-
const
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2892
|
+
const attachSelected = () => {
|
|
2893
|
+
const plan = planAttach(selectedSession);
|
|
2894
|
+
switch (plan.kind) {
|
|
2895
|
+
case "attach":
|
|
2896
|
+
void attachFlow(plan.session);
|
|
2897
|
+
return;
|
|
2898
|
+
case "starting":
|
|
2899
|
+
say(
|
|
2900
|
+
`still starting \xB7 ${sessionDisplayName(plan.session)} \u2014 a attaches once the row reads running`
|
|
2901
|
+
);
|
|
2902
|
+
return;
|
|
2903
|
+
case "settled":
|
|
2904
|
+
say(`settled \xB7 ${sessionDisplayName(plan.session)} \u2014 r resumes it, \u21E7D removes it`);
|
|
2905
|
+
return;
|
|
2906
|
+
case "pending":
|
|
2907
|
+
say("still provisioning \u2014 the row fills in when the workspace answers");
|
|
2908
|
+
return;
|
|
2909
|
+
case "none":
|
|
2910
|
+
say("no session selected \u2014 n starts one");
|
|
2911
|
+
return;
|
|
2343
2912
|
}
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2913
|
+
};
|
|
2914
|
+
const resumeSelected = () => {
|
|
2915
|
+
const plan = planResume(selectedSession);
|
|
2916
|
+
switch (plan.kind) {
|
|
2917
|
+
case "resume":
|
|
2918
|
+
if (gate.held(`resume:${plan.session.id}`)) {
|
|
2919
|
+
say(`already resuming \xB7 ${sessionDisplayName(plan.session)}`);
|
|
2920
|
+
return;
|
|
2921
|
+
}
|
|
2922
|
+
openPicker(plan.session);
|
|
2923
|
+
return;
|
|
2924
|
+
case "live":
|
|
2925
|
+
say(`already live \xB7 ${sessionDisplayName(plan.session)} \u2014 a attaches`);
|
|
2926
|
+
return;
|
|
2927
|
+
case "pending":
|
|
2928
|
+
say("still provisioning \u2014 nothing to resume yet");
|
|
2929
|
+
return;
|
|
2930
|
+
case "none":
|
|
2931
|
+
say("no session selected \u2014 n starts one");
|
|
2932
|
+
return;
|
|
2353
2933
|
}
|
|
2354
|
-
if (rows.length === 0) return;
|
|
2355
|
-
const next = Math.max(0, Math.min(rows.length - 1, rowIndex + delta));
|
|
2356
|
-
const row = rows[next];
|
|
2357
|
-
if (row !== void 0) setSessionKey(rowKeyOf(row));
|
|
2358
2934
|
};
|
|
2359
|
-
const
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2935
|
+
const resumeSession = (projectId, session, harness) => {
|
|
2936
|
+
const gateKey = `resume:${session.id}`;
|
|
2937
|
+
if (!gate.take(gateKey)) {
|
|
2938
|
+
say(`already resuming \xB7 ${sessionDisplayName(session)}`);
|
|
2939
|
+
return;
|
|
2940
|
+
}
|
|
2941
|
+
setFocus("sessions");
|
|
2942
|
+
resumeMutation.mutate({ projectId, session, harness, gateKey });
|
|
2363
2943
|
};
|
|
2364
|
-
const
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
if (choice === void 0 || projectId === void 0) return;
|
|
2369
|
-
setPicker(null);
|
|
2370
|
-
if (picker.worktree !== void 0 && picker.worktree.id !== null) {
|
|
2371
|
-
if (choice.harness !== null) {
|
|
2372
|
-
setFocus("sessions");
|
|
2373
|
-
launchInWorktreeMutation.mutate({
|
|
2374
|
-
projectId,
|
|
2375
|
-
worktreeId: picker.worktree.id,
|
|
2376
|
-
harness: choice.harness,
|
|
2377
|
-
pendingKey: pendingId()
|
|
2378
|
-
});
|
|
2379
|
-
}
|
|
2380
|
-
} else if (picker.session !== null) {
|
|
2381
|
-
resumeSession(projectId, picker.session, choice.harness);
|
|
2382
|
-
}
|
|
2944
|
+
const newSession = () => {
|
|
2945
|
+
const projectId = selectedProject?.project.id;
|
|
2946
|
+
if (projectId === void 0) {
|
|
2947
|
+
say("no project selected");
|
|
2383
2948
|
return;
|
|
2384
2949
|
}
|
|
2950
|
+
const group = selectedGroup;
|
|
2385
2951
|
if (focus === "projects") {
|
|
2386
|
-
|
|
2952
|
+
openCreateModal(projectId);
|
|
2387
2953
|
return;
|
|
2388
2954
|
}
|
|
2389
|
-
if (
|
|
2390
|
-
|
|
2391
|
-
if (target2.kind === "attach") {
|
|
2392
|
-
void attachFlow(target2.session);
|
|
2393
|
-
} else if (target2.kind === "wait") {
|
|
2394
|
-
sayStillStarting(target2.session);
|
|
2395
|
-
} else if (selectedRow.group.id !== null) {
|
|
2396
|
-
openPicker(null, selectedRow.group);
|
|
2397
|
-
}
|
|
2955
|
+
if (group === null || group.id === null) {
|
|
2956
|
+
openCreateModal(projectId);
|
|
2398
2957
|
return;
|
|
2399
2958
|
}
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
if (isPendingId(item.session.id)) {
|
|
2403
|
-
say("still provisioning \u2014 the row fills in when the workspace answers");
|
|
2959
|
+
if (gate.held(`worktree-session:${group.id}`)) {
|
|
2960
|
+
say(`already starting a session in ${group.name}`);
|
|
2404
2961
|
return;
|
|
2405
2962
|
}
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2963
|
+
openPicker(null, group);
|
|
2964
|
+
};
|
|
2965
|
+
const launchInWorktree = (projectId, group, harness) => {
|
|
2966
|
+
if (group.id === null) return;
|
|
2967
|
+
const gateKey = `worktree-session:${group.id}`;
|
|
2968
|
+
if (!gate.take(gateKey)) {
|
|
2969
|
+
say(`already starting a session in ${group.name}`);
|
|
2970
|
+
return;
|
|
2413
2971
|
}
|
|
2972
|
+
setFocus("sessions");
|
|
2973
|
+
launchInWorktreeMutation.mutate({
|
|
2974
|
+
projectId,
|
|
2975
|
+
worktreeId: group.id,
|
|
2976
|
+
harness,
|
|
2977
|
+
pendingKey: pendingId(),
|
|
2978
|
+
gateKey
|
|
2979
|
+
});
|
|
2414
2980
|
};
|
|
2981
|
+
const moveSelection = (delta) => {
|
|
2982
|
+
if (picker !== null) {
|
|
2983
|
+
setPickerIndex((current) => clampIndex(pickerItems.length, current + delta));
|
|
2984
|
+
return;
|
|
2985
|
+
}
|
|
2986
|
+
switch (focus) {
|
|
2987
|
+
case "projects": {
|
|
2988
|
+
const item = projectItems[clampIndex(projectItems.length, projectIndex + delta)];
|
|
2989
|
+
if (item === void 0) return;
|
|
2990
|
+
setProjectKey(item.project.id);
|
|
2991
|
+
setWorktreeKey(null);
|
|
2992
|
+
setSessionKey(null);
|
|
2993
|
+
setPreviewOffset(0);
|
|
2994
|
+
return;
|
|
2995
|
+
}
|
|
2996
|
+
case "worktrees": {
|
|
2997
|
+
const group = worktreeGroups[clampIndex(worktreeGroups.length, worktreeIndex + delta)];
|
|
2998
|
+
if (group === void 0) return;
|
|
2999
|
+
setWorktreeKey(group.key);
|
|
3000
|
+
setSessionKey(null);
|
|
3001
|
+
setPreviewOffset(0);
|
|
3002
|
+
return;
|
|
3003
|
+
}
|
|
3004
|
+
case "sessions": {
|
|
3005
|
+
const item = sessionItems[clampIndex(sessionItems.length, sessionIndex + delta)];
|
|
3006
|
+
if (item === void 0) return;
|
|
3007
|
+
selectSession(item.session.id);
|
|
3008
|
+
return;
|
|
3009
|
+
}
|
|
3010
|
+
case "detail":
|
|
3011
|
+
setPreviewOffset(
|
|
3012
|
+
Math.max(
|
|
3013
|
+
0,
|
|
3014
|
+
Math.min(Math.max(0, preview.length - previewRows), previewView.offset - delta)
|
|
3015
|
+
)
|
|
3016
|
+
);
|
|
3017
|
+
return;
|
|
3018
|
+
}
|
|
3019
|
+
};
|
|
3020
|
+
const moveColumn = (delta) => setFocus(stepColumn(focus, delta));
|
|
2415
3021
|
useKeyboard2((key) => {
|
|
2416
3022
|
if (reviewing !== null) return;
|
|
2417
3023
|
if (lockRef.current) return;
|
|
2418
3024
|
if (key.ctrl && key.name === "c") return onQuit();
|
|
3025
|
+
const verb = verbForKey(key.name ?? "", key.shift === true);
|
|
3026
|
+
if (verb !== "stop") setStopArmed(null);
|
|
3027
|
+
if (verb !== "remove") setRemoveArmed(null);
|
|
2419
3028
|
if (adoptOffer !== null) {
|
|
2420
3029
|
if (key.name === "return" || key.name === "linefeed" || key.name === "y") {
|
|
2421
3030
|
adoptMutation.mutate(adoptOffer);
|
|
@@ -2473,17 +3082,11 @@ attached \xB7 ${session.harness} \xB7 ${short} \xB7 detach: Ctrl+]
|
|
|
2473
3082
|
if (current === null) return current;
|
|
2474
3083
|
if (current.step === "base") {
|
|
2475
3084
|
const matches = filterBranches(current.branches ?? [], current.query).length;
|
|
2476
|
-
return {
|
|
2477
|
-
...current,
|
|
2478
|
-
baseIndex: Math.max(0, Math.min(matches - 1, current.baseIndex + delta))
|
|
2479
|
-
};
|
|
3085
|
+
return { ...current, baseIndex: clampIndex(matches, current.baseIndex + delta) };
|
|
2480
3086
|
}
|
|
2481
3087
|
if (current.step === "harness") {
|
|
2482
3088
|
const count = deriveHarnesses(null).length;
|
|
2483
|
-
return {
|
|
2484
|
-
...current,
|
|
2485
|
-
harnessIndex: Math.max(0, Math.min(count - 1, current.harnessIndex + delta))
|
|
2486
|
-
};
|
|
3089
|
+
return { ...current, harnessIndex: clampIndex(count, current.harnessIndex + delta) };
|
|
2487
3090
|
}
|
|
2488
3091
|
return current;
|
|
2489
3092
|
});
|
|
@@ -2504,8 +3107,20 @@ attached \xB7 ${session.harness} \xB7 ${short} \xB7 detach: Ctrl+]
|
|
|
2504
3107
|
return moveSelection(-1);
|
|
2505
3108
|
case "return":
|
|
2506
3109
|
case "linefeed":
|
|
2507
|
-
case "l":
|
|
2508
|
-
|
|
3110
|
+
case "l": {
|
|
3111
|
+
const choice = pickerItems[pickerIndex];
|
|
3112
|
+
const projectId = selectedProject?.project.id;
|
|
3113
|
+
const target = picker;
|
|
3114
|
+
if (choice === void 0 || projectId === void 0) return;
|
|
3115
|
+
setPicker(null);
|
|
3116
|
+
if (target.worktree !== void 0) {
|
|
3117
|
+
if (choice.harness !== null)
|
|
3118
|
+
launchInWorktree(projectId, target.worktree, choice.harness);
|
|
3119
|
+
} else if (target.session !== null) {
|
|
3120
|
+
resumeSession(projectId, target.session, choice.harness);
|
|
3121
|
+
}
|
|
3122
|
+
return;
|
|
3123
|
+
}
|
|
2509
3124
|
case "escape":
|
|
2510
3125
|
case "q":
|
|
2511
3126
|
case "h":
|
|
@@ -2514,71 +3129,67 @@ attached \xB7 ${session.harness} \xB7 ${short} \xB7 detach: Ctrl+]
|
|
|
2514
3129
|
return;
|
|
2515
3130
|
}
|
|
2516
3131
|
}
|
|
2517
|
-
if (key.
|
|
2518
|
-
if (
|
|
2519
|
-
|
|
2520
|
-
|
|
3132
|
+
if (key.ctrl === true) return;
|
|
3133
|
+
if (focus === "projects" && ["attach", "resume", "stop", "remove", "rename", "openWeb", "review"].includes(verb ?? "")) {
|
|
3134
|
+
say("select a session first \u2014 \u2192 opens worktrees");
|
|
3135
|
+
return;
|
|
3136
|
+
}
|
|
3137
|
+
switch (verb) {
|
|
3138
|
+
case "quit":
|
|
2521
3139
|
return onQuit();
|
|
2522
|
-
case "
|
|
2523
|
-
case "j":
|
|
3140
|
+
case "moveDown":
|
|
2524
3141
|
return moveSelection(1);
|
|
2525
|
-
case "
|
|
2526
|
-
case "k":
|
|
3142
|
+
case "moveUp":
|
|
2527
3143
|
return moveSelection(-1);
|
|
2528
|
-
case "
|
|
2529
|
-
return
|
|
2530
|
-
case "
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
case "
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
case "
|
|
2539
|
-
|
|
2540
|
-
case "
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
case "tab":
|
|
2544
|
-
if (showProjectsPane) setFocus(focus === "projects" ? "sessions" : "projects");
|
|
2545
|
-
return;
|
|
2546
|
-
case "n":
|
|
3144
|
+
case "pageDown":
|
|
3145
|
+
return moveSelection(10);
|
|
3146
|
+
case "pageUp":
|
|
3147
|
+
return moveSelection(-10);
|
|
3148
|
+
case "columnRight":
|
|
3149
|
+
return moveColumn(1);
|
|
3150
|
+
case "columnLeft":
|
|
3151
|
+
return moveColumn(-1);
|
|
3152
|
+
case "attach":
|
|
3153
|
+
return attachSelected();
|
|
3154
|
+
case "resume":
|
|
3155
|
+
return resumeSelected();
|
|
3156
|
+
case "newSession":
|
|
3157
|
+
return newSession();
|
|
3158
|
+
case "newWorktree":
|
|
2547
3159
|
if (selectedProject !== null) openCreateModal(selectedProject.project.id);
|
|
2548
3160
|
return;
|
|
2549
|
-
case "
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
3161
|
+
case "stop":
|
|
3162
|
+
return armStop();
|
|
3163
|
+
case "remove":
|
|
3164
|
+
return armRemove();
|
|
3165
|
+
case "refresh":
|
|
3166
|
+
say("refreshing\u2026");
|
|
3167
|
+
refetch();
|
|
3168
|
+
void queryClient.invalidateQueries({ queryKey: ["transcript"] });
|
|
2557
3169
|
return;
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
if (item !== null && !isPendingId(item.session.id)) setEditing(item.session);
|
|
3170
|
+
case "rename": {
|
|
3171
|
+
const session = selectedSession;
|
|
3172
|
+
if (session !== null && !isPendingId(session.id)) setEditing(session);
|
|
2562
3173
|
return;
|
|
2563
3174
|
}
|
|
2564
|
-
case "
|
|
2565
|
-
const
|
|
2566
|
-
if (
|
|
2567
|
-
openUrl(`${ctx.config.url}/sessions/${
|
|
2568
|
-
say(`opened \xB7 ${ctx.config.url}/sessions/${
|
|
3175
|
+
case "openWeb": {
|
|
3176
|
+
const session = selectedSession;
|
|
3177
|
+
if (session !== null && !isPendingId(session.id)) {
|
|
3178
|
+
openUrl(`${ctx.config.url}/sessions/${session.id}`);
|
|
3179
|
+
say(`opened \xB7 ${ctx.config.url}/sessions/${session.id.slice(0, 8)}\u2026`);
|
|
2569
3180
|
}
|
|
2570
3181
|
return;
|
|
2571
3182
|
}
|
|
2572
|
-
case "
|
|
2573
|
-
const
|
|
2574
|
-
if (
|
|
2575
|
-
if (isPendingId(
|
|
3183
|
+
case "review": {
|
|
3184
|
+
const session = selectedSession;
|
|
3185
|
+
if (session === null) return;
|
|
3186
|
+
if (isPendingId(session.id)) {
|
|
2576
3187
|
say("still provisioning \u2014 nothing to review yet");
|
|
2577
3188
|
return;
|
|
2578
3189
|
}
|
|
2579
3190
|
const target = reviewTargetForSession(
|
|
2580
|
-
|
|
2581
|
-
|
|
3191
|
+
session,
|
|
3192
|
+
selectedItem?.annotation ?? selectedGroup?.annotation,
|
|
2582
3193
|
selectedProject?.project.name ?? "project"
|
|
2583
3194
|
);
|
|
2584
3195
|
if (target === null) {
|
|
@@ -2588,22 +3199,28 @@ attached \xB7 ${session.harness} \xB7 ${short} \xB7 detach: Ctrl+]
|
|
|
2588
3199
|
setReviewing(target);
|
|
2589
3200
|
return;
|
|
2590
3201
|
}
|
|
2591
|
-
case
|
|
2592
|
-
say("refreshing\u2026");
|
|
2593
|
-
refetch();
|
|
2594
|
-
return;
|
|
2595
|
-
default:
|
|
3202
|
+
case null:
|
|
2596
3203
|
return;
|
|
2597
3204
|
}
|
|
2598
3205
|
});
|
|
2599
3206
|
const liveTotal = projectItems.reduce((sum, item) => sum + item.live, 0);
|
|
2600
|
-
const
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
3207
|
+
const columnTitle = (column) => {
|
|
3208
|
+
if (column === "worktrees") {
|
|
3209
|
+
return selectedProject === null ? "worktrees" : `worktrees \xB7 ${worktreeGroups.length}${selectedProject.live > 0 ? ` \xB7 ${selectedProject.live} live` : ""}`;
|
|
3210
|
+
}
|
|
3211
|
+
if (column === "sessions") {
|
|
3212
|
+
return selectedGroup === null ? "sessions" : `sessions \xB7 ${sessionItems.length}`;
|
|
3213
|
+
}
|
|
3214
|
+
if (column === "detail") {
|
|
3215
|
+
if (selectedSession === null) return "session";
|
|
3216
|
+
return previewView.offset === 0 ? "session \xB7 read-only" : `session \xB7 read-only \xB7 ${previewView.later} newer below`;
|
|
3217
|
+
}
|
|
3218
|
+
return COLUMN_TITLE[column];
|
|
3219
|
+
};
|
|
3220
|
+
const pickerTitle = picker === null ? "" : picker.worktree === void 0 ? picker.session === null ? "new session \u2014 pick a harness" : `resume ${sessionDisplayName(picker.session)} \u2014 pick a harness` : `new session in ${picker.worktree.name} \u2014 pick a harness`;
|
|
2604
3221
|
const creatingHeight = 2 + 1 + 1 + 6 + 1 + deriveHarnesses(null).length;
|
|
2605
|
-
const footerText = adoptOffer
|
|
2606
|
-
const loadFailure = data === void 0 && failureReason !== null ?
|
|
3222
|
+
const footerText = adoptOffer === null ? editing === null ? creating === null ? picker === null ? ` ${fitHints(verbHints(focus), Math.max(12, terminalCols - 2))}` : " \u2191\u2193 move \xB7 enter start \xB7 esc cancel" : creating.step === "name" ? " enter continue \xB7 esc cancel" : creating.step === "base" ? " type to filter \xB7 \u2191\u2193 move \xB7 enter choose base \xB7 esc back" : " \u2191\u2193 move \xB7 enter launch \xB7 esc back" : " enter save \xB7 esc cancel" : " enter adopt \xB7 \u2190\u2192 auth mode \xB7 esc not now";
|
|
3223
|
+
const loadFailure = data === void 0 && failureReason !== null ? errorText(failureReason) : null;
|
|
2607
3224
|
if (reviewing !== null) {
|
|
2608
3225
|
return /* @__PURE__ */ jsx2(
|
|
2609
3226
|
ReviewScreen,
|
|
@@ -2620,7 +3237,160 @@ attached \xB7 ${session.harness} \xB7 ${short} \xB7 detach: Ctrl+]
|
|
|
2620
3237
|
}
|
|
2621
3238
|
);
|
|
2622
3239
|
}
|
|
2623
|
-
|
|
3240
|
+
const breadcrumbValue = (column) => {
|
|
3241
|
+
if (column === "projects") return selectedProject?.project.name ?? "no project";
|
|
3242
|
+
if (column === "worktrees") return selectedGroup?.name ?? "no worktree";
|
|
3243
|
+
return selectedSession === null ? "no session" : sessionDisplayName(selectedSession);
|
|
3244
|
+
};
|
|
3245
|
+
const sectionFact = (section) => {
|
|
3246
|
+
if (section === "projects") {
|
|
3247
|
+
if (selectedProject === null) return "";
|
|
3248
|
+
return selectedProject.live > 0 ? `${selectedProject.live} live` : "settled";
|
|
3249
|
+
}
|
|
3250
|
+
if (section === "worktrees") {
|
|
3251
|
+
if (selectedGroup === null) return "";
|
|
3252
|
+
return selectedGroup.live > 0 ? foldGroupStatus(selectedGroup) : "settled";
|
|
3253
|
+
}
|
|
3254
|
+
return selectedSession?.status ?? "";
|
|
3255
|
+
};
|
|
3256
|
+
const sectionEmpty = (section) => section === "projects" ? selectedProject === null : section === "worktrees" ? selectedGroup === null : selectedSession === null;
|
|
3257
|
+
const paneTitle = (column, width) => fit(columnTitle(column), Math.max(6, width - 6));
|
|
3258
|
+
const renderSection = (entry) => {
|
|
3259
|
+
const inner = Math.max(4, layout.sidebarWidth - 2);
|
|
3260
|
+
if (!entry.expanded) {
|
|
3261
|
+
return /* @__PURE__ */ jsx2(
|
|
3262
|
+
Pane,
|
|
3263
|
+
{
|
|
3264
|
+
title: paneTitle(entry.section, layout.sidebarWidth),
|
|
3265
|
+
focused: false,
|
|
3266
|
+
height: entry.height,
|
|
3267
|
+
children: /* @__PURE__ */ jsx2(
|
|
3268
|
+
SummaryRow,
|
|
3269
|
+
{
|
|
3270
|
+
name: breadcrumbValue(entry.section),
|
|
3271
|
+
fact: sectionFact(entry.section),
|
|
3272
|
+
width: inner,
|
|
3273
|
+
empty: sectionEmpty(entry.section)
|
|
3274
|
+
}
|
|
3275
|
+
)
|
|
3276
|
+
},
|
|
3277
|
+
entry.section
|
|
3278
|
+
);
|
|
3279
|
+
}
|
|
3280
|
+
const focused = focus === entry.section;
|
|
3281
|
+
switch (entry.section) {
|
|
3282
|
+
case "projects":
|
|
3283
|
+
return /* @__PURE__ */ jsx2(
|
|
3284
|
+
Pane,
|
|
3285
|
+
{
|
|
3286
|
+
title: paneTitle("projects", layout.sidebarWidth),
|
|
3287
|
+
focused,
|
|
3288
|
+
grow: true,
|
|
3289
|
+
children: /* @__PURE__ */ jsxs2(
|
|
3290
|
+
"scrollbox",
|
|
3291
|
+
{
|
|
3292
|
+
ref: projectScrollRef,
|
|
3293
|
+
flexGrow: 1,
|
|
3294
|
+
flexShrink: 1,
|
|
3295
|
+
minHeight: 0,
|
|
3296
|
+
style: paneScrollStyle,
|
|
3297
|
+
children: [
|
|
3298
|
+
projectItems.map((item, index) => /* @__PURE__ */ jsx2(
|
|
3299
|
+
ProjectRow,
|
|
3300
|
+
{
|
|
3301
|
+
item,
|
|
3302
|
+
selected: index === projectIndex,
|
|
3303
|
+
width: inner
|
|
3304
|
+
},
|
|
3305
|
+
item.project.id
|
|
3306
|
+
)),
|
|
3307
|
+
data !== void 0 && projectItems.length === 0 ? /* @__PURE__ */ jsx2(EmptyNote, { text: "none adopted yet" }) : null
|
|
3308
|
+
]
|
|
3309
|
+
}
|
|
3310
|
+
)
|
|
3311
|
+
},
|
|
3312
|
+
entry.section
|
|
3313
|
+
);
|
|
3314
|
+
case "worktrees":
|
|
3315
|
+
return /* @__PURE__ */ jsx2(
|
|
3316
|
+
Pane,
|
|
3317
|
+
{
|
|
3318
|
+
title: paneTitle("worktrees", layout.sidebarWidth),
|
|
3319
|
+
focused,
|
|
3320
|
+
grow: true,
|
|
3321
|
+
children: /* @__PURE__ */ jsxs2(
|
|
3322
|
+
"scrollbox",
|
|
3323
|
+
{
|
|
3324
|
+
ref: worktreeScrollRef,
|
|
3325
|
+
flexGrow: 1,
|
|
3326
|
+
flexShrink: 1,
|
|
3327
|
+
minHeight: 0,
|
|
3328
|
+
style: paneScrollStyle,
|
|
3329
|
+
children: [
|
|
3330
|
+
worktreeGroups.map((group, index) => /* @__PURE__ */ jsx2(
|
|
3331
|
+
WorktreeRow,
|
|
3332
|
+
{
|
|
3333
|
+
group,
|
|
3334
|
+
selected: index === worktreeIndex,
|
|
3335
|
+
width: inner
|
|
3336
|
+
},
|
|
3337
|
+
group.key
|
|
3338
|
+
)),
|
|
3339
|
+
data !== void 0 && worktreeGroups.length === 0 ? /* @__PURE__ */ jsx2(EmptyNote, { text: "no worktrees \u2014 w starts one" }) : null
|
|
3340
|
+
]
|
|
3341
|
+
}
|
|
3342
|
+
)
|
|
3343
|
+
},
|
|
3344
|
+
entry.section
|
|
3345
|
+
);
|
|
3346
|
+
case "sessions":
|
|
3347
|
+
return /* @__PURE__ */ jsx2(
|
|
3348
|
+
Pane,
|
|
3349
|
+
{
|
|
3350
|
+
title: paneTitle("sessions", layout.sidebarWidth),
|
|
3351
|
+
focused,
|
|
3352
|
+
grow: true,
|
|
3353
|
+
children: /* @__PURE__ */ jsxs2(
|
|
3354
|
+
"scrollbox",
|
|
3355
|
+
{
|
|
3356
|
+
ref: sessionScrollRef,
|
|
3357
|
+
flexGrow: 1,
|
|
3358
|
+
flexShrink: 1,
|
|
3359
|
+
minHeight: 0,
|
|
3360
|
+
style: paneScrollStyle,
|
|
3361
|
+
children: [
|
|
3362
|
+
sessionItems.map((item, index) => /* @__PURE__ */ jsx2(
|
|
3363
|
+
SessionRow,
|
|
3364
|
+
{
|
|
3365
|
+
item,
|
|
3366
|
+
selected: index === sessionIndex,
|
|
3367
|
+
width: inner
|
|
3368
|
+
},
|
|
3369
|
+
item.session.id
|
|
3370
|
+
)),
|
|
3371
|
+
data !== void 0 && sessionItems.length === 0 ? /* @__PURE__ */ jsx2(
|
|
3372
|
+
EmptyNote,
|
|
3373
|
+
{
|
|
3374
|
+
text: selectedGroup === null ? "no worktree selected" : "no sessions \u2014 n starts one"
|
|
3375
|
+
}
|
|
3376
|
+
) : null,
|
|
3377
|
+
loadFailure === null ? null : /* @__PURE__ */ jsx2("text", { height: 1, fg: INK_2, bg: "transparent", children: ` ${loadFailure} \u2014 retrying` })
|
|
3378
|
+
]
|
|
3379
|
+
}
|
|
3380
|
+
)
|
|
3381
|
+
},
|
|
3382
|
+
entry.section
|
|
3383
|
+
);
|
|
3384
|
+
}
|
|
3385
|
+
};
|
|
3386
|
+
const renderDetail = () => /* @__PURE__ */ jsx2(Pane, { title: paneTitle("detail", detailWidth + 2), focused: focus === "detail", grow: true, children: selectedItem === null ? /* @__PURE__ */ jsx2(EmptyNote, { text: "no session selected \u2014 n starts one" }) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
3387
|
+
/* @__PURE__ */ jsx2(SessionFacts, { group: selectedGroup, item: selectedItem, rows: factRows }),
|
|
3388
|
+
showFactRule ? /* @__PURE__ */ jsx2("text", { height: 1, bg: "transparent", fg: FAINT, children: ` ${"\u2500".repeat(Math.max(4, detailWidth - 4))}` }) : null,
|
|
3389
|
+
/* @__PURE__ */ jsx2("box", { flexGrow: 1, flexShrink: 1, minHeight: 0, flexDirection: "column", children: previewSessionId === null ? /* @__PURE__ */ jsx2(EmptyNote, { text: "provisioning \u2014 no record yet" }) : selectedSession?.harness === "shell" ? /* @__PURE__ */ jsx2(EmptyNote, { text: "shell \u2014 no conversation record; a attaches if live" }) : transcript.isPending ? /* @__PURE__ */ jsx2(EmptyNote, { text: "reading the record\u2026" }) : transcript.error === null ? previewView.lines.length === 0 ? /* @__PURE__ */ jsx2(EmptyNote, { text: "no conversation recorded yet" }) : previewView.lines.map((line, index) => /* @__PURE__ */ jsx2(PreviewRow, { line }, `${index}-${line.kind}`)) : /* @__PURE__ */ jsx2("text", { height: 1, fg: INK_2, bg: "transparent", children: ` could not read the record \u2014 ${errorText(transcript.error)}` }) })
|
|
3390
|
+
] }) });
|
|
3391
|
+
const offNav = layout.offscreen.filter(isNavSection);
|
|
3392
|
+
const detailOffscreen = layout.offscreen.includes("detail");
|
|
3393
|
+
return /* @__PURE__ */ jsxs2("box", { flexGrow: 1, flexDirection: "column", backgroundColor: CANVAS, children: [
|
|
2624
3394
|
/* @__PURE__ */ jsxs2("box", { height: 1, flexDirection: "row", justifyContent: "space-between", children: [
|
|
2625
3395
|
/* @__PURE__ */ jsxs2("text", { height: 1, bg: "transparent", children: [
|
|
2626
3396
|
/* @__PURE__ */ jsx2("span", { fg: INK, children: " mend" }),
|
|
@@ -2635,6 +3405,13 @@ attached \xB7 ${session.harness} \xB7 ${short} \xB7 detach: Ctrl+]
|
|
|
2635
3405
|
liveTotal,
|
|
2636
3406
|
" live"
|
|
2637
3407
|
] }),
|
|
3408
|
+
gate.count() > 0 ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
3409
|
+
/* @__PURE__ */ jsx2("span", { fg: FAINT, children: " \xB7 " }),
|
|
3410
|
+
/* @__PURE__ */ jsxs2("span", { fg: INK_2, children: [
|
|
3411
|
+
gate.count(),
|
|
3412
|
+
" starting"
|
|
3413
|
+
] })
|
|
3414
|
+
] }) : null,
|
|
2638
3415
|
shareState === "off" ? null : /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
2639
3416
|
/* @__PURE__ */ jsx2("span", { fg: FAINT, children: " \xB7 " }),
|
|
2640
3417
|
/* @__PURE__ */ jsx2("span", { fg: shareState === "connected" ? MUTED : FAINT, children: shareState === "connected" ? "agent shared" : "agent share reconnecting" })
|
|
@@ -2642,70 +3419,69 @@ attached \xB7 ${session.harness} \xB7 ${short} \xB7 detach: Ctrl+]
|
|
|
2642
3419
|
] }),
|
|
2643
3420
|
/* @__PURE__ */ jsx2("text", { height: 1, fg: FAINT, bg: "transparent", children: `${ctx.config.url} ` })
|
|
2644
3421
|
] }),
|
|
3422
|
+
!layout.breadcrumb ? null : /* @__PURE__ */ jsxs2("text", { height: 1, bg: "transparent", children: [
|
|
3423
|
+
/* @__PURE__ */ jsx2("span", { fg: FAINT, children: " " }),
|
|
3424
|
+
offNav.map((column, index) => /* @__PURE__ */ jsxs2("span", { children: [
|
|
3425
|
+
index > 0 ? /* @__PURE__ */ jsx2("span", { fg: FAINT, children: " \u25B8 " }) : null,
|
|
3426
|
+
/* @__PURE__ */ jsx2("span", { fg: index === offNav.length - 1 ? INK_2 : MUTED, children: fit(breadcrumbValue(column), 28) })
|
|
3427
|
+
] }, column)),
|
|
3428
|
+
/* @__PURE__ */ jsx2("span", { fg: FAINT, children: detailOffscreen ? `${offNav.length > 0 ? " \xB7 " : ""}record hidden \xB7 \u2192 opens it` : " \u25B8" })
|
|
3429
|
+
] }),
|
|
2645
3430
|
/* @__PURE__ */ jsxs2("box", { flexGrow: 1, flexShrink: 1, minHeight: 0, flexDirection: "row", children: [
|
|
2646
|
-
|
|
2647
|
-
"
|
|
2648
|
-
{
|
|
2649
|
-
ref: projectScrollRef,
|
|
2650
|
-
flexGrow: 1,
|
|
2651
|
-
flexShrink: 1,
|
|
2652
|
-
minHeight: 0,
|
|
2653
|
-
style: paneScrollStyle,
|
|
2654
|
-
children: [
|
|
2655
|
-
projectItems.map((item, index) => /* @__PURE__ */ jsx2(
|
|
2656
|
-
ProjectRow,
|
|
2657
|
-
{
|
|
2658
|
-
item,
|
|
2659
|
-
selected: index === projectIndex,
|
|
2660
|
-
nameWidth
|
|
2661
|
-
},
|
|
2662
|
-
item.project.id
|
|
2663
|
-
)),
|
|
2664
|
-
data !== void 0 && projectItems.length === 0 ? /* @__PURE__ */ jsx2("text", { height: 1, fg: FAINT, bg: "transparent", children: " none adopted yet" }) : null
|
|
2665
|
-
]
|
|
2666
|
-
}
|
|
2667
|
-
) }) : null,
|
|
2668
|
-
/* @__PURE__ */ jsx2(Pane, { title: sessionsTitle, focused: focus === "sessions", grow: true, children: /* @__PURE__ */ jsxs2(
|
|
2669
|
-
"scrollbox",
|
|
3431
|
+
layout.sidebarWidth === 0 ? null : /* @__PURE__ */ jsx2(
|
|
3432
|
+
"box",
|
|
2670
3433
|
{
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
flexShrink: 1,
|
|
3434
|
+
width: layout.sidebarWidth,
|
|
3435
|
+
flexShrink: 0,
|
|
2674
3436
|
minHeight: 0,
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
(row, index) => row.kind === "worktree" ? /* @__PURE__ */ jsx2(
|
|
2679
|
-
WorktreeHeaderRow,
|
|
2680
|
-
{
|
|
2681
|
-
group: row.group,
|
|
2682
|
-
selected: index === rowIndex
|
|
2683
|
-
},
|
|
2684
|
-
rowKeyOf(row)
|
|
2685
|
-
) : /* @__PURE__ */ jsx2(
|
|
2686
|
-
SessionChildRow,
|
|
2687
|
-
{
|
|
2688
|
-
item: row.item,
|
|
2689
|
-
selected: index === rowIndex
|
|
2690
|
-
},
|
|
2691
|
-
rowKeyOf(row)
|
|
2692
|
-
)
|
|
2693
|
-
),
|
|
2694
|
-
data !== void 0 && rows.length === 0 ? /* @__PURE__ */ jsx2("text", { height: 1, fg: FAINT, bg: "transparent", children: " no sessions yet \u2014 n starts one" }) : null,
|
|
2695
|
-
loadFailure !== null ? /* @__PURE__ */ jsx2("text", { height: 1, fg: INK_2, bg: "transparent", children: ` ${loadFailure} \u2014 retrying` }) : null
|
|
2696
|
-
]
|
|
3437
|
+
flexDirection: "column",
|
|
3438
|
+
backgroundColor: "transparent",
|
|
3439
|
+
children: layout.sections.map(renderSection)
|
|
2697
3440
|
}
|
|
2698
|
-
)
|
|
3441
|
+
),
|
|
3442
|
+
detailWidth === 0 ? null : renderDetail()
|
|
2699
3443
|
] }),
|
|
2700
|
-
picker
|
|
3444
|
+
picker === null ? null : /* @__PURE__ */ jsx2(
|
|
2701
3445
|
"box",
|
|
2702
3446
|
{
|
|
3447
|
+
position: "absolute",
|
|
3448
|
+
zIndex: 12,
|
|
3449
|
+
left: Math.max(1, Math.floor((terminalCols - Math.min(74, terminalCols - 4)) / 2)),
|
|
3450
|
+
top: Math.max(1, Math.floor((terminalRows - (2 + pickerItems.length)) / 2)),
|
|
3451
|
+
width: Math.min(74, terminalCols - 4),
|
|
3452
|
+
height: 2 + pickerItems.length,
|
|
2703
3453
|
border: true,
|
|
2704
3454
|
borderStyle: "rounded",
|
|
2705
|
-
borderColor:
|
|
3455
|
+
borderColor: ACCENT,
|
|
3456
|
+
title: ` ${pickerTitle} `,
|
|
3457
|
+
titleAlignment: "left",
|
|
3458
|
+
backgroundColor: SURFACE,
|
|
3459
|
+
flexDirection: "column",
|
|
3460
|
+
children: pickerItems.map((item, index) => /* @__PURE__ */ jsx2(
|
|
3461
|
+
HarnessRow,
|
|
3462
|
+
{
|
|
3463
|
+
item,
|
|
3464
|
+
selected: index === pickerIndex,
|
|
3465
|
+
background: SURFACE
|
|
3466
|
+
},
|
|
3467
|
+
String(item.harness)
|
|
3468
|
+
))
|
|
3469
|
+
}
|
|
3470
|
+
),
|
|
3471
|
+
editing === null ? null : /* @__PURE__ */ jsx2(
|
|
3472
|
+
"box",
|
|
3473
|
+
{
|
|
3474
|
+
position: "absolute",
|
|
3475
|
+
zIndex: 12,
|
|
3476
|
+
left: Math.max(1, Math.floor((terminalCols - Math.min(74, terminalCols - 4)) / 2)),
|
|
3477
|
+
top: Math.max(1, Math.floor((terminalRows - 3) / 2)),
|
|
3478
|
+
width: Math.min(74, terminalCols - 4),
|
|
3479
|
+
border: true,
|
|
3480
|
+
borderStyle: "rounded",
|
|
3481
|
+
borderColor: ACCENT,
|
|
2706
3482
|
title: ` label \u2014 ${editing.harness} ${editing.id.slice(0, 8)} `,
|
|
2707
3483
|
titleAlignment: "left",
|
|
2708
|
-
backgroundColor:
|
|
3484
|
+
backgroundColor: SURFACE,
|
|
2709
3485
|
height: 3,
|
|
2710
3486
|
flexShrink: 0,
|
|
2711
3487
|
children: /* @__PURE__ */ jsx2(
|
|
@@ -2714,8 +3490,8 @@ attached \xB7 ${session.harness} \xB7 ${short} \xB7 detach: Ctrl+]
|
|
|
2714
3490
|
focused: true,
|
|
2715
3491
|
value: editing.label ?? "",
|
|
2716
3492
|
placeholder: "a few words for what this session is doing (empty clears)",
|
|
2717
|
-
backgroundColor:
|
|
2718
|
-
focusedBackgroundColor:
|
|
3493
|
+
backgroundColor: SURFACE,
|
|
3494
|
+
focusedBackgroundColor: SURFACE,
|
|
2719
3495
|
textColor: INK,
|
|
2720
3496
|
focusedTextColor: INK,
|
|
2721
3497
|
placeholderColor: FAINT,
|
|
@@ -2727,8 +3503,8 @@ attached \xB7 ${session.harness} \xB7 ${short} \xB7 detach: Ctrl+]
|
|
|
2727
3503
|
}
|
|
2728
3504
|
)
|
|
2729
3505
|
}
|
|
2730
|
-
)
|
|
2731
|
-
adoptOffer
|
|
3506
|
+
),
|
|
3507
|
+
adoptOffer === null ? null : /* @__PURE__ */ jsxs2(
|
|
2732
3508
|
"box",
|
|
2733
3509
|
{
|
|
2734
3510
|
position: "absolute",
|
|
@@ -2739,7 +3515,7 @@ attached \xB7 ${session.harness} \xB7 ${short} \xB7 detach: Ctrl+]
|
|
|
2739
3515
|
height: 7,
|
|
2740
3516
|
border: true,
|
|
2741
3517
|
borderStyle: "rounded",
|
|
2742
|
-
borderColor:
|
|
3518
|
+
borderColor: ACCENT,
|
|
2743
3519
|
title: " adopt this repository URL? ",
|
|
2744
3520
|
titleAlignment: "left",
|
|
2745
3521
|
backgroundColor: SURFACE,
|
|
@@ -2755,15 +3531,15 @@ attached \xB7 ${session.harness} \xB7 ${short} \xB7 detach: Ctrl+]
|
|
|
2755
3531
|
/* @__PURE__ */ jsx2("span", { fg: FAINT, children: " auth " }),
|
|
2756
3532
|
ADOPT_AUTH_MODES.map((candidate, index) => /* @__PURE__ */ jsxs2("span", { children: [
|
|
2757
3533
|
index > 0 ? /* @__PURE__ */ jsx2("span", { fg: FAINT, children: " \xB7 " }) : null,
|
|
2758
|
-
/* @__PURE__ */ jsx2("span", { fg: index === adoptOffer.modeIndex ?
|
|
3534
|
+
/* @__PURE__ */ jsx2("span", { fg: index === adoptOffer.modeIndex ? ACCENT : FAINT, children: index === adoptOffer.modeIndex ? `\u25B8 ${candidate.label}` : candidate.label })
|
|
2759
3535
|
] }, candidate.mode))
|
|
2760
3536
|
] }),
|
|
2761
3537
|
/* @__PURE__ */ jsx2("text", { height: 1, bg: SURFACE, fg: FAINT, children: ` ${ADOPT_AUTH_MODES[adoptOffer.modeIndex]?.hint ?? ""}` }),
|
|
2762
3538
|
/* @__PURE__ */ jsx2("text", { height: 1, bg: SURFACE, fg: FAINT, children: " enter adopt \xB7 \u2190\u2192 auth mode \xB7 esc not now" })
|
|
2763
3539
|
]
|
|
2764
3540
|
}
|
|
2765
|
-
)
|
|
2766
|
-
creating
|
|
3541
|
+
),
|
|
3542
|
+
creating === null ? null : /* @__PURE__ */ jsxs2(
|
|
2767
3543
|
"box",
|
|
2768
3544
|
{
|
|
2769
3545
|
position: "absolute",
|
|
@@ -2774,7 +3550,7 @@ attached \xB7 ${session.harness} \xB7 ${short} \xB7 detach: Ctrl+]
|
|
|
2774
3550
|
height: creatingHeight,
|
|
2775
3551
|
border: true,
|
|
2776
3552
|
borderStyle: "rounded",
|
|
2777
|
-
borderColor:
|
|
3553
|
+
borderColor: ACCENT,
|
|
2778
3554
|
title: ` new worktree \u2014 ${selectedProject?.project.name ?? "project"} `,
|
|
2779
3555
|
titleAlignment: "left",
|
|
2780
3556
|
backgroundColor: SURFACE,
|
|
@@ -2784,7 +3560,7 @@ attached \xB7 ${session.harness} \xB7 ${short} \xB7 detach: Ctrl+]
|
|
|
2784
3560
|
/* @__PURE__ */ jsxs2("text", { height: 1, bg: SURFACE, children: [
|
|
2785
3561
|
/* @__PURE__ */ jsx2(Gutter, { selected: creating.step === "name" }),
|
|
2786
3562
|
/* @__PURE__ */ jsx2("span", { fg: FAINT, children: "name " }),
|
|
2787
|
-
creating.step
|
|
3563
|
+
creating.step === "name" ? null : /* @__PURE__ */ jsx2("span", { fg: INK, children: creating.name === "" ? "auto" : creating.name }),
|
|
2788
3564
|
creating.joins ? /* @__PURE__ */ jsx2("span", { fg: FAINT, children: " \xB7 joins the existing worktree" }) : null
|
|
2789
3565
|
] }),
|
|
2790
3566
|
creating.step === "name" ? /* @__PURE__ */ jsx2(
|
|
@@ -2816,9 +3592,9 @@ attached \xB7 ${session.harness} \xB7 ${short} \xB7 detach: Ctrl+]
|
|
|
2816
3592
|
/* @__PURE__ */ jsxs2("text", { height: 1, bg: SURFACE, children: [
|
|
2817
3593
|
/* @__PURE__ */ jsx2(Gutter, { selected: creating.step === "base" }),
|
|
2818
3594
|
/* @__PURE__ */ jsx2("span", { fg: FAINT, children: "base " }),
|
|
2819
|
-
creating.step
|
|
3595
|
+
creating.step === "base" ? null : /* @__PURE__ */ jsx2("span", { fg: creating.step === "name" && !creating.joins ? FAINT : INK, children: creating.joins ? "fixed by the existing worktree" : creating.base ?? "default branch" })
|
|
2820
3596
|
] }),
|
|
2821
|
-
creating.step === "base"
|
|
3597
|
+
creating.step === "base" && creating.branches !== null ? /* @__PURE__ */ jsx2(
|
|
2822
3598
|
"input",
|
|
2823
3599
|
{
|
|
2824
3600
|
focused: true,
|
|
@@ -2845,7 +3621,17 @@ attached \xB7 ${session.harness} \xB7 ${short} \xB7 detach: Ctrl+]
|
|
|
2845
3621
|
const branch = creating.joins || creating.branches === null ? void 0 : filterBranches(creating.branches, creating.query)[index];
|
|
2846
3622
|
const active = creating.step === "base";
|
|
2847
3623
|
if (branch === void 0) {
|
|
2848
|
-
|
|
3624
|
+
const notice = creating.joins ? null : baseStepNotice(creating);
|
|
3625
|
+
return /* @__PURE__ */ jsx2(
|
|
3626
|
+
"text",
|
|
3627
|
+
{
|
|
3628
|
+
height: 1,
|
|
3629
|
+
bg: SURFACE,
|
|
3630
|
+
fg: creating.branchError === null ? FAINT : ERROR,
|
|
3631
|
+
children: index === 0 && notice !== null ? ` ${fit(notice, 66)}` : " "
|
|
3632
|
+
},
|
|
3633
|
+
`slot-${index}`
|
|
3634
|
+
);
|
|
2849
3635
|
}
|
|
2850
3636
|
const highlighted = active && index === creating.baseIndex;
|
|
2851
3637
|
return /* @__PURE__ */ jsx2(
|
|
@@ -2859,7 +3645,7 @@ attached \xB7 ${session.harness} \xB7 ${short} \xB7 detach: Ctrl+]
|
|
|
2859
3645
|
/* @__PURE__ */ jsx2(Gutter, { selected: highlighted }),
|
|
2860
3646
|
/* @__PURE__ */ jsx2("span", { fg: active ? INK : FAINT, children: branch.name.slice(0, 40).padEnd(41) }),
|
|
2861
3647
|
/* @__PURE__ */ jsx2("span", { fg: FAINT, children: branch.sha.slice(0, 8).padEnd(10) }),
|
|
2862
|
-
/* @__PURE__ */ jsx2("span", { fg: active ? MUTED : FAINT, children:
|
|
3648
|
+
/* @__PURE__ */ jsx2("span", { fg: active ? MUTED : FAINT, children: shortAge(branch.committedAt).padEnd(6) }),
|
|
2863
3649
|
branch.isDefault ? /* @__PURE__ */ jsx2("span", { fg: FAINT, children: "default" }) : null
|
|
2864
3650
|
] })
|
|
2865
3651
|
},
|
|
@@ -2891,13 +3677,13 @@ attached \xB7 ${session.harness} \xB7 ${short} \xB7 detach: Ctrl+]
|
|
|
2891
3677
|
})
|
|
2892
3678
|
]
|
|
2893
3679
|
}
|
|
2894
|
-
)
|
|
2895
|
-
/* @__PURE__ */ jsx2(StatusLine, { busy, busyStarted, status }),
|
|
3680
|
+
),
|
|
3681
|
+
loadFailure === null ? /* @__PURE__ */ jsx2(StatusLine, { busy, busyStarted, status }) : /* @__PURE__ */ jsx2("text", { height: 1, fg: ERROR, bg: "transparent", children: ` could not load workbench \u2014 ${loadFailure} \xB7 retrying` }),
|
|
2896
3682
|
/* @__PURE__ */ jsx2("text", { height: 1, fg: FAINT, bg: "transparent", children: footerText })
|
|
2897
3683
|
] });
|
|
2898
3684
|
};
|
|
2899
3685
|
var runDashboard = async (ctx) => {
|
|
2900
|
-
const renderer = await createCliRenderer({ exitOnCtrlC: false });
|
|
3686
|
+
const renderer = await createCliRenderer({ exitOnCtrlC: false, backgroundColor: CANVAS });
|
|
2901
3687
|
const queryClient = new QueryClient();
|
|
2902
3688
|
const controller = new AbortController();
|
|
2903
3689
|
let eventTimer = null;
|
|
@@ -2948,7 +3734,7 @@ var runDashboard = async (ctx) => {
|
|
|
2948
3734
|
} catch {
|
|
2949
3735
|
if (controller.signal.aborted) return;
|
|
2950
3736
|
}
|
|
2951
|
-
pendingFamilies.add("workbench").add("review");
|
|
3737
|
+
pendingFamilies.add("workbench").add("review").add("transcript");
|
|
2952
3738
|
if (eventTimer === null && !controller.signal.aborted) {
|
|
2953
3739
|
eventTimer = setTimeout(flushInvalidations, 250);
|
|
2954
3740
|
}
|