wave-code 0.0.2
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 +120 -0
- package/bin/wave-code.js +16 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +62 -0
- package/dist/components/App.d.ts +8 -0
- package/dist/components/App.d.ts.map +1 -0
- package/dist/components/App.js +10 -0
- package/dist/components/BashHistorySelector.d.ts +10 -0
- package/dist/components/BashHistorySelector.d.ts.map +1 -0
- package/dist/components/BashHistorySelector.js +83 -0
- package/dist/components/BashShellManager.d.ts +6 -0
- package/dist/components/BashShellManager.d.ts.map +1 -0
- package/dist/components/BashShellManager.js +116 -0
- package/dist/components/ChatInterface.d.ts +3 -0
- package/dist/components/ChatInterface.d.ts.map +1 -0
- package/dist/components/ChatInterface.js +31 -0
- package/dist/components/CommandOutputDisplay.d.ts +9 -0
- package/dist/components/CommandOutputDisplay.d.ts.map +1 -0
- package/dist/components/CommandOutputDisplay.js +40 -0
- package/dist/components/CommandSelector.d.ts +11 -0
- package/dist/components/CommandSelector.d.ts.map +1 -0
- package/dist/components/CommandSelector.js +60 -0
- package/dist/components/CompressDisplay.d.ts +9 -0
- package/dist/components/CompressDisplay.d.ts.map +1 -0
- package/dist/components/CompressDisplay.js +17 -0
- package/dist/components/DiffViewer.d.ts +9 -0
- package/dist/components/DiffViewer.d.ts.map +1 -0
- package/dist/components/DiffViewer.js +221 -0
- package/dist/components/FileSelector.d.ts +13 -0
- package/dist/components/FileSelector.d.ts.map +1 -0
- package/dist/components/FileSelector.js +48 -0
- package/dist/components/InputBox.d.ts +23 -0
- package/dist/components/InputBox.d.ts.map +1 -0
- package/dist/components/InputBox.js +124 -0
- package/dist/components/McpManager.d.ts +10 -0
- package/dist/components/McpManager.d.ts.map +1 -0
- package/dist/components/McpManager.js +123 -0
- package/dist/components/MemoryDisplay.d.ts +8 -0
- package/dist/components/MemoryDisplay.d.ts.map +1 -0
- package/dist/components/MemoryDisplay.js +25 -0
- package/dist/components/MemoryTypeSelector.d.ts +8 -0
- package/dist/components/MemoryTypeSelector.d.ts.map +1 -0
- package/dist/components/MemoryTypeSelector.js +38 -0
- package/dist/components/MessageList.d.ts +12 -0
- package/dist/components/MessageList.d.ts.map +1 -0
- package/dist/components/MessageList.js +36 -0
- package/dist/components/ToolResultDisplay.d.ts +9 -0
- package/dist/components/ToolResultDisplay.d.ts.map +1 -0
- package/dist/components/ToolResultDisplay.js +52 -0
- package/dist/contexts/useAppConfig.d.ts +11 -0
- package/dist/contexts/useAppConfig.d.ts.map +1 -0
- package/dist/contexts/useAppConfig.js +13 -0
- package/dist/contexts/useChat.d.ts +36 -0
- package/dist/contexts/useChat.d.ts.map +1 -0
- package/dist/contexts/useChat.js +208 -0
- package/dist/hooks/useBashHistorySelector.d.ts +15 -0
- package/dist/hooks/useBashHistorySelector.d.ts.map +1 -0
- package/dist/hooks/useBashHistorySelector.js +61 -0
- package/dist/hooks/useCommandSelector.d.ts +24 -0
- package/dist/hooks/useCommandSelector.d.ts.map +1 -0
- package/dist/hooks/useCommandSelector.js +98 -0
- package/dist/hooks/useFileSelector.d.ts +16 -0
- package/dist/hooks/useFileSelector.d.ts.map +1 -0
- package/dist/hooks/useFileSelector.js +174 -0
- package/dist/hooks/useImageManager.d.ts +13 -0
- package/dist/hooks/useImageManager.d.ts.map +1 -0
- package/dist/hooks/useImageManager.js +46 -0
- package/dist/hooks/useInputHistory.d.ts +11 -0
- package/dist/hooks/useInputHistory.d.ts.map +1 -0
- package/dist/hooks/useInputHistory.js +64 -0
- package/dist/hooks/useInputKeyboardHandler.d.ts +83 -0
- package/dist/hooks/useInputKeyboardHandler.d.ts.map +1 -0
- package/dist/hooks/useInputKeyboardHandler.js +507 -0
- package/dist/hooks/useInputState.d.ts +14 -0
- package/dist/hooks/useInputState.d.ts.map +1 -0
- package/dist/hooks/useInputState.js +57 -0
- package/dist/hooks/useMemoryTypeSelector.d.ts +9 -0
- package/dist/hooks/useMemoryTypeSelector.d.ts.map +1 -0
- package/dist/hooks/useMemoryTypeSelector.js +27 -0
- package/dist/hooks/usePagination.d.ts +20 -0
- package/dist/hooks/usePagination.d.ts.map +1 -0
- package/dist/hooks/usePagination.js +168 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +91 -0
- package/dist/plain-cli.d.ts +7 -0
- package/dist/plain-cli.d.ts.map +1 -0
- package/dist/plain-cli.js +49 -0
- package/dist/utils/clipboard.d.ts +22 -0
- package/dist/utils/clipboard.d.ts.map +1 -0
- package/dist/utils/clipboard.js +347 -0
- package/dist/utils/constants.d.ts +17 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/constants.js +18 -0
- package/dist/utils/logger.d.ts +72 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +245 -0
- package/package.json +60 -0
- package/src/cli.tsx +82 -0
- package/src/components/App.tsx +31 -0
- package/src/components/BashHistorySelector.tsx +163 -0
- package/src/components/BashShellManager.tsx +306 -0
- package/src/components/ChatInterface.tsx +88 -0
- package/src/components/CommandOutputDisplay.tsx +81 -0
- package/src/components/CommandSelector.tsx +144 -0
- package/src/components/CompressDisplay.tsx +58 -0
- package/src/components/DiffViewer.tsx +321 -0
- package/src/components/FileSelector.tsx +137 -0
- package/src/components/InputBox.tsx +310 -0
- package/src/components/McpManager.tsx +328 -0
- package/src/components/MemoryDisplay.tsx +62 -0
- package/src/components/MemoryTypeSelector.tsx +96 -0
- package/src/components/MessageList.tsx +215 -0
- package/src/components/ToolResultDisplay.tsx +138 -0
- package/src/contexts/useAppConfig.tsx +32 -0
- package/src/contexts/useChat.tsx +300 -0
- package/src/hooks/useBashHistorySelector.ts +77 -0
- package/src/hooks/useCommandSelector.ts +131 -0
- package/src/hooks/useFileSelector.ts +227 -0
- package/src/hooks/useImageManager.ts +64 -0
- package/src/hooks/useInputHistory.ts +74 -0
- package/src/hooks/useInputKeyboardHandler.ts +778 -0
- package/src/hooks/useInputState.ts +66 -0
- package/src/hooks/useMemoryTypeSelector.ts +40 -0
- package/src/hooks/usePagination.ts +203 -0
- package/src/index.ts +108 -0
- package/src/plain-cli.ts +66 -0
- package/src/utils/clipboard.ts +384 -0
- package/src/utils/constants.ts +22 -0
- package/src/utils/logger.ts +301 -0
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { Box, Text, useInput } from "ink";
|
|
3
|
+
import { McpServerStatus } from "wave-agent-sdk";
|
|
4
|
+
|
|
5
|
+
export interface McpManagerProps {
|
|
6
|
+
onCancel: () => void;
|
|
7
|
+
servers: McpServerStatus[];
|
|
8
|
+
onConnectServer: (serverName: string) => Promise<boolean>;
|
|
9
|
+
onDisconnectServer: (serverName: string) => Promise<boolean>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const McpManager: React.FC<McpManagerProps> = ({
|
|
13
|
+
onCancel,
|
|
14
|
+
servers,
|
|
15
|
+
onConnectServer,
|
|
16
|
+
onDisconnectServer,
|
|
17
|
+
}) => {
|
|
18
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
19
|
+
const [viewMode, setViewMode] = useState<"list" | "detail">("list");
|
|
20
|
+
|
|
21
|
+
// Dynamically calculate selectedServer based on selectedIndex and servers
|
|
22
|
+
const selectedServer =
|
|
23
|
+
viewMode === "detail" &&
|
|
24
|
+
servers.length > 0 &&
|
|
25
|
+
selectedIndex < servers.length
|
|
26
|
+
? servers[selectedIndex]
|
|
27
|
+
: null;
|
|
28
|
+
|
|
29
|
+
const formatTime = (timestamp: number): string => {
|
|
30
|
+
return new Date(timestamp).toLocaleTimeString();
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const getStatusColor = (status: string): string => {
|
|
34
|
+
switch (status) {
|
|
35
|
+
case "connected":
|
|
36
|
+
return "green";
|
|
37
|
+
case "connecting":
|
|
38
|
+
return "yellow";
|
|
39
|
+
case "error":
|
|
40
|
+
return "red";
|
|
41
|
+
default:
|
|
42
|
+
return "gray";
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const getStatusIcon = (status: string): string => {
|
|
47
|
+
switch (status) {
|
|
48
|
+
case "connected":
|
|
49
|
+
return "✓";
|
|
50
|
+
case "connecting":
|
|
51
|
+
return "⟳";
|
|
52
|
+
case "error":
|
|
53
|
+
return "✗";
|
|
54
|
+
default:
|
|
55
|
+
return "○";
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const handleConnect = async (serverName: string) => {
|
|
60
|
+
await onConnectServer(serverName);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const handleDisconnect = async (serverName: string) => {
|
|
64
|
+
await onDisconnectServer(serverName);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
useInput((input, key) => {
|
|
68
|
+
if (viewMode === "list") {
|
|
69
|
+
// List mode navigation
|
|
70
|
+
if (key.return) {
|
|
71
|
+
if (servers.length > 0 && selectedIndex < servers.length) {
|
|
72
|
+
setViewMode("detail");
|
|
73
|
+
}
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (key.escape) {
|
|
78
|
+
onCancel();
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (key.upArrow) {
|
|
83
|
+
setSelectedIndex(Math.max(0, selectedIndex - 1));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (key.downArrow) {
|
|
88
|
+
setSelectedIndex(Math.min(servers.length - 1, selectedIndex + 1));
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Hotkeys for server actions
|
|
93
|
+
if (
|
|
94
|
+
input === "c" &&
|
|
95
|
+
servers.length > 0 &&
|
|
96
|
+
selectedIndex < servers.length
|
|
97
|
+
) {
|
|
98
|
+
const server = servers[selectedIndex];
|
|
99
|
+
if (server.status === "disconnected" || server.status === "error") {
|
|
100
|
+
handleConnect(server.name);
|
|
101
|
+
}
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (
|
|
106
|
+
input === "d" &&
|
|
107
|
+
servers.length > 0 &&
|
|
108
|
+
selectedIndex < servers.length
|
|
109
|
+
) {
|
|
110
|
+
const server = servers[selectedIndex];
|
|
111
|
+
if (server.status === "connected") {
|
|
112
|
+
handleDisconnect(server.name);
|
|
113
|
+
}
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
} else if (viewMode === "detail") {
|
|
117
|
+
// Detail mode navigation
|
|
118
|
+
if (key.escape) {
|
|
119
|
+
setViewMode("list");
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (selectedServer) {
|
|
124
|
+
if (
|
|
125
|
+
input === "c" &&
|
|
126
|
+
(selectedServer.status === "disconnected" ||
|
|
127
|
+
selectedServer.status === "error")
|
|
128
|
+
) {
|
|
129
|
+
handleConnect(selectedServer.name);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (input === "d" && selectedServer.status === "connected") {
|
|
134
|
+
handleDisconnect(selectedServer.name);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
if (viewMode === "detail" && selectedServer) {
|
|
142
|
+
return (
|
|
143
|
+
<Box
|
|
144
|
+
flexDirection="column"
|
|
145
|
+
borderStyle="single"
|
|
146
|
+
borderColor="cyan"
|
|
147
|
+
padding={1}
|
|
148
|
+
gap={1}
|
|
149
|
+
marginBottom={1}
|
|
150
|
+
>
|
|
151
|
+
<Box>
|
|
152
|
+
<Text color="cyan" bold>
|
|
153
|
+
MCP Server Details: {selectedServer.name}
|
|
154
|
+
</Text>
|
|
155
|
+
</Box>
|
|
156
|
+
|
|
157
|
+
<Box flexDirection="column" gap={1}>
|
|
158
|
+
<Box>
|
|
159
|
+
<Text>
|
|
160
|
+
<Text color="blue">Status:</Text>{" "}
|
|
161
|
+
<Text color={getStatusColor(selectedServer.status)}>
|
|
162
|
+
{getStatusIcon(selectedServer.status)} {selectedServer.status}
|
|
163
|
+
</Text>
|
|
164
|
+
</Text>
|
|
165
|
+
</Box>
|
|
166
|
+
<Box>
|
|
167
|
+
<Text>
|
|
168
|
+
<Text color="blue">Command:</Text> {selectedServer.config.command}
|
|
169
|
+
</Text>
|
|
170
|
+
</Box>
|
|
171
|
+
{selectedServer.config.args && (
|
|
172
|
+
<Box>
|
|
173
|
+
<Text>
|
|
174
|
+
<Text color="blue">Args:</Text>{" "}
|
|
175
|
+
{selectedServer.config.args.join(" ")}
|
|
176
|
+
</Text>
|
|
177
|
+
</Box>
|
|
178
|
+
)}
|
|
179
|
+
{selectedServer.toolCount !== undefined && (
|
|
180
|
+
<Box>
|
|
181
|
+
<Text>
|
|
182
|
+
<Text color="blue">Tools:</Text> {selectedServer.toolCount}{" "}
|
|
183
|
+
tools
|
|
184
|
+
</Text>
|
|
185
|
+
</Box>
|
|
186
|
+
)}
|
|
187
|
+
{selectedServer.capabilities && (
|
|
188
|
+
<Box>
|
|
189
|
+
<Text>
|
|
190
|
+
<Text color="blue">Capabilities:</Text>{" "}
|
|
191
|
+
{selectedServer.capabilities.join(", ")}
|
|
192
|
+
</Text>
|
|
193
|
+
</Box>
|
|
194
|
+
)}
|
|
195
|
+
{selectedServer.lastConnected && (
|
|
196
|
+
<Box>
|
|
197
|
+
<Text>
|
|
198
|
+
<Text color="blue">Last Connected:</Text>{" "}
|
|
199
|
+
{formatTime(selectedServer.lastConnected)}
|
|
200
|
+
</Text>
|
|
201
|
+
</Box>
|
|
202
|
+
)}
|
|
203
|
+
{selectedServer.error && (
|
|
204
|
+
<Box>
|
|
205
|
+
<Text>
|
|
206
|
+
<Text color="red">Error:</Text> {selectedServer.error}
|
|
207
|
+
</Text>
|
|
208
|
+
</Box>
|
|
209
|
+
)}
|
|
210
|
+
</Box>
|
|
211
|
+
|
|
212
|
+
{selectedServer.config.env &&
|
|
213
|
+
Object.keys(selectedServer.config.env).length > 0 && (
|
|
214
|
+
<Box flexDirection="column" marginTop={1}>
|
|
215
|
+
<Text color="blue" bold>
|
|
216
|
+
Environment Variables:
|
|
217
|
+
</Text>
|
|
218
|
+
<Box borderStyle="single" borderColor="blue" padding={1}>
|
|
219
|
+
{Object.entries(selectedServer.config.env).map(
|
|
220
|
+
([key, value]) => (
|
|
221
|
+
<Text key={key}>
|
|
222
|
+
{key}={value}
|
|
223
|
+
</Text>
|
|
224
|
+
),
|
|
225
|
+
)}
|
|
226
|
+
</Box>
|
|
227
|
+
</Box>
|
|
228
|
+
)}
|
|
229
|
+
|
|
230
|
+
<Box marginTop={1}>
|
|
231
|
+
<Text dimColor>
|
|
232
|
+
{selectedServer.status === "disconnected" ||
|
|
233
|
+
selectedServer.status === "error"
|
|
234
|
+
? "c to connect · "
|
|
235
|
+
: ""}
|
|
236
|
+
{selectedServer.status === "connected" ? "d to disconnect · " : ""}
|
|
237
|
+
Esc to go back
|
|
238
|
+
</Text>
|
|
239
|
+
</Box>
|
|
240
|
+
</Box>
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (servers.length === 0) {
|
|
245
|
+
return (
|
|
246
|
+
<Box
|
|
247
|
+
flexDirection="column"
|
|
248
|
+
borderStyle="single"
|
|
249
|
+
borderColor="cyan"
|
|
250
|
+
padding={1}
|
|
251
|
+
marginBottom={1}
|
|
252
|
+
>
|
|
253
|
+
<Text color="cyan" bold>
|
|
254
|
+
Manage MCP servers
|
|
255
|
+
</Text>
|
|
256
|
+
<Text>No MCP servers configured</Text>
|
|
257
|
+
<Text dimColor>
|
|
258
|
+
Create a .mcp.json file in your project root to add servers
|
|
259
|
+
</Text>
|
|
260
|
+
<Text dimColor>Press Escape to close</Text>
|
|
261
|
+
</Box>
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return (
|
|
266
|
+
<Box
|
|
267
|
+
flexDirection="column"
|
|
268
|
+
borderStyle="single"
|
|
269
|
+
borderColor="cyan"
|
|
270
|
+
padding={1}
|
|
271
|
+
gap={1}
|
|
272
|
+
marginBottom={1}
|
|
273
|
+
>
|
|
274
|
+
<Box>
|
|
275
|
+
<Text color="cyan" bold>
|
|
276
|
+
Manage MCP servers
|
|
277
|
+
</Text>
|
|
278
|
+
</Box>
|
|
279
|
+
<Text dimColor>Select a server to view details</Text>
|
|
280
|
+
|
|
281
|
+
{servers.map((server, index) => (
|
|
282
|
+
<Box key={server.name} flexDirection="column">
|
|
283
|
+
<Text
|
|
284
|
+
color={index === selectedIndex ? "black" : "white"}
|
|
285
|
+
backgroundColor={index === selectedIndex ? "cyan" : undefined}
|
|
286
|
+
>
|
|
287
|
+
{index === selectedIndex ? "▶ " : " "}
|
|
288
|
+
{index + 1}.{" "}
|
|
289
|
+
<Text color={getStatusColor(server.status)}>
|
|
290
|
+
{getStatusIcon(server.status)}
|
|
291
|
+
</Text>{" "}
|
|
292
|
+
{server.name}
|
|
293
|
+
{server.status === "connected" && server.toolCount && (
|
|
294
|
+
<Text color="green"> · {server.toolCount} tools</Text>
|
|
295
|
+
)}
|
|
296
|
+
</Text>
|
|
297
|
+
{index === selectedIndex && (
|
|
298
|
+
<Box marginLeft={4} flexDirection="column">
|
|
299
|
+
<Text color="gray" dimColor>
|
|
300
|
+
{server.config.command}
|
|
301
|
+
{server.config.args ? ` ${server.config.args.join(" ")}` : ""}
|
|
302
|
+
</Text>
|
|
303
|
+
{server.lastConnected && (
|
|
304
|
+
<Text color="gray" dimColor>
|
|
305
|
+
Last connected: {formatTime(server.lastConnected)}
|
|
306
|
+
</Text>
|
|
307
|
+
)}
|
|
308
|
+
</Box>
|
|
309
|
+
)}
|
|
310
|
+
</Box>
|
|
311
|
+
))}
|
|
312
|
+
|
|
313
|
+
<Box marginTop={1}>
|
|
314
|
+
<Text dimColor>
|
|
315
|
+
↑/↓ to select · Enter to view ·{" "}
|
|
316
|
+
{servers[selectedIndex]?.status === "disconnected" ||
|
|
317
|
+
servers[selectedIndex]?.status === "error"
|
|
318
|
+
? "c to connect · "
|
|
319
|
+
: ""}
|
|
320
|
+
{servers[selectedIndex]?.status === "connected"
|
|
321
|
+
? "d to disconnect · "
|
|
322
|
+
: ""}
|
|
323
|
+
Esc to close
|
|
324
|
+
</Text>
|
|
325
|
+
</Box>
|
|
326
|
+
</Box>
|
|
327
|
+
);
|
|
328
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import type { MemoryBlock } from "wave-agent-sdk";
|
|
4
|
+
|
|
5
|
+
interface MemoryDisplayProps {
|
|
6
|
+
block: MemoryBlock;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const MemoryDisplay: React.FC<MemoryDisplayProps> = ({ block }) => {
|
|
10
|
+
const { content, isSuccess, memoryType, storagePath } = block;
|
|
11
|
+
|
|
12
|
+
const getStatusIcon = () => {
|
|
13
|
+
return isSuccess ? "💾" : "⚠️";
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const getStatusColor = () => {
|
|
17
|
+
return isSuccess ? "green" : "red";
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const getStatusText = () => {
|
|
21
|
+
return isSuccess ? "Added to memory" : "Failed to add memory";
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const getStorageText = () => {
|
|
25
|
+
if (!isSuccess) return null;
|
|
26
|
+
|
|
27
|
+
if (memoryType === "user") {
|
|
28
|
+
return `Memory saved to ${storagePath || "user-memory.md"}`;
|
|
29
|
+
} else {
|
|
30
|
+
return `Memory saved to ${storagePath || "WAVE.md"}`;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<Box flexDirection="column">
|
|
36
|
+
<Box>
|
|
37
|
+
<Text color={getStatusColor()}>{getStatusIcon()} </Text>
|
|
38
|
+
<Text color={getStatusColor()}>{getStatusText()}</Text>
|
|
39
|
+
</Box>
|
|
40
|
+
|
|
41
|
+
{content && (
|
|
42
|
+
<Box marginTop={1} paddingLeft={2}>
|
|
43
|
+
<Box
|
|
44
|
+
borderLeft
|
|
45
|
+
borderColor={isSuccess ? "green" : "red"}
|
|
46
|
+
paddingLeft={1}
|
|
47
|
+
>
|
|
48
|
+
<Text color="gray">{content}</Text>
|
|
49
|
+
</Box>
|
|
50
|
+
</Box>
|
|
51
|
+
)}
|
|
52
|
+
|
|
53
|
+
{isSuccess && (
|
|
54
|
+
<Box paddingLeft={2} marginTop={1}>
|
|
55
|
+
<Text color="yellow" dimColor>
|
|
56
|
+
{getStorageText()}
|
|
57
|
+
</Text>
|
|
58
|
+
</Box>
|
|
59
|
+
)}
|
|
60
|
+
</Box>
|
|
61
|
+
);
|
|
62
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { Box, Text, useInput } from "ink";
|
|
3
|
+
|
|
4
|
+
export interface MemoryTypeSelectorProps {
|
|
5
|
+
message: string;
|
|
6
|
+
onSelect: (type: "project" | "user") => void;
|
|
7
|
+
onCancel: () => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const MemoryTypeSelector: React.FC<MemoryTypeSelectorProps> = ({
|
|
11
|
+
message,
|
|
12
|
+
onSelect,
|
|
13
|
+
onCancel,
|
|
14
|
+
}) => {
|
|
15
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
16
|
+
|
|
17
|
+
const options = [
|
|
18
|
+
{
|
|
19
|
+
type: "project" as const,
|
|
20
|
+
label: "Project Memory",
|
|
21
|
+
description: "Save to current project (WAVE.md)",
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
type: "user" as const,
|
|
25
|
+
label: "User Memory",
|
|
26
|
+
description: "Save to user global memory",
|
|
27
|
+
},
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
useInput((input, key) => {
|
|
31
|
+
if (key.return) {
|
|
32
|
+
const selectedOption = options[selectedIndex];
|
|
33
|
+
onSelect(selectedOption.type);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (key.escape) {
|
|
38
|
+
onCancel();
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (key.upArrow) {
|
|
43
|
+
setSelectedIndex(Math.max(0, selectedIndex - 1));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (key.downArrow) {
|
|
48
|
+
setSelectedIndex(Math.min(options.length - 1, selectedIndex + 1));
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<Box
|
|
55
|
+
flexDirection="column"
|
|
56
|
+
borderStyle="single"
|
|
57
|
+
borderColor="green"
|
|
58
|
+
padding={1}
|
|
59
|
+
gap={1}
|
|
60
|
+
marginBottom={1}
|
|
61
|
+
>
|
|
62
|
+
<Box>
|
|
63
|
+
<Text color="green" bold>
|
|
64
|
+
Save Memory: "{message.substring(1).trim()}"
|
|
65
|
+
</Text>
|
|
66
|
+
</Box>
|
|
67
|
+
|
|
68
|
+
<Text color="gray">Choose where to save this memory:</Text>
|
|
69
|
+
|
|
70
|
+
{options.map((option, index) => (
|
|
71
|
+
<Box key={option.type} flexDirection="column">
|
|
72
|
+
<Text
|
|
73
|
+
color={index === selectedIndex ? "black" : "white"}
|
|
74
|
+
backgroundColor={index === selectedIndex ? "green" : undefined}
|
|
75
|
+
bold={index === selectedIndex}
|
|
76
|
+
>
|
|
77
|
+
{option.label}
|
|
78
|
+
</Text>
|
|
79
|
+
{index === selectedIndex && (
|
|
80
|
+
<Box marginLeft={2}>
|
|
81
|
+
<Text color="gray" dimColor>
|
|
82
|
+
{option.description}
|
|
83
|
+
</Text>
|
|
84
|
+
</Box>
|
|
85
|
+
)}
|
|
86
|
+
</Box>
|
|
87
|
+
))}
|
|
88
|
+
|
|
89
|
+
<Box>
|
|
90
|
+
<Text dimColor>
|
|
91
|
+
Use ↑↓ to navigate, Enter to select, Escape to cancel
|
|
92
|
+
</Text>
|
|
93
|
+
</Box>
|
|
94
|
+
</Box>
|
|
95
|
+
);
|
|
96
|
+
};
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import React, { useMemo } from "react";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import type { Message } from "wave-agent-sdk";
|
|
4
|
+
import { DiffViewer } from "./DiffViewer.js";
|
|
5
|
+
import { CommandOutputDisplay } from "./CommandOutputDisplay.js";
|
|
6
|
+
import { ToolResultDisplay } from "./ToolResultDisplay.js";
|
|
7
|
+
import { MemoryDisplay } from "./MemoryDisplay.js";
|
|
8
|
+
import { CompressDisplay } from "./CompressDisplay.js";
|
|
9
|
+
import { usePagination } from "../hooks/usePagination.js";
|
|
10
|
+
|
|
11
|
+
// Function to render a single message
|
|
12
|
+
const renderMessageItem = (
|
|
13
|
+
message: Message,
|
|
14
|
+
originalIndex: number,
|
|
15
|
+
isExpanded: boolean,
|
|
16
|
+
previousMessage?: Message,
|
|
17
|
+
) => {
|
|
18
|
+
const shouldShowHeader = previousMessage?.role !== message.role;
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<Box key={`message-${originalIndex}`} flexDirection="column" marginTop={1}>
|
|
22
|
+
{shouldShowHeader && (
|
|
23
|
+
<Box>
|
|
24
|
+
<Text color={message.role === "user" ? "cyan" : "green"} bold>
|
|
25
|
+
{message.role === "user" ? "👤 You" : "🤖 Assistant"}
|
|
26
|
+
<Text color="gray" dimColor>
|
|
27
|
+
{" "}
|
|
28
|
+
#{originalIndex + 1}
|
|
29
|
+
</Text>
|
|
30
|
+
</Text>
|
|
31
|
+
</Box>
|
|
32
|
+
)}
|
|
33
|
+
|
|
34
|
+
<Box
|
|
35
|
+
marginLeft={2}
|
|
36
|
+
flexDirection="column"
|
|
37
|
+
gap={1}
|
|
38
|
+
marginTop={shouldShowHeader ? 1 : 0}
|
|
39
|
+
>
|
|
40
|
+
{message.blocks.map((block, blockIndex) => (
|
|
41
|
+
<Box key={blockIndex}>
|
|
42
|
+
{block.type === "text" && block.content.trim() && (
|
|
43
|
+
<Box>
|
|
44
|
+
<Text>{block.content}</Text>
|
|
45
|
+
</Box>
|
|
46
|
+
)}
|
|
47
|
+
|
|
48
|
+
{block.type === "error" && (
|
|
49
|
+
<Box>
|
|
50
|
+
<Text color="red">❌ Error: {block.content}</Text>
|
|
51
|
+
</Box>
|
|
52
|
+
)}
|
|
53
|
+
|
|
54
|
+
{block.type === "diff" && (
|
|
55
|
+
<DiffViewer block={block} isExpanded={isExpanded} />
|
|
56
|
+
)}
|
|
57
|
+
|
|
58
|
+
{block.type === "command_output" && (
|
|
59
|
+
<CommandOutputDisplay block={block} isExpanded={isExpanded} />
|
|
60
|
+
)}
|
|
61
|
+
|
|
62
|
+
{block.type === "tool" && (
|
|
63
|
+
<ToolResultDisplay block={block} isExpanded={isExpanded} />
|
|
64
|
+
)}
|
|
65
|
+
|
|
66
|
+
{block.type === "image" && (
|
|
67
|
+
<Box>
|
|
68
|
+
<Text color="magenta" bold>
|
|
69
|
+
📷 Image
|
|
70
|
+
</Text>
|
|
71
|
+
{block.imageUrls && block.imageUrls.length > 0 && (
|
|
72
|
+
<Text color="gray" dimColor>
|
|
73
|
+
{" "}
|
|
74
|
+
({block.imageUrls.length})
|
|
75
|
+
</Text>
|
|
76
|
+
)}
|
|
77
|
+
</Box>
|
|
78
|
+
)}
|
|
79
|
+
|
|
80
|
+
{block.type === "memory" && <MemoryDisplay block={block} />}
|
|
81
|
+
|
|
82
|
+
{block.type === "compress" && (
|
|
83
|
+
<CompressDisplay block={block} isExpanded={isExpanded} />
|
|
84
|
+
)}
|
|
85
|
+
|
|
86
|
+
{block.type === "custom_command" && (
|
|
87
|
+
<Box>
|
|
88
|
+
<Text color="cyan" bold>
|
|
89
|
+
⚡
|
|
90
|
+
</Text>
|
|
91
|
+
<Text>{block.originalInput || `/${block.commandName}`}</Text>
|
|
92
|
+
</Box>
|
|
93
|
+
)}
|
|
94
|
+
</Box>
|
|
95
|
+
))}
|
|
96
|
+
</Box>
|
|
97
|
+
</Box>
|
|
98
|
+
);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export interface MessageListProps {
|
|
102
|
+
messages: Message[];
|
|
103
|
+
isLoading?: boolean;
|
|
104
|
+
isCommandRunning?: boolean;
|
|
105
|
+
isCompressing?: boolean;
|
|
106
|
+
latestTotalTokens?: number;
|
|
107
|
+
isExpanded?: boolean;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export const MessageList: React.FC<MessageListProps> = ({
|
|
111
|
+
messages,
|
|
112
|
+
isLoading = false,
|
|
113
|
+
isCommandRunning = false,
|
|
114
|
+
isCompressing = false,
|
|
115
|
+
latestTotalTokens = 0,
|
|
116
|
+
isExpanded = false,
|
|
117
|
+
}) => {
|
|
118
|
+
// Use original messages for pagination calculation
|
|
119
|
+
const { displayInfo } = usePagination(messages);
|
|
120
|
+
|
|
121
|
+
// Get current page messages while preserving original index information
|
|
122
|
+
const currentMessagesWithIndex = useMemo(() => {
|
|
123
|
+
return messages
|
|
124
|
+
.slice(displayInfo.startIndex, displayInfo.endIndex)
|
|
125
|
+
.map((message, index) => ({
|
|
126
|
+
message,
|
|
127
|
+
originalIndex: displayInfo.startIndex + index,
|
|
128
|
+
}));
|
|
129
|
+
}, [messages, displayInfo.startIndex, displayInfo.endIndex]);
|
|
130
|
+
|
|
131
|
+
// Empty message state
|
|
132
|
+
if (messages.length === 0) {
|
|
133
|
+
return (
|
|
134
|
+
<Box flexDirection="column" paddingY={1}>
|
|
135
|
+
<Text color="gray">Welcome to WAVE Code Assistant!</Text>
|
|
136
|
+
</Box>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
<Box flexDirection="column">
|
|
142
|
+
{/* Message list */}
|
|
143
|
+
<Box flexDirection="column">
|
|
144
|
+
{currentMessagesWithIndex.map(({ message, originalIndex }) => {
|
|
145
|
+
// Get previous message
|
|
146
|
+
const previousMessage =
|
|
147
|
+
originalIndex > 0 ? messages[originalIndex - 1] : undefined;
|
|
148
|
+
return renderMessageItem(
|
|
149
|
+
message,
|
|
150
|
+
originalIndex,
|
|
151
|
+
isExpanded,
|
|
152
|
+
previousMessage,
|
|
153
|
+
);
|
|
154
|
+
})}
|
|
155
|
+
</Box>
|
|
156
|
+
|
|
157
|
+
{/* Loading state display - only show in non-expanded state */}
|
|
158
|
+
{!isExpanded && (isLoading || isCommandRunning || isCompressing) && (
|
|
159
|
+
<Box marginTop={1} flexDirection="column" gap={1}>
|
|
160
|
+
{isLoading && (
|
|
161
|
+
<Box>
|
|
162
|
+
<Text color="yellow">💭 AI is thinking... </Text>
|
|
163
|
+
<Text color="gray" dimColor>
|
|
164
|
+
{" "}
|
|
165
|
+
|{" "}
|
|
166
|
+
</Text>
|
|
167
|
+
<Text color="blue" bold>
|
|
168
|
+
{latestTotalTokens.toLocaleString()}
|
|
169
|
+
</Text>
|
|
170
|
+
<Text color="gray" dimColor>
|
|
171
|
+
{" "}
|
|
172
|
+
tokens |{" "}
|
|
173
|
+
</Text>
|
|
174
|
+
<Text color="red" bold>
|
|
175
|
+
Esc
|
|
176
|
+
</Text>
|
|
177
|
+
<Text color="gray" dimColor>
|
|
178
|
+
{" "}
|
|
179
|
+
to abort
|
|
180
|
+
</Text>
|
|
181
|
+
</Box>
|
|
182
|
+
)}
|
|
183
|
+
{isCommandRunning && (
|
|
184
|
+
<Text color="blue">🚀 Command is running...</Text>
|
|
185
|
+
)}
|
|
186
|
+
{isCompressing && (
|
|
187
|
+
<Text color="magenta">🗜️ Compressing message history...</Text>
|
|
188
|
+
)}
|
|
189
|
+
</Box>
|
|
190
|
+
)}
|
|
191
|
+
|
|
192
|
+
{/* Bottom info and shortcut key hints */}
|
|
193
|
+
{messages.length > 0 && (
|
|
194
|
+
<Box marginTop={1}>
|
|
195
|
+
<Box justifyContent="space-between" width="100%">
|
|
196
|
+
<Box>
|
|
197
|
+
<Text color="gray">
|
|
198
|
+
Messages {messages.length} Page {displayInfo.currentPage}/
|
|
199
|
+
{displayInfo.totalPages}
|
|
200
|
+
</Text>
|
|
201
|
+
<Text color="gray" dimColor>
|
|
202
|
+
{" "}
|
|
203
|
+
← <Text color="cyan">Ctrl+U/D</Text> Navigate
|
|
204
|
+
</Text>
|
|
205
|
+
</Box>
|
|
206
|
+
<Text color="gray" dimColor>
|
|
207
|
+
<Text color="cyan">Ctrl+O</Text> Toggle{" "}
|
|
208
|
+
{isExpanded ? "Collapse" : "Expand"}
|
|
209
|
+
</Text>
|
|
210
|
+
</Box>
|
|
211
|
+
</Box>
|
|
212
|
+
)}
|
|
213
|
+
</Box>
|
|
214
|
+
);
|
|
215
|
+
};
|