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,5 +1,5 @@
|
|
|
1
1
|
import React, { useMemo } from "react";
|
|
2
|
-
import { Box, Text
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
3
|
import {
|
|
4
4
|
WRITE_TOOL_NAME,
|
|
5
5
|
EDIT_TOOL_NAME,
|
|
@@ -11,19 +11,12 @@ import { diffLines, diffWords } from "diff";
|
|
|
11
11
|
interface DiffDisplayProps {
|
|
12
12
|
toolName?: string;
|
|
13
13
|
parameters?: string;
|
|
14
|
-
isExpanded?: boolean;
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
export const DiffDisplay: React.FC<DiffDisplayProps> = ({
|
|
18
17
|
toolName,
|
|
19
18
|
parameters,
|
|
20
|
-
isExpanded = false,
|
|
21
19
|
}) => {
|
|
22
|
-
const { stdout } = useStdout();
|
|
23
|
-
const maxHeight = useMemo(() => {
|
|
24
|
-
return Math.max(5, (stdout?.rows || 24) - 20);
|
|
25
|
-
}, [stdout?.rows]);
|
|
26
|
-
|
|
27
20
|
const showDiff =
|
|
28
21
|
toolName &&
|
|
29
22
|
[WRITE_TOOL_NAME, EDIT_TOOL_NAME, MULTI_EDIT_TOOL_NAME].includes(toolName);
|
|
@@ -107,7 +100,7 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
|
|
|
107
100
|
}
|
|
108
101
|
};
|
|
109
102
|
|
|
110
|
-
// Render expanded diff display
|
|
103
|
+
// Render expanded diff display
|
|
111
104
|
const renderExpandedDiff = () => {
|
|
112
105
|
try {
|
|
113
106
|
if (changes.length === 0) return null;
|
|
@@ -122,9 +115,8 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
|
|
|
122
115
|
change.newContent || "",
|
|
123
116
|
);
|
|
124
117
|
|
|
118
|
+
// Process line diffs
|
|
125
119
|
const diffElements: React.ReactNode[] = [];
|
|
126
|
-
|
|
127
|
-
// Process line diffs and apply word-level diff to changed lines
|
|
128
120
|
lineDiffs.forEach((part, partIndex) => {
|
|
129
121
|
if (part.added) {
|
|
130
122
|
const lines = part.value
|
|
@@ -161,7 +153,60 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
|
|
|
161
153
|
const lines = part.value
|
|
162
154
|
.split("\n")
|
|
163
155
|
.filter((line) => line !== "");
|
|
164
|
-
|
|
156
|
+
|
|
157
|
+
const isFirstBlock = partIndex === 0;
|
|
158
|
+
const isLastBlock = partIndex === lineDiffs.length - 1;
|
|
159
|
+
|
|
160
|
+
let linesToDisplay = lines;
|
|
161
|
+
let showEllipsisTop = false;
|
|
162
|
+
let showEllipsisBottom = false;
|
|
163
|
+
|
|
164
|
+
if (isFirstBlock && !isLastBlock) {
|
|
165
|
+
// First block: keep last 3
|
|
166
|
+
if (lines.length > 3) {
|
|
167
|
+
linesToDisplay = lines.slice(-3);
|
|
168
|
+
showEllipsisTop = true;
|
|
169
|
+
}
|
|
170
|
+
} else if (isLastBlock && !isFirstBlock) {
|
|
171
|
+
// Last block: keep first 3
|
|
172
|
+
if (lines.length > 3) {
|
|
173
|
+
linesToDisplay = lines.slice(0, 3);
|
|
174
|
+
showEllipsisBottom = true;
|
|
175
|
+
}
|
|
176
|
+
} else if (!isFirstBlock && !isLastBlock) {
|
|
177
|
+
// Middle block: keep first 3 and last 3
|
|
178
|
+
if (lines.length > 6) {
|
|
179
|
+
linesToDisplay = [...lines.slice(0, 3), ...lines.slice(-3)];
|
|
180
|
+
showEllipsisTop = false; // We'll put ellipsis in the middle
|
|
181
|
+
}
|
|
182
|
+
} else if (isFirstBlock && isLastBlock) {
|
|
183
|
+
// Only one block (no changes?) - keep all or apply a general limit
|
|
184
|
+
// For now, let's keep all if it's the only block
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (showEllipsisTop) {
|
|
188
|
+
diffElements.push(
|
|
189
|
+
<Box key={`ellipsis-top-${changeIndex}-${partIndex}`}>
|
|
190
|
+
<Text color="gray"> ...</Text>
|
|
191
|
+
</Box>,
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
linesToDisplay.forEach((line, lineIndex) => {
|
|
196
|
+
// If it's a middle block and we are at the split point
|
|
197
|
+
if (
|
|
198
|
+
!isFirstBlock &&
|
|
199
|
+
!isLastBlock &&
|
|
200
|
+
lines.length > 6 &&
|
|
201
|
+
lineIndex === 3
|
|
202
|
+
) {
|
|
203
|
+
diffElements.push(
|
|
204
|
+
<Box key={`ellipsis-mid-${changeIndex}-${partIndex}`}>
|
|
205
|
+
<Text color="gray"> ...</Text>
|
|
206
|
+
</Box>,
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
165
210
|
diffElements.push(
|
|
166
211
|
<Box
|
|
167
212
|
key={`context-${changeIndex}-${partIndex}-${lineIndex}`}
|
|
@@ -172,73 +217,57 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
|
|
|
172
217
|
</Box>,
|
|
173
218
|
);
|
|
174
219
|
});
|
|
220
|
+
|
|
221
|
+
if (showEllipsisBottom) {
|
|
222
|
+
diffElements.push(
|
|
223
|
+
<Box key={`ellipsis-bottom-${changeIndex}-${partIndex}`}>
|
|
224
|
+
<Text color="gray"> ...</Text>
|
|
225
|
+
</Box>,
|
|
226
|
+
);
|
|
227
|
+
}
|
|
175
228
|
}
|
|
176
229
|
});
|
|
177
230
|
|
|
178
|
-
//
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
231
|
+
// If it's a single line change (one removed, one added), use word-level diff
|
|
232
|
+
if (
|
|
233
|
+
diffElements.length === 2 &&
|
|
234
|
+
React.isValidElement(diffElements[0]) &&
|
|
235
|
+
React.isValidElement(diffElements[1]) &&
|
|
236
|
+
typeof diffElements[0].key === "string" &&
|
|
237
|
+
diffElements[0].key.includes("remove-") &&
|
|
238
|
+
typeof diffElements[1].key === "string" &&
|
|
239
|
+
diffElements[1].key.includes("add-")
|
|
240
|
+
) {
|
|
241
|
+
const removedText = extractTextFromElement(diffElements[0]);
|
|
242
|
+
const addedText = extractTextFromElement(diffElements[1]);
|
|
185
243
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
const isNextAdded =
|
|
193
|
-
typeof nextKey === "string" && nextKey.includes("add-");
|
|
194
|
-
|
|
195
|
-
if (
|
|
196
|
-
isCurrentRemoved &&
|
|
197
|
-
isNextAdded &&
|
|
198
|
-
React.isValidElement(current) &&
|
|
199
|
-
React.isValidElement(next)
|
|
200
|
-
) {
|
|
201
|
-
// Extract the text content from the removed and added lines
|
|
202
|
-
const removedText = extractTextFromElement(current);
|
|
203
|
-
const addedText = extractTextFromElement(next);
|
|
204
|
-
|
|
205
|
-
if (removedText && addedText) {
|
|
206
|
-
// Apply word-level diff
|
|
207
|
-
const { removedParts, addedParts } = renderWordLevelDiff(
|
|
208
|
-
removedText,
|
|
209
|
-
addedText,
|
|
210
|
-
`word-${changeIndex}-${i}`,
|
|
211
|
-
);
|
|
212
|
-
|
|
213
|
-
allElements.push(
|
|
214
|
-
<Box
|
|
215
|
-
key={`word-diff-removed-${changeIndex}-${i}`}
|
|
216
|
-
flexDirection="row"
|
|
217
|
-
>
|
|
218
|
-
<Text color="red">-</Text>
|
|
219
|
-
{removedParts}
|
|
220
|
-
</Box>,
|
|
221
|
-
);
|
|
222
|
-
allElements.push(
|
|
223
|
-
<Box
|
|
224
|
-
key={`word-diff-added-${changeIndex}-${i}`}
|
|
225
|
-
flexDirection="row"
|
|
226
|
-
>
|
|
227
|
-
<Text color="green">+</Text>
|
|
228
|
-
{addedParts}
|
|
229
|
-
</Box>,
|
|
230
|
-
);
|
|
244
|
+
if (removedText && addedText) {
|
|
245
|
+
const { removedParts, addedParts } = renderWordLevelDiff(
|
|
246
|
+
removedText,
|
|
247
|
+
addedText,
|
|
248
|
+
`word-${changeIndex}`,
|
|
249
|
+
);
|
|
231
250
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
251
|
+
allElements.push(
|
|
252
|
+
<Box
|
|
253
|
+
key={`word-diff-removed-${changeIndex}`}
|
|
254
|
+
flexDirection="row"
|
|
255
|
+
>
|
|
256
|
+
<Text color="red">-</Text>
|
|
257
|
+
{removedParts}
|
|
258
|
+
</Box>,
|
|
259
|
+
);
|
|
260
|
+
allElements.push(
|
|
261
|
+
<Box key={`word-diff-added-${changeIndex}`} flexDirection="row">
|
|
262
|
+
<Text color="green">+</Text>
|
|
263
|
+
{addedParts}
|
|
264
|
+
</Box>,
|
|
265
|
+
);
|
|
238
266
|
} else {
|
|
239
|
-
allElements.push(
|
|
240
|
-
i += 1;
|
|
267
|
+
allElements.push(...diffElements);
|
|
241
268
|
}
|
|
269
|
+
} else {
|
|
270
|
+
allElements.push(...diffElements);
|
|
242
271
|
}
|
|
243
272
|
} catch (error) {
|
|
244
273
|
console.warn(
|
|
@@ -255,21 +284,7 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
|
|
|
255
284
|
}
|
|
256
285
|
});
|
|
257
286
|
|
|
258
|
-
|
|
259
|
-
const displayElements = isTruncated
|
|
260
|
-
? allElements.slice(0, maxHeight - 1)
|
|
261
|
-
: allElements;
|
|
262
|
-
|
|
263
|
-
return (
|
|
264
|
-
<Box flexDirection="column">
|
|
265
|
-
{displayElements}
|
|
266
|
-
{isTruncated && (
|
|
267
|
-
<Text color="yellow" dimColor>
|
|
268
|
-
... (truncated {allElements.length - (maxHeight - 1)} more lines)
|
|
269
|
-
</Text>
|
|
270
|
-
)}
|
|
271
|
-
</Box>
|
|
272
|
-
);
|
|
287
|
+
return <Box flexDirection="column">{allElements}</Box>;
|
|
273
288
|
} catch (error) {
|
|
274
289
|
console.warn("Error rendering expanded diff:", error);
|
|
275
290
|
return (
|
|
@@ -280,26 +295,6 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
|
|
|
280
295
|
}
|
|
281
296
|
};
|
|
282
297
|
|
|
283
|
-
// Helper function to extract text content from a React element
|
|
284
|
-
const extractTextFromElement = (element: React.ReactNode): string | null => {
|
|
285
|
-
if (!React.isValidElement(element)) return null;
|
|
286
|
-
|
|
287
|
-
// Navigate through Box -> Text structure
|
|
288
|
-
const children = (
|
|
289
|
-
element.props as unknown as { children?: React.ReactNode[] }
|
|
290
|
-
).children;
|
|
291
|
-
if (Array.isArray(children) && children.length >= 2) {
|
|
292
|
-
const textElement = children[1]; // Second child should be the Text with content
|
|
293
|
-
if (
|
|
294
|
-
React.isValidElement(textElement) &&
|
|
295
|
-
(textElement.props as unknown as { children?: string }).children
|
|
296
|
-
) {
|
|
297
|
-
return (textElement.props as unknown as { children: string }).children;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
return null;
|
|
301
|
-
};
|
|
302
|
-
|
|
303
298
|
// Don't render anything if no diff should be shown
|
|
304
299
|
if (!showDiff) {
|
|
305
300
|
return null;
|
|
@@ -316,3 +311,23 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
|
|
|
316
311
|
</Box>
|
|
317
312
|
);
|
|
318
313
|
};
|
|
314
|
+
|
|
315
|
+
// Helper function to extract text content from a React element
|
|
316
|
+
const extractTextFromElement = (element: React.ReactNode): string | null => {
|
|
317
|
+
if (!React.isValidElement(element)) return null;
|
|
318
|
+
|
|
319
|
+
// Navigate through Box -> Text structure
|
|
320
|
+
const children = (
|
|
321
|
+
element.props as unknown as { children?: React.ReactNode[] }
|
|
322
|
+
).children;
|
|
323
|
+
if (Array.isArray(children) && children.length >= 2) {
|
|
324
|
+
const textElement = children[1]; // Second child should be the Text with content
|
|
325
|
+
if (
|
|
326
|
+
React.isValidElement(textElement) &&
|
|
327
|
+
(textElement.props as unknown as { children?: string }).children
|
|
328
|
+
) {
|
|
329
|
+
return (textElement.props as unknown as { children: string }).children;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return null;
|
|
333
|
+
};
|
|
@@ -52,7 +52,6 @@ export const FileSelector: React.FC<FileSelectorProps> = ({
|
|
|
52
52
|
borderBottom={false}
|
|
53
53
|
borderLeft={false}
|
|
54
54
|
borderRight={false}
|
|
55
|
-
paddingTop={1}
|
|
56
55
|
>
|
|
57
56
|
<Text color="yellow">📁 No files found for "{searchQuery}"</Text>
|
|
58
57
|
<Text dimColor>Press Escape to cancel</Text>
|
|
@@ -91,7 +90,6 @@ export const FileSelector: React.FC<FileSelectorProps> = ({
|
|
|
91
90
|
borderBottom={false}
|
|
92
91
|
borderLeft={false}
|
|
93
92
|
borderRight={false}
|
|
94
|
-
paddingTop={1}
|
|
95
93
|
>
|
|
96
94
|
<Text color="cyan" bold>
|
|
97
95
|
📁 Select File/Directory{" "}
|
|
@@ -13,6 +13,7 @@ export const HistorySearch: React.FC<HistorySearchProps> = ({
|
|
|
13
13
|
onSelect,
|
|
14
14
|
onCancel,
|
|
15
15
|
}) => {
|
|
16
|
+
const MAX_VISIBLE_ITEMS = 5;
|
|
16
17
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
17
18
|
const [entries, setEntries] = useState<PromptEntry[]>([]);
|
|
18
19
|
|
|
@@ -30,8 +31,8 @@ export const HistorySearch: React.FC<HistorySearchProps> = ({
|
|
|
30
31
|
useEffect(() => {
|
|
31
32
|
const fetchHistory = async () => {
|
|
32
33
|
const results = await PromptHistoryManager.searchHistory(searchQuery);
|
|
33
|
-
const limitedResults = results.slice(0,
|
|
34
|
-
setEntries(limitedResults); // Limit to
|
|
34
|
+
const limitedResults = results.slice(0, 20);
|
|
35
|
+
setEntries(limitedResults); // Limit to 20 results
|
|
35
36
|
setSelectedIndex(0);
|
|
36
37
|
};
|
|
37
38
|
fetchHistory();
|
|
@@ -75,7 +76,6 @@ export const HistorySearch: React.FC<HistorySearchProps> = ({
|
|
|
75
76
|
borderBottom={false}
|
|
76
77
|
borderLeft={false}
|
|
77
78
|
borderRight={false}
|
|
78
|
-
paddingTop={1}
|
|
79
79
|
>
|
|
80
80
|
<Text color="yellow">
|
|
81
81
|
No history found {searchQuery && `for "${searchQuery}"`}
|
|
@@ -102,6 +102,19 @@ export const HistorySearch: React.FC<HistorySearchProps> = ({
|
|
|
102
102
|
}
|
|
103
103
|
};
|
|
104
104
|
|
|
105
|
+
// Calculate visible window
|
|
106
|
+
const startIndex = Math.max(
|
|
107
|
+
0,
|
|
108
|
+
Math.min(
|
|
109
|
+
selectedIndex - Math.floor(MAX_VISIBLE_ITEMS / 2),
|
|
110
|
+
Math.max(0, entries.length - MAX_VISIBLE_ITEMS),
|
|
111
|
+
),
|
|
112
|
+
);
|
|
113
|
+
const visibleEntries = entries.slice(
|
|
114
|
+
startIndex,
|
|
115
|
+
startIndex + MAX_VISIBLE_ITEMS,
|
|
116
|
+
);
|
|
117
|
+
|
|
105
118
|
return (
|
|
106
119
|
<Box
|
|
107
120
|
flexDirection="column"
|
|
@@ -110,7 +123,6 @@ export const HistorySearch: React.FC<HistorySearchProps> = ({
|
|
|
110
123
|
borderBottom={false}
|
|
111
124
|
borderLeft={false}
|
|
112
125
|
borderRight={false}
|
|
113
|
-
paddingTop={1}
|
|
114
126
|
gap={1}
|
|
115
127
|
>
|
|
116
128
|
<Box>
|
|
@@ -119,24 +131,36 @@ export const HistorySearch: React.FC<HistorySearchProps> = ({
|
|
|
119
131
|
</Text>
|
|
120
132
|
</Box>
|
|
121
133
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
134
|
+
<Box flexDirection="column">
|
|
135
|
+
{visibleEntries.map((entry, index) => {
|
|
136
|
+
const actualIndex = startIndex + index;
|
|
137
|
+
const isSelected = actualIndex === selectedIndex;
|
|
138
|
+
return (
|
|
139
|
+
<Box
|
|
140
|
+
key={actualIndex}
|
|
141
|
+
flexDirection="row"
|
|
142
|
+
justifyContent="space-between"
|
|
143
|
+
>
|
|
144
|
+
<Box flexShrink={1}>
|
|
145
|
+
<Text
|
|
146
|
+
color={isSelected ? "black" : "white"}
|
|
147
|
+
backgroundColor={isSelected ? "blue" : undefined}
|
|
148
|
+
wrap="truncate-end"
|
|
149
|
+
>
|
|
150
|
+
{entry.prompt.replace(/\n/g, " ")}
|
|
151
|
+
</Text>
|
|
152
|
+
</Box>
|
|
153
|
+
{isSelected && (
|
|
154
|
+
<Box marginLeft={2} flexShrink={0}>
|
|
155
|
+
<Text color="gray" dimColor>
|
|
156
|
+
{formatTimestamp(entry.timestamp)}
|
|
157
|
+
</Text>
|
|
158
|
+
</Box>
|
|
159
|
+
)}
|
|
136
160
|
</Box>
|
|
137
|
-
)
|
|
138
|
-
|
|
139
|
-
|
|
161
|
+
);
|
|
162
|
+
})}
|
|
163
|
+
</Box>
|
|
140
164
|
|
|
141
165
|
<Box>
|
|
142
166
|
<Text dimColor>
|
|
@@ -4,8 +4,7 @@ import { useInput } from "ink";
|
|
|
4
4
|
import { FileSelector } from "./FileSelector.js";
|
|
5
5
|
import { CommandSelector } from "./CommandSelector.js";
|
|
6
6
|
import { HistorySearch } from "./HistorySearch.js";
|
|
7
|
-
import {
|
|
8
|
-
import { BashShellManager } from "./BashShellManager.js";
|
|
7
|
+
import { BackgroundTaskManager } from "./BackgroundTaskManager.js";
|
|
9
8
|
import { McpManager } from "./McpManager.js";
|
|
10
9
|
import { RewindCommand } from "./RewindCommand.js";
|
|
11
10
|
import { useInputManager } from "../hooks/useInputManager.js";
|
|
@@ -14,7 +13,7 @@ import { useChat } from "../contexts/useChat.js";
|
|
|
14
13
|
import type { McpServerStatus, SlashCommand } from "wave-agent-sdk";
|
|
15
14
|
|
|
16
15
|
export const INPUT_PLACEHOLDER_TEXT =
|
|
17
|
-
"Type your message (use @ to reference files, / for commands,
|
|
16
|
+
"Type your message (use @ to reference files, / for commands, Ctrl+R to search history, Ctrl+O to expand messages, Ctrl+T to toggle tasks)...";
|
|
18
17
|
|
|
19
18
|
export const INPUT_PLACEHOLDER_TEXT_PREFIX = INPUT_PLACEHOLDER_TEXT.substring(
|
|
20
19
|
0,
|
|
@@ -25,13 +24,11 @@ export interface InputBoxProps {
|
|
|
25
24
|
isLoading?: boolean;
|
|
26
25
|
isCommandRunning?: boolean;
|
|
27
26
|
workdir?: string;
|
|
28
|
-
userInputHistory?: string[];
|
|
29
27
|
sendMessage?: (
|
|
30
28
|
message: string,
|
|
31
29
|
images?: Array<{ path: string; mimeType: string }>,
|
|
32
30
|
) => void;
|
|
33
31
|
abortMessage?: () => void;
|
|
34
|
-
saveMemory?: (message: string, type: "project" | "user") => Promise<void>;
|
|
35
32
|
// MCP related properties
|
|
36
33
|
mcpServers?: McpServerStatus[];
|
|
37
34
|
connectMcpServer?: (serverName: string) => Promise<boolean>;
|
|
@@ -44,10 +41,8 @@ export interface InputBoxProps {
|
|
|
44
41
|
export const InputBox: React.FC<InputBoxProps> = ({
|
|
45
42
|
isLoading = false,
|
|
46
43
|
isCommandRunning = false,
|
|
47
|
-
userInputHistory = [],
|
|
48
44
|
sendMessage = () => {},
|
|
49
45
|
abortMessage = () => {},
|
|
50
|
-
saveMemory = async () => {},
|
|
51
46
|
mcpServers = [],
|
|
52
47
|
connectMcpServer = async () => false,
|
|
53
48
|
disconnectMcpServer = async () => false,
|
|
@@ -58,7 +53,9 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
58
53
|
permissionMode: chatPermissionMode,
|
|
59
54
|
setPermissionMode: setChatPermissionMode,
|
|
60
55
|
handleRewindSelect,
|
|
56
|
+
backgroundCurrentTask,
|
|
61
57
|
messages,
|
|
58
|
+
getFullMessageThread,
|
|
62
59
|
} = useChat();
|
|
63
60
|
|
|
64
61
|
// Input manager with all input state and functionality (including images)
|
|
@@ -82,26 +79,19 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
82
79
|
handleCancelCommandSelect,
|
|
83
80
|
handleHistorySearchSelect,
|
|
84
81
|
handleCancelHistorySearch,
|
|
85
|
-
// Memory type selector
|
|
86
|
-
showMemoryTypeSelector,
|
|
87
|
-
memoryMessage,
|
|
88
|
-
handleMemoryTypeSelect,
|
|
89
|
-
handleCancelMemoryTypeSelect,
|
|
90
82
|
// History search
|
|
91
83
|
showHistorySearch,
|
|
92
84
|
historySearchQuery,
|
|
93
|
-
//
|
|
94
|
-
|
|
85
|
+
// Task/MCP Manager
|
|
86
|
+
showBackgroundTaskManager,
|
|
95
87
|
showMcpManager,
|
|
96
88
|
showRewindManager,
|
|
97
|
-
|
|
89
|
+
setShowBackgroundTaskManager,
|
|
98
90
|
setShowMcpManager,
|
|
99
91
|
setShowRewindManager,
|
|
100
92
|
// Permission mode
|
|
101
93
|
permissionMode,
|
|
102
94
|
setPermissionMode,
|
|
103
|
-
// Input history
|
|
104
|
-
setUserInputHistory,
|
|
105
95
|
// Main handler
|
|
106
96
|
handleInput,
|
|
107
97
|
// Manager ready state
|
|
@@ -109,8 +99,8 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
109
99
|
} = useInputManager({
|
|
110
100
|
onSendMessage: sendMessage,
|
|
111
101
|
onHasSlashCommand: hasSlashCommand,
|
|
112
|
-
onSaveMemory: saveMemory,
|
|
113
102
|
onAbortMessage: abortMessage,
|
|
103
|
+
onBackgroundCurrentTask: backgroundCurrentTask,
|
|
114
104
|
onPermissionModeChange: setChatPermissionMode,
|
|
115
105
|
});
|
|
116
106
|
|
|
@@ -119,11 +109,6 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
119
109
|
setPermissionMode(chatPermissionMode);
|
|
120
110
|
}, [chatPermissionMode, setPermissionMode]);
|
|
121
111
|
|
|
122
|
-
// Set user input history when it changes
|
|
123
|
-
useEffect(() => {
|
|
124
|
-
setUserInputHistory(userInputHistory);
|
|
125
|
-
}, [userInputHistory, setUserInputHistory]);
|
|
126
|
-
|
|
127
112
|
// Use the InputManager's unified input handler
|
|
128
113
|
useInput(async (input, key) => {
|
|
129
114
|
await handleInput(
|
|
@@ -175,6 +160,7 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
175
160
|
messages={messages}
|
|
176
161
|
onSelect={handleRewindSelectWithClose}
|
|
177
162
|
onCancel={handleRewindCancel}
|
|
163
|
+
getFullMessageThread={getFullMessageThread}
|
|
178
164
|
/>
|
|
179
165
|
);
|
|
180
166
|
}
|
|
@@ -208,18 +194,12 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
208
194
|
/>
|
|
209
195
|
)}
|
|
210
196
|
|
|
211
|
-
{
|
|
212
|
-
<
|
|
213
|
-
|
|
214
|
-
onSelect={handleMemoryTypeSelect}
|
|
215
|
-
onCancel={handleCancelMemoryTypeSelect}
|
|
197
|
+
{showBackgroundTaskManager && (
|
|
198
|
+
<BackgroundTaskManager
|
|
199
|
+
onCancel={() => setShowBackgroundTaskManager(false)}
|
|
216
200
|
/>
|
|
217
201
|
)}
|
|
218
202
|
|
|
219
|
-
{showBashManager && (
|
|
220
|
-
<BashShellManager onCancel={() => setShowBashManager(false)} />
|
|
221
|
-
)}
|
|
222
|
-
|
|
223
203
|
{showMcpManager && (
|
|
224
204
|
<McpManager
|
|
225
205
|
onCancel={() => setShowMcpManager(false)}
|
|
@@ -229,7 +209,7 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
229
209
|
/>
|
|
230
210
|
)}
|
|
231
211
|
|
|
232
|
-
{
|
|
212
|
+
{showBackgroundTaskManager || showMcpManager || showRewindManager || (
|
|
233
213
|
<Box flexDirection="column">
|
|
234
214
|
<Box
|
|
235
215
|
borderStyle="single"
|
|
@@ -251,7 +231,7 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
251
231
|
)}
|
|
252
232
|
</Text>
|
|
253
233
|
</Box>
|
|
254
|
-
<Box paddingRight={1}>
|
|
234
|
+
<Box paddingRight={1} justifyContent="space-between" width="100%">
|
|
255
235
|
<Text color="gray">
|
|
256
236
|
Mode:{" "}
|
|
257
237
|
<Text color={permissionMode === "plan" ? "yellow" : "cyan"}>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
|
|
4
|
+
export interface LoadingIndicatorProps {
|
|
5
|
+
isLoading?: boolean;
|
|
6
|
+
isCommandRunning?: boolean;
|
|
7
|
+
isCompressing?: boolean;
|
|
8
|
+
latestTotalTokens?: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const LoadingIndicator = ({
|
|
12
|
+
isLoading = false,
|
|
13
|
+
isCommandRunning = false,
|
|
14
|
+
isCompressing = false,
|
|
15
|
+
latestTotalTokens = 0,
|
|
16
|
+
}: LoadingIndicatorProps) => {
|
|
17
|
+
return (
|
|
18
|
+
<Box flexDirection="column">
|
|
19
|
+
{isLoading && (
|
|
20
|
+
<Box>
|
|
21
|
+
<Text color="yellow">💭 AI is thinking... </Text>
|
|
22
|
+
{latestTotalTokens > 0 && (
|
|
23
|
+
<>
|
|
24
|
+
<Text color="gray" dimColor>
|
|
25
|
+
|{" "}
|
|
26
|
+
</Text>
|
|
27
|
+
<Text color="blue" bold>
|
|
28
|
+
{latestTotalTokens.toLocaleString()}
|
|
29
|
+
</Text>
|
|
30
|
+
<Text color="gray" dimColor>
|
|
31
|
+
{" "}
|
|
32
|
+
tokens{" "}
|
|
33
|
+
</Text>
|
|
34
|
+
</>
|
|
35
|
+
)}
|
|
36
|
+
<Text color="gray" dimColor>
|
|
37
|
+
|{" "}
|
|
38
|
+
</Text>
|
|
39
|
+
<Text color="red" bold>
|
|
40
|
+
Esc
|
|
41
|
+
</Text>
|
|
42
|
+
<Text color="gray" dimColor>
|
|
43
|
+
{" "}
|
|
44
|
+
to abort
|
|
45
|
+
</Text>
|
|
46
|
+
</Box>
|
|
47
|
+
)}
|
|
48
|
+
{isCommandRunning && <Text color="blue">🚀 Command is running...</Text>}
|
|
49
|
+
{isCompressing && (
|
|
50
|
+
<Text color="magenta">🗜️ Compressing message history...</Text>
|
|
51
|
+
)}
|
|
52
|
+
</Box>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
LoadingIndicator.displayName = "LoadingIndicator";
|