sncommit 1.0.0 → 1.0.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 +1 -1
- package/dist/index.js +93 -99
- package/package.json +5 -2
- package/bun.lock +0 -705
- package/eslint.config.mjs +0 -34
- package/src/components/App.tsx +0 -357
- package/src/components/CommitSuggestions.tsx +0 -157
- package/src/components/ConfigApp.tsx +0 -258
- package/src/components/CustomInputPrompt.tsx +0 -82
- package/src/components/StagedFiles.tsx +0 -52
- package/src/components/TuiDialog.tsx +0 -177
- package/src/index.tsx +0 -150
- package/src/services/git.ts +0 -128
- package/src/services/groq.ts +0 -360
- package/src/suppress-warnings.ts +0 -18
- package/src/types/index.ts +0 -36
- package/src/utils/config.ts +0 -66
- package/tsconfig.json +0 -19
package/eslint.config.mjs
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import js from "@eslint/js";
|
|
2
|
-
import globals from "globals";
|
|
3
|
-
import reactHooks from "eslint-plugin-react-hooks";
|
|
4
|
-
import react from "eslint-plugin-react";
|
|
5
|
-
import tseslint from "typescript-eslint";
|
|
6
|
-
import { defineConfig } from "eslint/config";
|
|
7
|
-
|
|
8
|
-
export default defineConfig(
|
|
9
|
-
{ ignores: ["dist", "eslint.config.mjs"] },
|
|
10
|
-
{
|
|
11
|
-
extends: [js.configs.recommended, ...tseslint.configs.recommended],
|
|
12
|
-
files: ["**/*.{ts,tsx,js,jsx}"],
|
|
13
|
-
languageOptions: {
|
|
14
|
-
ecmaVersion: 2020,
|
|
15
|
-
globals: globals.node,
|
|
16
|
-
},
|
|
17
|
-
plugins: {
|
|
18
|
-
"react-hooks": reactHooks,
|
|
19
|
-
react,
|
|
20
|
-
},
|
|
21
|
-
rules: {
|
|
22
|
-
...reactHooks.configs.recommended.rules,
|
|
23
|
-
"react/react-in-jsx-scope": "off",
|
|
24
|
-
// Allow explicit any for now as we might have some generic types or old code
|
|
25
|
-
"@typescript-eslint/no-explicit-any": "warn",
|
|
26
|
-
"@typescript-eslint/no-unused-vars": [
|
|
27
|
-
"warn",
|
|
28
|
-
{ argsIgnorePattern: "^_" },
|
|
29
|
-
],
|
|
30
|
-
// Suppress punycode warning in index.tsx requires process manipulation which is hacky but fine
|
|
31
|
-
"@typescript-eslint/ban-ts-comment": "off",
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
);
|
package/src/components/App.tsx
DELETED
|
@@ -1,357 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect } from "react";
|
|
2
|
-
import { Box, Text, useApp, useInput, useStdout } from "ink";
|
|
3
|
-
import { GitService } from "../services/git";
|
|
4
|
-
import { GroqService } from "../services/groq";
|
|
5
|
-
import { configManager } from "../utils/config";
|
|
6
|
-
import { StagedFiles } from "./StagedFiles";
|
|
7
|
-
import { CommitSuggestions } from "./CommitSuggestions";
|
|
8
|
-
import { CustomInputPrompt } from "./CustomInputPrompt";
|
|
9
|
-
import { AppState, GitFile } from "../types";
|
|
10
|
-
|
|
11
|
-
interface AppProps {
|
|
12
|
-
addAll?: boolean;
|
|
13
|
-
onExit: (message?: string) => void;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export const BetterCommitApp: React.FC<AppProps> = ({
|
|
17
|
-
addAll: _addAll = false,
|
|
18
|
-
onExit,
|
|
19
|
-
}) => {
|
|
20
|
-
const { exit } = useApp();
|
|
21
|
-
const { write } = useStdout();
|
|
22
|
-
const [state, setState] = useState<AppState>({
|
|
23
|
-
stagedFiles: [],
|
|
24
|
-
suggestions: [],
|
|
25
|
-
selectedIndex: 0,
|
|
26
|
-
isLoading: false,
|
|
27
|
-
error: undefined,
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
const [isUsingFallback, setIsUsingFallback] = useState(false);
|
|
31
|
-
|
|
32
|
-
const [successMessage, setSuccessMessage] = useState<string | undefined>(
|
|
33
|
-
undefined,
|
|
34
|
-
);
|
|
35
|
-
const [customInput, setCustomInput] = useState("");
|
|
36
|
-
const [isCustomInputMode, setIsCustomInputMode] = useState(false);
|
|
37
|
-
|
|
38
|
-
// Auto-exit when showing success message
|
|
39
|
-
useEffect(() => {
|
|
40
|
-
if (successMessage) {
|
|
41
|
-
write("\x1B[2J\x1B[0f");
|
|
42
|
-
const timer = setTimeout(() => {
|
|
43
|
-
onExit(successMessage);
|
|
44
|
-
exit();
|
|
45
|
-
}, 1500);
|
|
46
|
-
return () => clearTimeout(timer);
|
|
47
|
-
}
|
|
48
|
-
}, [successMessage, onExit, exit, write]);
|
|
49
|
-
|
|
50
|
-
// Handle global exit keys (disabled when custom input is active)
|
|
51
|
-
useInput(
|
|
52
|
-
(input, key) => {
|
|
53
|
-
if (key.escape || (key.ctrl && input === "c")) {
|
|
54
|
-
onExit("Operation cancelled");
|
|
55
|
-
exit();
|
|
56
|
-
}
|
|
57
|
-
},
|
|
58
|
-
{ isActive: !isCustomInputMode },
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
const config = configManager.getConfig();
|
|
62
|
-
const gitService = new GitService();
|
|
63
|
-
|
|
64
|
-
useEffect(() => {
|
|
65
|
-
const initializeApp = async () => {
|
|
66
|
-
try {
|
|
67
|
-
const stagedFiles = await gitService.getStagedFiles();
|
|
68
|
-
// No need to check for empty staged files here as index.tsx handles it
|
|
69
|
-
|
|
70
|
-
await gitService.getDiff();
|
|
71
|
-
|
|
72
|
-
await generateSuggestions(stagedFiles);
|
|
73
|
-
|
|
74
|
-
setState((prev) => ({
|
|
75
|
-
...prev,
|
|
76
|
-
stagedFiles,
|
|
77
|
-
}));
|
|
78
|
-
} catch (error) {
|
|
79
|
-
setState((prev) => ({
|
|
80
|
-
...prev,
|
|
81
|
-
error: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
82
|
-
}));
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
initializeApp();
|
|
87
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
88
|
-
}, []);
|
|
89
|
-
|
|
90
|
-
const generateSuggestions = async (stagedFiles: GitFile[]) => {
|
|
91
|
-
if (!config.groqApiKey || config.groqApiKey.trim() === "") {
|
|
92
|
-
setState((prev) => ({
|
|
93
|
-
...prev,
|
|
94
|
-
error:
|
|
95
|
-
'Groq API key not configured. Run "better-commit --config" to set it up.',
|
|
96
|
-
isLoading: false,
|
|
97
|
-
}));
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
try {
|
|
102
|
-
const groqService = new GroqService(config.groqApiKey, config);
|
|
103
|
-
const diff = await gitService.getDiff();
|
|
104
|
-
const diffStats = await gitService.getDiffStats();
|
|
105
|
-
const recentCommits = await gitService.getRecentCommits(
|
|
106
|
-
config.maxHistoryCommits || 50,
|
|
107
|
-
);
|
|
108
|
-
|
|
109
|
-
const suggestions = await groqService.generateCommitSuggestions(
|
|
110
|
-
stagedFiles,
|
|
111
|
-
diff,
|
|
112
|
-
recentCommits,
|
|
113
|
-
diffStats,
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
const hasFallback = suggestions.some((s) => s.isFallback);
|
|
117
|
-
setState((prev) => ({
|
|
118
|
-
...prev,
|
|
119
|
-
suggestions,
|
|
120
|
-
isLoading: false,
|
|
121
|
-
error: undefined,
|
|
122
|
-
}));
|
|
123
|
-
setIsUsingFallback(hasFallback);
|
|
124
|
-
} catch (error) {
|
|
125
|
-
setState((prev) => ({
|
|
126
|
-
...prev,
|
|
127
|
-
error: `Failed to generate suggestions: ${error instanceof Error ? error.message : String(error)}`,
|
|
128
|
-
isLoading: false,
|
|
129
|
-
}));
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
const handleSelect = (index: number) => {
|
|
134
|
-
setState((prev) => ({ ...prev, selectedIndex: index }));
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
const handleCommit = async (index: number) => {
|
|
138
|
-
const selectedSuggestion = state.suggestions[index];
|
|
139
|
-
if (selectedSuggestion) {
|
|
140
|
-
try {
|
|
141
|
-
await gitService.commit(selectedSuggestion.message);
|
|
142
|
-
setSuccessMessage(selectedSuggestion.message);
|
|
143
|
-
} catch (error) {
|
|
144
|
-
setState((prev) => ({
|
|
145
|
-
...prev,
|
|
146
|
-
error: `Failed to commit: ${error instanceof Error ? error.message : String(error)}`,
|
|
147
|
-
}));
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
const handleTryAgain = () => {
|
|
153
|
-
setState((prev) => ({ ...prev, isLoading: true, suggestions: [] }));
|
|
154
|
-
generateSuggestions(state.stagedFiles);
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
const handleCustomInput = () => {
|
|
158
|
-
setIsCustomInputMode(true);
|
|
159
|
-
setCustomInput("");
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
const handleCustomInputCancel = () => {
|
|
163
|
-
setIsCustomInputMode(false);
|
|
164
|
-
onExit("Custom input cancelled");
|
|
165
|
-
exit();
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
const handleCustomInputSubmit = async () => {
|
|
169
|
-
if (!customInput.trim()) {
|
|
170
|
-
setIsCustomInputMode(false);
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
setIsCustomInputMode(false);
|
|
175
|
-
setState((prev) => ({ ...prev, isLoading: true, suggestions: [] }));
|
|
176
|
-
await generateSuggestionsFromCustomInput(customInput.trim());
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
const generateSuggestionsFromCustomInput = async (userInput: string) => {
|
|
180
|
-
if (!config.groqApiKey || config.groqApiKey.trim() === "") {
|
|
181
|
-
setState((prev) => ({
|
|
182
|
-
...prev,
|
|
183
|
-
error:
|
|
184
|
-
'Groq API key not configured. Run "better-commit --config" to set it up.',
|
|
185
|
-
isLoading: false,
|
|
186
|
-
}));
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
try {
|
|
191
|
-
const groqService = new GroqService(config.groqApiKey, config);
|
|
192
|
-
const diff = await gitService.getDiff();
|
|
193
|
-
const diffStats = await gitService.getDiffStats();
|
|
194
|
-
const recentCommits = await gitService.getRecentCommits(
|
|
195
|
-
config.maxHistoryCommits || 50,
|
|
196
|
-
);
|
|
197
|
-
|
|
198
|
-
const suggestions =
|
|
199
|
-
await groqService.generateCommitSuggestionsFromCustomInput(
|
|
200
|
-
state.stagedFiles,
|
|
201
|
-
diff,
|
|
202
|
-
userInput,
|
|
203
|
-
recentCommits,
|
|
204
|
-
diffStats,
|
|
205
|
-
);
|
|
206
|
-
|
|
207
|
-
const hasFallback = suggestions.some((s) => s.isFallback);
|
|
208
|
-
setState((prev) => ({
|
|
209
|
-
...prev,
|
|
210
|
-
suggestions,
|
|
211
|
-
isLoading: false,
|
|
212
|
-
error: undefined,
|
|
213
|
-
}));
|
|
214
|
-
setIsUsingFallback(hasFallback);
|
|
215
|
-
} catch (error) {
|
|
216
|
-
setState((prev) => ({
|
|
217
|
-
...prev,
|
|
218
|
-
error: `Failed to generate suggestions: ${error instanceof Error ? error.message : String(error)}`,
|
|
219
|
-
isLoading: false,
|
|
220
|
-
}));
|
|
221
|
-
}
|
|
222
|
-
};
|
|
223
|
-
|
|
224
|
-
// Error State
|
|
225
|
-
if (state.error) {
|
|
226
|
-
return (
|
|
227
|
-
<Box flexDirection="column" padding={2}>
|
|
228
|
-
<Box
|
|
229
|
-
borderStyle="single"
|
|
230
|
-
borderColor="#ef4444"
|
|
231
|
-
padding={2}
|
|
232
|
-
marginBottom={1}
|
|
233
|
-
>
|
|
234
|
-
<Text color="#ef4444" bold>
|
|
235
|
-
Error
|
|
236
|
-
</Text>
|
|
237
|
-
<Box marginTop={1}>
|
|
238
|
-
<Text color="#e5e7eb">{state.error}</Text>
|
|
239
|
-
</Box>
|
|
240
|
-
</Box>
|
|
241
|
-
<Box>
|
|
242
|
-
<Text color="#6b7280">
|
|
243
|
-
Press <Text color="#ef4444">Esc</Text> or{" "}
|
|
244
|
-
<Text color="#ef4444">Ctrl+C</Text> to exit
|
|
245
|
-
</Text>
|
|
246
|
-
</Box>
|
|
247
|
-
</Box>
|
|
248
|
-
);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// Success State
|
|
252
|
-
if (successMessage) {
|
|
253
|
-
return (
|
|
254
|
-
<Box
|
|
255
|
-
flexDirection="column"
|
|
256
|
-
padding={2}
|
|
257
|
-
justifyContent="center"
|
|
258
|
-
alignItems="center"
|
|
259
|
-
flexGrow={1}
|
|
260
|
-
width="100%"
|
|
261
|
-
height="100%"
|
|
262
|
-
>
|
|
263
|
-
<Box
|
|
264
|
-
flexDirection="column"
|
|
265
|
-
borderStyle="single"
|
|
266
|
-
borderColor="#10b981"
|
|
267
|
-
padding={2}
|
|
268
|
-
width={60}
|
|
269
|
-
>
|
|
270
|
-
<Box marginBottom={1} justifyContent="center">
|
|
271
|
-
<Text color="#10b981" bold>
|
|
272
|
-
Commit Successful
|
|
273
|
-
</Text>
|
|
274
|
-
</Box>
|
|
275
|
-
<Box marginBottom={1} justifyContent="center">
|
|
276
|
-
<Text color="#e5e7eb">"{successMessage}"</Text>
|
|
277
|
-
</Box>
|
|
278
|
-
<Box justifyContent="center">
|
|
279
|
-
<Text color="#10b981">
|
|
280
|
-
Changes have been committed successfully!
|
|
281
|
-
</Text>
|
|
282
|
-
</Box>
|
|
283
|
-
</Box>
|
|
284
|
-
</Box>
|
|
285
|
-
);
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
// Loading State
|
|
289
|
-
if (state.stagedFiles.length === 0 && !state.error) {
|
|
290
|
-
return (
|
|
291
|
-
<Box flexDirection="column" padding={2} justifyContent="center">
|
|
292
|
-
<Text bold color="#8b5cf6">
|
|
293
|
-
Better Commit
|
|
294
|
-
</Text>
|
|
295
|
-
<Box marginTop={1}>
|
|
296
|
-
<Text color="#6b7280">Loading staged files...</Text>
|
|
297
|
-
</Box>
|
|
298
|
-
</Box>
|
|
299
|
-
);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
return (
|
|
303
|
-
<Box flexDirection="column" padding={1}>
|
|
304
|
-
{/* Header */}
|
|
305
|
-
<Box marginBottom={1}>
|
|
306
|
-
<Text bold color="#8b5cf6">
|
|
307
|
-
Better Commit
|
|
308
|
-
</Text>
|
|
309
|
-
<Text color="#6b7280"> • AI-Powered Commit Suggestions</Text>
|
|
310
|
-
</Box>
|
|
311
|
-
|
|
312
|
-
{/* Staged Files */}
|
|
313
|
-
<StagedFiles files={state.stagedFiles} />
|
|
314
|
-
|
|
315
|
-
{/* Commit Suggestions or Custom Input */}
|
|
316
|
-
{isCustomInputMode ? (
|
|
317
|
-
<CustomInputPrompt
|
|
318
|
-
value={customInput}
|
|
319
|
-
onChange={setCustomInput}
|
|
320
|
-
onSubmit={handleCustomInputSubmit}
|
|
321
|
-
onCancel={handleCustomInputCancel}
|
|
322
|
-
/>
|
|
323
|
-
) : (
|
|
324
|
-
<CommitSuggestions
|
|
325
|
-
suggestions={state.suggestions}
|
|
326
|
-
selectedIndex={state.selectedIndex}
|
|
327
|
-
onSelect={handleSelect}
|
|
328
|
-
onCommit={handleCommit}
|
|
329
|
-
onTryAgain={handleTryAgain}
|
|
330
|
-
onCustomInput={handleCustomInput}
|
|
331
|
-
isLoading={state.isLoading}
|
|
332
|
-
isUsingFallback={isUsingFallback}
|
|
333
|
-
/>
|
|
334
|
-
)}
|
|
335
|
-
|
|
336
|
-
{/* Footer - hide when custom input is open */}
|
|
337
|
-
{!isCustomInputMode && (
|
|
338
|
-
<Box
|
|
339
|
-
borderTop={true}
|
|
340
|
-
borderStyle="single"
|
|
341
|
-
borderColor="#374151"
|
|
342
|
-
paddingTop={1}
|
|
343
|
-
paddingLeft={1}
|
|
344
|
-
paddingRight={1}
|
|
345
|
-
>
|
|
346
|
-
<Text color="#6b7280">
|
|
347
|
-
<Text color="#60a5fa">↑↓</Text> navigate{" "}
|
|
348
|
-
<Text color="#10b981">Enter</Text> select{" "}
|
|
349
|
-
<Text color="#ef4444">Esc</Text> exit
|
|
350
|
-
</Text>
|
|
351
|
-
</Box>
|
|
352
|
-
)}
|
|
353
|
-
</Box>
|
|
354
|
-
);
|
|
355
|
-
};
|
|
356
|
-
|
|
357
|
-
export const App = BetterCommitApp;
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect } from "react";
|
|
2
|
-
import { Box, Text, useInput } from "ink";
|
|
3
|
-
import { CommitSuggestion } from "../types";
|
|
4
|
-
|
|
5
|
-
interface CommitSuggestionsProps {
|
|
6
|
-
suggestions: CommitSuggestion[];
|
|
7
|
-
selectedIndex: number;
|
|
8
|
-
onSelect: (index: number) => void;
|
|
9
|
-
onCommit: (index: number) => void;
|
|
10
|
-
onTryAgain: () => void;
|
|
11
|
-
onCustomInput: () => void;
|
|
12
|
-
isLoading: boolean;
|
|
13
|
-
isUsingFallback?: boolean;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export const CommitSuggestions: React.FC<CommitSuggestionsProps> = ({
|
|
17
|
-
suggestions,
|
|
18
|
-
selectedIndex,
|
|
19
|
-
onSelect,
|
|
20
|
-
onCommit,
|
|
21
|
-
onTryAgain,
|
|
22
|
-
onCustomInput,
|
|
23
|
-
isLoading,
|
|
24
|
-
isUsingFallback = false,
|
|
25
|
-
}) => {
|
|
26
|
-
const totalOptions = isUsingFallback
|
|
27
|
-
? suggestions.length
|
|
28
|
-
: suggestions.length + 2;
|
|
29
|
-
const [frame, setFrame] = useState(0);
|
|
30
|
-
|
|
31
|
-
const spinnerFrames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
32
|
-
// Alternative spinners:
|
|
33
|
-
// const spinnerFrames = ['◐', '◓', '◑', '◒'];
|
|
34
|
-
|
|
35
|
-
useEffect(() => {
|
|
36
|
-
if (isLoading) {
|
|
37
|
-
const timer = setInterval(() => {
|
|
38
|
-
setFrame((prev) => (prev + 1) % spinnerFrames.length);
|
|
39
|
-
}, 80);
|
|
40
|
-
return () => clearInterval(timer);
|
|
41
|
-
}
|
|
42
|
-
}, [isLoading]);
|
|
43
|
-
|
|
44
|
-
useInput((input, key) => {
|
|
45
|
-
if (key.upArrow) {
|
|
46
|
-
const newIndex = selectedIndex > 0 ? selectedIndex - 1 : totalOptions - 1;
|
|
47
|
-
onSelect(newIndex);
|
|
48
|
-
} else if (key.downArrow) {
|
|
49
|
-
const newIndex = selectedIndex < totalOptions - 1 ? selectedIndex + 1 : 0;
|
|
50
|
-
onSelect(newIndex);
|
|
51
|
-
} else if (key.return) {
|
|
52
|
-
if (selectedIndex < suggestions.length) {
|
|
53
|
-
onCommit(selectedIndex);
|
|
54
|
-
} else if (!isUsingFallback) {
|
|
55
|
-
if (selectedIndex === suggestions.length) {
|
|
56
|
-
onTryAgain();
|
|
57
|
-
} else if (selectedIndex === suggestions.length + 1) {
|
|
58
|
-
onCustomInput();
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
if (isLoading) {
|
|
65
|
-
return (
|
|
66
|
-
<Box borderStyle="round" borderColor="#374151" paddingX={1}>
|
|
67
|
-
<Text color="#6b7280">
|
|
68
|
-
{spinnerFrames[frame]} Generating commit suggestions...
|
|
69
|
-
</Text>
|
|
70
|
-
</Box>
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (suggestions.length === 0) {
|
|
75
|
-
return (
|
|
76
|
-
<Box borderStyle="round" borderColor="#ef4444" paddingX={1}>
|
|
77
|
-
<Text color="#ef4444">No suggestions available</Text>
|
|
78
|
-
</Box>
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return (
|
|
83
|
-
<Box
|
|
84
|
-
flexDirection="column"
|
|
85
|
-
borderStyle="round"
|
|
86
|
-
borderColor="#374151"
|
|
87
|
-
paddingX={1}
|
|
88
|
-
>
|
|
89
|
-
<Box>
|
|
90
|
-
<Text bold color="#8b5cf6">
|
|
91
|
-
Commit Messages
|
|
92
|
-
</Text>
|
|
93
|
-
</Box>
|
|
94
|
-
|
|
95
|
-
{suggestions.map((suggestion, index) => {
|
|
96
|
-
const isSelected = index === selectedIndex;
|
|
97
|
-
const displayMsg =
|
|
98
|
-
suggestion.message.length > 65
|
|
99
|
-
? suggestion.message.substring(0, 62) + "..."
|
|
100
|
-
: suggestion.message;
|
|
101
|
-
|
|
102
|
-
return (
|
|
103
|
-
<Box key={index} marginLeft={1}>
|
|
104
|
-
<Text color={isSelected ? "#10b981" : "#6b7280"}>
|
|
105
|
-
{isSelected ? "❯" : " "}
|
|
106
|
-
</Text>
|
|
107
|
-
<Text color={isSelected ? "#ffffff" : "#9ca3af"} bold={isSelected}>
|
|
108
|
-
{" "}
|
|
109
|
-
{displayMsg}
|
|
110
|
-
</Text>
|
|
111
|
-
</Box>
|
|
112
|
-
);
|
|
113
|
-
})}
|
|
114
|
-
|
|
115
|
-
{!isUsingFallback && (
|
|
116
|
-
<Box marginTop={1} marginLeft={1} flexDirection="row">
|
|
117
|
-
<Box marginRight={3}>
|
|
118
|
-
<Text
|
|
119
|
-
color={
|
|
120
|
-
selectedIndex === suggestions.length ? "#10b981" : "#6b7280"
|
|
121
|
-
}
|
|
122
|
-
>
|
|
123
|
-
{selectedIndex === suggestions.length ? "❯" : " "}
|
|
124
|
-
</Text>
|
|
125
|
-
<Text
|
|
126
|
-
color={
|
|
127
|
-
selectedIndex === suggestions.length ? "#60a5fa" : "#6b7280"
|
|
128
|
-
}
|
|
129
|
-
bold={selectedIndex === suggestions.length}
|
|
130
|
-
>
|
|
131
|
-
{" "}
|
|
132
|
-
↻ Try again
|
|
133
|
-
</Text>
|
|
134
|
-
</Box>
|
|
135
|
-
<Box>
|
|
136
|
-
<Text
|
|
137
|
-
color={
|
|
138
|
-
selectedIndex === suggestions.length + 1 ? "#10b981" : "#6b7280"
|
|
139
|
-
}
|
|
140
|
-
>
|
|
141
|
-
{selectedIndex === suggestions.length + 1 ? "❯" : " "}
|
|
142
|
-
</Text>
|
|
143
|
-
<Text
|
|
144
|
-
color={
|
|
145
|
-
selectedIndex === suggestions.length + 1 ? "#f59e0b" : "#6b7280"
|
|
146
|
-
}
|
|
147
|
-
bold={selectedIndex === suggestions.length + 1}
|
|
148
|
-
>
|
|
149
|
-
{" "}
|
|
150
|
-
✎ Custom input
|
|
151
|
-
</Text>
|
|
152
|
-
</Box>
|
|
153
|
-
</Box>
|
|
154
|
-
)}
|
|
155
|
-
</Box>
|
|
156
|
-
);
|
|
157
|
-
};
|