tmux-ide 2.9.0-beta.39 → 2.9.0-beta.41
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 +5 -0
- package/bin/cli.js +6 -3
- package/package.json +6 -3
- package/packages/daemon/dist/tui/mirror/runtime/application-fleet-tabs.js +29 -2
- package/packages/daemon/dist/tui/mirror/runtime/application-home-agent-roster.jsx +10 -13
- package/packages/daemon/dist/tui/mirror/runtime/application-home-agents-owner.js +1 -0
- package/packages/daemon/dist/tui/mirror/runtime/application-machine-navigation.js +45 -7
- package/packages/daemon/dist/tui/mirror/runtime/application-machine-sidebar.jsx +44 -44
- package/packages/daemon/dist/tui/mirror/runtime/application-palette-command-owner.js +2 -2
- package/packages/daemon/dist/tui/mirror/runtime/application-palette-input.js +1 -0
- package/packages/daemon/dist/tui/mirror/ui/agent-status-marker.js +6 -5
- package/packages/daemon/dist/tui/mirror/ui/icons.js +9 -0
- package/packages/daemon/dist/tui/mirror/ui/index.js +3 -0
- package/packages/daemon/dist/tui/mirror/ui/session-row.jsx +71 -0
- package/packages/daemon/dist/tui/mirror/ui/working-sessions.jsx +61 -0
- package/packages/daemon/dist/tui/mirror/workspace/application-action-descriptions.js +51 -0
- package/packages/daemon/dist/tui/mirror/workspace/application-command-description.js +10 -3
- package/packages/daemon/dist/tui/mirror/workspace/application-shortcuts.js +15 -7
- package/packages/daemon/dist/tui/mirror/workspace/palette-list-layout.js +1 -1
- package/packages/daemon/src/tui/mirror/runtime/application-fleet-tabs.ts +34 -2
- package/packages/daemon/src/tui/mirror/runtime/application-home-agent-roster.tsx +14 -18
- package/packages/daemon/src/tui/mirror/runtime/application-home-agents-owner.ts +1 -0
- package/packages/daemon/src/tui/mirror/runtime/application-machine-navigation.ts +44 -7
- package/packages/daemon/src/tui/mirror/runtime/application-machine-sidebar.tsx +70 -80
- package/packages/daemon/src/tui/mirror/runtime/application-palette-command-owner.ts +5 -3
- package/packages/daemon/src/tui/mirror/runtime/application-palette-input.ts +1 -0
- package/packages/daemon/src/tui/mirror/runtime/application-reference-sheet.tsx +80 -5
- package/packages/daemon/src/tui/mirror/runtime/application-shell-catalog.tsx +6 -2
- package/packages/daemon/src/tui/mirror/runtime/application-shell-overlays.tsx +6 -5
- package/packages/daemon/src/tui/mirror/runtime/application-shell-view.tsx +6 -2
- package/packages/daemon/src/tui/mirror/ui/agent-status-marker.ts +7 -5
- package/packages/daemon/src/tui/mirror/ui/icons.ts +9 -0
- package/packages/daemon/src/tui/mirror/ui/index.ts +4 -0
- package/packages/daemon/src/tui/mirror/ui/session-row.tsx +122 -0
- package/packages/daemon/src/tui/mirror/ui/working-sessions.tsx +99 -0
- package/packages/daemon/src/tui/mirror/workspace/application-action-descriptions.ts +52 -0
- package/packages/daemon/src/tui/mirror/workspace/application-command-description.ts +12 -3
- package/packages/daemon/src/tui/mirror/workspace/application-shortcuts.ts +19 -7
- package/packages/daemon/src/tui/mirror/workspace/palette-list-layout.ts +1 -1
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { CHROME_ACTIONS, SIDEBAR_ACTIONS } from "../workspace/application-action-descriptions.ts";
|
|
1
2
|
import { createAgentStatusMarker } from "../ui/agent-status-marker.ts";
|
|
2
3
|
import type { TmuxServerDescriptor, TmuxServerScope } from "@tmux-ide/contracts";
|
|
3
4
|
import { fleetHostColor, summarizeFleetActivity } from "./fleet-presentation.ts";
|
|
4
|
-
import {
|
|
5
|
+
import { WorkingSessions } from "../ui/working-sessions.tsx";
|
|
6
|
+
import type { SessionRowModel } from "../ui/session-row.tsx";
|
|
5
7
|
/* @jsxImportSource @opentui/solid */
|
|
6
8
|
import {
|
|
7
9
|
fleetConnectionMessage,
|
|
@@ -79,15 +81,7 @@ export interface ApplicationMachineSidebarModel {
|
|
|
79
81
|
paneId: string,
|
|
80
82
|
source: "keyboard" | "mouse",
|
|
81
83
|
) => void;
|
|
82
|
-
readonly tabs?: Accessor<
|
|
83
|
-
readonly {
|
|
84
|
-
key: string;
|
|
85
|
-
label: string;
|
|
86
|
-
hostLabel: string;
|
|
87
|
-
active: boolean;
|
|
88
|
-
available: boolean;
|
|
89
|
-
}[]
|
|
90
|
-
>;
|
|
84
|
+
readonly tabs?: Accessor<readonly SessionRowModel[]>;
|
|
91
85
|
readonly onOpenTab?: (key: string) => void;
|
|
92
86
|
readonly onCloseTab?: (key: string) => void;
|
|
93
87
|
readonly onOpenSwitcher?: () => void;
|
|
@@ -117,8 +111,8 @@ export function ApplicationMachineSidebar(props: {
|
|
|
117
111
|
readonly theme: SemanticThemeSnapshot;
|
|
118
112
|
readonly agents?: JSX.Element;
|
|
119
113
|
readonly agentRows?: number;
|
|
114
|
+
readonly onHelp?: (source: "keyboard" | "mouse") => void;
|
|
120
115
|
}) {
|
|
121
|
-
const [showHelp, setShowHelp] = createSignal(false);
|
|
122
116
|
const [localFocused, setLocalFocused] = createSignal(false);
|
|
123
117
|
const focused = () => props.model.focused?.() ?? localFocused();
|
|
124
118
|
const [localCollapsed, setCollapsed] = createSignal<ReadonlySet<string>>(new Set());
|
|
@@ -133,16 +127,13 @@ export function ApplicationMachineSidebar(props: {
|
|
|
133
127
|
const controlsHeight = () => (props.height >= 8 && controlGroup() ? 4 : 0);
|
|
134
128
|
const searchHeight = () =>
|
|
135
129
|
(props.model.onOpenSwitcher ? 1 : 0) + (props.model.onOpenAttention ? 1 : 0);
|
|
130
|
+
const [workingFocused, setWorkingFocused] = createSignal(true);
|
|
136
131
|
const tabHeight = () =>
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
return tabs.slice(start, start + tabHeight());
|
|
143
|
-
};
|
|
144
|
-
const helpHeight = () =>
|
|
145
|
-
showHelp() ? Math.min(5, Math.max(0, Math.floor(props.height / 3))) : 0;
|
|
132
|
+
(props.model.tabs?.().length ?? 0) > 0 && props.height >= 12
|
|
133
|
+
? 1 +
|
|
134
|
+
Math.min(props.model.tabs!().length, Math.max(1, Math.floor((props.height / 2 - 1) / 3))) *
|
|
135
|
+
3
|
|
136
|
+
: 0;
|
|
146
137
|
const machineHeight = () =>
|
|
147
138
|
Math.max(
|
|
148
139
|
0,
|
|
@@ -152,7 +143,6 @@ export function ApplicationMachineSidebar(props: {
|
|
|
152
143
|
agentHeight() -
|
|
153
144
|
controlsHeight() -
|
|
154
145
|
searchHeight() -
|
|
155
|
-
helpHeight() -
|
|
156
146
|
tabHeight(),
|
|
157
147
|
);
|
|
158
148
|
const active = (row: Row) =>
|
|
@@ -276,6 +266,7 @@ export function ApplicationMachineSidebar(props: {
|
|
|
276
266
|
props.model.onCollapse?.(preferenceKey(group), value);
|
|
277
267
|
};
|
|
278
268
|
const activate = (row: Row, source: "keyboard" | "mouse") => {
|
|
269
|
+
setWorkingFocused(false);
|
|
279
270
|
setSelectedKey(row.key);
|
|
280
271
|
setLocalFocused(true);
|
|
281
272
|
props.model.onFocus?.();
|
|
@@ -315,7 +306,7 @@ export function ApplicationMachineSidebar(props: {
|
|
|
315
306
|
const rowHeight = (row: Row) =>
|
|
316
307
|
(row.agent ? (row.agentHeading ? 3 : 2) : row.serverHeading ? 2 : 1) + sectionGap(row);
|
|
317
308
|
const revealFocusedRow = () => {
|
|
318
|
-
if (!focused() || !scroll) return;
|
|
309
|
+
if (!focused() || (workingFocused() && tabHeight()) || !scroll) return;
|
|
319
310
|
const y = rows()
|
|
320
311
|
.slice(0, index())
|
|
321
312
|
.reduce((sum, row) => sum + rowHeight(row), 0);
|
|
@@ -333,6 +324,14 @@ export function ApplicationMachineSidebar(props: {
|
|
|
333
324
|
useKeyboardRoute((event) => {
|
|
334
325
|
if (!focused() || event.eventType !== "press" || event.meta) return false;
|
|
335
326
|
let key = event.name.toLowerCase();
|
|
327
|
+
if (key === "tab" && tabHeight()) {
|
|
328
|
+
setWorkingFocused((value) => !value);
|
|
329
|
+
event.preventDefault();
|
|
330
|
+
event.stopPropagation();
|
|
331
|
+
return true;
|
|
332
|
+
}
|
|
333
|
+
if (workingFocused() && tabHeight() && !["escape", "?", "question", "/", "a"].includes(key))
|
|
334
|
+
return false;
|
|
336
335
|
if (event.ctrl && key === "d") key = "halfdown";
|
|
337
336
|
else if (event.ctrl && key === "u") key = "halfup";
|
|
338
337
|
else if (!event.ctrl && !event.meta)
|
|
@@ -345,7 +344,7 @@ export function ApplicationMachineSidebar(props: {
|
|
|
345
344
|
l: "right",
|
|
346
345
|
g: event.shift ? "end" : "home",
|
|
347
346
|
"/": "search",
|
|
348
|
-
|
|
347
|
+
[SIDEBAR_ACTIONS.help.key]: "help",
|
|
349
348
|
question: "help",
|
|
350
349
|
} as Record<string, string>
|
|
351
350
|
)[key] ?? key;
|
|
@@ -368,9 +367,9 @@ export function ApplicationMachineSidebar(props: {
|
|
|
368
367
|
"return",
|
|
369
368
|
"space",
|
|
370
369
|
"escape",
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
370
|
+
SIDEBAR_ACTIONS.add.key,
|
|
371
|
+
SIDEBAR_ACTIONS.retry.key,
|
|
372
|
+
SIDEBAR_ACTIONS.disconnect.key,
|
|
374
373
|
"f",
|
|
375
374
|
].includes(key)
|
|
376
375
|
)
|
|
@@ -378,7 +377,7 @@ export function ApplicationMachineSidebar(props: {
|
|
|
378
377
|
event.preventDefault();
|
|
379
378
|
event.stopPropagation();
|
|
380
379
|
if (key === "help") {
|
|
381
|
-
|
|
380
|
+
props.onHelp?.("keyboard");
|
|
382
381
|
return true;
|
|
383
382
|
}
|
|
384
383
|
if (key === "search") {
|
|
@@ -386,15 +385,11 @@ export function ApplicationMachineSidebar(props: {
|
|
|
386
385
|
return true;
|
|
387
386
|
}
|
|
388
387
|
if (key === "escape") {
|
|
389
|
-
if (showHelp()) {
|
|
390
|
-
setShowHelp(false);
|
|
391
|
-
return true;
|
|
392
|
-
}
|
|
393
388
|
setLocalFocused(false);
|
|
394
389
|
props.model.onBlur?.();
|
|
395
390
|
return true;
|
|
396
391
|
}
|
|
397
|
-
if (key ===
|
|
392
|
+
if (key === SIDEBAR_ACTIONS.add.key) {
|
|
398
393
|
props.model.onAddMachine?.();
|
|
399
394
|
return true;
|
|
400
395
|
}
|
|
@@ -409,9 +404,9 @@ export function ApplicationMachineSidebar(props: {
|
|
|
409
404
|
);
|
|
410
405
|
return true;
|
|
411
406
|
}
|
|
412
|
-
if (key ===
|
|
407
|
+
if (key === SIDEBAR_ACTIONS.retry.key || key === SIDEBAR_ACTIONS.disconnect.key) {
|
|
413
408
|
if (row.group.id !== "local") {
|
|
414
|
-
if (key ===
|
|
409
|
+
if (key === SIDEBAR_ACTIONS.retry.key) props.model.onRetryMachine?.(row.group.id);
|
|
415
410
|
else props.model.onDisconnectMachine?.(row.group.id);
|
|
416
411
|
}
|
|
417
412
|
return true;
|
|
@@ -460,43 +455,43 @@ export function ApplicationMachineSidebar(props: {
|
|
|
460
455
|
overflow="hidden"
|
|
461
456
|
>
|
|
462
457
|
<box height={1} flexShrink={0} flexDirection="row" overflow="hidden">
|
|
463
|
-
<text fg={props.theme.roles.text.secondary}>
|
|
458
|
+
<text fg={props.theme.roles.text.secondary}>
|
|
459
|
+
{tabHeight() ? " Workspace" : " Machines"}
|
|
460
|
+
</text>
|
|
464
461
|
<box flexGrow={1} />
|
|
465
462
|
<Show when={props.model.tabs?.().length && props.width >= 28}>
|
|
466
|
-
<KeyHint
|
|
463
|
+
<KeyHint
|
|
464
|
+
theme={props.theme}
|
|
465
|
+
keys={CHROME_ACTIONS.tabs.keys}
|
|
466
|
+
label={CHROME_ACTIONS.tabs.label}
|
|
467
|
+
quiet
|
|
468
|
+
/>
|
|
467
469
|
</Show>
|
|
468
470
|
<KeyHint
|
|
469
471
|
theme={props.theme}
|
|
470
|
-
keys=
|
|
471
|
-
label={props.width >= 20 ?
|
|
472
|
+
keys={SIDEBAR_ACTIONS.help.keys}
|
|
473
|
+
label={props.width >= 20 ? SIDEBAR_ACTIONS.help.label : undefined}
|
|
472
474
|
quiet
|
|
473
475
|
button
|
|
474
|
-
onPress={() =>
|
|
476
|
+
onPress={() => props.onHelp?.("mouse")}
|
|
475
477
|
/>
|
|
476
478
|
</box>
|
|
477
|
-
<
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
size="compact"
|
|
494
|
-
width={3}
|
|
495
|
-
onPress={() => props.model.onCloseTab?.(tab.key)}
|
|
496
|
-
/>
|
|
497
|
-
</box>
|
|
498
|
-
)}
|
|
499
|
-
</For>
|
|
479
|
+
<Show when={tabHeight() > 0}>
|
|
480
|
+
<WorkingSessions
|
|
481
|
+
theme={props.theme}
|
|
482
|
+
rows={props.model.tabs?.() ?? []}
|
|
483
|
+
width={props.width}
|
|
484
|
+
height={tabHeight()}
|
|
485
|
+
focused={focused() && workingFocused()}
|
|
486
|
+
onFocus={() => {
|
|
487
|
+
setWorkingFocused(true);
|
|
488
|
+
setLocalFocused(true);
|
|
489
|
+
props.model.onFocus?.();
|
|
490
|
+
}}
|
|
491
|
+
onOpen={(key) => props.model.onOpenTab?.(key)}
|
|
492
|
+
onClose={props.model.onCloseTab}
|
|
493
|
+
/>
|
|
494
|
+
</Show>
|
|
500
495
|
<scrollbox
|
|
501
496
|
ref={(value) => {
|
|
502
497
|
scroll = value;
|
|
@@ -619,7 +614,9 @@ export function ApplicationMachineSidebar(props: {
|
|
|
619
614
|
: `${connectionDetail(row.group)}${row.group.agents?.length ? " ?" : ""}`
|
|
620
615
|
}
|
|
621
616
|
selected={Boolean((row.session || row.agent) && active(row))}
|
|
622
|
-
focused={Boolean(
|
|
617
|
+
focused={Boolean(
|
|
618
|
+
focused() && !(workingFocused() && tabHeight()) && row.key === selectedKey(),
|
|
619
|
+
)}
|
|
623
620
|
status={
|
|
624
621
|
(row.agent?.attention ?? activity(row).kind === "attention")
|
|
625
622
|
? "blocked"
|
|
@@ -657,18 +654,11 @@ export function ApplicationMachineSidebar(props: {
|
|
|
657
654
|
</scrollbox>
|
|
658
655
|
)}
|
|
659
656
|
</For>
|
|
660
|
-
<Show when={helpHeight() > 0}>
|
|
661
|
-
<text height={helpHeight()} fg={props.theme.roles.text.secondary}>
|
|
662
|
-
{
|
|
663
|
-
"j/k ↑↓ move · g/G ends\nPgUp/Dn · Ctrl-U/D half\nh/l ←→ collapse/expand\nEnter open · / search\nf favorite · Esc terminal"
|
|
664
|
-
}
|
|
665
|
-
</text>
|
|
666
|
-
</Show>
|
|
667
657
|
<Show when={props.model.onOpenSwitcher}>
|
|
668
658
|
<KeyHint
|
|
669
659
|
theme={props.theme}
|
|
670
|
-
keys=
|
|
671
|
-
label="
|
|
660
|
+
keys={CHROME_ACTIONS.sessions.keys}
|
|
661
|
+
label="Browse all sessions"
|
|
672
662
|
width={props.width}
|
|
673
663
|
quiet
|
|
674
664
|
button
|
|
@@ -678,8 +668,8 @@ export function ApplicationMachineSidebar(props: {
|
|
|
678
668
|
<Show when={props.model.onOpenAttention}>
|
|
679
669
|
<KeyHint
|
|
680
670
|
theme={props.theme}
|
|
681
|
-
keys=
|
|
682
|
-
label={
|
|
671
|
+
keys={CHROME_ACTIONS.attention.keys}
|
|
672
|
+
label={`${CHROME_ACTIONS.attention.label} (${props.model.groups().reduce((sum, group) => sum + (group.state === "ready" ? (group.agents ?? []).filter((agent) => agent.attention && !agent.disabled).length : 0), 0)})`}
|
|
683
673
|
width={props.width}
|
|
684
674
|
quiet
|
|
685
675
|
button
|
|
@@ -696,8 +686,8 @@ export function ApplicationMachineSidebar(props: {
|
|
|
696
686
|
</text>
|
|
697
687
|
<KeyHint
|
|
698
688
|
theme={props.theme}
|
|
699
|
-
keys=
|
|
700
|
-
label=
|
|
689
|
+
keys={SIDEBAR_ACTIONS.retry.keys}
|
|
690
|
+
label={SIDEBAR_ACTIONS.retry.label}
|
|
701
691
|
width={props.width}
|
|
702
692
|
quiet
|
|
703
693
|
button
|
|
@@ -705,8 +695,8 @@ export function ApplicationMachineSidebar(props: {
|
|
|
705
695
|
/>
|
|
706
696
|
<KeyHint
|
|
707
697
|
theme={props.theme}
|
|
708
|
-
keys=
|
|
709
|
-
label=
|
|
698
|
+
keys={SIDEBAR_ACTIONS.disconnect.keys}
|
|
699
|
+
label={SIDEBAR_ACTIONS.disconnect.label}
|
|
710
700
|
width={props.width}
|
|
711
701
|
quiet
|
|
712
702
|
button
|
|
@@ -719,8 +709,8 @@ export function ApplicationMachineSidebar(props: {
|
|
|
719
709
|
{(add) => (
|
|
720
710
|
<KeyHint
|
|
721
711
|
theme={props.theme}
|
|
722
|
-
keys=
|
|
723
|
-
label=
|
|
712
|
+
keys={SIDEBAR_ACTIONS.add.keys}
|
|
713
|
+
label={SIDEBAR_ACTIONS.add.label}
|
|
724
714
|
width={props.width}
|
|
725
715
|
quiet
|
|
726
716
|
button
|
|
@@ -39,7 +39,7 @@ export function createApplicationPaletteCommandOwner(options: {
|
|
|
39
39
|
readonly disabledReason?: (command: ApplicationPaletteCommand) => string | null;
|
|
40
40
|
readonly targetKey?: () => string;
|
|
41
41
|
}) {
|
|
42
|
-
const [referencePage, setReferencePage] = createSignal<"shortcuts" | "changes">();
|
|
42
|
+
const [referencePage, setReferencePage] = createSignal<"shortcuts" | "changes" | "help">();
|
|
43
43
|
const [closeArmed, setCloseArmed] = createSignal(false);
|
|
44
44
|
const [busy, setBusy] = createSignal(false);
|
|
45
45
|
let armedTarget: string | undefined;
|
|
@@ -66,9 +66,11 @@ export function createApplicationPaletteCommandOwner(options: {
|
|
|
66
66
|
confirmed = false,
|
|
67
67
|
): void => {
|
|
68
68
|
if (busy()) return;
|
|
69
|
-
if (command === "shortcuts" || command === "whats-new") {
|
|
69
|
+
if (command === "help" || command === "shortcuts" || command === "whats-new") {
|
|
70
70
|
setCloseArmed(false);
|
|
71
|
-
setReferencePage(
|
|
71
|
+
setReferencePage(
|
|
72
|
+
command === "help" ? "help" : command === "shortcuts" ? "shortcuts" : "changes",
|
|
73
|
+
);
|
|
72
74
|
return;
|
|
73
75
|
}
|
|
74
76
|
if (command === "switch-session") {
|
|
@@ -7,6 +7,7 @@ import { OverlaySearchField } from "../ui/overlay-search-field.tsx";
|
|
|
7
7
|
import { overlaySurfaceMetrics } from "../ui/overlay-model.ts";
|
|
8
8
|
import { useKeyboardRoute, usePasteRoute } from "../ui/keyboard-router.tsx";
|
|
9
9
|
import { clipTerminal, terminalDisplayWidth } from "../terminal-text.ts";
|
|
10
|
+
import { CHROME_ACTIONS } from "../workspace/application-action-descriptions.ts";
|
|
10
11
|
import { APPLICATION_SHORTCUTS } from "../workspace/application-shortcuts.ts";
|
|
11
12
|
import { commandSearchMatch } from "../workspace/application-command-description.ts";
|
|
12
13
|
import {
|
|
@@ -15,6 +16,24 @@ import {
|
|
|
15
16
|
} from "./application-pane-rename-input.ts";
|
|
16
17
|
|
|
17
18
|
const releases = [
|
|
19
|
+
{
|
|
20
|
+
version: "2.9.0-beta.41",
|
|
21
|
+
lines: [
|
|
22
|
+
"Working sessions with clear machine/server context and shared status icons.",
|
|
23
|
+
"New-result markers for observed background completions; open to acknowledge.",
|
|
24
|
+
"Tab switches sidebar sections. Browse all sessions with F6.",
|
|
25
|
+
"Using tmux-ide in Commands, with reversible Help and shortcut navigation.",
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
version: "2.9.0-beta.40",
|
|
30
|
+
lines: [
|
|
31
|
+
"Using tmux-ide: a scrollable guide to machines, sessions and agents.",
|
|
32
|
+
"Home filters for All, Working and Needs attention, with a clear Open action.",
|
|
33
|
+
"Consistent shortcut buttons in the sidebar and footer.",
|
|
34
|
+
"Segmented pane headers and dimmed backgrounds for dialogs.",
|
|
35
|
+
],
|
|
36
|
+
},
|
|
18
37
|
{
|
|
19
38
|
version: "2.9.0-beta.30",
|
|
20
39
|
lines: [
|
|
@@ -44,7 +63,7 @@ const releases = [
|
|
|
44
63
|
type Row = { kind: "heading" | "text" | "action" | "gap"; label: string; detail?: string };
|
|
45
64
|
|
|
46
65
|
export function ApplicationReferenceSheet(props: {
|
|
47
|
-
page: "shortcuts" | "changes";
|
|
66
|
+
page: "shortcuts" | "changes" | "help";
|
|
48
67
|
width: number;
|
|
49
68
|
height: number;
|
|
50
69
|
theme: SemanticThemeSnapshot;
|
|
@@ -78,7 +97,55 @@ export function ApplicationReferenceSheet(props: {
|
|
|
78
97
|
}
|
|
79
98
|
if (!result.length) result.push({ kind: "text", label: "No matching shortcuts" });
|
|
80
99
|
} else {
|
|
81
|
-
|
|
100
|
+
const sections =
|
|
101
|
+
page() === "help"
|
|
102
|
+
? [
|
|
103
|
+
{
|
|
104
|
+
version: "Find your work",
|
|
105
|
+
lines: [
|
|
106
|
+
"Machines are your local computer and connected SSH hosts. Each session contains terminal windows and panes.",
|
|
107
|
+
"Open a session or agent in the sidebar to jump to its terminal. Home brings agents from your connected machines together.",
|
|
108
|
+
],
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
version: "Move around",
|
|
112
|
+
lines: [
|
|
113
|
+
[
|
|
114
|
+
CHROME_ACTIONS.home,
|
|
115
|
+
CHROME_ACTIONS.terminals,
|
|
116
|
+
CHROME_ACTIONS.sessions,
|
|
117
|
+
CHROME_ACTIONS.attention,
|
|
118
|
+
]
|
|
119
|
+
.map((action) => `${action.keys} ${action.label}`)
|
|
120
|
+
.join(" · "),
|
|
121
|
+
`${CHROME_ACTIONS.sidebar.keys} shows or hides the sidebar. ${CHROME_ACTIONS.commands.keys} opens Commands for actions, appearance and keyboard shortcuts.`,
|
|
122
|
+
],
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
version: "Use the sidebar",
|
|
126
|
+
lines: [
|
|
127
|
+
"Click a row to open it. With the sidebar focused, use ↑↓ or j/k to move and Enter to open.",
|
|
128
|
+
"←→ or h/l collapse/expand machines. / searches sessions. f toggles a session favorite. Esc returns to the terminal.",
|
|
129
|
+
"PgUp/PgDn scroll a page; Ctrl-U/D scroll half a page. g/G jump to the first or last row.",
|
|
130
|
+
],
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
version: "Follow your agents",
|
|
134
|
+
lines: [
|
|
135
|
+
"On Home, search by name and choose All, Working or Needs attention. Select an agent and press Enter to open its pane.",
|
|
136
|
+
"Unavailable means the connection or observation is missing; it does not mean the agent has stopped.",
|
|
137
|
+
],
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
version: "Connect another machine",
|
|
141
|
+
lines: [
|
|
142
|
+
"Choose Add machine in the sidebar and enter an SSH alias or user@host. Set up SSH key access and host trust first.",
|
|
143
|
+
"Run tmux-ide --headless on the remote machine before connecting.",
|
|
144
|
+
],
|
|
145
|
+
},
|
|
146
|
+
]
|
|
147
|
+
: releases;
|
|
148
|
+
for (const release of sections) {
|
|
82
149
|
if (result.length) result.push({ kind: "gap", label: "" });
|
|
83
150
|
result.push({ kind: "heading", label: release.version });
|
|
84
151
|
for (const line of release.lines) {
|
|
@@ -115,7 +182,8 @@ export function ApplicationReferenceSheet(props: {
|
|
|
115
182
|
const key = event.name.toLowerCase();
|
|
116
183
|
if (key === "escape") props.onClose();
|
|
117
184
|
else if (key === "tab") {
|
|
118
|
-
|
|
185
|
+
const pages = ["help", "shortcuts", "changes"] as const;
|
|
186
|
+
setPage(pages[(pages.indexOf(page()) + (event.shift ? 2 : 1)) % pages.length]!);
|
|
119
187
|
setOffset(0);
|
|
120
188
|
} else if (key === "down") move(1);
|
|
121
189
|
else if (key === "up") move(-1);
|
|
@@ -139,8 +207,14 @@ export function ApplicationReferenceSheet(props: {
|
|
|
139
207
|
viewportHeight={props.height}
|
|
140
208
|
width={metrics().width}
|
|
141
209
|
height={metrics().height}
|
|
142
|
-
title={
|
|
143
|
-
|
|
210
|
+
title={
|
|
211
|
+
page() === "help"
|
|
212
|
+
? "Using tmux-ide"
|
|
213
|
+
: page() === "shortcuts"
|
|
214
|
+
? "Keyboard shortcuts"
|
|
215
|
+
: "What's new"
|
|
216
|
+
}
|
|
217
|
+
footer={`Tab ${page() === "help" ? "shortcuts" : page() === "shortcuts" ? "what’s new" : "help"} · ↑↓ scroll · Esc back`}
|
|
144
218
|
zIndex={100}
|
|
145
219
|
onDismiss={props.onClose}
|
|
146
220
|
>
|
|
@@ -160,6 +234,7 @@ export function ApplicationReferenceSheet(props: {
|
|
|
160
234
|
flexDirection="column"
|
|
161
235
|
overflow="hidden"
|
|
162
236
|
onMouseScroll={(event) => {
|
|
237
|
+
if (!event.scroll) return;
|
|
163
238
|
event.preventDefault();
|
|
164
239
|
move(event.scroll.direction === "up" ? -3 : 3);
|
|
165
240
|
}}
|
|
@@ -51,8 +51,8 @@ export interface ApplicationCatalogShellProps {
|
|
|
51
51
|
readonly paletteSelection?: Accessor<number>;
|
|
52
52
|
readonly palettePreviewActive?: Accessor<boolean>;
|
|
53
53
|
readonly onPaletteModalChange?: (open: boolean) => void;
|
|
54
|
-
readonly paletteReferencePage?: Accessor<"shortcuts" | "changes" | undefined>;
|
|
55
|
-
readonly onPaletteReferenceChange?: (page: "shortcuts" | "changes" | undefined) => void;
|
|
54
|
+
readonly paletteReferencePage?: Accessor<"shortcuts" | "changes" | "help" | undefined>;
|
|
55
|
+
readonly onPaletteReferenceChange?: (page: "shortcuts" | "changes" | "help" | undefined) => void;
|
|
56
56
|
readonly paletteKeyboardHint?: Accessor<string>;
|
|
57
57
|
readonly paletteQuery?: Accessor<string>;
|
|
58
58
|
readonly paletteDisabledReason?: (command: ApplicationPaletteCommand) => string | null;
|
|
@@ -418,6 +418,10 @@ export function ApplicationCatalogShell(props: ApplicationCatalogShellProps): JS
|
|
|
418
418
|
{(model) => (
|
|
419
419
|
<ApplicationMachineSidebar
|
|
420
420
|
model={model()}
|
|
421
|
+
onHelp={(source) => {
|
|
422
|
+
props.onSetPaletteOpen(true, source);
|
|
423
|
+
props.onPaletteReferenceChange?.("help");
|
|
424
|
+
}}
|
|
421
425
|
width={chrome().sidebar.width}
|
|
422
426
|
height={chrome().sidebar.height}
|
|
423
427
|
theme={props.theme}
|
|
@@ -106,8 +106,8 @@ export function MinimalPalette(props: {
|
|
|
106
106
|
readonly title?: string;
|
|
107
107
|
readonly onViewport?: (rows: number) => void;
|
|
108
108
|
readonly onFavorite?: (command: ApplicationPaletteCommand) => void;
|
|
109
|
-
readonly referencePage?: "shortcuts" | "changes";
|
|
110
|
-
readonly onReferenceChange?: (page: "shortcuts" | "changes" | undefined) => void;
|
|
109
|
+
readonly referencePage?: "shortcuts" | "changes" | "help";
|
|
110
|
+
readonly onReferenceChange?: (page: "shortcuts" | "changes" | "help" | undefined) => void;
|
|
111
111
|
readonly keyboardHint?: string;
|
|
112
112
|
readonly previewActive?: boolean;
|
|
113
113
|
readonly onModalChange?: (open: boolean) => void;
|
|
@@ -123,8 +123,8 @@ export function MinimalPalette(props: {
|
|
|
123
123
|
}): JSX.Element {
|
|
124
124
|
const [expanded, setExpanded] = createSignal(false);
|
|
125
125
|
const [modal, setModal] = createSignal(false);
|
|
126
|
-
const [sheet, setSheet] = createSignal<"shortcuts" | "changes">();
|
|
127
|
-
const openSheet = (page: "shortcuts" | "changes" | undefined) => {
|
|
126
|
+
const [sheet, setSheet] = createSignal<"shortcuts" | "changes" | "help">();
|
|
127
|
+
const openSheet = (page: "shortcuts" | "changes" | "help" | undefined) => {
|
|
128
128
|
props.onReferenceChange?.(page);
|
|
129
129
|
setSheet(page);
|
|
130
130
|
setModal(!!page);
|
|
@@ -344,7 +344,8 @@ export function MinimalPalette(props: {
|
|
|
344
344
|
onPress={() => {
|
|
345
345
|
props.onSelect?.(row().index);
|
|
346
346
|
const command = row().command;
|
|
347
|
-
if (command === "
|
|
347
|
+
if (command === "help") openSheet("help");
|
|
348
|
+
else if (command === "shortcuts" || command === "whats-new")
|
|
348
349
|
openSheet(PALETTE_REFERENCE_COMMANDS[command].page);
|
|
349
350
|
else props.onActivate(command);
|
|
350
351
|
}}
|
|
@@ -100,8 +100,8 @@ export interface ApplicationShellViewProps {
|
|
|
100
100
|
readonly paletteOpen: Accessor<boolean>;
|
|
101
101
|
readonly paneRenameDialog?: Accessor<ApplicationPaneRenameDraft | null>;
|
|
102
102
|
readonly paletteSelection?: Accessor<number>;
|
|
103
|
-
readonly paletteReferencePage?: Accessor<"shortcuts" | "changes" | undefined>;
|
|
104
|
-
readonly onPaletteReferenceChange?: (page: "shortcuts" | "changes" | undefined) => void;
|
|
103
|
+
readonly paletteReferencePage?: Accessor<"shortcuts" | "changes" | "help" | undefined>;
|
|
104
|
+
readonly onPaletteReferenceChange?: (page: "shortcuts" | "changes" | "help" | undefined) => void;
|
|
105
105
|
readonly paletteKeyboardHint?: Accessor<string>;
|
|
106
106
|
readonly palettePreviewActive?: Accessor<boolean>;
|
|
107
107
|
readonly onPaletteModalChange?: (open: boolean) => void;
|
|
@@ -455,6 +455,10 @@ export function ApplicationShellView(props: ApplicationShellViewProps): JSX.Elem
|
|
|
455
455
|
{(model) => (
|
|
456
456
|
<ApplicationMachineSidebar
|
|
457
457
|
model={model()}
|
|
458
|
+
onHelp={(source) => {
|
|
459
|
+
props.onSetPaletteOpen(true, source);
|
|
460
|
+
props.onPaletteReferenceChange?.("help");
|
|
461
|
+
}}
|
|
458
462
|
agentRows={
|
|
459
463
|
model()
|
|
460
464
|
.groups()
|
|
@@ -2,6 +2,8 @@ import { batch, createEffect, createSignal, onCleanup } from "solid-js";
|
|
|
2
2
|
import type { AgentActivity } from "@tmux-ide/contracts";
|
|
3
3
|
import type { SemanticThemeSnapshot } from "../theme.ts";
|
|
4
4
|
|
|
5
|
+
import { UI_ICONS } from "./icons.ts";
|
|
6
|
+
|
|
5
7
|
const FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
6
8
|
const listeners = new Set<(frame: number) => void>();
|
|
7
9
|
let timer: ReturnType<typeof setInterval> | undefined;
|
|
@@ -46,24 +48,24 @@ export function createAgentStatusMarker(options: {
|
|
|
46
48
|
if (animated()) onCleanup(subscribe(setCurrent));
|
|
47
49
|
});
|
|
48
50
|
return () => {
|
|
49
|
-
if (options.unavailable?.()) return
|
|
50
|
-
if (options.attention?.()) return
|
|
51
|
+
if (options.unavailable?.()) return UI_ICONS.unknown;
|
|
52
|
+
if (options.attention?.()) return UI_ICONS.attention;
|
|
51
53
|
switch (options.status()) {
|
|
52
54
|
case "running":
|
|
53
55
|
case "working":
|
|
54
56
|
return animated() ? FRAMES[current()]! : options.theme().glyphs.active;
|
|
55
57
|
case "waiting":
|
|
56
58
|
case "blocked":
|
|
57
|
-
return
|
|
59
|
+
return UI_ICONS.attention;
|
|
58
60
|
case "complete":
|
|
59
61
|
case "done":
|
|
60
62
|
return options.theme().glyphs.check;
|
|
61
63
|
case "failed":
|
|
62
|
-
return
|
|
64
|
+
return UI_ICONS.failed;
|
|
63
65
|
case "idle":
|
|
64
66
|
return options.theme().glyphs.inactive;
|
|
65
67
|
default:
|
|
66
|
-
return
|
|
68
|
+
return UI_ICONS.unknown;
|
|
67
69
|
}
|
|
68
70
|
};
|
|
69
71
|
}
|
|
@@ -57,3 +57,7 @@ export {
|
|
|
57
57
|
type StatusBarSegmentProps,
|
|
58
58
|
type StatusSegmentProps,
|
|
59
59
|
} from "./status-bar.tsx";
|
|
60
|
+
|
|
61
|
+
export { UI_ICONS } from "./icons.ts";
|
|
62
|
+
export { SessionRow, sessionRowStatus, type SessionRowModel } from "./session-row.tsx";
|
|
63
|
+
export { WorkingSessions } from "./working-sessions.tsx";
|