wave-code 0.0.4 → 0.0.6
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/README.md +2 -2
- package/dist/components/ChatInterface.d.ts.map +1 -1
- package/dist/components/ChatInterface.js +4 -24
- package/dist/components/CommandSelector.js +4 -4
- package/dist/components/DiffViewer.d.ts +1 -1
- package/dist/components/DiffViewer.d.ts.map +1 -1
- package/dist/components/DiffViewer.js +15 -15
- package/dist/components/FileSelector.js +2 -2
- package/dist/components/InputBox.d.ts.map +1 -1
- package/dist/components/InputBox.js +46 -101
- package/dist/components/Markdown.d.ts +6 -0
- package/dist/components/Markdown.d.ts.map +1 -0
- package/dist/components/Markdown.js +22 -0
- package/dist/components/MessageItem.d.ts +9 -0
- package/dist/components/MessageItem.d.ts.map +1 -0
- package/dist/components/MessageItem.js +15 -0
- package/dist/components/MessageList.d.ts +1 -1
- package/dist/components/MessageList.d.ts.map +1 -1
- package/dist/components/MessageList.js +33 -32
- package/dist/components/SubagentBlock.d.ts +1 -2
- package/dist/components/SubagentBlock.d.ts.map +1 -1
- package/dist/components/SubagentBlock.js +29 -20
- package/dist/components/ToolResultDisplay.js +5 -5
- package/dist/contexts/useChat.d.ts +1 -0
- package/dist/contexts/useChat.d.ts.map +1 -1
- package/dist/contexts/useChat.js +29 -2
- package/dist/hooks/useInputManager.d.ts +93 -0
- package/dist/hooks/useInputManager.d.ts.map +1 -0
- package/dist/hooks/useInputManager.js +332 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -10
- package/dist/managers/InputManager.d.ts +171 -0
- package/dist/managers/InputManager.d.ts.map +1 -0
- package/dist/managers/InputManager.js +826 -0
- package/dist/print-cli.d.ts +8 -0
- package/dist/print-cli.d.ts.map +1 -0
- package/dist/print-cli.js +128 -0
- package/dist/utils/constants.d.ts +1 -1
- package/dist/utils/constants.js +1 -1
- package/dist/utils/fileSearch.d.ts +20 -0
- package/dist/utils/fileSearch.d.ts.map +1 -0
- package/dist/utils/fileSearch.js +102 -0
- package/dist/utils/logger.js +3 -3
- package/dist/utils/usageSummary.d.ts +33 -0
- package/dist/utils/usageSummary.d.ts.map +1 -0
- package/dist/utils/usageSummary.js +154 -0
- package/package.json +10 -6
- package/src/components/ChatInterface.tsx +13 -43
- package/src/components/CommandSelector.tsx +5 -5
- package/src/components/DiffViewer.tsx +18 -16
- package/src/components/FileSelector.tsx +2 -2
- package/src/components/InputBox.tsx +78 -169
- package/src/components/Markdown.tsx +29 -0
- package/src/components/MessageItem.tsx +104 -0
- package/src/components/MessageList.tsx +142 -198
- package/src/components/SubagentBlock.tsx +56 -73
- package/src/components/ToolResultDisplay.tsx +6 -6
- package/src/contexts/useChat.tsx +34 -2
- package/src/hooks/useInputManager.ts +461 -0
- package/src/index.ts +20 -10
- package/src/managers/InputManager.ts +1132 -0
- package/src/print-cli.ts +160 -0
- package/src/utils/constants.ts +1 -1
- package/src/utils/fileSearch.ts +133 -0
- package/src/utils/logger.ts +3 -3
- package/src/utils/usageSummary.ts +234 -0
- package/dist/hooks/useBashHistorySelector.d.ts +0 -15
- package/dist/hooks/useBashHistorySelector.d.ts.map +0 -1
- package/dist/hooks/useBashHistorySelector.js +0 -61
- package/dist/hooks/useCommandSelector.d.ts +0 -24
- package/dist/hooks/useCommandSelector.d.ts.map +0 -1
- package/dist/hooks/useCommandSelector.js +0 -98
- package/dist/hooks/useFileSelector.d.ts +0 -16
- package/dist/hooks/useFileSelector.d.ts.map +0 -1
- package/dist/hooks/useFileSelector.js +0 -174
- package/dist/hooks/useImageManager.d.ts +0 -13
- package/dist/hooks/useImageManager.d.ts.map +0 -1
- package/dist/hooks/useImageManager.js +0 -46
- package/dist/hooks/useInputHistory.d.ts +0 -11
- package/dist/hooks/useInputHistory.d.ts.map +0 -1
- package/dist/hooks/useInputHistory.js +0 -64
- package/dist/hooks/useInputKeyboardHandler.d.ts +0 -83
- package/dist/hooks/useInputKeyboardHandler.d.ts.map +0 -1
- package/dist/hooks/useInputKeyboardHandler.js +0 -507
- package/dist/hooks/useInputState.d.ts +0 -14
- package/dist/hooks/useInputState.d.ts.map +0 -1
- package/dist/hooks/useInputState.js +0 -57
- package/dist/hooks/useMemoryTypeSelector.d.ts +0 -9
- package/dist/hooks/useMemoryTypeSelector.d.ts.map +0 -1
- package/dist/hooks/useMemoryTypeSelector.js +0 -27
- package/dist/plain-cli.d.ts +0 -7
- package/dist/plain-cli.d.ts.map +0 -1
- package/dist/plain-cli.js +0 -44
- package/src/hooks/useBashHistorySelector.ts +0 -77
- package/src/hooks/useCommandSelector.ts +0 -131
- package/src/hooks/useFileSelector.ts +0 -227
- package/src/hooks/useImageManager.ts +0 -64
- package/src/hooks/useInputHistory.ts +0 -74
- package/src/hooks/useInputKeyboardHandler.ts +0 -778
- package/src/hooks/useInputState.ts +0 -66
- package/src/hooks/useMemoryTypeSelector.ts +0 -40
- package/src/plain-cli.ts +0 -60
|
@@ -5,7 +5,7 @@ import type { DiffBlock } from "wave-agent-sdk";
|
|
|
5
5
|
|
|
6
6
|
interface DiffViewerProps {
|
|
7
7
|
block: DiffBlock;
|
|
8
|
-
|
|
8
|
+
isStatic?: boolean;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
// Render word-level diff
|
|
@@ -48,7 +48,7 @@ const renderWordLevelDiff = (removedLine: string, addedLine: string) => {
|
|
|
48
48
|
|
|
49
49
|
export const DiffViewer: React.FC<DiffViewerProps> = ({
|
|
50
50
|
block,
|
|
51
|
-
|
|
51
|
+
isStatic = true,
|
|
52
52
|
}) => {
|
|
53
53
|
const { diffResult } = block;
|
|
54
54
|
|
|
@@ -246,21 +246,22 @@ export const DiffViewer: React.FC<DiffViewerProps> = ({
|
|
|
246
246
|
// Handle remaining deleted lines at the end
|
|
247
247
|
flushPendingLines();
|
|
248
248
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
type: "separator",
|
|
257
|
-
});
|
|
258
|
-
return truncatedLines;
|
|
259
|
-
}
|
|
249
|
+
return lines;
|
|
250
|
+
}, [diffResult]);
|
|
251
|
+
|
|
252
|
+
// Truncate to last 10 lines for non-static items
|
|
253
|
+
const displayLines = useMemo(() => {
|
|
254
|
+
if (isStatic) {
|
|
255
|
+
return diffLines;
|
|
260
256
|
}
|
|
261
257
|
|
|
262
|
-
|
|
263
|
-
|
|
258
|
+
const MAX_LINES = 10;
|
|
259
|
+
if (diffLines.length <= MAX_LINES) {
|
|
260
|
+
return diffLines;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return diffLines.slice(-MAX_LINES);
|
|
264
|
+
}, [diffLines, isStatic]);
|
|
264
265
|
|
|
265
266
|
if (!diffResult || diffResult.length === 0) {
|
|
266
267
|
return (
|
|
@@ -270,11 +271,12 @@ export const DiffViewer: React.FC<DiffViewerProps> = ({
|
|
|
270
271
|
);
|
|
271
272
|
}
|
|
272
273
|
|
|
274
|
+
// Show traditional diff view
|
|
273
275
|
return (
|
|
274
276
|
<Box flexDirection="column">
|
|
275
277
|
<Box flexDirection="column">
|
|
276
278
|
<Box flexDirection="column">
|
|
277
|
-
{
|
|
279
|
+
{displayLines.map((line, index) => {
|
|
278
280
|
// If has word-level diff, render special effects
|
|
279
281
|
if (line.wordDiff) {
|
|
280
282
|
const prefix = line.type === "removed" ? "- " : "+ ";
|
|
@@ -22,7 +22,7 @@ export const FileSelector: React.FC<FileSelectorProps> = ({
|
|
|
22
22
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
23
23
|
|
|
24
24
|
useInput((input, key) => {
|
|
25
|
-
if (key.return) {
|
|
25
|
+
if (key.return || key.tab) {
|
|
26
26
|
if (files.length > 0 && selectedIndex < files.length) {
|
|
27
27
|
onSelect(files[selectedIndex].path);
|
|
28
28
|
}
|
|
@@ -126,7 +126,7 @@ export const FileSelector: React.FC<FileSelectorProps> = ({
|
|
|
126
126
|
|
|
127
127
|
<Box marginTop={1}>
|
|
128
128
|
<Text dimColor>
|
|
129
|
-
Use ↑↓ to navigate, Enter to select, Escape to cancel
|
|
129
|
+
Use ↑↓ to navigate, Enter/Tab to select, Escape to cancel
|
|
130
130
|
</Text>
|
|
131
131
|
<Text dimColor>
|
|
132
132
|
File {selectedIndex + 1} of {files.length}
|
|
@@ -1,19 +1,14 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useEffect, useMemo } from "react";
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
|
+
import { useInput } from "ink";
|
|
3
4
|
import { FileSelector } from "./FileSelector.js";
|
|
4
5
|
import { CommandSelector } from "./CommandSelector.js";
|
|
5
6
|
import { BashHistorySelector } from "./BashHistorySelector.js";
|
|
6
7
|
import { MemoryTypeSelector } from "./MemoryTypeSelector.js";
|
|
7
8
|
import { BashShellManager } from "./BashShellManager.js";
|
|
8
9
|
import { McpManager } from "./McpManager.js";
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
import { useCommandSelector } from "../hooks/useCommandSelector.js";
|
|
12
|
-
import { useBashHistorySelector } from "../hooks/useBashHistorySelector.js";
|
|
13
|
-
import { useMemoryTypeSelector } from "../hooks/useMemoryTypeSelector.js";
|
|
14
|
-
import { useInputHistory } from "../hooks/useInputHistory.js";
|
|
15
|
-
import { useInputKeyboardHandler } from "../hooks/useInputKeyboardHandler.js";
|
|
16
|
-
import { useImageManager } from "../hooks/useImageManager.js";
|
|
10
|
+
import { useInputManager } from "../hooks/useInputManager.js";
|
|
11
|
+
|
|
17
12
|
import type { McpServerStatus, SlashCommand } from "wave-agent-sdk";
|
|
18
13
|
|
|
19
14
|
export const INPUT_PLACEHOLDER_TEXT =
|
|
@@ -58,172 +53,83 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
58
53
|
slashCommands = [],
|
|
59
54
|
hasSlashCommand = () => false,
|
|
60
55
|
}) => {
|
|
61
|
-
// Get current working directory
|
|
62
|
-
const currentWorkdir = workdir || process.cwd();
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
// MCP manager state
|
|
66
|
-
const [showMcpManager, setShowMcpManager] = useState(false);
|
|
67
|
-
// Basic input state
|
|
56
|
+
// Get current working directory - memoized to avoid repeated process.cwd() calls
|
|
57
|
+
const currentWorkdir = useMemo(() => workdir || process.cwd(), [workdir]);
|
|
58
|
+
|
|
59
|
+
// Input manager with all input state and functionality (including images)
|
|
68
60
|
const {
|
|
69
61
|
inputText,
|
|
70
|
-
setInputText,
|
|
71
62
|
cursorPosition,
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
moveCursorLeft,
|
|
77
|
-
moveCursorRight,
|
|
78
|
-
moveCursorToStart,
|
|
79
|
-
moveCursorToEnd,
|
|
80
|
-
} = useInputState();
|
|
81
|
-
|
|
82
|
-
// File selector functionality
|
|
83
|
-
const {
|
|
63
|
+
// Image management
|
|
64
|
+
attachedImages,
|
|
65
|
+
clearImages,
|
|
66
|
+
// File selector
|
|
84
67
|
showFileSelector,
|
|
85
68
|
filteredFiles,
|
|
86
|
-
searchQuery,
|
|
87
|
-
|
|
88
|
-
handleFileSelect: handleFileSelectorSelect,
|
|
69
|
+
fileSearchQuery: searchQuery,
|
|
70
|
+
handleFileSelect,
|
|
89
71
|
handleCancelFileSelect,
|
|
90
|
-
|
|
91
|
-
checkForAtDeletion,
|
|
92
|
-
atPosition,
|
|
93
|
-
} = useFileSelector();
|
|
94
|
-
|
|
95
|
-
// Command selector functionality
|
|
96
|
-
const {
|
|
72
|
+
// Command selector
|
|
97
73
|
showCommandSelector,
|
|
98
74
|
commandSearchQuery,
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
handleCommandInsert: handleCommandSelectorInsert,
|
|
75
|
+
handleCommandSelect,
|
|
76
|
+
handleCommandInsert,
|
|
102
77
|
handleCancelCommandSelect,
|
|
103
|
-
|
|
104
|
-
checkForSlashDeletion,
|
|
105
|
-
slashPosition,
|
|
106
|
-
} = useCommandSelector({
|
|
107
|
-
onShowBashManager: () => setShowBashManager(true),
|
|
108
|
-
onShowMcpManager: () => setShowMcpManager(true),
|
|
109
|
-
sendMessage: async (content: string) => {
|
|
110
|
-
await sendMessage(content);
|
|
111
|
-
},
|
|
112
|
-
hasSlashCommand,
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
// Bash history selector functionality
|
|
116
|
-
const {
|
|
78
|
+
// Bash history selector
|
|
117
79
|
showBashHistorySelector,
|
|
118
80
|
bashHistorySearchQuery,
|
|
119
|
-
|
|
120
|
-
handleBashHistorySelect: handleBashHistorySelectorSelect,
|
|
121
|
-
handleBashHistoryExecute,
|
|
81
|
+
handleBashHistorySelect,
|
|
122
82
|
handleCancelBashHistorySelect,
|
|
123
|
-
|
|
124
|
-
checkForExclamationDeletion,
|
|
125
|
-
exclamationPosition,
|
|
126
|
-
} = useBashHistorySelector();
|
|
127
|
-
|
|
128
|
-
// Memory type selector functionality
|
|
129
|
-
const {
|
|
83
|
+
// Memory type selector
|
|
130
84
|
showMemoryTypeSelector,
|
|
131
85
|
memoryMessage,
|
|
132
|
-
|
|
133
|
-
handleMemoryTypeSelect: handleMemoryTypeSelectorSelect,
|
|
86
|
+
handleMemoryTypeSelect,
|
|
134
87
|
handleCancelMemoryTypeSelect,
|
|
135
|
-
|
|
88
|
+
// Bash/MCP Manager
|
|
89
|
+
showBashManager,
|
|
90
|
+
showMcpManager,
|
|
91
|
+
setShowBashManager,
|
|
92
|
+
setShowMcpManager,
|
|
93
|
+
// Input history
|
|
94
|
+
setUserInputHistory,
|
|
95
|
+
// Complex handlers combining multiple operations
|
|
96
|
+
handleBashHistoryExecuteAndSend,
|
|
97
|
+
// Main handler
|
|
98
|
+
handleInput,
|
|
99
|
+
// Manager ready state
|
|
100
|
+
isManagerReady,
|
|
101
|
+
} = useInputManager({
|
|
102
|
+
onSendMessage: sendMessage,
|
|
103
|
+
onHasSlashCommand: hasSlashCommand,
|
|
104
|
+
onSaveMemory: saveMemory,
|
|
105
|
+
onAbortMessage: abortMessage,
|
|
106
|
+
});
|
|
136
107
|
|
|
137
|
-
//
|
|
138
|
-
|
|
139
|
-
userInputHistory
|
|
108
|
+
// Set user input history when it changes
|
|
109
|
+
useEffect(() => {
|
|
110
|
+
setUserInputHistory(userInputHistory);
|
|
111
|
+
}, [userInputHistory, setUserInputHistory]);
|
|
112
|
+
|
|
113
|
+
// Use the InputManager's unified input handler
|
|
114
|
+
useInput(async (input, key) => {
|
|
115
|
+
await handleInput(
|
|
116
|
+
input,
|
|
117
|
+
key,
|
|
118
|
+
attachedImages,
|
|
119
|
+
isLoading,
|
|
120
|
+
isCommandRunning,
|
|
121
|
+
clearImages,
|
|
122
|
+
);
|
|
140
123
|
});
|
|
141
124
|
|
|
142
|
-
//
|
|
143
|
-
const { attachedImages, clearImages, handlePasteImage } =
|
|
144
|
-
useImageManager(insertTextAtCursor);
|
|
125
|
+
// These methods are already memoized in useInputManager, no need to wrap again
|
|
145
126
|
|
|
146
|
-
//
|
|
147
|
-
const {
|
|
148
|
-
handleFileSelect,
|
|
149
|
-
handleCommandSelect,
|
|
150
|
-
handleBashHistorySelect,
|
|
151
|
-
handleBashHistoryExecute: keyboardHandleBashHistoryExecute,
|
|
152
|
-
handleMemoryTypeSelect,
|
|
153
|
-
} = useInputKeyboardHandler({
|
|
154
|
-
inputText,
|
|
155
|
-
setInputText,
|
|
156
|
-
cursorPosition,
|
|
157
|
-
setCursorPosition,
|
|
158
|
-
moveCursorLeft,
|
|
159
|
-
moveCursorRight,
|
|
160
|
-
moveCursorToStart,
|
|
161
|
-
moveCursorToEnd,
|
|
162
|
-
deleteCharAtCursor,
|
|
163
|
-
insertTextAtCursor,
|
|
164
|
-
clearInput,
|
|
165
|
-
resetHistoryNavigation,
|
|
166
|
-
navigateHistory,
|
|
167
|
-
handlePasteImage,
|
|
168
|
-
attachedImages,
|
|
169
|
-
clearImages,
|
|
170
|
-
showFileSelector,
|
|
171
|
-
activateFileSelector,
|
|
172
|
-
handleFileSelect: handleFileSelectorSelect,
|
|
173
|
-
handleCancelFileSelect,
|
|
174
|
-
updateSearchQuery,
|
|
175
|
-
checkForAtDeletion,
|
|
176
|
-
atPosition,
|
|
177
|
-
showCommandSelector,
|
|
178
|
-
activateCommandSelector,
|
|
179
|
-
handleCommandSelect: handleCommandSelectorSelect,
|
|
180
|
-
handleCommandInsert: handleCommandSelectorInsert,
|
|
181
|
-
handleCancelCommandSelect,
|
|
182
|
-
updateCommandSearchQuery,
|
|
183
|
-
checkForSlashDeletion,
|
|
184
|
-
slashPosition,
|
|
185
|
-
showBashHistorySelector,
|
|
186
|
-
activateBashHistorySelector,
|
|
187
|
-
handleBashHistorySelect: handleBashHistorySelectorSelect,
|
|
188
|
-
handleBashHistoryExecute,
|
|
189
|
-
handleCancelBashHistorySelect,
|
|
190
|
-
updateBashHistorySearchQuery,
|
|
191
|
-
checkForExclamationDeletion,
|
|
192
|
-
exclamationPosition,
|
|
193
|
-
showMemoryTypeSelector,
|
|
194
|
-
activateMemoryTypeSelector,
|
|
195
|
-
handleMemoryTypeSelect: handleMemoryTypeSelectorSelect,
|
|
196
|
-
showBashManager,
|
|
197
|
-
showMcpManager,
|
|
198
|
-
isCommandRunning,
|
|
199
|
-
isLoading,
|
|
200
|
-
sendMessage,
|
|
201
|
-
abortMessage,
|
|
202
|
-
saveMemory,
|
|
203
|
-
});
|
|
127
|
+
// These methods are already memoized in useInputManager and combine multiple operations
|
|
204
128
|
|
|
205
129
|
const isPlaceholder = !inputText;
|
|
206
130
|
const placeholderText = INPUT_PLACEHOLDER_TEXT;
|
|
207
131
|
|
|
208
|
-
//
|
|
209
|
-
const handleCommandInsert = useCallback(
|
|
210
|
-
(command: string) => {
|
|
211
|
-
const result = handleCommandSelectorInsert(
|
|
212
|
-
command,
|
|
213
|
-
inputText,
|
|
214
|
-
cursorPosition,
|
|
215
|
-
);
|
|
216
|
-
setInputText(result.newInput);
|
|
217
|
-
setCursorPosition(result.newCursorPosition);
|
|
218
|
-
},
|
|
219
|
-
[
|
|
220
|
-
handleCommandSelectorInsert,
|
|
221
|
-
inputText,
|
|
222
|
-
cursorPosition,
|
|
223
|
-
setInputText,
|
|
224
|
-
setCursorPosition,
|
|
225
|
-
],
|
|
226
|
-
);
|
|
132
|
+
// handleCommandSelectorInsert is already memoized in useInputManager, no need to wrap again
|
|
227
133
|
|
|
228
134
|
// Split text into three parts: before cursor, cursor position, after cursor
|
|
229
135
|
const displayText = isPlaceholder ? placeholderText : inputText;
|
|
@@ -235,8 +141,13 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
235
141
|
// Always show cursor, allow user to continue input during loading
|
|
236
142
|
const shouldShowCursor = true;
|
|
237
143
|
|
|
144
|
+
// Only show the Box after InputManager is created on first mount
|
|
145
|
+
if (!isManagerReady) {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
|
|
238
149
|
return (
|
|
239
|
-
<Box flexDirection="column"
|
|
150
|
+
<Box flexDirection="column">
|
|
240
151
|
{showFileSelector && (
|
|
241
152
|
<FileSelector
|
|
242
153
|
files={filteredFiles}
|
|
@@ -261,7 +172,7 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
261
172
|
searchQuery={bashHistorySearchQuery}
|
|
262
173
|
workdir={currentWorkdir}
|
|
263
174
|
onSelect={handleBashHistorySelect}
|
|
264
|
-
onExecute={
|
|
175
|
+
onExecute={handleBashHistoryExecuteAndSend}
|
|
265
176
|
onCancel={handleCancelBashHistorySelect}
|
|
266
177
|
/>
|
|
267
178
|
)}
|
|
@@ -288,21 +199,19 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
288
199
|
)}
|
|
289
200
|
{showBashManager || showMcpManager || (
|
|
290
201
|
<Box borderStyle="single" borderColor="gray" paddingX={1}>
|
|
291
|
-
<
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
</Text>
|
|
305
|
-
</Box>
|
|
202
|
+
<Text color={isPlaceholder ? "gray" : "white"}>
|
|
203
|
+
{shouldShowCursor ? (
|
|
204
|
+
<>
|
|
205
|
+
{beforeCursor}
|
|
206
|
+
<Text backgroundColor="white" color="black">
|
|
207
|
+
{atCursor}
|
|
208
|
+
</Text>
|
|
209
|
+
{afterCursor}
|
|
210
|
+
</>
|
|
211
|
+
) : (
|
|
212
|
+
displayText
|
|
213
|
+
)}
|
|
214
|
+
</Text>
|
|
306
215
|
</Box>
|
|
307
216
|
)}
|
|
308
217
|
</Box>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React, { useMemo } from "react";
|
|
2
|
+
import { Text } from "ink";
|
|
3
|
+
import { marked } from "marked";
|
|
4
|
+
import TerminalRenderer from "marked-terminal";
|
|
5
|
+
|
|
6
|
+
export interface MarkdownProps {
|
|
7
|
+
children: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Markdown component using marked-terminal with proper unescape option
|
|
11
|
+
export const Markdown = React.memo(({ children }: MarkdownProps) => {
|
|
12
|
+
const result = useMemo(() => {
|
|
13
|
+
// Configure marked with TerminalRenderer using default options
|
|
14
|
+
marked.setOptions({
|
|
15
|
+
renderer: new TerminalRenderer({
|
|
16
|
+
// Use official unescape option to handle HTML entities
|
|
17
|
+
unescape: true,
|
|
18
|
+
}),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const output = marked(children);
|
|
22
|
+
return typeof output === "string" ? output.trim() : "";
|
|
23
|
+
}, [children]);
|
|
24
|
+
|
|
25
|
+
return <Text>{result}</Text>;
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Add display name for debugging
|
|
29
|
+
Markdown.displayName = "Markdown";
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import type { Message } from "wave-agent-sdk";
|
|
4
|
+
import { MessageSource } from "wave-agent-sdk";
|
|
5
|
+
import { DiffViewer } from "./DiffViewer.js";
|
|
6
|
+
import { CommandOutputDisplay } from "./CommandOutputDisplay.js";
|
|
7
|
+
import { ToolResultDisplay } from "./ToolResultDisplay.js";
|
|
8
|
+
import { MemoryDisplay } from "./MemoryDisplay.js";
|
|
9
|
+
import { CompressDisplay } from "./CompressDisplay.js";
|
|
10
|
+
import { SubagentBlock } from "./SubagentBlock.js";
|
|
11
|
+
import { Markdown } from "./Markdown.js";
|
|
12
|
+
|
|
13
|
+
export interface MessageItemProps {
|
|
14
|
+
message: Message;
|
|
15
|
+
isExpanded: boolean;
|
|
16
|
+
shouldShowHeader: boolean;
|
|
17
|
+
isStatic?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const MessageItem = ({
|
|
21
|
+
message,
|
|
22
|
+
isExpanded,
|
|
23
|
+
shouldShowHeader,
|
|
24
|
+
isStatic = true,
|
|
25
|
+
}: MessageItemProps) => {
|
|
26
|
+
if (message.blocks.length === 0) return null;
|
|
27
|
+
return (
|
|
28
|
+
<Box flexDirection="column" gap={1} marginTop={1}>
|
|
29
|
+
{shouldShowHeader && (
|
|
30
|
+
<Box>
|
|
31
|
+
<Text color={message.role === "user" ? "cyan" : "green"} bold>
|
|
32
|
+
{message.role === "user" ? "👤 You" : "🤖 Assistant"}
|
|
33
|
+
</Text>
|
|
34
|
+
</Box>
|
|
35
|
+
)}
|
|
36
|
+
|
|
37
|
+
<Box flexDirection="column" gap={1}>
|
|
38
|
+
{message.blocks.map((block, blockIndex) => (
|
|
39
|
+
<Box key={blockIndex}>
|
|
40
|
+
{block.type === "text" && block.content.trim() && (
|
|
41
|
+
<Box>
|
|
42
|
+
{block.customCommandContent && (
|
|
43
|
+
<Text color="cyan" bold>
|
|
44
|
+
⚡{" "}
|
|
45
|
+
</Text>
|
|
46
|
+
)}
|
|
47
|
+
{block.source === MessageSource.HOOK && (
|
|
48
|
+
<Text color="magenta" bold>
|
|
49
|
+
🔗{" "}
|
|
50
|
+
</Text>
|
|
51
|
+
)}
|
|
52
|
+
{isStatic ? (
|
|
53
|
+
<Markdown>{block.content}</Markdown>
|
|
54
|
+
) : (
|
|
55
|
+
<Text>{block.content.split("\n").slice(-10).join("\n")}</Text>
|
|
56
|
+
)}
|
|
57
|
+
</Box>
|
|
58
|
+
)}
|
|
59
|
+
|
|
60
|
+
{block.type === "error" && (
|
|
61
|
+
<Box>
|
|
62
|
+
<Text color="red">❌ Error: {block.content}</Text>
|
|
63
|
+
</Box>
|
|
64
|
+
)}
|
|
65
|
+
|
|
66
|
+
{block.type === "diff" && (
|
|
67
|
+
<DiffViewer block={block} isStatic={isStatic} />
|
|
68
|
+
)}
|
|
69
|
+
|
|
70
|
+
{block.type === "command_output" && (
|
|
71
|
+
<CommandOutputDisplay block={block} isExpanded={isExpanded} />
|
|
72
|
+
)}
|
|
73
|
+
|
|
74
|
+
{block.type === "tool" && (
|
|
75
|
+
<ToolResultDisplay block={block} isExpanded={isExpanded} />
|
|
76
|
+
)}
|
|
77
|
+
|
|
78
|
+
{block.type === "image" && (
|
|
79
|
+
<Box>
|
|
80
|
+
<Text color="magenta" bold>
|
|
81
|
+
📷 Image
|
|
82
|
+
</Text>
|
|
83
|
+
{block.imageUrls && block.imageUrls.length > 0 && (
|
|
84
|
+
<Text color="gray" dimColor>
|
|
85
|
+
{" "}
|
|
86
|
+
({block.imageUrls.length})
|
|
87
|
+
</Text>
|
|
88
|
+
)}
|
|
89
|
+
</Box>
|
|
90
|
+
)}
|
|
91
|
+
|
|
92
|
+
{block.type === "memory" && <MemoryDisplay block={block} />}
|
|
93
|
+
|
|
94
|
+
{block.type === "compress" && (
|
|
95
|
+
<CompressDisplay block={block} isExpanded={isExpanded} />
|
|
96
|
+
)}
|
|
97
|
+
|
|
98
|
+
{block.type === "subagent" && <SubagentBlock block={block} />}
|
|
99
|
+
</Box>
|
|
100
|
+
))}
|
|
101
|
+
</Box>
|
|
102
|
+
</Box>
|
|
103
|
+
);
|
|
104
|
+
};
|