@ohzw/worktree-command-tui 0.1.0 → 0.1.2
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/dist/app.d.ts +3 -2
- package/dist/app.js +458 -42
- package/dist/components/ActionPanel.d.ts +6 -2
- package/dist/components/ActionPanel.js +141 -82
- package/dist/components/ContextBar.d.ts +4 -1
- package/dist/components/ContextBar.js +37 -4
- package/dist/components/FloatingLogWindow.d.ts +7 -0
- package/dist/components/FloatingLogWindow.js +5 -0
- package/dist/components/HelpWindow.d.ts +7 -0
- package/dist/components/HelpWindow.js +29 -0
- package/dist/components/LogPanel.d.ts +22 -0
- package/dist/components/LogPanel.js +260 -0
- package/dist/components/WorktreeList.d.ts +3 -1
- package/dist/components/WorktreeList.js +25 -30
- package/dist/core/command-runner.d.ts +11 -0
- package/dist/core/command-runner.js +44 -0
- package/dist/core/config-lifecycle.d.ts +25 -0
- package/dist/core/config-lifecycle.js +143 -0
- package/dist/core/config.d.ts +2 -3
- package/dist/core/config.js +0 -48
- package/dist/core/git-metadata.d.ts +25 -0
- package/dist/core/git-metadata.js +84 -0
- package/dist/core/git-worktrees.d.ts +2 -1
- package/dist/core/git-worktrees.js +30 -11
- package/dist/core/github-metadata.d.ts +14 -0
- package/dist/core/github-metadata.js +137 -0
- package/dist/core/init.d.ts +3 -2
- package/dist/core/init.js +9 -57
- package/dist/core/log-reader.d.ts +7 -0
- package/dist/core/log-reader.js +43 -0
- package/dist/core/runtime-state.d.ts +42 -0
- package/dist/core/runtime-state.js +125 -0
- package/dist/core/runtime.d.ts +20 -33
- package/dist/core/runtime.js +116 -173
- package/dist/core/tui-interaction.d.ts +31 -0
- package/dist/core/tui-interaction.js +59 -0
- package/dist/core/worktree-projection.d.ts +76 -0
- package/dist/core/worktree-projection.js +124 -0
- package/dist/main.js +24 -2
- package/dist/render-options.d.ts +1 -0
- package/dist/render-options.js +1 -0
- package/dist/repro.d.ts +1 -0
- package/dist/repro.js +13 -0
- package/dist/terminal/viewport.d.ts +15 -0
- package/dist/terminal/viewport.js +49 -0
- package/dist/ui-theme.d.ts +3 -0
- package/dist/ui-theme.js +38 -0
- package/package.json +2 -1
|
@@ -1,129 +1,188 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
return value
|
|
8
|
-
.replace(/[\r\n\t\u2028\u2029]+/g, ' ')
|
|
9
|
-
.replace(/[\u0000-\u001f\u007f-\u009f]/g, '')
|
|
10
|
-
.replace(/\p{Cf}/gu, '')
|
|
11
|
-
.replace(/\s+/g, ' ')
|
|
12
|
-
.trim();
|
|
3
|
+
import { getOrderedNonActiveTags, projectAction, projectNote, projectPullRequest, projectUpstream, projectWorkingTree, sanitizeInlineText, } from '../core/worktree-projection.js';
|
|
4
|
+
import { getScrollbarThumbRows, sliceListViewport } from '../terminal/viewport.js';
|
|
5
|
+
export function getActionVariant(selectedRow, activePath) {
|
|
6
|
+
return projectAction(selectedRow, activePath).severity;
|
|
13
7
|
}
|
|
14
8
|
function formatUpstream(selectedRow) {
|
|
15
|
-
|
|
9
|
+
const upstream = projectUpstream(selectedRow);
|
|
10
|
+
if (upstream.kind === 'unavailable') {
|
|
16
11
|
return 'unavailable';
|
|
17
12
|
}
|
|
18
|
-
if (
|
|
13
|
+
if (upstream.kind === 'none') {
|
|
19
14
|
return '-';
|
|
20
15
|
}
|
|
21
|
-
return `${
|
|
16
|
+
return `${upstream.branch} (↑${upstream.ahead} ↓${upstream.behind})`;
|
|
22
17
|
}
|
|
23
|
-
function
|
|
24
|
-
if (
|
|
25
|
-
return '
|
|
18
|
+
function getTagColor(tag) {
|
|
19
|
+
if (tag === 'active') {
|
|
20
|
+
return 'green';
|
|
26
21
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return 'clean';
|
|
22
|
+
if (tag === 'external') {
|
|
23
|
+
return 'yellow';
|
|
30
24
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
parts.push(`index ${staged}`);
|
|
25
|
+
if (tag === 'main') {
|
|
26
|
+
return 'blue';
|
|
34
27
|
}
|
|
35
|
-
if (
|
|
36
|
-
|
|
28
|
+
if (tag === 'invalid') {
|
|
29
|
+
return 'red';
|
|
37
30
|
}
|
|
38
|
-
|
|
39
|
-
|
|
31
|
+
return 'magenta';
|
|
32
|
+
}
|
|
33
|
+
function getTagLabel(tag) {
|
|
34
|
+
return tag === 'main' ? 'root' : tag;
|
|
35
|
+
}
|
|
36
|
+
function getWorkingTreePartLabel(kind) {
|
|
37
|
+
if (kind === 'staged') {
|
|
38
|
+
return 'index';
|
|
40
39
|
}
|
|
41
|
-
if (
|
|
42
|
-
|
|
40
|
+
if (kind === 'unstaged') {
|
|
41
|
+
return 'worktree';
|
|
43
42
|
}
|
|
44
|
-
return
|
|
43
|
+
return kind;
|
|
45
44
|
}
|
|
46
|
-
function
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
if (selectedRow.pullRequest.kind === 'unavailable') {
|
|
45
|
+
function formatWorkingTree(selectedRow) {
|
|
46
|
+
const workingTree = projectWorkingTree(selectedRow);
|
|
47
|
+
if (workingTree.kind === 'unavailable') {
|
|
51
48
|
return 'unavailable';
|
|
52
49
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
function getPullRequestLabel(selectedRow) {
|
|
57
|
-
if (selectedRow.pullRequest?.kind === 'found' && selectedRow.pullRequest.state !== 'OPEN') {
|
|
58
|
-
return 'Last PR';
|
|
50
|
+
if (workingTree.kind === 'clean') {
|
|
51
|
+
return 'clean';
|
|
59
52
|
}
|
|
60
|
-
|
|
53
|
+
const parts = workingTree.parts.map(part => `${getWorkingTreePartLabel(part.kind)} ${part.count}`);
|
|
54
|
+
return `dirty (${parts.join(' · ')})`;
|
|
61
55
|
}
|
|
62
|
-
function
|
|
63
|
-
if (
|
|
64
|
-
return '
|
|
56
|
+
function formatUtcDateTime(timestampMs) {
|
|
57
|
+
if (timestampMs === undefined || !Number.isFinite(timestampMs)) {
|
|
58
|
+
return '-';
|
|
65
59
|
}
|
|
66
|
-
|
|
60
|
+
const iso = new Date(timestampMs).toISOString();
|
|
61
|
+
return `${iso.slice(0, 10)} ${iso.slice(11, 19)} UTC`;
|
|
67
62
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
63
|
+
function formatPullRequest(selectedRow) {
|
|
64
|
+
const pullRequest = projectPullRequest(selectedRow);
|
|
65
|
+
if (pullRequest.kind === 'none') {
|
|
66
|
+
return 'none';
|
|
67
|
+
}
|
|
68
|
+
if (pullRequest.kind === 'unavailable') {
|
|
69
|
+
return 'unavailable';
|
|
71
70
|
}
|
|
72
|
-
|
|
71
|
+
const draft = pullRequest.isDraft ? 'draft/' : '';
|
|
72
|
+
const stateText = pullRequest.state.toLowerCase();
|
|
73
|
+
return `#${pullRequest.number} ${draft}${stateText} → ${pullRequest.baseBranch}`;
|
|
74
|
+
}
|
|
75
|
+
function getPullRequestColorFromProjection(pullRequest) {
|
|
76
|
+
if (pullRequest.kind === 'unavailable') {
|
|
73
77
|
return 'red';
|
|
74
78
|
}
|
|
75
|
-
if (
|
|
79
|
+
if (pullRequest.kind !== 'found' || pullRequest.isHistorical) {
|
|
76
80
|
return undefined;
|
|
77
81
|
}
|
|
78
|
-
return
|
|
82
|
+
return pullRequest.isDraft ? 'yellow' : 'green';
|
|
83
|
+
}
|
|
84
|
+
export function getPullRequestColor(selectedRow) {
|
|
85
|
+
return getPullRequestColorFromProjection(projectPullRequest(selectedRow));
|
|
86
|
+
}
|
|
87
|
+
function getPullRequestLabel(pullRequest) {
|
|
88
|
+
return pullRequest.kind === 'found' && pullRequest.isHistorical ? 'Last PR' : 'PR';
|
|
89
|
+
}
|
|
90
|
+
function getPullRequestTitleLabel(pullRequest) {
|
|
91
|
+
return pullRequest.kind === 'found' && pullRequest.isHistorical ? 'Last PR Title' : 'PR Title';
|
|
92
|
+
}
|
|
93
|
+
function getPullRequestDimColor(pullRequest) {
|
|
94
|
+
return pullRequest.kind === 'found' && pullRequest.isHistorical;
|
|
79
95
|
}
|
|
80
96
|
function getActionMessage(selectedRow, activePath) {
|
|
81
|
-
|
|
97
|
+
const action = projectAction(selectedRow, activePath);
|
|
98
|
+
if (action.kind === 'blocked') {
|
|
82
99
|
return 'Cannot start this worktree.';
|
|
83
100
|
}
|
|
84
|
-
if (
|
|
101
|
+
if (action.kind === 'active') {
|
|
85
102
|
return 'Already active. Press s to stop the current session.';
|
|
86
103
|
}
|
|
87
104
|
return 'Press Enter to start here and switch the active session.';
|
|
88
105
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if ((selectedRow.workingTree?.conflicts ?? 0) > 0) {
|
|
94
|
-
return 'red';
|
|
106
|
+
function getNotes(selectedRow) {
|
|
107
|
+
const note = projectNote(selectedRow);
|
|
108
|
+
if (note.kind === 'invalid') {
|
|
109
|
+
return note.invalidReason;
|
|
95
110
|
}
|
|
96
|
-
if (
|
|
97
|
-
|
|
98
|
-
|| (selectedRow.workingTree?.untracked ?? 0) > 0) {
|
|
99
|
-
return 'yellow';
|
|
111
|
+
if (note.kind === 'external') {
|
|
112
|
+
return 'External worktree managed outside the main checkout path.';
|
|
100
113
|
}
|
|
101
|
-
return
|
|
114
|
+
return 'Ready to launch with the configured command in this worktree.';
|
|
102
115
|
}
|
|
103
|
-
function
|
|
104
|
-
if (
|
|
105
|
-
return
|
|
116
|
+
function getVariantColor(variant) {
|
|
117
|
+
if (variant === 'success') {
|
|
118
|
+
return 'green';
|
|
106
119
|
}
|
|
107
|
-
if (
|
|
108
|
-
return '
|
|
120
|
+
if (variant === 'error') {
|
|
121
|
+
return 'red';
|
|
109
122
|
}
|
|
110
|
-
|
|
111
|
-
|
|
123
|
+
return 'blue';
|
|
124
|
+
}
|
|
125
|
+
function getVariantIcon(variant) {
|
|
126
|
+
if (variant === 'success') {
|
|
127
|
+
return '✓';
|
|
112
128
|
}
|
|
113
|
-
|
|
129
|
+
if (variant === 'error') {
|
|
130
|
+
return '✘';
|
|
131
|
+
}
|
|
132
|
+
return 'ℹ';
|
|
114
133
|
}
|
|
115
|
-
function
|
|
116
|
-
return
|
|
134
|
+
function section(label) {
|
|
135
|
+
return { text: `[${label}]`, color: 'cyan', bold: true };
|
|
117
136
|
}
|
|
118
|
-
|
|
137
|
+
function divider() {
|
|
138
|
+
return { text: ' ', dimColor: true };
|
|
139
|
+
}
|
|
140
|
+
function getPanelLines(selectedRow, activePath, setupAvailable, compactDetails) {
|
|
119
141
|
if (!selectedRow) {
|
|
120
|
-
return
|
|
142
|
+
return [{ text: 'No worktrees found.', dimColor: true }];
|
|
121
143
|
}
|
|
122
|
-
const
|
|
144
|
+
const lines = [section('Identity')];
|
|
123
145
|
const showFullPath = !compactDetails && selectedRow.shortPath !== selectedRow.path;
|
|
124
146
|
const showTags = !compactDetails;
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
147
|
+
const pullRequest = projectPullRequest(selectedRow);
|
|
148
|
+
const pullRequestTitle = pullRequest.kind === 'found' && !compactDetails ? pullRequest.title : null;
|
|
149
|
+
lines.push({ text: `Branch: ${sanitizeInlineText(selectedRow.branch)}`, bold: true }, { text: `Path: ${sanitizeInlineText(selectedRow.shortPath)}` });
|
|
150
|
+
if (showFullPath) {
|
|
151
|
+
lines.push({ text: `Full Path: ${sanitizeInlineText(selectedRow.path)}` });
|
|
152
|
+
}
|
|
153
|
+
lines.push({ text: `HEAD: ${selectedRow.headSha || '-'}` });
|
|
154
|
+
lines.push({ text: `Branch Created: ${formatUtcDateTime(selectedRow.branchCreatedAtMs)}` });
|
|
155
|
+
if (showTags) {
|
|
156
|
+
for (const { tag } of getOrderedNonActiveTags(selectedRow.tags)) {
|
|
157
|
+
lines.push({ text: getTagLabel(tag).toUpperCase(), color: getTagColor(tag) });
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
lines.push(divider(), section('Git / PR'), { text: `Upstream: ${formatUpstream(selectedRow)}` }, { text: `Status: ${formatWorkingTree(selectedRow)}` }, {
|
|
161
|
+
text: `${getPullRequestLabel(pullRequest)}: ${formatPullRequest(selectedRow)}`,
|
|
162
|
+
color: getPullRequestColorFromProjection(pullRequest),
|
|
163
|
+
dimColor: getPullRequestDimColor(pullRequest),
|
|
164
|
+
});
|
|
165
|
+
if (pullRequestTitle) {
|
|
166
|
+
lines.push({ text: `${getPullRequestTitleLabel(pullRequest)}: ${pullRequestTitle}`, dimColor: getPullRequestDimColor(pullRequest) });
|
|
167
|
+
}
|
|
168
|
+
const actionVariant = getActionVariant(selectedRow, activePath);
|
|
169
|
+
const noteVariant = projectNote(selectedRow).severity;
|
|
170
|
+
lines.push(divider(), section('Action'), { text: `${getVariantIcon(actionVariant)} ${getActionMessage(selectedRow, activePath)}`, color: getVariantColor(actionVariant) });
|
|
171
|
+
if (setupAvailable) {
|
|
172
|
+
lines.push({ text: 'ℹ Press i to run setup in this worktree.', color: 'blue' });
|
|
173
|
+
}
|
|
174
|
+
lines.push(section('Notes'), { text: `${getVariantIcon(noteVariant)} ${getNotes(selectedRow)}`, color: getVariantColor(noteVariant) });
|
|
175
|
+
return lines;
|
|
176
|
+
}
|
|
177
|
+
export function ActionPanel({ selectedRow, activePath, setupAvailable, stacked, width, height, compactDetails, scrollOffset = 0, }) {
|
|
178
|
+
const lines = getPanelLines(selectedRow, activePath, setupAvailable, compactDetails ?? false);
|
|
179
|
+
const viewport = height === undefined ? undefined : sliceListViewport(lines, height - 3, scrollOffset);
|
|
180
|
+
const contentViewportHeight = viewport?.viewportHeight;
|
|
181
|
+
const effectiveScrollOffset = viewport?.scrollOffset ?? 0;
|
|
182
|
+
const visibleLines = viewport?.visibleItems ?? lines;
|
|
183
|
+
const showScrollbar = viewport !== undefined && lines.length > viewport.viewportHeight;
|
|
184
|
+
const scrollbarThumbRows = showScrollbar
|
|
185
|
+
? getScrollbarThumbRows(lines.length, viewport.viewportHeight, viewport.scrollOffset)
|
|
186
|
+
: new Set();
|
|
187
|
+
return (_jsxs(Box, { width: width, height: height, flexGrow: stacked ? 0 : 1, flexShrink: 1, borderStyle: "round", borderColor: "magenta", flexDirection: "column", paddingX: 1, overflow: "hidden", children: [_jsx(Text, { bold: true, color: "magenta", wrap: "truncate-end", children: "Selection / Action" }), _jsx(Box, { height: contentViewportHeight, flexDirection: "column", overflow: "hidden", children: visibleLines.map((line, index) => (_jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { flexGrow: 1, flexShrink: 1, children: _jsx(Text, { color: line.color, dimColor: line.dimColor, bold: line.bold, wrap: "truncate-end", children: line.text }) }), showScrollbar ? (_jsx(Text, { color: scrollbarThumbRows.has(index) ? 'magenta' : 'gray', dimColor: !scrollbarThumbRows.has(index), children: scrollbarThumbRows.has(index) ? '█' : '│' })) : null] }, `${effectiveScrollOffset + index}-${line.text}`))) })] }));
|
|
129
188
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { AppStatus } from '../core/runtime.js';
|
|
3
|
-
export declare function ContextBar({ status }: {
|
|
3
|
+
export declare function ContextBar({ status, setupAvailable, editorAvailable, confirmationOpen, }: {
|
|
4
4
|
status: AppStatus;
|
|
5
|
+
setupAvailable: boolean;
|
|
6
|
+
editorAvailable: boolean;
|
|
7
|
+
confirmationOpen: boolean;
|
|
5
8
|
}): React.JSX.Element;
|
|
@@ -1,12 +1,45 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React from 'react';
|
|
2
3
|
import { Box, Text } from 'ink';
|
|
3
|
-
|
|
4
|
+
import { Spinner } from '@inkjs/ui';
|
|
5
|
+
const KIND_TO_ICON = {
|
|
6
|
+
idle: 'ℹ',
|
|
7
|
+
starting: '⚠',
|
|
8
|
+
'setting-up': '⚠',
|
|
9
|
+
running: '✓',
|
|
10
|
+
stopping: '⚠',
|
|
11
|
+
error: '✘',
|
|
12
|
+
};
|
|
13
|
+
const KIND_TO_COLOR = {
|
|
4
14
|
idle: 'blue',
|
|
5
15
|
starting: 'yellow',
|
|
16
|
+
'setting-up': 'yellow',
|
|
6
17
|
running: 'green',
|
|
7
18
|
stopping: 'yellow',
|
|
8
19
|
error: 'red',
|
|
9
20
|
};
|
|
10
|
-
|
|
11
|
-
|
|
21
|
+
function buildKeyHints(setupAvailable, editorAvailable, confirmationOpen) {
|
|
22
|
+
if (confirmationOpen) {
|
|
23
|
+
return [
|
|
24
|
+
{ binding: 'd/y', label: 'Confirm' },
|
|
25
|
+
{ binding: 'Esc/n/q', label: 'Cancel' },
|
|
26
|
+
];
|
|
27
|
+
}
|
|
28
|
+
const hints = [
|
|
29
|
+
{ binding: '↑↓/jk', label: 'Move' },
|
|
30
|
+
{ binding: 'Enter', label: 'Switch' },
|
|
31
|
+
];
|
|
32
|
+
if (setupAvailable) {
|
|
33
|
+
hints.push({ binding: 'i', label: 'Setup' });
|
|
34
|
+
}
|
|
35
|
+
if (editorAvailable) {
|
|
36
|
+
hints.push({ binding: 'e', label: 'Editor' });
|
|
37
|
+
}
|
|
38
|
+
hints.push({ binding: 'o', label: 'Open PR' }, { binding: 'd', label: 'Delete' }, { binding: 'L', label: 'Logs' }, { binding: 's', label: 'Stop' }, { binding: 'r', label: 'Refresh' }, { binding: '?', label: 'Help' }, { binding: 'q', label: 'Quit' });
|
|
39
|
+
return hints;
|
|
40
|
+
}
|
|
41
|
+
export function ContextBar({ status, setupAvailable, editorAvailable, confirmationOpen, }) {
|
|
42
|
+
const isBusy = status.kind === 'setting-up' || status.kind === 'starting' || status.kind === 'stopping';
|
|
43
|
+
const keyHints = buildKeyHints(setupAvailable, editorAvailable, confirmationOpen);
|
|
44
|
+
return (_jsxs(Box, { borderStyle: "round", borderColor: KIND_TO_COLOR[status.kind], flexDirection: "column", paddingX: 1, children: [isBusy ? (_jsx(Spinner, { label: `Status: ${status.kind} — ${status.message}` })) : (_jsxs(Text, { color: KIND_TO_COLOR[status.kind], wrap: "truncate-end", children: [KIND_TO_ICON[status.kind], " Status: ", status.kind, " \u2014 ", status.message] })), _jsx(Text, { wrap: "truncate-end", children: keyHints.map((hint, hintIndex) => (_jsxs(React.Fragment, { children: [hintIndex === 0 ? null : _jsx(Text, { dimColor: true, children: " | " }), _jsx(Text, { color: "white", children: hint.binding }), _jsxs(Text, { dimColor: true, children: [" ", hint.label] })] }, hint.binding))) })] }));
|
|
12
45
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { LogPanel } from './LogPanel.js';
|
|
3
|
+
export function FloatingLogWindow({ logs, width, height, scrollOffset, }) {
|
|
4
|
+
return (_jsx(LogPanel, { logs: logs, width: width, height: height, scrollOffset: scrollOffset, title: "Logs (*.log \u00B7 tail 120 \u00B7 full screen)" }));
|
|
5
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
function buildHelpLines(setupAvailable, editorAvailable) {
|
|
4
|
+
const lines = [
|
|
5
|
+
{ section: 'Movement', binding: '↑↓/jk', description: 'move selection' },
|
|
6
|
+
{ section: 'Movement', binding: 'g/G', description: 'first/last' },
|
|
7
|
+
{ section: 'Scroll', binding: 'Wheel', description: 'pane under cursor' },
|
|
8
|
+
{ section: 'Scroll', binding: 'PageUp/PageDn', description: 'selection page' },
|
|
9
|
+
{ section: 'Logs', binding: 'L', description: 'full-screen logs' },
|
|
10
|
+
{ section: 'Logs', binding: '[/]', description: 'scroll log' },
|
|
11
|
+
{ section: 'Actions', binding: 'Enter', description: 'start/switch worktree' },
|
|
12
|
+
];
|
|
13
|
+
if (setupAvailable) {
|
|
14
|
+
lines.push({ section: 'Actions', binding: 'i', description: 'setup selected worktree' });
|
|
15
|
+
}
|
|
16
|
+
if (editorAvailable) {
|
|
17
|
+
lines.push({ section: 'Actions', binding: 'e', description: 'open selected worktree in editor' });
|
|
18
|
+
}
|
|
19
|
+
lines.push({ section: 'Actions', binding: 'o', description: 'open selected pull request' }, { section: 'Actions', binding: 'd', description: 'arm worktree deletion' }, { section: 'Actions', binding: 's', description: 'stop active session' }, { section: 'Actions', binding: 'r', description: 'refresh' }, { section: 'Actions', binding: 'q', description: 'quit' }, { section: 'Help', binding: 'Esc/q/?', description: 'close help' }, { section: 'Help', binding: 'd/y', description: 'confirm delete after arming' }, { section: 'Help', binding: 'Esc/n/q', description: 'cancel delete confirmation' });
|
|
20
|
+
return lines;
|
|
21
|
+
}
|
|
22
|
+
export function HelpWindow({ setupAvailable, editorAvailable, width, height }) {
|
|
23
|
+
let previousSection = '';
|
|
24
|
+
return (_jsxs(Box, { width: width, height: height, borderStyle: "round", borderColor: "blue", flexDirection: "column", paddingX: 1, children: [_jsx(Text, { bold: true, color: "blue", wrap: "truncate-end", children: "Keyboard Help" }), _jsx(Text, { dimColor: true, wrap: "truncate-end", children: "Primary shortcuts are shown in the status footer. Advanced shortcuts live here." }), buildHelpLines(setupAvailable, editorAvailable).map(line => {
|
|
25
|
+
const section = line.section === previousSection ? '' : line.section;
|
|
26
|
+
previousSection = line.section;
|
|
27
|
+
return (_jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { width: 10, children: _jsx(Text, { dimColor: true, wrap: "truncate-end", children: section }) }), _jsx(Box, { width: 16, children: _jsx(Text, { color: "white", wrap: "truncate-end", children: line.binding }) }), _jsx(Text, { dimColor: true, wrap: "truncate-end", children: line.description })] }, `${line.section}-${line.binding}`));
|
|
28
|
+
})] }));
|
|
29
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { AppLogEntry } from '../core/runtime.js';
|
|
2
|
+
type LogColor = 'black' | 'red' | 'green' | 'yellow' | 'blue' | 'magenta' | 'cyan' | 'white' | 'gray';
|
|
3
|
+
type LineStyle = {
|
|
4
|
+
color?: LogColor;
|
|
5
|
+
dimColor?: boolean;
|
|
6
|
+
bold?: boolean;
|
|
7
|
+
};
|
|
8
|
+
type LineSegment = {
|
|
9
|
+
text: string;
|
|
10
|
+
} & LineStyle;
|
|
11
|
+
type LineSpec = {
|
|
12
|
+
segments: LineSegment[];
|
|
13
|
+
};
|
|
14
|
+
export declare function buildLogLines(logs: AppLogEntry[]): LineSpec[];
|
|
15
|
+
export declare function LogPanel({ logs, width, height, scrollOffset, title, }: {
|
|
16
|
+
logs: AppLogEntry[];
|
|
17
|
+
width?: number;
|
|
18
|
+
height?: number;
|
|
19
|
+
scrollOffset?: number;
|
|
20
|
+
title?: string;
|
|
21
|
+
}): import("react").JSX.Element;
|
|
22
|
+
export {};
|