@sealant/mend 0.36.0-next.684 → 0.36.0-next.721
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-DHM7VXTR.js → chunk-GF5A6W2Y.js} +12 -1
- package/dist/{dashboard-OBFWHSPD.js → dashboard-MPO6H5YF.js} +823 -176
- package/dist/main.js +1221 -84
- package/man/mend-accounts.1 +1 -1
- package/man/mend-adopt.1 +1 -1
- 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-pi.1 +1 -1
- package/man/mend-connect.1 +1 -1
- package/man/mend-continue.1 +1 -1
- package/man/mend-doctor.1 +1 -1
- package/man/mend-dotfiles-repo.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 +1 -1
- package/man/mend-folder-list.1 +1 -1
- package/man/mend-folder-push.1 +1 -1
- package/man/mend-folder-rm.1 +1 -1
- package/man/mend-git-author.1 +1 -1
- package/man/mend-help.1 +1 -1
- package/man/mend-invite.1 +1 -1
- 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-land.1 +1 -1
- package/man/mend-login.1 +1 -1
- package/man/mend-logout.1 +1 -1
- package/man/mend-logs.1 +1 -1
- package/man/mend-man.1 +1 -1
- package/man/mend-members.1 +1 -1
- package/man/mend-memory-import.1 +1 -1
- package/man/mend-memory-rm.1 +1 -1
- package/man/mend-memory-show.1 +1 -1
- package/man/mend-memory.1 +1 -1
- package/man/mend-models.1 +1 -1
- package/man/mend-operator-exposure.1 +2 -2
- package/man/mend-operator-gate.1 +1 -1
- package/man/mend-operator-grant-owner.1 +1 -1
- package/man/mend-operator-org-create.1 +1 -1
- package/man/mend-operator-org-invite-owner.1 +1 -1
- package/man/mend-operator-org-list.1 +1 -1
- package/man/mend-operator-org-rename.1 +1 -1
- package/man/mend-operator-reset-link.1 +1 -1
- package/man/mend-pair.1 +1 -1
- package/man/mend-projects.1 +1 -1
- package/man/mend-pull.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-secrets-add.1 +1 -1
- package/man/mend-secrets-rm.1 +1 -1
- package/man/mend-secrets.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 +37 -6
- 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 +1 -1
- 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-snake.1 +2 -2
- package/man/mend-ssh-keys-remove.1 +1 -1
- package/man/mend-ssh-keys.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 +3 -1
- package/man/mend-uninstall.1 +1 -1
- package/man/mend-version.1 +1 -1
- package/man/mend-wait.1 +1 -1
- package/man/mend-workspace-replace.1 +1 -1
- package/man/mend-worktrees-rm.1 +1 -1
- package/man/mend-worktrees.1 +1 -1
- package/man/mend.1 +2 -2
- package/package.json +2 -2
|
@@ -17,6 +17,8 @@ import {
|
|
|
17
17
|
normalizeProjectName,
|
|
18
18
|
pendingId,
|
|
19
19
|
repositoryCloneUrlIssue,
|
|
20
|
+
serviceViewHostPort,
|
|
21
|
+
serviceViewStatus,
|
|
20
22
|
servicesHoldLine,
|
|
21
23
|
sessionWorkspaceLines,
|
|
22
24
|
withoutLaunchPhase,
|
|
@@ -24,7 +26,7 @@ import {
|
|
|
24
26
|
workspaceReadsOf,
|
|
25
27
|
worktreeJoinLine,
|
|
26
28
|
wrapWords
|
|
27
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-GF5A6W2Y.js";
|
|
28
30
|
|
|
29
31
|
// src/dashboard.tsx
|
|
30
32
|
import { CliRenderEvents, createCliRenderer } from "@opentui/core";
|
|
@@ -60,6 +62,15 @@ var submitDashboardAdoption = async (api, input) => {
|
|
|
60
62
|
};
|
|
61
63
|
|
|
62
64
|
// src/dashboard-model.ts
|
|
65
|
+
var serviceRowOf = (view) => ({
|
|
66
|
+
id: view.service.id,
|
|
67
|
+
sessionId: view.service.sessionId,
|
|
68
|
+
label: view.service.name,
|
|
69
|
+
status: serviceViewStatus(view),
|
|
70
|
+
workspacePort: view.service.workspacePort,
|
|
71
|
+
protocol: view.service.transport,
|
|
72
|
+
hostPort: serviceViewHostPort(view)
|
|
73
|
+
});
|
|
63
74
|
var worktreeName = (branch) => branch.startsWith("mend/") ? branch.slice("mend/".length) : branch;
|
|
64
75
|
var worktreeDisplayName = (session) => {
|
|
65
76
|
const name = worktreeName(session.branch);
|
|
@@ -76,7 +87,7 @@ var fetchWorkbench = async (ctx) => {
|
|
|
76
87
|
ctx.api("GET", "/services")
|
|
77
88
|
]);
|
|
78
89
|
const servicesBySession = /* @__PURE__ */ new Map();
|
|
79
|
-
for (const service of services) {
|
|
90
|
+
for (const service of services.map(serviceRowOf)) {
|
|
80
91
|
const bucket = servicesBySession.get(service.sessionId) ?? [];
|
|
81
92
|
bucket.push(service);
|
|
82
93
|
servicesBySession.set(service.sessionId, bucket);
|
|
@@ -205,7 +216,7 @@ var pseudoGroup = (session, item) => ({
|
|
|
205
216
|
live: LIVE_STATUSES.has(session.status) ? 1 : 0,
|
|
206
217
|
annotation: item(session).annotation
|
|
207
218
|
});
|
|
208
|
-
var deriveHarnesses = (resuming) => {
|
|
219
|
+
var deriveHarnesses = (resuming, joining = null) => {
|
|
209
220
|
if (resuming !== null) {
|
|
210
221
|
const others = Object.keys(HARNESS_COMMANDS).filter((h) => h !== resuming.harness);
|
|
211
222
|
return [
|
|
@@ -223,11 +234,12 @@ var deriveHarnesses = (resuming) => {
|
|
|
223
234
|
)
|
|
224
235
|
];
|
|
225
236
|
}
|
|
237
|
+
const where = joining === null ? "new worktree" : `joins ${joining}`;
|
|
226
238
|
return Object.keys(HARNESS_COMMANDS).map(
|
|
227
239
|
(harness) => ({
|
|
228
240
|
harness,
|
|
229
241
|
label: harness,
|
|
230
|
-
hint: harness === "shell" ?
|
|
242
|
+
hint: harness === "shell" ? `a plain bash session \xB7 ${where}, recorded` : `mend ${harness} \xB7 ${where}, recorded session`
|
|
231
243
|
})
|
|
232
244
|
);
|
|
233
245
|
};
|
|
@@ -487,8 +499,35 @@ var advanceFromBase = (current) => {
|
|
|
487
499
|
var COLUMNS = ["projects", "worktrees", "sessions", "detail"];
|
|
488
500
|
var NAV_SECTIONS = ["projects", "worktrees", "sessions"];
|
|
489
501
|
var isNavSection = (column) => column !== "detail";
|
|
502
|
+
var PANE_NUMBER = {
|
|
503
|
+
projects: "1",
|
|
504
|
+
worktrees: "2",
|
|
505
|
+
sessions: "3",
|
|
506
|
+
detail: "0"
|
|
507
|
+
};
|
|
508
|
+
var paneForDigit = (digit) => COLUMNS.find((column) => PANE_NUMBER[column] === digit) ?? null;
|
|
509
|
+
var numberedTitle = (column, title) => `[${PANE_NUMBER[column]}] ${title}`;
|
|
510
|
+
var cyclePane = (focus, delta) => {
|
|
511
|
+
const at = COLUMNS.indexOf(focus);
|
|
512
|
+
if (at === -1) return "sessions";
|
|
513
|
+
const next = ((at + delta) % COLUMNS.length + COLUMNS.length) % COLUMNS.length;
|
|
514
|
+
return COLUMNS[next] ?? focus;
|
|
515
|
+
};
|
|
516
|
+
var backFrom = (focus, lastNav) => focus === "detail" ? lastNav : stepColumn(focus, -1);
|
|
517
|
+
var SCREEN_MODES = ["normal", "half", "full"];
|
|
518
|
+
var stepScreenMode = (mode, delta) => {
|
|
519
|
+
const at = SCREEN_MODES.indexOf(mode);
|
|
520
|
+
const next = ((at + delta) % SCREEN_MODES.length + SCREEN_MODES.length) % SCREEN_MODES.length;
|
|
521
|
+
return SCREEN_MODES[next] ?? mode;
|
|
522
|
+
};
|
|
523
|
+
var matchesFilter = (query, ...fields) => {
|
|
524
|
+
const needle = query.trim().toLowerCase();
|
|
525
|
+
if (needle === "") return true;
|
|
526
|
+
return fields.some((field) => field !== null && field.toLowerCase().includes(needle));
|
|
527
|
+
};
|
|
490
528
|
var expandedSection = (focus, lastNav) => isNavSection(focus) ? focus : lastNav;
|
|
491
529
|
var SIDEBAR_SHARE = 0.25;
|
|
530
|
+
var HALF_SIDEBAR_SHARE = 0.5;
|
|
492
531
|
var MIN_SIDEBAR_WIDTH = 24;
|
|
493
532
|
var MIN_DETAIL_WIDTH = 48;
|
|
494
533
|
var SPLIT_MIN_WIDTH = MIN_SIDEBAR_WIDTH + MIN_DETAIL_WIDTH;
|
|
@@ -504,13 +543,11 @@ var stackSections = (body, open) => {
|
|
|
504
543
|
(section) => section === open ? { section, expanded: true, height: room, rows: room - 2 } : { section, expanded: false, height: MIN_SECTION_HEIGHT, rows: 1 }
|
|
505
544
|
);
|
|
506
545
|
};
|
|
507
|
-
var planLayout = (width, height, focus, lastNav) => {
|
|
508
|
-
const split = width >= SPLIT_MIN_WIDTH;
|
|
546
|
+
var planLayout = (width, height, focus, lastNav, mode = "normal") => {
|
|
547
|
+
const split = mode !== "full" && width >= SPLIT_MIN_WIDTH;
|
|
509
548
|
const navVisible = split || isNavSection(focus);
|
|
510
|
-
const
|
|
511
|
-
|
|
512
|
-
Math.min(width - MIN_DETAIL_WIDTH, Math.round(width * SIDEBAR_SHARE))
|
|
513
|
-
) : navVisible ? Math.max(4, width) : 0;
|
|
549
|
+
const share = mode === "half" ? HALF_SIDEBAR_SHARE : SIDEBAR_SHARE;
|
|
550
|
+
const sidebarOuter = split ? Math.max(MIN_SIDEBAR_WIDTH, Math.min(width - MIN_DETAIL_WIDTH, Math.round(width * share))) : navVisible ? Math.max(4, width) : 0;
|
|
514
551
|
const detailOuter = split ? width - sidebarOuter : navVisible ? 0 : Math.max(4, width);
|
|
515
552
|
const build = (body2, breadcrumb) => {
|
|
516
553
|
const sections = navVisible ? stackSections(body2, expandedSection(focus, lastNav)) : [];
|
|
@@ -593,6 +630,11 @@ var worktreeJoinRows = (data, projectId, worktree, viewerId, width) => {
|
|
|
593
630
|
const line = worktreeJoinLine(detail.sessions, id, viewerId);
|
|
594
631
|
return line === null ? [] : wrapWords(line, width);
|
|
595
632
|
};
|
|
633
|
+
var KEY_GROUP_TITLE = {
|
|
634
|
+
navigate: "move around",
|
|
635
|
+
session: "act on the selection",
|
|
636
|
+
dashboard: "the dashboard"
|
|
637
|
+
};
|
|
596
638
|
var KEY_BINDINGS = [
|
|
597
639
|
{
|
|
598
640
|
verb: "moveUp",
|
|
@@ -603,36 +645,123 @@ var KEY_BINDINGS = [
|
|
|
603
645
|
worktrees: "\u2191\u2193 move",
|
|
604
646
|
sessions: "\u2191\u2193 move",
|
|
605
647
|
detail: "\u2191\u2193 scroll"
|
|
606
|
-
}
|
|
648
|
+
},
|
|
649
|
+
help: "up the list, or back through the record",
|
|
650
|
+
group: "navigate"
|
|
651
|
+
},
|
|
652
|
+
{
|
|
653
|
+
verb: "moveDown",
|
|
654
|
+
keys: ["down", "j"],
|
|
655
|
+
shift: false,
|
|
656
|
+
hints: {},
|
|
657
|
+
help: "down the list, or toward the newest line",
|
|
658
|
+
group: "navigate"
|
|
659
|
+
},
|
|
660
|
+
{ verb: "pageUp", keys: ["pageup"], shift: false, hints: {}, help: "up ten", group: "navigate" },
|
|
661
|
+
{
|
|
662
|
+
verb: "pageDown",
|
|
663
|
+
keys: ["pagedown"],
|
|
664
|
+
shift: false,
|
|
665
|
+
hints: {},
|
|
666
|
+
help: "down ten",
|
|
667
|
+
group: "navigate"
|
|
668
|
+
},
|
|
669
|
+
{
|
|
670
|
+
verb: "jumpProjects",
|
|
671
|
+
keys: ["1"],
|
|
672
|
+
shift: false,
|
|
673
|
+
hints: {},
|
|
674
|
+
help: "jump to [1] projects",
|
|
675
|
+
group: "navigate"
|
|
676
|
+
},
|
|
677
|
+
{
|
|
678
|
+
verb: "jumpWorktrees",
|
|
679
|
+
keys: ["2"],
|
|
680
|
+
shift: false,
|
|
681
|
+
hints: {},
|
|
682
|
+
help: "jump to [2] worktrees",
|
|
683
|
+
group: "navigate"
|
|
684
|
+
},
|
|
685
|
+
{
|
|
686
|
+
verb: "jumpSessions",
|
|
687
|
+
keys: ["3"],
|
|
688
|
+
shift: false,
|
|
689
|
+
hints: {},
|
|
690
|
+
help: "jump to [3] sessions",
|
|
691
|
+
group: "navigate"
|
|
692
|
+
},
|
|
693
|
+
{
|
|
694
|
+
verb: "jumpDetail",
|
|
695
|
+
keys: ["0"],
|
|
696
|
+
shift: false,
|
|
697
|
+
hints: {},
|
|
698
|
+
help: "jump to [0], the session pane",
|
|
699
|
+
group: "navigate"
|
|
607
700
|
},
|
|
608
|
-
{ verb: "moveDown", keys: ["down", "j"], shift: false, hints: {} },
|
|
609
|
-
{ verb: "pageUp", keys: ["pageup"], shift: false, hints: {} },
|
|
610
|
-
{ verb: "pageDown", keys: ["pagedown"], shift: false, hints: {} },
|
|
611
701
|
{
|
|
612
702
|
verb: "columnRight",
|
|
613
|
-
keys: ["return", "linefeed", "l", "right"
|
|
703
|
+
keys: ["return", "linefeed", "l", "right"],
|
|
614
704
|
shift: false,
|
|
615
|
-
hints: { projects: "\u2192 worktrees", worktrees: "\u2190\u2192 panes", sessions: "\u2190\u2192 panes" }
|
|
705
|
+
hints: { projects: "\u2192 worktrees", worktrees: "\u2190\u2192 panes", sessions: "\u2190\u2192 panes" },
|
|
706
|
+
help: "drill in: projects, worktrees, sessions, then [0]",
|
|
707
|
+
group: "navigate"
|
|
616
708
|
},
|
|
617
709
|
{
|
|
618
710
|
verb: "columnLeft",
|
|
619
711
|
keys: ["left", "h", "-", "backspace"],
|
|
620
712
|
shift: false,
|
|
621
|
-
hints: { detail: "\u2190 sessions" }
|
|
713
|
+
hints: { detail: "\u2190 sessions" },
|
|
714
|
+
help: "one pane back up the hierarchy",
|
|
715
|
+
group: "navigate"
|
|
716
|
+
},
|
|
717
|
+
{
|
|
718
|
+
verb: "nextPane",
|
|
719
|
+
keys: ["tab"],
|
|
720
|
+
shift: false,
|
|
721
|
+
hints: {},
|
|
722
|
+
help: "next pane, round to the first after the last",
|
|
723
|
+
group: "navigate"
|
|
724
|
+
},
|
|
725
|
+
{ verb: "prevPane", keys: ["backtab"], shift: "any", hints: {} },
|
|
726
|
+
{
|
|
727
|
+
verb: "prevPane",
|
|
728
|
+
keys: ["tab"],
|
|
729
|
+
shift: true,
|
|
730
|
+
hints: {},
|
|
731
|
+
help: "previous pane, round to the last before the first",
|
|
732
|
+
group: "navigate"
|
|
733
|
+
},
|
|
734
|
+
{
|
|
735
|
+
verb: "back",
|
|
736
|
+
keys: ["escape"],
|
|
737
|
+
shift: false,
|
|
738
|
+
hints: {},
|
|
739
|
+
help: "clear the filter, else back to where you came from",
|
|
740
|
+
group: "navigate"
|
|
741
|
+
},
|
|
742
|
+
{
|
|
743
|
+
verb: "filter",
|
|
744
|
+
keys: ["/"],
|
|
745
|
+
shift: false,
|
|
746
|
+
hints: {},
|
|
747
|
+
help: "filter this list; enter keeps the filter, esc clears it",
|
|
748
|
+
group: "navigate"
|
|
622
749
|
},
|
|
623
|
-
{ verb: "columnLeft", keys: ["backtab"], shift: "any", hints: {} },
|
|
624
|
-
{ verb: "columnLeft", keys: ["tab"], shift: true, hints: {} },
|
|
625
750
|
{
|
|
626
751
|
verb: "attach",
|
|
627
752
|
keys: ["a"],
|
|
628
753
|
shift: false,
|
|
629
|
-
hints: { worktrees: "a attach", sessions: "a attach", detail: "a attach" }
|
|
754
|
+
hints: { worktrees: "a attach", sessions: "a attach", detail: "a attach" },
|
|
755
|
+
help: "attach this terminal to the live session",
|
|
756
|
+
group: "session"
|
|
630
757
|
},
|
|
631
758
|
{
|
|
632
759
|
verb: "resume",
|
|
633
760
|
keys: ["r"],
|
|
634
761
|
shift: false,
|
|
635
|
-
hints: { worktrees: "r resume", sessions: "r resume", detail: "r resume" }
|
|
762
|
+
hints: { worktrees: "r resume", sessions: "r resume", detail: "r resume" },
|
|
763
|
+
help: "resume the settled session, on a harness you pick",
|
|
764
|
+
group: "session"
|
|
636
765
|
},
|
|
637
766
|
{
|
|
638
767
|
verb: "newSession",
|
|
@@ -642,60 +771,166 @@ var KEY_BINDINGS = [
|
|
|
642
771
|
projects: "n/w new worktree",
|
|
643
772
|
worktrees: "n new session",
|
|
644
773
|
sessions: "n new session"
|
|
645
|
-
}
|
|
774
|
+
},
|
|
775
|
+
help: "another session in this worktree, or a new worktree",
|
|
776
|
+
group: "session"
|
|
646
777
|
},
|
|
647
778
|
{
|
|
648
779
|
verb: "newWorktree",
|
|
649
780
|
keys: ["w"],
|
|
650
781
|
shift: false,
|
|
651
|
-
hints: { worktrees: "w new worktree", sessions: "w new worktree" }
|
|
782
|
+
hints: { worktrees: "w new worktree", sessions: "w new worktree" },
|
|
783
|
+
help: "a new worktree in this project",
|
|
784
|
+
group: "session"
|
|
652
785
|
},
|
|
653
786
|
{
|
|
654
787
|
verb: "stop",
|
|
655
788
|
keys: ["k"],
|
|
656
789
|
shift: true,
|
|
657
|
-
hints: { worktrees: "\u21E7K stop all", sessions: "\u21E7K stop" }
|
|
790
|
+
hints: { worktrees: "\u21E7K stop all", sessions: "\u21E7K stop" },
|
|
791
|
+
help: "stop it, or every live session in [2] (press twice)",
|
|
792
|
+
group: "session"
|
|
658
793
|
},
|
|
659
794
|
{ verb: "stop", keys: ["x"], shift: false, hints: {} },
|
|
660
795
|
{
|
|
661
796
|
verb: "remove",
|
|
662
797
|
keys: ["d"],
|
|
663
798
|
shift: true,
|
|
664
|
-
hints: { worktrees: "\u21E7D remove worktree", sessions: "\u21E7D remove" }
|
|
799
|
+
hints: { worktrees: "\u21E7D remove worktree", sessions: "\u21E7D remove" },
|
|
800
|
+
help: "remove it, or the worktree from [2] (press twice)",
|
|
801
|
+
group: "session"
|
|
665
802
|
},
|
|
666
803
|
{
|
|
667
804
|
verb: "review",
|
|
668
805
|
keys: ["v"],
|
|
669
806
|
shift: false,
|
|
670
|
-
hints: { worktrees: "v review", sessions: "v review", detail: "v review" }
|
|
807
|
+
hints: { worktrees: "v review", sessions: "v review", detail: "v review" },
|
|
808
|
+
help: "review the change in this terminal",
|
|
809
|
+
group: "session"
|
|
671
810
|
},
|
|
672
811
|
{
|
|
673
812
|
verb: "rename",
|
|
674
813
|
keys: ["e"],
|
|
675
814
|
shift: false,
|
|
676
|
-
hints: { sessions: "e rename", detail: "e rename" }
|
|
815
|
+
hints: { sessions: "e rename", detail: "e rename" },
|
|
816
|
+
help: "label the session; empty clears it",
|
|
817
|
+
group: "session"
|
|
818
|
+
},
|
|
819
|
+
{
|
|
820
|
+
verb: "openWeb",
|
|
821
|
+
keys: ["o"],
|
|
822
|
+
shift: false,
|
|
823
|
+
hints: { sessions: "o web", detail: "o web" },
|
|
824
|
+
help: "open the session in the browser",
|
|
825
|
+
group: "session"
|
|
677
826
|
},
|
|
678
|
-
{ verb: "openWeb", keys: ["o"], shift: false, hints: { sessions: "o web", detail: "o web" } },
|
|
679
827
|
{
|
|
680
828
|
verb: "refresh",
|
|
681
829
|
keys: ["r"],
|
|
682
830
|
shift: true,
|
|
683
|
-
hints: { projects: "\u21E7R refresh", worktrees: "\u21E7R refresh", detail: "\u21E7R refresh" }
|
|
831
|
+
hints: { projects: "\u21E7R refresh", worktrees: "\u21E7R refresh", detail: "\u21E7R refresh" },
|
|
832
|
+
help: "read everything from the server again",
|
|
833
|
+
group: "dashboard"
|
|
834
|
+
},
|
|
835
|
+
{
|
|
836
|
+
verb: "nextScreenMode",
|
|
837
|
+
keys: ["+"],
|
|
838
|
+
shift: "any",
|
|
839
|
+
hints: {},
|
|
840
|
+
help: "screen mode: normal, half, full",
|
|
841
|
+
group: "dashboard"
|
|
842
|
+
},
|
|
843
|
+
{
|
|
844
|
+
verb: "prevScreenMode",
|
|
845
|
+
keys: ["_"],
|
|
846
|
+
shift: "any",
|
|
847
|
+
hints: {},
|
|
848
|
+
help: "screen mode, the other way",
|
|
849
|
+
group: "dashboard"
|
|
850
|
+
},
|
|
851
|
+
{
|
|
852
|
+
verb: "help",
|
|
853
|
+
keys: ["?"],
|
|
854
|
+
shift: "any",
|
|
855
|
+
hints: {},
|
|
856
|
+
help: "this list",
|
|
857
|
+
group: "dashboard",
|
|
858
|
+
footer: "? keys"
|
|
684
859
|
},
|
|
685
860
|
{
|
|
686
861
|
verb: "quit",
|
|
687
862
|
keys: ["q"],
|
|
688
863
|
shift: false,
|
|
689
|
-
hints: { projects: "q quit", worktrees: "q quit", sessions: "q quit", detail: "q quit" }
|
|
864
|
+
hints: { projects: "q quit", worktrees: "q quit", sessions: "q quit", detail: "q quit" },
|
|
865
|
+
help: "quit; sessions keep running",
|
|
866
|
+
group: "dashboard"
|
|
690
867
|
}
|
|
691
868
|
];
|
|
869
|
+
var SYMBOL_STROKES = /* @__PURE__ */ new Set(["?", "+", "_"]);
|
|
870
|
+
var strokeName = (key) => {
|
|
871
|
+
const sequence = key.sequence ?? "";
|
|
872
|
+
return SYMBOL_STROKES.has(sequence) ? sequence : key.name ?? "";
|
|
873
|
+
};
|
|
692
874
|
var verbForKey = (name, shift) => KEY_BINDINGS.find(
|
|
693
875
|
(binding) => binding.keys.includes(name) && (binding.shift === "any" || binding.shift === shift)
|
|
694
876
|
)?.verb ?? null;
|
|
877
|
+
var gameSwallows = (verb) => verb !== null;
|
|
695
878
|
var verbHints = (focus) => KEY_BINDINGS.flatMap((binding) => {
|
|
696
879
|
const hint = binding.hints[focus];
|
|
697
880
|
return hint === void 0 ? [] : [hint];
|
|
698
881
|
});
|
|
882
|
+
var FOOTER_TAIL = KEY_BINDINGS.flatMap(
|
|
883
|
+
(binding) => binding.footer === void 0 ? [] : [binding.footer]
|
|
884
|
+
);
|
|
885
|
+
var footerHints = (focus, width) => {
|
|
886
|
+
const tail = FOOTER_TAIL.map((hint) => ` \xB7 ${hint}`).join("");
|
|
887
|
+
return `${fitHints(verbHints(focus), Math.max(1, width - tail.length))}${tail}`;
|
|
888
|
+
};
|
|
889
|
+
var KEY_LABEL = {
|
|
890
|
+
up: "\u2191",
|
|
891
|
+
down: "\u2193",
|
|
892
|
+
left: "\u2190",
|
|
893
|
+
right: "\u2192",
|
|
894
|
+
return: "enter",
|
|
895
|
+
tab: "tab",
|
|
896
|
+
backtab: "\u21E7tab",
|
|
897
|
+
escape: "esc",
|
|
898
|
+
backspace: "\u232B",
|
|
899
|
+
pageup: "PgUp",
|
|
900
|
+
pagedown: "PgDn"
|
|
901
|
+
};
|
|
902
|
+
var keyLabel = (key, shift) => {
|
|
903
|
+
const base = KEY_LABEL[key] ?? key;
|
|
904
|
+
return shift === true ? `\u21E7${base.length === 1 ? base.toUpperCase() : base}` : base;
|
|
905
|
+
};
|
|
906
|
+
var helpSections = (focus) => {
|
|
907
|
+
const rows = /* @__PURE__ */ new Map();
|
|
908
|
+
for (const binding of KEY_BINDINGS) {
|
|
909
|
+
const entry = rows.get(binding.verb) ?? { keys: [], help: "", group: null };
|
|
910
|
+
for (const key of binding.keys) {
|
|
911
|
+
if (key === "linefeed") continue;
|
|
912
|
+
const label = keyLabel(key, binding.shift);
|
|
913
|
+
if (!entry.keys.includes(label)) entry.keys.push(label);
|
|
914
|
+
}
|
|
915
|
+
if (entry.help === "" && binding.help !== void 0) entry.help = binding.help;
|
|
916
|
+
if (entry.group === null && binding.group !== void 0) entry.group = binding.group;
|
|
917
|
+
rows.set(binding.verb, entry);
|
|
918
|
+
}
|
|
919
|
+
const all = [...rows].map(([verb, entry]) => ({
|
|
920
|
+
verb,
|
|
921
|
+
keys: entry.keys.join(" "),
|
|
922
|
+
help: entry.help,
|
|
923
|
+
group: entry.group ?? "dashboard",
|
|
924
|
+
here: KEY_BINDINGS.some(
|
|
925
|
+
(binding) => binding.verb === verb && binding.hints[focus] !== void 0
|
|
926
|
+
)
|
|
927
|
+
}));
|
|
928
|
+
const groups = ["navigate", "session", "dashboard"];
|
|
929
|
+
return groups.flatMap((group) => {
|
|
930
|
+
const ordered = all.filter((row) => row.group === group);
|
|
931
|
+
return ordered.length === 0 ? [] : [{ title: KEY_GROUP_TITLE[group], rows: ordered.map(({ group: _group, ...row }) => row) }];
|
|
932
|
+
});
|
|
933
|
+
};
|
|
699
934
|
|
|
700
935
|
// src/dashboard-preview.ts
|
|
701
936
|
import { stripVTControlCharacters } from "node:util";
|
|
@@ -2146,9 +2381,6 @@ function ReviewScreen({
|
|
|
2146
2381
|
);
|
|
2147
2382
|
}
|
|
2148
2383
|
|
|
2149
|
-
// src/snake.tsx
|
|
2150
|
-
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
2151
|
-
|
|
2152
2384
|
// src/snake-game.ts
|
|
2153
2385
|
var OPPOSITE = {
|
|
2154
2386
|
up: "down",
|
|
@@ -2230,94 +2462,294 @@ var render = (game) => {
|
|
|
2230
2462
|
return rows.map((row) => row.join(""));
|
|
2231
2463
|
};
|
|
2232
2464
|
|
|
2465
|
+
// src/snake-play.ts
|
|
2466
|
+
var TICK_MS = 140;
|
|
2467
|
+
var COUNTDOWN_STEP_MS = 1e3;
|
|
2468
|
+
var COUNTDOWN = { kind: "countdown", step: 3 };
|
|
2469
|
+
var RUNNING = { kind: "running" };
|
|
2470
|
+
var PAUSED = { kind: "paused" };
|
|
2471
|
+
var READY = { kind: "ready" };
|
|
2472
|
+
var newSnakePlay = (width, height, random) => ({
|
|
2473
|
+
game: newSnakeGame(width, height, random),
|
|
2474
|
+
clock: READY,
|
|
2475
|
+
focused: false,
|
|
2476
|
+
width,
|
|
2477
|
+
height
|
|
2478
|
+
});
|
|
2479
|
+
var countDown = (play, random) => ({
|
|
2480
|
+
...play,
|
|
2481
|
+
game: play.game.over ? newSnakeGame(play.width, play.height, random) : play.game,
|
|
2482
|
+
clock: COUNTDOWN
|
|
2483
|
+
});
|
|
2484
|
+
var reduceSnake = (play, event, random) => {
|
|
2485
|
+
switch (event.type) {
|
|
2486
|
+
case "focus":
|
|
2487
|
+
return play.focused ? play : { ...countDown(play, random), focused: true };
|
|
2488
|
+
case "blur": {
|
|
2489
|
+
if (!play.focused) return play;
|
|
2490
|
+
const clock = play.clock.kind === "running" ? PAUSED : play.clock.kind === "countdown" ? READY : play.clock;
|
|
2491
|
+
return { ...play, focused: false, clock };
|
|
2492
|
+
}
|
|
2493
|
+
case "resize": {
|
|
2494
|
+
if (event.width === play.width && event.height === play.height) return play;
|
|
2495
|
+
return {
|
|
2496
|
+
...play,
|
|
2497
|
+
width: event.width,
|
|
2498
|
+
height: event.height,
|
|
2499
|
+
game: newSnakeGame(event.width, event.height, random),
|
|
2500
|
+
clock: play.clock.kind === "running" ? COUNTDOWN : play.clock
|
|
2501
|
+
};
|
|
2502
|
+
}
|
|
2503
|
+
case "count": {
|
|
2504
|
+
if (!play.focused || play.clock.kind !== "countdown") return play;
|
|
2505
|
+
const { step } = play.clock;
|
|
2506
|
+
return {
|
|
2507
|
+
...play,
|
|
2508
|
+
clock: step === 0 ? RUNNING : { kind: "countdown", step: step === 3 ? 2 : step === 2 ? 1 : 0 }
|
|
2509
|
+
};
|
|
2510
|
+
}
|
|
2511
|
+
case "tick": {
|
|
2512
|
+
if (!play.focused || play.clock.kind !== "running") return play;
|
|
2513
|
+
const game = tick(play.game, random);
|
|
2514
|
+
return { ...play, game, clock: game.over ? READY : play.clock };
|
|
2515
|
+
}
|
|
2516
|
+
case "steer": {
|
|
2517
|
+
if (!play.focused) return play;
|
|
2518
|
+
if (play.game.over) return countDown(play, random);
|
|
2519
|
+
const game = turn(play.game, event.direction);
|
|
2520
|
+
return play.clock.kind === "running" || play.clock.kind === "countdown" ? { ...play, game } : { ...play, game, clock: COUNTDOWN };
|
|
2521
|
+
}
|
|
2522
|
+
case "togglePause":
|
|
2523
|
+
if (!play.focused) return play;
|
|
2524
|
+
return play.clock.kind === "running" || play.clock.kind === "countdown" ? { ...play, clock: PAUSED } : countDown(play, random);
|
|
2525
|
+
case "play":
|
|
2526
|
+
if (!play.focused || play.clock.kind === "running" || play.clock.kind === "countdown") {
|
|
2527
|
+
return play;
|
|
2528
|
+
}
|
|
2529
|
+
return countDown(play, random);
|
|
2530
|
+
}
|
|
2531
|
+
};
|
|
2532
|
+
var KEY_DIRECTIONS = {
|
|
2533
|
+
up: "up",
|
|
2534
|
+
down: "down",
|
|
2535
|
+
left: "left",
|
|
2536
|
+
right: "right",
|
|
2537
|
+
k: "up",
|
|
2538
|
+
j: "down",
|
|
2539
|
+
h: "left",
|
|
2540
|
+
l: "right"
|
|
2541
|
+
};
|
|
2542
|
+
var snakeKey = (name) => {
|
|
2543
|
+
const direction = KEY_DIRECTIONS[name];
|
|
2544
|
+
if (direction !== void 0) return { type: "steer", direction };
|
|
2545
|
+
if (name === "space" || name === "p") return { type: "togglePause" };
|
|
2546
|
+
if (name === "return" || name === "linefeed") return { type: "play" };
|
|
2547
|
+
if (name === "escape" || name === "q") return { type: "leave" };
|
|
2548
|
+
return null;
|
|
2549
|
+
};
|
|
2550
|
+
var snakeHints = (play) => play.game.over ? "enter/arrows again \xB7 esc/q leave" : play.clock.kind === "paused" || play.clock.kind === "ready" ? "enter/space resume \xB7 esc/q leave" : "\u2190\u2191\u2193\u2192/hjkl steer \xB7 space/p pause \xB7 esc/q leave";
|
|
2551
|
+
var SEGMENTS = {
|
|
2552
|
+
"1": "bc",
|
|
2553
|
+
"2": "abdeg",
|
|
2554
|
+
"3": "abcdg",
|
|
2555
|
+
G: "acdef",
|
|
2556
|
+
O: "abcdef"
|
|
2557
|
+
};
|
|
2558
|
+
var glyphPixels = (char, length, thickness) => {
|
|
2559
|
+
const lit = new Set(SEGMENTS[char] ?? "");
|
|
2560
|
+
const on = (...segments) => segments.some((segment) => lit.has(segment));
|
|
2561
|
+
const width = length + 2 * thickness;
|
|
2562
|
+
const height = 2 * length + 3 * thickness;
|
|
2563
|
+
const column = (x) => x < thickness ? "left" : x < thickness + length ? "middle" : "right";
|
|
2564
|
+
const row = (y) => y < thickness ? "top" : y < thickness + length ? "upper" : y < 2 * thickness + length ? "centre" : y < 2 * thickness + 2 * length ? "lower" : "bottom";
|
|
2565
|
+
const pixel = (x, y) => {
|
|
2566
|
+
const where = `${column(x)} ${row(y)}`;
|
|
2567
|
+
switch (where) {
|
|
2568
|
+
case "middle top":
|
|
2569
|
+
return on("a");
|
|
2570
|
+
case "middle centre":
|
|
2571
|
+
return on("g");
|
|
2572
|
+
case "middle bottom":
|
|
2573
|
+
return on("d");
|
|
2574
|
+
case "left upper":
|
|
2575
|
+
return on("f");
|
|
2576
|
+
case "right upper":
|
|
2577
|
+
return on("b");
|
|
2578
|
+
case "left lower":
|
|
2579
|
+
return on("e");
|
|
2580
|
+
case "right lower":
|
|
2581
|
+
return on("c");
|
|
2582
|
+
case "left top":
|
|
2583
|
+
return on("a", "f");
|
|
2584
|
+
case "right top":
|
|
2585
|
+
return on("a", "b");
|
|
2586
|
+
case "left centre":
|
|
2587
|
+
return on("f", "e", "g");
|
|
2588
|
+
case "right centre":
|
|
2589
|
+
return on("b", "c", "g");
|
|
2590
|
+
case "left bottom":
|
|
2591
|
+
return on("e", "d");
|
|
2592
|
+
case "right bottom":
|
|
2593
|
+
return on("c", "d");
|
|
2594
|
+
default:
|
|
2595
|
+
return false;
|
|
2596
|
+
}
|
|
2597
|
+
};
|
|
2598
|
+
return Array.from(
|
|
2599
|
+
{ length: height },
|
|
2600
|
+
(_row, y) => Array.from({ length: width }, (_cell, x) => pixel(x, y))
|
|
2601
|
+
);
|
|
2602
|
+
};
|
|
2603
|
+
var halfBlocks = (pixels) => {
|
|
2604
|
+
const rows = [];
|
|
2605
|
+
for (let y = 0; y < pixels.length; y += 2) {
|
|
2606
|
+
const top = pixels[y] ?? [];
|
|
2607
|
+
const bottom = pixels[y + 1] ?? [];
|
|
2608
|
+
rows.push(
|
|
2609
|
+
top.map((upper, x) => {
|
|
2610
|
+
const lower = bottom[x] === true;
|
|
2611
|
+
return upper && lower ? "\u2588" : upper ? "\u2580" : lower ? "\u2584" : " ";
|
|
2612
|
+
}).join("")
|
|
2613
|
+
);
|
|
2614
|
+
}
|
|
2615
|
+
return rows;
|
|
2616
|
+
};
|
|
2617
|
+
var drawLabel = (label, length, thickness) => {
|
|
2618
|
+
const glyphs = [...label].map((char) => glyphPixels(char, length, thickness));
|
|
2619
|
+
const gap = Array.from({ length: thickness }, () => false);
|
|
2620
|
+
const height = 2 * length + 3 * thickness;
|
|
2621
|
+
const pixels = Array.from(
|
|
2622
|
+
{ length: height },
|
|
2623
|
+
(_, y) => glyphs.flatMap((glyph, index) => index === 0 ? glyph[y] : [...gap, ...glyph[y]])
|
|
2624
|
+
);
|
|
2625
|
+
const lit = (x) => pixels.some((line) => line[x] === true);
|
|
2626
|
+
const width = pixels[0]?.length ?? 0;
|
|
2627
|
+
let first = 0;
|
|
2628
|
+
while (first < width && !lit(first)) first += 1;
|
|
2629
|
+
let last = width - 1;
|
|
2630
|
+
while (last > first && !lit(last)) last -= 1;
|
|
2631
|
+
return halfBlocks(pixels.map((line) => line.slice(first, last + 1)));
|
|
2632
|
+
};
|
|
2633
|
+
var countdownLabel = (step) => step === 0 ? "GO" : String(step);
|
|
2634
|
+
var MAX_SEGMENT = 12;
|
|
2635
|
+
var countdownArt = (label, cols, rows) => {
|
|
2636
|
+
const marginRows = Math.floor(rows / 8);
|
|
2637
|
+
const marginCols = cols >= 12 ? 1 : 0;
|
|
2638
|
+
const fitRows = rows - 2 * marginRows;
|
|
2639
|
+
const fitCols = cols - 2 * marginCols;
|
|
2640
|
+
for (let length = MAX_SEGMENT; length >= 1; length -= 1) {
|
|
2641
|
+
const thickness = length >= 5 ? 2 : 1;
|
|
2642
|
+
const art = drawLabel(label, length, thickness);
|
|
2643
|
+
if (art.length <= fitRows && (art[0]?.length ?? 0) <= fitCols) return art;
|
|
2644
|
+
}
|
|
2645
|
+
return label.length <= cols ? [label] : [label.slice(0, Math.max(1, cols))];
|
|
2646
|
+
};
|
|
2647
|
+
var overlayArt = (board, art) => {
|
|
2648
|
+
const height = board.length;
|
|
2649
|
+
const width = [...board[0] ?? ""].length;
|
|
2650
|
+
const artWidth = Math.max(0, ...art.map((line) => [...line].length));
|
|
2651
|
+
const top = Math.floor((height - art.length) / 2);
|
|
2652
|
+
const left = Math.floor((width - artWidth) / 2);
|
|
2653
|
+
return board.map((line, y) => {
|
|
2654
|
+
const artLine = art[y - top];
|
|
2655
|
+
const artCells = artLine === void 0 ? [] : [...artLine];
|
|
2656
|
+
return [...line].map((cell, x) => {
|
|
2657
|
+
const char = artCells[x - left];
|
|
2658
|
+
return { board: cell, art: char === void 0 || char === " " ? null : char };
|
|
2659
|
+
});
|
|
2660
|
+
});
|
|
2661
|
+
};
|
|
2662
|
+
|
|
2233
2663
|
// src/snake.tsx
|
|
2664
|
+
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
2234
2665
|
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "@opentui/react/jsx-runtime";
|
|
2235
|
-
var TICK_MS = 140;
|
|
2236
2666
|
var useSnake = (options) => {
|
|
2237
|
-
const { width, height,
|
|
2238
|
-
const [
|
|
2239
|
-
|
|
2667
|
+
const { width, height, focused } = options;
|
|
2668
|
+
const [stored, setPlay] = useState2(() => newSnakePlay(width, height, Math.random));
|
|
2669
|
+
let play = reduceSnake(stored, { type: "resize", width, height }, Math.random);
|
|
2670
|
+
if (play.focused !== focused) {
|
|
2671
|
+
play = reduceSnake(play, { type: focused ? "focus" : "blur" }, Math.random);
|
|
2672
|
+
}
|
|
2673
|
+
if (play !== stored) setPlay(play);
|
|
2674
|
+
const { clock } = play;
|
|
2675
|
+
const step = clock.kind === "countdown" ? clock.step : null;
|
|
2240
2676
|
useEffect2(() => {
|
|
2241
|
-
if (
|
|
2242
|
-
|
|
2243
|
-
|
|
2677
|
+
if (clock.kind === "countdown") {
|
|
2678
|
+
const timer2 = setTimeout(
|
|
2679
|
+
() => setPlay((current) => reduceSnake(current, { type: "count" }, Math.random)),
|
|
2680
|
+
COUNTDOWN_STEP_MS
|
|
2681
|
+
);
|
|
2682
|
+
return () => clearTimeout(timer2);
|
|
2244
2683
|
}
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
return () => clearInterval(timer);
|
|
2250
|
-
}, [enabled, paused]);
|
|
2251
|
-
const steer = (direction) => {
|
|
2252
|
-
setPaused(false);
|
|
2253
|
-
setGame(
|
|
2254
|
-
(current) => current.over ? newSnakeGame(current.width, current.height, Math.random) : turn(current, direction)
|
|
2684
|
+
if (clock.kind !== "running") return;
|
|
2685
|
+
const timer = setInterval(
|
|
2686
|
+
() => setPlay((current) => reduceSnake(current, { type: "tick" }, Math.random)),
|
|
2687
|
+
TICK_MS
|
|
2255
2688
|
);
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2689
|
+
return () => clearInterval(timer);
|
|
2690
|
+
}, [clock.kind, step]);
|
|
2691
|
+
const send = (event) => setPlay((current) => reduceSnake(current, event, Math.random));
|
|
2692
|
+
return { play, send };
|
|
2259
2693
|
};
|
|
2260
|
-
var SnakeHeading = ({
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
/* @__PURE__ */
|
|
2268
|
-
|
|
2269
|
-
/* @__PURE__ */ jsx2("span", { fg: INK, children: "play snake while you wait" }),
|
|
2270
|
-
/* @__PURE__ */ jsx2("span", { fg: FAINT, children: " \xB7 score " }),
|
|
2271
|
-
/* @__PURE__ */ jsx2("span", { fg: game.over ? MUTED : ACCENT, children: String(game.score) }),
|
|
2272
|
-
state === null ? null : /* @__PURE__ */ jsx2("span", { fg: FAINT, children: ` \xB7 ${state}` })
|
|
2273
|
-
] }),
|
|
2274
|
-
/* @__PURE__ */ jsx2("text", { height: 1, bg: "transparent", fg: FAINT, children: ` ${hint}` })
|
|
2694
|
+
var SnakeHeading = ({ play }) => {
|
|
2695
|
+
const { game, clock } = play;
|
|
2696
|
+
const state = game.over ? "over" : clock.kind === "paused" ? "paused" : null;
|
|
2697
|
+
return /* @__PURE__ */ jsxs2("text", { height: 1, bg: "transparent", children: [
|
|
2698
|
+
/* @__PURE__ */ jsx2("span", { children: " " }),
|
|
2699
|
+
/* @__PURE__ */ jsx2("span", { fg: INK, children: "play snake while you wait" }),
|
|
2700
|
+
/* @__PURE__ */ jsx2("span", { fg: FAINT, children: " \xB7 score " }),
|
|
2701
|
+
/* @__PURE__ */ jsx2("span", { fg: game.over ? MUTED : ACCENT, children: String(game.score) }),
|
|
2702
|
+
state === null ? null : /* @__PURE__ */ jsx2("span", { fg: FAINT, children: ` \xB7 ${state}` })
|
|
2275
2703
|
] });
|
|
2276
2704
|
};
|
|
2705
|
+
var SNAKE_IDLE = "enter plays";
|
|
2706
|
+
var snakeTooSmall = (short) => `make the terminal ${short ? "taller" : "wider"} to play snake`;
|
|
2707
|
+
var SNAKE_BEHIND_DIALOG = "the countdown starts when this dialog closes";
|
|
2708
|
+
var SnakeFooter = ({
|
|
2709
|
+
play,
|
|
2710
|
+
idle
|
|
2711
|
+
}) => /* @__PURE__ */ jsx2("text", { height: 1, bg: "transparent", fg: FAINT, children: ` ${play.focused ? snakeHints(play) : idle}` });
|
|
2712
|
+
var cellColor = (cell) => cell === "\u25C6" ? ACCENT : cell === "\u2588" ? GREEN : cell === "\u25CF" ? INK : FAINT;
|
|
2713
|
+
var SnakeRows = ({ play }) => {
|
|
2714
|
+
const { game, clock } = play;
|
|
2715
|
+
const board = render(game);
|
|
2716
|
+
if (clock.kind !== "countdown") {
|
|
2717
|
+
return /* @__PURE__ */ jsx2(Fragment2, { children: board.map((row, index) => /* @__PURE__ */ jsx2("text", { height: 1, bg: "transparent", children: [...row].map((cell, x) => /* @__PURE__ */ jsx2("span", { fg: cellColor(cell), children: cell === "\xB7" ? " " : cell }, x)) }, index)) });
|
|
2718
|
+
}
|
|
2719
|
+
const cells = overlayArt(
|
|
2720
|
+
board,
|
|
2721
|
+
countdownArt(countdownLabel(clock.step), game.width, game.height)
|
|
2722
|
+
);
|
|
2723
|
+
return /* @__PURE__ */ jsx2(Fragment2, { children: cells.map((row, index) => /* @__PURE__ */ jsx2("text", { height: 1, bg: "transparent", children: row.map(
|
|
2724
|
+
(cell, x) => cell.board !== "\xB7" || cell.art === null ? /* @__PURE__ */ jsx2("span", { fg: cellColor(cell.board), children: cell.board === "\xB7" ? " " : cell.board }, x) : /* @__PURE__ */ jsx2("span", { fg: ACCENT, children: cell.art }, x)
|
|
2725
|
+
) }, index)) });
|
|
2726
|
+
};
|
|
2277
2727
|
var SnakeBoard = ({
|
|
2278
2728
|
handle,
|
|
2279
|
-
|
|
2729
|
+
idle
|
|
2280
2730
|
}) => {
|
|
2281
|
-
const {
|
|
2282
|
-
const rows = render(game);
|
|
2731
|
+
const { play } = handle;
|
|
2283
2732
|
return /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
2284
|
-
/* @__PURE__ */ jsx2(
|
|
2285
|
-
SnakeHeading,
|
|
2286
|
-
{
|
|
2287
|
-
handle,
|
|
2288
|
-
hint: focused ? "arrows steer \xB7 space pauses \xB7 esc puts it away" : "focus this pane to play \xB7 esc puts it away"
|
|
2289
|
-
}
|
|
2290
|
-
),
|
|
2733
|
+
/* @__PURE__ */ jsx2(SnakeHeading, { play }),
|
|
2291
2734
|
/* @__PURE__ */ jsx2(
|
|
2292
2735
|
"box",
|
|
2293
2736
|
{
|
|
2294
2737
|
border: true,
|
|
2295
2738
|
borderStyle: "rounded",
|
|
2296
|
-
borderColor: focused ? ACCENT : RULE,
|
|
2297
|
-
|
|
2298
|
-
|
|
2739
|
+
borderColor: play.focused ? ACCENT : RULE,
|
|
2740
|
+
title: " snake ",
|
|
2741
|
+
titleAlignment: "left",
|
|
2742
|
+
width: play.game.width + 2,
|
|
2743
|
+
height: play.game.height + 2,
|
|
2299
2744
|
flexShrink: 0,
|
|
2300
2745
|
marginLeft: 2,
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
{
|
|
2304
|
-
fg: cell === "\u25C6" ? ACCENT : cell === "\u2588" ? GREEN : cell === "\u25CF" ? INK : FAINT,
|
|
2305
|
-
children: cell === "\xB7" ? " " : cell
|
|
2306
|
-
},
|
|
2307
|
-
x
|
|
2308
|
-
)) }, index))
|
|
2746
|
+
flexDirection: "column",
|
|
2747
|
+
children: /* @__PURE__ */ jsx2(SnakeRows, { play })
|
|
2309
2748
|
}
|
|
2310
|
-
)
|
|
2749
|
+
),
|
|
2750
|
+
/* @__PURE__ */ jsx2(SnakeFooter, { play, idle })
|
|
2311
2751
|
] });
|
|
2312
2752
|
};
|
|
2313
|
-
var SnakeRows = ({ game }) => /* @__PURE__ */ jsx2(Fragment2, { children: render(game).map((row, index) => /* @__PURE__ */ jsx2("text", { height: 1, bg: "transparent", children: [...row].map((cell, x) => /* @__PURE__ */ jsx2(
|
|
2314
|
-
"span",
|
|
2315
|
-
{
|
|
2316
|
-
fg: cell === "\u25C6" ? ACCENT : cell === "\u2588" ? GREEN : cell === "\u25CF" ? INK : FAINT,
|
|
2317
|
-
children: cell === "\xB7" ? " " : cell
|
|
2318
|
-
},
|
|
2319
|
-
x
|
|
2320
|
-
)) }, index)) });
|
|
2321
2753
|
|
|
2322
2754
|
// src/dashboard.tsx
|
|
2323
2755
|
import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs3 } from "@opentui/react/jsx-runtime";
|
|
@@ -2351,12 +2783,6 @@ var lastSessionWords = (group) => {
|
|
|
2351
2783
|
return [group.sessions.length === 0 ? `created ${ago}` : `last session created ${ago}`];
|
|
2352
2784
|
};
|
|
2353
2785
|
var fit = (text, width) => text.length <= width ? text : `${text.slice(0, Math.max(0, width - 1))}\u2026`;
|
|
2354
|
-
var COLUMN_TITLE = {
|
|
2355
|
-
projects: "projects",
|
|
2356
|
-
worktrees: "worktrees",
|
|
2357
|
-
sessions: "sessions",
|
|
2358
|
-
detail: "session"
|
|
2359
|
-
};
|
|
2360
2786
|
var WORKTREE_ROW_HEIGHT = 2;
|
|
2361
2787
|
var SESSION_ROW_HEIGHT = 2;
|
|
2362
2788
|
var Gutter = ({ selected }) => /* @__PURE__ */ jsx3("span", { fg: selected ? ACCENT : FAINT, children: selected ? "\u258C " : " " });
|
|
@@ -2470,11 +2896,12 @@ var SessionRow = ({
|
|
|
2470
2896
|
var HarnessRow = ({
|
|
2471
2897
|
item,
|
|
2472
2898
|
selected,
|
|
2473
|
-
background
|
|
2899
|
+
background,
|
|
2900
|
+
width
|
|
2474
2901
|
}) => /* @__PURE__ */ jsx3("box", { height: 1, flexShrink: 0, backgroundColor: selected ? WASH : background, children: /* @__PURE__ */ jsxs3("text", { height: 1, bg: selected ? WASH : background, children: [
|
|
2475
2902
|
/* @__PURE__ */ jsx3(Gutter, { selected }),
|
|
2476
2903
|
/* @__PURE__ */ jsx3("span", { fg: INK, children: item.label.padEnd(12) }),
|
|
2477
|
-
/* @__PURE__ */ jsx3("span", { fg: FAINT, children: item.hint })
|
|
2904
|
+
/* @__PURE__ */ jsx3("span", { fg: FAINT, children: fit(item.hint, Math.max(4, width - 2 - 12)) })
|
|
2478
2905
|
] }) });
|
|
2479
2906
|
var PREVIEW_GLYPH = {
|
|
2480
2907
|
user: "\u203A ",
|
|
@@ -2620,10 +3047,23 @@ var keepSpanVisible = (scroll, top, height, viewRows) => {
|
|
|
2620
3047
|
};
|
|
2621
3048
|
var PREVIEW_MAX_LINES = 300;
|
|
2622
3049
|
var SESSION_FACT_ROWS = 6;
|
|
3050
|
+
var SNAKE_CHROME_ROWS = 5;
|
|
3051
|
+
var SNAKE_MIN_BOARD_ROWS = 4;
|
|
3052
|
+
var SNAKE_MIN_BOARD_COLS = 8;
|
|
3053
|
+
var SNAKE_MIN_PANE_ROWS = SNAKE_CHROME_ROWS + SNAKE_MIN_BOARD_ROWS;
|
|
3054
|
+
var SNAKE_MIN_PANE_COLS = SNAKE_MIN_BOARD_COLS + 4;
|
|
2623
3055
|
var noShare = () => "off";
|
|
2624
3056
|
var NO_TUNNELS = [];
|
|
2625
3057
|
var noTunnels = () => NO_TUNNELS;
|
|
2626
|
-
var
|
|
3058
|
+
var NO_FILTERS = {
|
|
3059
|
+
projects: "",
|
|
3060
|
+
worktrees: "",
|
|
3061
|
+
sessions: ""
|
|
3062
|
+
};
|
|
3063
|
+
var App = ({
|
|
3064
|
+
ctx,
|
|
3065
|
+
onQuit
|
|
3066
|
+
}) => {
|
|
2627
3067
|
const renderer = useRenderer();
|
|
2628
3068
|
const queryClient = useQueryClient2();
|
|
2629
3069
|
const { data, failureReason } = useQuery2({
|
|
@@ -2633,8 +3073,13 @@ var App = ({ ctx, onQuit }) => {
|
|
|
2633
3073
|
retry: true,
|
|
2634
3074
|
retryDelay: 1e3
|
|
2635
3075
|
});
|
|
2636
|
-
const [
|
|
3076
|
+
const [columnFocus, setFocusState] = useState3("sessions");
|
|
2637
3077
|
const [lastNav, setLastNav] = useState3("sessions");
|
|
3078
|
+
const [screenMode, setScreenMode] = useState3("normal");
|
|
3079
|
+
const [filters, setFilters] = useState3(NO_FILTERS);
|
|
3080
|
+
const [filtering, setFiltering] = useState3(null);
|
|
3081
|
+
const [helpOpen, setHelpOpen] = useState3(false);
|
|
3082
|
+
const [helpOffset, setHelpOffset] = useState3(0);
|
|
2638
3083
|
const [projectKey, setProjectKey] = useState3(null);
|
|
2639
3084
|
const [worktreeKey, setWorktreeKey] = useState3(null);
|
|
2640
3085
|
const [sessionKey, setSessionKey] = useState3(null);
|
|
@@ -2661,12 +3106,16 @@ var App = ({ ctx, onQuit }) => {
|
|
|
2661
3106
|
const adoptOfferDecided = useRef2(false);
|
|
2662
3107
|
const [reviewing, setReviewing] = useState3(null);
|
|
2663
3108
|
const lockRef = useRef2(false);
|
|
3109
|
+
const [snakeFocus, setSnakeFocus] = useState3(null);
|
|
3110
|
+
const [snakeOverlay, setSnakeOverlay] = useState3(ctx.openSnake === true);
|
|
2664
3111
|
const [gate] = useState3(createLaunchGate);
|
|
2665
3112
|
const creatingRef = useRef2(creating);
|
|
2666
3113
|
creatingRef.current = creating;
|
|
2667
3114
|
const branchRequest = useRef2(0);
|
|
2668
3115
|
const { width: terminalCols, height: terminalRows } = useTerminalDimensions2();
|
|
2669
|
-
const projectItems = deriveProjects(data)
|
|
3116
|
+
const projectItems = deriveProjects(data).filter(
|
|
3117
|
+
(item) => matchesFilter(filters.projects, item.project.name)
|
|
3118
|
+
);
|
|
2670
3119
|
const homeProject = data === void 0 ? void 0 : matchProjectByCwd(data.projects, cwdFacts(ctx.cwd));
|
|
2671
3120
|
const projectIndexRaw = projectKey === null ? -1 : projectItems.findIndex((p) => p.project.id === projectKey);
|
|
2672
3121
|
const projectIndex = projectIndexRaw === -1 ? Math.max(
|
|
@@ -2674,20 +3123,38 @@ var App = ({ ctx, onQuit }) => {
|
|
|
2674
3123
|
homeProject === void 0 ? 0 : projectItems.findIndex((p) => p.project.id === homeProject.id)
|
|
2675
3124
|
) : projectIndexRaw;
|
|
2676
3125
|
const selectedProject = projectItems[projectIndex] ?? null;
|
|
2677
|
-
const worktreeGroups = deriveWorktrees(data, selectedProject?.project.id ?? null)
|
|
3126
|
+
const worktreeGroups = deriveWorktrees(data, selectedProject?.project.id ?? null).filter(
|
|
3127
|
+
(group) => matchesFilter(filters.worktrees, group.name, groupBaseLabel(group))
|
|
3128
|
+
);
|
|
2678
3129
|
const worktreeIndexRaw = worktreeKey === null ? -1 : worktreeGroups.findIndex((group) => group.key === worktreeKey);
|
|
2679
3130
|
const sessionWorktreeIndex = worktreeGroups.findIndex(
|
|
2680
3131
|
(group) => group.sessions.some((item) => item.session.id === sessionKey)
|
|
2681
3132
|
);
|
|
2682
3133
|
const worktreeIndex = worktreeIndexRaw === -1 ? Math.max(0, sessionWorktreeIndex) : worktreeIndexRaw;
|
|
2683
3134
|
const selectedGroup = worktreeGroups[worktreeIndex] ?? null;
|
|
2684
|
-
const sessionItems = selectedGroup?.sessions ?? []
|
|
3135
|
+
const sessionItems = (selectedGroup?.sessions ?? []).filter(
|
|
3136
|
+
(item) => matchesFilter(
|
|
3137
|
+
filters.sessions,
|
|
3138
|
+
sessionDisplayName(item.session),
|
|
3139
|
+
item.session.harness,
|
|
3140
|
+
item.session.status
|
|
3141
|
+
)
|
|
3142
|
+
);
|
|
2685
3143
|
const sessionIndexRaw = sessionKey === null ? -1 : sessionItems.findIndex((item) => item.session.id === sessionKey);
|
|
2686
3144
|
const sessionIndex = sessionIndexRaw === -1 ? 0 : sessionIndexRaw;
|
|
2687
3145
|
const selectedItem = sessionItems[sessionIndex] ?? null;
|
|
2688
3146
|
const selectedSession = selectedItem?.session ?? null;
|
|
2689
|
-
const pickerItems = picker === null ? [] : deriveHarnesses(picker.session);
|
|
2690
|
-
const
|
|
3147
|
+
const pickerItems = picker === null ? [] : deriveHarnesses(picker.session, picker.worktree?.name ?? null);
|
|
3148
|
+
const waiting = selectedSession !== null && (selectedSession.status === "starting" || isPendingId(selectedSession.id));
|
|
3149
|
+
const dialogOpen = picker !== null || editing !== null || creating !== null || adoptOffer !== null || reviewing !== null || helpOpen || filtering !== null;
|
|
3150
|
+
const gameLayout = planLayout(terminalCols, terminalRows, "detail", lastNav);
|
|
3151
|
+
const snakeFits = gameLayout.detailRows >= SNAKE_MIN_PANE_ROWS && gameLayout.detailWidth >= SNAKE_MIN_PANE_COLS;
|
|
3152
|
+
const overlayFits = terminalRows >= 12 && terminalCols >= 22;
|
|
3153
|
+
const overlayOpen = snakeOverlay && !dialogOpen;
|
|
3154
|
+
const overlayFocused = overlayOpen && overlayFits;
|
|
3155
|
+
const snakeFocused = snakeFocus !== null && waiting && snakeFits && !dialogOpen && !snakeOverlay;
|
|
3156
|
+
const focus = snakeFocused ? "detail" : columnFocus;
|
|
3157
|
+
const layout = planLayout(terminalCols, terminalRows, focus, lastNav, screenMode);
|
|
2691
3158
|
const detailWidth = layout.detailWidth;
|
|
2692
3159
|
const sectionRows = (section) => layout.sections.find((entry) => entry.section === section)?.rows ?? 0;
|
|
2693
3160
|
const previewSessionId = selectedSession !== null && !isPendingId(selectedSession.id) ? selectedSession.id : null;
|
|
@@ -2738,27 +3205,28 @@ var App = ({ ctx, onQuit }) => {
|
|
|
2738
3205
|
[transcript.data, detailWidth]
|
|
2739
3206
|
);
|
|
2740
3207
|
const [previewOffset, setPreviewOffset] = useState3(0);
|
|
2741
|
-
const
|
|
3208
|
+
const allFactRows = Math.max(
|
|
2742
3209
|
0,
|
|
2743
3210
|
Math.min(SESSION_FACT_ROWS + workspaceRows.length, layout.detailRows - 2)
|
|
2744
3211
|
);
|
|
3212
|
+
const factRows = waiting ? Math.max(0, Math.min(allFactRows, layout.detailRows - SNAKE_MIN_PANE_ROWS - 1)) : allFactRows;
|
|
2745
3213
|
const showFactRule = factRows > 0 && layout.detailRows - factRows > 1;
|
|
2746
|
-
const
|
|
3214
|
+
const snakeRoom = {
|
|
3215
|
+
cols: detailWidth - 4,
|
|
3216
|
+
rows: layout.detailRows - factRows - (showFactRule ? 1 : 0) - SNAKE_CHROME_ROWS
|
|
3217
|
+
};
|
|
3218
|
+
const snakeShown = snakeRoom.rows >= SNAKE_MIN_BOARD_ROWS && snakeRoom.cols >= SNAKE_MIN_BOARD_COLS;
|
|
2747
3219
|
const snake = useSnake({
|
|
2748
|
-
width: Math.max(
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
enabled: waiting
|
|
3220
|
+
width: Math.max(SNAKE_MIN_BOARD_COLS, Math.min(40, snakeRoom.cols)),
|
|
3221
|
+
height: Math.max(SNAKE_MIN_BOARD_ROWS, Math.min(14, snakeRoom.rows)),
|
|
3222
|
+
focused: snakeFocused
|
|
2752
3223
|
});
|
|
2753
|
-
const [snakeAwayFor, setSnakeAwayFor] = useState3(null);
|
|
2754
|
-
const snakeShown = waiting && snakeAwayFor !== selectedSession?.id;
|
|
2755
|
-
const [snakeOverlay, setSnakeOverlay] = useState3(ctx.openSnake === true);
|
|
2756
3224
|
const overlayWidth = Math.max(16, Math.min(60, terminalCols - 8));
|
|
2757
3225
|
const overlayHeight = Math.max(6, Math.min(20, terminalRows - 10));
|
|
2758
3226
|
const overlaySnake = useSnake({
|
|
2759
3227
|
width: overlayWidth,
|
|
2760
3228
|
height: overlayHeight,
|
|
2761
|
-
|
|
3229
|
+
focused: overlayFocused
|
|
2762
3230
|
});
|
|
2763
3231
|
const previewRows = Math.max(1, layout.detailRows - factRows - (showFactRule ? 1 : 0));
|
|
2764
3232
|
const previewView = previewWindow(preview, previewRows, previewOffset);
|
|
@@ -2771,6 +3239,9 @@ var App = ({ ctx, onQuit }) => {
|
|
|
2771
3239
|
setPreviewOffset(0);
|
|
2772
3240
|
};
|
|
2773
3241
|
const say = (text) => setStatus({ text, at: Date.now() });
|
|
3242
|
+
const clearFilters = (...sections) => setFilters(
|
|
3243
|
+
(current) => sections.every((section) => current[section] === "") ? current : { ...current, ...Object.fromEntries(sections.map((section) => [section, ""])) }
|
|
3244
|
+
);
|
|
2774
3245
|
const refetch = () => void queryClient.invalidateQueries({ queryKey: WORKBENCH_KEY });
|
|
2775
3246
|
useEffect3(() => {
|
|
2776
3247
|
const share = ctx.agentShare;
|
|
@@ -2909,6 +3380,7 @@ ${tunneled}
|
|
|
2909
3380
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2910
3381
|
})
|
|
2911
3382
|
);
|
|
3383
|
+
clearFilters("worktrees", "sessions");
|
|
2912
3384
|
setWorktreeKey(vars.pendingKey);
|
|
2913
3385
|
selectSession(vars.pendingKey);
|
|
2914
3386
|
setBusy(`provisioning ${vars.harness} workspace \xB7`);
|
|
@@ -2916,6 +3388,7 @@ ${tunneled}
|
|
|
2916
3388
|
},
|
|
2917
3389
|
onError: (error, vars) => {
|
|
2918
3390
|
patchWorkbench((current) => removeSession(current, vars.projectId, vars.pendingKey));
|
|
3391
|
+
setSnakeFocus((current) => current?.start === vars.gateKey ? null : current);
|
|
2919
3392
|
setBusy(null);
|
|
2920
3393
|
say(errorText(error));
|
|
2921
3394
|
},
|
|
@@ -2956,7 +3429,8 @@ ${tunneled}
|
|
|
2956
3429
|
);
|
|
2957
3430
|
setBusyStarted(Date.now());
|
|
2958
3431
|
},
|
|
2959
|
-
onError: (error) => {
|
|
3432
|
+
onError: (error, vars) => {
|
|
3433
|
+
setSnakeFocus((current) => current?.start === vars.gateKey ? null : current);
|
|
2960
3434
|
setBusy(null);
|
|
2961
3435
|
say(errorText(error));
|
|
2962
3436
|
refetch();
|
|
@@ -3014,6 +3488,7 @@ ${tunneled}
|
|
|
3014
3488
|
return;
|
|
3015
3489
|
}
|
|
3016
3490
|
const project = result.project;
|
|
3491
|
+
clearFilters("projects");
|
|
3017
3492
|
say(`adopted \xB7 ${project.name} \xB7 w starts a worktree`);
|
|
3018
3493
|
setProjectKey(project.id);
|
|
3019
3494
|
setWorktreeKey(null);
|
|
@@ -3101,12 +3576,14 @@ ${tunneled}
|
|
|
3101
3576
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3102
3577
|
})
|
|
3103
3578
|
);
|
|
3579
|
+
clearFilters("sessions");
|
|
3104
3580
|
selectSession(vars.pendingKey);
|
|
3105
3581
|
setBusy(`starting ${vars.harness} in the worktree \xB7`);
|
|
3106
3582
|
setBusyStarted(Date.now());
|
|
3107
3583
|
},
|
|
3108
3584
|
onError: (error, vars) => {
|
|
3109
3585
|
patchWorkbench((current) => removeSession(current, vars.projectId, vars.pendingKey));
|
|
3586
|
+
setSnakeFocus((current) => current?.start === vars.gateKey ? null : current);
|
|
3110
3587
|
setBusy(null);
|
|
3111
3588
|
say(errorText(error));
|
|
3112
3589
|
},
|
|
@@ -3333,6 +3810,7 @@ ${tunneled}
|
|
|
3333
3810
|
return;
|
|
3334
3811
|
}
|
|
3335
3812
|
setCreating(null);
|
|
3813
|
+
setSnakeFocus({ returnTo: focus, start: gateKey });
|
|
3336
3814
|
setFocus("sessions");
|
|
3337
3815
|
launchMutation.mutate({
|
|
3338
3816
|
projectId: current.projectId,
|
|
@@ -3406,6 +3884,7 @@ ${tunneled}
|
|
|
3406
3884
|
say(`already resuming \xB7 ${sessionDisplayName(session)}`);
|
|
3407
3885
|
return;
|
|
3408
3886
|
}
|
|
3887
|
+
setSnakeFocus({ returnTo: focus, start: gateKey });
|
|
3409
3888
|
setFocus("sessions");
|
|
3410
3889
|
resumeMutation.mutate({ projectId, session, harness, gateKey });
|
|
3411
3890
|
};
|
|
@@ -3437,6 +3916,7 @@ ${tunneled}
|
|
|
3437
3916
|
say(`already starting a session in ${group.name}`);
|
|
3438
3917
|
return;
|
|
3439
3918
|
}
|
|
3919
|
+
setSnakeFocus({ returnTo: focus, start: gateKey });
|
|
3440
3920
|
setFocus("sessions");
|
|
3441
3921
|
launchInWorktreeMutation.mutate({
|
|
3442
3922
|
projectId,
|
|
@@ -3485,34 +3965,34 @@ ${tunneled}
|
|
|
3485
3965
|
return;
|
|
3486
3966
|
}
|
|
3487
3967
|
};
|
|
3488
|
-
const
|
|
3968
|
+
const goTo = (next) => {
|
|
3969
|
+
if (next === "detail" && waiting && snakeFits) {
|
|
3970
|
+
setSnakeFocus({ returnTo: focus, start: null });
|
|
3971
|
+
return;
|
|
3972
|
+
}
|
|
3973
|
+
setFocus(next);
|
|
3974
|
+
};
|
|
3975
|
+
const moveColumn = (delta) => goTo(stepColumn(focus, delta));
|
|
3489
3976
|
useKeyboard2((key) => {
|
|
3490
3977
|
if (reviewing !== null) return;
|
|
3491
3978
|
if (lockRef.current) return;
|
|
3492
3979
|
if (key.ctrl && key.name === "c") return onQuit();
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
if (waiting && focus === "detail" && picker === null && editing === null && creating === null) {
|
|
3504
|
-
if (snakeShown) {
|
|
3505
|
-
if (key.name === "up" || key.name === "down" || key.name === "left" || key.name === "right") {
|
|
3506
|
-
snake.steer(key.name);
|
|
3507
|
-
return;
|
|
3980
|
+
const verb = verbForKey(strokeName(key), key.shift === true);
|
|
3981
|
+
const game = overlayFocused ? overlaySnake : snakeFocused ? snake : null;
|
|
3982
|
+
if (game !== null || overlayOpen) {
|
|
3983
|
+
const action = snakeKey(key.name ?? "");
|
|
3984
|
+
if (action?.type === "leave") {
|
|
3985
|
+
if (overlayOpen) {
|
|
3986
|
+
setSnakeOverlay(false);
|
|
3987
|
+
} else if (snakeFocus !== null) {
|
|
3988
|
+
setFocus(snakeFocus.returnTo);
|
|
3989
|
+
setSnakeFocus(null);
|
|
3508
3990
|
}
|
|
3509
|
-
|
|
3510
|
-
if (key.name === "escape") return setSnakeAwayFor(selectedSession?.id ?? null);
|
|
3511
|
-
} else if (key.name === "space") {
|
|
3512
|
-
return setSnakeAwayFor(null);
|
|
3991
|
+
return;
|
|
3513
3992
|
}
|
|
3993
|
+
if (action !== null) return game?.send(action);
|
|
3994
|
+
if (gameSwallows(verb) || game === null) return;
|
|
3514
3995
|
}
|
|
3515
|
-
const verb = verbForKey(key.name ?? "", key.shift === true);
|
|
3516
3996
|
if (verb !== "stop") setStopArmed(null);
|
|
3517
3997
|
if (verb !== "remove") setRemoveArmed(null);
|
|
3518
3998
|
if (adoptOffer !== null) {
|
|
@@ -3587,6 +4067,26 @@ ${tunneled}
|
|
|
3587
4067
|
}
|
|
3588
4068
|
return;
|
|
3589
4069
|
}
|
|
4070
|
+
if (helpOpen) {
|
|
4071
|
+
if (key.name === "escape" || key.name === "q" || verb === "help") {
|
|
4072
|
+
setHelpOpen(false);
|
|
4073
|
+
} else if (verb === "moveDown" || verb === "pageDown") {
|
|
4074
|
+
setHelpOffset(
|
|
4075
|
+
(current) => Math.max(0, Math.min(helpMaxOffset, current + (verb === "pageDown" ? 10 : 1)))
|
|
4076
|
+
);
|
|
4077
|
+
} else if (verb === "moveUp" || verb === "pageUp") {
|
|
4078
|
+
setHelpOffset((current) => Math.max(0, current - (verb === "pageUp" ? 10 : 1)));
|
|
4079
|
+
}
|
|
4080
|
+
return;
|
|
4081
|
+
}
|
|
4082
|
+
if (filtering !== null) {
|
|
4083
|
+
if (key.name === "escape") {
|
|
4084
|
+
clearFilters(filtering);
|
|
4085
|
+
setFiltering(null);
|
|
4086
|
+
} else if (key.name === "down") moveSelection(1);
|
|
4087
|
+
else if (key.name === "up") moveSelection(-1);
|
|
4088
|
+
return;
|
|
4089
|
+
}
|
|
3590
4090
|
if (picker !== null) {
|
|
3591
4091
|
switch (key.name) {
|
|
3592
4092
|
case "down":
|
|
@@ -3619,6 +4119,10 @@ ${tunneled}
|
|
|
3619
4119
|
return;
|
|
3620
4120
|
}
|
|
3621
4121
|
}
|
|
4122
|
+
if (snakeFocus !== null && verb !== null) setSnakeFocus(null);
|
|
4123
|
+
if (key.name === "space" && waiting && snakeFits && focus === "detail") {
|
|
4124
|
+
return setSnakeFocus({ returnTo: focus, start: null });
|
|
4125
|
+
}
|
|
3622
4126
|
if (key.ctrl === true) return;
|
|
3623
4127
|
if (focus === "projects" && ["attach", "resume", "stop", "remove", "rename", "openWeb", "review"].includes(verb ?? "")) {
|
|
3624
4128
|
say("select a session first \xB7 \u2192 opens worktrees");
|
|
@@ -3639,6 +4143,37 @@ ${tunneled}
|
|
|
3639
4143
|
return moveColumn(1);
|
|
3640
4144
|
case "columnLeft":
|
|
3641
4145
|
return moveColumn(-1);
|
|
4146
|
+
case "nextPane":
|
|
4147
|
+
return goTo(cyclePane(focus, 1));
|
|
4148
|
+
case "prevPane":
|
|
4149
|
+
return goTo(cyclePane(focus, -1));
|
|
4150
|
+
case "jumpProjects":
|
|
4151
|
+
case "jumpWorktrees":
|
|
4152
|
+
case "jumpSessions":
|
|
4153
|
+
case "jumpDetail":
|
|
4154
|
+
return goTo(paneForDigit(strokeName(key)) ?? focus);
|
|
4155
|
+
case "back":
|
|
4156
|
+
if (isNavSection(focus) && filters[focus] !== "") {
|
|
4157
|
+
clearFilters(focus);
|
|
4158
|
+
return;
|
|
4159
|
+
}
|
|
4160
|
+
return goTo(backFrom(focus, lastNav));
|
|
4161
|
+
case "filter":
|
|
4162
|
+
if (!isNavSection(focus)) {
|
|
4163
|
+
say("/ filters a list \xB7 1 2 3 jump to one");
|
|
4164
|
+
return;
|
|
4165
|
+
}
|
|
4166
|
+
return setFiltering(focus);
|
|
4167
|
+
case "nextScreenMode":
|
|
4168
|
+
case "prevScreenMode": {
|
|
4169
|
+
const next = stepScreenMode(screenMode, verb === "nextScreenMode" ? 1 : -1);
|
|
4170
|
+
setScreenMode(next);
|
|
4171
|
+
say(`screen mode \xB7 ${next}${next === "normal" ? "" : " \xB7 + and _ cycle it"}`);
|
|
4172
|
+
return;
|
|
4173
|
+
}
|
|
4174
|
+
case "help":
|
|
4175
|
+
setHelpOffset(0);
|
|
4176
|
+
return setHelpOpen(true);
|
|
3642
4177
|
case "attach":
|
|
3643
4178
|
return attachSelected();
|
|
3644
4179
|
case "resume":
|
|
@@ -3695,18 +4230,20 @@ ${tunneled}
|
|
|
3695
4230
|
});
|
|
3696
4231
|
const liveTotal = projectItems.reduce((sum, item) => sum + item.live, 0);
|
|
3697
4232
|
const stoppingTotal = projectItems.reduce((sum, item) => sum + item.stopping, 0);
|
|
3698
|
-
const
|
|
4233
|
+
const named = (section) => filters[section] === "" ? section : `${section} /${filters[section]}`;
|
|
4234
|
+
const columnTitle = (column) => numberedTitle(column, columnWords(column));
|
|
4235
|
+
const columnWords = (column) => {
|
|
3699
4236
|
if (column === "worktrees") {
|
|
3700
|
-
return selectedProject === null ? "worktrees" :
|
|
4237
|
+
return selectedProject === null ? named("worktrees") : `${named("worktrees")} \xB7 ${worktreeGroups.length}${selectedProject.live > 0 ? ` \xB7 ${selectedProject.live} live` : ""}`;
|
|
3701
4238
|
}
|
|
3702
4239
|
if (column === "sessions") {
|
|
3703
|
-
return selectedGroup === null ? "sessions" :
|
|
4240
|
+
return selectedGroup === null ? named("sessions") : `${named("sessions")} \xB7 ${sessionItems.length}`;
|
|
3704
4241
|
}
|
|
3705
4242
|
if (column === "detail") {
|
|
3706
4243
|
if (selectedSession === null) return "session";
|
|
3707
4244
|
return previewView.offset === 0 ? "session \xB7 read-only" : `session \xB7 read-only \xB7 ${previewView.later} newer below`;
|
|
3708
4245
|
}
|
|
3709
|
-
return
|
|
4246
|
+
return named("projects");
|
|
3710
4247
|
};
|
|
3711
4248
|
const pickerTitle = picker === null ? "" : picker.worktree === void 0 ? picker.session === null ? "new session \xB7 pick a harness" : `resume ${sessionDisplayName(picker.session)} \xB7 pick a harness` : `new session in ${picker.worktree.name} \xB7 pick a harness`;
|
|
3712
4249
|
const modalWidth = Math.min(74, terminalCols - 4);
|
|
@@ -3726,7 +4263,20 @@ ${tunneled}
|
|
|
3726
4263
|
modalWidth - 7
|
|
3727
4264
|
);
|
|
3728
4265
|
const creatingHeight = 2 + 1 + 1 + 6 + 1 + deriveHarnesses(null).length;
|
|
3729
|
-
const
|
|
4266
|
+
const modalFieldWidth = Math.max(4, modalWidth - 2 - 11 - 1);
|
|
4267
|
+
const footerText = adoptOffer === null ? editing === null ? creating === null ? picker === null ? overlayFocused || snakeFocused ? ` ${snakeHints(overlayFocused ? overlaySnake.play : snake.play)}` : ` ${footerHints(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";
|
|
4268
|
+
const helpWidth = Math.min(96, terminalCols - 4);
|
|
4269
|
+
const helpLines = helpSections(focus).flatMap((section, index) => [
|
|
4270
|
+
...index === 0 ? [] : [{ kind: "blank", key: `blank-${section.title}` }],
|
|
4271
|
+
{ kind: "title", key: `title-${section.title}`, text: section.title },
|
|
4272
|
+
...section.rows.map((row) => ({ kind: "row", key: row.verb, row }))
|
|
4273
|
+
]);
|
|
4274
|
+
const helpRows = Math.max(1, Math.min(helpLines.length, terminalRows - 6));
|
|
4275
|
+
const helpMaxOffset = Math.max(0, helpLines.length - helpRows);
|
|
4276
|
+
const helpView = helpLines.slice(
|
|
4277
|
+
Math.min(helpOffset, helpMaxOffset),
|
|
4278
|
+
Math.min(helpOffset, helpMaxOffset) + helpRows
|
|
4279
|
+
);
|
|
3730
4280
|
const loadFailure = data === void 0 && failureReason !== null ? errorText(failureReason) : null;
|
|
3731
4281
|
if (reviewing !== null) {
|
|
3732
4282
|
return /* @__PURE__ */ jsx3(
|
|
@@ -3811,7 +4361,12 @@ ${tunneled}
|
|
|
3811
4361
|
},
|
|
3812
4362
|
item.project.id
|
|
3813
4363
|
)),
|
|
3814
|
-
data !== void 0 && projectItems.length === 0 ? /* @__PURE__ */ jsx3(
|
|
4364
|
+
data !== void 0 && projectItems.length === 0 ? /* @__PURE__ */ jsx3(
|
|
4365
|
+
EmptyNote,
|
|
4366
|
+
{
|
|
4367
|
+
text: filters.projects === "" ? "none adopted yet" : `nothing matches /${filters.projects} \xB7 esc clears`
|
|
4368
|
+
}
|
|
4369
|
+
) : null
|
|
3815
4370
|
]
|
|
3816
4371
|
}
|
|
3817
4372
|
)
|
|
@@ -3843,7 +4398,12 @@ ${tunneled}
|
|
|
3843
4398
|
},
|
|
3844
4399
|
group.key
|
|
3845
4400
|
)),
|
|
3846
|
-
data !== void 0 && worktreeGroups.length === 0 ? /* @__PURE__ */ jsx3(
|
|
4401
|
+
data !== void 0 && worktreeGroups.length === 0 ? /* @__PURE__ */ jsx3(
|
|
4402
|
+
EmptyNote,
|
|
4403
|
+
{
|
|
4404
|
+
text: filters.worktrees === "" ? "no worktrees \xB7 w starts one" : `nothing matches /${filters.worktrees} \xB7 esc clears`
|
|
4405
|
+
}
|
|
4406
|
+
) : null
|
|
3847
4407
|
]
|
|
3848
4408
|
}
|
|
3849
4409
|
)
|
|
@@ -3878,7 +4438,7 @@ ${tunneled}
|
|
|
3878
4438
|
data !== void 0 && sessionItems.length === 0 ? /* @__PURE__ */ jsx3(
|
|
3879
4439
|
EmptyNote,
|
|
3880
4440
|
{
|
|
3881
|
-
text: selectedGroup === null ? "no worktree selected" : "no sessions \xB7 n starts one"
|
|
4441
|
+
text: selectedGroup === null ? "no worktree selected" : filters.sessions === "" ? "no sessions \xB7 n starts one" : `nothing matches /${filters.sessions} \xB7 esc clears`
|
|
3882
4442
|
}
|
|
3883
4443
|
) : null,
|
|
3884
4444
|
loadFailure === null ? null : /* @__PURE__ */ jsx3("text", { height: 1, fg: INK_2, bg: "transparent", children: ` ${loadFailure} \xB7 retrying` })
|
|
@@ -3908,7 +4468,13 @@ ${tunneled}
|
|
|
3908
4468
|
/* @__PURE__ */ jsx3("span", { fg: INK_2, children: "starting" }),
|
|
3909
4469
|
/* @__PURE__ */ jsx3("span", { fg: FAINT, children: ` \xB7 ${selectedSession === null ? "launching" : startingExplanationOf(selectedSession)}` })
|
|
3910
4470
|
] }),
|
|
3911
|
-
snakeShown ? /* @__PURE__ */ jsx3(
|
|
4471
|
+
snakeShown ? /* @__PURE__ */ jsx3(
|
|
4472
|
+
SnakeBoard,
|
|
4473
|
+
{
|
|
4474
|
+
handle: snake,
|
|
4475
|
+
idle: snakeFocus !== null && dialogOpen ? SNAKE_BEHIND_DIALOG : SNAKE_IDLE
|
|
4476
|
+
}
|
|
4477
|
+
) : /* @__PURE__ */ jsx3(EmptyNote, { text: snakeTooSmall(snakeRoom.rows < SNAKE_MIN_BOARD_ROWS) })
|
|
3912
4478
|
] }) : previewSessionId === null ? /* @__PURE__ */ jsx3(EmptyNote, { text: "provisioning \xB7 no record yet" }) : selectedSession?.harness === "shell" ? /* @__PURE__ */ jsx3(EmptyNote, { text: "shell \xB7 no conversation record; a attaches if live" }) : transcript.isPending ? /* @__PURE__ */ jsx3(EmptyNote, { text: "reading the record\u2026" }) : transcript.error === null ? previewView.lines.length === 0 ? /* @__PURE__ */ jsx3(EmptyNote, { text: "no conversation recorded yet" }) : previewView.lines.map((line, index) => /* @__PURE__ */ jsx3(PreviewRow, { line }, `${index}-${line.kind}`)) : /* @__PURE__ */ jsx3("text", { height: 1, fg: INK_2, bg: "transparent", children: ` could not read the record \xB7 ${errorText(transcript.error)}` }) })
|
|
3913
4479
|
] }) });
|
|
3914
4480
|
const offNav = layout.offscreen.filter(isNavSection);
|
|
@@ -3947,7 +4513,7 @@ ${tunneled}
|
|
|
3947
4513
|
/* @__PURE__ */ jsx3("span", { fg: shareState === "connected" ? MUTED : FAINT, children: shareState === "connected" ? "agent shared" : "agent share reconnecting" })
|
|
3948
4514
|
] })
|
|
3949
4515
|
] }),
|
|
3950
|
-
/* @__PURE__ */ jsx3("text", { height: 1, fg: FAINT, bg: "transparent", children: `${ctx.config.url} ` })
|
|
4516
|
+
/* @__PURE__ */ jsx3("text", { height: 1, fg: FAINT, bg: "transparent", children: `${screenMode === "normal" ? "" : `${screenMode} screen \xB7 `}${ctx.config.url} ` })
|
|
3951
4517
|
] }),
|
|
3952
4518
|
!layout.breadcrumb ? null : /* @__PURE__ */ jsxs3("text", { height: 1, bg: "transparent", children: [
|
|
3953
4519
|
/* @__PURE__ */ jsx3("span", { fg: FAINT, children: " " }),
|
|
@@ -3971,28 +4537,51 @@ ${tunneled}
|
|
|
3971
4537
|
),
|
|
3972
4538
|
detailWidth === 0 ? null : renderDetail()
|
|
3973
4539
|
] }),
|
|
3974
|
-
snakeOverlay ? /* @__PURE__ */
|
|
4540
|
+
snakeOverlay && !overlayFits ? /* @__PURE__ */ jsx3(
|
|
3975
4541
|
"box",
|
|
3976
4542
|
{
|
|
3977
4543
|
position: "absolute",
|
|
3978
|
-
zIndex:
|
|
3979
|
-
left:
|
|
3980
|
-
top: Math.max(1, Math.floor((terminalRows -
|
|
3981
|
-
width:
|
|
3982
|
-
height:
|
|
4544
|
+
zIndex: 9,
|
|
4545
|
+
left: 1,
|
|
4546
|
+
top: Math.max(1, Math.floor((terminalRows - 3) / 2)),
|
|
4547
|
+
width: Math.max(4, terminalCols - 2),
|
|
4548
|
+
height: 3,
|
|
3983
4549
|
border: true,
|
|
3984
4550
|
borderStyle: "rounded",
|
|
3985
|
-
borderColor:
|
|
3986
|
-
title: " snake ",
|
|
3987
|
-
titleAlignment: "left",
|
|
4551
|
+
borderColor: RULE,
|
|
3988
4552
|
backgroundColor: SURFACE,
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
]
|
|
4553
|
+
children: /* @__PURE__ */ jsx3("text", { height: 1, bg: "transparent", fg: FAINT, children: fit(
|
|
4554
|
+
` ${snakeTooSmall(terminalRows < 12)} \xB7 esc closes`,
|
|
4555
|
+
Math.max(1, terminalCols - 4)
|
|
4556
|
+
) })
|
|
3994
4557
|
}
|
|
3995
4558
|
) : null,
|
|
4559
|
+
snakeOverlay && overlayFits ? (
|
|
4560
|
+
// Under every dialog: one that opens over the game has the keyboard until it closes.
|
|
4561
|
+
/* @__PURE__ */ jsxs3(
|
|
4562
|
+
"box",
|
|
4563
|
+
{
|
|
4564
|
+
position: "absolute",
|
|
4565
|
+
zIndex: 9,
|
|
4566
|
+
left: Math.max(1, Math.floor((terminalCols - (overlayWidth + 4)) / 2)),
|
|
4567
|
+
top: Math.max(1, Math.floor((terminalRows - (overlayHeight + 4)) / 2)),
|
|
4568
|
+
width: overlayWidth + 4,
|
|
4569
|
+
height: overlayHeight + 4,
|
|
4570
|
+
border: true,
|
|
4571
|
+
borderStyle: "rounded",
|
|
4572
|
+
borderColor: overlayFocused ? ACCENT : RULE,
|
|
4573
|
+
title: " snake ",
|
|
4574
|
+
titleAlignment: "left",
|
|
4575
|
+
backgroundColor: SURFACE,
|
|
4576
|
+
flexDirection: "column",
|
|
4577
|
+
children: [
|
|
4578
|
+
/* @__PURE__ */ jsx3(SnakeHeading, { play: overlaySnake.play }),
|
|
4579
|
+
/* @__PURE__ */ jsx3("box", { marginLeft: 1, flexDirection: "column", flexShrink: 0, children: /* @__PURE__ */ jsx3(SnakeRows, { play: overlaySnake.play }) }),
|
|
4580
|
+
/* @__PURE__ */ jsx3(SnakeFooter, { play: overlaySnake.play, idle: SNAKE_BEHIND_DIALOG })
|
|
4581
|
+
]
|
|
4582
|
+
}
|
|
4583
|
+
)
|
|
4584
|
+
) : null,
|
|
3996
4585
|
picker === null ? null : /* @__PURE__ */ jsxs3(
|
|
3997
4586
|
"box",
|
|
3998
4587
|
{
|
|
@@ -4020,7 +4609,8 @@ ${tunneled}
|
|
|
4020
4609
|
{
|
|
4021
4610
|
item,
|
|
4022
4611
|
selected: index === pickerIndex,
|
|
4023
|
-
background: SURFACE
|
|
4612
|
+
background: SURFACE,
|
|
4613
|
+
width: modalWidth - 2
|
|
4024
4614
|
},
|
|
4025
4615
|
String(item.harness)
|
|
4026
4616
|
))
|
|
@@ -4127,7 +4717,10 @@ ${tunneled}
|
|
|
4127
4717
|
{
|
|
4128
4718
|
focused: true,
|
|
4129
4719
|
value: creating.name,
|
|
4130
|
-
placeholder:
|
|
4720
|
+
placeholder: fit(
|
|
4721
|
+
"e.g. fix-auth (empty = auto \xB7 an existing name joins it)",
|
|
4722
|
+
modalFieldWidth
|
|
4723
|
+
),
|
|
4131
4724
|
backgroundColor: SURFACE,
|
|
4132
4725
|
focusedBackgroundColor: SURFACE,
|
|
4133
4726
|
textColor: INK,
|
|
@@ -4158,7 +4751,10 @@ ${tunneled}
|
|
|
4158
4751
|
{
|
|
4159
4752
|
focused: true,
|
|
4160
4753
|
value: "",
|
|
4161
|
-
placeholder:
|
|
4754
|
+
placeholder: fit(
|
|
4755
|
+
"filter \xB7 enter takes the highlighted branch, or the default",
|
|
4756
|
+
modalFieldWidth
|
|
4757
|
+
),
|
|
4162
4758
|
backgroundColor: SURFACE,
|
|
4163
4759
|
focusedBackgroundColor: SURFACE,
|
|
4164
4760
|
textColor: INK,
|
|
@@ -4191,7 +4787,7 @@ ${tunneled}
|
|
|
4191
4787
|
height: 1,
|
|
4192
4788
|
bg: SURFACE,
|
|
4193
4789
|
fg: creating.branchError === null ? FAINT : ERROR,
|
|
4194
|
-
children: index === 0 && notice !== null ? ` ${fit(notice,
|
|
4790
|
+
children: index === 0 && notice !== null ? ` ${fit(notice, modalWidth - 7)}` : " "
|
|
4195
4791
|
},
|
|
4196
4792
|
`slot-${index}`
|
|
4197
4793
|
);
|
|
@@ -4219,7 +4815,7 @@ ${tunneled}
|
|
|
4219
4815
|
/* @__PURE__ */ jsx3(Gutter, { selected: creating.step === "harness" }),
|
|
4220
4816
|
/* @__PURE__ */ jsx3("span", { fg: FAINT, children: "harness" })
|
|
4221
4817
|
] }),
|
|
4222
|
-
deriveHarnesses(null).map((item, index) => {
|
|
4818
|
+
deriveHarnesses(null, creating.joins ? creating.name : null).map((item, index) => {
|
|
4223
4819
|
const active = creating.step === "harness";
|
|
4224
4820
|
const highlighted = active && index === creating.harnessIndex;
|
|
4225
4821
|
return /* @__PURE__ */ jsx3(
|
|
@@ -4232,7 +4828,7 @@ ${tunneled}
|
|
|
4232
4828
|
/* @__PURE__ */ jsx3("span", { fg: FAINT, children: " " }),
|
|
4233
4829
|
/* @__PURE__ */ jsx3(Gutter, { selected: highlighted }),
|
|
4234
4830
|
/* @__PURE__ */ jsx3("span", { fg: active ? INK : FAINT, children: item.label.padEnd(12) }),
|
|
4235
|
-
/* @__PURE__ */ jsx3("span", { fg: FAINT, children: active ? item.hint : "" })
|
|
4831
|
+
/* @__PURE__ */ jsx3("span", { fg: FAINT, children: active ? fit(item.hint, Math.max(4, modalWidth - 2 - 3 - 2 - 12)) : "" })
|
|
4236
4832
|
] })
|
|
4237
4833
|
},
|
|
4238
4834
|
String(item.harness)
|
|
@@ -4242,7 +4838,57 @@ ${tunneled}
|
|
|
4242
4838
|
}
|
|
4243
4839
|
),
|
|
4244
4840
|
loadFailure === null ? /* @__PURE__ */ jsx3(StatusLine, { busy, busyStarted, status }) : /* @__PURE__ */ jsx3("text", { height: 1, fg: ERROR, bg: "transparent", children: ` could not load workbench \xB7 ${loadFailure} \xB7 retrying` }),
|
|
4245
|
-
/* @__PURE__ */
|
|
4841
|
+
helpOpen ? /* @__PURE__ */ jsxs3(
|
|
4842
|
+
"box",
|
|
4843
|
+
{
|
|
4844
|
+
position: "absolute",
|
|
4845
|
+
zIndex: 14,
|
|
4846
|
+
left: Math.max(1, Math.floor((terminalCols - helpWidth) / 2)),
|
|
4847
|
+
top: Math.max(1, Math.floor((terminalRows - (helpRows + 3)) / 2)),
|
|
4848
|
+
width: helpWidth,
|
|
4849
|
+
height: helpRows + 3,
|
|
4850
|
+
border: true,
|
|
4851
|
+
borderStyle: "rounded",
|
|
4852
|
+
borderColor: ACCENT,
|
|
4853
|
+
title: ` keys \xB7 ${fit(columnTitle(focus), Math.max(6, helpWidth - 14))} `,
|
|
4854
|
+
titleAlignment: "left",
|
|
4855
|
+
backgroundColor: SURFACE,
|
|
4856
|
+
flexDirection: "column",
|
|
4857
|
+
children: [
|
|
4858
|
+
helpView.map(
|
|
4859
|
+
(line) => line.kind === "blank" ? /* @__PURE__ */ jsx3("text", { height: 1, bg: SURFACE, children: " " }, line.key) : line.kind === "title" ? /* @__PURE__ */ jsx3("text", { height: 1, bg: SURFACE, fg: MUTED, children: ` ${line.text}` }, line.key) : /* @__PURE__ */ jsxs3("text", { height: 1, bg: SURFACE, children: [
|
|
4860
|
+
/* @__PURE__ */ jsx3("span", { fg: INK, children: ` ${fit(line.row.keys, 18).padEnd(19)}` }),
|
|
4861
|
+
/* @__PURE__ */ jsx3("span", { fg: line.row.here ? INK_2 : FAINT, children: fit(line.row.help, Math.max(8, helpWidth - 25)) })
|
|
4862
|
+
] }, line.key)
|
|
4863
|
+
),
|
|
4864
|
+
/* @__PURE__ */ jsx3("text", { height: 1, bg: SURFACE, fg: FAINT, children: ` ${helpMaxOffset > 0 ? "\u2191\u2193 scroll \xB7 " : ""}esc closes` })
|
|
4865
|
+
]
|
|
4866
|
+
}
|
|
4867
|
+
) : null,
|
|
4868
|
+
filtering === null ? /* @__PURE__ */ jsx3("text", { height: 1, fg: FAINT, bg: "transparent", children: footerText }) : /* @__PURE__ */ jsxs3("box", { height: 1, flexShrink: 0, flexDirection: "row", children: [
|
|
4869
|
+
/* @__PURE__ */ jsx3("text", { height: 1, bg: "transparent", fg: ACCENT, children: " / " }),
|
|
4870
|
+
/* @__PURE__ */ jsx3(
|
|
4871
|
+
"input",
|
|
4872
|
+
{
|
|
4873
|
+
focused: true,
|
|
4874
|
+
value: filters[filtering],
|
|
4875
|
+
placeholder: `filter ${columnTitle(filtering)} \xB7 enter keeps it \xB7 esc clears it`,
|
|
4876
|
+
backgroundColor: CANVAS,
|
|
4877
|
+
focusedBackgroundColor: CANVAS,
|
|
4878
|
+
textColor: INK,
|
|
4879
|
+
focusedTextColor: INK,
|
|
4880
|
+
placeholderColor: FAINT,
|
|
4881
|
+
cursorColor: INK,
|
|
4882
|
+
flexGrow: 1,
|
|
4883
|
+
onInput: (value) => {
|
|
4884
|
+
const section = filtering;
|
|
4885
|
+
const clean = value.replace(/\t/g, "");
|
|
4886
|
+
setFilters((current) => ({ ...current, [section]: clean }));
|
|
4887
|
+
},
|
|
4888
|
+
onSubmit: () => setFiltering(null)
|
|
4889
|
+
}
|
|
4890
|
+
)
|
|
4891
|
+
] })
|
|
4246
4892
|
] });
|
|
4247
4893
|
};
|
|
4248
4894
|
var runDashboard = async (ctx) => {
|
|
@@ -4313,5 +4959,6 @@ var runDashboard = async (ctx) => {
|
|
|
4313
4959
|
});
|
|
4314
4960
|
};
|
|
4315
4961
|
export {
|
|
4962
|
+
App,
|
|
4316
4963
|
runDashboard
|
|
4317
4964
|
};
|