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
|
@@ -1,33 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from "react";
|
|
3
3
|
import { Box, Text, useInput } from "ink";
|
|
4
|
-
import { BASH_TOOL_NAME,
|
|
5
|
-
import { DiffDisplay } from "./DiffDisplay.js";
|
|
6
|
-
import { PlanDisplay } from "./PlanDisplay.js";
|
|
7
|
-
// Helper function to generate descriptive action text
|
|
8
|
-
const getActionDescription = (toolName, toolInput) => {
|
|
9
|
-
if (!toolInput) {
|
|
10
|
-
return "Execute operation";
|
|
11
|
-
}
|
|
12
|
-
switch (toolName) {
|
|
13
|
-
case BASH_TOOL_NAME:
|
|
14
|
-
return `Execute command: ${toolInput.command || "unknown command"}`;
|
|
15
|
-
case EDIT_TOOL_NAME:
|
|
16
|
-
return `Edit file: ${toolInput.file_path || "unknown file"}`;
|
|
17
|
-
case MULTI_EDIT_TOOL_NAME:
|
|
18
|
-
return `Edit multiple sections in: ${toolInput.file_path || "unknown file"}`;
|
|
19
|
-
case DELETE_FILE_TOOL_NAME:
|
|
20
|
-
return `Delete file: ${toolInput.target_file || "unknown file"}`;
|
|
21
|
-
case WRITE_TOOL_NAME:
|
|
22
|
-
return `Write to file: ${toolInput.file_path || "unknown file"}`;
|
|
23
|
-
case EXIT_PLAN_MODE_TOOL_NAME:
|
|
24
|
-
return "Review and approve the plan";
|
|
25
|
-
case ASK_USER_QUESTION_TOOL_NAME:
|
|
26
|
-
return "Answer questions to clarify intent";
|
|
27
|
-
default:
|
|
28
|
-
return "Execute operation";
|
|
29
|
-
}
|
|
30
|
-
};
|
|
4
|
+
import { BASH_TOOL_NAME, EXIT_PLAN_MODE_TOOL_NAME, ASK_USER_QUESTION_TOOL_NAME, } from "wave-agent-sdk";
|
|
31
5
|
const getHeaderColor = (header) => {
|
|
32
6
|
const colors = ["red", "green", "blue", "magenta", "cyan"];
|
|
33
7
|
let hash = 0;
|
|
@@ -36,18 +10,19 @@ const getHeaderColor = (header) => {
|
|
|
36
10
|
}
|
|
37
11
|
return colors[Math.abs(hash) % colors.length];
|
|
38
12
|
};
|
|
39
|
-
export const
|
|
13
|
+
export const ConfirmationSelector = ({ toolName, toolInput, suggestedPrefix, hidePersistentOption, isExpanded = false, onDecision, onCancel, onAbort, }) => {
|
|
40
14
|
const [state, setState] = useState({
|
|
41
15
|
selectedOption: "allow",
|
|
42
16
|
alternativeText: "",
|
|
17
|
+
alternativeCursorPosition: 0,
|
|
43
18
|
hasUserInput: false,
|
|
44
19
|
});
|
|
45
|
-
// Specialized state for AskUserQuestion
|
|
46
20
|
const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0);
|
|
47
21
|
const [selectedOptionIndex, setSelectedOptionIndex] = useState(0);
|
|
48
22
|
const [selectedOptionIndices, setSelectedOptionIndices] = useState(new Set());
|
|
49
23
|
const [userAnswers, setUserAnswers] = useState({});
|
|
50
24
|
const [otherText, setOtherText] = useState("");
|
|
25
|
+
const [otherCursorPosition, setOtherCursorPosition] = useState(0);
|
|
51
26
|
const questions = toolInput?.questions || [];
|
|
52
27
|
const currentQuestion = questions[currentQuestionIndex];
|
|
53
28
|
const getAutoOptionText = () => {
|
|
@@ -63,7 +38,6 @@ export const Confirmation = ({ toolName, toolInput, suggestedPrefix, hidePersist
|
|
|
63
38
|
return "Yes, and auto-accept edits";
|
|
64
39
|
};
|
|
65
40
|
useInput((input, key) => {
|
|
66
|
-
// Handle ESC to cancel and abort
|
|
67
41
|
if (key.escape) {
|
|
68
42
|
onCancel();
|
|
69
43
|
onAbort();
|
|
@@ -73,7 +47,7 @@ export const Confirmation = ({ toolName, toolInput, suggestedPrefix, hidePersist
|
|
|
73
47
|
if (!currentQuestion)
|
|
74
48
|
return;
|
|
75
49
|
const options = [...currentQuestion.options, { label: "Other" }];
|
|
76
|
-
const isMultiSelect =
|
|
50
|
+
const isMultiSelect = currentQuestion.multiSelect;
|
|
77
51
|
const isOtherFocused = selectedOptionIndex === options.length - 1;
|
|
78
52
|
if (key.return) {
|
|
79
53
|
let answer = "";
|
|
@@ -107,9 +81,9 @@ export const Confirmation = ({ toolName, toolInput, suggestedPrefix, hidePersist
|
|
|
107
81
|
setSelectedOptionIndex(0);
|
|
108
82
|
setSelectedOptionIndices(new Set());
|
|
109
83
|
setOtherText("");
|
|
84
|
+
setOtherCursorPosition(0);
|
|
110
85
|
}
|
|
111
86
|
else {
|
|
112
|
-
// All questions answered
|
|
113
87
|
onDecision({
|
|
114
88
|
behavior: "allow",
|
|
115
89
|
message: JSON.stringify(newAnswers),
|
|
@@ -122,45 +96,54 @@ export const Confirmation = ({ toolName, toolInput, suggestedPrefix, hidePersist
|
|
|
122
96
|
(!isOtherFocused || !selectedOptionIndices.has(selectedOptionIndex))) {
|
|
123
97
|
setSelectedOptionIndices((prev) => {
|
|
124
98
|
const next = new Set(prev);
|
|
125
|
-
if (next.has(selectedOptionIndex))
|
|
99
|
+
if (next.has(selectedOptionIndex))
|
|
126
100
|
next.delete(selectedOptionIndex);
|
|
127
|
-
|
|
128
|
-
else {
|
|
101
|
+
else
|
|
129
102
|
next.add(selectedOptionIndex);
|
|
130
|
-
}
|
|
131
103
|
return next;
|
|
132
104
|
});
|
|
133
105
|
return;
|
|
134
106
|
}
|
|
135
|
-
if (!isOtherFocused)
|
|
107
|
+
if (!isOtherFocused)
|
|
136
108
|
return;
|
|
137
|
-
}
|
|
138
|
-
// If isOtherFocused is true, fall through to handle space as text input
|
|
139
109
|
}
|
|
140
110
|
if (key.upArrow) {
|
|
141
|
-
if (selectedOptionIndex > 0)
|
|
111
|
+
if (selectedOptionIndex > 0)
|
|
142
112
|
setSelectedOptionIndex(selectedOptionIndex - 1);
|
|
143
|
-
}
|
|
144
113
|
return;
|
|
145
114
|
}
|
|
146
115
|
if (key.downArrow) {
|
|
147
|
-
if (selectedOptionIndex < options.length - 1)
|
|
116
|
+
if (selectedOptionIndex < options.length - 1)
|
|
148
117
|
setSelectedOptionIndex(selectedOptionIndex + 1);
|
|
149
|
-
}
|
|
150
118
|
return;
|
|
151
119
|
}
|
|
152
120
|
if (isOtherFocused) {
|
|
121
|
+
if (key.leftArrow) {
|
|
122
|
+
setOtherCursorPosition((prev) => Math.max(0, prev - 1));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (key.rightArrow) {
|
|
126
|
+
setOtherCursorPosition((prev) => Math.min(otherText.length, prev + 1));
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
153
129
|
if (key.backspace || key.delete) {
|
|
154
|
-
|
|
130
|
+
if (otherCursorPosition > 0) {
|
|
131
|
+
setOtherText((prev) => prev.slice(0, otherCursorPosition - 1) +
|
|
132
|
+
prev.slice(otherCursorPosition));
|
|
133
|
+
setOtherCursorPosition((prev) => prev - 1);
|
|
134
|
+
}
|
|
135
|
+
return;
|
|
155
136
|
}
|
|
156
|
-
|
|
157
|
-
setOtherText((prev) => prev +
|
|
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;
|
|
158
143
|
}
|
|
159
|
-
return;
|
|
160
144
|
}
|
|
161
145
|
return;
|
|
162
146
|
}
|
|
163
|
-
// Handle Enter to confirm selection
|
|
164
147
|
if (key.return) {
|
|
165
148
|
if (state.selectedOption === "allow") {
|
|
166
149
|
if (toolName === EXIT_PLAN_MODE_TOOL_NAME) {
|
|
@@ -175,38 +158,40 @@ export const Confirmation = ({ toolName, toolInput, suggestedPrefix, hidePersist
|
|
|
175
158
|
const rule = suggestedPrefix
|
|
176
159
|
? `Bash(${suggestedPrefix}*)`
|
|
177
160
|
: `Bash(${toolInput?.command})`;
|
|
178
|
-
onDecision({
|
|
179
|
-
behavior: "allow",
|
|
180
|
-
newPermissionRule: rule,
|
|
181
|
-
});
|
|
161
|
+
onDecision({ behavior: "allow", newPermissionRule: rule });
|
|
182
162
|
}
|
|
183
163
|
else {
|
|
184
|
-
onDecision({
|
|
185
|
-
behavior: "allow",
|
|
186
|
-
newPermissionMode: "acceptEdits",
|
|
187
|
-
});
|
|
164
|
+
onDecision({ behavior: "allow", newPermissionMode: "acceptEdits" });
|
|
188
165
|
}
|
|
189
166
|
}
|
|
190
|
-
else {
|
|
191
|
-
|
|
192
|
-
if (state.alternativeText.trim()) {
|
|
193
|
-
onDecision({
|
|
194
|
-
behavior: "deny",
|
|
195
|
-
message: state.alternativeText.trim(),
|
|
196
|
-
});
|
|
197
|
-
}
|
|
167
|
+
else if (state.alternativeText.trim()) {
|
|
168
|
+
onDecision({ behavior: "deny", message: state.alternativeText.trim() });
|
|
198
169
|
}
|
|
199
170
|
return;
|
|
200
171
|
}
|
|
201
|
-
|
|
172
|
+
if (state.selectedOption === "alternative") {
|
|
173
|
+
if (key.leftArrow) {
|
|
174
|
+
setState((prev) => ({
|
|
175
|
+
...prev,
|
|
176
|
+
alternativeCursorPosition: Math.max(0, prev.alternativeCursorPosition - 1),
|
|
177
|
+
}));
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (key.rightArrow) {
|
|
181
|
+
setState((prev) => ({
|
|
182
|
+
...prev,
|
|
183
|
+
alternativeCursorPosition: Math.min(prev.alternativeText.length, prev.alternativeCursorPosition + 1),
|
|
184
|
+
}));
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
202
188
|
if (key.upArrow) {
|
|
203
189
|
setState((prev) => {
|
|
204
|
-
if (prev.selectedOption === "alternative")
|
|
190
|
+
if (prev.selectedOption === "alternative")
|
|
205
191
|
return {
|
|
206
192
|
...prev,
|
|
207
193
|
selectedOption: hidePersistentOption ? "allow" : "auto",
|
|
208
194
|
};
|
|
209
|
-
}
|
|
210
195
|
if (prev.selectedOption === "auto")
|
|
211
196
|
return { ...prev, selectedOption: "allow" };
|
|
212
197
|
return prev;
|
|
@@ -215,61 +200,67 @@ export const Confirmation = ({ toolName, toolInput, suggestedPrefix, hidePersist
|
|
|
215
200
|
}
|
|
216
201
|
if (key.downArrow) {
|
|
217
202
|
setState((prev) => {
|
|
218
|
-
if (prev.selectedOption === "allow")
|
|
203
|
+
if (prev.selectedOption === "allow")
|
|
219
204
|
return {
|
|
220
205
|
...prev,
|
|
221
206
|
selectedOption: hidePersistentOption ? "alternative" : "auto",
|
|
222
207
|
};
|
|
223
|
-
}
|
|
224
208
|
if (prev.selectedOption === "auto")
|
|
225
209
|
return { ...prev, selectedOption: "alternative" };
|
|
226
210
|
return prev;
|
|
227
211
|
});
|
|
228
212
|
return;
|
|
229
213
|
}
|
|
230
|
-
// Handle text input for alternative option
|
|
231
214
|
if (input && !key.ctrl && !key.meta && !("alt" in key && key.alt)) {
|
|
232
|
-
// Focus on alternative option when user starts typing
|
|
233
|
-
setState((prev) => ({
|
|
234
|
-
selectedOption: "alternative",
|
|
235
|
-
alternativeText: prev.alternativeText + input,
|
|
236
|
-
hasUserInput: true,
|
|
237
|
-
}));
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
// Handle backspace and delete (same behavior - delete one character)
|
|
241
|
-
if (key.backspace || key.delete) {
|
|
242
215
|
setState((prev) => {
|
|
243
|
-
const
|
|
216
|
+
const nextText = prev.alternativeText.slice(0, prev.alternativeCursorPosition) +
|
|
217
|
+
input +
|
|
218
|
+
prev.alternativeText.slice(prev.alternativeCursorPosition);
|
|
244
219
|
return {
|
|
245
220
|
...prev,
|
|
246
221
|
selectedOption: "alternative",
|
|
247
|
-
alternativeText:
|
|
248
|
-
|
|
222
|
+
alternativeText: nextText,
|
|
223
|
+
alternativeCursorPosition: prev.alternativeCursorPosition + input.length,
|
|
224
|
+
hasUserInput: true,
|
|
249
225
|
};
|
|
250
226
|
});
|
|
251
227
|
return;
|
|
252
228
|
}
|
|
229
|
+
if (key.backspace || key.delete) {
|
|
230
|
+
setState((prev) => {
|
|
231
|
+
if (prev.alternativeCursorPosition > 0) {
|
|
232
|
+
const nextText = prev.alternativeText.slice(0, prev.alternativeCursorPosition - 1) +
|
|
233
|
+
prev.alternativeText.slice(prev.alternativeCursorPosition);
|
|
234
|
+
return {
|
|
235
|
+
...prev,
|
|
236
|
+
selectedOption: "alternative",
|
|
237
|
+
alternativeText: nextText,
|
|
238
|
+
alternativeCursorPosition: prev.alternativeCursorPosition - 1,
|
|
239
|
+
hasUserInput: nextText.length > 0,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
return prev;
|
|
243
|
+
});
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
253
246
|
});
|
|
254
247
|
const placeholderText = "Type here to tell Wave what to do differently";
|
|
255
248
|
const showPlaceholder = state.selectedOption === "alternative" && !state.hasUserInput;
|
|
256
|
-
return (_jsxs(Box, { flexDirection: "column",
|
|
249
|
+
return (_jsxs(Box, { flexDirection: "column", children: [toolName === ASK_USER_QUESTION_TOOL_NAME &&
|
|
257
250
|
currentQuestion &&
|
|
258
|
-
!isExpanded && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Box, { backgroundColor: getHeaderColor(currentQuestion.header), paddingX: 1, marginRight: 1, children: _jsx(Text, { color: "black", bold: true, children: currentQuestion.header.slice(0, 12).toUpperCase() }) }), _jsx(Text, { bold: true, children: currentQuestion.question })] }), _jsx(Box, { flexDirection: "column", children: (() => {
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
})
|
|
270
|
-
toolName === EXIT_PLAN_MODE_TOOL_NAME &&
|
|
271
|
-
!!toolInput?.plan_content && (_jsx(PlanDisplay, { plan: toolInput.plan_content, isExpanded: isExpanded })), 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
|
|
251
|
+
!isExpanded && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Box, { backgroundColor: getHeaderColor(currentQuestion.header), paddingX: 1, marginRight: 1, children: _jsx(Text, { color: "black", bold: true, children: currentQuestion.header.slice(0, 12).toUpperCase() }) }), _jsx(Text, { bold: true, children: currentQuestion.question })] }), _jsx(Box, { flexDirection: "column", children: [...currentQuestion.options, { label: "Other" }].map((option, index) => {
|
|
252
|
+
const isSelected = selectedOptionIndex === index;
|
|
253
|
+
const isChecked = currentQuestion.multiSelect
|
|
254
|
+
? selectedOptionIndices.has(index)
|
|
255
|
+
: isSelected;
|
|
256
|
+
const isOther = index === currentQuestion.options.length;
|
|
257
|
+
return (_jsx(Box, { children: _jsxs(Text, { color: isSelected ? "black" : "white", backgroundColor: isSelected ? "yellow" : undefined, children: [isSelected ? "> " : " ", currentQuestion.multiSelect
|
|
258
|
+
? isChecked
|
|
259
|
+
? "[x] "
|
|
260
|
+
: "[ ] "
|
|
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
|
|
272
263
|
? "Yes, proceed with default mode"
|
|
273
|
-
: "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 ||
|
|
274
|
-
"Type here to tell Wave what to do differently" }))] }) }, "alternative-option")] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Use \u2191\u2193 to navigate \u2022 ESC to cancel" }) })] }))] }));
|
|
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 do differently") }))] }) }, "alternative-option")] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Use \u2191\u2193 to navigate \u2022 ESC to cancel" }) })] }))] }));
|
|
275
265
|
};
|
|
266
|
+
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;AAUvC,UAAU,gBAAgB;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"DiffDisplay.d.ts","sourceRoot":"","sources":["../../src/components/DiffDisplay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkB,MAAM,OAAO,CAAC;AAUvC,UAAU,gBAAgB;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAySlD,CAAC"}
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import React, { useMemo } from "react";
|
|
3
|
-
import { Box, Text
|
|
3
|
+
import { Box, Text } from "ink";
|
|
4
4
|
import { WRITE_TOOL_NAME, EDIT_TOOL_NAME, MULTI_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 { stdout } = useStdout();
|
|
9
|
-
const maxHeight = useMemo(() => {
|
|
10
|
-
return Math.max(5, (stdout?.rows || 24) - 20);
|
|
11
|
-
}, [stdout?.rows]);
|
|
7
|
+
export const DiffDisplay = ({ toolName, parameters, }) => {
|
|
12
8
|
const showDiff = toolName &&
|
|
13
9
|
[WRITE_TOOL_NAME, EDIT_TOOL_NAME, MULTI_EDIT_TOOL_NAME].includes(toolName);
|
|
14
10
|
// Diff detection and transformation using typed parameters
|
|
@@ -58,7 +54,7 @@ export const DiffDisplay = ({ toolName, parameters, isExpanded = false, }) => {
|
|
|
58
54
|
};
|
|
59
55
|
}
|
|
60
56
|
};
|
|
61
|
-
// Render expanded diff display
|
|
57
|
+
// Render expanded diff display
|
|
62
58
|
const renderExpandedDiff = () => {
|
|
63
59
|
try {
|
|
64
60
|
if (changes.length === 0)
|
|
@@ -68,8 +64,8 @@ export const DiffDisplay = ({ toolName, parameters, isExpanded = false, }) => {
|
|
|
68
64
|
try {
|
|
69
65
|
// Get line-level diff to understand the structure
|
|
70
66
|
const lineDiffs = diffLines(change.oldContent || "", change.newContent || "");
|
|
67
|
+
// Process line diffs
|
|
71
68
|
const diffElements = [];
|
|
72
|
-
// Process line diffs and apply word-level diff to changed lines
|
|
73
69
|
lineDiffs.forEach((part, partIndex) => {
|
|
74
70
|
if (part.added) {
|
|
75
71
|
const lines = part.value
|
|
@@ -92,46 +88,76 @@ export const DiffDisplay = ({ toolName, parameters, isExpanded = false, }) => {
|
|
|
92
88
|
const lines = part.value
|
|
93
89
|
.split("\n")
|
|
94
90
|
.filter((line) => line !== "");
|
|
95
|
-
|
|
91
|
+
const isFirstBlock = partIndex === 0;
|
|
92
|
+
const isLastBlock = partIndex === lineDiffs.length - 1;
|
|
93
|
+
let linesToDisplay = lines;
|
|
94
|
+
let showEllipsisTop = false;
|
|
95
|
+
let showEllipsisBottom = false;
|
|
96
|
+
if (isFirstBlock && !isLastBlock) {
|
|
97
|
+
// First block: keep last 3
|
|
98
|
+
if (lines.length > 3) {
|
|
99
|
+
linesToDisplay = lines.slice(-3);
|
|
100
|
+
showEllipsisTop = true;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else if (isLastBlock && !isFirstBlock) {
|
|
104
|
+
// Last block: keep first 3
|
|
105
|
+
if (lines.length > 3) {
|
|
106
|
+
linesToDisplay = lines.slice(0, 3);
|
|
107
|
+
showEllipsisBottom = true;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
else if (!isFirstBlock && !isLastBlock) {
|
|
111
|
+
// Middle block: keep first 3 and last 3
|
|
112
|
+
if (lines.length > 6) {
|
|
113
|
+
linesToDisplay = [...lines.slice(0, 3), ...lines.slice(-3)];
|
|
114
|
+
showEllipsisTop = false; // We'll put ellipsis in the middle
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else if (isFirstBlock && isLastBlock) {
|
|
118
|
+
// Only one block (no changes?) - keep all or apply a general limit
|
|
119
|
+
// For now, let's keep all if it's the only block
|
|
120
|
+
}
|
|
121
|
+
if (showEllipsisTop) {
|
|
122
|
+
diffElements.push(_jsx(Box, { children: _jsx(Text, { color: "gray", children: " ..." }) }, `ellipsis-top-${changeIndex}-${partIndex}`));
|
|
123
|
+
}
|
|
124
|
+
linesToDisplay.forEach((line, lineIndex) => {
|
|
125
|
+
// If it's a middle block and we are at the split point
|
|
126
|
+
if (!isFirstBlock &&
|
|
127
|
+
!isLastBlock &&
|
|
128
|
+
lines.length > 6 &&
|
|
129
|
+
lineIndex === 3) {
|
|
130
|
+
diffElements.push(_jsx(Box, { children: _jsx(Text, { color: "gray", children: " ..." }) }, `ellipsis-mid-${changeIndex}-${partIndex}`));
|
|
131
|
+
}
|
|
96
132
|
diffElements.push(_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { color: "white", children: " " }), _jsx(Text, { color: "white", children: line })] }, `context-${changeIndex}-${partIndex}-${lineIndex}`));
|
|
97
133
|
});
|
|
134
|
+
if (showEllipsisBottom) {
|
|
135
|
+
diffElements.push(_jsx(Box, { children: _jsx(Text, { color: "gray", children: " ..." }) }, `ellipsis-bottom-${changeIndex}-${partIndex}`));
|
|
136
|
+
}
|
|
98
137
|
}
|
|
99
138
|
});
|
|
100
|
-
//
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const
|
|
109
|
-
const
|
|
110
|
-
if (
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
// Extract the text content from the removed and added lines
|
|
115
|
-
const removedText = extractTextFromElement(current);
|
|
116
|
-
const addedText = extractTextFromElement(next);
|
|
117
|
-
if (removedText && addedText) {
|
|
118
|
-
// Apply word-level diff
|
|
119
|
-
const { removedParts, addedParts } = renderWordLevelDiff(removedText, addedText, `word-${changeIndex}-${i}`);
|
|
120
|
-
allElements.push(_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { color: "red", children: "-" }), removedParts] }, `word-diff-removed-${changeIndex}-${i}`));
|
|
121
|
-
allElements.push(_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { color: "green", children: "+" }), addedParts] }, `word-diff-added-${changeIndex}-${i}`));
|
|
122
|
-
i += 2; // Skip the next element since we processed it
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
// Fallback to original elements
|
|
126
|
-
allElements.push(current);
|
|
127
|
-
i += 1;
|
|
128
|
-
}
|
|
139
|
+
// If it's a single line change (one removed, one added), use word-level diff
|
|
140
|
+
if (diffElements.length === 2 &&
|
|
141
|
+
React.isValidElement(diffElements[0]) &&
|
|
142
|
+
React.isValidElement(diffElements[1]) &&
|
|
143
|
+
typeof diffElements[0].key === "string" &&
|
|
144
|
+
diffElements[0].key.includes("remove-") &&
|
|
145
|
+
typeof diffElements[1].key === "string" &&
|
|
146
|
+
diffElements[1].key.includes("add-")) {
|
|
147
|
+
const removedText = extractTextFromElement(diffElements[0]);
|
|
148
|
+
const addedText = extractTextFromElement(diffElements[1]);
|
|
149
|
+
if (removedText && addedText) {
|
|
150
|
+
const { removedParts, addedParts } = renderWordLevelDiff(removedText, addedText, `word-${changeIndex}`);
|
|
151
|
+
allElements.push(_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { color: "red", children: "-" }), removedParts] }, `word-diff-removed-${changeIndex}`));
|
|
152
|
+
allElements.push(_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { color: "green", children: "+" }), addedParts] }, `word-diff-added-${changeIndex}`));
|
|
129
153
|
}
|
|
130
154
|
else {
|
|
131
|
-
allElements.push(
|
|
132
|
-
i += 1;
|
|
155
|
+
allElements.push(...diffElements);
|
|
133
156
|
}
|
|
134
157
|
}
|
|
158
|
+
else {
|
|
159
|
+
allElements.push(...diffElements);
|
|
160
|
+
}
|
|
135
161
|
}
|
|
136
162
|
catch (error) {
|
|
137
163
|
console.warn(`Error rendering diff for change ${changeIndex}:`, error);
|
|
@@ -139,35 +165,31 @@ export const DiffDisplay = ({ toolName, parameters, isExpanded = false, }) => {
|
|
|
139
165
|
allElements.push(_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "red", children: ["-", change.oldContent || ""] }), _jsxs(Text, { color: "green", children: ["+", change.newContent || ""] })] }, `fallback-${changeIndex}`));
|
|
140
166
|
}
|
|
141
167
|
});
|
|
142
|
-
|
|
143
|
-
const displayElements = isTruncated
|
|
144
|
-
? allElements.slice(0, maxHeight - 1)
|
|
145
|
-
: allElements;
|
|
146
|
-
return (_jsxs(Box, { flexDirection: "column", children: [displayElements, isTruncated && (_jsxs(Text, { color: "yellow", dimColor: true, children: ["... (truncated ", allElements.length - (maxHeight - 1), " more lines)"] }))] }));
|
|
168
|
+
return _jsx(Box, { flexDirection: "column", children: allElements });
|
|
147
169
|
}
|
|
148
170
|
catch (error) {
|
|
149
171
|
console.warn("Error rendering expanded diff:", error);
|
|
150
172
|
return (_jsx(Box, { children: _jsx(Text, { color: "gray", children: "Error rendering diff display" }) }));
|
|
151
173
|
}
|
|
152
174
|
};
|
|
153
|
-
// Helper function to extract text content from a React element
|
|
154
|
-
const extractTextFromElement = (element) => {
|
|
155
|
-
if (!React.isValidElement(element))
|
|
156
|
-
return null;
|
|
157
|
-
// Navigate through Box -> Text structure
|
|
158
|
-
const children = element.props.children;
|
|
159
|
-
if (Array.isArray(children) && children.length >= 2) {
|
|
160
|
-
const textElement = children[1]; // Second child should be the Text with content
|
|
161
|
-
if (React.isValidElement(textElement) &&
|
|
162
|
-
textElement.props.children) {
|
|
163
|
-
return textElement.props.children;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
return null;
|
|
167
|
-
};
|
|
168
175
|
// Don't render anything if no diff should be shown
|
|
169
176
|
if (!showDiff) {
|
|
170
177
|
return null;
|
|
171
178
|
}
|
|
172
179
|
return (_jsx(Box, { flexDirection: "column", children: _jsxs(Box, { paddingLeft: 2, borderLeft: true, borderColor: "cyan", flexDirection: "column", children: [_jsx(Text, { color: "cyan", bold: true, children: "Diff:" }), renderExpandedDiff()] }) }));
|
|
173
180
|
};
|
|
181
|
+
// Helper function to extract text content from a React element
|
|
182
|
+
const extractTextFromElement = (element) => {
|
|
183
|
+
if (!React.isValidElement(element))
|
|
184
|
+
return null;
|
|
185
|
+
// Navigate through Box -> Text structure
|
|
186
|
+
const children = element.props.children;
|
|
187
|
+
if (Array.isArray(children) && children.length >= 2) {
|
|
188
|
+
const textElement = children[1]; // Second child should be the Text with content
|
|
189
|
+
if (React.isValidElement(textElement) &&
|
|
190
|
+
textElement.props.children) {
|
|
191
|
+
return textElement.props.children;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return null;
|
|
195
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileSelector.d.ts","sourceRoot":"","sources":["../../src/components/FileSelector.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAExC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,
|
|
1
|
+
{"version":3,"file":"FileSelector.d.ts","sourceRoot":"","sources":["../../src/components/FileSelector.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAExC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CA2HpD,CAAC"}
|
|
@@ -24,7 +24,7 @@ export const FileSelector = ({ files, searchQuery, onSelect, onCancel, }) => {
|
|
|
24
24
|
}
|
|
25
25
|
});
|
|
26
26
|
if (files.length === 0) {
|
|
27
|
-
return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "yellow", borderBottom: false, borderLeft: false, borderRight: false,
|
|
27
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "yellow", borderBottom: false, borderLeft: false, borderRight: false, children: [_jsxs(Text, { color: "yellow", children: ["\uD83D\uDCC1 No files found for \"", searchQuery, "\""] }), _jsx(Text, { dimColor: true, children: "Press Escape to cancel" })] }));
|
|
28
28
|
}
|
|
29
29
|
const maxDisplay = 10;
|
|
30
30
|
// Calculate display window start and end positions
|
|
@@ -39,7 +39,7 @@ export const FileSelector = ({ files, searchQuery, onSelect, onCancel, }) => {
|
|
|
39
39
|
};
|
|
40
40
|
};
|
|
41
41
|
const { startIndex, endIndex, displayFiles } = getDisplayWindow();
|
|
42
|
-
return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "cyan", borderBottom: false, borderLeft: false, borderRight: false,
|
|
42
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "cyan", borderBottom: false, borderLeft: false, borderRight: false, children: [_jsxs(Text, { color: "cyan", bold: true, children: ["\uD83D\uDCC1 Select File/Directory", " ", searchQuery && `(filtering: "${searchQuery}")`] }), startIndex > 0 && (_jsxs(Text, { dimColor: true, children: ["... ", startIndex, " more files above"] })), displayFiles.map((fileItem, displayIndex) => {
|
|
43
43
|
const actualIndex = startIndex + displayIndex;
|
|
44
44
|
const isSelected = actualIndex === selectedIndex;
|
|
45
45
|
const icon = fileItem.type === "directory" ? "📁" : "📄";
|
|
@@ -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,CAiKtD,CAAC"}
|
|
@@ -3,6 +3,7 @@ import React, { useState, useEffect } from "react";
|
|
|
3
3
|
import { Box, Text, useInput } from "ink";
|
|
4
4
|
import { PromptHistoryManager } from "wave-agent-sdk";
|
|
5
5
|
export const HistorySearch = ({ searchQuery, onSelect, onCancel, }) => {
|
|
6
|
+
const MAX_VISIBLE_ITEMS = 5;
|
|
6
7
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
7
8
|
const [entries, setEntries] = useState([]);
|
|
8
9
|
const entriesRef = React.useRef([]);
|
|
@@ -16,8 +17,8 @@ export const HistorySearch = ({ searchQuery, onSelect, onCancel, }) => {
|
|
|
16
17
|
useEffect(() => {
|
|
17
18
|
const fetchHistory = async () => {
|
|
18
19
|
const results = await PromptHistoryManager.searchHistory(searchQuery);
|
|
19
|
-
const limitedResults = results.slice(0,
|
|
20
|
-
setEntries(limitedResults); // Limit to
|
|
20
|
+
const limitedResults = results.slice(0, 20);
|
|
21
|
+
setEntries(limitedResults); // Limit to 20 results
|
|
21
22
|
setSelectedIndex(0);
|
|
22
23
|
};
|
|
23
24
|
fetchHistory();
|
|
@@ -44,7 +45,7 @@ export const HistorySearch = ({ searchQuery, onSelect, onCancel, }) => {
|
|
|
44
45
|
}
|
|
45
46
|
});
|
|
46
47
|
if (entries.length === 0) {
|
|
47
|
-
return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "yellow", borderBottom: false, borderLeft: false, borderRight: false,
|
|
48
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "yellow", borderBottom: false, borderLeft: false, borderRight: false, children: [_jsxs(Text, { color: "yellow", children: ["No history found ", searchQuery && `for "${searchQuery}"`] }), _jsx(Text, { dimColor: true, children: "Press Escape to cancel" })] }));
|
|
48
49
|
}
|
|
49
50
|
const formatTimestamp = (timestamp) => {
|
|
50
51
|
const date = new Date(timestamp);
|
|
@@ -63,5 +64,12 @@ export const HistorySearch = ({ searchQuery, onSelect, onCancel, }) => {
|
|
|
63
64
|
return diffMinutes > 0 ? `${diffMinutes}m ago` : "just now";
|
|
64
65
|
}
|
|
65
66
|
};
|
|
66
|
-
|
|
67
|
+
// Calculate visible window
|
|
68
|
+
const startIndex = Math.max(0, Math.min(selectedIndex - Math.floor(MAX_VISIBLE_ITEMS / 2), Math.max(0, entries.length - MAX_VISIBLE_ITEMS)));
|
|
69
|
+
const visibleEntries = entries.slice(startIndex, startIndex + MAX_VISIBLE_ITEMS);
|
|
70
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "blue", borderBottom: false, borderLeft: false, borderRight: false, gap: 1, children: [_jsx(Box, { children: _jsxs(Text, { color: "blue", bold: true, children: ["Prompt History ", searchQuery && `(filtering: "${searchQuery}")`] }) }), _jsx(Box, { flexDirection: "column", children: visibleEntries.map((entry, index) => {
|
|
71
|
+
const actualIndex = startIndex + index;
|
|
72
|
+
const isSelected = actualIndex === selectedIndex;
|
|
73
|
+
return (_jsxs(Box, { flexDirection: "row", justifyContent: "space-between", children: [_jsx(Box, { flexShrink: 1, children: _jsx(Text, { color: isSelected ? "black" : "white", backgroundColor: isSelected ? "blue" : undefined, wrap: "truncate-end", children: entry.prompt.replace(/\n/g, " ") }) }), isSelected && (_jsx(Box, { marginLeft: 2, flexShrink: 0, children: _jsx(Text, { color: "gray", dimColor: true, children: formatTimestamp(entry.timestamp) }) }))] }, actualIndex));
|
|
74
|
+
}) }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: "Use \u2191\u2193 to navigate, Enter to select, Escape to cancel" }) })] }));
|
|
67
75
|
};
|