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
|
@@ -2,55 +2,57 @@ import React, { useState, useEffect } from "react";
|
|
|
2
2
|
import { Box, Text, useInput } from "ink";
|
|
3
3
|
import { useChat } from "../contexts/useChat.js";
|
|
4
4
|
|
|
5
|
-
interface
|
|
5
|
+
interface Task {
|
|
6
6
|
id: string;
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
type: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
status: "running" | "completed" | "failed" | "killed";
|
|
9
10
|
startTime: number;
|
|
10
11
|
exitCode?: number;
|
|
11
12
|
runtime?: number;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
export interface
|
|
15
|
+
export interface BackgroundTaskManagerProps {
|
|
15
16
|
onCancel: () => void;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
export const
|
|
19
|
+
export const BackgroundTaskManager: React.FC<BackgroundTaskManagerProps> = ({
|
|
19
20
|
onCancel,
|
|
20
21
|
}) => {
|
|
21
|
-
const {
|
|
22
|
+
const { backgroundTasks, getBackgroundTaskOutput, stopBackgroundTask } =
|
|
22
23
|
useChat();
|
|
23
|
-
const [
|
|
24
|
+
const [tasks, setTasks] = useState<Task[]>([]);
|
|
24
25
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
25
26
|
const [viewMode, setViewMode] = useState<"list" | "detail">("list");
|
|
26
|
-
const [
|
|
27
|
+
const [detailTaskId, setDetailTaskId] = useState<string | null>(null);
|
|
27
28
|
const [detailOutput, setDetailOutput] = useState<{
|
|
28
29
|
stdout: string;
|
|
29
30
|
stderr: string;
|
|
30
31
|
status: string;
|
|
31
32
|
} | null>(null);
|
|
32
33
|
|
|
33
|
-
// Convert
|
|
34
|
+
// Convert backgroundTasks to local Task format
|
|
34
35
|
useEffect(() => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
id:
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
36
|
+
setTasks(
|
|
37
|
+
backgroundTasks.map((task) => ({
|
|
38
|
+
id: task.id,
|
|
39
|
+
type: task.type,
|
|
40
|
+
description: task.description,
|
|
41
|
+
status: task.status,
|
|
42
|
+
startTime: task.startTime,
|
|
43
|
+
exitCode: task.exitCode,
|
|
44
|
+
runtime: task.runtime,
|
|
43
45
|
})),
|
|
44
46
|
);
|
|
45
|
-
}, [
|
|
47
|
+
}, [backgroundTasks]);
|
|
46
48
|
|
|
47
|
-
// Load detail output for selected
|
|
49
|
+
// Load detail output for selected task
|
|
48
50
|
useEffect(() => {
|
|
49
|
-
if (viewMode === "detail" &&
|
|
50
|
-
const output =
|
|
51
|
+
if (viewMode === "detail" && detailTaskId) {
|
|
52
|
+
const output = getBackgroundTaskOutput(detailTaskId);
|
|
51
53
|
setDetailOutput(output);
|
|
52
54
|
}
|
|
53
|
-
}, [viewMode,
|
|
55
|
+
}, [viewMode, detailTaskId, getBackgroundTaskOutput]);
|
|
54
56
|
|
|
55
57
|
const formatDuration = (ms: number): string => {
|
|
56
58
|
if (ms < 1000) return `${ms}ms`;
|
|
@@ -64,17 +66,17 @@ export const BashShellManager: React.FC<BashShellManagerProps> = ({
|
|
|
64
66
|
return new Date(timestamp).toLocaleTimeString();
|
|
65
67
|
};
|
|
66
68
|
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
+
const stopTask = (taskId: string) => {
|
|
70
|
+
stopBackgroundTask(taskId);
|
|
69
71
|
};
|
|
70
72
|
|
|
71
73
|
useInput((input, key) => {
|
|
72
74
|
if (viewMode === "list") {
|
|
73
75
|
// List mode navigation
|
|
74
76
|
if (key.return) {
|
|
75
|
-
if (
|
|
76
|
-
const
|
|
77
|
-
|
|
77
|
+
if (tasks.length > 0 && selectedIndex < tasks.length) {
|
|
78
|
+
const selectedTask = tasks[selectedIndex];
|
|
79
|
+
setDetailTaskId(selectedTask.id);
|
|
78
80
|
setViewMode("detail");
|
|
79
81
|
}
|
|
80
82
|
return;
|
|
@@ -91,14 +93,14 @@ export const BashShellManager: React.FC<BashShellManagerProps> = ({
|
|
|
91
93
|
}
|
|
92
94
|
|
|
93
95
|
if (key.downArrow) {
|
|
94
|
-
setSelectedIndex(Math.min(
|
|
96
|
+
setSelectedIndex(Math.min(tasks.length - 1, selectedIndex + 1));
|
|
95
97
|
return;
|
|
96
98
|
}
|
|
97
99
|
|
|
98
|
-
if (input === "k" &&
|
|
99
|
-
const
|
|
100
|
-
if (
|
|
101
|
-
|
|
100
|
+
if (input === "k" && tasks.length > 0 && selectedIndex < tasks.length) {
|
|
101
|
+
const selectedTask = tasks[selectedIndex];
|
|
102
|
+
if (selectedTask.status === "running") {
|
|
103
|
+
stopTask(selectedTask.id);
|
|
102
104
|
}
|
|
103
105
|
return;
|
|
104
106
|
}
|
|
@@ -106,24 +108,24 @@ export const BashShellManager: React.FC<BashShellManagerProps> = ({
|
|
|
106
108
|
// Detail mode navigation
|
|
107
109
|
if (key.escape) {
|
|
108
110
|
setViewMode("list");
|
|
109
|
-
|
|
111
|
+
setDetailTaskId(null);
|
|
110
112
|
setDetailOutput(null);
|
|
111
113
|
return;
|
|
112
114
|
}
|
|
113
115
|
|
|
114
|
-
if (input === "k" &&
|
|
115
|
-
const
|
|
116
|
-
if (
|
|
117
|
-
|
|
116
|
+
if (input === "k" && detailTaskId) {
|
|
117
|
+
const task = tasks.find((t) => t.id === detailTaskId);
|
|
118
|
+
if (task && task.status === "running") {
|
|
119
|
+
stopTask(detailTaskId);
|
|
118
120
|
}
|
|
119
121
|
return;
|
|
120
122
|
}
|
|
121
123
|
}
|
|
122
124
|
});
|
|
123
125
|
|
|
124
|
-
if (viewMode === "detail" &&
|
|
125
|
-
const
|
|
126
|
-
if (!
|
|
126
|
+
if (viewMode === "detail" && detailTaskId && detailOutput) {
|
|
127
|
+
const task = tasks.find((t) => t.id === detailTaskId);
|
|
128
|
+
if (!task) {
|
|
127
129
|
setViewMode("list");
|
|
128
130
|
return null;
|
|
129
131
|
}
|
|
@@ -141,31 +143,37 @@ export const BashShellManager: React.FC<BashShellManagerProps> = ({
|
|
|
141
143
|
>
|
|
142
144
|
<Box>
|
|
143
145
|
<Text color="cyan" bold>
|
|
144
|
-
Background
|
|
146
|
+
Background Task Details: {task.id}
|
|
145
147
|
</Text>
|
|
146
148
|
</Box>
|
|
147
149
|
|
|
148
150
|
<Box flexDirection="column" gap={1}>
|
|
149
151
|
<Box>
|
|
150
152
|
<Text>
|
|
151
|
-
<Text color="blue">
|
|
153
|
+
<Text color="blue">Type:</Text> {task.type}
|
|
152
154
|
</Text>
|
|
153
155
|
</Box>
|
|
156
|
+
{task.description && (
|
|
157
|
+
<Box>
|
|
158
|
+
<Text>
|
|
159
|
+
<Text color="blue">Description:</Text> {task.description}
|
|
160
|
+
</Text>
|
|
161
|
+
</Box>
|
|
162
|
+
)}
|
|
154
163
|
<Box>
|
|
155
164
|
<Text>
|
|
156
|
-
<Text color="blue">Status:</Text> {
|
|
157
|
-
{
|
|
158
|
-
` (exit code: ${shell.exitCode})`}
|
|
165
|
+
<Text color="blue">Status:</Text> {task.status}
|
|
166
|
+
{task.exitCode !== undefined && ` (exit code: ${task.exitCode})`}
|
|
159
167
|
</Text>
|
|
160
168
|
</Box>
|
|
161
169
|
<Box>
|
|
162
170
|
<Text>
|
|
163
|
-
<Text color="blue">Started:</Text> {formatTime(
|
|
164
|
-
{
|
|
171
|
+
<Text color="blue">Started:</Text> {formatTime(task.startTime)}
|
|
172
|
+
{task.runtime !== undefined && (
|
|
165
173
|
<Text>
|
|
166
174
|
{" "}
|
|
167
175
|
| <Text color="blue">Runtime:</Text>{" "}
|
|
168
|
-
{formatDuration(
|
|
176
|
+
{formatDuration(task.runtime)}
|
|
169
177
|
</Text>
|
|
170
178
|
)}
|
|
171
179
|
</Text>
|
|
@@ -175,7 +183,7 @@ export const BashShellManager: React.FC<BashShellManagerProps> = ({
|
|
|
175
183
|
{detailOutput.stdout && (
|
|
176
184
|
<Box flexDirection="column" marginTop={1}>
|
|
177
185
|
<Text color="green" bold>
|
|
178
|
-
|
|
186
|
+
OUTPUT (last 10 lines):
|
|
179
187
|
</Text>
|
|
180
188
|
<Box borderStyle="single" borderColor="green" padding={1}>
|
|
181
189
|
<Text>
|
|
@@ -188,7 +196,7 @@ export const BashShellManager: React.FC<BashShellManagerProps> = ({
|
|
|
188
196
|
{detailOutput.stderr && (
|
|
189
197
|
<Box flexDirection="column" marginTop={1}>
|
|
190
198
|
<Text color="red" bold>
|
|
191
|
-
|
|
199
|
+
ERRORS:
|
|
192
200
|
</Text>
|
|
193
201
|
<Box borderStyle="single" borderColor="red" padding={1}>
|
|
194
202
|
<Text color="red">
|
|
@@ -200,14 +208,14 @@ export const BashShellManager: React.FC<BashShellManagerProps> = ({
|
|
|
200
208
|
|
|
201
209
|
<Box marginTop={1}>
|
|
202
210
|
<Text dimColor>
|
|
203
|
-
{
|
|
211
|
+
{task.status === "running" ? "k to stop · " : ""}Esc to go back
|
|
204
212
|
</Text>
|
|
205
213
|
</Box>
|
|
206
214
|
</Box>
|
|
207
215
|
);
|
|
208
216
|
}
|
|
209
217
|
|
|
210
|
-
if (!
|
|
218
|
+
if (!backgroundTasks) {
|
|
211
219
|
return (
|
|
212
220
|
<Box
|
|
213
221
|
flexDirection="column"
|
|
@@ -219,15 +227,15 @@ export const BashShellManager: React.FC<BashShellManagerProps> = ({
|
|
|
219
227
|
paddingTop={1}
|
|
220
228
|
>
|
|
221
229
|
<Text color="cyan" bold>
|
|
222
|
-
Background
|
|
230
|
+
Background Tasks
|
|
223
231
|
</Text>
|
|
224
|
-
<Text>Background
|
|
232
|
+
<Text>Background tasks not available</Text>
|
|
225
233
|
<Text dimColor>Press Escape to close</Text>
|
|
226
234
|
</Box>
|
|
227
235
|
);
|
|
228
236
|
}
|
|
229
237
|
|
|
230
|
-
if (
|
|
238
|
+
if (tasks.length === 0) {
|
|
231
239
|
return (
|
|
232
240
|
<Box
|
|
233
241
|
flexDirection="column"
|
|
@@ -239,9 +247,9 @@ export const BashShellManager: React.FC<BashShellManagerProps> = ({
|
|
|
239
247
|
paddingTop={1}
|
|
240
248
|
>
|
|
241
249
|
<Text color="cyan" bold>
|
|
242
|
-
Background
|
|
250
|
+
Background Tasks
|
|
243
251
|
</Text>
|
|
244
|
-
<Text>No background
|
|
252
|
+
<Text>No background tasks found</Text>
|
|
245
253
|
<Text dimColor>Press Escape to close</Text>
|
|
246
254
|
</Box>
|
|
247
255
|
);
|
|
@@ -260,42 +268,40 @@ export const BashShellManager: React.FC<BashShellManagerProps> = ({
|
|
|
260
268
|
>
|
|
261
269
|
<Box>
|
|
262
270
|
<Text color="cyan" bold>
|
|
263
|
-
Background
|
|
271
|
+
Background Tasks
|
|
264
272
|
</Text>
|
|
265
273
|
</Box>
|
|
266
|
-
<Text dimColor>Select a
|
|
274
|
+
<Text dimColor>Select a task to view details</Text>
|
|
267
275
|
|
|
268
|
-
{
|
|
269
|
-
<Box key={
|
|
276
|
+
{tasks.map((task, index) => (
|
|
277
|
+
<Box key={task.id} flexDirection="column">
|
|
270
278
|
<Text
|
|
271
279
|
color={index === selectedIndex ? "black" : "white"}
|
|
272
280
|
backgroundColor={index === selectedIndex ? "cyan" : undefined}
|
|
273
281
|
>
|
|
274
282
|
{index === selectedIndex ? "▶ " : " "}
|
|
275
|
-
{index + 1}.{
|
|
276
|
-
{
|
|
277
|
-
? shell.command.substring(0, 47) + "..."
|
|
278
|
-
: shell.command}
|
|
283
|
+
{index + 1}. [{task.id}] {task.type}
|
|
284
|
+
{task.description ? `: ${task.description}` : ""}
|
|
279
285
|
<Text
|
|
280
286
|
color={
|
|
281
|
-
|
|
287
|
+
task.status === "running"
|
|
282
288
|
? "green"
|
|
283
|
-
:
|
|
289
|
+
: task.status === "completed"
|
|
284
290
|
? "blue"
|
|
285
291
|
: "red"
|
|
286
292
|
}
|
|
287
293
|
>
|
|
288
294
|
{" "}
|
|
289
|
-
({
|
|
295
|
+
({task.status})
|
|
290
296
|
</Text>
|
|
291
297
|
</Text>
|
|
292
298
|
{index === selectedIndex && (
|
|
293
299
|
<Box marginLeft={4} flexDirection="column">
|
|
294
300
|
<Text color="gray" dimColor>
|
|
295
|
-
|
|
296
|
-
{
|
|
297
|
-
` | Runtime: ${formatDuration(
|
|
298
|
-
{
|
|
301
|
+
Started: {formatTime(task.startTime)}
|
|
302
|
+
{task.runtime !== undefined &&
|
|
303
|
+
` | Runtime: ${formatDuration(task.runtime)}`}
|
|
304
|
+
{task.exitCode !== undefined && ` | Exit: ${task.exitCode}`}
|
|
299
305
|
</Text>
|
|
300
306
|
</Box>
|
|
301
307
|
)}
|
|
@@ -305,7 +311,7 @@ export const BashShellManager: React.FC<BashShellManagerProps> = ({
|
|
|
305
311
|
<Box marginTop={1}>
|
|
306
312
|
<Text dimColor>
|
|
307
313
|
↑/↓ to select · Enter to view ·{" "}
|
|
308
|
-
{
|
|
314
|
+
{tasks[selectedIndex]?.status === "running" ? "k to stop · " : ""}Esc
|
|
309
315
|
to close
|
|
310
316
|
</Text>
|
|
311
317
|
</Box>
|
|
@@ -1,20 +1,26 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { Box } from "ink";
|
|
1
|
+
import React, { useState, useCallback } from "react";
|
|
2
|
+
import { Box, useStdout } from "ink";
|
|
3
3
|
import { MessageList } from "./MessageList.js";
|
|
4
4
|
import { InputBox } from "./InputBox.js";
|
|
5
|
-
import {
|
|
5
|
+
import { LoadingIndicator } from "./LoadingIndicator.js";
|
|
6
|
+
import { TaskList } from "./TaskList.js";
|
|
7
|
+
import { ConfirmationDetails } from "./ConfirmationDetails.js";
|
|
8
|
+
import { ConfirmationSelector } from "./ConfirmationSelector.js";
|
|
9
|
+
|
|
6
10
|
import { useChat } from "../contexts/useChat.js";
|
|
11
|
+
import type { PermissionDecision } from "wave-agent-sdk";
|
|
7
12
|
|
|
8
13
|
export const ChatInterface: React.FC = () => {
|
|
14
|
+
const { stdout } = useStdout();
|
|
15
|
+
const [isDetailsTooTall, setIsDetailsTooTall] = useState(false);
|
|
16
|
+
|
|
9
17
|
const {
|
|
10
18
|
messages,
|
|
11
19
|
isLoading,
|
|
12
20
|
isCommandRunning,
|
|
13
|
-
userInputHistory,
|
|
14
21
|
isCompressing,
|
|
15
22
|
sendMessage,
|
|
16
23
|
abortMessage,
|
|
17
|
-
saveMemory,
|
|
18
24
|
mcpServers,
|
|
19
25
|
connectMcpServer,
|
|
20
26
|
disconnectMcpServer,
|
|
@@ -26,45 +32,95 @@ export const ChatInterface: React.FC = () => {
|
|
|
26
32
|
isConfirmationVisible,
|
|
27
33
|
confirmingTool,
|
|
28
34
|
handleConfirmationDecision,
|
|
29
|
-
handleConfirmationCancel,
|
|
30
|
-
|
|
35
|
+
handleConfirmationCancel: originalHandleConfirmationCancel,
|
|
36
|
+
setWasLastDetailsTooTall,
|
|
31
37
|
} = useChat();
|
|
32
38
|
|
|
39
|
+
const handleHeightMeasured = useCallback(
|
|
40
|
+
(height: number) => {
|
|
41
|
+
const terminalHeight = stdout?.rows || 24;
|
|
42
|
+
if (height > terminalHeight - 10) {
|
|
43
|
+
setIsDetailsTooTall(true);
|
|
44
|
+
} else {
|
|
45
|
+
setIsDetailsTooTall(false);
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
[stdout?.rows],
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
const handleConfirmationCancel = useCallback(() => {
|
|
52
|
+
if (isDetailsTooTall) {
|
|
53
|
+
setWasLastDetailsTooTall((prev) => prev + 1);
|
|
54
|
+
setIsDetailsTooTall(false);
|
|
55
|
+
}
|
|
56
|
+
originalHandleConfirmationCancel();
|
|
57
|
+
}, [
|
|
58
|
+
isDetailsTooTall,
|
|
59
|
+
originalHandleConfirmationCancel,
|
|
60
|
+
setWasLastDetailsTooTall,
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
const wrappedHandleConfirmationDecision = useCallback(
|
|
64
|
+
(decision: PermissionDecision) => {
|
|
65
|
+
if (isDetailsTooTall) {
|
|
66
|
+
setWasLastDetailsTooTall((prev) => prev + 1);
|
|
67
|
+
setIsDetailsTooTall(false);
|
|
68
|
+
}
|
|
69
|
+
handleConfirmationDecision(decision);
|
|
70
|
+
},
|
|
71
|
+
[isDetailsTooTall, handleConfirmationDecision, setWasLastDetailsTooTall],
|
|
72
|
+
);
|
|
73
|
+
|
|
33
74
|
if (!sessionId) return null;
|
|
34
75
|
|
|
35
76
|
return (
|
|
36
|
-
<Box flexDirection="column"
|
|
77
|
+
<Box flexDirection="column">
|
|
37
78
|
<MessageList
|
|
38
79
|
messages={messages}
|
|
39
80
|
isLoading={isLoading}
|
|
40
81
|
isCommandRunning={isCommandRunning}
|
|
41
|
-
isCompressing={isCompressing}
|
|
42
|
-
latestTotalTokens={latestTotalTokens}
|
|
43
82
|
isExpanded={isExpanded}
|
|
44
|
-
|
|
83
|
+
forceStaticLastMessage={isDetailsTooTall}
|
|
45
84
|
/>
|
|
46
85
|
|
|
86
|
+
{(isLoading || isCommandRunning || isCompressing) &&
|
|
87
|
+
!isConfirmationVisible && (
|
|
88
|
+
<LoadingIndicator
|
|
89
|
+
isLoading={isLoading}
|
|
90
|
+
isCommandRunning={isCommandRunning}
|
|
91
|
+
isCompressing={isCompressing}
|
|
92
|
+
latestTotalTokens={latestTotalTokens}
|
|
93
|
+
/>
|
|
94
|
+
)}
|
|
95
|
+
{!isConfirmationVisible && <TaskList />}
|
|
96
|
+
|
|
47
97
|
{isConfirmationVisible && (
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
98
|
+
<>
|
|
99
|
+
<ConfirmationDetails
|
|
100
|
+
toolName={confirmingTool!.name}
|
|
101
|
+
toolInput={confirmingTool!.input}
|
|
102
|
+
isExpanded={isExpanded}
|
|
103
|
+
onHeightMeasured={handleHeightMeasured}
|
|
104
|
+
/>
|
|
105
|
+
<ConfirmationSelector
|
|
106
|
+
toolName={confirmingTool!.name}
|
|
107
|
+
toolInput={confirmingTool!.input}
|
|
108
|
+
suggestedPrefix={confirmingTool!.suggestedPrefix}
|
|
109
|
+
hidePersistentOption={confirmingTool!.hidePersistentOption}
|
|
110
|
+
isExpanded={isExpanded}
|
|
111
|
+
onDecision={wrappedHandleConfirmationDecision}
|
|
112
|
+
onCancel={handleConfirmationCancel}
|
|
113
|
+
onAbort={abortMessage}
|
|
114
|
+
/>
|
|
115
|
+
</>
|
|
58
116
|
)}
|
|
59
117
|
|
|
60
118
|
{!isConfirmationVisible && !isExpanded && (
|
|
61
119
|
<InputBox
|
|
62
120
|
isLoading={isLoading}
|
|
63
121
|
isCommandRunning={isCommandRunning}
|
|
64
|
-
userInputHistory={userInputHistory}
|
|
65
122
|
sendMessage={sendMessage}
|
|
66
123
|
abortMessage={abortMessage}
|
|
67
|
-
saveMemory={saveMemory}
|
|
68
124
|
mcpServers={mcpServers}
|
|
69
125
|
connectMcpServer={connectMcpServer}
|
|
70
126
|
disconnectMcpServer={disconnectMcpServer}
|
|
@@ -4,9 +4,9 @@ import type { SlashCommand } from "wave-agent-sdk";
|
|
|
4
4
|
|
|
5
5
|
const AVAILABLE_COMMANDS: SlashCommand[] = [
|
|
6
6
|
{
|
|
7
|
-
id: "
|
|
8
|
-
name: "
|
|
9
|
-
description: "View and manage background
|
|
7
|
+
id: "tasks",
|
|
8
|
+
name: "tasks",
|
|
9
|
+
description: "View and manage background tasks (shells and subagents)",
|
|
10
10
|
handler: () => {}, // Handler here won't be used, actual processing is in the hook
|
|
11
11
|
},
|
|
12
12
|
{
|
|
@@ -39,6 +39,7 @@ export const CommandSelector: React.FC<CommandSelectorProps> = ({
|
|
|
39
39
|
onCancel,
|
|
40
40
|
commands = [], // Default to empty array
|
|
41
41
|
}) => {
|
|
42
|
+
const MAX_VISIBLE_ITEMS = 3;
|
|
42
43
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
43
44
|
|
|
44
45
|
// Merge agent commands and local commands
|
|
@@ -51,6 +52,19 @@ export const CommandSelector: React.FC<CommandSelectorProps> = ({
|
|
|
51
52
|
command.id.toLowerCase().includes(searchQuery.toLowerCase()),
|
|
52
53
|
);
|
|
53
54
|
|
|
55
|
+
// Calculate visible window
|
|
56
|
+
const startIndex = Math.max(
|
|
57
|
+
0,
|
|
58
|
+
Math.min(
|
|
59
|
+
selectedIndex - Math.floor(MAX_VISIBLE_ITEMS / 2),
|
|
60
|
+
Math.max(0, filteredCommands.length - MAX_VISIBLE_ITEMS),
|
|
61
|
+
),
|
|
62
|
+
);
|
|
63
|
+
const visibleCommands = filteredCommands.slice(
|
|
64
|
+
startIndex,
|
|
65
|
+
startIndex + MAX_VISIBLE_ITEMS,
|
|
66
|
+
);
|
|
67
|
+
|
|
54
68
|
useInput((input, key) => {
|
|
55
69
|
if (key.return) {
|
|
56
70
|
if (
|
|
@@ -101,7 +115,6 @@ export const CommandSelector: React.FC<CommandSelectorProps> = ({
|
|
|
101
115
|
borderBottom={false}
|
|
102
116
|
borderLeft={false}
|
|
103
117
|
borderRight={false}
|
|
104
|
-
paddingTop={1}
|
|
105
118
|
>
|
|
106
119
|
<Text color="yellow">No commands found for "{searchQuery}"</Text>
|
|
107
120
|
<Text dimColor>Press Escape to cancel</Text>
|
|
@@ -117,7 +130,6 @@ export const CommandSelector: React.FC<CommandSelectorProps> = ({
|
|
|
117
130
|
borderBottom={false}
|
|
118
131
|
borderLeft={false}
|
|
119
132
|
borderRight={false}
|
|
120
|
-
paddingTop={1}
|
|
121
133
|
gap={1}
|
|
122
134
|
>
|
|
123
135
|
<Box>
|
|
@@ -126,23 +138,29 @@ export const CommandSelector: React.FC<CommandSelectorProps> = ({
|
|
|
126
138
|
</Text>
|
|
127
139
|
</Box>
|
|
128
140
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
{command.description}
|
|
141
|
+
<Box flexDirection="column">
|
|
142
|
+
{visibleCommands.map((command, index) => {
|
|
143
|
+
const actualIndex = startIndex + index;
|
|
144
|
+
const isSelected = actualIndex === selectedIndex;
|
|
145
|
+
return (
|
|
146
|
+
<Box key={command.id} flexDirection="column">
|
|
147
|
+
<Text
|
|
148
|
+
color={isSelected ? "black" : "white"}
|
|
149
|
+
backgroundColor={isSelected ? "magenta" : undefined}
|
|
150
|
+
>
|
|
151
|
+
{isSelected ? "▶ " : " "}/{command.id}
|
|
141
152
|
</Text>
|
|
153
|
+
{isSelected && (
|
|
154
|
+
<Box marginLeft={4}>
|
|
155
|
+
<Text color="gray" dimColor>
|
|
156
|
+
{command.description}
|
|
157
|
+
</Text>
|
|
158
|
+
</Box>
|
|
159
|
+
)}
|
|
142
160
|
</Box>
|
|
143
|
-
)
|
|
144
|
-
|
|
145
|
-
|
|
161
|
+
);
|
|
162
|
+
})}
|
|
163
|
+
</Box>
|
|
146
164
|
|
|
147
165
|
<Box>
|
|
148
166
|
<Text dimColor>
|
|
@@ -7,25 +7,16 @@ interface CompressDisplayProps {
|
|
|
7
7
|
isExpanded?: boolean;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
export const CompressDisplay: React.FC<CompressDisplayProps> = ({
|
|
11
|
-
block,
|
|
12
|
-
isExpanded = false,
|
|
13
|
-
}) => {
|
|
10
|
+
export const CompressDisplay: React.FC<CompressDisplayProps> = ({ block }) => {
|
|
14
11
|
const { content } = block;
|
|
15
|
-
const MAX_LINES = 3; // Set maximum display lines for compressed content
|
|
16
12
|
|
|
17
|
-
const { displayContent
|
|
13
|
+
const { displayContent } = useMemo(() => {
|
|
18
14
|
if (!content) {
|
|
19
|
-
return { displayContent: ""
|
|
15
|
+
return { displayContent: "" };
|
|
20
16
|
}
|
|
21
17
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const display = overflow ? lines.slice(0, MAX_LINES).join("\n") : content;
|
|
26
|
-
|
|
27
|
-
return { displayContent: display, isOverflowing: overflow };
|
|
28
|
-
}, [content, isExpanded]);
|
|
18
|
+
return { displayContent: content };
|
|
19
|
+
}, [content]);
|
|
29
20
|
|
|
30
21
|
return (
|
|
31
22
|
<Box flexDirection="column">
|
|
@@ -43,14 +34,6 @@ export const CompressDisplay: React.FC<CompressDisplayProps> = ({
|
|
|
43
34
|
>
|
|
44
35
|
<Text color="white">{displayContent}</Text>
|
|
45
36
|
</Box>
|
|
46
|
-
{isOverflowing && (
|
|
47
|
-
<Box paddingLeft={2} marginTop={1}>
|
|
48
|
-
<Text color="yellow" dimColor>
|
|
49
|
-
Content truncated ({content.split("\n").length} lines total,
|
|
50
|
-
showing first {MAX_LINES} lines. Press Ctrl+O to expand.
|
|
51
|
-
</Text>
|
|
52
|
-
</Box>
|
|
53
|
-
)}
|
|
54
37
|
</Box>
|
|
55
38
|
)}
|
|
56
39
|
</Box>
|