@akiojin/gwt 2.2.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +4 -4
- package/README.md +4 -4
- package/dist/cli/ui/components/App.d.ts +4 -4
- package/dist/cli/ui/components/App.d.ts.map +1 -1
- package/dist/cli/ui/components/App.js +144 -105
- package/dist/cli/ui/components/App.js.map +1 -1
- package/dist/cli/ui/components/common/Confirm.d.ts +1 -1
- package/dist/cli/ui/components/common/Confirm.d.ts.map +1 -1
- package/dist/cli/ui/components/common/Confirm.js +7 -7
- package/dist/cli/ui/components/common/Confirm.js.map +1 -1
- package/dist/cli/ui/components/common/ErrorBoundary.d.ts +1 -1
- package/dist/cli/ui/components/common/ErrorBoundary.d.ts.map +1 -1
- package/dist/cli/ui/components/common/ErrorBoundary.js +4 -4
- package/dist/cli/ui/components/common/ErrorBoundary.js.map +1 -1
- package/dist/cli/ui/components/common/Input.d.ts +2 -2
- package/dist/cli/ui/components/common/Input.d.ts.map +1 -1
- package/dist/cli/ui/components/common/Input.js +4 -4
- package/dist/cli/ui/components/common/Input.js.map +1 -1
- package/dist/cli/ui/components/common/LoadingIndicator.d.ts +1 -1
- package/dist/cli/ui/components/common/LoadingIndicator.d.ts.map +1 -1
- package/dist/cli/ui/components/common/LoadingIndicator.js +4 -4
- package/dist/cli/ui/components/common/LoadingIndicator.js.map +1 -1
- package/dist/cli/ui/components/common/Select.d.ts +1 -1
- package/dist/cli/ui/components/common/Select.d.ts.map +1 -1
- package/dist/cli/ui/components/common/Select.js +11 -12
- package/dist/cli/ui/components/common/Select.js.map +1 -1
- package/dist/cli/ui/components/screens/AIToolSelectorScreen.d.ts +2 -2
- package/dist/cli/ui/components/screens/AIToolSelectorScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/AIToolSelectorScreen.js +11 -11
- package/dist/cli/ui/components/screens/AIToolSelectorScreen.js.map +1 -1
- package/dist/cli/ui/components/screens/BranchCreatorScreen.d.ts +1 -1
- package/dist/cli/ui/components/screens/BranchCreatorScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/BranchCreatorScreen.js +39 -36
- package/dist/cli/ui/components/screens/BranchCreatorScreen.js.map +1 -1
- package/dist/cli/ui/components/screens/BranchListScreen.d.ts +3 -3
- package/dist/cli/ui/components/screens/BranchListScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/BranchListScreen.js +55 -50
- package/dist/cli/ui/components/screens/BranchListScreen.js.map +1 -1
- package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.d.ts +2 -2
- package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.js +25 -25
- package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.js.map +1 -1
- package/dist/cli/ui/components/screens/PRCleanupScreen.d.ts +2 -2
- package/dist/cli/ui/components/screens/PRCleanupScreen.js +21 -21
- package/dist/cli/ui/components/screens/SessionSelectorScreen.d.ts +1 -1
- package/dist/cli/ui/components/screens/SessionSelectorScreen.js +8 -8
- package/dist/cli/ui/components/screens/WorktreeManagerScreen.d.ts +1 -1
- package/dist/cli/ui/components/screens/WorktreeManagerScreen.js +8 -8
- package/dist/cli/ui/screens/BranchActionSelectorScreen.d.ts.map +1 -1
- package/dist/cli/ui/screens/BranchActionSelectorScreen.js +7 -4
- package/dist/cli/ui/screens/BranchActionSelectorScreen.js.map +1 -1
- package/dist/cli/ui/types.d.ts.map +1 -1
- package/dist/client/assets/{index-V6hDu9KS.js → index-Difv1Hwu.js} +2 -2
- package/dist/client/index.html +1 -1
- package/dist/config/builtin-tools.d.ts +10 -2
- package/dist/config/builtin-tools.d.ts.map +1 -1
- package/dist/config/builtin-tools.js +40 -4
- package/dist/config/builtin-tools.js.map +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js.map +1 -1
- package/dist/config/tools.d.ts.map +1 -1
- package/dist/config/tools.js +4 -3
- package/dist/config/tools.js.map +1 -1
- package/dist/gemini.d.ts +12 -0
- package/dist/gemini.d.ts.map +1 -0
- package/dist/gemini.js +154 -0
- package/dist/gemini.js.map +1 -0
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -1
- package/dist/qwen.d.ts +12 -0
- package/dist/qwen.d.ts.map +1 -0
- package/dist/qwen.js +154 -0
- package/dist/qwen.js.map +1 -0
- package/dist/services/git.service.d.ts.map +1 -1
- package/dist/services/git.service.js.map +1 -1
- package/dist/web/client/src/components/BranchGraph.d.ts.map +1 -1
- package/dist/web/client/src/components/BranchGraph.js +1 -1
- package/dist/web/client/src/components/BranchGraph.js.map +1 -1
- package/dist/web/client/src/components/EnvEditor.d.ts.map +1 -1
- package/dist/web/client/src/components/EnvEditor.js +7 -4
- package/dist/web/client/src/components/EnvEditor.js.map +1 -1
- package/dist/web/client/src/pages/BranchDetailPage.d.ts.map +1 -1
- package/dist/web/client/src/pages/BranchDetailPage.js +55 -18
- package/dist/web/client/src/pages/BranchDetailPage.js.map +1 -1
- package/dist/web/client/src/pages/BranchListPage.d.ts.map +1 -1
- package/dist/web/client/src/pages/BranchListPage.js +10 -4
- package/dist/web/client/src/pages/BranchListPage.js.map +1 -1
- package/dist/web/client/src/pages/ConfigManagementPage.d.ts.map +1 -1
- package/dist/web/client/src/pages/ConfigManagementPage.js +4 -2
- package/dist/web/client/src/pages/ConfigManagementPage.js.map +1 -1
- package/package.json +2 -1
- package/src/cli/ui/__tests__/acceptance/navigation.acceptance.test.tsx +69 -50
- package/src/cli/ui/__tests__/components/App.protected-branch.test.tsx +67 -45
- package/src/cli/ui/__tests__/components/App.shortcuts.test.tsx +117 -75
- package/src/cli/ui/__tests__/components/App.test.tsx +45 -37
- package/src/cli/ui/__tests__/components/common/Confirm.test.tsx +35 -22
- package/src/cli/ui/__tests__/components/common/ErrorBoundary.test.tsx +22 -22
- package/src/cli/ui/__tests__/components/common/Input.test.tsx +29 -22
- package/src/cli/ui/__tests__/components/common/LoadingIndicator.test.tsx +40 -34
- package/src/cli/ui/__tests__/components/common/Select.memo.test.tsx +57 -66
- package/src/cli/ui/__tests__/components/common/Select.test.tsx +121 -91
- package/src/cli/ui/__tests__/components/parts/Footer.test.tsx +18 -16
- package/src/cli/ui/__tests__/components/parts/Header.test.tsx +13 -13
- package/src/cli/ui/__tests__/components/parts/ScrollableList.test.tsx +20 -20
- package/src/cli/ui/__tests__/components/parts/Stats.test.tsx +38 -26
- package/src/cli/ui/__tests__/components/screens/AIToolSelectorScreen.test.tsx +31 -31
- package/src/cli/ui/__tests__/components/screens/BranchCreatorScreen.test.tsx +73 -37
- package/src/cli/ui/__tests__/components/screens/BranchListScreen.test.tsx +261 -153
- package/src/cli/ui/__tests__/components/screens/ExecutionModeSelectorScreen.test.tsx +38 -32
- package/src/cli/ui/__tests__/components/screens/PRCleanupScreen.test.tsx +39 -39
- package/src/cli/ui/__tests__/components/screens/SessionSelectorScreen.test.tsx +49 -21
- package/src/cli/ui/__tests__/components/screens/WorktreeManagerScreen.test.tsx +52 -28
- package/src/cli/ui/__tests__/integration/edgeCases.test.tsx +84 -48
- package/src/cli/ui/__tests__/integration/navigation.test.tsx +111 -83
- package/src/cli/ui/__tests__/integration/realtimeUpdate.test.tsx +111 -108
- package/src/cli/ui/__tests__/performance/branchList.performance.test.tsx +50 -37
- package/src/cli/ui/__tests__/performance/useMemoOptimization.test.tsx +75 -76
- package/src/cli/ui/components/App.tsx +247 -150
- package/src/cli/ui/components/common/Confirm.tsx +13 -9
- package/src/cli/ui/components/common/ErrorBoundary.tsx +8 -5
- package/src/cli/ui/components/common/Input.tsx +12 -4
- package/src/cli/ui/components/common/LoadingIndicator.tsx +8 -5
- package/src/cli/ui/components/common/Select.tsx +28 -17
- package/src/cli/ui/components/parts/Header.test.tsx +5 -15
- package/src/cli/ui/components/screens/AIToolSelectorScreen.tsx +19 -13
- package/src/cli/ui/components/screens/BranchCreatorScreen.tsx +74 -54
- package/src/cli/ui/components/screens/BranchListScreen.tsx +92 -75
- package/src/cli/ui/components/screens/ExecutionModeSelectorScreen.tsx +35 -28
- package/src/cli/ui/components/screens/PRCleanupScreen.tsx +22 -22
- package/src/cli/ui/components/screens/SessionSelectorScreen.tsx +8 -8
- package/src/cli/ui/components/screens/WorktreeManagerScreen.tsx +8 -8
- package/src/cli/ui/screens/BranchActionSelectorScreen.tsx +9 -4
- package/src/cli/ui/types.ts +8 -1
- package/src/config/builtin-tools.ts +42 -4
- package/src/config/index.ts +2 -12
- package/src/config/tools.ts +16 -6
- package/src/gemini.ts +202 -0
- package/src/git.ts +2 -1
- package/src/index.ts +30 -0
- package/src/qwen.ts +208 -0
- package/src/services/git.service.ts +2 -1
- package/src/web/client/src/components/BranchGraph.tsx +3 -2
- package/src/web/client/src/components/EnvEditor.tsx +44 -11
- package/src/web/client/src/pages/BranchDetailPage.tsx +165 -54
- package/src/web/client/src/pages/BranchListPage.tsx +37 -13
- package/src/web/client/src/pages/ConfigManagementPage.tsx +28 -9
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import React, { useCallback, useState, useMemo } from
|
|
2
|
-
import { Box, Text, useInput } from
|
|
3
|
-
import { Header } from
|
|
4
|
-
import { Stats } from
|
|
5
|
-
import { Footer } from
|
|
6
|
-
import { Select } from
|
|
7
|
-
import { Input } from
|
|
8
|
-
import { LoadingIndicator } from
|
|
9
|
-
import { useTerminalSize } from
|
|
10
|
-
import type { BranchItem, Statistics } from
|
|
11
|
-
import stringWidth from
|
|
12
|
-
import chalk from
|
|
1
|
+
import React, { useCallback, useState, useMemo } from "react";
|
|
2
|
+
import { Box, Text, useInput } from "ink";
|
|
3
|
+
import { Header } from "../parts/Header.js";
|
|
4
|
+
import { Stats } from "../parts/Stats.js";
|
|
5
|
+
import { Footer } from "../parts/Footer.js";
|
|
6
|
+
import { Select } from "../common/Select.js";
|
|
7
|
+
import { Input } from "../common/Input.js";
|
|
8
|
+
import { LoadingIndicator } from "../common/LoadingIndicator.js";
|
|
9
|
+
import { useTerminalSize } from "../../hooks/useTerminalSize.js";
|
|
10
|
+
import type { BranchItem, Statistics } from "../../types.js";
|
|
11
|
+
import stringWidth from "string-width";
|
|
12
|
+
import chalk from "chalk";
|
|
13
13
|
|
|
14
14
|
const WIDTH_OVERRIDES: Record<string, number> = {
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
"⬆": 1,
|
|
16
|
+
"☁": 1,
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
const measureDisplayWidth = (value: string): number => {
|
|
@@ -29,7 +29,7 @@ const measureDisplayWidth = (value: string): number => {
|
|
|
29
29
|
return width;
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
-
type IndicatorColor =
|
|
32
|
+
type IndicatorColor = "cyan" | "green" | "yellow" | "red";
|
|
33
33
|
|
|
34
34
|
interface CleanupIndicator {
|
|
35
35
|
icon: string;
|
|
@@ -95,26 +95,28 @@ export function BranchListScreen({
|
|
|
95
95
|
const { rows } = useTerminalSize();
|
|
96
96
|
|
|
97
97
|
// Filter state - allow test control via props
|
|
98
|
-
const [internalFilterQuery, setInternalFilterQuery] = useState(
|
|
99
|
-
const filterQuery =
|
|
98
|
+
const [internalFilterQuery, setInternalFilterQuery] = useState("");
|
|
99
|
+
const filterQuery =
|
|
100
|
+
testFilterQuery !== undefined ? testFilterQuery : internalFilterQuery;
|
|
100
101
|
const setFilterQuery = useCallback(
|
|
101
102
|
(query: string) => {
|
|
102
103
|
setInternalFilterQuery(query);
|
|
103
104
|
testOnFilterQueryChange?.(query);
|
|
104
105
|
},
|
|
105
|
-
[testOnFilterQueryChange]
|
|
106
|
+
[testOnFilterQueryChange],
|
|
106
107
|
);
|
|
107
108
|
|
|
108
109
|
// Focus management: true = filter mode, false = branch selection mode
|
|
109
110
|
// Allow test control via props
|
|
110
111
|
const [internalFilterMode, setInternalFilterMode] = useState(false);
|
|
111
|
-
const filterMode =
|
|
112
|
+
const filterMode =
|
|
113
|
+
testFilterMode !== undefined ? testFilterMode : internalFilterMode;
|
|
112
114
|
const setFilterMode = useCallback(
|
|
113
115
|
(mode: boolean) => {
|
|
114
116
|
setInternalFilterMode(mode);
|
|
115
117
|
testOnFilterModeChange?.(mode);
|
|
116
118
|
},
|
|
117
|
-
[testOnFilterModeChange]
|
|
119
|
+
[testOnFilterModeChange],
|
|
118
120
|
);
|
|
119
121
|
|
|
120
122
|
// Handle keyboard input
|
|
@@ -129,7 +131,7 @@ export function BranchListScreen({
|
|
|
129
131
|
if (key.escape) {
|
|
130
132
|
if (filterQuery) {
|
|
131
133
|
// Clear filter query first
|
|
132
|
-
setFilterQuery(
|
|
134
|
+
setFilterQuery("");
|
|
133
135
|
return;
|
|
134
136
|
}
|
|
135
137
|
if (filterMode) {
|
|
@@ -140,17 +142,17 @@ export function BranchListScreen({
|
|
|
140
142
|
}
|
|
141
143
|
|
|
142
144
|
// Enter filter mode with 'f' key (only in branch selection mode)
|
|
143
|
-
if (input ===
|
|
145
|
+
if (input === "f" && !filterMode) {
|
|
144
146
|
setFilterMode(true);
|
|
145
147
|
return;
|
|
146
148
|
}
|
|
147
149
|
|
|
148
150
|
// Global shortcuts (blocked by Input component when typing in filter mode)
|
|
149
|
-
if (input ===
|
|
150
|
-
onNavigate(
|
|
151
|
-
} else if (input ===
|
|
151
|
+
if (input === "m" && onNavigate) {
|
|
152
|
+
onNavigate("worktree-manager");
|
|
153
|
+
} else if (input === "c") {
|
|
152
154
|
onCleanupCommand?.();
|
|
153
|
-
} else if (input ===
|
|
155
|
+
} else if (input === "r" && onRefresh) {
|
|
154
156
|
onRefresh();
|
|
155
157
|
}
|
|
156
158
|
});
|
|
@@ -189,51 +191,52 @@ export function BranchListScreen({
|
|
|
189
191
|
const statsLines = 1;
|
|
190
192
|
const emptyLine = 1;
|
|
191
193
|
const footerLines = 1;
|
|
192
|
-
const fixedLines =
|
|
194
|
+
const fixedLines =
|
|
195
|
+
headerLines + filterLines + statsLines + emptyLine + footerLines;
|
|
193
196
|
const contentHeight = rows - fixedLines;
|
|
194
197
|
const limit = Math.max(5, contentHeight); // Minimum 5 items visible
|
|
195
198
|
|
|
196
199
|
// Footer actions
|
|
197
200
|
const footerActions = [
|
|
198
|
-
{ key:
|
|
199
|
-
{ key:
|
|
200
|
-
{ key:
|
|
201
|
-
{ key:
|
|
202
|
-
{ key:
|
|
201
|
+
{ key: "enter", description: "Select" },
|
|
202
|
+
{ key: "f", description: "Filter" },
|
|
203
|
+
{ key: "r", description: "Refresh" },
|
|
204
|
+
{ key: "m", description: "Manage worktrees" },
|
|
205
|
+
{ key: "c", description: "Cleanup branches" },
|
|
203
206
|
];
|
|
204
207
|
|
|
205
208
|
const formatLatestCommit = useCallback((timestamp?: number) => {
|
|
206
209
|
if (!timestamp || Number.isNaN(timestamp)) {
|
|
207
|
-
return
|
|
210
|
+
return "---";
|
|
208
211
|
}
|
|
209
212
|
|
|
210
213
|
const date = new Date(timestamp * 1000);
|
|
211
214
|
const year = date.getFullYear();
|
|
212
|
-
const month = String(date.getMonth() + 1).padStart(2,
|
|
213
|
-
const day = String(date.getDate()).padStart(2,
|
|
214
|
-
const hours = String(date.getHours()).padStart(2,
|
|
215
|
-
const minutes = String(date.getMinutes()).padStart(2,
|
|
215
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
216
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
217
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
218
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
216
219
|
|
|
217
220
|
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
|
218
221
|
}, []);
|
|
219
222
|
|
|
220
223
|
const truncateToWidth = useCallback((value: string, maxWidth: number) => {
|
|
221
224
|
if (maxWidth <= 0) {
|
|
222
|
-
return
|
|
225
|
+
return "";
|
|
223
226
|
}
|
|
224
227
|
|
|
225
228
|
if (stringWidth(value) <= maxWidth) {
|
|
226
229
|
return value;
|
|
227
230
|
}
|
|
228
231
|
|
|
229
|
-
const ellipsis =
|
|
232
|
+
const ellipsis = "…";
|
|
230
233
|
const ellipsisWidth = stringWidth(ellipsis);
|
|
231
234
|
if (ellipsisWidth >= maxWidth) {
|
|
232
235
|
return ellipsis;
|
|
233
236
|
}
|
|
234
237
|
|
|
235
238
|
let currentWidth = 0;
|
|
236
|
-
let result =
|
|
239
|
+
let result = "";
|
|
237
240
|
|
|
238
241
|
for (const char of value) {
|
|
239
242
|
const charWidth = stringWidth(char);
|
|
@@ -250,60 +253,67 @@ export function BranchListScreen({
|
|
|
250
253
|
const renderBranchRow = useCallback(
|
|
251
254
|
(item: BranchItem, isSelected: boolean, context: { columns: number }) => {
|
|
252
255
|
const columns = Math.max(20, context.columns);
|
|
253
|
-
const arrow = isSelected ?
|
|
256
|
+
const arrow = isSelected ? ">" : " ";
|
|
254
257
|
const timestampText = formatLatestCommit(item.latestCommitTimestamp);
|
|
255
258
|
const timestampWidth = stringWidth(timestampText);
|
|
256
259
|
|
|
257
260
|
const indicatorInfo = cleanupUI?.indicators?.[item.name];
|
|
258
|
-
let indicatorIcon = indicatorInfo?.icon ??
|
|
261
|
+
let indicatorIcon = indicatorInfo?.icon ?? "";
|
|
259
262
|
if (indicatorIcon && indicatorInfo?.color && !isSelected) {
|
|
260
263
|
switch (indicatorInfo.color) {
|
|
261
|
-
case
|
|
264
|
+
case "cyan":
|
|
262
265
|
indicatorIcon = chalk.cyan(indicatorIcon);
|
|
263
266
|
break;
|
|
264
|
-
case
|
|
267
|
+
case "green":
|
|
265
268
|
indicatorIcon = chalk.green(indicatorIcon);
|
|
266
269
|
break;
|
|
267
|
-
case
|
|
270
|
+
case "yellow":
|
|
268
271
|
indicatorIcon = chalk.yellow(indicatorIcon);
|
|
269
272
|
break;
|
|
270
|
-
case
|
|
273
|
+
case "red":
|
|
271
274
|
indicatorIcon = chalk.red(indicatorIcon);
|
|
272
275
|
break;
|
|
273
276
|
default:
|
|
274
277
|
break;
|
|
275
278
|
}
|
|
276
279
|
}
|
|
277
|
-
const indicatorPrefix = indicatorIcon ? `${indicatorIcon} ` :
|
|
280
|
+
const indicatorPrefix = indicatorIcon ? `${indicatorIcon} ` : "";
|
|
278
281
|
const staticPrefix = `${arrow} ${indicatorPrefix}`;
|
|
279
282
|
const staticPrefixWidth = stringWidth(staticPrefix);
|
|
280
283
|
|
|
281
|
-
const availableLeftWidth = Math.max(
|
|
284
|
+
const availableLeftWidth = Math.max(
|
|
285
|
+
staticPrefixWidth,
|
|
286
|
+
columns - timestampWidth - 1,
|
|
287
|
+
);
|
|
282
288
|
const maxLabelWidth = Math.max(0, availableLeftWidth - staticPrefixWidth);
|
|
283
289
|
const truncatedLabel = truncateToWidth(item.label, maxLabelWidth);
|
|
284
290
|
const leftText = `${staticPrefix}${truncatedLabel}`;
|
|
285
291
|
|
|
286
292
|
const leftMeasuredWidth = stringWidth(leftText);
|
|
287
293
|
const leftDisplayWidth = measureDisplayWidth(leftText);
|
|
288
|
-
const baseGapWidth = Math.max(
|
|
289
|
-
|
|
294
|
+
const baseGapWidth = Math.max(
|
|
295
|
+
1,
|
|
296
|
+
columns - leftMeasuredWidth - timestampWidth,
|
|
297
|
+
);
|
|
298
|
+
const displayGapWidth = Math.max(
|
|
299
|
+
1,
|
|
300
|
+
columns - leftDisplayWidth - timestampWidth,
|
|
301
|
+
);
|
|
290
302
|
const cursorShift = Math.max(0, displayGapWidth - baseGapWidth);
|
|
291
303
|
|
|
292
|
-
const gap =
|
|
293
|
-
const cursorAdjust = cursorShift > 0 ? `\u001b[${cursorShift}C` :
|
|
304
|
+
const gap = " ".repeat(baseGapWidth);
|
|
305
|
+
const cursorAdjust = cursorShift > 0 ? `\u001b[${cursorShift}C` : "";
|
|
294
306
|
|
|
295
307
|
let line = `${leftText}${gap}${cursorAdjust}${timestampText}`;
|
|
296
308
|
const paddingWidth = Math.max(0, columns - stringWidth(line));
|
|
297
309
|
if (paddingWidth > 0) {
|
|
298
|
-
line +=
|
|
310
|
+
line += " ".repeat(paddingWidth);
|
|
299
311
|
}
|
|
300
312
|
|
|
301
|
-
const output = isSelected
|
|
302
|
-
? `\u001b[46m\u001b[30m${line}\u001b[0m`
|
|
303
|
-
: line;
|
|
313
|
+
const output = isSelected ? `\u001b[46m\u001b[30m${line}\u001b[0m` : line;
|
|
304
314
|
return <Text>{output}</Text>;
|
|
305
315
|
},
|
|
306
|
-
[cleanupUI, formatLatestCommit, truncateToWidth]
|
|
316
|
+
[cleanupUI, formatLatestCommit, truncateToWidth],
|
|
307
317
|
);
|
|
308
318
|
|
|
309
319
|
return (
|
|
@@ -325,14 +335,14 @@ export function BranchListScreen({
|
|
|
325
335
|
onChange={setFilterQuery}
|
|
326
336
|
onSubmit={() => {}} // No-op: filter is applied in real-time
|
|
327
337
|
placeholder="Type to search..."
|
|
328
|
-
blockKeys={[
|
|
338
|
+
blockKeys={["c", "r", "m", "f"]} // Block shortcuts while typing
|
|
329
339
|
/>
|
|
330
340
|
) : (
|
|
331
|
-
<Text dimColor>{filterQuery ||
|
|
341
|
+
<Text dimColor>{filterQuery || "(press f to filter)"}</Text>
|
|
332
342
|
)}
|
|
333
343
|
{filterQuery && (
|
|
334
344
|
<Text dimColor>
|
|
335
|
-
{
|
|
345
|
+
{" "}
|
|
336
346
|
(Showing {filteredBranches.length} of {branches.length})
|
|
337
347
|
</Text>
|
|
338
348
|
)}
|
|
@@ -370,22 +380,29 @@ export function BranchListScreen({
|
|
|
370
380
|
</Box>
|
|
371
381
|
)}
|
|
372
382
|
|
|
373
|
-
{!loading &&
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
383
|
+
{!loading &&
|
|
384
|
+
!error &&
|
|
385
|
+
branches.length > 0 &&
|
|
386
|
+
filteredBranches.length === 0 &&
|
|
387
|
+
filterQuery && (
|
|
388
|
+
<Box>
|
|
389
|
+
<Text dimColor>No branches match your filter</Text>
|
|
390
|
+
</Box>
|
|
391
|
+
)}
|
|
378
392
|
|
|
379
|
-
{!loading &&
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
393
|
+
{!loading &&
|
|
394
|
+
!error &&
|
|
395
|
+
branches.length > 0 &&
|
|
396
|
+
filteredBranches.length > 0 && (
|
|
397
|
+
<Select
|
|
398
|
+
items={filteredBranches}
|
|
399
|
+
onSelect={onSelect}
|
|
400
|
+
limit={limit}
|
|
401
|
+
disabled={Boolean(cleanupUI?.inputLocked)}
|
|
402
|
+
renderIndicator={() => null}
|
|
403
|
+
renderItem={renderBranchRow}
|
|
404
|
+
/>
|
|
405
|
+
)}
|
|
389
406
|
</Box>
|
|
390
407
|
|
|
391
408
|
{cleanupUI?.footerMessage && (
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import React, { useState } from
|
|
2
|
-
import { Box, Text, useInput } from
|
|
3
|
-
import { Header } from
|
|
4
|
-
import { Footer } from
|
|
5
|
-
import { Select } from
|
|
6
|
-
import { useTerminalSize } from
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { Box, Text, useInput } from "ink";
|
|
3
|
+
import { Header } from "../parts/Header.js";
|
|
4
|
+
import { Footer } from "../parts/Footer.js";
|
|
5
|
+
import { Select } from "../common/Select.js";
|
|
6
|
+
import { useTerminalSize } from "../../hooks/useTerminalSize.js";
|
|
7
7
|
|
|
8
|
-
export type ExecutionMode =
|
|
8
|
+
export type ExecutionMode = "normal" | "continue" | "resume";
|
|
9
9
|
|
|
10
10
|
export interface ExecutionModeItem {
|
|
11
11
|
label: string;
|
|
@@ -62,33 +62,34 @@ export function ExecutionModeSelectorScreen({
|
|
|
62
62
|
// Execution mode options (Step 1)
|
|
63
63
|
const modeItems: ExecutionModeItem[] = [
|
|
64
64
|
{
|
|
65
|
-
label:
|
|
66
|
-
value:
|
|
67
|
-
description:
|
|
65
|
+
label: "Normal",
|
|
66
|
+
value: "normal",
|
|
67
|
+
description: "Start fresh session",
|
|
68
68
|
},
|
|
69
69
|
{
|
|
70
|
-
label:
|
|
71
|
-
value:
|
|
72
|
-
description:
|
|
70
|
+
label: "Continue",
|
|
71
|
+
value: "continue",
|
|
72
|
+
description: "Continue from last session",
|
|
73
73
|
},
|
|
74
74
|
{
|
|
75
|
-
label:
|
|
76
|
-
value:
|
|
77
|
-
description:
|
|
75
|
+
label: "Resume",
|
|
76
|
+
value: "resume",
|
|
77
|
+
description: "Resume specific session",
|
|
78
78
|
},
|
|
79
79
|
];
|
|
80
80
|
|
|
81
81
|
// Skip permissions options (Step 2)
|
|
82
82
|
const skipPermissionsItems: SkipPermissionsItem[] = [
|
|
83
83
|
{
|
|
84
|
-
label:
|
|
85
|
-
value:
|
|
86
|
-
description:
|
|
84
|
+
label: "No",
|
|
85
|
+
value: "no",
|
|
86
|
+
description: "Normal permission checks",
|
|
87
87
|
},
|
|
88
88
|
{
|
|
89
|
-
label:
|
|
90
|
-
value:
|
|
91
|
-
description:
|
|
89
|
+
label: "Yes",
|
|
90
|
+
value: "yes",
|
|
91
|
+
description:
|
|
92
|
+
"Skip permission checks (--dangerously-skip-permissions / --yolo)",
|
|
92
93
|
},
|
|
93
94
|
];
|
|
94
95
|
|
|
@@ -103,22 +104,22 @@ export function ExecutionModeSelectorScreen({
|
|
|
103
104
|
if (selectedMode) {
|
|
104
105
|
onSelect({
|
|
105
106
|
mode: selectedMode,
|
|
106
|
-
skipPermissions: item.value ===
|
|
107
|
+
skipPermissions: item.value === "yes",
|
|
107
108
|
});
|
|
108
109
|
}
|
|
109
110
|
};
|
|
110
111
|
|
|
111
112
|
// Footer actions
|
|
112
113
|
const footerActions = [
|
|
113
|
-
{ key:
|
|
114
|
-
{ key:
|
|
114
|
+
{ key: "enter", description: "Select" },
|
|
115
|
+
{ key: "esc", description: step === 2 ? "Back to mode selection" : "Back" },
|
|
115
116
|
];
|
|
116
117
|
|
|
117
118
|
return (
|
|
118
119
|
<Box flexDirection="column" height={rows}>
|
|
119
120
|
{/* Header */}
|
|
120
121
|
<Header
|
|
121
|
-
title={step === 1 ?
|
|
122
|
+
title={step === 1 ? "Execution Mode" : "Skip Permissions"}
|
|
122
123
|
titleColor="magenta"
|
|
123
124
|
version={version}
|
|
124
125
|
/>
|
|
@@ -135,9 +136,15 @@ export function ExecutionModeSelectorScreen({
|
|
|
135
136
|
) : (
|
|
136
137
|
<>
|
|
137
138
|
<Box marginBottom={1}>
|
|
138
|
-
<Text>
|
|
139
|
+
<Text>
|
|
140
|
+
Skip permission checks? (--dangerously-skip-permissions /
|
|
141
|
+
--yolo)
|
|
142
|
+
</Text>
|
|
139
143
|
</Box>
|
|
140
|
-
<Select
|
|
144
|
+
<Select
|
|
145
|
+
items={skipPermissionsItems}
|
|
146
|
+
onSelect={handleSkipPermissionsSelect}
|
|
147
|
+
/>
|
|
141
148
|
</>
|
|
142
149
|
)}
|
|
143
150
|
</Box>
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import { Box, Text, useInput } from
|
|
3
|
-
import { Header } from
|
|
4
|
-
import { Footer } from
|
|
5
|
-
import { Select } from
|
|
6
|
-
import { useTerminalSize } from
|
|
7
|
-
import type { CleanupTarget } from
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Box, Text, useInput } from "ink";
|
|
3
|
+
import { Header } from "../parts/Header.js";
|
|
4
|
+
import { Footer } from "../parts/Footer.js";
|
|
5
|
+
import { Select } from "../common/Select.js";
|
|
6
|
+
import { useTerminalSize } from "../../hooks/useTerminalSize.js";
|
|
7
|
+
import type { CleanupTarget } from "../../types.js";
|
|
8
8
|
|
|
9
9
|
export interface PRItem {
|
|
10
10
|
label: string;
|
|
@@ -44,7 +44,7 @@ export function PRCleanupScreen({
|
|
|
44
44
|
useInput((input, key) => {
|
|
45
45
|
if (key.escape) {
|
|
46
46
|
onBack();
|
|
47
|
-
} else if (input ===
|
|
47
|
+
} else if (input === "r") {
|
|
48
48
|
onRefresh();
|
|
49
49
|
}
|
|
50
50
|
});
|
|
@@ -53,28 +53,28 @@ export function PRCleanupScreen({
|
|
|
53
53
|
const prItems: PRItem[] = targets.map((target) => {
|
|
54
54
|
const pr = target.pullRequest;
|
|
55
55
|
const flags: string[] = [];
|
|
56
|
-
if (target.cleanupType ===
|
|
57
|
-
flags.push(
|
|
56
|
+
if (target.cleanupType === "worktree-and-branch") {
|
|
57
|
+
flags.push("worktree");
|
|
58
58
|
} else {
|
|
59
|
-
flags.push(
|
|
59
|
+
flags.push("branch");
|
|
60
60
|
}
|
|
61
|
-
if (target.reasons?.includes(
|
|
62
|
-
flags.push(
|
|
61
|
+
if (target.reasons?.includes("merged-pr")) {
|
|
62
|
+
flags.push("merged");
|
|
63
63
|
}
|
|
64
|
-
if (target.reasons?.includes(
|
|
65
|
-
flags.push(
|
|
64
|
+
if (target.reasons?.includes("no-diff-with-base")) {
|
|
65
|
+
flags.push("base");
|
|
66
66
|
}
|
|
67
67
|
if (target.hasUncommittedChanges) {
|
|
68
|
-
flags.push(
|
|
68
|
+
flags.push("changes");
|
|
69
69
|
}
|
|
70
70
|
if (target.hasUnpushedCommits) {
|
|
71
|
-
flags.push(
|
|
71
|
+
flags.push("unpushed");
|
|
72
72
|
}
|
|
73
73
|
if (target.isAccessible === false) {
|
|
74
|
-
flags.push(
|
|
74
|
+
flags.push("inaccessible");
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
const flagText = flags.length > 0 ? ` [${flags.join(
|
|
77
|
+
const flagText = flags.length > 0 ? ` [${flags.join(", ")}]` : "";
|
|
78
78
|
|
|
79
79
|
const label = pr
|
|
80
80
|
? `${target.branch} - #${pr.number} ${pr.title}${flagText}`
|
|
@@ -98,9 +98,9 @@ export function PRCleanupScreen({
|
|
|
98
98
|
|
|
99
99
|
// Footer actions
|
|
100
100
|
const footerActions = [
|
|
101
|
-
{ key:
|
|
102
|
-
{ key:
|
|
103
|
-
{ key:
|
|
101
|
+
{ key: "enter", description: "Cleanup" },
|
|
102
|
+
{ key: "r", description: "Refresh" },
|
|
103
|
+
{ key: "esc", description: "Back" },
|
|
104
104
|
];
|
|
105
105
|
|
|
106
106
|
return (
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import { Box, Text, useInput } from
|
|
3
|
-
import { Header } from
|
|
4
|
-
import { Footer } from
|
|
5
|
-
import { Select } from
|
|
6
|
-
import { useTerminalSize } from
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Box, Text, useInput } from "ink";
|
|
3
|
+
import { Header } from "../parts/Header.js";
|
|
4
|
+
import { Footer } from "../parts/Footer.js";
|
|
5
|
+
import { Select } from "../common/Select.js";
|
|
6
|
+
import { useTerminalSize } from "../../hooks/useTerminalSize.js";
|
|
7
7
|
|
|
8
8
|
export interface SessionItem {
|
|
9
9
|
label: string;
|
|
@@ -59,8 +59,8 @@ export function SessionSelectorScreen({
|
|
|
59
59
|
|
|
60
60
|
// Footer actions
|
|
61
61
|
const footerActions = [
|
|
62
|
-
{ key:
|
|
63
|
-
{ key:
|
|
62
|
+
{ key: "enter", description: "Select" },
|
|
63
|
+
{ key: "esc", description: "Back" },
|
|
64
64
|
];
|
|
65
65
|
|
|
66
66
|
return (
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import { Box, Text, useInput } from
|
|
3
|
-
import { Header } from
|
|
4
|
-
import { Footer } from
|
|
5
|
-
import { Select } from
|
|
6
|
-
import { useTerminalSize } from
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Box, Text, useInput } from "ink";
|
|
3
|
+
import { Header } from "../parts/Header.js";
|
|
4
|
+
import { Footer } from "../parts/Footer.js";
|
|
5
|
+
import { Select } from "../common/Select.js";
|
|
6
|
+
import { useTerminalSize } from "../../hooks/useTerminalSize.js";
|
|
7
7
|
|
|
8
8
|
export interface WorktreeItem {
|
|
9
9
|
branch: string;
|
|
@@ -64,8 +64,8 @@ export function WorktreeManagerScreen({
|
|
|
64
64
|
|
|
65
65
|
// Footer actions
|
|
66
66
|
const footerActions = [
|
|
67
|
-
{ key:
|
|
68
|
-
{ key:
|
|
67
|
+
{ key: "enter", description: "Select" },
|
|
68
|
+
{ key: "esc", description: "Back" },
|
|
69
69
|
];
|
|
70
70
|
|
|
71
71
|
return (
|
|
@@ -46,7 +46,9 @@ export function BranchActionSelectorScreen({
|
|
|
46
46
|
(mode === "protected" ? "Switch to root branch" : "Use existing branch");
|
|
47
47
|
const secondaryActionLabel =
|
|
48
48
|
secondaryLabel ??
|
|
49
|
-
(mode === "protected"
|
|
49
|
+
(mode === "protected"
|
|
50
|
+
? "Create new branch from this branch"
|
|
51
|
+
: "Create new branch");
|
|
50
52
|
|
|
51
53
|
const items: SelectItem[] = [
|
|
52
54
|
{
|
|
@@ -74,15 +76,18 @@ export function BranchActionSelectorScreen({
|
|
|
74
76
|
|
|
75
77
|
// Footer actions
|
|
76
78
|
const footerActions = [
|
|
77
|
-
{ key:
|
|
78
|
-
{ key:
|
|
79
|
+
{ key: "enter", description: "Select" },
|
|
80
|
+
{ key: "esc", description: "Back" },
|
|
79
81
|
];
|
|
80
82
|
|
|
81
83
|
return (
|
|
82
84
|
<Box flexDirection="column">
|
|
83
85
|
<Box marginBottom={1}>
|
|
84
86
|
<Text>
|
|
85
|
-
Selected branch:
|
|
87
|
+
Selected branch:{" "}
|
|
88
|
+
<Text bold color="cyan">
|
|
89
|
+
{selectedBranch}
|
|
90
|
+
</Text>
|
|
86
91
|
</Text>
|
|
87
92
|
</Box>
|
|
88
93
|
{infoMessage ? (
|
package/src/cli/ui/types.ts
CHANGED
|
@@ -8,7 +8,14 @@ export interface WorktreeInfo {
|
|
|
8
8
|
export interface BranchInfo {
|
|
9
9
|
name: string;
|
|
10
10
|
type: "local" | "remote";
|
|
11
|
-
branchType:
|
|
11
|
+
branchType:
|
|
12
|
+
| "feature"
|
|
13
|
+
| "bugfix"
|
|
14
|
+
| "hotfix"
|
|
15
|
+
| "release"
|
|
16
|
+
| "main"
|
|
17
|
+
| "develop"
|
|
18
|
+
| "other";
|
|
12
19
|
isCurrent: boolean;
|
|
13
20
|
description?: string;
|
|
14
21
|
worktree?: WorktreeInfo;
|