wave-code 0.4.0 → 0.6.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/commands/plugin/uninstall.js +1 -1
- package/dist/components/App.d.ts.map +1 -1
- package/dist/components/App.js +38 -2
- package/dist/components/BackgroundTaskManager.d.ts +6 -0
- package/dist/components/BackgroundTaskManager.d.ts.map +1 -0
- package/dist/components/BackgroundTaskManager.js +114 -0
- package/dist/components/ChatInterface.d.ts.map +1 -1
- package/dist/components/ChatInterface.js +39 -5
- package/dist/components/CommandSelector.d.ts.map +1 -1
- package/dist/components/CommandSelector.js +13 -5
- 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} +92 -101
- package/dist/components/DiffDisplay.d.ts +0 -1
- package/dist/components/DiffDisplay.d.ts.map +1 -1
- package/dist/components/DiffDisplay.js +82 -60
- package/dist/components/FileSelector.d.ts.map +1 -1
- package/dist/components/FileSelector.js +2 -2
- 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 +9 -18
- 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 -120
- package/dist/components/MessageItem.d.ts.map +1 -1
- package/dist/components/MessageItem.js +1 -2
- package/dist/components/MessageList.d.ts +2 -3
- package/dist/components/MessageList.d.ts.map +1 -1
- package/dist/components/MessageList.js +7 -7
- package/dist/components/PlanDisplay.d.ts.map +1 -1
- package/dist/components/PlanDisplay.js +4 -12
- package/dist/components/PluginDetail.js +1 -1
- package/dist/components/RewindCommand.d.ts +4 -0
- package/dist/components/RewindCommand.d.ts.map +1 -1
- package/dist/components/RewindCommand.js +19 -2
- package/dist/components/SubagentBlock.d.ts.map +1 -1
- package/dist/components/SubagentBlock.js +12 -5
- package/dist/components/TaskList.d.ts +3 -0
- package/dist/components/TaskList.d.ts.map +1 -0
- package/dist/components/TaskList.js +49 -0
- package/dist/components/ToolResultDisplay.d.ts.map +1 -1
- package/dist/components/ToolResultDisplay.js +2 -1
- package/dist/contexts/useChat.d.ts +15 -6
- package/dist/contexts/useChat.d.ts.map +1 -1
- package/dist/contexts/useChat.js +52 -43
- package/dist/hooks/useInputManager.d.ts +2 -13
- package/dist/hooks/useInputManager.d.ts.map +1 -1
- package/dist/hooks/useInputManager.js +8 -57
- package/dist/hooks/usePluginManager.d.ts.map +1 -1
- package/dist/hooks/usePluginManager.js +8 -4
- 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 +5 -28
- package/dist/managers/InputManager.d.ts.map +1 -1
- package/dist/managers/InputManager.js +26 -127
- package/package.json +9 -10
- package/src/commands/plugin/uninstall.ts +1 -1
- package/src/components/App.tsx +50 -3
- package/src/components/{BashShellManager.tsx → BackgroundTaskManager.tsx} +79 -73
- package/src/components/ChatInterface.tsx +79 -23
- package/src/components/CommandSelector.tsx +38 -20
- package/src/components/CompressDisplay.tsx +5 -22
- package/src/components/ConfirmationDetails.tsx +108 -0
- package/src/components/{Confirmation.tsx → ConfirmationSelector.tsx} +162 -187
- package/src/components/DiffDisplay.tsx +122 -107
- package/src/components/FileSelector.tsx +0 -2
- package/src/components/HistorySearch.tsx +45 -21
- package/src/components/InputBox.tsx +14 -34
- package/src/components/LoadingIndicator.tsx +56 -0
- package/src/components/Markdown.tsx +126 -318
- package/src/components/MessageItem.tsx +1 -3
- package/src/components/MessageList.tsx +10 -67
- package/src/components/PlanDisplay.tsx +5 -33
- package/src/components/PluginDetail.tsx +1 -1
- package/src/components/RewindCommand.tsx +38 -1
- package/src/components/SubagentBlock.tsx +28 -14
- package/src/components/TaskList.tsx +70 -0
- package/src/components/ToolResultDisplay.tsx +6 -2
- package/src/contexts/useChat.tsx +82 -60
- package/src/hooks/useInputManager.ts +9 -73
- package/src/hooks/usePluginManager.ts +10 -4
- package/src/hooks/useTasks.ts +6 -0
- package/src/managers/InputManager.ts +30 -157
- package/dist/components/BashShellManager.d.ts +0 -6
- package/dist/components/BashShellManager.d.ts.map +0 -1
- package/dist/components/BashShellManager.js +0 -116
- 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/src/components/MemoryDisplay.tsx +0 -62
- package/src/components/MemoryTypeSelector.tsx +0 -98
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import React, { useLayoutEffect, useRef, useState } from "react";
|
|
2
|
+
import { Box, Text, useStdout, measureElement, Static } from "ink";
|
|
3
|
+
import {
|
|
4
|
+
BASH_TOOL_NAME,
|
|
5
|
+
EDIT_TOOL_NAME,
|
|
6
|
+
MULTI_EDIT_TOOL_NAME,
|
|
7
|
+
DELETE_FILE_TOOL_NAME,
|
|
8
|
+
WRITE_TOOL_NAME,
|
|
9
|
+
EXIT_PLAN_MODE_TOOL_NAME,
|
|
10
|
+
ASK_USER_QUESTION_TOOL_NAME,
|
|
11
|
+
} from "wave-agent-sdk";
|
|
12
|
+
import { DiffDisplay } from "./DiffDisplay.js";
|
|
13
|
+
import { PlanDisplay } from "./PlanDisplay.js";
|
|
14
|
+
|
|
15
|
+
// Helper function to generate descriptive action text
|
|
16
|
+
const getActionDescription = (
|
|
17
|
+
toolName: string,
|
|
18
|
+
toolInput?: Record<string, unknown>,
|
|
19
|
+
): string => {
|
|
20
|
+
if (!toolInput) {
|
|
21
|
+
return "Execute operation";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
switch (toolName) {
|
|
25
|
+
case BASH_TOOL_NAME:
|
|
26
|
+
return `Execute command: ${toolInput.command || "unknown command"}`;
|
|
27
|
+
case EDIT_TOOL_NAME:
|
|
28
|
+
return `Edit file: ${toolInput.file_path || "unknown file"}`;
|
|
29
|
+
case MULTI_EDIT_TOOL_NAME:
|
|
30
|
+
return `Edit multiple sections in: ${toolInput.file_path || "unknown file"}`;
|
|
31
|
+
case DELETE_FILE_TOOL_NAME:
|
|
32
|
+
return `Delete file: ${toolInput.target_file || "unknown file"}`;
|
|
33
|
+
case WRITE_TOOL_NAME:
|
|
34
|
+
return `Write to file: ${toolInput.file_path || "unknown file"}`;
|
|
35
|
+
case EXIT_PLAN_MODE_TOOL_NAME:
|
|
36
|
+
return "Review and approve the plan";
|
|
37
|
+
case ASK_USER_QUESTION_TOOL_NAME:
|
|
38
|
+
return "Answer questions to clarify intent";
|
|
39
|
+
default:
|
|
40
|
+
return "Execute operation";
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export interface ConfirmationDetailsProps {
|
|
45
|
+
toolName: string;
|
|
46
|
+
toolInput?: Record<string, unknown>;
|
|
47
|
+
isExpanded?: boolean;
|
|
48
|
+
onHeightMeasured?: (height: number) => void;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const ConfirmationDetails: React.FC<ConfirmationDetailsProps> = ({
|
|
52
|
+
toolName,
|
|
53
|
+
toolInput,
|
|
54
|
+
isExpanded = false,
|
|
55
|
+
onHeightMeasured,
|
|
56
|
+
}) => {
|
|
57
|
+
const { stdout } = useStdout();
|
|
58
|
+
const [isStatic, setIsStatic] = useState(false);
|
|
59
|
+
const boxRef = useRef(null);
|
|
60
|
+
|
|
61
|
+
useLayoutEffect(() => {
|
|
62
|
+
if (boxRef.current) {
|
|
63
|
+
const { height } = measureElement(boxRef.current);
|
|
64
|
+
const terminalHeight = stdout?.rows || 24;
|
|
65
|
+
if (height > terminalHeight - 10) {
|
|
66
|
+
setIsStatic(true);
|
|
67
|
+
}
|
|
68
|
+
onHeightMeasured?.(height);
|
|
69
|
+
}
|
|
70
|
+
}, [stdout?.rows, onHeightMeasured]);
|
|
71
|
+
|
|
72
|
+
const content = (
|
|
73
|
+
<Box
|
|
74
|
+
ref={boxRef}
|
|
75
|
+
flexDirection="column"
|
|
76
|
+
borderStyle="single"
|
|
77
|
+
borderColor="yellow"
|
|
78
|
+
borderBottom={false}
|
|
79
|
+
borderLeft={false}
|
|
80
|
+
borderRight={false}
|
|
81
|
+
paddingTop={1}
|
|
82
|
+
>
|
|
83
|
+
<Text color="yellow" bold>
|
|
84
|
+
Tool: {toolName}
|
|
85
|
+
</Text>
|
|
86
|
+
<Text color="yellow">{getActionDescription(toolName, toolInput)}</Text>
|
|
87
|
+
|
|
88
|
+
<DiffDisplay toolName={toolName} parameters={JSON.stringify(toolInput)} />
|
|
89
|
+
|
|
90
|
+
{toolName !== ASK_USER_QUESTION_TOOL_NAME &&
|
|
91
|
+
toolName === EXIT_PLAN_MODE_TOOL_NAME &&
|
|
92
|
+
!!toolInput?.plan_content && (
|
|
93
|
+
<PlanDisplay
|
|
94
|
+
plan={toolInput.plan_content as string}
|
|
95
|
+
isExpanded={isExpanded}
|
|
96
|
+
/>
|
|
97
|
+
)}
|
|
98
|
+
</Box>
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
if (isStatic) {
|
|
102
|
+
return <Static items={[1]}>{() => content}</Static>;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return content;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
ConfirmationDetails.displayName = "ConfirmationDetails";
|
|
@@ -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;
|
|
@@ -65,10 +30,11 @@ export interface ConfirmationProps {
|
|
|
65
30
|
interface ConfirmationState {
|
|
66
31
|
selectedOption: "allow" | "auto" | "alternative";
|
|
67
32
|
alternativeText: string;
|
|
68
|
-
|
|
33
|
+
alternativeCursorPosition: number;
|
|
34
|
+
hasUserInput: boolean;
|
|
69
35
|
}
|
|
70
36
|
|
|
71
|
-
export const
|
|
37
|
+
export const ConfirmationSelector: React.FC<ConfirmationSelectorProps> = ({
|
|
72
38
|
toolName,
|
|
73
39
|
toolInput,
|
|
74
40
|
suggestedPrefix,
|
|
@@ -81,10 +47,10 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
81
47
|
const [state, setState] = useState<ConfirmationState>({
|
|
82
48
|
selectedOption: "allow",
|
|
83
49
|
alternativeText: "",
|
|
50
|
+
alternativeCursorPosition: 0,
|
|
84
51
|
hasUserInput: false,
|
|
85
52
|
});
|
|
86
53
|
|
|
87
|
-
// Specialized state for AskUserQuestion
|
|
88
54
|
const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0);
|
|
89
55
|
const [selectedOptionIndex, setSelectedOptionIndex] = useState(0);
|
|
90
56
|
const [selectedOptionIndices, setSelectedOptionIndices] = useState<
|
|
@@ -92,6 +58,7 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
92
58
|
>(new Set());
|
|
93
59
|
const [userAnswers, setUserAnswers] = useState<Record<string, string>>({});
|
|
94
60
|
const [otherText, setOtherText] = useState("");
|
|
61
|
+
const [otherCursorPosition, setOtherCursorPosition] = useState(0);
|
|
95
62
|
|
|
96
63
|
const questions =
|
|
97
64
|
(toolInput as unknown as AskUserQuestionInput)?.questions || [];
|
|
@@ -111,7 +78,6 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
111
78
|
};
|
|
112
79
|
|
|
113
80
|
useInput((input, key) => {
|
|
114
|
-
// Handle ESC to cancel and abort
|
|
115
81
|
if (key.escape) {
|
|
116
82
|
onCancel();
|
|
117
83
|
onAbort();
|
|
@@ -120,10 +86,8 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
120
86
|
|
|
121
87
|
if (toolName === ASK_USER_QUESTION_TOOL_NAME) {
|
|
122
88
|
if (!currentQuestion) return;
|
|
123
|
-
|
|
124
89
|
const options = [...currentQuestion.options, { label: "Other" }];
|
|
125
|
-
const isMultiSelect =
|
|
126
|
-
|
|
90
|
+
const isMultiSelect = currentQuestion.multiSelect;
|
|
127
91
|
const isOtherFocused = selectedOptionIndex === options.length - 1;
|
|
128
92
|
|
|
129
93
|
if (key.return) {
|
|
@@ -132,7 +96,6 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
132
96
|
const selectedLabels = Array.from(selectedOptionIndices)
|
|
133
97
|
.filter((i) => i < currentQuestion.options.length)
|
|
134
98
|
.map((i) => currentQuestion.options[i].label);
|
|
135
|
-
|
|
136
99
|
const isOtherChecked = selectedOptionIndices.has(options.length - 1);
|
|
137
100
|
if (isOtherChecked && otherText.trim()) {
|
|
138
101
|
selectedLabels.push(otherText.trim());
|
|
@@ -145,22 +108,19 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
145
108
|
answer = options[selectedOptionIndex].label;
|
|
146
109
|
}
|
|
147
110
|
}
|
|
148
|
-
|
|
149
111
|
if (!answer) return;
|
|
150
|
-
|
|
151
112
|
const newAnswers = {
|
|
152
113
|
...userAnswers,
|
|
153
114
|
[currentQuestion.question]: answer,
|
|
154
115
|
};
|
|
155
116
|
setUserAnswers(newAnswers);
|
|
156
|
-
|
|
157
117
|
if (currentQuestionIndex < questions.length - 1) {
|
|
158
118
|
setCurrentQuestionIndex(currentQuestionIndex + 1);
|
|
159
119
|
setSelectedOptionIndex(0);
|
|
160
120
|
setSelectedOptionIndices(new Set());
|
|
161
121
|
setOtherText("");
|
|
122
|
+
setOtherCursorPosition(0);
|
|
162
123
|
} else {
|
|
163
|
-
// All questions answered
|
|
164
124
|
onDecision({
|
|
165
125
|
behavior: "allow",
|
|
166
126
|
message: JSON.stringify(newAnswers),
|
|
@@ -176,49 +136,62 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
176
136
|
) {
|
|
177
137
|
setSelectedOptionIndices((prev) => {
|
|
178
138
|
const next = new Set(prev);
|
|
179
|
-
if (next.has(selectedOptionIndex))
|
|
180
|
-
|
|
181
|
-
} else {
|
|
182
|
-
next.add(selectedOptionIndex);
|
|
183
|
-
}
|
|
139
|
+
if (next.has(selectedOptionIndex)) next.delete(selectedOptionIndex);
|
|
140
|
+
else next.add(selectedOptionIndex);
|
|
184
141
|
return next;
|
|
185
142
|
});
|
|
186
143
|
return;
|
|
187
144
|
}
|
|
188
|
-
|
|
189
|
-
if (!isOtherFocused) {
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
// If isOtherFocused is true, fall through to handle space as text input
|
|
145
|
+
if (!isOtherFocused) return;
|
|
193
146
|
}
|
|
194
147
|
|
|
195
148
|
if (key.upArrow) {
|
|
196
|
-
if (selectedOptionIndex > 0)
|
|
149
|
+
if (selectedOptionIndex > 0)
|
|
197
150
|
setSelectedOptionIndex(selectedOptionIndex - 1);
|
|
198
|
-
}
|
|
199
151
|
return;
|
|
200
152
|
}
|
|
201
|
-
|
|
202
153
|
if (key.downArrow) {
|
|
203
|
-
if (selectedOptionIndex < options.length - 1)
|
|
154
|
+
if (selectedOptionIndex < options.length - 1)
|
|
204
155
|
setSelectedOptionIndex(selectedOptionIndex + 1);
|
|
205
|
-
}
|
|
206
156
|
return;
|
|
207
157
|
}
|
|
208
158
|
|
|
209
159
|
if (isOtherFocused) {
|
|
160
|
+
if (key.leftArrow) {
|
|
161
|
+
setOtherCursorPosition((prev) => Math.max(0, prev - 1));
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (key.rightArrow) {
|
|
165
|
+
setOtherCursorPosition((prev) =>
|
|
166
|
+
Math.min(otherText.length, prev + 1),
|
|
167
|
+
);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
210
170
|
if (key.backspace || key.delete) {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
171
|
+
if (otherCursorPosition > 0) {
|
|
172
|
+
setOtherText(
|
|
173
|
+
(prev) =>
|
|
174
|
+
prev.slice(0, otherCursorPosition - 1) +
|
|
175
|
+
prev.slice(otherCursorPosition),
|
|
176
|
+
);
|
|
177
|
+
setOtherCursorPosition((prev) => prev - 1);
|
|
178
|
+
}
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (input && !key.ctrl && !key.meta) {
|
|
182
|
+
setOtherText(
|
|
183
|
+
(prev) =>
|
|
184
|
+
prev.slice(0, otherCursorPosition) +
|
|
185
|
+
input +
|
|
186
|
+
prev.slice(otherCursorPosition),
|
|
187
|
+
);
|
|
188
|
+
setOtherCursorPosition((prev) => prev + input.length);
|
|
189
|
+
return;
|
|
214
190
|
}
|
|
215
|
-
return;
|
|
216
191
|
}
|
|
217
|
-
|
|
218
192
|
return;
|
|
219
193
|
}
|
|
220
194
|
|
|
221
|
-
// Handle Enter to confirm selection
|
|
222
195
|
if (key.return) {
|
|
223
196
|
if (state.selectedOption === "allow") {
|
|
224
197
|
if (toolName === EXIT_PLAN_MODE_TOOL_NAME) {
|
|
@@ -231,37 +204,46 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
231
204
|
const rule = suggestedPrefix
|
|
232
205
|
? `Bash(${suggestedPrefix}*)`
|
|
233
206
|
: `Bash(${toolInput?.command})`;
|
|
234
|
-
onDecision({
|
|
235
|
-
behavior: "allow",
|
|
236
|
-
newPermissionRule: rule,
|
|
237
|
-
});
|
|
207
|
+
onDecision({ behavior: "allow", newPermissionRule: rule });
|
|
238
208
|
} else {
|
|
239
|
-
onDecision({
|
|
240
|
-
behavior: "allow",
|
|
241
|
-
newPermissionMode: "acceptEdits",
|
|
242
|
-
});
|
|
243
|
-
}
|
|
244
|
-
} else {
|
|
245
|
-
// For alternative option, require text input
|
|
246
|
-
if (state.alternativeText.trim()) {
|
|
247
|
-
onDecision({
|
|
248
|
-
behavior: "deny",
|
|
249
|
-
message: state.alternativeText.trim(),
|
|
250
|
-
});
|
|
209
|
+
onDecision({ behavior: "allow", newPermissionMode: "acceptEdits" });
|
|
251
210
|
}
|
|
211
|
+
} else if (state.alternativeText.trim()) {
|
|
212
|
+
onDecision({ behavior: "deny", message: state.alternativeText.trim() });
|
|
252
213
|
}
|
|
253
214
|
return;
|
|
254
215
|
}
|
|
255
216
|
|
|
256
|
-
|
|
217
|
+
if (state.selectedOption === "alternative") {
|
|
218
|
+
if (key.leftArrow) {
|
|
219
|
+
setState((prev) => ({
|
|
220
|
+
...prev,
|
|
221
|
+
alternativeCursorPosition: Math.max(
|
|
222
|
+
0,
|
|
223
|
+
prev.alternativeCursorPosition - 1,
|
|
224
|
+
),
|
|
225
|
+
}));
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
if (key.rightArrow) {
|
|
229
|
+
setState((prev) => ({
|
|
230
|
+
...prev,
|
|
231
|
+
alternativeCursorPosition: Math.min(
|
|
232
|
+
prev.alternativeText.length,
|
|
233
|
+
prev.alternativeCursorPosition + 1,
|
|
234
|
+
),
|
|
235
|
+
}));
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
257
240
|
if (key.upArrow) {
|
|
258
241
|
setState((prev) => {
|
|
259
|
-
if (prev.selectedOption === "alternative")
|
|
242
|
+
if (prev.selectedOption === "alternative")
|
|
260
243
|
return {
|
|
261
244
|
...prev,
|
|
262
245
|
selectedOption: hidePersistentOption ? "allow" : "auto",
|
|
263
246
|
};
|
|
264
|
-
}
|
|
265
247
|
if (prev.selectedOption === "auto")
|
|
266
248
|
return { ...prev, selectedOption: "allow" };
|
|
267
249
|
return prev;
|
|
@@ -271,12 +253,11 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
271
253
|
|
|
272
254
|
if (key.downArrow) {
|
|
273
255
|
setState((prev) => {
|
|
274
|
-
if (prev.selectedOption === "allow")
|
|
256
|
+
if (prev.selectedOption === "allow")
|
|
275
257
|
return {
|
|
276
258
|
...prev,
|
|
277
259
|
selectedOption: hidePersistentOption ? "alternative" : "auto",
|
|
278
260
|
};
|
|
279
|
-
}
|
|
280
261
|
if (prev.selectedOption === "auto")
|
|
281
262
|
return { ...prev, selectedOption: "alternative" };
|
|
282
263
|
return prev;
|
|
@@ -284,30 +265,42 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
284
265
|
return;
|
|
285
266
|
}
|
|
286
267
|
|
|
287
|
-
// Handle text input for alternative option
|
|
288
268
|
if (input && !key.ctrl && !key.meta && !("alt" in key && key.alt)) {
|
|
289
|
-
// Focus on alternative option when user starts typing
|
|
290
|
-
setState((prev) => ({
|
|
291
|
-
selectedOption: "alternative",
|
|
292
|
-
alternativeText: prev.alternativeText + input,
|
|
293
|
-
hasUserInput: true,
|
|
294
|
-
}));
|
|
295
|
-
return;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// Handle backspace and delete (same behavior - delete one character)
|
|
299
|
-
if (key.backspace || key.delete) {
|
|
300
269
|
setState((prev) => {
|
|
301
|
-
const
|
|
270
|
+
const nextText =
|
|
271
|
+
prev.alternativeText.slice(0, prev.alternativeCursorPosition) +
|
|
272
|
+
input +
|
|
273
|
+
prev.alternativeText.slice(prev.alternativeCursorPosition);
|
|
302
274
|
return {
|
|
303
275
|
...prev,
|
|
304
276
|
selectedOption: "alternative",
|
|
305
|
-
alternativeText:
|
|
306
|
-
|
|
277
|
+
alternativeText: nextText,
|
|
278
|
+
alternativeCursorPosition:
|
|
279
|
+
prev.alternativeCursorPosition + input.length,
|
|
280
|
+
hasUserInput: true,
|
|
307
281
|
};
|
|
308
282
|
});
|
|
309
283
|
return;
|
|
310
284
|
}
|
|
285
|
+
|
|
286
|
+
if (key.backspace || key.delete) {
|
|
287
|
+
setState((prev) => {
|
|
288
|
+
if (prev.alternativeCursorPosition > 0) {
|
|
289
|
+
const nextText =
|
|
290
|
+
prev.alternativeText.slice(0, prev.alternativeCursorPosition - 1) +
|
|
291
|
+
prev.alternativeText.slice(prev.alternativeCursorPosition);
|
|
292
|
+
return {
|
|
293
|
+
...prev,
|
|
294
|
+
selectedOption: "alternative",
|
|
295
|
+
alternativeText: nextText,
|
|
296
|
+
alternativeCursorPosition: prev.alternativeCursorPosition - 1,
|
|
297
|
+
hasUserInput: nextText.length > 0,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
return prev;
|
|
301
|
+
});
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
311
304
|
});
|
|
312
305
|
|
|
313
306
|
const placeholderText = "Type here to tell Wave what to do differently";
|
|
@@ -315,26 +308,7 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
315
308
|
state.selectedOption === "alternative" && !state.hasUserInput;
|
|
316
309
|
|
|
317
310
|
return (
|
|
318
|
-
<Box
|
|
319
|
-
flexDirection="column"
|
|
320
|
-
borderStyle="single"
|
|
321
|
-
borderColor="yellow"
|
|
322
|
-
borderBottom={false}
|
|
323
|
-
borderLeft={false}
|
|
324
|
-
borderRight={false}
|
|
325
|
-
paddingTop={1}
|
|
326
|
-
>
|
|
327
|
-
<Text color="yellow" bold>
|
|
328
|
-
Tool: {toolName}
|
|
329
|
-
</Text>
|
|
330
|
-
<Text color="yellow">{getActionDescription(toolName, toolInput)}</Text>
|
|
331
|
-
|
|
332
|
-
<DiffDisplay
|
|
333
|
-
toolName={toolName}
|
|
334
|
-
parameters={JSON.stringify(toolInput)}
|
|
335
|
-
isExpanded={isExpanded}
|
|
336
|
-
/>
|
|
337
|
-
|
|
311
|
+
<Box flexDirection="column">
|
|
338
312
|
{toolName === ASK_USER_QUESTION_TOOL_NAME &&
|
|
339
313
|
currentQuestion &&
|
|
340
314
|
!isExpanded && (
|
|
@@ -351,53 +325,52 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
351
325
|
</Box>
|
|
352
326
|
<Text bold>{currentQuestion.question}</Text>
|
|
353
327
|
</Box>
|
|
354
|
-
|
|
355
328
|
<Box flexDirection="column">
|
|
356
|
-
{
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
:{" "}
|
|
386
|
-
{otherText || (
|
|
387
|
-
<Text color="gray" dimColor>
|
|
388
|
-
[Type your answer...]
|
|
329
|
+
{[...currentQuestion.options, { label: "Other" }].map(
|
|
330
|
+
(option, index) => {
|
|
331
|
+
const isSelected = selectedOptionIndex === index;
|
|
332
|
+
const isChecked = currentQuestion.multiSelect
|
|
333
|
+
? selectedOptionIndices.has(index)
|
|
334
|
+
: isSelected;
|
|
335
|
+
const isOther = index === currentQuestion.options.length;
|
|
336
|
+
return (
|
|
337
|
+
<Box key={index}>
|
|
338
|
+
<Text
|
|
339
|
+
color={isSelected ? "black" : "white"}
|
|
340
|
+
backgroundColor={isSelected ? "yellow" : undefined}
|
|
341
|
+
>
|
|
342
|
+
{isSelected ? "> " : " "}
|
|
343
|
+
{currentQuestion.multiSelect
|
|
344
|
+
? isChecked
|
|
345
|
+
? "[x] "
|
|
346
|
+
: "[ ] "
|
|
347
|
+
: ""}
|
|
348
|
+
{option.label}
|
|
349
|
+
{option.description ? ` - ${option.description}` : ""}
|
|
350
|
+
{isOther && isSelected && (
|
|
351
|
+
<Text>
|
|
352
|
+
:{" "}
|
|
353
|
+
{otherText ? (
|
|
354
|
+
<>
|
|
355
|
+
{otherText.slice(0, otherCursorPosition)}
|
|
356
|
+
<Text backgroundColor="white" color="black">
|
|
357
|
+
{otherText[otherCursorPosition] || " "}
|
|
389
358
|
</Text>
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
359
|
+
{otherText.slice(otherCursorPosition + 1)}
|
|
360
|
+
</>
|
|
361
|
+
) : (
|
|
362
|
+
<Text color="gray" dimColor>
|
|
363
|
+
[Type your answer...]
|
|
364
|
+
</Text>
|
|
365
|
+
)}
|
|
366
|
+
</Text>
|
|
367
|
+
)}
|
|
368
|
+
</Text>
|
|
369
|
+
</Box>
|
|
370
|
+
);
|
|
371
|
+
},
|
|
372
|
+
)}
|
|
399
373
|
</Box>
|
|
400
|
-
|
|
401
374
|
<Box marginTop={1}>
|
|
402
375
|
<Text dimColor>
|
|
403
376
|
Question {currentQuestionIndex + 1} of {questions.length} •
|
|
@@ -408,23 +381,12 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
408
381
|
</Box>
|
|
409
382
|
)}
|
|
410
383
|
|
|
411
|
-
{toolName !== ASK_USER_QUESTION_TOOL_NAME &&
|
|
412
|
-
toolName === EXIT_PLAN_MODE_TOOL_NAME &&
|
|
413
|
-
!!toolInput?.plan_content && (
|
|
414
|
-
<PlanDisplay
|
|
415
|
-
plan={toolInput.plan_content as string}
|
|
416
|
-
isExpanded={isExpanded}
|
|
417
|
-
/>
|
|
418
|
-
)}
|
|
419
|
-
|
|
420
384
|
{toolName !== ASK_USER_QUESTION_TOOL_NAME && !isExpanded && (
|
|
421
385
|
<>
|
|
422
386
|
<Box marginTop={1}>
|
|
423
387
|
<Text>Do you want to proceed?</Text>
|
|
424
388
|
</Box>
|
|
425
|
-
|
|
426
389
|
<Box marginTop={1} flexDirection="column">
|
|
427
|
-
{/* Option 1: Yes */}
|
|
428
390
|
<Box key="allow-option">
|
|
429
391
|
<Text
|
|
430
392
|
color={state.selectedOption === "allow" ? "black" : "white"}
|
|
@@ -439,8 +401,6 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
439
401
|
: "Yes"}
|
|
440
402
|
</Text>
|
|
441
403
|
</Box>
|
|
442
|
-
|
|
443
|
-
{/* Option 2: Auto-accept/Persistent */}
|
|
444
404
|
{!hidePersistentOption && (
|
|
445
405
|
<Box key="auto-option">
|
|
446
406
|
<Text
|
|
@@ -455,8 +415,6 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
455
415
|
</Text>
|
|
456
416
|
</Box>
|
|
457
417
|
)}
|
|
458
|
-
|
|
459
|
-
{/* Option 3: Alternative */}
|
|
460
418
|
<Box key="alternative-option">
|
|
461
419
|
<Text
|
|
462
420
|
color={
|
|
@@ -474,14 +432,29 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
474
432
|
</Text>
|
|
475
433
|
) : (
|
|
476
434
|
<Text>
|
|
477
|
-
{state.alternativeText
|
|
478
|
-
|
|
435
|
+
{state.alternativeText ? (
|
|
436
|
+
<>
|
|
437
|
+
{state.alternativeText.slice(
|
|
438
|
+
0,
|
|
439
|
+
state.alternativeCursorPosition,
|
|
440
|
+
)}
|
|
441
|
+
<Text backgroundColor="white" color="black">
|
|
442
|
+
{state.alternativeText[
|
|
443
|
+
state.alternativeCursorPosition
|
|
444
|
+
] || " "}
|
|
445
|
+
</Text>
|
|
446
|
+
{state.alternativeText.slice(
|
|
447
|
+
state.alternativeCursorPosition + 1,
|
|
448
|
+
)}
|
|
449
|
+
</>
|
|
450
|
+
) : (
|
|
451
|
+
"Type here to tell Wave what to do differently"
|
|
452
|
+
)}
|
|
479
453
|
</Text>
|
|
480
454
|
)}
|
|
481
455
|
</Text>
|
|
482
456
|
</Box>
|
|
483
457
|
</Box>
|
|
484
|
-
|
|
485
458
|
<Box marginTop={1}>
|
|
486
459
|
<Text dimColor>Use ↑↓ to navigate • ESC to cancel</Text>
|
|
487
460
|
</Box>
|
|
@@ -490,3 +463,5 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
490
463
|
</Box>
|
|
491
464
|
);
|
|
492
465
|
};
|
|
466
|
+
|
|
467
|
+
ConfirmationSelector.displayName = "ConfirmationSelector";
|