ccmanager 0.0.1

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.
Files changed (40) hide show
  1. package/README.md +85 -0
  2. package/dist/app.d.ts +6 -0
  3. package/dist/app.js +57 -0
  4. package/dist/cli.d.ts +2 -0
  5. package/dist/cli.js +24 -0
  6. package/dist/components/App.d.ts +3 -0
  7. package/dist/components/App.js +228 -0
  8. package/dist/components/ConfigureShortcuts.d.ts +6 -0
  9. package/dist/components/ConfigureShortcuts.js +139 -0
  10. package/dist/components/Confirmation.d.ts +12 -0
  11. package/dist/components/Confirmation.js +42 -0
  12. package/dist/components/DeleteWorktree.d.ts +7 -0
  13. package/dist/components/DeleteWorktree.js +116 -0
  14. package/dist/components/Menu.d.ts +9 -0
  15. package/dist/components/Menu.js +154 -0
  16. package/dist/components/MergeWorktree.d.ts +7 -0
  17. package/dist/components/MergeWorktree.js +142 -0
  18. package/dist/components/NewWorktree.d.ts +7 -0
  19. package/dist/components/NewWorktree.js +49 -0
  20. package/dist/components/Session.d.ts +10 -0
  21. package/dist/components/Session.js +121 -0
  22. package/dist/constants/statusIcons.d.ts +18 -0
  23. package/dist/constants/statusIcons.js +27 -0
  24. package/dist/services/sessionManager.d.ts +16 -0
  25. package/dist/services/sessionManager.js +190 -0
  26. package/dist/services/sessionManager.test.d.ts +1 -0
  27. package/dist/services/sessionManager.test.js +99 -0
  28. package/dist/services/shortcutManager.d.ts +17 -0
  29. package/dist/services/shortcutManager.js +167 -0
  30. package/dist/services/worktreeService.d.ts +24 -0
  31. package/dist/services/worktreeService.js +220 -0
  32. package/dist/types/index.d.ts +36 -0
  33. package/dist/types/index.js +4 -0
  34. package/dist/utils/logger.d.ts +14 -0
  35. package/dist/utils/logger.js +21 -0
  36. package/dist/utils/promptDetector.d.ts +1 -0
  37. package/dist/utils/promptDetector.js +20 -0
  38. package/dist/utils/promptDetector.test.d.ts +1 -0
  39. package/dist/utils/promptDetector.test.js +81 -0
  40. package/package.json +70 -0
