centaurus-cli 2.6.2 → 2.7.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.
- package/dist/cli-adapter.d.ts +13 -22
- package/dist/cli-adapter.d.ts.map +1 -1
- package/dist/cli-adapter.js +383 -240
- package/dist/cli-adapter.js.map +1 -1
- package/dist/config/defaultConfig.d.ts +1 -0
- package/dist/config/defaultConfig.d.ts.map +1 -1
- package/dist/config/defaultConfig.js +3 -1
- package/dist/config/defaultConfig.js.map +1 -1
- package/dist/config/types.d.ts +1 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +1 -0
- package/dist/config/types.js.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/services/ai-service-client.d.ts +1 -1
- package/dist/services/ai-service-client.d.ts.map +1 -1
- package/dist/services/ai-service-client.js +7 -2
- package/dist/services/ai-service-client.js.map +1 -1
- package/dist/services/api-client.d.ts +6 -0
- package/dist/services/api-client.d.ts.map +1 -1
- package/dist/services/api-client.js +16 -0
- package/dist/services/api-client.js.map +1 -1
- package/dist/tools/command.d.ts.map +1 -1
- package/dist/tools/command.js +77 -25
- package/dist/tools/command.js.map +1 -1
- package/dist/tools/file-ops.d.ts.map +1 -1
- package/dist/tools/file-ops.js +28 -4
- package/dist/tools/file-ops.js.map +1 -1
- package/dist/tools/registry.d.ts +1 -0
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +25 -1
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/task-complete.d.ts +3 -0
- package/dist/tools/task-complete.d.ts.map +1 -0
- package/dist/tools/task-complete.js +48 -0
- package/dist/tools/task-complete.js.map +1 -0
- package/dist/tools/types.d.ts +1 -0
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/web-search.d.ts.map +1 -1
- package/dist/tools/web-search.js +16 -2
- package/dist/tools/web-search.js.map +1 -1
- package/dist/ui/components/AgentTimer.d.ts +7 -0
- package/dist/ui/components/AgentTimer.d.ts.map +1 -0
- package/dist/ui/components/AgentTimer.js +27 -0
- package/dist/ui/components/AgentTimer.js.map +1 -0
- package/dist/ui/components/App.d.ts +2 -0
- package/dist/ui/components/App.d.ts.map +1 -1
- package/dist/ui/components/App.js +229 -53
- package/dist/ui/components/App.js.map +1 -1
- package/dist/ui/components/InputBox.d.ts.map +1 -1
- package/dist/ui/components/InputBox.js +579 -130
- package/dist/ui/components/InputBox.js.map +1 -1
- package/dist/ui/components/InteractiveShell.d.ts +16 -0
- package/dist/ui/components/InteractiveShell.d.ts.map +1 -0
- package/dist/ui/components/InteractiveShell.js +152 -0
- package/dist/ui/components/InteractiveShell.js.map +1 -0
- package/dist/ui/components/LoadingIndicator.js +1 -1
- package/dist/ui/components/LoadingIndicator.js.map +1 -1
- package/dist/ui/components/MarkdownRenderer.js +1 -1
- package/dist/ui/components/StreamingMessageDisplay.js +1 -1
- package/dist/ui/components/StreamingMessageDisplay.js.map +1 -1
- package/dist/ui/components/ToolExecutionMessage.d.ts.map +1 -1
- package/dist/ui/components/ToolExecutionMessage.js +43 -47
- package/dist/ui/components/ToolExecutionMessage.js.map +1 -1
- package/dist/utils/ansi-encoder.d.ts +2 -0
- package/dist/utils/ansi-encoder.d.ts.map +1 -0
- package/dist/utils/ansi-encoder.js +57 -0
- package/dist/utils/ansi-encoder.js.map +1 -0
- package/dist/utils/command-history.d.ts +14 -0
- package/dist/utils/command-history.d.ts.map +1 -0
- package/dist/utils/command-history.js +140 -0
- package/dist/utils/command-history.js.map +1 -0
- package/dist/utils/input-classifier.d.ts +26 -0
- package/dist/utils/input-classifier.d.ts.map +1 -0
- package/dist/utils/input-classifier.js +154 -0
- package/dist/utils/input-classifier.js.map +1 -0
- package/dist/utils/markdown-parser.d.ts.map +1 -1
- package/dist/utils/markdown-parser.js +6 -5
- package/dist/utils/markdown-parser.js.map +1 -1
- package/dist/utils/shell.d.ts +7 -0
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +97 -37
- package/dist/utils/shell.js.map +1 -1
- package/models-config.json +30 -0
- package/package.json +2 -1
- package/prompts/system-prompt-autonomous.md +428 -0
- package/dist/tools/ToolRegistry.d.ts +0 -55
- package/dist/tools/ToolRegistry.d.ts.map +0 -1
- package/dist/tools/ToolRegistry.js +0 -600
- package/dist/tools/ToolRegistry.js.map +0 -1
- package/prompts/system-prompt-enhanced.md +0 -659
- package/prompts/system-prompt.md +0 -206
|
@@ -1,169 +1,540 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
1
|
+
import React, { useState, useEffect, useRef, useMemo } from 'react';
|
|
2
2
|
import { Box, Text, useInput } from 'ink';
|
|
3
|
-
import TextInput from 'ink-text-input';
|
|
4
3
|
import * as fs from 'fs';
|
|
5
4
|
import * as path from 'path';
|
|
6
5
|
import { Breadcrumbs } from './Breadcrumbs.js';
|
|
7
6
|
import { ContextWindowIndicator } from './ContextWindowIndicator.js';
|
|
7
|
+
import { detectIntent } from '../../utils/input-classifier.js';
|
|
8
|
+
import { CommandHistoryManager } from '../../utils/command-history.js';
|
|
9
|
+
const getVisualLines = (text, width) => {
|
|
10
|
+
const logicalLines = text.split('\n');
|
|
11
|
+
const visualLines = [];
|
|
12
|
+
let currentOffset = 0;
|
|
13
|
+
logicalLines.forEach((line, i) => {
|
|
14
|
+
if (line.length === 0) {
|
|
15
|
+
visualLines.push({ start: currentOffset, end: currentOffset, isHardEnd: true });
|
|
16
|
+
currentOffset += 1;
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
let remaining = line;
|
|
20
|
+
let lineStartOffset = currentOffset;
|
|
21
|
+
while (remaining.length > 0) {
|
|
22
|
+
let splitIndex = remaining.length;
|
|
23
|
+
let isHardEnd = false;
|
|
24
|
+
if (remaining.length > width) {
|
|
25
|
+
splitIndex = width;
|
|
26
|
+
const lastSpace = remaining.lastIndexOf(' ', width);
|
|
27
|
+
if (lastSpace > 0) {
|
|
28
|
+
splitIndex = lastSpace + 1;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
isHardEnd = true;
|
|
33
|
+
}
|
|
34
|
+
visualLines.push({
|
|
35
|
+
start: lineStartOffset,
|
|
36
|
+
end: lineStartOffset + splitIndex,
|
|
37
|
+
isHardEnd: isHardEnd
|
|
38
|
+
});
|
|
39
|
+
lineStartOffset += splitIndex;
|
|
40
|
+
remaining = remaining.slice(splitIndex);
|
|
41
|
+
}
|
|
42
|
+
currentOffset += line.length + (i < logicalLines.length - 1 ? 1 : 0);
|
|
43
|
+
});
|
|
44
|
+
return visualLines;
|
|
45
|
+
};
|
|
8
46
|
export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...', autoAcceptMode, model, planMode = false, commandMode = false, currentWorkingDirectory, commandHistory = [], onToggleAutoAccept, onToggleCommandMode, isActive = true, subshellContext, currentTokens = 0, maxTokens = 1000000 }) => {
|
|
9
47
|
const [value, setValue] = useState('');
|
|
48
|
+
const [cursorOffset, setCursorOffset] = useState(0);
|
|
10
49
|
const [completions, setCompletions] = useState([]);
|
|
11
50
|
const [completionIndex, setCompletionIndex] = useState(0);
|
|
12
|
-
const [inputKey, setInputKey] = useState(0);
|
|
13
51
|
const [historyIndex, setHistoryIndex] = useState(-1);
|
|
14
52
|
const [tempValue, setTempValue] = useState('');
|
|
15
|
-
const ignoreNextChangeRef =
|
|
53
|
+
const ignoreNextChangeRef = useRef(false);
|
|
54
|
+
// Auto Mode State
|
|
55
|
+
const [isAutoMode, setIsAutoMode] = useState(true);
|
|
56
|
+
const [detectedIntent, setDetectedIntent] = useState('ai');
|
|
57
|
+
// Autocomplete State
|
|
58
|
+
const [autocompleteSuggestion, setAutocompleteSuggestion] = useState(null);
|
|
59
|
+
// Undo/Redo State
|
|
60
|
+
const [undoStack, setUndoStack] = useState([]);
|
|
61
|
+
const [redoStack, setRedoStack] = useState([]);
|
|
62
|
+
// Selection State
|
|
63
|
+
const [selection, setSelection] = useState(null);
|
|
64
|
+
// Configuration for scrolling
|
|
65
|
+
const MAX_VISIBLE_LINES = 9;
|
|
66
|
+
// Load history on mount
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
CommandHistoryManager.getInstance().load();
|
|
69
|
+
}, []);
|
|
16
70
|
// Force clear value if it becomes empty or whitespace-only after external changes
|
|
17
|
-
|
|
18
|
-
if (value && value.trim() === '') {
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
if (value && value.trim() === '' && cursorOffset > 0) {
|
|
19
73
|
setValue('');
|
|
20
|
-
|
|
74
|
+
setCursorOffset(0);
|
|
21
75
|
}
|
|
22
|
-
}, [value]);
|
|
23
|
-
// Determine current working directory
|
|
24
|
-
const currentDir =
|
|
76
|
+
}, [value, cursorOffset]);
|
|
77
|
+
// Determine current working directory
|
|
78
|
+
const currentDir = useMemo(() => {
|
|
25
79
|
const cwd = currentWorkingDirectory || process.cwd();
|
|
26
|
-
// Truncate long paths to prevent layout shifts
|
|
27
80
|
if (cwd.length > 60) {
|
|
28
81
|
return '...' + cwd.slice(-57);
|
|
29
82
|
}
|
|
30
83
|
return cwd;
|
|
31
84
|
}, [currentWorkingDirectory]);
|
|
85
|
+
// Autocomplete Logic
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
if (!value || value.trim() === '') {
|
|
88
|
+
setAutocompleteSuggestion(null);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
// Only show suggestions if we are in command mode (manual or auto-detected)
|
|
92
|
+
// OR if we are in Auto mode and it looks like a command
|
|
93
|
+
const shouldSuggest = commandMode || (isAutoMode && detectedIntent === 'command');
|
|
94
|
+
if (shouldSuggest) {
|
|
95
|
+
const matches = CommandHistoryManager.getInstance().getMatches(value, currentDir);
|
|
96
|
+
if (matches.length > 0) {
|
|
97
|
+
setAutocompleteSuggestion(matches[0]);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
setAutocompleteSuggestion(null);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
setAutocompleteSuggestion(null);
|
|
105
|
+
}
|
|
106
|
+
}, [value, commandMode, isAutoMode, detectedIntent, currentDir]);
|
|
107
|
+
// Auto-classification effect (Synchronous Heuristics Only)
|
|
108
|
+
useEffect(() => {
|
|
109
|
+
// Only run classification if in Auto Mode
|
|
110
|
+
if (!isAutoMode) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
// Only classify if value is non-empty and not just whitespace
|
|
114
|
+
const trimmedValue = value.trim();
|
|
115
|
+
if (!trimmedValue || !onToggleCommandMode) {
|
|
116
|
+
// Default to AI if empty
|
|
117
|
+
setDetectedIntent('ai');
|
|
118
|
+
if (commandMode)
|
|
119
|
+
onToggleCommandMode();
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
// Run local heuristic detection immediately
|
|
123
|
+
const intent = detectIntent(trimmedValue);
|
|
124
|
+
setDetectedIntent(intent);
|
|
125
|
+
// Switch mode based on intent
|
|
126
|
+
if (intent === 'command') {
|
|
127
|
+
if (!commandMode)
|
|
128
|
+
onToggleCommandMode();
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
// intent === 'ai'
|
|
132
|
+
if (commandMode)
|
|
133
|
+
onToggleCommandMode();
|
|
134
|
+
}
|
|
135
|
+
}, [value, commandMode, onToggleCommandMode, isAutoMode]);
|
|
136
|
+
const pushToUndoStack = () => {
|
|
137
|
+
setUndoStack(prev => [...prev, { value, cursorOffset }]);
|
|
138
|
+
setRedoStack([]); // Clear redo stack on new action
|
|
139
|
+
};
|
|
140
|
+
const handleUndo = () => {
|
|
141
|
+
if (undoStack.length === 0)
|
|
142
|
+
return;
|
|
143
|
+
const previousState = undoStack[undoStack.length - 1];
|
|
144
|
+
setRedoStack(prev => [...prev, { value, cursorOffset }]);
|
|
145
|
+
setUndoStack(prev => prev.slice(0, -1));
|
|
146
|
+
setValue(previousState.value);
|
|
147
|
+
setCursorOffset(previousState.cursorOffset);
|
|
148
|
+
setSelection(null);
|
|
149
|
+
};
|
|
32
150
|
useInput((input, key) => {
|
|
33
|
-
|
|
151
|
+
if (!isActive)
|
|
152
|
+
return;
|
|
153
|
+
// Detect OS for platform-specific key handling
|
|
154
|
+
const isWindows = process.platform === 'win32';
|
|
155
|
+
const inputCharCode = input ? input.charCodeAt(0) : null;
|
|
156
|
+
// DELETE WORD BACKWARDS - Check this FIRST before standard backspace/delete
|
|
157
|
+
// Triggers on any of these conditions:
|
|
158
|
+
// 1. Ctrl+W (char code 23) - Standard Unix terminal shortcut
|
|
159
|
+
// 2. Meta/Alt + Backspace/Delete - Mac alternative
|
|
160
|
+
// 3. Ctrl + Delete - Works on all platforms (Ctrl+Backspace doesn't work on Windows/Ink)
|
|
161
|
+
// 4. Windows Ctrl+Del sends char code 127
|
|
162
|
+
const isDeleteWord = inputCharCode === 23 || // Ctrl+W
|
|
163
|
+
(key.meta && (key.backspace || key.delete)) || // Meta/Alt + Backspace/Delete
|
|
164
|
+
(key.ctrl && key.delete) || // Ctrl+Delete (NOTE: Ctrl+Backspace not detectable on Windows/Ink)
|
|
165
|
+
(isWindows && inputCharCode === 127); // Windows: Ctrl+Del sends char 127
|
|
166
|
+
if (isDeleteWord) {
|
|
167
|
+
pushToUndoStack();
|
|
168
|
+
if (cursorOffset > 0) {
|
|
169
|
+
let newOffset = cursorOffset;
|
|
170
|
+
// Skip whitespace backwards
|
|
171
|
+
while (newOffset > 0 && /\s/.test(value[newOffset - 1])) {
|
|
172
|
+
newOffset--;
|
|
173
|
+
}
|
|
174
|
+
// Skip non-whitespace backwards
|
|
175
|
+
while (newOffset > 0 && !/\s/.test(value[newOffset - 1])) {
|
|
176
|
+
newOffset--;
|
|
177
|
+
}
|
|
178
|
+
const newValue = value.slice(0, newOffset) + value.slice(cursorOffset);
|
|
179
|
+
setValue(newValue);
|
|
180
|
+
setCursorOffset(newOffset);
|
|
181
|
+
}
|
|
182
|
+
setHistoryIndex(-1);
|
|
183
|
+
setCompletions([]);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
// Ctrl+T: Toggle auto-accept
|
|
34
187
|
if (key.ctrl && input.toLowerCase() === 't') {
|
|
35
188
|
ignoreNextChangeRef.current = true;
|
|
36
189
|
onToggleAutoAccept();
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
ignoreNextChangeRef.current = false;
|
|
40
|
-
}, 100);
|
|
41
|
-
return; // Return immediately to prevent any further processing
|
|
190
|
+
setTimeout(() => { ignoreNextChangeRef.current = false; }, 100);
|
|
191
|
+
return;
|
|
42
192
|
}
|
|
43
|
-
// Ctrl+D
|
|
193
|
+
// Ctrl+D: Cycle modes (Agent -> Terminal -> Auto -> Agent)
|
|
44
194
|
if (key.ctrl && input.toLowerCase() === 'd') {
|
|
45
195
|
if (onToggleCommandMode) {
|
|
46
196
|
ignoreNextChangeRef.current = true;
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
197
|
+
// Cycle Logic
|
|
198
|
+
if (!isAutoMode && !commandMode) {
|
|
199
|
+
// Agent -> Terminal
|
|
200
|
+
onToggleCommandMode();
|
|
201
|
+
}
|
|
202
|
+
else if (!isAutoMode && commandMode) {
|
|
203
|
+
// Terminal -> Auto
|
|
204
|
+
setIsAutoMode(true);
|
|
205
|
+
// Trigger initial detection for Auto mode
|
|
206
|
+
const intent = detectIntent(value);
|
|
207
|
+
setDetectedIntent(intent);
|
|
208
|
+
// Set command mode based on intent
|
|
209
|
+
if (intent === 'ai' && commandMode)
|
|
210
|
+
onToggleCommandMode();
|
|
211
|
+
// if intent is command, we are already in command mode
|
|
58
212
|
}
|
|
213
|
+
else if (isAutoMode) {
|
|
214
|
+
// Auto -> Agent
|
|
215
|
+
setIsAutoMode(false);
|
|
216
|
+
// Ensure we go back to Agent mode (commandMode = false)
|
|
217
|
+
if (commandMode)
|
|
218
|
+
onToggleCommandMode();
|
|
219
|
+
}
|
|
220
|
+
setTimeout(() => { ignoreNextChangeRef.current = false; }, 100);
|
|
59
221
|
}
|
|
60
|
-
return;
|
|
222
|
+
return;
|
|
61
223
|
}
|
|
62
|
-
//
|
|
63
|
-
if (key.
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
224
|
+
// Ctrl+Z: Undo
|
|
225
|
+
if (key.ctrl && input.toLowerCase() === 'z') {
|
|
226
|
+
handleUndo();
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
// Ctrl+A: Select All
|
|
230
|
+
if (key.ctrl && input.toLowerCase() === 'a') {
|
|
231
|
+
setSelection({ start: 0, end: value.length });
|
|
232
|
+
setCursorOffset(value.length);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
// DELETE CHAR - Only runs if Delete Word did NOT trigger
|
|
236
|
+
// Triggers on:
|
|
237
|
+
// 1. Backspace or Delete key flag is present
|
|
238
|
+
// 2. OR char code 8 (standard backspace)
|
|
239
|
+
// 3. OR char code 127 (DEL) on non-Windows (Mac/Linux treat 127 as normal backspace)
|
|
240
|
+
const isDeleteChar = key.backspace ||
|
|
241
|
+
key.delete ||
|
|
242
|
+
inputCharCode === 8 ||
|
|
243
|
+
(!isWindows && inputCharCode === 127);
|
|
244
|
+
if (isDeleteChar) {
|
|
245
|
+
pushToUndoStack();
|
|
246
|
+
if (selection) {
|
|
247
|
+
// Delete selection
|
|
248
|
+
const start = Math.min(selection.start, selection.end);
|
|
249
|
+
const end = Math.max(selection.start, selection.end);
|
|
250
|
+
const newValue = value.slice(0, start) + value.slice(end);
|
|
251
|
+
setValue(newValue);
|
|
252
|
+
setCursorOffset(start);
|
|
253
|
+
setSelection(null);
|
|
71
254
|
}
|
|
72
|
-
else if (
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
setValue(
|
|
76
|
-
setInputKey(prev => prev + 1);
|
|
255
|
+
else if (key.delete && cursorOffset < value.length) {
|
|
256
|
+
// Delete key: Delete character at cursor (forward delete)
|
|
257
|
+
const newValue = value.slice(0, cursorOffset) + value.slice(cursorOffset + 1);
|
|
258
|
+
setValue(newValue);
|
|
77
259
|
}
|
|
260
|
+
else if (cursorOffset > 0) {
|
|
261
|
+
// Backspace: Delete character before cursor
|
|
262
|
+
const newValue = value.slice(0, cursorOffset - 1) + value.slice(cursorOffset);
|
|
263
|
+
setValue(newValue);
|
|
264
|
+
setCursorOffset(cursorOffset - 1);
|
|
265
|
+
}
|
|
266
|
+
// Reset history/completions on edit
|
|
267
|
+
setHistoryIndex(-1);
|
|
268
|
+
setCompletions([]);
|
|
78
269
|
return;
|
|
79
270
|
}
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
271
|
+
// Handle Enter
|
|
272
|
+
// Check input length to distinguish single key press from paste
|
|
273
|
+
if (key.return && input.length <= 1) {
|
|
274
|
+
// Check for Shift+Enter, Ctrl+Enter, OR explicit newline char
|
|
275
|
+
// Note: Shift+Enter doesn't work on Windows/Ink, so Ctrl+Enter is the alternative
|
|
276
|
+
// Alt+Enter can't be used as it fullscreens the terminal on Windows
|
|
277
|
+
if (key.shift || key.ctrl || input === '\n') {
|
|
278
|
+
pushToUndoStack();
|
|
279
|
+
// Shift+Enter or Ctrl+Enter: Insert newline
|
|
280
|
+
// If selection exists, replace it
|
|
281
|
+
let newValue = value;
|
|
282
|
+
let newOffset = cursorOffset;
|
|
283
|
+
if (selection) {
|
|
284
|
+
const start = Math.min(selection.start, selection.end);
|
|
285
|
+
const end = Math.max(selection.start, selection.end);
|
|
286
|
+
newValue = value.slice(0, start) + '\n' + value.slice(end);
|
|
287
|
+
newOffset = start + 1;
|
|
288
|
+
setSelection(null);
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
newValue = value.slice(0, cursorOffset) + '\n' + value.slice(cursorOffset);
|
|
292
|
+
newOffset = cursorOffset + 1;
|
|
293
|
+
}
|
|
294
|
+
setValue(newValue);
|
|
295
|
+
setCursorOffset(newOffset);
|
|
87
296
|
}
|
|
88
297
|
else {
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
setValue(tempValue);
|
|
92
|
-
setInputKey(prev => prev + 1);
|
|
298
|
+
// Enter: Submit
|
|
299
|
+
handleSubmit();
|
|
93
300
|
}
|
|
94
301
|
return;
|
|
95
302
|
}
|
|
96
|
-
//
|
|
97
|
-
if (key.
|
|
98
|
-
|
|
303
|
+
// Handle Arrows
|
|
304
|
+
if (key.upArrow || key.downArrow || key.leftArrow || key.rightArrow) {
|
|
305
|
+
// Clear selection on arrow keys
|
|
306
|
+
if (selection) {
|
|
307
|
+
setSelection(null);
|
|
308
|
+
// If left/right, maybe move cursor to start/end of selection?
|
|
309
|
+
// For now, just clear selection and let default logic run (or reset cursor to one end)
|
|
310
|
+
if (key.leftArrow)
|
|
311
|
+
setCursorOffset(Math.min(selection.start, selection.end));
|
|
312
|
+
if (key.rightArrow)
|
|
313
|
+
setCursorOffset(Math.max(selection.start, selection.end));
|
|
314
|
+
if (key.upArrow || key.downArrow) {
|
|
315
|
+
// Keep cursor where it is (at the end usually) or move it?
|
|
316
|
+
// Let's just fall through to normal arrow logic from current cursorOffset
|
|
317
|
+
}
|
|
318
|
+
if (key.leftArrow || key.rightArrow)
|
|
319
|
+
return; // We handled the move
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (key.upArrow) {
|
|
323
|
+
const width = (process.stdout.columns || 80) - 6;
|
|
324
|
+
const visualLines = getVisualLines(value, width);
|
|
325
|
+
let currentVisualLineIndex = visualLines.findIndex(line => {
|
|
326
|
+
if (cursorOffset >= line.start && cursorOffset < line.end)
|
|
327
|
+
return true;
|
|
328
|
+
if (cursorOffset === line.end && line.isHardEnd)
|
|
329
|
+
return true;
|
|
330
|
+
return false;
|
|
331
|
+
});
|
|
332
|
+
// If cursor is at the very end of the last line
|
|
333
|
+
if (currentVisualLineIndex === -1 && cursorOffset === value.length) {
|
|
334
|
+
currentVisualLineIndex = visualLines.length - 1;
|
|
335
|
+
}
|
|
336
|
+
if (currentVisualLineIndex <= 0) {
|
|
337
|
+
// Top line: History navigation
|
|
338
|
+
if (commandHistory.length > 0) {
|
|
339
|
+
if (historyIndex === -1) {
|
|
340
|
+
setTempValue(value);
|
|
341
|
+
const newIndex = commandHistory.length - 1;
|
|
342
|
+
setHistoryIndex(newIndex);
|
|
343
|
+
const newValue = commandHistory[newIndex];
|
|
344
|
+
setValue(newValue);
|
|
345
|
+
setCursorOffset(newValue.length);
|
|
346
|
+
}
|
|
347
|
+
else if (historyIndex > 0) {
|
|
348
|
+
const newIndex = historyIndex - 1;
|
|
349
|
+
setHistoryIndex(newIndex);
|
|
350
|
+
const newValue = commandHistory[newIndex];
|
|
351
|
+
setValue(newValue);
|
|
352
|
+
setCursorOffset(newValue.length);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
// Move cursor up one visual line
|
|
358
|
+
const currentLine = visualLines[currentVisualLineIndex];
|
|
359
|
+
const targetLine = visualLines[currentVisualLineIndex - 1];
|
|
360
|
+
const offsetInLine = cursorOffset - currentLine.start;
|
|
361
|
+
const newOffset = targetLine.start + Math.min(offsetInLine, targetLine.end - targetLine.start);
|
|
362
|
+
setCursorOffset(newOffset);
|
|
363
|
+
}
|
|
99
364
|
return;
|
|
100
365
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
366
|
+
if (key.downArrow) {
|
|
367
|
+
const width = (process.stdout.columns || 80) - 6;
|
|
368
|
+
const visualLines = getVisualLines(value, width);
|
|
369
|
+
let currentVisualLineIndex = visualLines.findIndex(line => {
|
|
370
|
+
if (cursorOffset >= line.start && cursorOffset < line.end)
|
|
371
|
+
return true;
|
|
372
|
+
if (cursorOffset === line.end && line.isHardEnd)
|
|
373
|
+
return true;
|
|
374
|
+
return false;
|
|
375
|
+
});
|
|
376
|
+
// If cursor is at the very end of the last line
|
|
377
|
+
if (currentVisualLineIndex === -1 && cursorOffset === value.length) {
|
|
378
|
+
currentVisualLineIndex = visualLines.length - 1;
|
|
379
|
+
}
|
|
380
|
+
if (currentVisualLineIndex === visualLines.length - 1) {
|
|
381
|
+
// Bottom line: History navigation
|
|
382
|
+
if (historyIndex !== -1) {
|
|
383
|
+
if (historyIndex < commandHistory.length - 1) {
|
|
384
|
+
const newIndex = historyIndex + 1;
|
|
385
|
+
setHistoryIndex(newIndex);
|
|
386
|
+
const newValue = commandHistory[newIndex];
|
|
387
|
+
setValue(newValue);
|
|
388
|
+
setCursorOffset(newValue.length);
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
setHistoryIndex(-1);
|
|
392
|
+
setValue(tempValue);
|
|
393
|
+
setCursorOffset(tempValue.length);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
// Move cursor down one visual line
|
|
399
|
+
const currentLine = visualLines[currentVisualLineIndex];
|
|
400
|
+
const targetLine = visualLines[currentVisualLineIndex + 1];
|
|
401
|
+
const offsetInLine = cursorOffset - currentLine.start;
|
|
402
|
+
const newOffset = targetLine.start + Math.min(offsetInLine, targetLine.end - targetLine.start);
|
|
403
|
+
setCursorOffset(newOffset);
|
|
404
|
+
}
|
|
107
405
|
return;
|
|
108
406
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
407
|
+
if (key.leftArrow) {
|
|
408
|
+
if (key.ctrl) {
|
|
409
|
+
// Ctrl+Left: Move word backwards
|
|
410
|
+
let newOffset = cursorOffset;
|
|
411
|
+
if (newOffset > 0) {
|
|
412
|
+
// Skip whitespace backwards
|
|
413
|
+
while (newOffset > 0 && /\s/.test(value[newOffset - 1])) {
|
|
414
|
+
newOffset--;
|
|
415
|
+
}
|
|
416
|
+
// Skip non-whitespace backwards
|
|
417
|
+
while (newOffset > 0 && !/\s/.test(value[newOffset - 1])) {
|
|
418
|
+
newOffset--;
|
|
419
|
+
}
|
|
420
|
+
setCursorOffset(newOffset);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
else if (cursorOffset > 0) {
|
|
424
|
+
setCursorOffset(cursorOffset - 1);
|
|
425
|
+
}
|
|
113
426
|
return;
|
|
114
427
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
428
|
+
if (key.rightArrow) {
|
|
429
|
+
// Autocomplete Logic (Only at end of line)
|
|
430
|
+
if (autocompleteSuggestion && cursorOffset === value.length) {
|
|
431
|
+
if (key.ctrl) {
|
|
432
|
+
// Ctrl+Right: Accept FULL suggestion
|
|
433
|
+
setValue(autocompleteSuggestion);
|
|
434
|
+
setCursorOffset(autocompleteSuggestion.length);
|
|
435
|
+
setAutocompleteSuggestion(null);
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
// Right: Accept NEXT WORD
|
|
440
|
+
const remaining = autocompleteSuggestion.slice(value.length);
|
|
441
|
+
// Match next chunk of non-whitespace (including preceding whitespace)
|
|
442
|
+
const match = remaining.match(/^(\s*\S+)/);
|
|
443
|
+
if (match) {
|
|
444
|
+
const toAdd = match[0];
|
|
445
|
+
const newValue = value + toAdd;
|
|
446
|
+
setValue(newValue);
|
|
447
|
+
setCursorOffset(newValue.length);
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
else if (remaining.length > 0) {
|
|
451
|
+
// Fallback: if only whitespace remains or regex fails, take it all
|
|
452
|
+
const newValue = value + remaining;
|
|
453
|
+
setValue(newValue);
|
|
454
|
+
setCursorOffset(newValue.length);
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
// Navigation Logic (if not completing)
|
|
460
|
+
if (key.ctrl) {
|
|
461
|
+
// Ctrl+Right: Move word forwards
|
|
462
|
+
let newOffset = cursorOffset;
|
|
463
|
+
if (newOffset < value.length) {
|
|
464
|
+
// Skip non-whitespace forwards
|
|
465
|
+
while (newOffset < value.length && !/\s/.test(value[newOffset])) {
|
|
466
|
+
newOffset++;
|
|
467
|
+
}
|
|
468
|
+
// Skip whitespace forwards
|
|
469
|
+
while (newOffset < value.length && /\s/.test(value[newOffset])) {
|
|
470
|
+
newOffset++;
|
|
471
|
+
}
|
|
472
|
+
setCursorOffset(newOffset);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
else if (cursorOffset < value.length) {
|
|
476
|
+
setCursorOffset(cursorOffset + 1);
|
|
477
|
+
}
|
|
119
478
|
return;
|
|
120
479
|
}
|
|
121
|
-
//
|
|
122
|
-
if (
|
|
123
|
-
|
|
124
|
-
|
|
480
|
+
// Tab Completion
|
|
481
|
+
if (key.tab && !key.shift) {
|
|
482
|
+
// Only file completion (only in command mode)
|
|
483
|
+
if (commandMode) {
|
|
484
|
+
handleTabCompletion();
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
125
487
|
}
|
|
126
|
-
//
|
|
127
|
-
|
|
488
|
+
// Regular Input
|
|
489
|
+
// Ignore control keys to prevent printing garbage (like 'v' for Ctrl+V)
|
|
490
|
+
if (input && !key.ctrl && !key.meta) {
|
|
491
|
+
pushToUndoStack();
|
|
492
|
+
// Handle paste with newlines
|
|
493
|
+
const cleanedInput = input.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
494
|
+
let newValue = value;
|
|
495
|
+
let newOffset = cursorOffset;
|
|
496
|
+
if (selection) {
|
|
497
|
+
const start = Math.min(selection.start, selection.end);
|
|
498
|
+
const end = Math.max(selection.start, selection.end);
|
|
499
|
+
newValue = value.slice(0, start) + cleanedInput + value.slice(end);
|
|
500
|
+
newOffset = start + cleanedInput.length;
|
|
501
|
+
setSelection(null);
|
|
502
|
+
}
|
|
503
|
+
else {
|
|
504
|
+
newValue = value.slice(0, cursorOffset) + cleanedInput + value.slice(cursorOffset);
|
|
505
|
+
newOffset = cursorOffset + cleanedInput.length;
|
|
506
|
+
}
|
|
507
|
+
setValue(newValue);
|
|
508
|
+
setCursorOffset(newOffset);
|
|
509
|
+
// Reset history/completions
|
|
128
510
|
setHistoryIndex(-1);
|
|
129
|
-
|
|
511
|
+
setCompletions([]);
|
|
130
512
|
}
|
|
131
|
-
|
|
132
|
-
// ink-text-input doesn't handle newlines well, so we keep them in the value
|
|
133
|
-
setValue(newValue);
|
|
134
|
-
};
|
|
513
|
+
}, { isActive });
|
|
135
514
|
const handleTabCompletion = async () => {
|
|
136
515
|
if (!value)
|
|
137
516
|
return;
|
|
138
|
-
// Get the last word (path) from the command
|
|
139
517
|
const words = value.split(' ');
|
|
140
518
|
const lastWord = words[words.length - 1];
|
|
141
|
-
// If we have existing completions, cycle through them
|
|
142
519
|
if (completions.length > 0) {
|
|
143
520
|
const nextIndex = (completionIndex + 1) % completions.length;
|
|
144
521
|
setCompletionIndex(nextIndex);
|
|
145
|
-
// Replace the last word with the next completion
|
|
146
522
|
words[words.length - 1] = completions[nextIndex];
|
|
147
523
|
const newValue = words.join(' ');
|
|
148
|
-
// Force remount to reset cursor position
|
|
149
524
|
setValue(newValue);
|
|
150
|
-
|
|
525
|
+
setCursorOffset(newValue.length);
|
|
151
526
|
return;
|
|
152
527
|
}
|
|
153
|
-
// Get completions for the current path
|
|
154
528
|
try {
|
|
155
529
|
const cwd = currentWorkingDirectory || process.cwd();
|
|
156
530
|
let searchDir = cwd;
|
|
157
531
|
let searchPattern = lastWord;
|
|
158
532
|
let dirPart = '';
|
|
159
|
-
// If the path contains a directory separator, split it
|
|
160
533
|
if (lastWord.includes('/') || lastWord.includes('\\')) {
|
|
161
534
|
const lastSep = Math.max(lastWord.lastIndexOf('/'), lastWord.lastIndexOf('\\'));
|
|
162
535
|
dirPart = lastWord.substring(0, lastSep + 1);
|
|
163
536
|
searchPattern = lastWord.substring(lastSep + 1);
|
|
164
|
-
// For subshells, use their path format
|
|
165
537
|
if (subshellContext && subshellContext.type !== 'local') {
|
|
166
|
-
// Use forward slashes for Unix-like paths
|
|
167
538
|
searchDir = dirPart.startsWith('/') ? dirPart.slice(0, -1) : cwd + '/' + dirPart.slice(0, -1);
|
|
168
539
|
}
|
|
169
540
|
else {
|
|
@@ -171,27 +542,19 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
171
542
|
}
|
|
172
543
|
}
|
|
173
544
|
let entries;
|
|
174
|
-
// Check if we're in a subshell context
|
|
175
545
|
if (subshellContext && subshellContext.type !== 'local' && subshellContext.handler) {
|
|
176
|
-
// Use the subshell handler to list directory
|
|
177
546
|
const dirEntries = await subshellContext.handler.listDirectory(searchDir);
|
|
178
|
-
entries = dirEntries.map(entry => ({
|
|
179
|
-
name: entry.name,
|
|
180
|
-
type: entry.type
|
|
181
|
-
}));
|
|
547
|
+
entries = dirEntries.map(entry => ({ name: entry.name, type: entry.type }));
|
|
182
548
|
}
|
|
183
549
|
else {
|
|
184
|
-
|
|
185
|
-
if (!fs.existsSync(searchDir)) {
|
|
550
|
+
if (!fs.existsSync(searchDir))
|
|
186
551
|
return;
|
|
187
|
-
}
|
|
188
552
|
const fsEntries = fs.readdirSync(searchDir, { withFileTypes: true });
|
|
189
553
|
entries = fsEntries.map(entry => ({
|
|
190
554
|
name: entry.name,
|
|
191
555
|
type: entry.isDirectory() ? 'directory' : 'file'
|
|
192
556
|
}));
|
|
193
557
|
}
|
|
194
|
-
// Filter matches
|
|
195
558
|
const matches = entries
|
|
196
559
|
.filter(entry => entry.name.toLowerCase().startsWith(searchPattern.toLowerCase()))
|
|
197
560
|
.map(entry => {
|
|
@@ -200,48 +563,132 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
200
563
|
return entry.type === 'directory' ? fullPath + separator : fullPath;
|
|
201
564
|
})
|
|
202
565
|
.sort();
|
|
203
|
-
if (matches.length
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
setValue(newValue);
|
|
209
|
-
setInputKey(prev => prev + 1);
|
|
210
|
-
setCompletions([]);
|
|
211
|
-
}
|
|
212
|
-
else if (matches.length > 1) {
|
|
213
|
-
// Multiple matches - set up for cycling
|
|
214
|
-
setCompletions(matches);
|
|
215
|
-
setCompletionIndex(0);
|
|
566
|
+
if (matches.length > 0) {
|
|
567
|
+
if (matches.length > 1) {
|
|
568
|
+
setCompletions(matches);
|
|
569
|
+
setCompletionIndex(0);
|
|
570
|
+
}
|
|
216
571
|
words[words.length - 1] = matches[0];
|
|
217
572
|
const newValue = words.join(' ');
|
|
218
|
-
// Force remount to reset cursor position
|
|
219
573
|
setValue(newValue);
|
|
220
|
-
|
|
574
|
+
setCursorOffset(newValue.length);
|
|
221
575
|
}
|
|
222
576
|
}
|
|
223
577
|
catch (error) {
|
|
224
|
-
// Ignore errors
|
|
578
|
+
// Ignore errors
|
|
225
579
|
}
|
|
226
580
|
};
|
|
227
581
|
const handleSubmit = () => {
|
|
228
|
-
|
|
229
|
-
if (!isActive) {
|
|
582
|
+
if (!isActive)
|
|
230
583
|
return;
|
|
231
|
-
}
|
|
232
584
|
const trimmedValue = value.trim();
|
|
233
585
|
if (trimmedValue) {
|
|
586
|
+
// Save to history if it was a command
|
|
587
|
+
if (commandMode) {
|
|
588
|
+
CommandHistoryManager.getInstance().addCommand(trimmedValue, currentDir);
|
|
589
|
+
}
|
|
234
590
|
onSubmit(trimmedValue);
|
|
235
591
|
setValue('');
|
|
592
|
+
setCursorOffset(0);
|
|
236
593
|
setCompletions([]);
|
|
237
594
|
setCompletionIndex(0);
|
|
238
595
|
setHistoryIndex(-1);
|
|
239
596
|
setTempValue('');
|
|
240
|
-
|
|
597
|
+
setUndoStack([]);
|
|
598
|
+
setRedoStack([]);
|
|
599
|
+
setSelection(null);
|
|
600
|
+
setAutocompleteSuggestion(null);
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
// Rendering Logic with Scrolling
|
|
604
|
+
const renderInput = () => {
|
|
605
|
+
const lines = value.split('\n');
|
|
606
|
+
// If empty, show placeholder
|
|
607
|
+
if (lines.length === 1 && lines[0] === '') {
|
|
608
|
+
return React.createElement(Text, { color: "gray" }, placeholder);
|
|
609
|
+
}
|
|
610
|
+
// Calculate cursor line and column
|
|
611
|
+
let currentPos = 0;
|
|
612
|
+
let cursorLine = 0;
|
|
613
|
+
let cursorCol = 0;
|
|
614
|
+
for (let i = 0; i < lines.length; i++) {
|
|
615
|
+
if (currentPos + lines[i].length >= cursorOffset) {
|
|
616
|
+
cursorLine = i;
|
|
617
|
+
cursorCol = cursorOffset - currentPos;
|
|
618
|
+
break;
|
|
619
|
+
}
|
|
620
|
+
currentPos += lines[i].length + 1; // +1 for newline
|
|
621
|
+
}
|
|
622
|
+
// Edge case: cursor at very end of content
|
|
623
|
+
if (cursorOffset === value.length && lines.length > 0) {
|
|
624
|
+
cursorLine = lines.length - 1;
|
|
625
|
+
cursorCol = lines[lines.length - 1].length;
|
|
626
|
+
}
|
|
627
|
+
// Calculate visible range
|
|
628
|
+
let startLine = 0;
|
|
629
|
+
if (lines.length > MAX_VISIBLE_LINES) {
|
|
630
|
+
if (cursorLine < MAX_VISIBLE_LINES) {
|
|
631
|
+
startLine = 0;
|
|
632
|
+
}
|
|
633
|
+
else {
|
|
634
|
+
startLine = cursorLine - MAX_VISIBLE_LINES + 1;
|
|
635
|
+
}
|
|
241
636
|
}
|
|
637
|
+
const endLine = Math.min(startLine + MAX_VISIBLE_LINES, lines.length);
|
|
638
|
+
const visibleLines = lines.slice(startLine, endLine);
|
|
639
|
+
return (React.createElement(Box, { flexDirection: "column", flexGrow: 1 },
|
|
640
|
+
startLine > 0 && React.createElement(Text, { color: "gray" }, "\u2191 ..."),
|
|
641
|
+
visibleLines.map((line, idx) => {
|
|
642
|
+
const actualLineIndex = startLine + idx;
|
|
643
|
+
const isCursorLine = actualLineIndex === cursorLine;
|
|
644
|
+
const isLastLine = actualLineIndex === lines.length - 1;
|
|
645
|
+
// Calculate absolute position of this line start
|
|
646
|
+
let lineStartPos = 0;
|
|
647
|
+
for (let k = 0; k < actualLineIndex; k++)
|
|
648
|
+
lineStartPos += lines[k].length + 1;
|
|
649
|
+
if (!isActive) {
|
|
650
|
+
if (line.length === 0) {
|
|
651
|
+
return React.createElement(Text, { key: idx }, " ");
|
|
652
|
+
}
|
|
653
|
+
return React.createElement(Text, { key: idx }, line);
|
|
654
|
+
}
|
|
655
|
+
// Render with selection and cursor
|
|
656
|
+
const chars = line.split('');
|
|
657
|
+
const renderedChars = chars.map((char, charIdx) => {
|
|
658
|
+
const absPos = lineStartPos + charIdx;
|
|
659
|
+
const isSelected = selection &&
|
|
660
|
+
absPos >= Math.min(selection.start, selection.end) &&
|
|
661
|
+
absPos < Math.max(selection.start, selection.end);
|
|
662
|
+
const isCursor = isCursorLine && charIdx === cursorCol;
|
|
663
|
+
if (isCursor) {
|
|
664
|
+
return React.createElement(Text, { key: charIdx, inverse: true, color: isSelected ? "yellow" : undefined }, char);
|
|
665
|
+
}
|
|
666
|
+
if (isSelected) {
|
|
667
|
+
return React.createElement(Text, { key: charIdx, backgroundColor: "white", color: "black" }, char);
|
|
668
|
+
}
|
|
669
|
+
return React.createElement(Text, { key: charIdx }, char);
|
|
670
|
+
});
|
|
671
|
+
// Handle cursor at end of line
|
|
672
|
+
if (isCursorLine && cursorCol === line.length) {
|
|
673
|
+
renderedChars.push(React.createElement(Text, { key: "cursor", inverse: true }, " "));
|
|
674
|
+
}
|
|
675
|
+
// Render Autocomplete Ghost Text
|
|
676
|
+
// Only on the last line, if suggestion exists and matches start
|
|
677
|
+
if (isLastLine && autocompleteSuggestion && autocompleteSuggestion.startsWith(value)) {
|
|
678
|
+
const suffix = autocompleteSuggestion.slice(value.length);
|
|
679
|
+
if (suffix) {
|
|
680
|
+
renderedChars.push(React.createElement(Text, { key: "ghost", color: "gray" }, suffix));
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
if (renderedChars.length === 0) {
|
|
684
|
+
return React.createElement(Text, { key: idx }, " ");
|
|
685
|
+
}
|
|
686
|
+
return React.createElement(Text, { key: idx }, renderedChars);
|
|
687
|
+
}),
|
|
688
|
+
endLine < lines.length && React.createElement(Text, { color: "gray" }, "\u2193 ...")));
|
|
242
689
|
};
|
|
243
|
-
return (React.createElement(Box, { flexDirection: "column", borderStyle: "round", borderColor: "#
|
|
244
|
-
React.createElement(Box, { marginY: 1, justifyContent: "space-between" },
|
|
690
|
+
return (React.createElement(Box, { flexDirection: "column", borderStyle: "round", borderColor: commandMode ? "#00cc66" : "#257aa5ff", paddingX: 1, paddingY: 0, width: "100%" },
|
|
691
|
+
React.createElement(Box, { marginY: 1, justifyContent: "space-between", width: "100%" },
|
|
245
692
|
React.createElement(Box, null,
|
|
246
693
|
subshellContext && subshellContext.type !== 'local' && (React.createElement(Breadcrumbs, { context: subshellContext })),
|
|
247
694
|
React.createElement(Text, { color: "#666666" }, "CWD: "),
|
|
@@ -252,13 +699,15 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
252
699
|
React.createElement(Text, { color: "#00ccff" }, model))),
|
|
253
700
|
React.createElement(Box, null,
|
|
254
701
|
React.createElement(Text, { color: "#666666" }, "Mode: "),
|
|
255
|
-
|
|
256
|
-
|
|
702
|
+
isAutoMode ? (React.createElement(Text, null,
|
|
703
|
+
React.createElement(Text, { color: "#00ccff", bold: true }, "Auto ["),
|
|
704
|
+
detectedIntent === 'command' ? (React.createElement(Text, { color: "#00cc66", bold: true }, "Terminal")) : (React.createElement(Text, { color: "#00ccff" }, "Agent")),
|
|
705
|
+
React.createElement(Text, { color: "#00ccff", bold: true }, "]"))) : commandMode ? (React.createElement(Text, { color: "#00cc66", bold: true }, "Terminal")) : planMode ? (React.createElement(Text, { color: "#ffaa00", bold: true }, "Plan")) : (React.createElement(Text, { color: "#00ccff" }, "Agent"))))),
|
|
706
|
+
React.createElement(Box, { flexDirection: "row", width: "100%" },
|
|
257
707
|
React.createElement(Text, { color: "#666666" }, "> "),
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
React.createElement(Text, { color: "#666666", dimColor: true }, commandMode ? ('Ctrl+D to exit command mode • Tab for autocomplete') : ('Ctrl+D for command mode • Ctrl+T to toggle auto-accept')),
|
|
708
|
+
renderInput()),
|
|
709
|
+
React.createElement(Box, { marginY: 1, justifyContent: "space-between", width: "100%" },
|
|
710
|
+
React.createElement(Text, { color: "#666666", dimColor: true }, isAutoMode ? ('Ctrl+D to cycle modes • Auto-detecting intent') : commandMode ? ('Ctrl+D to cycle modes • Tab for autocomplete') : ('Ctrl+D to cycle modes • Shift+Enter for new line')),
|
|
262
711
|
React.createElement(Box, { gap: 1 },
|
|
263
712
|
!commandMode && autoAcceptMode ? (React.createElement(Text, { color: "#00cc66", bold: true }, "[AUTO-ACCEPT: ON]")) : !commandMode ? (React.createElement(Text, { color: "#666666", dimColor: true }, "[AUTO-ACCEPT: OFF]")) : null,
|
|
264
713
|
!commandMode && (React.createElement(Box, { marginLeft: 1 },
|