@akiojin/gwt 2.0.4 → 2.2.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 +3 -3
- package/dist/cli/ui/components/common/Input.d.ts +6 -1
- package/dist/cli/ui/components/common/Input.d.ts.map +1 -1
- package/dist/cli/ui/components/common/Input.js +10 -2
- package/dist/cli/ui/components/common/Input.js.map +1 -1
- package/dist/cli/ui/components/screens/BranchCreatorScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/BranchCreatorScreen.js +5 -0
- package/dist/cli/ui/components/screens/BranchCreatorScreen.js.map +1 -1
- package/dist/cli/ui/components/screens/BranchListScreen.d.ts +5 -1
- package/dist/cli/ui/components/screens/BranchListScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/BranchListScreen.js +78 -9
- package/dist/cli/ui/components/screens/BranchListScreen.js.map +1 -1
- package/dist/cli/ui/types.d.ts +2 -2
- package/dist/cli/ui/types.d.ts.map +1 -1
- package/dist/cli/ui/utils/branchFormatter.d.ts.map +1 -1
- package/dist/cli/ui/utils/branchFormatter.js +1 -0
- package/dist/cli/ui/utils/branchFormatter.js.map +1 -1
- package/dist/client/assets/index-V6hDu9KS.js +81 -0
- package/dist/client/index.html +1 -1
- package/dist/config/constants.d.ts +2 -0
- package/dist/config/constants.d.ts.map +1 -1
- package/dist/config/constants.js +2 -0
- package/dist/config/constants.js.map +1 -1
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +2 -0
- package/dist/git.js.map +1 -1
- package/dist/services/git.service.d.ts.map +1 -1
- package/dist/services/git.service.js +2 -0
- package/dist/services/git.service.js.map +1 -1
- package/package.json +2 -1
- package/src/cli/ui/__tests__/components/screens/BranchCreatorScreen.test.tsx +1 -0
- package/src/cli/ui/__tests__/components/screens/BranchListScreen.test.tsx +313 -0
- package/src/cli/ui/__tests__/utils/branchFormatter.test.ts +14 -0
- package/src/cli/ui/components/common/Input.tsx +16 -2
- package/src/cli/ui/components/screens/BranchCreatorScreen.tsx +6 -1
- package/src/cli/ui/components/screens/BranchListScreen.tsx +117 -9
- package/src/cli/ui/types.ts +2 -1
- package/src/cli/ui/utils/branchFormatter.ts +1 -0
- package/src/config/constants.ts +2 -0
- package/src/git.ts +1 -0
- package/src/services/git.service.ts +1 -0
- package/dist/client/assets/index-CqaYsI0z.js +0 -81
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Box, Text } from 'ink';
|
|
2
|
+
import { Box, Text, useInput } from 'ink';
|
|
3
3
|
import TextInput from 'ink-text-input';
|
|
4
4
|
|
|
5
5
|
export interface InputProps {
|
|
@@ -9,12 +9,26 @@ export interface InputProps {
|
|
|
9
9
|
placeholder?: string;
|
|
10
10
|
label?: string;
|
|
11
11
|
mask?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Block specific key bindings to prevent parent handlers from processing them
|
|
14
|
+
* Useful for blocking shortcuts like 'c', 'r', 'm' while typing
|
|
15
|
+
*/
|
|
16
|
+
blockKeys?: string[];
|
|
12
17
|
}
|
|
13
18
|
|
|
14
19
|
/**
|
|
15
20
|
* Input component - wrapper around ink-text-input with optional label
|
|
16
21
|
*/
|
|
17
|
-
export function Input({ value, onChange, onSubmit, placeholder, label, mask }: InputProps) {
|
|
22
|
+
export function Input({ value, onChange, onSubmit, placeholder, label, mask, blockKeys }: InputProps) {
|
|
23
|
+
// Block specific keys from being processed by parent useInput handlers
|
|
24
|
+
// This prevents shortcuts (c/r/m) from triggering while typing in the input
|
|
25
|
+
useInput((input) => {
|
|
26
|
+
if (blockKeys && blockKeys.includes(input)) {
|
|
27
|
+
// Consume the key - don't let it propagate to parent handlers
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
18
32
|
return (
|
|
19
33
|
<Box flexDirection="column">
|
|
20
34
|
{label && (
|
|
@@ -7,7 +7,7 @@ import { Input } from '../common/Input.js';
|
|
|
7
7
|
import { useTerminalSize } from '../../hooks/useTerminalSize.js';
|
|
8
8
|
import { BRANCH_PREFIXES } from '../../../../config/constants.js';
|
|
9
9
|
|
|
10
|
-
type BranchType = 'feature' | 'hotfix' | 'release';
|
|
10
|
+
type BranchType = 'feature' | 'bugfix' | 'hotfix' | 'release';
|
|
11
11
|
type Step = 'type-selection' | 'name-input';
|
|
12
12
|
|
|
13
13
|
const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧'];
|
|
@@ -67,6 +67,11 @@ export function BranchCreatorScreen({
|
|
|
67
67
|
value: 'feature',
|
|
68
68
|
description: 'New feature development',
|
|
69
69
|
},
|
|
70
|
+
{
|
|
71
|
+
label: 'bugfix',
|
|
72
|
+
value: 'bugfix',
|
|
73
|
+
description: 'Bug fix',
|
|
74
|
+
},
|
|
70
75
|
{
|
|
71
76
|
label: 'hotfix',
|
|
72
77
|
value: 'hotfix',
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import React, { useCallback } from 'react';
|
|
1
|
+
import React, { useCallback, useState, useMemo } from 'react';
|
|
2
2
|
import { Box, Text, useInput } from 'ink';
|
|
3
3
|
import { Header } from '../parts/Header.js';
|
|
4
4
|
import { Stats } from '../parts/Stats.js';
|
|
5
5
|
import { Footer } from '../parts/Footer.js';
|
|
6
6
|
import { Select } from '../common/Select.js';
|
|
7
|
+
import { Input } from '../common/Input.js';
|
|
7
8
|
import { LoadingIndicator } from '../common/LoadingIndicator.js';
|
|
8
9
|
import { useTerminalSize } from '../../hooks/useTerminalSize.js';
|
|
9
10
|
import type { BranchItem, Statistics } from '../../types.js';
|
|
@@ -61,6 +62,11 @@ export interface BranchListScreenProps {
|
|
|
61
62
|
cleanupUI?: CleanupUIState;
|
|
62
63
|
version?: string | null;
|
|
63
64
|
workingDirectory?: string;
|
|
65
|
+
// Test support: allow external control of filter mode and query
|
|
66
|
+
testFilterMode?: boolean;
|
|
67
|
+
testOnFilterModeChange?: (mode: boolean) => void;
|
|
68
|
+
testFilterQuery?: string;
|
|
69
|
+
testOnFilterQueryChange?: (query: string) => void;
|
|
64
70
|
}
|
|
65
71
|
|
|
66
72
|
/**
|
|
@@ -81,16 +87,65 @@ export function BranchListScreen({
|
|
|
81
87
|
cleanupUI,
|
|
82
88
|
version,
|
|
83
89
|
workingDirectory,
|
|
90
|
+
testFilterMode,
|
|
91
|
+
testOnFilterModeChange,
|
|
92
|
+
testFilterQuery,
|
|
93
|
+
testOnFilterQueryChange,
|
|
84
94
|
}: BranchListScreenProps) {
|
|
85
95
|
const { rows } = useTerminalSize();
|
|
86
96
|
|
|
97
|
+
// Filter state - allow test control via props
|
|
98
|
+
const [internalFilterQuery, setInternalFilterQuery] = useState('');
|
|
99
|
+
const filterQuery = testFilterQuery !== undefined ? testFilterQuery : internalFilterQuery;
|
|
100
|
+
const setFilterQuery = useCallback(
|
|
101
|
+
(query: string) => {
|
|
102
|
+
setInternalFilterQuery(query);
|
|
103
|
+
testOnFilterQueryChange?.(query);
|
|
104
|
+
},
|
|
105
|
+
[testOnFilterQueryChange]
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
// Focus management: true = filter mode, false = branch selection mode
|
|
109
|
+
// Allow test control via props
|
|
110
|
+
const [internalFilterMode, setInternalFilterMode] = useState(false);
|
|
111
|
+
const filterMode = testFilterMode !== undefined ? testFilterMode : internalFilterMode;
|
|
112
|
+
const setFilterMode = useCallback(
|
|
113
|
+
(mode: boolean) => {
|
|
114
|
+
setInternalFilterMode(mode);
|
|
115
|
+
testOnFilterModeChange?.(mode);
|
|
116
|
+
},
|
|
117
|
+
[testOnFilterModeChange]
|
|
118
|
+
);
|
|
119
|
+
|
|
87
120
|
// Handle keyboard input
|
|
88
|
-
// Note:
|
|
89
|
-
|
|
121
|
+
// Note: Input component blocks specific keys (c/r/m/f) using blockKeys prop
|
|
122
|
+
// This prevents shortcuts from triggering while typing in the filter
|
|
123
|
+
useInput((input, key) => {
|
|
90
124
|
if (cleanupUI?.inputLocked) {
|
|
91
125
|
return;
|
|
92
126
|
}
|
|
93
127
|
|
|
128
|
+
// Escape key handling
|
|
129
|
+
if (key.escape) {
|
|
130
|
+
if (filterQuery) {
|
|
131
|
+
// Clear filter query first
|
|
132
|
+
setFilterQuery('');
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (filterMode) {
|
|
136
|
+
// Exit filter mode if query is empty
|
|
137
|
+
setFilterMode(false);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Enter filter mode with 'f' key (only in branch selection mode)
|
|
143
|
+
if (input === 'f' && !filterMode) {
|
|
144
|
+
setFilterMode(true);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Global shortcuts (blocked by Input component when typing in filter mode)
|
|
94
149
|
if (input === 'm' && onNavigate) {
|
|
95
150
|
onNavigate('worktree-manager');
|
|
96
151
|
} else if (input === 'c') {
|
|
@@ -100,23 +155,48 @@ export function BranchListScreen({
|
|
|
100
155
|
}
|
|
101
156
|
});
|
|
102
157
|
|
|
158
|
+
// Filter branches based on query
|
|
159
|
+
const filteredBranches = useMemo(() => {
|
|
160
|
+
if (!filterQuery.trim()) {
|
|
161
|
+
return branches;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const query = filterQuery.toLowerCase();
|
|
165
|
+
return branches.filter((branch) => {
|
|
166
|
+
// Search in branch name
|
|
167
|
+
if (branch.name.toLowerCase().includes(query)) {
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Search in PR title if available (only openPR has title)
|
|
172
|
+
if (branch.openPR?.title?.toLowerCase().includes(query)) {
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return false;
|
|
177
|
+
});
|
|
178
|
+
}, [branches, filterQuery]);
|
|
179
|
+
|
|
103
180
|
// Calculate available space for branch list
|
|
104
181
|
// Header: 2 lines (title + divider)
|
|
182
|
+
// Filter input: 1 line
|
|
105
183
|
// Stats: 1 line
|
|
106
184
|
// Empty line: 1 line
|
|
107
185
|
// Footer: 1 line
|
|
108
|
-
// Total fixed:
|
|
186
|
+
// Total fixed: 6 lines
|
|
109
187
|
const headerLines = 2;
|
|
188
|
+
const filterLines = 1;
|
|
110
189
|
const statsLines = 1;
|
|
111
190
|
const emptyLine = 1;
|
|
112
191
|
const footerLines = 1;
|
|
113
|
-
const fixedLines = headerLines + statsLines + emptyLine + footerLines;
|
|
192
|
+
const fixedLines = headerLines + filterLines + statsLines + emptyLine + footerLines;
|
|
114
193
|
const contentHeight = rows - fixedLines;
|
|
115
194
|
const limit = Math.max(5, contentHeight); // Minimum 5 items visible
|
|
116
195
|
|
|
117
196
|
// Footer actions
|
|
118
197
|
const footerActions = [
|
|
119
198
|
{ key: 'enter', description: 'Select' },
|
|
199
|
+
{ key: 'f', description: 'Filter' },
|
|
120
200
|
{ key: 'r', description: 'Refresh' },
|
|
121
201
|
{ key: 'm', description: 'Manage worktrees' },
|
|
122
202
|
{ key: 'c', description: 'Cleanup branches' },
|
|
@@ -219,7 +299,7 @@ export function BranchListScreen({
|
|
|
219
299
|
}
|
|
220
300
|
|
|
221
301
|
const output = isSelected
|
|
222
|
-
?
|
|
302
|
+
? `\u001b[46m\u001b[30m${line}\u001b[0m`
|
|
223
303
|
: line;
|
|
224
304
|
return <Text>{output}</Text>;
|
|
225
305
|
},
|
|
@@ -236,8 +316,30 @@ export function BranchListScreen({
|
|
|
236
316
|
{...(workingDirectory !== undefined && { workingDirectory })}
|
|
237
317
|
/>
|
|
238
318
|
|
|
319
|
+
{/* Filter Input - Always visible */}
|
|
320
|
+
<Box>
|
|
321
|
+
<Text dimColor>Filter: </Text>
|
|
322
|
+
{filterMode ? (
|
|
323
|
+
<Input
|
|
324
|
+
value={filterQuery}
|
|
325
|
+
onChange={setFilterQuery}
|
|
326
|
+
onSubmit={() => {}} // No-op: filter is applied in real-time
|
|
327
|
+
placeholder="Type to search..."
|
|
328
|
+
blockKeys={['c', 'r', 'm', 'f']} // Block shortcuts while typing
|
|
329
|
+
/>
|
|
330
|
+
) : (
|
|
331
|
+
<Text dimColor>{filterQuery || '(press f to filter)'}</Text>
|
|
332
|
+
)}
|
|
333
|
+
{filterQuery && (
|
|
334
|
+
<Text dimColor>
|
|
335
|
+
{' '}
|
|
336
|
+
(Showing {filteredBranches.length} of {branches.length})
|
|
337
|
+
</Text>
|
|
338
|
+
)}
|
|
339
|
+
</Box>
|
|
340
|
+
|
|
239
341
|
{/* Stats */}
|
|
240
|
-
<Box
|
|
342
|
+
<Box>
|
|
241
343
|
<Stats stats={stats} lastUpdated={lastUpdated} />
|
|
242
344
|
</Box>
|
|
243
345
|
|
|
@@ -268,9 +370,15 @@ export function BranchListScreen({
|
|
|
268
370
|
</Box>
|
|
269
371
|
)}
|
|
270
372
|
|
|
271
|
-
{!loading && !error && branches.length > 0 && (
|
|
373
|
+
{!loading && !error && branches.length > 0 && filteredBranches.length === 0 && filterQuery && (
|
|
374
|
+
<Box>
|
|
375
|
+
<Text dimColor>No branches match your filter</Text>
|
|
376
|
+
</Box>
|
|
377
|
+
)}
|
|
378
|
+
|
|
379
|
+
{!loading && !error && branches.length > 0 && filteredBranches.length > 0 && (
|
|
272
380
|
<Select
|
|
273
|
-
items={
|
|
381
|
+
items={filteredBranches}
|
|
274
382
|
onSelect={onSelect}
|
|
275
383
|
limit={limit}
|
|
276
384
|
disabled={Boolean(cleanupUI?.inputLocked)}
|
package/src/cli/ui/types.ts
CHANGED
|
@@ -8,7 +8,7 @@ export interface WorktreeInfo {
|
|
|
8
8
|
export interface BranchInfo {
|
|
9
9
|
name: string;
|
|
10
10
|
type: "local" | "remote";
|
|
11
|
-
branchType: "feature" | "hotfix" | "release" | "main" | "develop" | "other";
|
|
11
|
+
branchType: "feature" | "bugfix" | "hotfix" | "release" | "main" | "develop" | "other";
|
|
12
12
|
isCurrent: boolean;
|
|
13
13
|
description?: string;
|
|
14
14
|
worktree?: WorktreeInfo;
|
|
@@ -35,6 +35,7 @@ export interface EnhancedBranchChoice extends BranchChoice {
|
|
|
35
35
|
|
|
36
36
|
export type BranchType =
|
|
37
37
|
| "feature"
|
|
38
|
+
| "bugfix"
|
|
38
39
|
| "hotfix"
|
|
39
40
|
| "release"
|
|
40
41
|
| "main"
|
package/src/config/constants.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
// ブランチタイプ
|
|
6
6
|
export const BRANCH_TYPES = {
|
|
7
7
|
FEATURE: "feature",
|
|
8
|
+
BUGFIX: "bugfix",
|
|
8
9
|
HOTFIX: "hotfix",
|
|
9
10
|
RELEASE: "release",
|
|
10
11
|
MAIN: "main",
|
|
@@ -15,6 +16,7 @@ export const BRANCH_TYPES = {
|
|
|
15
16
|
// ブランチプレフィックス
|
|
16
17
|
export const BRANCH_PREFIXES = {
|
|
17
18
|
FEATURE: "feature/",
|
|
19
|
+
BUGFIX: "bugfix/",
|
|
18
20
|
HOTFIX: "hotfix/",
|
|
19
21
|
RELEASE: "release/",
|
|
20
22
|
} as const;
|
package/src/git.ts
CHANGED
|
@@ -351,6 +351,7 @@ function getBranchType(branchName: string): BranchInfo["branchType"] {
|
|
|
351
351
|
if (branchName === "main" || branchName === "master") return "main";
|
|
352
352
|
if (branchName === "develop" || branchName === "dev") return "develop";
|
|
353
353
|
if (branchName.startsWith("feature/")) return "feature";
|
|
354
|
+
if (branchName.startsWith("bugfix/") || branchName.startsWith("bug/")) return "bugfix";
|
|
354
355
|
if (branchName.startsWith("hotfix/")) return "hotfix";
|
|
355
356
|
if (branchName.startsWith("release/")) return "release";
|
|
356
357
|
return "other";
|
|
@@ -52,6 +52,7 @@ export class GitService {
|
|
|
52
52
|
|
|
53
53
|
private getBranchType(branchName: string): BranchInfo["branchType"] {
|
|
54
54
|
if (branchName.startsWith("feature/")) return "feature";
|
|
55
|
+
if (branchName.startsWith("bugfix/") || branchName.startsWith("bug/")) return "bugfix";
|
|
55
56
|
if (branchName.startsWith("hotfix/")) return "hotfix";
|
|
56
57
|
if (branchName.startsWith("release/")) return "release";
|
|
57
58
|
if (branchName === "main" || branchName === "master") return "main";
|