wave-code 0.7.1 → 0.8.0
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/cli.d.ts +2 -4
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +24 -52
- package/dist/components/App.d.ts +3 -4
- package/dist/components/App.d.ts.map +1 -1
- package/dist/components/App.js +49 -6
- package/dist/components/BackgroundTaskManager.d.ts.map +1 -1
- package/dist/components/BackgroundTaskManager.js +12 -20
- package/dist/components/BangDisplay.d.ts +9 -0
- package/dist/components/BangDisplay.d.ts.map +1 -0
- package/dist/components/{CommandOutputDisplay.js → BangDisplay.js} +1 -1
- package/dist/components/ChatInterface.d.ts.map +1 -1
- package/dist/components/ChatInterface.js +3 -2
- package/dist/components/CommandSelector.d.ts.map +1 -1
- package/dist/components/CommandSelector.js +18 -2
- package/dist/components/ConfirmationSelector.d.ts.map +1 -1
- package/dist/components/ConfirmationSelector.js +105 -8
- package/dist/components/HelpView.d.ts.map +1 -1
- package/dist/components/HelpView.js +2 -0
- package/dist/components/HistorySearch.d.ts.map +1 -1
- package/dist/components/HistorySearch.js +19 -25
- package/dist/components/InputBox.d.ts.map +1 -1
- package/dist/components/InputBox.js +9 -3
- package/dist/components/MarketplaceAddForm.d.ts.map +1 -1
- package/dist/components/MarketplaceAddForm.js +13 -6
- package/dist/components/MarketplaceDetail.d.ts.map +1 -1
- package/dist/components/MarketplaceDetail.js +8 -3
- package/dist/components/MessageBlockItem.js +2 -2
- package/dist/components/MessageList.d.ts +4 -1
- package/dist/components/MessageList.d.ts.map +1 -1
- package/dist/components/MessageList.js +15 -8
- package/dist/components/PluginDetail.d.ts.map +1 -1
- package/dist/components/PluginDetail.js +14 -3
- package/dist/components/PluginManagerShell.d.ts.map +1 -1
- package/dist/components/PluginManagerShell.js +3 -3
- package/dist/components/PluginManagerTypes.d.ts +2 -0
- package/dist/components/PluginManagerTypes.d.ts.map +1 -1
- package/dist/components/SessionSelector.d.ts.map +1 -1
- package/dist/components/SessionSelector.js +10 -13
- package/dist/components/StatusCommand.d.ts +6 -0
- package/dist/components/StatusCommand.d.ts.map +1 -0
- package/dist/components/StatusCommand.js +28 -0
- package/dist/components/WorktreeExitPrompt.d.ts +13 -0
- package/dist/components/WorktreeExitPrompt.d.ts.map +1 -0
- package/dist/components/WorktreeExitPrompt.js +26 -0
- package/dist/contexts/useChat.d.ts +9 -5
- package/dist/contexts/useChat.d.ts.map +1 -1
- package/dist/contexts/useChat.js +38 -8
- package/dist/contracts/status.d.ts +8 -0
- package/dist/contracts/status.d.ts.map +1 -0
- package/dist/contracts/status.js +1 -0
- package/dist/hooks/useInputManager.d.ts +2 -0
- package/dist/hooks/useInputManager.d.ts.map +1 -1
- package/dist/hooks/useInputManager.js +12 -0
- package/dist/hooks/usePluginManager.d.ts.map +1 -1
- package/dist/hooks/usePluginManager.js +41 -13
- package/dist/hooks/useTasks.js +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +53 -4
- package/dist/managers/InputManager.d.ts +6 -0
- package/dist/managers/InputManager.d.ts.map +1 -1
- package/dist/managers/InputManager.js +32 -13
- package/dist/print-cli.d.ts +2 -4
- package/dist/print-cli.d.ts.map +1 -1
- package/dist/print-cli.js +31 -2
- package/dist/session-selector-cli.d.ts +3 -1
- package/dist/session-selector-cli.d.ts.map +1 -1
- package/dist/session-selector-cli.js +2 -2
- package/dist/types.d.ts +11 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/utils/worktree.d.ts +23 -0
- package/dist/utils/worktree.d.ts.map +1 -0
- package/dist/utils/worktree.js +135 -0
- package/package.json +2 -2
- package/src/cli.tsx +36 -59
- package/src/components/App.tsx +99 -11
- package/src/components/BackgroundTaskManager.tsx +12 -20
- package/src/components/{CommandOutputDisplay.tsx → BangDisplay.tsx} +4 -4
- package/src/components/ChatInterface.tsx +8 -0
- package/src/components/CommandSelector.tsx +18 -1
- package/src/components/ConfirmationSelector.tsx +118 -9
- package/src/components/HelpView.tsx +2 -0
- package/src/components/HistorySearch.tsx +25 -30
- package/src/components/InputBox.tsx +11 -1
- package/src/components/MarketplaceAddForm.tsx +21 -8
- package/src/components/MarketplaceDetail.tsx +19 -4
- package/src/components/MessageBlockItem.tsx +3 -3
- package/src/components/MessageList.tsx +47 -23
- package/src/components/PluginDetail.tsx +30 -6
- package/src/components/PluginManagerShell.tsx +24 -6
- package/src/components/PluginManagerTypes.ts +2 -0
- package/src/components/SessionSelector.tsx +38 -24
- package/src/components/StatusCommand.tsx +94 -0
- package/src/components/WorktreeExitPrompt.tsx +86 -0
- package/src/contexts/useChat.tsx +57 -13
- package/src/contracts/status.ts +7 -0
- package/src/hooks/useInputManager.ts +12 -0
- package/src/hooks/usePluginManager.ts +47 -13
- package/src/hooks/useTasks.ts +2 -2
- package/src/index.ts +71 -12
- package/src/managers/InputManager.ts +37 -15
- package/src/print-cli.ts +48 -5
- package/src/session-selector-cli.tsx +6 -2
- package/src/types.ts +11 -0
- package/src/utils/worktree.ts +164 -0
- package/dist/components/CommandOutputDisplay.d.ts +0 -9
- package/dist/components/CommandOutputDisplay.d.ts.map +0 -1
|
@@ -85,14 +85,11 @@ export const BackgroundTaskManager: React.FC<BackgroundTaskManagerProps> = ({
|
|
|
85
85
|
if (viewMode === "list") {
|
|
86
86
|
// List mode navigation
|
|
87
87
|
if (key.return) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
return prev;
|
|
95
|
-
});
|
|
88
|
+
if (tasks.length > 0 && selectedIndex < tasks.length) {
|
|
89
|
+
const selectedTask = tasks[selectedIndex];
|
|
90
|
+
setDetailTaskId(selectedTask.id);
|
|
91
|
+
setViewMode("detail");
|
|
92
|
+
}
|
|
96
93
|
return;
|
|
97
94
|
}
|
|
98
95
|
|
|
@@ -102,25 +99,20 @@ export const BackgroundTaskManager: React.FC<BackgroundTaskManagerProps> = ({
|
|
|
102
99
|
}
|
|
103
100
|
|
|
104
101
|
if (key.upArrow) {
|
|
105
|
-
setSelectedIndex(
|
|
102
|
+
setSelectedIndex(Math.max(0, selectedIndex - 1));
|
|
106
103
|
return;
|
|
107
104
|
}
|
|
108
105
|
|
|
109
106
|
if (key.downArrow) {
|
|
110
|
-
setSelectedIndex(
|
|
107
|
+
setSelectedIndex(Math.min(tasks.length - 1, selectedIndex + 1));
|
|
111
108
|
return;
|
|
112
109
|
}
|
|
113
110
|
|
|
114
|
-
if (input === "k") {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
stopTask(selectedTask.id);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
return prev;
|
|
123
|
-
});
|
|
111
|
+
if (input === "k" && tasks.length > 0 && selectedIndex < tasks.length) {
|
|
112
|
+
const selectedTask = tasks[selectedIndex];
|
|
113
|
+
if (selectedTask.status === "running") {
|
|
114
|
+
stopTask(selectedTask.id);
|
|
115
|
+
}
|
|
124
116
|
return;
|
|
125
117
|
}
|
|
126
118
|
} else if (viewMode === "detail") {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import React, { useState, useEffect } from "react";
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
|
-
import type {
|
|
3
|
+
import type { BangBlock } from "wave-agent-sdk";
|
|
4
4
|
|
|
5
|
-
interface
|
|
6
|
-
block:
|
|
5
|
+
interface BangDisplayProps {
|
|
6
|
+
block: BangBlock;
|
|
7
7
|
isExpanded?: boolean;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
export const
|
|
10
|
+
export const BangDisplay: React.FC<BangDisplayProps> = ({
|
|
11
11
|
block,
|
|
12
12
|
isExpanded = false,
|
|
13
13
|
}) => {
|
|
@@ -36,8 +36,13 @@ export const ChatInterface: React.FC = () => {
|
|
|
36
36
|
handleConfirmationDecision,
|
|
37
37
|
handleConfirmationCancel: originalHandleConfirmationCancel,
|
|
38
38
|
setWasLastDetailsTooTall,
|
|
39
|
+
version,
|
|
40
|
+
workdir,
|
|
41
|
+
getModelConfig,
|
|
39
42
|
} = useChat();
|
|
40
43
|
|
|
44
|
+
const model = getModelConfig().model;
|
|
45
|
+
|
|
41
46
|
const handleDetailsHeightMeasured = useCallback((height: number) => {
|
|
42
47
|
setDetailsHeight(height);
|
|
43
48
|
}, []);
|
|
@@ -91,6 +96,9 @@ export const ChatInterface: React.FC = () => {
|
|
|
91
96
|
messages={messages}
|
|
92
97
|
isExpanded={isExpanded}
|
|
93
98
|
hideDynamicBlocks={isConfirmationVisible}
|
|
99
|
+
version={version}
|
|
100
|
+
workdir={workdir}
|
|
101
|
+
model={model}
|
|
94
102
|
/>
|
|
95
103
|
|
|
96
104
|
{(isLoading || isCommandRunning || isCompressing) &&
|
|
@@ -3,6 +3,12 @@ import { Box, Text, useInput } from "ink";
|
|
|
3
3
|
import type { SlashCommand } from "wave-agent-sdk";
|
|
4
4
|
|
|
5
5
|
const AVAILABLE_COMMANDS: SlashCommand[] = [
|
|
6
|
+
{
|
|
7
|
+
id: "clear",
|
|
8
|
+
name: "clear",
|
|
9
|
+
description: "Clear the chat session and terminal",
|
|
10
|
+
handler: () => {}, // Handler here won't be used, actual processing is in the hook
|
|
11
|
+
},
|
|
6
12
|
{
|
|
7
13
|
id: "tasks",
|
|
8
14
|
name: "tasks",
|
|
@@ -28,6 +34,12 @@ const AVAILABLE_COMMANDS: SlashCommand[] = [
|
|
|
28
34
|
description: "Show help and key bindings",
|
|
29
35
|
handler: () => {}, // Handler here won't be used, actual processing is in the hook
|
|
30
36
|
},
|
|
37
|
+
{
|
|
38
|
+
id: "status",
|
|
39
|
+
name: "status",
|
|
40
|
+
description: "Show agent status and configuration",
|
|
41
|
+
handler: () => {}, // Handler here won't be used, actual processing is in the hook
|
|
42
|
+
},
|
|
31
43
|
];
|
|
32
44
|
|
|
33
45
|
export interface CommandSelectorProps {
|
|
@@ -48,8 +60,13 @@ export const CommandSelector: React.FC<CommandSelectorProps> = ({
|
|
|
48
60
|
const MAX_VISIBLE_ITEMS = 3;
|
|
49
61
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
50
62
|
|
|
63
|
+
// Reset selected index when search query changes
|
|
64
|
+
React.useEffect(() => {
|
|
65
|
+
setSelectedIndex(0);
|
|
66
|
+
}, [searchQuery]);
|
|
67
|
+
|
|
51
68
|
// Merge agent commands and local commands
|
|
52
|
-
const allCommands = [...
|
|
69
|
+
const allCommands = [...AVAILABLE_COMMANDS, ...commands];
|
|
53
70
|
|
|
54
71
|
// Filter command list
|
|
55
72
|
const filteredCommands = allCommands.filter(
|
|
@@ -70,6 +70,15 @@ export const ConfirmationSelector: React.FC<ConfirmationSelectorProps> = ({
|
|
|
70
70
|
userAnswers: {} as Record<string, string>,
|
|
71
71
|
otherText: "",
|
|
72
72
|
otherCursorPosition: 0,
|
|
73
|
+
savedStates: {} as Record<
|
|
74
|
+
number,
|
|
75
|
+
{
|
|
76
|
+
selectedOptionIndex: number;
|
|
77
|
+
selectedOptionIndices: Set<number>;
|
|
78
|
+
otherText: string;
|
|
79
|
+
otherCursorPosition: number;
|
|
80
|
+
}
|
|
81
|
+
>,
|
|
73
82
|
});
|
|
74
83
|
|
|
75
84
|
const questions =
|
|
@@ -133,23 +142,75 @@ export const ConfirmationSelector: React.FC<ConfirmationSelectorProps> = ({
|
|
|
133
142
|
};
|
|
134
143
|
|
|
135
144
|
if (prev.currentQuestionIndex < questions.length - 1) {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
145
|
+
const nextIndex = prev.currentQuestionIndex + 1;
|
|
146
|
+
const savedStates = {
|
|
147
|
+
...prev.savedStates,
|
|
148
|
+
[prev.currentQuestionIndex]: {
|
|
149
|
+
selectedOptionIndex: prev.selectedOptionIndex,
|
|
150
|
+
selectedOptionIndices: prev.selectedOptionIndices,
|
|
151
|
+
otherText: prev.otherText,
|
|
152
|
+
otherCursorPosition: prev.otherCursorPosition,
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const nextState = savedStates[nextIndex] || {
|
|
139
157
|
selectedOptionIndex: 0,
|
|
140
|
-
selectedOptionIndices: new Set(),
|
|
141
|
-
userAnswers: newAnswers,
|
|
158
|
+
selectedOptionIndices: new Set<number>(),
|
|
142
159
|
otherText: "",
|
|
143
160
|
otherCursorPosition: 0,
|
|
144
161
|
};
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
...prev,
|
|
165
|
+
currentQuestionIndex: nextIndex,
|
|
166
|
+
...nextState,
|
|
167
|
+
userAnswers: newAnswers,
|
|
168
|
+
savedStates,
|
|
169
|
+
};
|
|
145
170
|
} else {
|
|
171
|
+
const finalAnswers = { ...newAnswers };
|
|
172
|
+
// Also collect from savedStates for any questions that were skipped via Tab
|
|
173
|
+
for (const [idxStr, s] of Object.entries(prev.savedStates)) {
|
|
174
|
+
const idx = parseInt(idxStr);
|
|
175
|
+
const q = questions[idx];
|
|
176
|
+
if (q && !finalAnswers[q.question]) {
|
|
177
|
+
const opts = [...q.options, { label: "Other" }];
|
|
178
|
+
let a = "";
|
|
179
|
+
if (q.multiSelect) {
|
|
180
|
+
const selectedLabels = Array.from(s.selectedOptionIndices)
|
|
181
|
+
.filter((i) => i < q.options.length)
|
|
182
|
+
.map((i) => q.options[i].label);
|
|
183
|
+
const isOtherChecked = s.selectedOptionIndices.has(
|
|
184
|
+
opts.length - 1,
|
|
185
|
+
);
|
|
186
|
+
if (isOtherChecked && s.otherText.trim()) {
|
|
187
|
+
selectedLabels.push(s.otherText.trim());
|
|
188
|
+
}
|
|
189
|
+
a = selectedLabels.join(", ");
|
|
190
|
+
} else {
|
|
191
|
+
if (s.selectedOptionIndex === opts.length - 1) {
|
|
192
|
+
a = s.otherText.trim();
|
|
193
|
+
} else {
|
|
194
|
+
a = opts[s.selectedOptionIndex].label;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (a) finalAnswers[q.question] = a;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Only submit if all questions have been answered
|
|
202
|
+
const allAnswered = questions.every(
|
|
203
|
+
(q) => finalAnswers[q.question],
|
|
204
|
+
);
|
|
205
|
+
if (!allAnswered) return prev;
|
|
206
|
+
|
|
146
207
|
onDecision({
|
|
147
208
|
behavior: "allow",
|
|
148
|
-
message: JSON.stringify(
|
|
209
|
+
message: JSON.stringify(finalAnswers),
|
|
149
210
|
});
|
|
150
211
|
return {
|
|
151
212
|
...prev,
|
|
152
|
-
userAnswers:
|
|
213
|
+
userAnswers: finalAnswers,
|
|
153
214
|
};
|
|
154
215
|
}
|
|
155
216
|
});
|
|
@@ -196,6 +257,41 @@ export const ConfirmationSelector: React.FC<ConfirmationSelectorProps> = ({
|
|
|
196
257
|
}));
|
|
197
258
|
return;
|
|
198
259
|
}
|
|
260
|
+
if (key.tab) {
|
|
261
|
+
setQuestionState((prev) => {
|
|
262
|
+
const direction = key.shift ? -1 : 1;
|
|
263
|
+
let nextIndex = prev.currentQuestionIndex + direction;
|
|
264
|
+
if (nextIndex < 0) nextIndex = questions.length - 1;
|
|
265
|
+
if (nextIndex >= questions.length) nextIndex = 0;
|
|
266
|
+
|
|
267
|
+
if (nextIndex === prev.currentQuestionIndex) return prev;
|
|
268
|
+
|
|
269
|
+
const savedStates = {
|
|
270
|
+
...prev.savedStates,
|
|
271
|
+
[prev.currentQuestionIndex]: {
|
|
272
|
+
selectedOptionIndex: prev.selectedOptionIndex,
|
|
273
|
+
selectedOptionIndices: prev.selectedOptionIndices,
|
|
274
|
+
otherText: prev.otherText,
|
|
275
|
+
otherCursorPosition: prev.otherCursorPosition,
|
|
276
|
+
},
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
const nextState = savedStates[nextIndex] || {
|
|
280
|
+
selectedOptionIndex: 0,
|
|
281
|
+
selectedOptionIndices: new Set<number>(),
|
|
282
|
+
otherText: "",
|
|
283
|
+
otherCursorPosition: 0,
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
return {
|
|
287
|
+
...prev,
|
|
288
|
+
currentQuestionIndex: nextIndex,
|
|
289
|
+
...nextState,
|
|
290
|
+
savedStates,
|
|
291
|
+
};
|
|
292
|
+
});
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
199
295
|
|
|
200
296
|
setQuestionState((prev) => {
|
|
201
297
|
const isOtherFocused = prev.selectedOptionIndex === options.length - 1;
|
|
@@ -321,6 +417,19 @@ export const ConfirmationSelector: React.FC<ConfirmationSelectorProps> = ({
|
|
|
321
417
|
return;
|
|
322
418
|
}
|
|
323
419
|
|
|
420
|
+
if (key.tab) {
|
|
421
|
+
const currentIndex = availableOptions.indexOf(state.selectedOption);
|
|
422
|
+
const direction = key.shift ? -1 : 1;
|
|
423
|
+
let nextIndex = currentIndex + direction;
|
|
424
|
+
if (nextIndex < 0) nextIndex = availableOptions.length - 1;
|
|
425
|
+
if (nextIndex >= availableOptions.length) nextIndex = 0;
|
|
426
|
+
setState((prev) => ({
|
|
427
|
+
...prev,
|
|
428
|
+
selectedOption: availableOptions[nextIndex],
|
|
429
|
+
}));
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
|
|
324
433
|
if (input && !key.ctrl && !key.meta && !("alt" in key && key.alt)) {
|
|
325
434
|
setState((prev) => {
|
|
326
435
|
const nextText =
|
|
@@ -436,7 +545,7 @@ export const ConfirmationSelector: React.FC<ConfirmationSelectorProps> = ({
|
|
|
436
545
|
Question {questionState.currentQuestionIndex + 1} of{" "}
|
|
437
546
|
{questions.length} •
|
|
438
547
|
{currentQuestion.multiSelect ? " Space to toggle •" : ""} Use ↑↓
|
|
439
|
-
to navigate • Enter to confirm
|
|
548
|
+
or Tab to navigate • Enter to confirm
|
|
440
549
|
</Text>
|
|
441
550
|
</Box>
|
|
442
551
|
</Box>
|
|
@@ -531,7 +640,7 @@ export const ConfirmationSelector: React.FC<ConfirmationSelectorProps> = ({
|
|
|
531
640
|
</Box>
|
|
532
641
|
</Box>
|
|
533
642
|
<Box marginTop={1}>
|
|
534
|
-
<Text dimColor>Use ↑↓ to navigate • ESC to cancel</Text>
|
|
643
|
+
<Text dimColor>Use ↑↓ or Tab to navigate • ESC to cancel</Text>
|
|
535
644
|
</Box>
|
|
536
645
|
</>
|
|
537
646
|
)}
|
|
@@ -21,6 +21,8 @@ export const HelpView: React.FC<HelpViewProps> = ({ onCancel }) => {
|
|
|
21
21
|
{ key: "Ctrl+B", description: "Background current task" },
|
|
22
22
|
{ key: "Ctrl+V", description: "Paste image" },
|
|
23
23
|
{ key: "Shift+Tab", description: "Cycle permission mode" },
|
|
24
|
+
{ key: "/status", description: "Show agent status and configuration" },
|
|
25
|
+
{ key: "/clear", description: "Clear the chat session and terminal" },
|
|
24
26
|
{
|
|
25
27
|
key: "Esc",
|
|
26
28
|
description: "Interrupt AI or command / Cancel selector / Close help",
|
|
@@ -14,34 +14,38 @@ export const HistorySearch: React.FC<HistorySearchProps> = ({
|
|
|
14
14
|
onCancel,
|
|
15
15
|
}) => {
|
|
16
16
|
const MAX_VISIBLE_ITEMS = 5;
|
|
17
|
-
const [
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
18
|
+
const [entries, setEntries] = useState<PromptEntry[]>([]);
|
|
19
|
+
|
|
20
|
+
const entriesRef = React.useRef<PromptEntry[]>([]);
|
|
21
|
+
const selectedIndexRef = React.useRef(0);
|
|
22
|
+
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
entriesRef.current = entries;
|
|
25
|
+
}, [entries]);
|
|
26
|
+
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
selectedIndexRef.current = selectedIndex;
|
|
29
|
+
}, [selectedIndex]);
|
|
21
30
|
|
|
22
31
|
useEffect(() => {
|
|
23
32
|
const fetchHistory = async () => {
|
|
24
33
|
const results = await PromptHistoryManager.searchHistory(searchQuery);
|
|
25
34
|
const limitedResults = results.slice(0, 20);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
selectedIndex: 0,
|
|
29
|
-
});
|
|
35
|
+
setEntries(limitedResults); // Limit to 20 results
|
|
36
|
+
setSelectedIndex(0);
|
|
30
37
|
};
|
|
31
38
|
fetchHistory();
|
|
32
39
|
}, [searchQuery]);
|
|
33
40
|
|
|
34
41
|
useInput((input, key) => {
|
|
35
42
|
if (key.return) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
return prev;
|
|
44
|
-
});
|
|
43
|
+
if (
|
|
44
|
+
entriesRef.current.length > 0 &&
|
|
45
|
+
selectedIndexRef.current < entriesRef.current.length
|
|
46
|
+
) {
|
|
47
|
+
onSelect(entriesRef.current[selectedIndexRef.current].prompt);
|
|
48
|
+
}
|
|
45
49
|
return;
|
|
46
50
|
}
|
|
47
51
|
|
|
@@ -51,27 +55,18 @@ export const HistorySearch: React.FC<HistorySearchProps> = ({
|
|
|
51
55
|
}
|
|
52
56
|
|
|
53
57
|
if (key.upArrow) {
|
|
54
|
-
|
|
55
|
-
...prev,
|
|
56
|
-
selectedIndex: Math.max(0, prev.selectedIndex - 1),
|
|
57
|
-
}));
|
|
58
|
+
setSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
58
59
|
return;
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
if (key.downArrow) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
prev.entries.length - 1,
|
|
66
|
-
prev.selectedIndex + 1,
|
|
67
|
-
),
|
|
68
|
-
}));
|
|
63
|
+
setSelectedIndex((prev) =>
|
|
64
|
+
Math.min(entriesRef.current.length - 1, prev + 1),
|
|
65
|
+
);
|
|
69
66
|
return;
|
|
70
67
|
}
|
|
71
68
|
});
|
|
72
69
|
|
|
73
|
-
const { entries, selectedIndex } = state;
|
|
74
|
-
|
|
75
70
|
if (entries.length === 0) {
|
|
76
71
|
return (
|
|
77
72
|
<Box
|
|
@@ -8,6 +8,7 @@ import { BackgroundTaskManager } from "./BackgroundTaskManager.js";
|
|
|
8
8
|
import { McpManager } from "./McpManager.js";
|
|
9
9
|
import { RewindCommand } from "./RewindCommand.js";
|
|
10
10
|
import { HelpView } from "./HelpView.js";
|
|
11
|
+
import { StatusCommand } from "./StatusCommand.js";
|
|
11
12
|
import { useInputManager } from "../hooks/useInputManager.js";
|
|
12
13
|
import { useChat } from "../contexts/useChat.js";
|
|
13
14
|
|
|
@@ -57,6 +58,7 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
57
58
|
backgroundCurrentTask,
|
|
58
59
|
messages,
|
|
59
60
|
getFullMessageThread,
|
|
61
|
+
clearMessages,
|
|
60
62
|
} = useChat();
|
|
61
63
|
|
|
62
64
|
// Input manager with all input state and functionality (including images)
|
|
@@ -88,10 +90,12 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
88
90
|
showMcpManager,
|
|
89
91
|
showRewindManager,
|
|
90
92
|
showHelp,
|
|
93
|
+
showStatusCommand,
|
|
91
94
|
setShowBackgroundTaskManager,
|
|
92
95
|
setShowMcpManager,
|
|
93
96
|
setShowRewindManager,
|
|
94
97
|
setShowHelp,
|
|
98
|
+
setShowStatusCommand,
|
|
95
99
|
// Permission mode
|
|
96
100
|
permissionMode,
|
|
97
101
|
setPermissionMode,
|
|
@@ -105,6 +109,7 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
105
109
|
onAbortMessage: abortMessage,
|
|
106
110
|
onBackgroundCurrentTask: backgroundCurrentTask,
|
|
107
111
|
onPermissionModeChange: setChatPermissionMode,
|
|
112
|
+
onClearMessages: clearMessages,
|
|
108
113
|
});
|
|
109
114
|
|
|
110
115
|
// Sync permission mode from useChat to InputManager
|
|
@@ -172,6 +177,10 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
172
177
|
return <HelpView onCancel={() => setShowHelp(false)} />;
|
|
173
178
|
}
|
|
174
179
|
|
|
180
|
+
if (showStatusCommand) {
|
|
181
|
+
return <StatusCommand onCancel={() => setShowStatusCommand(false)} />;
|
|
182
|
+
}
|
|
183
|
+
|
|
175
184
|
return (
|
|
176
185
|
<Box flexDirection="column">
|
|
177
186
|
{showFileSelector && (
|
|
@@ -219,7 +228,8 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
219
228
|
{showBackgroundTaskManager ||
|
|
220
229
|
showMcpManager ||
|
|
221
230
|
showRewindManager ||
|
|
222
|
-
showHelp ||
|
|
231
|
+
showHelp ||
|
|
232
|
+
showStatusCommand || (
|
|
223
233
|
<Box flexDirection="column">
|
|
224
234
|
<Box
|
|
225
235
|
borderStyle="single"
|
|
@@ -3,17 +3,21 @@ import { Box, Text, useInput } from "ink";
|
|
|
3
3
|
import { usePluginManagerContext } from "../contexts/PluginManagerContext.js";
|
|
4
4
|
|
|
5
5
|
export const MarketplaceAddForm: React.FC = () => {
|
|
6
|
-
const { actions } = usePluginManagerContext();
|
|
6
|
+
const { state, actions } = usePluginManagerContext();
|
|
7
7
|
const [source, setSource] = useState("");
|
|
8
8
|
|
|
9
9
|
useInput((input, key) => {
|
|
10
10
|
if (key.escape) {
|
|
11
11
|
actions.setView("MARKETPLACES");
|
|
12
|
+
} else if (state.isLoading) {
|
|
13
|
+
return;
|
|
12
14
|
} else if (key.return) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
setSource((prev) => {
|
|
16
|
+
if (prev.trim()) {
|
|
17
|
+
actions.addMarketplace(prev.trim());
|
|
18
|
+
}
|
|
19
|
+
return prev;
|
|
20
|
+
});
|
|
17
21
|
} else if (key.backspace || key.delete) {
|
|
18
22
|
setSource((prev) => prev.slice(0, -1));
|
|
19
23
|
} else if (input.length === 1) {
|
|
@@ -28,11 +32,20 @@ export const MarketplaceAddForm: React.FC = () => {
|
|
|
28
32
|
</Text>
|
|
29
33
|
<Box marginTop={1}>
|
|
30
34
|
<Text>Source (URL or Path): </Text>
|
|
31
|
-
<Text color="yellow">{source}</Text>
|
|
32
|
-
<Text color="yellow">_</Text>
|
|
35
|
+
<Text color={state.isLoading ? "gray" : "yellow"}>{source}</Text>
|
|
36
|
+
{!state.isLoading && <Text color="yellow">_</Text>}
|
|
33
37
|
</Box>
|
|
38
|
+
{state.isLoading && (
|
|
39
|
+
<Box marginTop={1}>
|
|
40
|
+
<Text color="yellow">⌛ Adding marketplace...</Text>
|
|
41
|
+
</Box>
|
|
42
|
+
)}
|
|
34
43
|
<Box marginTop={1}>
|
|
35
|
-
<Text dimColor>
|
|
44
|
+
<Text dimColor>
|
|
45
|
+
{state.isLoading
|
|
46
|
+
? "Please wait..."
|
|
47
|
+
: "Press Enter to add, Esc to cancel"}
|
|
48
|
+
</Text>
|
|
36
49
|
</Box>
|
|
37
50
|
</Box>
|
|
38
51
|
);
|
|
@@ -24,14 +24,13 @@ export const MarketplaceDetail: React.FC = () => {
|
|
|
24
24
|
setSelectedActionIndex((prev) =>
|
|
25
25
|
prev < ACTIONS.length - 1 ? prev + 1 : 0,
|
|
26
26
|
);
|
|
27
|
-
} else if (key.return && marketplace) {
|
|
27
|
+
} else if (key.return && marketplace && !state.isLoading) {
|
|
28
28
|
const action = ACTIONS[selectedActionIndex].id;
|
|
29
29
|
if (action === "update") {
|
|
30
30
|
actions.updateMarketplace(marketplace.name);
|
|
31
31
|
} else {
|
|
32
32
|
actions.removeMarketplace(marketplace.name);
|
|
33
33
|
}
|
|
34
|
-
actions.setView("MARKETPLACES");
|
|
35
34
|
}
|
|
36
35
|
});
|
|
37
36
|
|
|
@@ -56,19 +55,35 @@ export const MarketplaceDetail: React.FC = () => {
|
|
|
56
55
|
<Text>Source: {JSON.stringify(marketplace.source)}</Text>
|
|
57
56
|
</Box>
|
|
58
57
|
|
|
58
|
+
{state.isLoading && (
|
|
59
|
+
<Box marginBottom={1}>
|
|
60
|
+
<Text color="yellow">⌛ Processing operation...</Text>
|
|
61
|
+
</Box>
|
|
62
|
+
)}
|
|
63
|
+
|
|
59
64
|
<Box marginTop={1} flexDirection="column">
|
|
60
65
|
<Text bold>Marketplace Actions:</Text>
|
|
61
66
|
{ACTIONS.map((action, index) => (
|
|
62
67
|
<Text
|
|
63
68
|
key={action.id}
|
|
64
|
-
color={
|
|
69
|
+
color={
|
|
70
|
+
index === selectedActionIndex
|
|
71
|
+
? state.isLoading
|
|
72
|
+
? "gray"
|
|
73
|
+
: "yellow"
|
|
74
|
+
: undefined
|
|
75
|
+
}
|
|
65
76
|
>
|
|
66
77
|
{index === selectedActionIndex ? "> " : " "}
|
|
67
78
|
{action.label}
|
|
68
79
|
</Text>
|
|
69
80
|
))}
|
|
70
81
|
<Box marginTop={1}>
|
|
71
|
-
<Text dimColor>
|
|
82
|
+
<Text dimColor>
|
|
83
|
+
{state.isLoading
|
|
84
|
+
? "Please wait..."
|
|
85
|
+
: "Use ↑/↓ to select, Enter to confirm"}
|
|
86
|
+
</Text>
|
|
72
87
|
</Box>
|
|
73
88
|
<Box marginTop={1}>
|
|
74
89
|
<Text dimColor>Press Esc to go back</Text>
|
|
@@ -2,7 +2,7 @@ import React from "react";
|
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
3
|
import type { Message, MessageBlock } from "wave-agent-sdk";
|
|
4
4
|
import { MessageSource } from "wave-agent-sdk";
|
|
5
|
-
import {
|
|
5
|
+
import { BangDisplay } from "./BangDisplay.js";
|
|
6
6
|
import { ToolDisplay } from "./ToolDisplay.js";
|
|
7
7
|
import { CompressDisplay } from "./CompressDisplay.js";
|
|
8
8
|
import { ReasoningDisplay } from "./ReasoningDisplay.js";
|
|
@@ -51,8 +51,8 @@ export const MessageBlockItem = ({
|
|
|
51
51
|
</Box>
|
|
52
52
|
)}
|
|
53
53
|
|
|
54
|
-
{block.type === "
|
|
55
|
-
<
|
|
54
|
+
{block.type === "bang" && (
|
|
55
|
+
<BangDisplay block={block} isExpanded={isExpanded} />
|
|
56
56
|
)}
|
|
57
57
|
|
|
58
58
|
{block.type === "tool" && (
|