wave-code 0.6.5 → 0.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.d.ts +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/commands/plugin/disable.d.ts.map +1 -1
- package/dist/commands/plugin/disable.js +3 -10
- package/dist/commands/plugin/enable.d.ts.map +1 -1
- package/dist/commands/plugin/enable.js +3 -10
- package/dist/commands/plugin/install.d.ts.map +1 -1
- package/dist/commands/plugin/install.js +4 -11
- package/dist/commands/plugin/list.d.ts.map +1 -1
- package/dist/commands/plugin/list.js +5 -39
- package/dist/commands/plugin/marketplace.js +9 -9
- package/dist/commands/plugin/uninstall.d.ts.map +1 -1
- package/dist/commands/plugin/uninstall.js +4 -17
- package/dist/commands/plugin/update.js +3 -3
- package/dist/components/App.d.ts +1 -0
- package/dist/components/App.d.ts.map +1 -1
- package/dist/components/App.js +4 -4
- package/dist/components/BackgroundTaskManager.d.ts.map +1 -1
- package/dist/components/BackgroundTaskManager.js +34 -18
- package/dist/components/ChatInterface.d.ts.map +1 -1
- package/dist/components/ChatInterface.js +28 -15
- package/dist/components/ConfirmationDetails.d.ts +1 -0
- package/dist/components/ConfirmationDetails.d.ts.map +1 -1
- package/dist/components/ConfirmationDetails.js +7 -14
- package/dist/components/ConfirmationSelector.d.ts +1 -0
- package/dist/components/ConfirmationSelector.d.ts.map +1 -1
- package/dist/components/ConfirmationSelector.js +164 -117
- package/dist/components/DiffDisplay.d.ts +1 -0
- package/dist/components/DiffDisplay.d.ts.map +1 -1
- package/dist/components/DiffDisplay.js +94 -36
- package/dist/components/HistorySearch.d.ts.map +1 -1
- package/dist/components/HistorySearch.js +26 -20
- package/dist/components/Markdown.d.ts.map +1 -1
- package/dist/components/Markdown.js +3 -1
- package/dist/components/McpManager.d.ts.map +1 -1
- package/dist/components/McpManager.js +49 -52
- package/dist/components/MessageBlockItem.d.ts +9 -0
- package/dist/components/MessageBlockItem.d.ts.map +1 -0
- package/dist/components/MessageBlockItem.js +11 -0
- package/dist/components/MessageList.d.ts +2 -4
- package/dist/components/MessageList.d.ts.map +1 -1
- package/dist/components/MessageList.js +28 -23
- package/dist/components/PluginDetail.d.ts.map +1 -1
- package/dist/components/PluginDetail.js +19 -22
- package/dist/components/SessionSelector.d.ts.map +1 -1
- package/dist/components/SessionSelector.js +8 -5
- package/dist/components/TaskList.d.ts.map +1 -1
- package/dist/components/TaskList.js +2 -5
- package/dist/components/ToolDisplay.d.ts.map +1 -1
- package/dist/components/ToolDisplay.js +1 -1
- package/dist/contexts/useChat.d.ts +1 -0
- package/dist/contexts/useChat.d.ts.map +1 -1
- package/dist/contexts/useChat.js +20 -3
- package/dist/hooks/usePluginManager.d.ts.map +1 -1
- package/dist/hooks/usePluginManager.js +20 -39
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -0
- package/dist/print-cli.d.ts +1 -0
- package/dist/print-cli.d.ts.map +1 -1
- package/dist/print-cli.js +2 -1
- package/dist/utils/highlightUtils.d.ts +2 -0
- package/dist/utils/highlightUtils.d.ts.map +1 -0
- package/dist/utils/highlightUtils.js +69 -0
- package/dist/utils/toolParameterTransforms.d.ts +2 -6
- package/dist/utils/toolParameterTransforms.d.ts.map +1 -1
- package/dist/utils/toolParameterTransforms.js +10 -14
- package/package.json +4 -2
- package/src/cli.tsx +3 -0
- package/src/commands/plugin/disable.ts +3 -17
- package/src/commands/plugin/enable.ts +3 -17
- package/src/commands/plugin/install.ts +4 -18
- package/src/commands/plugin/list.ts +5 -55
- package/src/commands/plugin/marketplace.ts +9 -9
- package/src/commands/plugin/uninstall.ts +4 -26
- package/src/commands/plugin/update.ts +3 -3
- package/src/components/App.tsx +10 -2
- package/src/components/BackgroundTaskManager.tsx +69 -44
- package/src/components/ChatInterface.tsx +35 -23
- package/src/components/ConfirmationDetails.tsx +13 -15
- package/src/components/ConfirmationSelector.tsx +207 -128
- package/src/components/DiffDisplay.tsx +164 -75
- package/src/components/HistorySearch.tsx +31 -25
- package/src/components/Markdown.tsx +3 -1
- package/src/components/McpManager.tsx +51 -59
- package/src/components/MessageBlockItem.tsx +83 -0
- package/src/components/MessageList.tsx +55 -52
- package/src/components/PluginDetail.tsx +30 -31
- package/src/components/SessionSelector.tsx +8 -5
- package/src/components/TaskList.tsx +2 -5
- package/src/components/ToolDisplay.tsx +5 -1
- package/src/contexts/useChat.tsx +22 -2
- package/src/hooks/usePluginManager.ts +21 -57
- package/src/index.ts +17 -0
- package/src/print-cli.ts +3 -0
- package/src/utils/highlightUtils.ts +76 -0
- package/src/utils/toolParameterTransforms.ts +11 -20
- package/dist/components/MessageItem.d.ts +0 -8
- package/dist/components/MessageItem.d.ts.map +0 -1
- package/dist/components/MessageItem.js +0 -13
- package/src/components/MessageItem.tsx +0 -81
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from "react";
|
|
3
|
-
import { Box, Text, useInput } from "ink";
|
|
2
|
+
import { useLayoutEffect, useRef, useState } from "react";
|
|
3
|
+
import { Box, Text, useInput, useStdout, measureElement } from "ink";
|
|
4
4
|
import { BASH_TOOL_NAME, EXIT_PLAN_MODE_TOOL_NAME, ASK_USER_QUESTION_TOOL_NAME, } from "wave-agent-sdk";
|
|
5
5
|
const getHeaderColor = (header) => {
|
|
6
6
|
const colors = ["red", "green", "blue", "magenta", "cyan"];
|
|
@@ -10,24 +10,34 @@ const getHeaderColor = (header) => {
|
|
|
10
10
|
}
|
|
11
11
|
return colors[Math.abs(hash) % colors.length];
|
|
12
12
|
};
|
|
13
|
-
export const ConfirmationSelector = ({ toolName, toolInput, suggestedPrefix, hidePersistentOption, isExpanded = false, onDecision, onCancel, onAbort, }) => {
|
|
13
|
+
export const ConfirmationSelector = ({ toolName, toolInput, suggestedPrefix, hidePersistentOption, isExpanded = false, onDecision, onCancel, onAbort, onHeightMeasured, }) => {
|
|
14
|
+
const { stdout } = useStdout();
|
|
15
|
+
const boxRef = useRef(null);
|
|
16
|
+
useLayoutEffect(() => {
|
|
17
|
+
if (boxRef.current) {
|
|
18
|
+
const { height } = measureElement(boxRef.current);
|
|
19
|
+
onHeightMeasured?.(height);
|
|
20
|
+
}
|
|
21
|
+
}, [stdout?.rows, onHeightMeasured, toolName, toolInput, isExpanded]);
|
|
14
22
|
const [state, setState] = useState({
|
|
15
|
-
selectedOption: "allow",
|
|
23
|
+
selectedOption: toolName === EXIT_PLAN_MODE_TOOL_NAME ? "clear" : "allow",
|
|
16
24
|
alternativeText: "",
|
|
17
25
|
alternativeCursorPosition: 0,
|
|
18
26
|
hasUserInput: false,
|
|
19
27
|
});
|
|
20
|
-
const [
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
28
|
+
const [questionState, setQuestionState] = useState({
|
|
29
|
+
currentQuestionIndex: 0,
|
|
30
|
+
selectedOptionIndex: 0,
|
|
31
|
+
selectedOptionIndices: new Set(),
|
|
32
|
+
userAnswers: {},
|
|
33
|
+
otherText: "",
|
|
34
|
+
otherCursorPosition: 0,
|
|
35
|
+
});
|
|
26
36
|
const questions = toolInput?.questions || [];
|
|
27
|
-
const currentQuestion = questions[currentQuestionIndex];
|
|
37
|
+
const currentQuestion = questions[questionState.currentQuestionIndex];
|
|
28
38
|
const getAutoOptionText = () => {
|
|
29
39
|
if (toolName === EXIT_PLAN_MODE_TOOL_NAME) {
|
|
30
|
-
return "Yes,
|
|
40
|
+
return "Yes, auto-accept edits";
|
|
31
41
|
}
|
|
32
42
|
if (toolName === BASH_TOOL_NAME) {
|
|
33
43
|
if (suggestedPrefix) {
|
|
@@ -48,104 +58,140 @@ export const ConfirmationSelector = ({ toolName, toolInput, suggestedPrefix, hid
|
|
|
48
58
|
return;
|
|
49
59
|
const options = [...currentQuestion.options, { label: "Other" }];
|
|
50
60
|
const isMultiSelect = currentQuestion.multiSelect;
|
|
51
|
-
const isOtherFocused = selectedOptionIndex === options.length - 1;
|
|
52
61
|
if (key.return) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
62
|
+
setQuestionState((prev) => {
|
|
63
|
+
const isOtherFocused = prev.selectedOptionIndex === options.length - 1;
|
|
64
|
+
let answer = "";
|
|
65
|
+
if (isMultiSelect) {
|
|
66
|
+
const selectedLabels = Array.from(prev.selectedOptionIndices)
|
|
67
|
+
.filter((i) => i < currentQuestion.options.length)
|
|
68
|
+
.map((i) => currentQuestion.options[i].label);
|
|
69
|
+
const isOtherChecked = prev.selectedOptionIndices.has(options.length - 1);
|
|
70
|
+
if (isOtherChecked && prev.otherText.trim()) {
|
|
71
|
+
selectedLabels.push(prev.otherText.trim());
|
|
72
|
+
}
|
|
73
|
+
answer = selectedLabels.join(", ");
|
|
61
74
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
75
|
+
else {
|
|
76
|
+
if (isOtherFocused) {
|
|
77
|
+
answer = prev.otherText.trim();
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
answer = options[prev.selectedOptionIndex].label;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (!answer)
|
|
84
|
+
return prev;
|
|
85
|
+
const newAnswers = {
|
|
86
|
+
...prev.userAnswers,
|
|
87
|
+
[currentQuestion.question]: answer,
|
|
88
|
+
};
|
|
89
|
+
if (prev.currentQuestionIndex < questions.length - 1) {
|
|
90
|
+
return {
|
|
91
|
+
...prev,
|
|
92
|
+
currentQuestionIndex: prev.currentQuestionIndex + 1,
|
|
93
|
+
selectedOptionIndex: 0,
|
|
94
|
+
selectedOptionIndices: new Set(),
|
|
95
|
+
userAnswers: newAnswers,
|
|
96
|
+
otherText: "",
|
|
97
|
+
otherCursorPosition: 0,
|
|
98
|
+
};
|
|
67
99
|
}
|
|
68
100
|
else {
|
|
69
|
-
|
|
101
|
+
onDecision({
|
|
102
|
+
behavior: "allow",
|
|
103
|
+
message: JSON.stringify(newAnswers),
|
|
104
|
+
});
|
|
105
|
+
return {
|
|
106
|
+
...prev,
|
|
107
|
+
userAnswers: newAnswers,
|
|
108
|
+
};
|
|
70
109
|
}
|
|
71
|
-
}
|
|
72
|
-
if (!answer)
|
|
73
|
-
return;
|
|
74
|
-
const newAnswers = {
|
|
75
|
-
...userAnswers,
|
|
76
|
-
[currentQuestion.question]: answer,
|
|
77
|
-
};
|
|
78
|
-
setUserAnswers(newAnswers);
|
|
79
|
-
if (currentQuestionIndex < questions.length - 1) {
|
|
80
|
-
setCurrentQuestionIndex(currentQuestionIndex + 1);
|
|
81
|
-
setSelectedOptionIndex(0);
|
|
82
|
-
setSelectedOptionIndices(new Set());
|
|
83
|
-
setOtherText("");
|
|
84
|
-
setOtherCursorPosition(0);
|
|
85
|
-
}
|
|
86
|
-
else {
|
|
87
|
-
onDecision({
|
|
88
|
-
behavior: "allow",
|
|
89
|
-
message: JSON.stringify(newAnswers),
|
|
90
|
-
});
|
|
91
|
-
}
|
|
110
|
+
});
|
|
92
111
|
return;
|
|
93
112
|
}
|
|
94
113
|
if (input === " ") {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
114
|
+
setQuestionState((prev) => {
|
|
115
|
+
const isOtherFocused = prev.selectedOptionIndex === options.length - 1;
|
|
116
|
+
if (isMultiSelect &&
|
|
117
|
+
(!isOtherFocused ||
|
|
118
|
+
!prev.selectedOptionIndices.has(prev.selectedOptionIndex))) {
|
|
119
|
+
const nextIndices = new Set(prev.selectedOptionIndices);
|
|
120
|
+
if (nextIndices.has(prev.selectedOptionIndex))
|
|
121
|
+
nextIndices.delete(prev.selectedOptionIndex);
|
|
101
122
|
else
|
|
102
|
-
|
|
103
|
-
return
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
return;
|
|
123
|
+
nextIndices.add(prev.selectedOptionIndex);
|
|
124
|
+
return {
|
|
125
|
+
...prev,
|
|
126
|
+
selectedOptionIndices: nextIndices,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
return prev;
|
|
130
|
+
});
|
|
131
|
+
// If it's other and focused, we don't return here, allowing the input handler below to handle it
|
|
109
132
|
}
|
|
110
133
|
if (key.upArrow) {
|
|
111
|
-
|
|
112
|
-
|
|
134
|
+
setQuestionState((prev) => ({
|
|
135
|
+
...prev,
|
|
136
|
+
selectedOptionIndex: Math.max(0, prev.selectedOptionIndex - 1),
|
|
137
|
+
}));
|
|
113
138
|
return;
|
|
114
139
|
}
|
|
115
140
|
if (key.downArrow) {
|
|
116
|
-
|
|
117
|
-
|
|
141
|
+
setQuestionState((prev) => ({
|
|
142
|
+
...prev,
|
|
143
|
+
selectedOptionIndex: Math.min(options.length - 1, prev.selectedOptionIndex + 1),
|
|
144
|
+
}));
|
|
118
145
|
return;
|
|
119
146
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
prev.
|
|
133
|
-
|
|
147
|
+
setQuestionState((prev) => {
|
|
148
|
+
const isOtherFocused = prev.selectedOptionIndex === options.length - 1;
|
|
149
|
+
if (isOtherFocused) {
|
|
150
|
+
if (key.leftArrow) {
|
|
151
|
+
return {
|
|
152
|
+
...prev,
|
|
153
|
+
otherCursorPosition: Math.max(0, prev.otherCursorPosition - 1),
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
if (key.rightArrow) {
|
|
157
|
+
return {
|
|
158
|
+
...prev,
|
|
159
|
+
otherCursorPosition: Math.min(prev.otherText.length, prev.otherCursorPosition + 1),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
if (key.backspace || key.delete) {
|
|
163
|
+
if (prev.otherCursorPosition > 0) {
|
|
164
|
+
return {
|
|
165
|
+
...prev,
|
|
166
|
+
otherText: prev.otherText.slice(0, prev.otherCursorPosition - 1) +
|
|
167
|
+
prev.otherText.slice(prev.otherCursorPosition),
|
|
168
|
+
otherCursorPosition: prev.otherCursorPosition - 1,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (input && !key.ctrl && !key.meta) {
|
|
173
|
+
return {
|
|
174
|
+
...prev,
|
|
175
|
+
otherText: prev.otherText.slice(0, prev.otherCursorPosition) +
|
|
176
|
+
input +
|
|
177
|
+
prev.otherText.slice(prev.otherCursorPosition),
|
|
178
|
+
otherCursorPosition: prev.otherCursorPosition + input.length,
|
|
179
|
+
};
|
|
134
180
|
}
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
if (input && !key.ctrl && !key.meta) {
|
|
138
|
-
setOtherText((prev) => prev.slice(0, otherCursorPosition) +
|
|
139
|
-
input +
|
|
140
|
-
prev.slice(otherCursorPosition));
|
|
141
|
-
setOtherCursorPosition((prev) => prev + input.length);
|
|
142
|
-
return;
|
|
143
181
|
}
|
|
144
|
-
|
|
182
|
+
return prev;
|
|
183
|
+
});
|
|
145
184
|
return;
|
|
146
185
|
}
|
|
147
186
|
if (key.return) {
|
|
148
|
-
if (state.selectedOption === "
|
|
187
|
+
if (state.selectedOption === "clear") {
|
|
188
|
+
onDecision({
|
|
189
|
+
behavior: "allow",
|
|
190
|
+
newPermissionMode: "acceptEdits",
|
|
191
|
+
clearContext: true,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
else if (state.selectedOption === "allow") {
|
|
149
195
|
if (toolName === EXIT_PLAN_MODE_TOOL_NAME) {
|
|
150
196
|
onDecision({ behavior: "allow", newPermissionMode: "default" });
|
|
151
197
|
}
|
|
@@ -185,30 +231,31 @@ export const ConfirmationSelector = ({ toolName, toolInput, suggestedPrefix, hid
|
|
|
185
231
|
return;
|
|
186
232
|
}
|
|
187
233
|
}
|
|
234
|
+
const availableOptions = [];
|
|
235
|
+
if (toolName === EXIT_PLAN_MODE_TOOL_NAME)
|
|
236
|
+
availableOptions.push("clear");
|
|
237
|
+
availableOptions.push("allow");
|
|
238
|
+
if (!hidePersistentOption)
|
|
239
|
+
availableOptions.push("auto");
|
|
240
|
+
availableOptions.push("alternative");
|
|
188
241
|
if (key.upArrow) {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
return { ...prev, selectedOption: "allow" };
|
|
197
|
-
return prev;
|
|
198
|
-
});
|
|
242
|
+
const currentIndex = availableOptions.indexOf(state.selectedOption);
|
|
243
|
+
if (currentIndex > 0) {
|
|
244
|
+
setState((prev) => ({
|
|
245
|
+
...prev,
|
|
246
|
+
selectedOption: availableOptions[currentIndex - 1],
|
|
247
|
+
}));
|
|
248
|
+
}
|
|
199
249
|
return;
|
|
200
250
|
}
|
|
201
251
|
if (key.downArrow) {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
return { ...prev, selectedOption: "alternative" };
|
|
210
|
-
return prev;
|
|
211
|
-
});
|
|
252
|
+
const currentIndex = availableOptions.indexOf(state.selectedOption);
|
|
253
|
+
if (currentIndex < availableOptions.length - 1) {
|
|
254
|
+
setState((prev) => ({
|
|
255
|
+
...prev,
|
|
256
|
+
selectedOption: availableOptions[currentIndex + 1],
|
|
257
|
+
}));
|
|
258
|
+
}
|
|
212
259
|
return;
|
|
213
260
|
}
|
|
214
261
|
if (input && !key.ctrl && !key.meta && !("alt" in key && key.alt)) {
|
|
@@ -244,23 +291,23 @@ export const ConfirmationSelector = ({ toolName, toolInput, suggestedPrefix, hid
|
|
|
244
291
|
return;
|
|
245
292
|
}
|
|
246
293
|
});
|
|
247
|
-
const placeholderText = "Type here to tell Wave what to
|
|
294
|
+
const placeholderText = "Type here to tell Wave what to change";
|
|
248
295
|
const showPlaceholder = state.selectedOption === "alternative" && !state.hasUserInput;
|
|
249
|
-
return (_jsxs(Box, { flexDirection: "column", children: [toolName === ASK_USER_QUESTION_TOOL_NAME &&
|
|
296
|
+
return (_jsxs(Box, { ref: boxRef, flexDirection: "column", children: [toolName === ASK_USER_QUESTION_TOOL_NAME &&
|
|
250
297
|
currentQuestion &&
|
|
251
298
|
!isExpanded && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { color: getHeaderColor(currentQuestion.header), bold: true, children: currentQuestion.header.slice(0, 12).toUpperCase() }), _jsx(Box, { marginLeft: 1, children: _jsx(Text, { bold: true, children: currentQuestion.question }) })] }), _jsx(Box, { flexDirection: "column", children: [...currentQuestion.options, { label: "Other" }].map((option, index) => {
|
|
252
|
-
const isSelected = selectedOptionIndex === index;
|
|
299
|
+
const isSelected = questionState.selectedOptionIndex === index;
|
|
253
300
|
const isChecked = currentQuestion.multiSelect
|
|
254
|
-
? selectedOptionIndices.has(index)
|
|
301
|
+
? questionState.selectedOptionIndices.has(index)
|
|
255
302
|
: isSelected;
|
|
256
303
|
const isOther = index === currentQuestion.options.length;
|
|
257
304
|
return (_jsx(Box, { children: _jsxs(Text, { color: isSelected ? "black" : "white", backgroundColor: isSelected ? "yellow" : undefined, children: [isSelected ? "> " : " ", currentQuestion.multiSelect
|
|
258
305
|
? isChecked
|
|
259
306
|
? "[x] "
|
|
260
307
|
: "[ ] "
|
|
261
|
-
: "", option.label, option.description ? ` - ${option.description}` : "", isOther && isSelected && (_jsxs(Text, { children: [":", " ", otherText ? (_jsxs(_Fragment, { children: [otherText.slice(0, otherCursorPosition), _jsx(Text, { backgroundColor: "white", color: "black", children: otherText[otherCursorPosition] || " " }), otherText.slice(otherCursorPosition + 1)] })) : (_jsx(Text, { color: "gray", dimColor: true, children: "[Type your answer...]" }))] }))] }) }, index));
|
|
262
|
-
}) }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: ["Question ", currentQuestionIndex + 1, " of ", questions.length, " \u2022", currentQuestion.multiSelect ? " Space to toggle •" : "", " Use \u2191\u2193 to navigate \u2022 Enter to confirm"] }) })] })), toolName !== ASK_USER_QUESTION_TOOL_NAME && !isExpanded && (_jsxs(_Fragment, { children: [_jsx(Box, { marginTop: 1, children: _jsx(Text, { children: "Do you want to proceed?" }) }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Box, { children: _jsxs(Text, { color: state.selectedOption === "allow" ? "black" : "white", backgroundColor: state.selectedOption === "allow" ? "yellow" : undefined, bold: state.selectedOption === "allow", children: [state.selectedOption === "allow" ? "> " : " ", toolName === EXIT_PLAN_MODE_TOOL_NAME
|
|
263
|
-
? "Yes,
|
|
264
|
-
: "Yes"] }) }, "allow-option"), !hidePersistentOption && (_jsx(Box, { children: _jsxs(Text, { color: state.selectedOption === "auto" ? "black" : "white", backgroundColor: state.selectedOption === "auto" ? "yellow" : undefined, bold: state.selectedOption === "auto", children: [state.selectedOption === "auto" ? "> " : " ", getAutoOptionText()] }) }, "auto-option")), _jsx(Box, { children: _jsxs(Text, { color: state.selectedOption === "alternative" ? "black" : "white", backgroundColor: state.selectedOption === "alternative" ? "yellow" : undefined, bold: state.selectedOption === "alternative", children: [state.selectedOption === "alternative" ? "> " : " ", showPlaceholder ? (_jsx(Text, { color: "gray", dimColor: true, children: placeholderText })) : (_jsx(Text, { children: state.alternativeText ? (_jsxs(_Fragment, { children: [state.alternativeText.slice(0, state.alternativeCursorPosition), _jsx(Text, { backgroundColor: "white", color: "black", children: state.alternativeText[state.alternativeCursorPosition] || " " }), state.alternativeText.slice(state.alternativeCursorPosition + 1)] })) : ("Type here to tell Wave what to
|
|
308
|
+
: "", option.label, option.description ? ` - ${option.description}` : "", isOther && isSelected && (_jsxs(Text, { children: [":", " ", questionState.otherText ? (_jsxs(_Fragment, { children: [questionState.otherText.slice(0, questionState.otherCursorPosition), _jsx(Text, { backgroundColor: "white", color: "black", children: questionState.otherText[questionState.otherCursorPosition] || " " }), questionState.otherText.slice(questionState.otherCursorPosition + 1)] })) : (_jsx(Text, { color: "gray", dimColor: true, children: "[Type your answer...]" }))] }))] }) }, index));
|
|
309
|
+
}) }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: ["Question ", questionState.currentQuestionIndex + 1, " of", " ", questions.length, " \u2022", currentQuestion.multiSelect ? " Space to toggle •" : "", " Use \u2191\u2193 to navigate \u2022 Enter to confirm"] }) })] })), toolName !== ASK_USER_QUESTION_TOOL_NAME && !isExpanded && (_jsxs(_Fragment, { children: [_jsx(Box, { marginTop: 1, children: _jsx(Text, { children: "Do you want to proceed?" }) }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [toolName === EXIT_PLAN_MODE_TOOL_NAME && (_jsx(Box, { children: _jsxs(Text, { color: state.selectedOption === "clear" ? "black" : "white", backgroundColor: state.selectedOption === "clear" ? "yellow" : undefined, bold: state.selectedOption === "clear", children: [state.selectedOption === "clear" ? "> " : " ", "Yes, clear context and auto-accept edits"] }) }, "clear-option")), _jsx(Box, { children: _jsxs(Text, { color: state.selectedOption === "allow" ? "black" : "white", backgroundColor: state.selectedOption === "allow" ? "yellow" : undefined, bold: state.selectedOption === "allow", children: [state.selectedOption === "allow" ? "> " : " ", toolName === EXIT_PLAN_MODE_TOOL_NAME
|
|
310
|
+
? "Yes, manually approve edits"
|
|
311
|
+
: "Yes, proceed"] }) }, "allow-option"), !hidePersistentOption && (_jsx(Box, { children: _jsxs(Text, { color: state.selectedOption === "auto" ? "black" : "white", backgroundColor: state.selectedOption === "auto" ? "yellow" : undefined, bold: state.selectedOption === "auto", children: [state.selectedOption === "auto" ? "> " : " ", getAutoOptionText()] }) }, "auto-option")), _jsx(Box, { children: _jsxs(Text, { color: state.selectedOption === "alternative" ? "black" : "white", backgroundColor: state.selectedOption === "alternative" ? "yellow" : undefined, bold: state.selectedOption === "alternative", children: [state.selectedOption === "alternative" ? "> " : " ", showPlaceholder ? (_jsx(Text, { color: "gray", dimColor: true, children: placeholderText })) : (_jsx(Text, { children: state.alternativeText ? (_jsxs(_Fragment, { children: [state.alternativeText.slice(0, state.alternativeCursorPosition), _jsx(Text, { backgroundColor: "white", color: "black", children: state.alternativeText[state.alternativeCursorPosition] || " " }), state.alternativeText.slice(state.alternativeCursorPosition + 1)] })) : ("Type here to tell Wave what to change") }))] }) }, "alternative-option")] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Use \u2191\u2193 to navigate \u2022 ESC to cancel" }) })] }))] }));
|
|
265
312
|
};
|
|
266
313
|
ConfirmationSelector.displayName = "ConfirmationSelector";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DiffDisplay.d.ts","sourceRoot":"","sources":["../../src/components/DiffDisplay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkB,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"DiffDisplay.d.ts","sourceRoot":"","sources":["../../src/components/DiffDisplay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkB,MAAM,OAAO,CAAC;AAMvC,UAAU,gBAAgB;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CA8VlD,CAAC"}
|
|
@@ -1,25 +1,24 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import React, { useMemo } from "react";
|
|
3
3
|
import { Box, Text } from "ink";
|
|
4
|
-
import { WRITE_TOOL_NAME, EDIT_TOOL_NAME
|
|
4
|
+
import { WRITE_TOOL_NAME, EDIT_TOOL_NAME } from "wave-agent-sdk";
|
|
5
5
|
import { transformToolBlockToChanges } from "../utils/toolParameterTransforms.js";
|
|
6
6
|
import { diffLines, diffWords } from "diff";
|
|
7
|
-
export const DiffDisplay = ({ toolName, parameters, }) => {
|
|
8
|
-
const showDiff = toolName &&
|
|
9
|
-
[WRITE_TOOL_NAME, EDIT_TOOL_NAME, MULTI_EDIT_TOOL_NAME].includes(toolName);
|
|
7
|
+
export const DiffDisplay = ({ toolName, parameters, startLineNumber, }) => {
|
|
8
|
+
const showDiff = toolName && [WRITE_TOOL_NAME, EDIT_TOOL_NAME].includes(toolName);
|
|
10
9
|
// Diff detection and transformation using typed parameters
|
|
11
10
|
const changes = useMemo(() => {
|
|
12
11
|
if (!showDiff || !toolName || !parameters)
|
|
13
12
|
return [];
|
|
14
13
|
try {
|
|
15
14
|
// Use local transformation with JSON parsing and type guards
|
|
16
|
-
return transformToolBlockToChanges(toolName, parameters);
|
|
15
|
+
return transformToolBlockToChanges(toolName, parameters, startLineNumber);
|
|
17
16
|
}
|
|
18
17
|
catch (error) {
|
|
19
18
|
console.warn("Error transforming tool block to changes:", error);
|
|
20
19
|
return [];
|
|
21
20
|
}
|
|
22
|
-
}, [toolName, parameters, showDiff]);
|
|
21
|
+
}, [toolName, parameters, showDiff, startLineNumber]);
|
|
23
22
|
// Render word-level diff between two lines of text
|
|
24
23
|
const renderWordLevelDiff = (oldLine, newLine, keyPrefix) => {
|
|
25
24
|
try {
|
|
@@ -59,39 +58,51 @@ export const DiffDisplay = ({ toolName, parameters, }) => {
|
|
|
59
58
|
try {
|
|
60
59
|
if (changes.length === 0)
|
|
61
60
|
return null;
|
|
61
|
+
const maxLineNum = changes.reduce((max, change) => {
|
|
62
|
+
const oldLines = (change.oldContent || "").split("\n").length;
|
|
63
|
+
const newLines = (change.newContent || "").split("\n").length;
|
|
64
|
+
const start = change.startLineNumber || 1;
|
|
65
|
+
// For Edit tool, the diff might show context lines before/after the change.
|
|
66
|
+
// The startLineNumber is the line where old_string starts.
|
|
67
|
+
// diffLines will include context lines if they are part of the change object.
|
|
68
|
+
// However, our transformEditParameters currently only puts old_string/new_string.
|
|
69
|
+
// If we ever support context lines in the Change object, we need to be careful.
|
|
70
|
+
return Math.max(max, start + oldLines, start + newLines);
|
|
71
|
+
}, 0);
|
|
72
|
+
const maxDigits = Math.max(2, maxLineNum.toString().length);
|
|
73
|
+
const renderLine = (oldLineNum, newLineNum, prefix, content, color, key) => {
|
|
74
|
+
const formatNum = (num) => num === null
|
|
75
|
+
? " ".repeat(maxDigits)
|
|
76
|
+
: num.toString().padStart(maxDigits);
|
|
77
|
+
return (_jsxs(Box, { flexDirection: "row", children: [_jsxs(Text, { color: "gray", children: [formatNum(oldLineNum), " "] }), _jsxs(Text, { color: "gray", children: [formatNum(newLineNum), " "] }), _jsx(Text, { color: "gray", children: "| " }), _jsx(Text, { color: color, children: prefix }), _jsx(Text, { color: color, children: content })] }, key));
|
|
78
|
+
};
|
|
62
79
|
const allElements = [];
|
|
63
80
|
changes.forEach((change, changeIndex) => {
|
|
64
81
|
try {
|
|
65
|
-
// Add ellipsis between non-contiguous edits in MultiEdit
|
|
66
|
-
if (toolName === MULTI_EDIT_TOOL_NAME && changeIndex > 0) {
|
|
67
|
-
allElements.push(_jsx(Box, { children: _jsx(Text, { color: "gray", children: "..." }) }, `multi-edit-separator-${changeIndex}`));
|
|
68
|
-
}
|
|
69
82
|
// Get line-level diff to understand the structure
|
|
70
83
|
const lineDiffs = diffLines(change.oldContent || "", change.newContent || "");
|
|
84
|
+
let oldLineNum = change.startLineNumber || 1;
|
|
85
|
+
let newLineNum = change.startLineNumber || 1;
|
|
71
86
|
// Process line diffs
|
|
72
87
|
const diffElements = [];
|
|
73
88
|
lineDiffs.forEach((part, partIndex) => {
|
|
89
|
+
const lines = part.value.split("\n");
|
|
90
|
+
// diffLines might return a trailing empty string if the content ends with a newline
|
|
91
|
+
if (lines[lines.length - 1] === "") {
|
|
92
|
+
lines.pop();
|
|
93
|
+
}
|
|
74
94
|
if (part.added) {
|
|
75
|
-
const lines = part.value
|
|
76
|
-
.split("\n")
|
|
77
|
-
.filter((line) => line !== "");
|
|
78
95
|
lines.forEach((line, lineIndex) => {
|
|
79
|
-
diffElements.push(
|
|
96
|
+
diffElements.push(renderLine(null, newLineNum++, "+", line, "green", `add-${changeIndex}-${partIndex}-${lineIndex}`));
|
|
80
97
|
});
|
|
81
98
|
}
|
|
82
99
|
else if (part.removed) {
|
|
83
|
-
const lines = part.value
|
|
84
|
-
.split("\n")
|
|
85
|
-
.filter((line) => line !== "");
|
|
86
100
|
lines.forEach((line, lineIndex) => {
|
|
87
|
-
diffElements.push(
|
|
101
|
+
diffElements.push(renderLine(oldLineNum++, null, "-", line, "red", `remove-${changeIndex}-${partIndex}-${lineIndex}`));
|
|
88
102
|
});
|
|
89
103
|
}
|
|
90
104
|
else {
|
|
91
105
|
// Context lines - show unchanged content
|
|
92
|
-
const lines = part.value
|
|
93
|
-
.split("\n")
|
|
94
|
-
.filter((line) => line !== "");
|
|
95
106
|
const isFirstBlock = partIndex === 0;
|
|
96
107
|
const isLastBlock = partIndex === lineDiffs.length - 1;
|
|
97
108
|
let linesToDisplay = lines;
|
|
@@ -100,6 +111,9 @@ export const DiffDisplay = ({ toolName, parameters, }) => {
|
|
|
100
111
|
if (isFirstBlock && !isLastBlock) {
|
|
101
112
|
// First block: keep last 3
|
|
102
113
|
if (lines.length > 3) {
|
|
114
|
+
const skipCount = lines.length - 3;
|
|
115
|
+
oldLineNum += skipCount;
|
|
116
|
+
newLineNum += skipCount;
|
|
103
117
|
linesToDisplay = lines.slice(-3);
|
|
104
118
|
showEllipsisTop = true;
|
|
105
119
|
}
|
|
@@ -118,12 +132,8 @@ export const DiffDisplay = ({ toolName, parameters, }) => {
|
|
|
118
132
|
showEllipsisTop = false; // We'll put ellipsis in the middle
|
|
119
133
|
}
|
|
120
134
|
}
|
|
121
|
-
else if (isFirstBlock && isLastBlock) {
|
|
122
|
-
// Only one block (no changes?) - keep all or apply a general limit
|
|
123
|
-
// For now, let's keep all if it's the only block
|
|
124
|
-
}
|
|
125
135
|
if (showEllipsisTop) {
|
|
126
|
-
diffElements.push(_jsx(Box, { children:
|
|
136
|
+
diffElements.push(_jsx(Box, { children: _jsxs(Text, { color: "gray", children: [" ".repeat(maxDigits * 2 + 2), "..."] }) }, `ellipsis-top-${changeIndex}-${partIndex}`));
|
|
127
137
|
}
|
|
128
138
|
linesToDisplay.forEach((line, lineIndex) => {
|
|
129
139
|
// If it's a middle block and we are at the split point
|
|
@@ -131,12 +141,20 @@ export const DiffDisplay = ({ toolName, parameters, }) => {
|
|
|
131
141
|
!isLastBlock &&
|
|
132
142
|
lines.length > 6 &&
|
|
133
143
|
lineIndex === 3) {
|
|
134
|
-
|
|
144
|
+
const skipCount = lines.length - 6;
|
|
145
|
+
oldLineNum += skipCount;
|
|
146
|
+
newLineNum += skipCount;
|
|
147
|
+
diffElements.push(_jsx(Box, { children: _jsxs(Text, { color: "gray", children: [" ".repeat(maxDigits * 2 + 2), "..."] }) }, `ellipsis-mid-${changeIndex}-${partIndex}`));
|
|
135
148
|
}
|
|
136
|
-
diffElements.push(
|
|
149
|
+
diffElements.push(renderLine(oldLineNum++, newLineNum++, " ", line, "white", `context-${changeIndex}-${partIndex}-${lineIndex}`));
|
|
137
150
|
});
|
|
138
151
|
if (showEllipsisBottom) {
|
|
139
|
-
|
|
152
|
+
const skipCount = lines.length - linesToDisplay.length;
|
|
153
|
+
// We don't increment oldLineNum/newLineNum here because they are already incremented in the loop
|
|
154
|
+
// But we need to account for the lines we skipped at the end of this block
|
|
155
|
+
oldLineNum += skipCount;
|
|
156
|
+
newLineNum += skipCount;
|
|
157
|
+
diffElements.push(_jsx(Box, { children: _jsxs(Text, { color: "gray", children: [" ".repeat(maxDigits * 2 + 2), "..."] }) }, `ellipsis-bottom-${changeIndex}-${partIndex}`));
|
|
140
158
|
}
|
|
141
159
|
}
|
|
142
160
|
});
|
|
@@ -150,10 +168,12 @@ export const DiffDisplay = ({ toolName, parameters, }) => {
|
|
|
150
168
|
diffElements[1].key.includes("add-")) {
|
|
151
169
|
const removedText = extractTextFromElement(diffElements[0]);
|
|
152
170
|
const addedText = extractTextFromElement(diffElements[1]);
|
|
171
|
+
const oldLineNumVal = extractOldLineNumFromElement(diffElements[0]);
|
|
172
|
+
const newLineNumVal = extractNewLineNumFromElement(diffElements[1]);
|
|
153
173
|
if (removedText && addedText) {
|
|
154
174
|
const { removedParts, addedParts } = renderWordLevelDiff(removedText, addedText, `word-${changeIndex}`);
|
|
155
|
-
allElements.push(
|
|
156
|
-
allElements.push(
|
|
175
|
+
allElements.push(renderLine(oldLineNumVal, null, "-", removedParts, "red", `word-diff-removed-${changeIndex}`));
|
|
176
|
+
allElements.push(renderLine(null, newLineNumVal, "+", addedParts, "green", `word-diff-added-${changeIndex}`));
|
|
157
177
|
}
|
|
158
178
|
else {
|
|
159
179
|
allElements.push(...diffElements);
|
|
@@ -180,19 +200,57 @@ export const DiffDisplay = ({ toolName, parameters, }) => {
|
|
|
180
200
|
if (!showDiff) {
|
|
181
201
|
return null;
|
|
182
202
|
}
|
|
183
|
-
return (_jsx(Box, { flexDirection: "column", children:
|
|
203
|
+
return (_jsx(Box, { flexDirection: "column", children: _jsx(Box, { paddingLeft: 2, borderLeft: true, borderColor: "cyan", flexDirection: "column", children: renderExpandedDiff() }) }));
|
|
184
204
|
};
|
|
185
205
|
// Helper function to extract text content from a React element
|
|
186
206
|
const extractTextFromElement = (element) => {
|
|
187
207
|
if (!React.isValidElement(element))
|
|
188
208
|
return null;
|
|
189
209
|
// Navigate through Box -> Text structure
|
|
210
|
+
// Our new structure is: Box -> Text (old), Text (new), Text (|), Text (prefix), Text (content)
|
|
211
|
+
const children = element.props.children;
|
|
212
|
+
if (Array.isArray(children) && children.length >= 5) {
|
|
213
|
+
const textElement = children[4]; // Fifth child should be the Text with content
|
|
214
|
+
if (React.isValidElement(textElement)) {
|
|
215
|
+
const textChildren = textElement.props
|
|
216
|
+
.children;
|
|
217
|
+
return Array.isArray(textChildren)
|
|
218
|
+
? textChildren.join("")
|
|
219
|
+
: String(textChildren || "");
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return null;
|
|
223
|
+
};
|
|
224
|
+
const extractOldLineNumFromElement = (element) => {
|
|
225
|
+
if (!React.isValidElement(element))
|
|
226
|
+
return null;
|
|
227
|
+
const children = element.props.children;
|
|
228
|
+
if (Array.isArray(children) && children.length >= 1) {
|
|
229
|
+
const textElement = children[0];
|
|
230
|
+
if (React.isValidElement(textElement)) {
|
|
231
|
+
const textChildren = textElement.props
|
|
232
|
+
.children;
|
|
233
|
+
const val = (Array.isArray(textChildren)
|
|
234
|
+
? textChildren.join("")
|
|
235
|
+
: String(textChildren || "")).trim();
|
|
236
|
+
return val ? parseInt(val, 10) : null;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return null;
|
|
240
|
+
};
|
|
241
|
+
const extractNewLineNumFromElement = (element) => {
|
|
242
|
+
if (!React.isValidElement(element))
|
|
243
|
+
return null;
|
|
190
244
|
const children = element.props.children;
|
|
191
245
|
if (Array.isArray(children) && children.length >= 2) {
|
|
192
|
-
const textElement = children[1];
|
|
193
|
-
if (React.isValidElement(textElement)
|
|
194
|
-
textElement.props
|
|
195
|
-
|
|
246
|
+
const textElement = children[1];
|
|
247
|
+
if (React.isValidElement(textElement)) {
|
|
248
|
+
const textChildren = textElement.props
|
|
249
|
+
.children;
|
|
250
|
+
const val = (Array.isArray(textChildren)
|
|
251
|
+
? textChildren.join("")
|
|
252
|
+
: String(textChildren || "")).trim();
|
|
253
|
+
return val ? parseInt(val, 10) : null;
|
|
196
254
|
}
|
|
197
255
|
}
|
|
198
256
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HistorySearch.d.ts","sourceRoot":"","sources":["../../src/components/HistorySearch.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAInD,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,
|
|
1
|
+
{"version":3,"file":"HistorySearch.d.ts","sourceRoot":"","sources":["../../src/components/HistorySearch.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAInD,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAuKtD,CAAC"}
|