wave-code 0.5.0 → 0.6.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/dist/components/App.d.ts.map +1 -1
- package/dist/components/App.js +40 -2
- package/dist/components/BackgroundTaskManager.d.ts +6 -0
- package/dist/components/BackgroundTaskManager.d.ts.map +1 -0
- package/dist/components/{TaskManager.js → BackgroundTaskManager.js} +1 -1
- package/dist/components/ChatInterface.d.ts.map +1 -1
- package/dist/components/ChatInterface.js +40 -5
- package/dist/components/CommandOutputDisplay.d.ts.map +1 -1
- package/dist/components/CommandOutputDisplay.js +6 -17
- package/dist/components/CommandSelector.d.ts.map +1 -1
- package/dist/components/CommandSelector.js +16 -2
- package/dist/components/CompressDisplay.d.ts.map +1 -1
- package/dist/components/CompressDisplay.js +6 -10
- package/dist/components/ConfirmationDetails.d.ts +9 -0
- package/dist/components/ConfirmationDetails.d.ts.map +1 -0
- package/dist/components/ConfirmationDetails.js +53 -0
- package/dist/components/{Confirmation.d.ts → ConfirmationSelector.d.ts} +3 -3
- package/dist/components/ConfirmationSelector.d.ts.map +1 -0
- package/dist/components/{Confirmation.js → ConfirmationSelector.js} +34 -96
- package/dist/components/DiffDisplay.d.ts.map +1 -1
- package/dist/components/DiffDisplay.js +48 -1
- package/dist/components/FileSelector.d.ts.map +1 -1
- package/dist/components/FileSelector.js +2 -2
- package/dist/components/HelpView.d.ts +6 -0
- package/dist/components/HelpView.d.ts.map +1 -0
- package/dist/components/HelpView.js +24 -0
- package/dist/components/HistorySearch.d.ts.map +1 -1
- package/dist/components/HistorySearch.js +12 -4
- package/dist/components/InputBox.d.ts +1 -3
- package/dist/components/InputBox.d.ts.map +1 -1
- package/dist/components/InputBox.js +14 -17
- package/dist/components/LoadingIndicator.d.ts +11 -0
- package/dist/components/LoadingIndicator.d.ts.map +1 -0
- package/dist/components/LoadingIndicator.js +6 -0
- package/dist/components/Markdown.d.ts.map +1 -1
- package/dist/components/Markdown.js +114 -121
- package/dist/components/MessageItem.d.ts +1 -1
- package/dist/components/MessageItem.d.ts.map +1 -1
- package/dist/components/MessageItem.js +3 -5
- package/dist/components/MessageList.d.ts +2 -3
- package/dist/components/MessageList.d.ts.map +1 -1
- package/dist/components/MessageList.js +29 -12
- package/dist/components/PlanDisplay.d.ts.map +1 -1
- package/dist/components/PlanDisplay.js +4 -12
- package/dist/components/RewindCommand.d.ts +4 -0
- package/dist/components/RewindCommand.d.ts.map +1 -1
- package/dist/components/RewindCommand.js +20 -3
- package/dist/components/TaskList.d.ts +3 -0
- package/dist/components/TaskList.d.ts.map +1 -0
- package/dist/components/TaskList.js +40 -0
- package/dist/components/ToolDisplay.d.ts +9 -0
- package/dist/components/ToolDisplay.d.ts.map +1 -0
- package/dist/components/ToolDisplay.js +44 -0
- package/dist/contexts/useChat.d.ts +11 -3
- package/dist/contexts/useChat.d.ts.map +1 -1
- package/dist/contexts/useChat.js +51 -32
- package/dist/hooks/useInputManager.d.ts +4 -15
- package/dist/hooks/useInputManager.d.ts.map +1 -1
- package/dist/hooks/useInputManager.js +20 -65
- package/dist/hooks/useTasks.d.ts +2 -0
- package/dist/hooks/useTasks.d.ts.map +1 -0
- package/dist/hooks/useTasks.js +5 -0
- package/dist/managers/InputManager.d.ts +8 -30
- package/dist/managers/InputManager.d.ts.map +1 -1
- package/dist/managers/InputManager.js +38 -144
- package/dist/print-cli.d.ts.map +1 -1
- package/dist/print-cli.js +11 -30
- package/package.json +5 -6
- package/src/components/App.tsx +51 -3
- package/src/components/{TaskManager.tsx → BackgroundTaskManager.tsx} +4 -2
- package/src/components/ChatInterface.tsx +80 -23
- package/src/components/CommandOutputDisplay.tsx +16 -38
- package/src/components/CommandSelector.tsx +41 -17
- package/src/components/CompressDisplay.tsx +5 -22
- package/src/components/ConfirmationDetails.tsx +108 -0
- package/src/components/{Confirmation.tsx → ConfirmationSelector.tsx} +74 -193
- package/src/components/DiffDisplay.tsx +71 -1
- package/src/components/FileSelector.tsx +0 -2
- package/src/components/HelpView.tsx +59 -0
- package/src/components/HistorySearch.tsx +45 -21
- package/src/components/InputBox.tsx +51 -63
- package/src/components/LoadingIndicator.tsx +56 -0
- package/src/components/Markdown.tsx +126 -323
- package/src/components/MessageItem.tsx +13 -24
- package/src/components/MessageList.tsx +48 -82
- package/src/components/PlanDisplay.tsx +4 -27
- package/src/components/RewindCommand.tsx +39 -2
- package/src/components/TaskList.tsx +58 -0
- package/src/components/{ToolResultDisplay.tsx → ToolDisplay.tsx} +8 -18
- package/src/contexts/useChat.tsx +73 -41
- package/src/hooks/useInputManager.ts +21 -83
- package/src/hooks/useTasks.ts +6 -0
- package/src/managers/InputManager.ts +43 -179
- package/src/print-cli.ts +17 -35
- package/dist/components/Confirmation.d.ts.map +0 -1
- package/dist/components/MemoryDisplay.d.ts +0 -8
- package/dist/components/MemoryDisplay.d.ts.map +0 -1
- package/dist/components/MemoryDisplay.js +0 -25
- package/dist/components/MemoryTypeSelector.d.ts +0 -8
- package/dist/components/MemoryTypeSelector.d.ts.map +0 -1
- package/dist/components/MemoryTypeSelector.js +0 -38
- package/dist/components/SubagentBlock.d.ts +0 -8
- package/dist/components/SubagentBlock.d.ts.map +0 -1
- package/dist/components/SubagentBlock.js +0 -70
- package/dist/components/TaskManager.d.ts +0 -6
- package/dist/components/TaskManager.d.ts.map +0 -1
- package/dist/components/ToolResultDisplay.d.ts +0 -9
- package/dist/components/ToolResultDisplay.d.ts.map +0 -1
- package/dist/components/ToolResultDisplay.js +0 -54
- package/src/components/MemoryDisplay.tsx +0 -62
- package/src/components/MemoryTypeSelector.tsx +0 -98
- package/src/components/SubagentBlock.tsx +0 -143
|
@@ -3,44 +3,9 @@ import { Box, Text, useInput } from "ink";
|
|
|
3
3
|
import type { PermissionDecision, AskUserQuestionInput } from "wave-agent-sdk";
|
|
4
4
|
import {
|
|
5
5
|
BASH_TOOL_NAME,
|
|
6
|
-
EDIT_TOOL_NAME,
|
|
7
|
-
MULTI_EDIT_TOOL_NAME,
|
|
8
|
-
DELETE_FILE_TOOL_NAME,
|
|
9
|
-
WRITE_TOOL_NAME,
|
|
10
6
|
EXIT_PLAN_MODE_TOOL_NAME,
|
|
11
7
|
ASK_USER_QUESTION_TOOL_NAME,
|
|
12
8
|
} from "wave-agent-sdk";
|
|
13
|
-
import { DiffDisplay } from "./DiffDisplay.js";
|
|
14
|
-
import { PlanDisplay } from "./PlanDisplay.js";
|
|
15
|
-
|
|
16
|
-
// Helper function to generate descriptive action text
|
|
17
|
-
const getActionDescription = (
|
|
18
|
-
toolName: string,
|
|
19
|
-
toolInput?: Record<string, unknown>,
|
|
20
|
-
): string => {
|
|
21
|
-
if (!toolInput) {
|
|
22
|
-
return "Execute operation";
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
switch (toolName) {
|
|
26
|
-
case BASH_TOOL_NAME:
|
|
27
|
-
return `Execute command: ${toolInput.command || "unknown command"}`;
|
|
28
|
-
case EDIT_TOOL_NAME:
|
|
29
|
-
return `Edit file: ${toolInput.file_path || "unknown file"}`;
|
|
30
|
-
case MULTI_EDIT_TOOL_NAME:
|
|
31
|
-
return `Edit multiple sections in: ${toolInput.file_path || "unknown file"}`;
|
|
32
|
-
case DELETE_FILE_TOOL_NAME:
|
|
33
|
-
return `Delete file: ${toolInput.target_file || "unknown file"}`;
|
|
34
|
-
case WRITE_TOOL_NAME:
|
|
35
|
-
return `Write to file: ${toolInput.file_path || "unknown file"}`;
|
|
36
|
-
case EXIT_PLAN_MODE_TOOL_NAME:
|
|
37
|
-
return "Review and approve the plan";
|
|
38
|
-
case ASK_USER_QUESTION_TOOL_NAME:
|
|
39
|
-
return "Answer questions to clarify intent";
|
|
40
|
-
default:
|
|
41
|
-
return "Execute operation";
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
9
|
|
|
45
10
|
const getHeaderColor = (header: string) => {
|
|
46
11
|
const colors = ["red", "green", "blue", "magenta", "cyan"] as const;
|
|
@@ -51,7 +16,7 @@ const getHeaderColor = (header: string) => {
|
|
|
51
16
|
return colors[Math.abs(hash) % colors.length];
|
|
52
17
|
};
|
|
53
18
|
|
|
54
|
-
export interface
|
|
19
|
+
export interface ConfirmationSelectorProps {
|
|
55
20
|
toolName: string;
|
|
56
21
|
toolInput?: Record<string, unknown>;
|
|
57
22
|
suggestedPrefix?: string;
|
|
@@ -66,10 +31,10 @@ interface ConfirmationState {
|
|
|
66
31
|
selectedOption: "allow" | "auto" | "alternative";
|
|
67
32
|
alternativeText: string;
|
|
68
33
|
alternativeCursorPosition: number;
|
|
69
|
-
hasUserInput: boolean;
|
|
34
|
+
hasUserInput: boolean;
|
|
70
35
|
}
|
|
71
36
|
|
|
72
|
-
export const
|
|
37
|
+
export const ConfirmationSelector: React.FC<ConfirmationSelectorProps> = ({
|
|
73
38
|
toolName,
|
|
74
39
|
toolInput,
|
|
75
40
|
suggestedPrefix,
|
|
@@ -86,7 +51,6 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
86
51
|
hasUserInput: false,
|
|
87
52
|
});
|
|
88
53
|
|
|
89
|
-
// Specialized state for AskUserQuestion
|
|
90
54
|
const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0);
|
|
91
55
|
const [selectedOptionIndex, setSelectedOptionIndex] = useState(0);
|
|
92
56
|
const [selectedOptionIndices, setSelectedOptionIndices] = useState<
|
|
@@ -114,7 +78,6 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
114
78
|
};
|
|
115
79
|
|
|
116
80
|
useInput((input, key) => {
|
|
117
|
-
// Handle ESC to cancel and abort
|
|
118
81
|
if (key.escape) {
|
|
119
82
|
onCancel();
|
|
120
83
|
onAbort();
|
|
@@ -123,10 +86,8 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
123
86
|
|
|
124
87
|
if (toolName === ASK_USER_QUESTION_TOOL_NAME) {
|
|
125
88
|
if (!currentQuestion) return;
|
|
126
|
-
|
|
127
89
|
const options = [...currentQuestion.options, { label: "Other" }];
|
|
128
|
-
const isMultiSelect =
|
|
129
|
-
|
|
90
|
+
const isMultiSelect = currentQuestion.multiSelect;
|
|
130
91
|
const isOtherFocused = selectedOptionIndex === options.length - 1;
|
|
131
92
|
|
|
132
93
|
if (key.return) {
|
|
@@ -135,7 +96,6 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
135
96
|
const selectedLabels = Array.from(selectedOptionIndices)
|
|
136
97
|
.filter((i) => i < currentQuestion.options.length)
|
|
137
98
|
.map((i) => currentQuestion.options[i].label);
|
|
138
|
-
|
|
139
99
|
const isOtherChecked = selectedOptionIndices.has(options.length - 1);
|
|
140
100
|
if (isOtherChecked && otherText.trim()) {
|
|
141
101
|
selectedLabels.push(otherText.trim());
|
|
@@ -148,15 +108,12 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
148
108
|
answer = options[selectedOptionIndex].label;
|
|
149
109
|
}
|
|
150
110
|
}
|
|
151
|
-
|
|
152
111
|
if (!answer) return;
|
|
153
|
-
|
|
154
112
|
const newAnswers = {
|
|
155
113
|
...userAnswers,
|
|
156
114
|
[currentQuestion.question]: answer,
|
|
157
115
|
};
|
|
158
116
|
setUserAnswers(newAnswers);
|
|
159
|
-
|
|
160
117
|
if (currentQuestionIndex < questions.length - 1) {
|
|
161
118
|
setCurrentQuestionIndex(currentQuestionIndex + 1);
|
|
162
119
|
setSelectedOptionIndex(0);
|
|
@@ -164,7 +121,6 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
164
121
|
setOtherText("");
|
|
165
122
|
setOtherCursorPosition(0);
|
|
166
123
|
} else {
|
|
167
|
-
// All questions answered
|
|
168
124
|
onDecision({
|
|
169
125
|
behavior: "allow",
|
|
170
126
|
message: JSON.stringify(newAnswers),
|
|
@@ -180,33 +136,23 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
180
136
|
) {
|
|
181
137
|
setSelectedOptionIndices((prev) => {
|
|
182
138
|
const next = new Set(prev);
|
|
183
|
-
if (next.has(selectedOptionIndex))
|
|
184
|
-
|
|
185
|
-
} else {
|
|
186
|
-
next.add(selectedOptionIndex);
|
|
187
|
-
}
|
|
139
|
+
if (next.has(selectedOptionIndex)) next.delete(selectedOptionIndex);
|
|
140
|
+
else next.add(selectedOptionIndex);
|
|
188
141
|
return next;
|
|
189
142
|
});
|
|
190
143
|
return;
|
|
191
144
|
}
|
|
192
|
-
|
|
193
|
-
if (!isOtherFocused) {
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
// If isOtherFocused is true, fall through to handle space as text input
|
|
145
|
+
if (!isOtherFocused) return;
|
|
197
146
|
}
|
|
198
147
|
|
|
199
148
|
if (key.upArrow) {
|
|
200
|
-
if (selectedOptionIndex > 0)
|
|
149
|
+
if (selectedOptionIndex > 0)
|
|
201
150
|
setSelectedOptionIndex(selectedOptionIndex - 1);
|
|
202
|
-
}
|
|
203
151
|
return;
|
|
204
152
|
}
|
|
205
|
-
|
|
206
153
|
if (key.downArrow) {
|
|
207
|
-
if (selectedOptionIndex < options.length - 1)
|
|
154
|
+
if (selectedOptionIndex < options.length - 1)
|
|
208
155
|
setSelectedOptionIndex(selectedOptionIndex + 1);
|
|
209
|
-
}
|
|
210
156
|
return;
|
|
211
157
|
}
|
|
212
158
|
|
|
@@ -223,34 +169,29 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
223
169
|
}
|
|
224
170
|
if (key.backspace || key.delete) {
|
|
225
171
|
if (otherCursorPosition > 0) {
|
|
226
|
-
setOtherText(
|
|
227
|
-
|
|
172
|
+
setOtherText(
|
|
173
|
+
(prev) =>
|
|
228
174
|
prev.slice(0, otherCursorPosition - 1) +
|
|
229
|
-
prev.slice(otherCursorPosition)
|
|
230
|
-
|
|
231
|
-
});
|
|
175
|
+
prev.slice(otherCursorPosition),
|
|
176
|
+
);
|
|
232
177
|
setOtherCursorPosition((prev) => prev - 1);
|
|
233
178
|
}
|
|
234
179
|
return;
|
|
235
180
|
}
|
|
236
181
|
if (input && !key.ctrl && !key.meta) {
|
|
237
|
-
setOtherText(
|
|
238
|
-
|
|
182
|
+
setOtherText(
|
|
183
|
+
(prev) =>
|
|
239
184
|
prev.slice(0, otherCursorPosition) +
|
|
240
185
|
input +
|
|
241
|
-
prev.slice(otherCursorPosition)
|
|
242
|
-
|
|
243
|
-
});
|
|
186
|
+
prev.slice(otherCursorPosition),
|
|
187
|
+
);
|
|
244
188
|
setOtherCursorPosition((prev) => prev + input.length);
|
|
245
189
|
return;
|
|
246
190
|
}
|
|
247
|
-
return;
|
|
248
191
|
}
|
|
249
|
-
|
|
250
192
|
return;
|
|
251
193
|
}
|
|
252
194
|
|
|
253
|
-
// Handle Enter to confirm selection
|
|
254
195
|
if (key.return) {
|
|
255
196
|
if (state.selectedOption === "allow") {
|
|
256
197
|
if (toolName === EXIT_PLAN_MODE_TOOL_NAME) {
|
|
@@ -263,24 +204,12 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
263
204
|
const rule = suggestedPrefix
|
|
264
205
|
? `Bash(${suggestedPrefix}*)`
|
|
265
206
|
: `Bash(${toolInput?.command})`;
|
|
266
|
-
onDecision({
|
|
267
|
-
behavior: "allow",
|
|
268
|
-
newPermissionRule: rule,
|
|
269
|
-
});
|
|
207
|
+
onDecision({ behavior: "allow", newPermissionRule: rule });
|
|
270
208
|
} else {
|
|
271
|
-
onDecision({
|
|
272
|
-
behavior: "allow",
|
|
273
|
-
newPermissionMode: "acceptEdits",
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
} else {
|
|
277
|
-
// For alternative option, require text input
|
|
278
|
-
if (state.alternativeText.trim()) {
|
|
279
|
-
onDecision({
|
|
280
|
-
behavior: "deny",
|
|
281
|
-
message: state.alternativeText.trim(),
|
|
282
|
-
});
|
|
209
|
+
onDecision({ behavior: "allow", newPermissionMode: "acceptEdits" });
|
|
283
210
|
}
|
|
211
|
+
} else if (state.alternativeText.trim()) {
|
|
212
|
+
onDecision({ behavior: "deny", message: state.alternativeText.trim() });
|
|
284
213
|
}
|
|
285
214
|
return;
|
|
286
215
|
}
|
|
@@ -308,15 +237,13 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
308
237
|
}
|
|
309
238
|
}
|
|
310
239
|
|
|
311
|
-
// Handle arrow keys for navigation
|
|
312
240
|
if (key.upArrow) {
|
|
313
241
|
setState((prev) => {
|
|
314
|
-
if (prev.selectedOption === "alternative")
|
|
242
|
+
if (prev.selectedOption === "alternative")
|
|
315
243
|
return {
|
|
316
244
|
...prev,
|
|
317
245
|
selectedOption: hidePersistentOption ? "allow" : "auto",
|
|
318
246
|
};
|
|
319
|
-
}
|
|
320
247
|
if (prev.selectedOption === "auto")
|
|
321
248
|
return { ...prev, selectedOption: "allow" };
|
|
322
249
|
return prev;
|
|
@@ -326,12 +253,11 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
326
253
|
|
|
327
254
|
if (key.downArrow) {
|
|
328
255
|
setState((prev) => {
|
|
329
|
-
if (prev.selectedOption === "allow")
|
|
256
|
+
if (prev.selectedOption === "allow")
|
|
330
257
|
return {
|
|
331
258
|
...prev,
|
|
332
259
|
selectedOption: hidePersistentOption ? "alternative" : "auto",
|
|
333
260
|
};
|
|
334
|
-
}
|
|
335
261
|
if (prev.selectedOption === "auto")
|
|
336
262
|
return { ...prev, selectedOption: "alternative" };
|
|
337
263
|
return prev;
|
|
@@ -339,9 +265,7 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
339
265
|
return;
|
|
340
266
|
}
|
|
341
267
|
|
|
342
|
-
// Handle text input for alternative option
|
|
343
268
|
if (input && !key.ctrl && !key.meta && !("alt" in key && key.alt)) {
|
|
344
|
-
// Focus on alternative option when user starts typing
|
|
345
269
|
setState((prev) => {
|
|
346
270
|
const nextText =
|
|
347
271
|
prev.alternativeText.slice(0, prev.alternativeCursorPosition) +
|
|
@@ -359,7 +283,6 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
359
283
|
return;
|
|
360
284
|
}
|
|
361
285
|
|
|
362
|
-
// Handle backspace and delete
|
|
363
286
|
if (key.backspace || key.delete) {
|
|
364
287
|
setState((prev) => {
|
|
365
288
|
if (prev.alternativeCursorPosition > 0) {
|
|
@@ -385,93 +308,65 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
385
308
|
state.selectedOption === "alternative" && !state.hasUserInput;
|
|
386
309
|
|
|
387
310
|
return (
|
|
388
|
-
<Box
|
|
389
|
-
flexDirection="column"
|
|
390
|
-
borderStyle="single"
|
|
391
|
-
borderColor="yellow"
|
|
392
|
-
borderBottom={false}
|
|
393
|
-
borderLeft={false}
|
|
394
|
-
borderRight={false}
|
|
395
|
-
paddingTop={1}
|
|
396
|
-
>
|
|
397
|
-
<Text color="yellow" bold>
|
|
398
|
-
Tool: {toolName}
|
|
399
|
-
</Text>
|
|
400
|
-
<Text color="yellow">{getActionDescription(toolName, toolInput)}</Text>
|
|
401
|
-
|
|
402
|
-
<DiffDisplay toolName={toolName} parameters={JSON.stringify(toolInput)} />
|
|
403
|
-
|
|
311
|
+
<Box flexDirection="column">
|
|
404
312
|
{toolName === ASK_USER_QUESTION_TOOL_NAME &&
|
|
405
313
|
currentQuestion &&
|
|
406
314
|
!isExpanded && (
|
|
407
315
|
<Box flexDirection="column" marginTop={1}>
|
|
408
316
|
<Box marginBottom={1}>
|
|
409
|
-
<
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
<Text color="black" bold>
|
|
415
|
-
{currentQuestion.header.slice(0, 12).toUpperCase()}
|
|
416
|
-
</Text>
|
|
317
|
+
<Text color={getHeaderColor(currentQuestion.header)} bold>
|
|
318
|
+
{currentQuestion.header.slice(0, 12).toUpperCase()}
|
|
319
|
+
</Text>
|
|
320
|
+
<Box marginLeft={1}>
|
|
321
|
+
<Text bold>{currentQuestion.question}</Text>
|
|
417
322
|
</Box>
|
|
418
|
-
<Text bold>{currentQuestion.question}</Text>
|
|
419
323
|
</Box>
|
|
420
|
-
|
|
421
324
|
<Box flexDirection="column">
|
|
422
|
-
{
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
:{" "}
|
|
452
|
-
{otherText ? (
|
|
453
|
-
<>
|
|
454
|
-
{otherText.slice(0, otherCursorPosition)}
|
|
455
|
-
<Text backgroundColor="white" color="black">
|
|
456
|
-
{otherText[otherCursorPosition] || " "}
|
|
457
|
-
</Text>
|
|
458
|
-
{otherText.slice(otherCursorPosition + 1)}
|
|
459
|
-
</>
|
|
460
|
-
) : (
|
|
461
|
-
<Text color="gray" dimColor>
|
|
462
|
-
[Type your answer...]
|
|
325
|
+
{[...currentQuestion.options, { label: "Other" }].map(
|
|
326
|
+
(option, index) => {
|
|
327
|
+
const isSelected = selectedOptionIndex === index;
|
|
328
|
+
const isChecked = currentQuestion.multiSelect
|
|
329
|
+
? selectedOptionIndices.has(index)
|
|
330
|
+
: isSelected;
|
|
331
|
+
const isOther = index === currentQuestion.options.length;
|
|
332
|
+
return (
|
|
333
|
+
<Box key={index}>
|
|
334
|
+
<Text
|
|
335
|
+
color={isSelected ? "black" : "white"}
|
|
336
|
+
backgroundColor={isSelected ? "yellow" : undefined}
|
|
337
|
+
>
|
|
338
|
+
{isSelected ? "> " : " "}
|
|
339
|
+
{currentQuestion.multiSelect
|
|
340
|
+
? isChecked
|
|
341
|
+
? "[x] "
|
|
342
|
+
: "[ ] "
|
|
343
|
+
: ""}
|
|
344
|
+
{option.label}
|
|
345
|
+
{option.description ? ` - ${option.description}` : ""}
|
|
346
|
+
{isOther && isSelected && (
|
|
347
|
+
<Text>
|
|
348
|
+
:{" "}
|
|
349
|
+
{otherText ? (
|
|
350
|
+
<>
|
|
351
|
+
{otherText.slice(0, otherCursorPosition)}
|
|
352
|
+
<Text backgroundColor="white" color="black">
|
|
353
|
+
{otherText[otherCursorPosition] || " "}
|
|
463
354
|
</Text>
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
355
|
+
{otherText.slice(otherCursorPosition + 1)}
|
|
356
|
+
</>
|
|
357
|
+
) : (
|
|
358
|
+
<Text color="gray" dimColor>
|
|
359
|
+
[Type your answer...]
|
|
360
|
+
</Text>
|
|
361
|
+
)}
|
|
362
|
+
</Text>
|
|
363
|
+
)}
|
|
364
|
+
</Text>
|
|
365
|
+
</Box>
|
|
366
|
+
);
|
|
367
|
+
},
|
|
368
|
+
)}
|
|
473
369
|
</Box>
|
|
474
|
-
|
|
475
370
|
<Box marginTop={1}>
|
|
476
371
|
<Text dimColor>
|
|
477
372
|
Question {currentQuestionIndex + 1} of {questions.length} •
|
|
@@ -482,23 +377,12 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
482
377
|
</Box>
|
|
483
378
|
)}
|
|
484
379
|
|
|
485
|
-
{toolName !== ASK_USER_QUESTION_TOOL_NAME &&
|
|
486
|
-
toolName === EXIT_PLAN_MODE_TOOL_NAME &&
|
|
487
|
-
!!toolInput?.plan_content && (
|
|
488
|
-
<PlanDisplay
|
|
489
|
-
plan={toolInput.plan_content as string}
|
|
490
|
-
isExpanded={isExpanded}
|
|
491
|
-
/>
|
|
492
|
-
)}
|
|
493
|
-
|
|
494
380
|
{toolName !== ASK_USER_QUESTION_TOOL_NAME && !isExpanded && (
|
|
495
381
|
<>
|
|
496
382
|
<Box marginTop={1}>
|
|
497
383
|
<Text>Do you want to proceed?</Text>
|
|
498
384
|
</Box>
|
|
499
|
-
|
|
500
385
|
<Box marginTop={1} flexDirection="column">
|
|
501
|
-
{/* Option 1: Yes */}
|
|
502
386
|
<Box key="allow-option">
|
|
503
387
|
<Text
|
|
504
388
|
color={state.selectedOption === "allow" ? "black" : "white"}
|
|
@@ -513,8 +397,6 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
513
397
|
: "Yes"}
|
|
514
398
|
</Text>
|
|
515
399
|
</Box>
|
|
516
|
-
|
|
517
|
-
{/* Option 2: Auto-accept/Persistent */}
|
|
518
400
|
{!hidePersistentOption && (
|
|
519
401
|
<Box key="auto-option">
|
|
520
402
|
<Text
|
|
@@ -529,8 +411,6 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
529
411
|
</Text>
|
|
530
412
|
</Box>
|
|
531
413
|
)}
|
|
532
|
-
|
|
533
|
-
{/* Option 3: Alternative */}
|
|
534
414
|
<Box key="alternative-option">
|
|
535
415
|
<Text
|
|
536
416
|
color={
|
|
@@ -571,7 +451,6 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
571
451
|
</Text>
|
|
572
452
|
</Box>
|
|
573
453
|
</Box>
|
|
574
|
-
|
|
575
454
|
<Box marginTop={1}>
|
|
576
455
|
<Text dimColor>Use ↑↓ to navigate • ESC to cancel</Text>
|
|
577
456
|
</Box>
|
|
@@ -580,3 +459,5 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
580
459
|
</Box>
|
|
581
460
|
);
|
|
582
461
|
};
|
|
462
|
+
|
|
463
|
+
ConfirmationSelector.displayName = "ConfirmationSelector";
|
|
@@ -109,6 +109,15 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
|
|
|
109
109
|
|
|
110
110
|
changes.forEach((change, changeIndex) => {
|
|
111
111
|
try {
|
|
112
|
+
// Add ellipsis between non-contiguous edits in MultiEdit
|
|
113
|
+
if (toolName === MULTI_EDIT_TOOL_NAME && changeIndex > 0) {
|
|
114
|
+
allElements.push(
|
|
115
|
+
<Box key={`multi-edit-separator-${changeIndex}`}>
|
|
116
|
+
<Text color="gray">...</Text>
|
|
117
|
+
</Box>,
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
112
121
|
// Get line-level diff to understand the structure
|
|
113
122
|
const lineDiffs = diffLines(
|
|
114
123
|
change.oldContent || "",
|
|
@@ -153,7 +162,60 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
|
|
|
153
162
|
const lines = part.value
|
|
154
163
|
.split("\n")
|
|
155
164
|
.filter((line) => line !== "");
|
|
156
|
-
|
|
165
|
+
|
|
166
|
+
const isFirstBlock = partIndex === 0;
|
|
167
|
+
const isLastBlock = partIndex === lineDiffs.length - 1;
|
|
168
|
+
|
|
169
|
+
let linesToDisplay = lines;
|
|
170
|
+
let showEllipsisTop = false;
|
|
171
|
+
let showEllipsisBottom = false;
|
|
172
|
+
|
|
173
|
+
if (isFirstBlock && !isLastBlock) {
|
|
174
|
+
// First block: keep last 3
|
|
175
|
+
if (lines.length > 3) {
|
|
176
|
+
linesToDisplay = lines.slice(-3);
|
|
177
|
+
showEllipsisTop = true;
|
|
178
|
+
}
|
|
179
|
+
} else if (isLastBlock && !isFirstBlock) {
|
|
180
|
+
// Last block: keep first 3
|
|
181
|
+
if (lines.length > 3) {
|
|
182
|
+
linesToDisplay = lines.slice(0, 3);
|
|
183
|
+
showEllipsisBottom = true;
|
|
184
|
+
}
|
|
185
|
+
} else if (!isFirstBlock && !isLastBlock) {
|
|
186
|
+
// Middle block: keep first 3 and last 3
|
|
187
|
+
if (lines.length > 6) {
|
|
188
|
+
linesToDisplay = [...lines.slice(0, 3), ...lines.slice(-3)];
|
|
189
|
+
showEllipsisTop = false; // We'll put ellipsis in the middle
|
|
190
|
+
}
|
|
191
|
+
} else if (isFirstBlock && isLastBlock) {
|
|
192
|
+
// Only one block (no changes?) - keep all or apply a general limit
|
|
193
|
+
// For now, let's keep all if it's the only block
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (showEllipsisTop) {
|
|
197
|
+
diffElements.push(
|
|
198
|
+
<Box key={`ellipsis-top-${changeIndex}-${partIndex}`}>
|
|
199
|
+
<Text color="gray"> ...</Text>
|
|
200
|
+
</Box>,
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
linesToDisplay.forEach((line, lineIndex) => {
|
|
205
|
+
// If it's a middle block and we are at the split point
|
|
206
|
+
if (
|
|
207
|
+
!isFirstBlock &&
|
|
208
|
+
!isLastBlock &&
|
|
209
|
+
lines.length > 6 &&
|
|
210
|
+
lineIndex === 3
|
|
211
|
+
) {
|
|
212
|
+
diffElements.push(
|
|
213
|
+
<Box key={`ellipsis-mid-${changeIndex}-${partIndex}`}>
|
|
214
|
+
<Text color="gray"> ...</Text>
|
|
215
|
+
</Box>,
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
157
219
|
diffElements.push(
|
|
158
220
|
<Box
|
|
159
221
|
key={`context-${changeIndex}-${partIndex}-${lineIndex}`}
|
|
@@ -164,6 +226,14 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
|
|
|
164
226
|
</Box>,
|
|
165
227
|
);
|
|
166
228
|
});
|
|
229
|
+
|
|
230
|
+
if (showEllipsisBottom) {
|
|
231
|
+
diffElements.push(
|
|
232
|
+
<Box key={`ellipsis-bottom-${changeIndex}-${partIndex}`}>
|
|
233
|
+
<Text color="gray"> ...</Text>
|
|
234
|
+
</Box>,
|
|
235
|
+
);
|
|
236
|
+
}
|
|
167
237
|
}
|
|
168
238
|
});
|
|
169
239
|
|
|
@@ -52,7 +52,6 @@ export const FileSelector: React.FC<FileSelectorProps> = ({
|
|
|
52
52
|
borderBottom={false}
|
|
53
53
|
borderLeft={false}
|
|
54
54
|
borderRight={false}
|
|
55
|
-
paddingTop={1}
|
|
56
55
|
>
|
|
57
56
|
<Text color="yellow">📁 No files found for "{searchQuery}"</Text>
|
|
58
57
|
<Text dimColor>Press Escape to cancel</Text>
|
|
@@ -91,7 +90,6 @@ export const FileSelector: React.FC<FileSelectorProps> = ({
|
|
|
91
90
|
borderBottom={false}
|
|
92
91
|
borderLeft={false}
|
|
93
92
|
borderRight={false}
|
|
94
|
-
paddingTop={1}
|
|
95
93
|
>
|
|
96
94
|
<Text color="cyan" bold>
|
|
97
95
|
📁 Select File/Directory{" "}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Box, Text, useInput } from "ink";
|
|
3
|
+
|
|
4
|
+
export interface HelpViewProps {
|
|
5
|
+
onCancel: () => void;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const HelpView: React.FC<HelpViewProps> = ({ onCancel }) => {
|
|
9
|
+
useInput((_, key) => {
|
|
10
|
+
if (key.escape || key.return) {
|
|
11
|
+
onCancel();
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const helpItems = [
|
|
16
|
+
{ key: "@", description: "Reference files" },
|
|
17
|
+
{ key: "/", description: "Commands" },
|
|
18
|
+
{ key: "Ctrl+R", description: "Search history" },
|
|
19
|
+
{ key: "Ctrl+O", description: "Expand/collapse messages" },
|
|
20
|
+
{ key: "Ctrl+T", description: "Toggle task list" },
|
|
21
|
+
{ key: "Ctrl+B", description: "Background current task" },
|
|
22
|
+
{ key: "Ctrl+V", description: "Paste image" },
|
|
23
|
+
{ key: "Shift+Tab", description: "Cycle permission mode" },
|
|
24
|
+
{
|
|
25
|
+
key: "Esc",
|
|
26
|
+
description: "Interrupt AI or command / Cancel selector / Close help",
|
|
27
|
+
},
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<Box
|
|
32
|
+
flexDirection="column"
|
|
33
|
+
borderStyle="single"
|
|
34
|
+
borderColor="cyan"
|
|
35
|
+
borderLeft={false}
|
|
36
|
+
borderRight={false}
|
|
37
|
+
paddingX={1}
|
|
38
|
+
>
|
|
39
|
+
<Box marginBottom={1}>
|
|
40
|
+
<Text color="cyan" bold underline>
|
|
41
|
+
Help & Key Bindings
|
|
42
|
+
</Text>
|
|
43
|
+
</Box>
|
|
44
|
+
|
|
45
|
+
{helpItems.map((item, index) => (
|
|
46
|
+
<Box key={index}>
|
|
47
|
+
<Box width={20}>
|
|
48
|
+
<Text color="yellow">{item.key}</Text>
|
|
49
|
+
</Box>
|
|
50
|
+
<Text color="white">{item.description}</Text>
|
|
51
|
+
</Box>
|
|
52
|
+
))}
|
|
53
|
+
|
|
54
|
+
<Box marginTop={1}>
|
|
55
|
+
<Text dimColor>Press Esc or Enter to close</Text>
|
|
56
|
+
</Box>
|
|
57
|
+
</Box>
|
|
58
|
+
);
|
|
59
|
+
};
|