codeep 1.2.10 → 1.2.12
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/dist/api/index.js +7 -7
- package/dist/config/index.js +3 -3
- package/dist/config/providers.d.ts +6 -0
- package/dist/config/providers.js +41 -2
- package/dist/config/providers.test.js +31 -2
- package/dist/hooks/index.js +1 -1
- package/dist/hooks/useAgent.js +3 -3
- package/dist/renderer/App.js +8 -8
- package/dist/renderer/ChatUI.js +3 -3
- package/dist/renderer/Screen.js +1 -1
- package/dist/renderer/components/Export.js +1 -1
- package/dist/renderer/components/Help.js +1 -1
- package/dist/renderer/components/Intro.js +1 -1
- package/dist/renderer/components/Login.js +3 -3
- package/dist/renderer/components/Logout.js +1 -1
- package/dist/renderer/components/Modal.js +2 -2
- package/dist/renderer/components/Permission.js +2 -2
- package/dist/renderer/components/Search.js +1 -1
- package/dist/renderer/components/SelectScreen.js +1 -1
- package/dist/renderer/components/Settings.js +3 -3
- package/dist/renderer/components/Status.js +1 -1
- package/dist/renderer/demo-app.js +1 -1
- package/dist/renderer/demo.js +1 -1
- package/dist/renderer/index.js +9 -9
- package/dist/renderer/main.js +31 -31
- package/dist/utils/agent.js +10 -10
- package/dist/utils/agent.test.js +1 -1
- package/dist/utils/codeReview.js +1 -1
- package/dist/utils/context.js +1 -1
- package/dist/utils/git.test.js +1 -1
- package/dist/utils/gitignore.test.js +1 -1
- package/dist/utils/keychain.js +1 -1
- package/dist/utils/project.test.js +1 -1
- package/dist/utils/ratelimit.js +1 -1
- package/dist/utils/ratelimit.test.js +1 -1
- package/dist/utils/retry.test.js +1 -1
- package/dist/utils/smartContext.js +1 -1
- package/dist/utils/smartContext.test.js +1 -1
- package/dist/utils/taskPlanner.js +2 -2
- package/dist/utils/tools.d.ts +64 -4
- package/dist/utils/tools.js +212 -7
- package/dist/utils/tools.test.js +1 -1
- package/dist/utils/validation.test.js +1 -1
- package/dist/utils/verify.js +1 -1
- package/package.json +1 -1
- package/bin/codeep.js +0 -2
- package/dist/app.d.ts +0 -2
- package/dist/app.js +0 -1501
- package/dist/components/AgentActions.d.ts +0 -18
- package/dist/components/AgentActions.js +0 -122
- package/dist/components/AgentProgress.d.ts +0 -59
- package/dist/components/AgentProgress.js +0 -368
- package/dist/components/Export.d.ts +0 -8
- package/dist/components/Export.js +0 -27
- package/dist/components/Help.d.ts +0 -6
- package/dist/components/Help.js +0 -7
- package/dist/components/Input.d.ts +0 -9
- package/dist/components/Input.js +0 -334
- package/dist/components/Loading.d.ts +0 -17
- package/dist/components/Loading.js +0 -52
- package/dist/components/Login.d.ts +0 -7
- package/dist/components/Login.js +0 -77
- package/dist/components/Logo.d.ts +0 -8
- package/dist/components/Logo.js +0 -89
- package/dist/components/LogoutPicker.d.ts +0 -8
- package/dist/components/LogoutPicker.js +0 -61
- package/dist/components/Message.d.ts +0 -10
- package/dist/components/Message.js +0 -242
- package/dist/components/MessageList.d.ts +0 -10
- package/dist/components/MessageList.js +0 -42
- package/dist/components/ProjectPermission.d.ts +0 -7
- package/dist/components/ProjectPermission.js +0 -65
- package/dist/components/Search.d.ts +0 -10
- package/dist/components/Search.js +0 -30
- package/dist/components/SessionPicker.d.ts +0 -9
- package/dist/components/SessionPicker.js +0 -88
- package/dist/components/Sessions.d.ts +0 -12
- package/dist/components/Sessions.js +0 -119
- package/dist/components/Settings.d.ts +0 -9
- package/dist/components/Settings.js +0 -198
- package/dist/components/Spinner.d.ts +0 -34
- package/dist/components/Spinner.js +0 -38
- package/dist/components/Status.d.ts +0 -2
- package/dist/components/Status.js +0 -13
- package/dist/components/StreamingMessage.d.ts +0 -14
- package/dist/components/StreamingMessage.js +0 -19
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -42
package/dist/components/Input.js
DELETED
|
@@ -1,334 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useMemo, useEffect, useRef } from 'react';
|
|
3
|
-
import { Text, Box, useInput } from 'ink';
|
|
4
|
-
import clipboardy from 'clipboardy';
|
|
5
|
-
const COMMANDS = [
|
|
6
|
-
{ cmd: '/help', desc: 'Show help' },
|
|
7
|
-
{ cmd: '/status', desc: 'Show status' },
|
|
8
|
-
{ cmd: '/version', desc: 'Show version' },
|
|
9
|
-
{ cmd: '/update', desc: 'Check updates' },
|
|
10
|
-
{ cmd: '/model', desc: 'Switch model' },
|
|
11
|
-
{ cmd: '/protocol', desc: 'Switch protocol' },
|
|
12
|
-
{ cmd: '/provider', desc: 'Switch provider' },
|
|
13
|
-
{ cmd: '/lang', desc: 'Set language' },
|
|
14
|
-
{ cmd: '/settings', desc: 'Adjust settings' },
|
|
15
|
-
{ cmd: '/sessions', desc: 'Manage sessions' },
|
|
16
|
-
{ cmd: '/sessions delete', desc: 'Delete session' },
|
|
17
|
-
{ cmd: '/rename', desc: 'Rename session' },
|
|
18
|
-
{ cmd: '/search', desc: 'Search history' },
|
|
19
|
-
{ cmd: '/export', desc: 'Export chat' },
|
|
20
|
-
{ cmd: '/diff', desc: 'Review git changes' },
|
|
21
|
-
{ cmd: '/diff --staged', desc: 'Review staged changes' },
|
|
22
|
-
{ cmd: '/commit', desc: 'Generate commit message' },
|
|
23
|
-
{ cmd: '/apply', desc: 'Apply file changes' },
|
|
24
|
-
{ cmd: '/copy', desc: 'Copy code block' },
|
|
25
|
-
{ cmd: '/paste', desc: 'Paste from clipboard' },
|
|
26
|
-
{ cmd: '/grant', desc: 'Grant write permission for agent' },
|
|
27
|
-
{ cmd: '/agent', desc: 'Run agent for a task' },
|
|
28
|
-
{ cmd: '/agent-dry', desc: 'Preview agent actions' },
|
|
29
|
-
{ cmd: '/agent-stop', desc: 'Stop running agent' },
|
|
30
|
-
{ cmd: '/scan', desc: 'Scan project for AI context' },
|
|
31
|
-
{ cmd: '/scan status', desc: 'Show scan status' },
|
|
32
|
-
{ cmd: '/scan clear', desc: 'Clear cached scan' },
|
|
33
|
-
{ cmd: '/clear', desc: 'Clear chat' },
|
|
34
|
-
{ cmd: '/login', desc: 'Change API key' },
|
|
35
|
-
{ cmd: '/logout', desc: 'Logout' },
|
|
36
|
-
{ cmd: '/exit', desc: 'Quit' },
|
|
37
|
-
];
|
|
38
|
-
export const ChatInput = ({ onSubmit, disabled, history = [], clearTrigger = 0 }) => {
|
|
39
|
-
const [value, setValue] = useState('');
|
|
40
|
-
const [cursorPos, setCursorPos] = useState(0);
|
|
41
|
-
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
42
|
-
const [pasteInfo, setPasteInfo] = useState(null);
|
|
43
|
-
const [historyIndex, setHistoryIndex] = useState(-1);
|
|
44
|
-
// Paste detection using timing - chars arriving fast = paste
|
|
45
|
-
const inputBuffer = useRef('');
|
|
46
|
-
const lastInputTime = useRef(0);
|
|
47
|
-
const pasteTimeout = useRef(null);
|
|
48
|
-
const charTimings = useRef([]);
|
|
49
|
-
// Clear input when clearTrigger changes
|
|
50
|
-
useEffect(() => {
|
|
51
|
-
if (clearTrigger > 0) {
|
|
52
|
-
setValue('');
|
|
53
|
-
setCursorPos(0);
|
|
54
|
-
setSelectedIndex(0);
|
|
55
|
-
setPasteInfo(null);
|
|
56
|
-
setHistoryIndex(-1);
|
|
57
|
-
}
|
|
58
|
-
}, [clearTrigger]);
|
|
59
|
-
// Filter commands based on input
|
|
60
|
-
const suggestions = useMemo(() => {
|
|
61
|
-
// No suggestions for empty input or just '/'
|
|
62
|
-
if (!value || value.length === 0 || value === '/')
|
|
63
|
-
return [];
|
|
64
|
-
if (!value.startsWith('/') || value.includes(' '))
|
|
65
|
-
return [];
|
|
66
|
-
return COMMANDS.filter(c => c.cmd.startsWith(value.toLowerCase()));
|
|
67
|
-
}, [value]);
|
|
68
|
-
// Reset selection when suggestions change
|
|
69
|
-
useEffect(() => {
|
|
70
|
-
if (suggestions.length > 0) {
|
|
71
|
-
setSelectedIndex(0);
|
|
72
|
-
}
|
|
73
|
-
}, [suggestions.length]);
|
|
74
|
-
// Process buffered input - called after paste timeout
|
|
75
|
-
const processBuffer = () => {
|
|
76
|
-
const buffer = inputBuffer.current;
|
|
77
|
-
inputBuffer.current = '';
|
|
78
|
-
if (!buffer)
|
|
79
|
-
return;
|
|
80
|
-
// If buffer has multiple chars (> 20), treat as paste and show indicator
|
|
81
|
-
if (buffer.length > 20) {
|
|
82
|
-
handlePastedText(buffer, true);
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
// Short buffer - just add to value normally
|
|
86
|
-
setValue(prev => prev + buffer);
|
|
87
|
-
setCursorPos(prev => prev + buffer.length);
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
const handlePastedText = (text, fromCtrlV = false) => {
|
|
91
|
-
const trimmed = text.trim();
|
|
92
|
-
if (!trimmed)
|
|
93
|
-
return;
|
|
94
|
-
const lines = trimmed.split(/\r?\n/);
|
|
95
|
-
const lineCount = lines.length;
|
|
96
|
-
const charCount = trimmed.length;
|
|
97
|
-
// For multi-line or long pastes - show info box with preview
|
|
98
|
-
if (lineCount > 1 || charCount > 80 || (fromCtrlV && charCount > 30)) {
|
|
99
|
-
// Create preview - first line truncated
|
|
100
|
-
const firstLine = lines[0].substring(0, 50);
|
|
101
|
-
const preview = firstLine + (lines[0].length > 50 || lineCount > 1 ? '...' : '');
|
|
102
|
-
setPasteInfo({
|
|
103
|
-
lines: lineCount,
|
|
104
|
-
chars: charCount,
|
|
105
|
-
preview,
|
|
106
|
-
fullText: trimmed,
|
|
107
|
-
});
|
|
108
|
-
// Show truncated text in input (not ugly indicator)
|
|
109
|
-
const displayText = trimmed.replace(/\r?\n/g, ' ').substring(0, 60);
|
|
110
|
-
const inputText = displayText + (trimmed.length > 60 ? '...' : '');
|
|
111
|
-
setValue(inputText);
|
|
112
|
-
setCursorPos(inputText.length);
|
|
113
|
-
}
|
|
114
|
-
else {
|
|
115
|
-
// Short paste - insert directly
|
|
116
|
-
setValue(prev => prev + trimmed);
|
|
117
|
-
setCursorPos(prev => prev + trimmed.length);
|
|
118
|
-
setPasteInfo(null);
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
|
-
// Main input handler
|
|
122
|
-
useInput((input, key) => {
|
|
123
|
-
if (disabled)
|
|
124
|
-
return;
|
|
125
|
-
// Handle Enter - submit
|
|
126
|
-
if (key.return) {
|
|
127
|
-
const trimmedValue = value.trim();
|
|
128
|
-
// Handle /paste command - read from clipboard
|
|
129
|
-
if (trimmedValue === '/paste') {
|
|
130
|
-
try {
|
|
131
|
-
const clipboardText = clipboardy.readSync();
|
|
132
|
-
if (clipboardText && clipboardText.trim()) {
|
|
133
|
-
handlePastedText(clipboardText.trim(), true);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
catch {
|
|
137
|
-
// Clipboard read failed
|
|
138
|
-
}
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
if (trimmedValue) {
|
|
142
|
-
// If we have paste info, submit the full pasted text
|
|
143
|
-
const submitValue = pasteInfo ? pasteInfo.fullText : trimmedValue;
|
|
144
|
-
onSubmit(submitValue);
|
|
145
|
-
setValue('');
|
|
146
|
-
setCursorPos(0);
|
|
147
|
-
setPasteInfo(null);
|
|
148
|
-
setHistoryIndex(-1);
|
|
149
|
-
}
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
// Handle Escape - clear paste info or input
|
|
153
|
-
if (key.escape) {
|
|
154
|
-
if (pasteInfo) {
|
|
155
|
-
// Clear pasted content
|
|
156
|
-
setValue('');
|
|
157
|
-
setCursorPos(0);
|
|
158
|
-
setPasteInfo(null);
|
|
159
|
-
}
|
|
160
|
-
else if (value) {
|
|
161
|
-
setValue('');
|
|
162
|
-
setCursorPos(0);
|
|
163
|
-
}
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
// Handle Backspace
|
|
167
|
-
if (key.backspace || key.delete) {
|
|
168
|
-
if (cursorPos > 0) {
|
|
169
|
-
// If paste info exists, clear everything on backspace
|
|
170
|
-
if (pasteInfo) {
|
|
171
|
-
setValue('');
|
|
172
|
-
setCursorPos(0);
|
|
173
|
-
setPasteInfo(null);
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
setValue(prev => prev.slice(0, cursorPos - 1) + prev.slice(cursorPos));
|
|
177
|
-
setCursorPos(prev => prev - 1);
|
|
178
|
-
}
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
// Handle Tab - autocomplete command
|
|
182
|
-
if (key.tab && suggestions.length > 0) {
|
|
183
|
-
setValue(suggestions[selectedIndex].cmd + ' ');
|
|
184
|
-
setCursorPos(suggestions[selectedIndex].cmd.length + 1);
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
// Handle Up Arrow - navigate suggestions or history
|
|
188
|
-
if (key.upArrow) {
|
|
189
|
-
if (suggestions.length > 0) {
|
|
190
|
-
setSelectedIndex(i => Math.max(0, i - 1));
|
|
191
|
-
}
|
|
192
|
-
else if (history.length > 0) {
|
|
193
|
-
const newIndex = historyIndex < history.length - 1 ? historyIndex + 1 : historyIndex;
|
|
194
|
-
setHistoryIndex(newIndex);
|
|
195
|
-
if (newIndex >= 0 && history[history.length - 1 - newIndex]) {
|
|
196
|
-
const historyValue = history[history.length - 1 - newIndex];
|
|
197
|
-
setValue(historyValue);
|
|
198
|
-
setCursorPos(historyValue.length);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
// Handle Down Arrow - navigate suggestions or history
|
|
204
|
-
if (key.downArrow) {
|
|
205
|
-
if (suggestions.length > 0) {
|
|
206
|
-
setSelectedIndex(i => Math.min(suggestions.length - 1, i + 1));
|
|
207
|
-
}
|
|
208
|
-
else if (historyIndex > 0) {
|
|
209
|
-
const newIndex = historyIndex - 1;
|
|
210
|
-
setHistoryIndex(newIndex);
|
|
211
|
-
const historyValue = history[history.length - 1 - newIndex];
|
|
212
|
-
setValue(historyValue);
|
|
213
|
-
setCursorPos(historyValue.length);
|
|
214
|
-
}
|
|
215
|
-
else if (historyIndex === 0) {
|
|
216
|
-
setHistoryIndex(-1);
|
|
217
|
-
setValue('');
|
|
218
|
-
setCursorPos(0);
|
|
219
|
-
}
|
|
220
|
-
return;
|
|
221
|
-
}
|
|
222
|
-
// Handle Left Arrow
|
|
223
|
-
if (key.leftArrow) {
|
|
224
|
-
setCursorPos(prev => Math.max(0, prev - 1));
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
// Handle Right Arrow
|
|
228
|
-
if (key.rightArrow) {
|
|
229
|
-
setCursorPos(prev => Math.min(value.length, prev + 1));
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
// Handle Ctrl+A - go to beginning
|
|
233
|
-
if (key.ctrl && input === 'a') {
|
|
234
|
-
setCursorPos(0);
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
// Handle Ctrl+E - go to end
|
|
238
|
-
if (key.ctrl && input === 'e') {
|
|
239
|
-
setCursorPos(value.length);
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
// Handle Ctrl+U - clear line
|
|
243
|
-
if (key.ctrl && input === 'u') {
|
|
244
|
-
setValue('');
|
|
245
|
-
setCursorPos(0);
|
|
246
|
-
setPasteInfo(null);
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
// Handle Ctrl+W - delete word
|
|
250
|
-
if (key.ctrl && input === 'w') {
|
|
251
|
-
const beforeCursor = value.slice(0, cursorPos);
|
|
252
|
-
const afterCursor = value.slice(cursorPos);
|
|
253
|
-
const lastSpace = beforeCursor.trimEnd().lastIndexOf(' ');
|
|
254
|
-
const newBefore = lastSpace >= 0 ? beforeCursor.slice(0, lastSpace + 1) : '';
|
|
255
|
-
setValue(newBefore + afterCursor);
|
|
256
|
-
setCursorPos(newBefore.length);
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
// Handle Ctrl+V - paste from clipboard
|
|
260
|
-
// Terminal sends ASCII 22 (\x16) for Ctrl+V
|
|
261
|
-
if (input === '\x16' || (key.ctrl && input === 'v')) {
|
|
262
|
-
try {
|
|
263
|
-
const clipboardText = clipboardy.readSync();
|
|
264
|
-
if (clipboardText) {
|
|
265
|
-
handlePastedText(clipboardText, true);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
catch {
|
|
269
|
-
// Clipboard read failed, ignore
|
|
270
|
-
}
|
|
271
|
-
return;
|
|
272
|
-
}
|
|
273
|
-
// Regular character input
|
|
274
|
-
if (input && !key.ctrl && !key.meta) {
|
|
275
|
-
// If we have paste info and user types new char, clear paste
|
|
276
|
-
if (pasteInfo) {
|
|
277
|
-
setPasteInfo(null);
|
|
278
|
-
setValue('');
|
|
279
|
-
setCursorPos(0);
|
|
280
|
-
}
|
|
281
|
-
const now = Date.now();
|
|
282
|
-
const timeSinceLastInput = now - lastInputTime.current;
|
|
283
|
-
lastInputTime.current = now;
|
|
284
|
-
// Track timing for paste detection
|
|
285
|
-
// Paste typically sends many chars with < 10ms between them
|
|
286
|
-
const isPasteLikeTiming = timeSinceLastInput < 15;
|
|
287
|
-
if (isPasteLikeTiming || inputBuffer.current.length > 0) {
|
|
288
|
-
// Add to buffer
|
|
289
|
-
inputBuffer.current += input;
|
|
290
|
-
charTimings.current.push(timeSinceLastInput);
|
|
291
|
-
// Clear existing timeout
|
|
292
|
-
if (pasteTimeout.current) {
|
|
293
|
-
clearTimeout(pasteTimeout.current);
|
|
294
|
-
}
|
|
295
|
-
// Set timeout to process buffer - wait a bit longer to collect all paste chars
|
|
296
|
-
pasteTimeout.current = setTimeout(() => {
|
|
297
|
-
const buffer = inputBuffer.current;
|
|
298
|
-
const timings = charTimings.current;
|
|
299
|
-
inputBuffer.current = '';
|
|
300
|
-
charTimings.current = [];
|
|
301
|
-
if (!buffer)
|
|
302
|
-
return;
|
|
303
|
-
// Calculate average timing - if most chars came fast, it's a paste
|
|
304
|
-
const fastChars = timings.filter(t => t < 15).length;
|
|
305
|
-
const isPaste = buffer.length > 5 && (fastChars / timings.length) > 0.5;
|
|
306
|
-
if (isPaste && (buffer.length > 30 || buffer.includes('\n'))) {
|
|
307
|
-
// Treat as paste
|
|
308
|
-
handlePastedText(buffer, true);
|
|
309
|
-
}
|
|
310
|
-
else {
|
|
311
|
-
// Just fast typing, add normally
|
|
312
|
-
setValue(prev => prev + buffer);
|
|
313
|
-
setCursorPos(prev => prev + buffer.length);
|
|
314
|
-
}
|
|
315
|
-
}, 50); // Wait 50ms to collect all paste chars
|
|
316
|
-
return; // Don't add to value yet
|
|
317
|
-
}
|
|
318
|
-
// Normal single character input (slow typing)
|
|
319
|
-
setValue(prev => prev.slice(0, cursorPos) + input + prev.slice(cursorPos));
|
|
320
|
-
setCursorPos(prev => prev + input.length);
|
|
321
|
-
}
|
|
322
|
-
}, { isActive: !disabled });
|
|
323
|
-
// Render input with cursor
|
|
324
|
-
const renderInput = () => {
|
|
325
|
-
if (!value) {
|
|
326
|
-
return _jsx(Text, { color: "cyan", children: "Type a message or /command..." });
|
|
327
|
-
}
|
|
328
|
-
const before = value.slice(0, cursorPos);
|
|
329
|
-
const cursor = value[cursorPos] || ' ';
|
|
330
|
-
const after = value.slice(cursorPos + 1);
|
|
331
|
-
return (_jsxs(Text, { children: [before, _jsx(Text, { backgroundColor: "white", color: "black", children: cursor }), after] }));
|
|
332
|
-
};
|
|
333
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: "#f02a30", bold: true, children: '> ' }), disabled ? (_jsx(Text, { color: "yellow", children: "Agent working... (Esc to stop)" })) : (renderInput())] }), value === '/' && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "cyan", dimColor: true, children: "Type command name (e.g., help, status, settings) or press Tab to see all" }) })), suggestions.length > 0 && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [suggestions.map((s, i) => (_jsxs(Text, { children: [i === selectedIndex ? _jsx(Text, { color: "#f02a30", children: "\u25B8 " }) : ' ', _jsx(Text, { color: i === selectedIndex ? '#f02a30' : undefined, bold: i === selectedIndex, children: s.cmd }), _jsxs(Text, { color: i === selectedIndex ? undefined : 'gray', children: [" - ", s.desc] })] }, s.cmd))), _jsx(Text, { color: "cyan", dimColor: true, children: "\u2191\u2193 navigate \u2022 Tab complete \u2022 Esc cancel" })] })), pasteInfo && (_jsx(Box, { borderStyle: "round", borderColor: "green", paddingX: 1, marginTop: 1, flexDirection: "column", children: _jsxs(Text, { children: [_jsx(Text, { color: "green", bold: true, children: "\uD83D\uDCCB " }), _jsx(Text, { color: "white", bold: true, children: pasteInfo.chars }), _jsx(Text, { color: "cyan", children: " chars" }), pasteInfo.lines > 1 && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "cyan", children: " \u2022 " }), _jsx(Text, { color: "white", bold: true, children: pasteInfo.lines }), _jsx(Text, { color: "cyan", children: " lines" })] })), _jsx(Text, { color: "cyan", dimColor: true, children: " (Enter send \u2022 Esc cancel)" })] }) }))] }));
|
|
334
|
-
};
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
interface LoadingProps {
|
|
3
|
-
isStreaming?: boolean;
|
|
4
|
-
}
|
|
5
|
-
/**
|
|
6
|
-
* Loading indicator with isolated animation
|
|
7
|
-
* Parent component won't re-render when spinner animates
|
|
8
|
-
*/
|
|
9
|
-
export declare const Loading: React.FC<LoadingProps>;
|
|
10
|
-
/**
|
|
11
|
-
* Simple inline spinner for smaller spaces
|
|
12
|
-
* Also uses isolated animation
|
|
13
|
-
*/
|
|
14
|
-
export declare const InlineSpinner: React.FC<{
|
|
15
|
-
text?: string;
|
|
16
|
-
}>;
|
|
17
|
-
export {};
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useEffect, memo } from 'react';
|
|
3
|
-
import { Text, Box } from 'ink';
|
|
4
|
-
// Spinner frames - Braille pattern for smooth animation
|
|
5
|
-
const SPINNER = ['⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷'];
|
|
6
|
-
/**
|
|
7
|
-
* Isolated spinner animation component
|
|
8
|
-
* Only this component re-renders during animation, not the parent
|
|
9
|
-
*/
|
|
10
|
-
const AnimatedSpinner = memo(() => {
|
|
11
|
-
const [frame, setFrame] = useState(0);
|
|
12
|
-
useEffect(() => {
|
|
13
|
-
const timer = setInterval(() => {
|
|
14
|
-
setFrame(f => (f + 1) % SPINNER.length);
|
|
15
|
-
}, 100);
|
|
16
|
-
return () => clearInterval(timer);
|
|
17
|
-
}, []);
|
|
18
|
-
return _jsx(Text, { color: "#f02a30", children: SPINNER[frame] });
|
|
19
|
-
});
|
|
20
|
-
AnimatedSpinner.displayName = 'AnimatedSpinner';
|
|
21
|
-
/**
|
|
22
|
-
* Animated dots component
|
|
23
|
-
* Isolated animation state
|
|
24
|
-
*/
|
|
25
|
-
const AnimatedDots = memo(() => {
|
|
26
|
-
const [count, setCount] = useState(0);
|
|
27
|
-
useEffect(() => {
|
|
28
|
-
const timer = setInterval(() => {
|
|
29
|
-
setCount(c => (c + 1) % 4);
|
|
30
|
-
}, 300);
|
|
31
|
-
return () => clearInterval(timer);
|
|
32
|
-
}, []);
|
|
33
|
-
return _jsx(Text, { color: "#f02a30", children: '.'.repeat(count) });
|
|
34
|
-
});
|
|
35
|
-
AnimatedDots.displayName = 'AnimatedDots';
|
|
36
|
-
/**
|
|
37
|
-
* Loading indicator with isolated animation
|
|
38
|
-
* Parent component won't re-render when spinner animates
|
|
39
|
-
*/
|
|
40
|
-
export const Loading = memo(({ isStreaming = false }) => {
|
|
41
|
-
const message = isStreaming ? 'Writing' : 'Thinking';
|
|
42
|
-
return (_jsxs(Box, { paddingLeft: 2, paddingY: 0, children: [_jsx(AnimatedSpinner, {}), _jsxs(Text, { color: "#f02a30", bold: true, children: [" ", message] }), _jsx(AnimatedDots, {})] }));
|
|
43
|
-
});
|
|
44
|
-
Loading.displayName = 'Loading';
|
|
45
|
-
/**
|
|
46
|
-
* Simple inline spinner for smaller spaces
|
|
47
|
-
* Also uses isolated animation
|
|
48
|
-
*/
|
|
49
|
-
export const InlineSpinner = memo(({ text = 'Loading' }) => {
|
|
50
|
-
return (_jsxs(Text, { children: [_jsx(AnimatedSpinner, {}), _jsxs(Text, { children: [" ", text] })] }));
|
|
51
|
-
});
|
|
52
|
-
InlineSpinner.displayName = 'InlineSpinner';
|
package/dist/components/Login.js
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from 'react';
|
|
3
|
-
import { Text, Box, useInput } from 'ink';
|
|
4
|
-
import TextInput from 'ink-text-input';
|
|
5
|
-
import open from 'open';
|
|
6
|
-
import { setApiKey, setProvider } from '../config/index.js';
|
|
7
|
-
import { validateApiKey } from '../api/index.js';
|
|
8
|
-
import { Logo } from './Logo.js';
|
|
9
|
-
import { getProviderList } from '../config/providers.js';
|
|
10
|
-
const PROVIDER_URLS = {
|
|
11
|
-
'z.ai': 'https://z.ai/subscribe?ic=NXYNXZOV14',
|
|
12
|
-
'minimax': 'https://platform.minimax.io/subscribe/coding-plan?code=2lWvoWUhrp&source=link',
|
|
13
|
-
};
|
|
14
|
-
export const Login = ({ onLogin, onCancel }) => {
|
|
15
|
-
const [step, setStep] = useState('provider');
|
|
16
|
-
const [selectedProvider, setSelectedProvider] = useState(0);
|
|
17
|
-
const [apiKey, setApiKeyState] = useState('');
|
|
18
|
-
const [error, setError] = useState('');
|
|
19
|
-
const [validating, setValidating] = useState(false);
|
|
20
|
-
const providers = getProviderList();
|
|
21
|
-
const currentProvider = providers[selectedProvider];
|
|
22
|
-
// Handle keyboard input
|
|
23
|
-
useInput((input, key) => {
|
|
24
|
-
if (step === 'provider') {
|
|
25
|
-
if (key.upArrow) {
|
|
26
|
-
setSelectedProvider(i => Math.max(0, i - 1));
|
|
27
|
-
}
|
|
28
|
-
if (key.downArrow) {
|
|
29
|
-
setSelectedProvider(i => Math.min(providers.length - 1, i + 1));
|
|
30
|
-
}
|
|
31
|
-
if (key.return) {
|
|
32
|
-
setProvider(currentProvider.id);
|
|
33
|
-
setStep('apikey');
|
|
34
|
-
}
|
|
35
|
-
// Escape to cancel login
|
|
36
|
-
if (key.escape && onCancel) {
|
|
37
|
-
onCancel();
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
else if (step === 'apikey') {
|
|
41
|
-
// G key to open browser for API key
|
|
42
|
-
if ((input === 'g' || input === 'G') && apiKey === '') {
|
|
43
|
-
const url = PROVIDER_URLS[currentProvider.id];
|
|
44
|
-
if (url)
|
|
45
|
-
open(url);
|
|
46
|
-
}
|
|
47
|
-
// Escape to go back to provider selection
|
|
48
|
-
if (key.escape) {
|
|
49
|
-
setStep('provider');
|
|
50
|
-
setError('');
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
const handleSubmit = async (key) => {
|
|
55
|
-
if (!key.trim()) {
|
|
56
|
-
setError('Please enter your API key');
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
setValidating(true);
|
|
60
|
-
setError('');
|
|
61
|
-
const result = await validateApiKey(key, currentProvider.id);
|
|
62
|
-
if (result.valid) {
|
|
63
|
-
setApiKey(key, currentProvider.id);
|
|
64
|
-
onLogin();
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
setError(result.error || 'Invalid API key');
|
|
68
|
-
setValidating(false);
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
// Provider selection screen
|
|
72
|
-
if (step === 'provider') {
|
|
73
|
-
return (_jsxs(Box, { flexDirection: "column", alignItems: "center", padding: 2, children: [_jsx(Logo, {}), _jsx(Text, { children: " " }), _jsx(Text, { color: "#f02a30", bold: true, children: "Select AI Provider" }), _jsx(Text, { children: " " }), providers.map((provider, i) => (_jsxs(Text, { children: [i === selectedProvider ? _jsx(Text, { color: "#f02a30", children: "\u25B8 " }) : ' ', _jsx(Text, { color: i === selectedProvider ? '#f02a30' : undefined, bold: i === selectedProvider, children: provider.name }), _jsxs(Text, { children: [" - ", provider.description] })] }, provider.id))), _jsx(Text, { children: " " }), _jsx(Text, { children: "\u2191/\u2193 Navigate, Enter = Select, Esc = Cancel" })] }));
|
|
74
|
-
}
|
|
75
|
-
// API key input screen
|
|
76
|
-
return (_jsxs(Box, { flexDirection: "column", alignItems: "center", padding: 2, children: [_jsx(Logo, {}), _jsx(Text, { children: " " }), _jsxs(Text, { color: "#f02a30", children: ["Enter your ", _jsx(Text, { bold: true, children: currentProvider.name }), " API key"] }), _jsx(Text, { children: " " }), _jsxs(Text, { children: ["Press ", _jsx(Text, { color: "#f02a30", children: "G" }), " to get an API key, or paste your key below:"] }), _jsx(Text, { children: " " }), _jsxs(Box, { children: [_jsx(Text, { color: "#f02a30", children: "Key: " }), validating ? (_jsx(Text, { children: "Validating..." })) : (_jsx(TextInput, { value: apiKey, onChange: setApiKeyState, onSubmit: handleSubmit, mask: "*", placeholder: "Enter API key..." }))] }), error && (_jsx(Text, { color: "#f02a30", children: error })), _jsx(Text, { children: " " }), _jsxs(Text, { children: ["Enter = login, ", _jsx(Text, { color: "#f02a30", children: "G" }), " = get key, Esc = back, Ctrl+C = exit"] })] }));
|
|
77
|
-
};
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
export declare const LOGO = "\n \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557\n\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\n\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\n\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2550\u255D\n\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551\n \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D ";
|
|
3
|
-
export declare const Logo: React.FC;
|
|
4
|
-
interface IntroProps {
|
|
5
|
-
onComplete: () => void;
|
|
6
|
-
}
|
|
7
|
-
export declare const IntroAnimation: React.FC<IntroProps>;
|
|
8
|
-
export {};
|
package/dist/components/Logo.js
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useEffect } from 'react';
|
|
3
|
-
import { Text, Box } from 'ink';
|
|
4
|
-
export const LOGO = `
|
|
5
|
-
██████╗ ██████╗ ██████╗ ███████╗███████╗██████╗
|
|
6
|
-
██╔════╝██╔═══██╗██╔══██╗██╔════╝██╔════╝██╔══██╗
|
|
7
|
-
██║ ██║ ██║██║ ██║█████╗ █████╗ ██████╔╝
|
|
8
|
-
██║ ██║ ██║██║ ██║██╔══╝ ██╔══╝ ██╔═══╝
|
|
9
|
-
╚██████╗╚██████╔╝██████╔╝███████╗███████╗██║
|
|
10
|
-
╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝╚══════╝╚═╝ `;
|
|
11
|
-
// Static logo for after intro
|
|
12
|
-
export const Logo = () => (_jsxs(Box, { flexDirection: "column", alignItems: "center", children: [_jsx(Text, { color: "#f02a30", bold: true, children: LOGO }), _jsx(Text, { color: "#f02a30", children: "Deep into Code." })] }));
|
|
13
|
-
const GLITCH_CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ@#$%&*<>?/;:[]=';
|
|
14
|
-
export const IntroAnimation = ({ onComplete }) => {
|
|
15
|
-
const [phase, setPhase] = useState('init');
|
|
16
|
-
const [progress, setProgress] = useState(0);
|
|
17
|
-
useEffect(() => {
|
|
18
|
-
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
19
|
-
const sequence = async () => {
|
|
20
|
-
// Phase 1: Initial Noise
|
|
21
|
-
setPhase('init');
|
|
22
|
-
// Run noise loop (similar to index.html's 10 steps of 50ms)
|
|
23
|
-
for (let i = 0; i < 10; i++) {
|
|
24
|
-
setProgress(Math.random()); // Trigger re-render
|
|
25
|
-
await delay(50);
|
|
26
|
-
}
|
|
27
|
-
// Phase 2: Unified Decryption
|
|
28
|
-
setPhase('decrypt');
|
|
29
|
-
const duration = 1500;
|
|
30
|
-
const startTime = Date.now();
|
|
31
|
-
const animate = () => {
|
|
32
|
-
const elapsed = Date.now() - startTime;
|
|
33
|
-
const p = Math.min(elapsed / duration, 1);
|
|
34
|
-
setProgress(p);
|
|
35
|
-
if (p < 1) {
|
|
36
|
-
setTimeout(animate, 16); // ~60 FPS
|
|
37
|
-
}
|
|
38
|
-
else {
|
|
39
|
-
// Phase 3: Done
|
|
40
|
-
setPhase('done');
|
|
41
|
-
// Short delay before firing complete
|
|
42
|
-
setTimeout(() => onComplete(), 200);
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
animate();
|
|
46
|
-
};
|
|
47
|
-
sequence();
|
|
48
|
-
}, [onComplete]);
|
|
49
|
-
const getDecryptedText = () => {
|
|
50
|
-
const rawLines = LOGO.split('\n');
|
|
51
|
-
// Handle leading newline similar to index.html logic if needed
|
|
52
|
-
// index.html logic: const linesToRender = rawLines[0] === '' ? rawLines.slice(1) : rawLines;
|
|
53
|
-
const lines = rawLines[0] === '' ? rawLines.slice(1) : rawLines;
|
|
54
|
-
return lines.map((line) => {
|
|
55
|
-
let resultLine = '';
|
|
56
|
-
for (let charIndex = 0; charIndex < line.length; charIndex++) {
|
|
57
|
-
const char = line[charIndex];
|
|
58
|
-
let isDecrypted = false;
|
|
59
|
-
if (phase === 'init') {
|
|
60
|
-
isDecrypted = false;
|
|
61
|
-
}
|
|
62
|
-
else if (phase === 'decrypt' || phase === 'done') {
|
|
63
|
-
const threshold = line.length > 0 ? charIndex / line.length : 0;
|
|
64
|
-
if (progress >= threshold - 0.1) {
|
|
65
|
-
isDecrypted = Math.random() > 0.2;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
if (phase === 'done')
|
|
69
|
-
isDecrypted = true;
|
|
70
|
-
if (char === ' ' && phase !== 'init') {
|
|
71
|
-
resultLine += ' ';
|
|
72
|
-
}
|
|
73
|
-
else if (isDecrypted) {
|
|
74
|
-
resultLine += char;
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
if (char === ' ' && Math.random() > 0.1) {
|
|
78
|
-
resultLine += ' ';
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
resultLine += GLITCH_CHARS.charAt(Math.floor(Math.random() * GLITCH_CHARS.length));
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
return resultLine;
|
|
86
|
-
}).join('\n');
|
|
87
|
-
};
|
|
88
|
-
return (_jsxs(Box, { flexDirection: "column", alignItems: "center", children: [_jsx(Text, { color: "#f02a30", bold: true, children: getDecryptedText() }), phase === 'done' && (_jsx(Text, { color: "#f02a30", children: "Deep into Code." }))] }));
|
|
89
|
-
};
|