package/README.md ADDED
@@ -0,0 +1,85 @@
1
+ # CCManager - Claude Code Worktree Manager
2
+
3
+ CCManager is a TUI application for managing multiple Claude Code sessions across Git worktrees.
4
+
5
+ ## Features
6
+
7
+ - Run multiple Claude Code sessions in parallel across different Git worktrees
8
+ - Switch between sessions seamlessly
9
+ - Visual status indicators for session states (busy, waiting, idle)
10
+ - Create, merge, and delete worktrees from within the app
11
+ - **Configurable keyboard shortcuts**
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ $ npm install
17
+ $ npm run build
18
+ $ npm start
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ ```bash
24
+ $ npx ccmanager
25
+ ```
26
+
27
+ ## Keyboard Shortcuts
28
+
29
+ ### Default Shortcuts
30
+
31
+ - **Ctrl+E**: Return to menu from active session (by default)
32
+
33
+ ### Customizing Shortcuts
34
+
35
+ You can customize keyboard shortcuts in two ways:
36
+
37
+ 1. **Through the UI**: Select "Configure Shortcuts" from the main menu
38
+ 2. **Configuration file**: Edit `~/.config/ccmanager/shortcuts.json`
39
+
40
+ Example configuration:
41
+ ```json
42
+ {
43
+ "returnToMenu": {
44
+ "ctrl": true,
45
+ "key": "r"
46
+ },
47
+ "exitApp": {
48
+ "ctrl": true,
49
+ "key": "x"
50
+ },
51
+ "cancel": {
52
+ "key": "escape"
53
+ }
54
+ }
55
+ ```
56
+
57
+ ### Restrictions
58
+
59
+ - Shortcuts must use a modifier key (Ctrl) except for special keys like Escape
60
+ - The following key combinations are reserved and cannot be used:
61
+ - Ctrl+C
62
+ - Ctrl+D
63
+ - Ctrl+[ (equivalent to Escape)
64
+
65
+ ## Development
66
+
67
+ ```bash
68
+ # Install dependencies
69
+ npm install
70
+
71
+ # Run in development mode
72
+ npm run dev
73
+
74
+ # Build
75
+ npm run build
76
+
77
+ # Run tests
78
+ npm test
79
+
80
+ # Run linter
81
+ npm run lint
82
+
83
+ # Run type checker
84
+ npm run typecheck
85
+ ```
package/dist/app.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ interface AppProps {
3
+ onReturnToMenu?: () => void;
4
+ }
5
+ declare const App: React.FC<AppProps>;
6
+ export default App;
package/dist/app.js ADDED
@@ -0,0 +1,57 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import { Box, Text, useInput, useApp, useStdout } from 'ink';
3
+ import { spawn } from 'node-pty';
4
+ const App = ({ onReturnToMenu }) => {
5
+ const { exit } = useApp();
6
+ const { stdout } = useStdout();
7
+ const [pty, setPty] = useState(null);
8
+ const [showMenu, setShowMenu] = useState(false);
9
+ useEffect(() => {
10
+ const ptyProcess = spawn('claude', [], {
11
+ name: 'xterm-color',
12
+ cols: process.stdout.columns || 80,
13
+ rows: process.stdout.rows || 24,
14
+ cwd: process.cwd(),
15
+ env: process.env,
16
+ });
17
+ ptyProcess.onData((data) => {
18
+ if (stdout) {
19
+ stdout.write(data);
20
+ }
21
+ });
22
+ ptyProcess.onExit(() => {
23
+ exit();
24
+ });
25
+ setPty(ptyProcess);
26
+ if (stdout) {
27
+ stdout.on('resize', () => {
28
+ ptyProcess.resize(process.stdout.columns || 80, process.stdout.rows || 24);
29
+ });
30
+ }
31
+ return () => {
32
+ ptyProcess.kill();
33
+ };
34
+ }, [exit, stdout]);
35
+ useInput((char, key) => {
36
+ if (!pty)
37
+ return;
38
+ if (key.ctrl && char === 'e') {
39
+ if (onReturnToMenu) {
40
+ onReturnToMenu();
41
+ }
42
+ else {
43
+ setShowMenu(true);
44
+ }
45
+ return;
46
+ }
47
+ // if (char) {
48
+ // pty.write(char);
49
+ // }
50
+ });
51
+ if (showMenu) {
52
+ return (React.createElement(Box, { flexDirection: "column" },
53
+ React.createElement(Text, { color: "green" }, "Press Ctrl+E to return to menu")));
54
+ }
55
+ return null;
56
+ };
57
+ export default App;
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ import React from 'react';
3
+ import { render } from 'ink';
4
+ import meow from 'meow';
5
+ import App from './components/App.js';
6
+ meow(`
7
+ Usage
8
+ $ ccmanager
9
+
10
+ Options
11
+ --help Show help
12
+ --version Show version
13
+
14
+ Examples
15
+ $ ccmanager
16
+ `, {
17
+ importMeta: import.meta,
18
+ });
19
+ // Check if we're in a TTY environment
20
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
21
+ console.error('Error: ccmanager must be run in an interactive terminal (TTY)');
22
+ process.exit(1);
23
+ }
24
+ render(React.createElement(App, null));
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ declare const App: React.FC;
3
+ export default App;
@@ -0,0 +1,228 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { useApp, Box, Text } from 'ink';
3
+ import Menu from './Menu.js';
4
+ import Session from './Session.js';
5
+ import NewWorktree from './NewWorktree.js';
6
+ import DeleteWorktree from './DeleteWorktree.js';
7
+ import MergeWorktree from './MergeWorktree.js';
8
+ import ConfigureShortcuts from './ConfigureShortcuts.js';
9
+ import { SessionManager } from '../services/sessionManager.js';
10
+ import { WorktreeService } from '../services/worktreeService.js';
11
+ import { shortcutManager } from '../services/shortcutManager.js';
12
+ const App = () => {
13
+ const { exit } = useApp();
14
+ const [view, setView] = useState('menu');
15
+ const [sessionManager] = useState(() => new SessionManager());
16
+ const [worktreeService] = useState(() => new WorktreeService());
17
+ const [activeSession, setActiveSession] = useState(null);
18
+ const [error, setError] = useState(null);
19
+ const [menuKey, setMenuKey] = useState(0); // Force menu refresh
20
+ useEffect(() => {
21
+ // Listen for session exits to return to menu automatically
22
+ const handleSessionExit = (session) => {
23
+ // If the exited session is the active one, return to menu
24
+ setActiveSession(current => {
25
+ if (current && session.id === current.id) {
26
+ // Session that exited is the active one, trigger return to menu
27
+ setTimeout(() => {
28
+ setActiveSession(null);
29
+ setError(null);
30
+ setView('menu');
31
+ setMenuKey(prev => prev + 1);
32
+ if (process.stdout.isTTY) {
33
+ process.stdout.write('\x1B[2J\x1B[H');
34
+ }
35
+ process.stdin.resume();
36
+ process.stdin.setEncoding('utf8');
37
+ }, 0);
38
+ }
39
+ return current;
40
+ });
41
+ };
42
+ sessionManager.on('sessionExit', handleSessionExit);
43
+ // Cleanup on unmount
44
+ return () => {
45
+ sessionManager.off('sessionExit', handleSessionExit);
46
+ sessionManager.destroy();
47
+ };
48
+ }, [sessionManager]);
49
+ const handleSelectWorktree = (worktree) => {
50
+ // Check if this is the new worktree option
51
+ if (worktree.path === '') {
52
+ setView('new-worktree');
53
+ return;
54
+ }
55
+ // Check if this is the delete worktree option
56
+ if (worktree.path === 'DELETE_WORKTREE') {
57
+ setView('delete-worktree');
58
+ return;
59
+ }
60
+ // Check if this is the merge worktree option
61
+ if (worktree.path === 'MERGE_WORKTREE') {
62
+ setView('merge-worktree');
63
+ return;
64
+ }
65
+ // Check if this is the configure shortcuts option
66
+ if (worktree.path === 'CONFIGURE_SHORTCUTS') {
67
+ setView('configure-shortcuts');
68
+ return;
69
+ }
70
+ // Check if this is the exit application option
71
+ if (worktree.path === 'EXIT_APPLICATION') {
72
+ sessionManager.destroy();
73
+ exit();
74
+ return;
75
+ }
76
+ // Get or create session for this worktree
77
+ let session = sessionManager.getSession(worktree.path);
78
+ if (!session) {
79
+ session = sessionManager.createSession(worktree.path);
80
+ }
81
+ setActiveSession(session);
82
+ setView('session');
83
+ };
84
+ const handleReturnToMenu = () => {
85
+ setActiveSession(null);
86
+ setError(null);
87
+ // Add a small delay to ensure Session cleanup completes
88
+ setTimeout(() => {
89
+ setView('menu');
90
+ setMenuKey(prev => prev + 1); // Force menu refresh
91
+ // Clear the screen when returning to menu
92
+ if (process.stdout.isTTY) {
93
+ process.stdout.write('\x1B[2J\x1B[H');
94
+ }
95
+ // Ensure stdin is in a clean state for Ink components
96
+ if (process.stdin.isTTY) {
97
+ // Flush any pending input to prevent escape sequences from leaking
98
+ process.stdin.read();
99
+ process.stdin.setRawMode(false);
100
+ process.stdin.resume();
101
+ process.stdin.setEncoding('utf8');
102
+ }
103
+ }, 50); // Small delay to ensure proper cleanup
104
+ };
105
+ const handleCreateWorktree = async (path, branch) => {
106
+ setView('creating-worktree');
107
+ setError(null);
108
+ // Create the worktree
109
+ const result = worktreeService.createWorktree(path, branch);
110
+ if (result.success) {
111
+ // Success - return to menu
112
+ handleReturnToMenu();
113
+ }
114
+ else {
115
+ // Show error
116
+ setError(result.error || 'Failed to create worktree');
117
+ setView('new-worktree');
118
+ }
119
+ };
120
+ const handleCancelNewWorktree = () => {
121
+ handleReturnToMenu();
122
+ };
123
+ const handleDeleteWorktrees = async (worktreePaths) => {
124
+ setView('deleting-worktree');
125
+ setError(null);
126
+ // Delete the worktrees
127
+ let hasError = false;
128
+ for (const path of worktreePaths) {
129
+ const result = worktreeService.deleteWorktree(path);
130
+ if (!result.success) {
131
+ hasError = true;
132
+ setError(result.error || 'Failed to delete worktree');
133
+ break;
134
+ }
135
+ }
136
+ if (!hasError) {
137
+ // Success - return to menu
138
+ handleReturnToMenu();
139
+ }
140
+ else {
141
+ // Show error
142
+ setView('delete-worktree');
143
+ }
144
+ };
145
+ const handleCancelDeleteWorktree = () => {
146
+ handleReturnToMenu();
147
+ };
148
+ const handleMergeWorktree = async (sourceBranch, targetBranch, deleteAfterMerge, useRebase) => {
149
+ setView('merging-worktree');
150
+ setError(null);
151
+ // Perform the merge
152
+ const mergeResult = worktreeService.mergeWorktree(sourceBranch, targetBranch, useRebase);
153
+ if (mergeResult.success) {
154
+ // If user wants to delete the merged branch
155
+ if (deleteAfterMerge) {
156
+ const deleteResult = worktreeService.deleteWorktreeByBranch(sourceBranch);
157
+ if (!deleteResult.success) {
158
+ setError(deleteResult.error || 'Failed to delete merged worktree');
159
+ setView('merge-worktree');
160
+ return;
161
+ }
162
+ }
163
+ // Success - return to menu
164
+ handleReturnToMenu();
165
+ }
166
+ else {
167
+ // Show error
168
+ setError(mergeResult.error || 'Failed to merge branches');
169
+ setView('merge-worktree');
170
+ }
171
+ };
172
+ const handleCancelMergeWorktree = () => {
173
+ handleReturnToMenu();
174
+ };
175
+ if (view === 'menu') {
176
+ return (React.createElement(Menu, { key: menuKey, sessionManager: sessionManager, onSelectWorktree: handleSelectWorktree }));
177
+ }
178
+ if (view === 'session' && activeSession) {
179
+ return (React.createElement(Box, { flexDirection: "column" },
180
+ React.createElement(Session, { key: activeSession.id, session: activeSession, sessionManager: sessionManager, onReturnToMenu: handleReturnToMenu }),
181
+ React.createElement(Box, { marginTop: 1 },
182
+ React.createElement(Text, { dimColor: true },
183
+ "Press ",
184
+ shortcutManager.getShortcutDisplay('returnToMenu'),
185
+ " to return to menu"))));
186
+ }
187
+ if (view === 'new-worktree') {
188
+ return (React.createElement(Box, { flexDirection: "column" },
189
+ error && (React.createElement(Box, { marginBottom: 1 },
190
+ React.createElement(Text, { color: "red" },
191
+ "Error: ",
192
+ error))),
193
+ React.createElement(NewWorktree, { onComplete: handleCreateWorktree, onCancel: handleCancelNewWorktree })));
194
+ }
195
+ if (view === 'creating-worktree') {
196
+ return (React.createElement(Box, { flexDirection: "column" },
197
+ React.createElement(Text, { color: "green" }, "Creating worktree...")));
198
+ }
199
+ if (view === 'delete-worktree') {
200
+ return (React.createElement(Box, { flexDirection: "column" },
201
+ error && (React.createElement(Box, { marginBottom: 1 },
202
+ React.createElement(Text, { color: "red" },
203
+ "Error: ",
204
+ error))),
205
+ React.createElement(DeleteWorktree, { onComplete: handleDeleteWorktrees, onCancel: handleCancelDeleteWorktree })));
206
+ }
207
+ if (view === 'deleting-worktree') {
208
+ return (React.createElement(Box, { flexDirection: "column" },
209
+ React.createElement(Text, { color: "red" }, "Deleting worktrees...")));
210
+ }
211
+ if (view === 'merge-worktree') {
212
+ return (React.createElement(Box, { flexDirection: "column" },
213
+ error && (React.createElement(Box, { marginBottom: 1 },
214
+ React.createElement(Text, { color: "red" },
215
+ "Error: ",
216
+ error))),
217
+ React.createElement(MergeWorktree, { onComplete: handleMergeWorktree, onCancel: handleCancelMergeWorktree })));
218
+ }
219
+ if (view === 'merging-worktree') {
220
+ return (React.createElement(Box, { flexDirection: "column" },
221
+ React.createElement(Text, { color: "green" }, "Merging worktrees...")));
222
+ }
223
+ if (view === 'configure-shortcuts') {
224
+ return React.createElement(ConfigureShortcuts, { onComplete: handleReturnToMenu });
225
+ }
226
+ return null;
227
+ };
228
+ export default App;
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ interface ConfigureShortcutsProps {
3
+ onComplete: () => void;
4
+ }
5
+ declare const ConfigureShortcuts: React.FC<ConfigureShortcutsProps>;
6
+ export default ConfigureShortcuts;
@@ -0,0 +1,139 @@
1
+ import React, { useState } from 'react';
2
+ import { Box, Text, useInput } from 'ink';
3
+ import SelectInput from 'ink-select-input';
4
+ import { shortcutManager } from '../services/shortcutManager.js';
5
+ const ConfigureShortcuts = ({ onComplete, }) => {
6
+ const [step, setStep] = useState('menu');
7
+ const [shortcuts, setShortcuts] = useState(shortcutManager.getShortcuts());
8
+ const [editingShortcut, setEditingShortcut] = useState(null);
9
+ const [error, setError] = useState(null);
10
+ const getShortcutDisplayFromState = (key) => {
11
+ const shortcut = shortcuts[key];
12
+ if (!shortcut)
13
+ return 'Not set';
14
+ const parts = [];
15
+ if (shortcut.ctrl)
16
+ parts.push('Ctrl');
17
+ if (shortcut.alt)
18
+ parts.push('Alt');
19
+ if (shortcut.shift)
20
+ parts.push('Shift');
21
+ if (shortcut.key === 'escape') {
22
+ parts.push('Esc');
23
+ }
24
+ else if (shortcut.key) {
25
+ parts.push(shortcut.key.toUpperCase());
26
+ }
27
+ return parts.join('+');
28
+ };
29
+ const shortcutItems = [
30
+ {
31
+ label: `Return to Menu: ${getShortcutDisplayFromState('returnToMenu')}`,
32
+ value: 'returnToMenu',
33
+ },
34
+ {
35
+ label: '---',
36
+ value: 'separator',
37
+ },
38
+ {
39
+ label: 'Save and Exit',
40
+ value: 'save',
41
+ },
42
+ {
43
+ label: 'Exit without Saving',
44
+ value: 'exit',
45
+ },
46
+ ];
47
+ useInput((input, key) => {
48
+ if (step === 'capturing' && editingShortcut) {
49
+ // Capture the key combination
50
+ const newShortcut = {
51
+ key: key.escape ? 'escape' : input || '',
52
+ ctrl: key.ctrl || false,
53
+ alt: false, // Ink doesn't support alt
54
+ shift: false, // Ink doesn't support shift
55
+ };
56
+ // Check for reserved keys
57
+ if (key.ctrl && input === 'c') {
58
+ setError('Ctrl+C is reserved and cannot be used');
59
+ setStep('menu');
60
+ return;
61
+ }
62
+ if (key.ctrl && input === 'd') {
63
+ setError('Ctrl+D is reserved and cannot be used');
64
+ setStep('menu');
65
+ return;
66
+ }
67
+ if (key.ctrl && input === '[') {
68
+ setError('Ctrl+[ is reserved and cannot be used');
69
+ setStep('menu');
70
+ return;
71
+ }
72
+ // Validate that a modifier is used (except for escape)
73
+ if (!key.escape && !key.ctrl) {
74
+ setError('Shortcuts must use a modifier key (Ctrl)');
75
+ setStep('menu');
76
+ return;
77
+ }
78
+ setShortcuts({
79
+ ...shortcuts,
80
+ [editingShortcut]: newShortcut,
81
+ });
82
+ setError(null);
83
+ setStep('menu');
84
+ }
85
+ else if (step === 'menu') {
86
+ if (key.escape) {
87
+ onComplete();
88
+ }
89
+ }
90
+ });
91
+ const handleSelect = (item) => {
92
+ if (item.value === 'separator') {
93
+ return;
94
+ }
95
+ if (item.value === 'save') {
96
+ const success = shortcutManager.saveShortcuts(shortcuts);
97
+ if (success) {
98
+ onComplete();
99
+ }
100
+ else {
101
+ setError('Failed to save shortcuts');
102
+ }
103
+ return;
104
+ }
105
+ if (item.value === 'exit') {
106
+ onComplete();
107
+ return;
108
+ }
109
+ // Start editing a shortcut
110
+ setEditingShortcut(item.value);
111
+ setStep('capturing');
112
+ setError(null);
113
+ };
114
+ if (step === 'capturing') {
115
+ return (React.createElement(Box, { flexDirection: "column" },
116
+ React.createElement(Text, { bold: true, color: "green" },
117
+ "Configure Shortcut: ",
118
+ editingShortcut),
119
+ React.createElement(Box, { marginTop: 1 },
120
+ React.createElement(Text, null, "Press the key combination you want to use")),
121
+ React.createElement(Box, { marginTop: 1 },
122
+ React.createElement(Text, { dimColor: true }, "Note: Shortcuts must use Ctrl as a modifier key")),
123
+ React.createElement(Box, { marginTop: 1 },
124
+ React.createElement(Text, { dimColor: true }, "Reserved: Ctrl+C, Ctrl+D, Ctrl+[ (Esc)"))));
125
+ }
126
+ return (React.createElement(Box, { flexDirection: "column" },
127
+ React.createElement(Box, { marginBottom: 1 },
128
+ React.createElement(Text, { bold: true, color: "green" }, "Configure Keyboard Shortcuts")),
129
+ error && (React.createElement(Box, { marginBottom: 1 },
130
+ React.createElement(Text, { color: "red" },
131
+ "Error: ",
132
+ error))),
133
+ React.createElement(Box, { marginBottom: 1 },
134
+ React.createElement(Text, { dimColor: true }, "Select a shortcut to change:")),
135
+ React.createElement(SelectInput, { items: shortcutItems, onSelect: handleSelect, isFocused: true }),
136
+ React.createElement(Box, { marginTop: 1 },
137
+ React.createElement(Text, { dimColor: true }, "Press Esc to exit without saving"))));
138
+ };
139
+ export default ConfigureShortcuts;
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ interface ConfirmationProps {
3
+ message: string | React.ReactNode;
4
+ onConfirm: () => void;
5
+ onCancel: () => void;
6
+ confirmText?: string;
7
+ cancelText?: string;
8
+ confirmColor?: string;
9
+ cancelColor?: string;
10
+ }
11
+ declare const Confirmation: React.FC<ConfirmationProps>;
12
+ export default Confirmation;
@@ -0,0 +1,42 @@
1
+ import React, { useState } from 'react';
2
+ import { Box, Text, useInput } from 'ink';
3
+ import { shortcutManager } from '../services/shortcutManager.js';
4
+ const Confirmation = ({ message, onConfirm, onCancel, confirmText = 'Yes', cancelText = 'No', confirmColor = 'green', cancelColor = 'red', }) => {
5
+ const [focused, setFocused] = useState(true); // true = confirm, false = cancel
6
+ useInput((input, key) => {
7
+ if (key.leftArrow || key.rightArrow) {
8
+ setFocused(!focused);
9
+ }
10
+ else if (key.return) {
11
+ if (focused) {
12
+ onConfirm();
13
+ }
14
+ else {
15
+ onCancel();
16
+ }
17
+ }
18
+ else if (shortcutManager.matchesShortcut('cancel', input, key)) {
19
+ onCancel();
20
+ }
21
+ });
22
+ return (React.createElement(Box, { flexDirection: "column" },
23
+ React.createElement(Box, { marginBottom: 1 }, message),
24
+ React.createElement(Box, null,
25
+ React.createElement(Box, { marginRight: 2 },
26
+ React.createElement(Text, { color: focused ? confirmColor : 'white', inverse: focused },
27
+ ' ',
28
+ confirmText,
29
+ ' ')),
30
+ React.createElement(Box, null,
31
+ React.createElement(Text, { color: !focused ? cancelColor : 'white', inverse: !focused },
32
+ ' ',
33
+ cancelText,
34
+ ' '))),
35
+ React.createElement(Box, { marginTop: 1 },
36
+ React.createElement(Text, { dimColor: true },
37
+ "Use \u2190 \u2192 to navigate, Enter to select,",
38
+ ' ',
39
+ shortcutManager.getShortcutDisplay('cancel'),
40
+ " to cancel"))));
41
+ };
42
+ export default Confirmation;
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ interface DeleteWorktreeProps {
3
+ onComplete: (worktreePaths: string[]) => void;
4
+ onCancel: () => void;
5
+ }
6
+ declare const DeleteWorktree: React.FC<DeleteWorktreeProps>;
7
+ export default DeleteWorktree;