wave-code 0.7.2 → 0.8.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/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/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 +7 -28
- package/dist/components/ConfirmationSelector.d.ts.map +1 -1
- package/dist/components/ConfirmationSelector.js +116 -11
- package/dist/components/HelpView.d.ts +2 -0
- package/dist/components/HelpView.d.ts.map +1 -1
- package/dist/components/HelpView.js +49 -3
- package/dist/components/InputBox.d.ts.map +1 -1
- package/dist/components/InputBox.js +10 -4
- 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 +5 -5
- 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/constants/commands.d.ts +3 -0
- package/dist/constants/commands.d.ts.map +1 -0
- package/dist/constants/commands.js +38 -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/highlightUtils.d.ts.map +1 -1
- package/dist/utils/highlightUtils.js +66 -42
- 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/{CommandOutputDisplay.tsx → BangDisplay.tsx} +4 -4
- package/src/components/ChatInterface.tsx +8 -0
- package/src/components/CommandSelector.tsx +7 -29
- package/src/components/ConfirmationSelector.tsx +131 -12
- package/src/components/HelpView.tsx +129 -14
- package/src/components/InputBox.tsx +14 -2
- 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 +33 -16
- package/src/components/StatusCommand.tsx +94 -0
- package/src/components/WorktreeExitPrompt.tsx +86 -0
- package/src/constants/commands.ts +41 -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/highlightUtils.ts +66 -42
- package/src/utils/worktree.ts +164 -0
- package/dist/components/CommandOutputDisplay.d.ts +0 -9
- package/dist/components/CommandOutputDisplay.d.ts.map +0 -1
|
@@ -1,14 +1,57 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import React, { useState } from "react";
|
|
2
2
|
import { Box, Text, useInput } from "ink";
|
|
3
|
+
import type { SlashCommand } from "wave-agent-sdk";
|
|
4
|
+
import { AVAILABLE_COMMANDS } from "../constants/commands.js";
|
|
3
5
|
|
|
4
6
|
export interface HelpViewProps {
|
|
5
7
|
onCancel: () => void;
|
|
8
|
+
commands?: SlashCommand[];
|
|
6
9
|
}
|
|
7
10
|
|
|
8
|
-
export const HelpView: React.FC<HelpViewProps> = ({
|
|
11
|
+
export const HelpView: React.FC<HelpViewProps> = ({
|
|
12
|
+
onCancel,
|
|
13
|
+
commands = [],
|
|
14
|
+
}) => {
|
|
15
|
+
const [activeTab, setActiveTab] = useState<
|
|
16
|
+
"general" | "commands" | "custom-commands"
|
|
17
|
+
>("general");
|
|
18
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
19
|
+
const MAX_VISIBLE_ITEMS = 10;
|
|
20
|
+
|
|
21
|
+
const tabs: ("general" | "commands" | "custom-commands")[] = [
|
|
22
|
+
"general",
|
|
23
|
+
"commands",
|
|
24
|
+
];
|
|
25
|
+
if (commands.length > 0) {
|
|
26
|
+
tabs.push("custom-commands");
|
|
27
|
+
}
|
|
28
|
+
|
|
9
29
|
useInput((_, key) => {
|
|
10
|
-
if (key.escape
|
|
30
|
+
if (key.escape) {
|
|
11
31
|
onCancel();
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (key.tab) {
|
|
36
|
+
setActiveTab((prev) => {
|
|
37
|
+
const currentIndex = tabs.indexOf(prev);
|
|
38
|
+
const nextIndex = (currentIndex + 1) % tabs.length;
|
|
39
|
+
return tabs[nextIndex];
|
|
40
|
+
});
|
|
41
|
+
setSelectedIndex(0);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (activeTab === "commands" || activeTab === "custom-commands") {
|
|
46
|
+
const currentCommands =
|
|
47
|
+
activeTab === "commands" ? AVAILABLE_COMMANDS : commands;
|
|
48
|
+
if (key.upArrow) {
|
|
49
|
+
setSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
50
|
+
} else if (key.downArrow) {
|
|
51
|
+
setSelectedIndex((prev) =>
|
|
52
|
+
Math.min(currentCommands.length - 1, prev + 1),
|
|
53
|
+
);
|
|
54
|
+
}
|
|
12
55
|
}
|
|
13
56
|
});
|
|
14
57
|
|
|
@@ -27,6 +70,29 @@ export const HelpView: React.FC<HelpViewProps> = ({ onCancel }) => {
|
|
|
27
70
|
},
|
|
28
71
|
];
|
|
29
72
|
|
|
73
|
+
// Calculate visible window for commands
|
|
74
|
+
const currentCommands =
|
|
75
|
+
activeTab === "commands" ? AVAILABLE_COMMANDS : commands;
|
|
76
|
+
const startIndex = Math.max(
|
|
77
|
+
0,
|
|
78
|
+
Math.min(
|
|
79
|
+
selectedIndex - Math.floor(MAX_VISIBLE_ITEMS / 2),
|
|
80
|
+
Math.max(0, currentCommands.length - MAX_VISIBLE_ITEMS),
|
|
81
|
+
),
|
|
82
|
+
);
|
|
83
|
+
const visibleCommands = currentCommands.slice(
|
|
84
|
+
startIndex,
|
|
85
|
+
startIndex + MAX_VISIBLE_ITEMS,
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
const footerText = [
|
|
89
|
+
"Tab switch",
|
|
90
|
+
activeTab !== "general" && "↑↓ navigate",
|
|
91
|
+
"Esc close",
|
|
92
|
+
]
|
|
93
|
+
.filter(Boolean)
|
|
94
|
+
.join(" • ");
|
|
95
|
+
|
|
30
96
|
return (
|
|
31
97
|
<Box
|
|
32
98
|
flexDirection="column"
|
|
@@ -35,24 +101,73 @@ export const HelpView: React.FC<HelpViewProps> = ({ onCancel }) => {
|
|
|
35
101
|
borderLeft={false}
|
|
36
102
|
borderRight={false}
|
|
37
103
|
paddingX={1}
|
|
104
|
+
width="100%"
|
|
38
105
|
>
|
|
39
|
-
<Box marginBottom={1}>
|
|
40
|
-
<Text
|
|
41
|
-
|
|
106
|
+
<Box marginBottom={1} gap={2}>
|
|
107
|
+
<Text
|
|
108
|
+
color={activeTab === "general" ? "cyan" : "gray"}
|
|
109
|
+
bold
|
|
110
|
+
underline={activeTab === "general"}
|
|
111
|
+
>
|
|
112
|
+
General
|
|
113
|
+
</Text>
|
|
114
|
+
<Text
|
|
115
|
+
color={activeTab === "commands" ? "cyan" : "gray"}
|
|
116
|
+
bold
|
|
117
|
+
underline={activeTab === "commands"}
|
|
118
|
+
>
|
|
119
|
+
Commands
|
|
42
120
|
</Text>
|
|
121
|
+
{commands.length > 0 && (
|
|
122
|
+
<Text
|
|
123
|
+
color={activeTab === "custom-commands" ? "cyan" : "gray"}
|
|
124
|
+
bold
|
|
125
|
+
underline={activeTab === "custom-commands"}
|
|
126
|
+
>
|
|
127
|
+
Custom Commands
|
|
128
|
+
</Text>
|
|
129
|
+
)}
|
|
43
130
|
</Box>
|
|
44
131
|
|
|
45
|
-
{
|
|
46
|
-
<Box
|
|
47
|
-
|
|
48
|
-
<
|
|
49
|
-
|
|
50
|
-
|
|
132
|
+
{activeTab === "general" ? (
|
|
133
|
+
<Box flexDirection="column">
|
|
134
|
+
{helpItems.map((item, index) => (
|
|
135
|
+
<Box key={index}>
|
|
136
|
+
<Box width={20}>
|
|
137
|
+
<Text color="yellow">{item.key}</Text>
|
|
138
|
+
</Box>
|
|
139
|
+
<Text color="white">{item.description}</Text>
|
|
140
|
+
</Box>
|
|
141
|
+
))}
|
|
142
|
+
</Box>
|
|
143
|
+
) : (
|
|
144
|
+
<Box flexDirection="column">
|
|
145
|
+
{visibleCommands.map((command, index) => {
|
|
146
|
+
const actualIndex = startIndex + index;
|
|
147
|
+
const isSelected = actualIndex === selectedIndex;
|
|
148
|
+
return (
|
|
149
|
+
<Box key={command.id} flexDirection="column">
|
|
150
|
+
<Text
|
|
151
|
+
color={isSelected ? "black" : "white"}
|
|
152
|
+
backgroundColor={isSelected ? "cyan" : undefined}
|
|
153
|
+
>
|
|
154
|
+
{isSelected ? "▶ " : " "}/{command.id}
|
|
155
|
+
</Text>
|
|
156
|
+
{isSelected && (
|
|
157
|
+
<Box marginLeft={4}>
|
|
158
|
+
<Text color="gray" dimColor>
|
|
159
|
+
{command.description}
|
|
160
|
+
</Text>
|
|
161
|
+
</Box>
|
|
162
|
+
)}
|
|
163
|
+
</Box>
|
|
164
|
+
);
|
|
165
|
+
})}
|
|
51
166
|
</Box>
|
|
52
|
-
)
|
|
167
|
+
)}
|
|
53
168
|
|
|
54
169
|
<Box marginTop={1}>
|
|
55
|
-
<Text dimColor>
|
|
170
|
+
<Text dimColor>{footerText}</Text>
|
|
56
171
|
</Box>
|
|
57
172
|
</Box>
|
|
58
173
|
);
|
|
@@ -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
|
|
@@ -169,7 +174,13 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
169
174
|
}
|
|
170
175
|
|
|
171
176
|
if (showHelp) {
|
|
172
|
-
return
|
|
177
|
+
return (
|
|
178
|
+
<HelpView onCancel={() => setShowHelp(false)} commands={slashCommands} />
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (showStatusCommand) {
|
|
183
|
+
return <StatusCommand onCancel={() => setShowStatusCommand(false)} />;
|
|
173
184
|
}
|
|
174
185
|
|
|
175
186
|
return (
|
|
@@ -219,7 +230,8 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
219
230
|
{showBackgroundTaskManager ||
|
|
220
231
|
showMcpManager ||
|
|
221
232
|
showRewindManager ||
|
|
222
|
-
showHelp ||
|
|
233
|
+
showHelp ||
|
|
234
|
+
showStatusCommand || (
|
|
223
235
|
<Box flexDirection="column">
|
|
224
236
|
<Box
|
|
225
237
|
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" && (
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import os from "os";
|
|
2
3
|
import { Box, Text, Static } from "ink";
|
|
3
4
|
import type { Message } from "wave-agent-sdk";
|
|
4
5
|
import { MessageBlockItem } from "./MessageBlockItem.js";
|
|
@@ -7,6 +8,9 @@ export interface MessageListProps {
|
|
|
7
8
|
messages: Message[];
|
|
8
9
|
isExpanded?: boolean;
|
|
9
10
|
hideDynamicBlocks?: boolean;
|
|
11
|
+
version?: string;
|
|
12
|
+
workdir?: string;
|
|
13
|
+
model?: string;
|
|
10
14
|
}
|
|
11
15
|
|
|
12
16
|
export const MessageList = React.memo(
|
|
@@ -14,17 +18,23 @@ export const MessageList = React.memo(
|
|
|
14
18
|
messages,
|
|
15
19
|
isExpanded = false,
|
|
16
20
|
hideDynamicBlocks = false,
|
|
21
|
+
version,
|
|
22
|
+
workdir,
|
|
23
|
+
model,
|
|
17
24
|
}: MessageListProps) => {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
const welcomeMessage = (
|
|
26
|
+
<Box flexDirection="column" paddingTop={1}>
|
|
27
|
+
<Text color="gray">
|
|
28
|
+
WAVE{version ? ` v${version}` : ""}
|
|
29
|
+
{model ? ` • ${model}` : ""}
|
|
30
|
+
</Text>
|
|
31
|
+
{workdir && (
|
|
32
|
+
<Text color="gray" wrap="truncate-middle">
|
|
33
|
+
{workdir.replace(os.homedir(), "~")}
|
|
34
|
+
</Text>
|
|
35
|
+
)}
|
|
36
|
+
</Box>
|
|
37
|
+
);
|
|
28
38
|
|
|
29
39
|
// Limit messages when expanded to prevent long rendering times
|
|
30
40
|
const maxExpandedMessages = 20;
|
|
@@ -54,7 +64,7 @@ export const MessageList = React.memo(
|
|
|
54
64
|
const isDynamic =
|
|
55
65
|
isLastMessage &&
|
|
56
66
|
((block.type === "tool" && block.stage !== "end") ||
|
|
57
|
-
(block.type === "
|
|
67
|
+
(block.type === "bang" && block.isRunning));
|
|
58
68
|
return { ...item, isDynamic };
|
|
59
69
|
});
|
|
60
70
|
|
|
@@ -63,20 +73,34 @@ export const MessageList = React.memo(
|
|
|
63
73
|
? []
|
|
64
74
|
: blocksWithStatus.filter((b) => b.isDynamic);
|
|
65
75
|
|
|
76
|
+
const staticItems = [
|
|
77
|
+
{ isWelcome: true, key: "welcome", block: undefined, message: undefined },
|
|
78
|
+
...staticBlocks.map((b) => ({ ...b, isWelcome: false })),
|
|
79
|
+
];
|
|
80
|
+
|
|
66
81
|
return (
|
|
67
82
|
<Box flexDirection="column" paddingBottom={1}>
|
|
68
|
-
{/* Static blocks */}
|
|
69
|
-
{
|
|
70
|
-
<Static items={
|
|
71
|
-
{(item) =>
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
83
|
+
{/* Static items (Welcome message + Static blocks) */}
|
|
84
|
+
{staticItems.length > 0 && (
|
|
85
|
+
<Static items={staticItems}>
|
|
86
|
+
{(item) => {
|
|
87
|
+
if (item.isWelcome) {
|
|
88
|
+
return (
|
|
89
|
+
<React.Fragment key={item.key}>
|
|
90
|
+
{welcomeMessage}
|
|
91
|
+
</React.Fragment>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
return (
|
|
95
|
+
<MessageBlockItem
|
|
96
|
+
key={item.key}
|
|
97
|
+
block={item.block!}
|
|
98
|
+
message={item.message!}
|
|
99
|
+
isExpanded={isExpanded}
|
|
100
|
+
paddingTop={1}
|
|
101
|
+
/>
|
|
102
|
+
);
|
|
103
|
+
}}
|
|
80
104
|
</Static>
|
|
81
105
|
)}
|
|
82
106
|
|
|
@@ -49,7 +49,7 @@ export const PluginDetail: React.FC = () => {
|
|
|
49
49
|
setSelectedScopeIndex((prev) =>
|
|
50
50
|
prev < SCOPES.length - 1 ? prev + 1 : 0,
|
|
51
51
|
);
|
|
52
|
-
} else if (key.return && plugin) {
|
|
52
|
+
} else if (key.return && plugin && !state.isLoading) {
|
|
53
53
|
if (isInstalledAndEnabled) {
|
|
54
54
|
setSelectedActionIndex((prev) => {
|
|
55
55
|
const action = INSTALLED_ACTIONS[prev].id;
|
|
@@ -70,7 +70,6 @@ export const PluginDetail: React.FC = () => {
|
|
|
70
70
|
return prev;
|
|
71
71
|
});
|
|
72
72
|
}
|
|
73
|
-
actions.setView("INSTALLED");
|
|
74
73
|
}
|
|
75
74
|
});
|
|
76
75
|
|
|
@@ -102,6 +101,11 @@ export const PluginDetail: React.FC = () => {
|
|
|
102
101
|
</Text>
|
|
103
102
|
</Box>
|
|
104
103
|
)}
|
|
104
|
+
{state.isLoading && (
|
|
105
|
+
<Box marginBottom={1}>
|
|
106
|
+
<Text color="yellow">⌛ Processing operation...</Text>
|
|
107
|
+
</Box>
|
|
108
|
+
)}
|
|
105
109
|
|
|
106
110
|
<Box marginTop={1} flexDirection="column">
|
|
107
111
|
{isInstalledAndEnabled ? (
|
|
@@ -110,14 +114,24 @@ export const PluginDetail: React.FC = () => {
|
|
|
110
114
|
{INSTALLED_ACTIONS.map((action, index) => (
|
|
111
115
|
<Text
|
|
112
116
|
key={action.id}
|
|
113
|
-
color={
|
|
117
|
+
color={
|
|
118
|
+
index === selectedActionIndex
|
|
119
|
+
? state.isLoading
|
|
120
|
+
? "gray"
|
|
121
|
+
: "yellow"
|
|
122
|
+
: undefined
|
|
123
|
+
}
|
|
114
124
|
>
|
|
115
125
|
{index === selectedActionIndex ? "> " : " "}
|
|
116
126
|
{action.label}
|
|
117
127
|
</Text>
|
|
118
128
|
))}
|
|
119
129
|
<Box marginTop={1}>
|
|
120
|
-
<Text dimColor>
|
|
130
|
+
<Text dimColor>
|
|
131
|
+
{state.isLoading
|
|
132
|
+
? "Please wait..."
|
|
133
|
+
: "Use ↑/↓ to select, Enter to confirm"}
|
|
134
|
+
</Text>
|
|
121
135
|
</Box>
|
|
122
136
|
</Box>
|
|
123
137
|
) : (
|
|
@@ -126,14 +140,24 @@ export const PluginDetail: React.FC = () => {
|
|
|
126
140
|
{SCOPES.map((scope, index) => (
|
|
127
141
|
<Text
|
|
128
142
|
key={scope.id}
|
|
129
|
-
color={
|
|
143
|
+
color={
|
|
144
|
+
index === selectedScopeIndex
|
|
145
|
+
? state.isLoading
|
|
146
|
+
? "gray"
|
|
147
|
+
: "green"
|
|
148
|
+
: undefined
|
|
149
|
+
}
|
|
130
150
|
>
|
|
131
151
|
{index === selectedScopeIndex ? "> " : " "}
|
|
132
152
|
{scope.label}
|
|
133
153
|
</Text>
|
|
134
154
|
))}
|
|
135
155
|
<Box marginTop={1}>
|
|
136
|
-
<Text dimColor>
|
|
156
|
+
<Text dimColor>
|
|
157
|
+
{state.isLoading
|
|
158
|
+
? "Please wait..."
|
|
159
|
+
: "Use ↑/↓ to select, Enter to install"}
|
|
160
|
+
</Text>
|
|
137
161
|
</Box>
|
|
138
162
|
</Box>
|
|
139
163
|
)}
|
|
@@ -172,14 +172,32 @@ export const PluginManagerShell: React.FC<{ children?: React.ReactNode }> = ({
|
|
|
172
172
|
borderLeft={false}
|
|
173
173
|
borderRight={false}
|
|
174
174
|
>
|
|
175
|
-
<
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
175
|
+
<Box flexGrow={1}>
|
|
176
|
+
<Text dimColor>
|
|
177
|
+
{state.isLoading
|
|
178
|
+
? "Loading..."
|
|
179
|
+
: "Use Tab to switch views, arrows to navigate, Enter to select, Esc to go back"}
|
|
180
|
+
</Text>
|
|
181
|
+
</Box>
|
|
182
|
+
{state.isLoading && (
|
|
183
|
+
<Box marginLeft={2}>
|
|
184
|
+
<Text color="yellow" bold>
|
|
185
|
+
⌛ Processing...
|
|
186
|
+
</Text>
|
|
187
|
+
</Box>
|
|
188
|
+
)}
|
|
189
|
+
{state.successMessage && (
|
|
190
|
+
<Box marginLeft={2}>
|
|
191
|
+
<Text color="green" bold>
|
|
192
|
+
✓ {state.successMessage}
|
|
193
|
+
</Text>
|
|
194
|
+
</Box>
|
|
195
|
+
)}
|
|
180
196
|
{state.error && (
|
|
181
197
|
<Box marginLeft={2}>
|
|
182
|
-
<Text color="red"
|
|
198
|
+
<Text color="red" bold>
|
|
199
|
+
✗ Error: {state.error}
|
|
200
|
+
</Text>
|
|
183
201
|
</Box>
|
|
184
202
|
)}
|
|
185
203
|
</Box>
|
|
@@ -17,6 +17,7 @@ export interface PluginManagerState {
|
|
|
17
17
|
selectedId: string | null; // Plugin name or Marketplace name
|
|
18
18
|
isLoading: boolean;
|
|
19
19
|
error: string | null;
|
|
20
|
+
successMessage: string | null;
|
|
20
21
|
searchQuery: string;
|
|
21
22
|
}
|
|
22
23
|
|
|
@@ -43,5 +44,6 @@ export interface PluginManagerContextType {
|
|
|
43
44
|
uninstallPlugin: (name: string, marketplace: string) => Promise<void>;
|
|
44
45
|
updatePlugin: (name: string, marketplace: string) => Promise<void>;
|
|
45
46
|
refresh: () => Promise<void>;
|
|
47
|
+
clearPluginFeedback: () => void;
|
|
46
48
|
};
|
|
47
49
|
}
|
|
@@ -56,15 +56,18 @@ export const SessionSelector: React.FC<SessionSelectorProps> = ({
|
|
|
56
56
|
);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
const
|
|
59
|
+
const MAX_VISIBLE_ITEMS = 3;
|
|
60
60
|
const startIndex = Math.max(
|
|
61
61
|
0,
|
|
62
62
|
Math.min(
|
|
63
|
-
selectedIndex - Math.floor(
|
|
64
|
-
sessions.length -
|
|
63
|
+
selectedIndex - Math.floor(MAX_VISIBLE_ITEMS / 2),
|
|
64
|
+
Math.max(0, sessions.length - MAX_VISIBLE_ITEMS),
|
|
65
65
|
),
|
|
66
66
|
);
|
|
67
|
-
const displaySessions = sessions.slice(
|
|
67
|
+
const displaySessions = sessions.slice(
|
|
68
|
+
startIndex,
|
|
69
|
+
startIndex + MAX_VISIBLE_ITEMS,
|
|
70
|
+
);
|
|
68
71
|
|
|
69
72
|
return (
|
|
70
73
|
<Box
|
|
@@ -92,26 +95,40 @@ export const SessionSelector: React.FC<SessionSelectorProps> = ({
|
|
|
92
95
|
return (
|
|
93
96
|
<Box key={session.id} flexDirection="column" width="100%">
|
|
94
97
|
<Box width="100%">
|
|
95
|
-
<
|
|
96
|
-
color={isSelected ? "black" : "white"}
|
|
98
|
+
<Box
|
|
97
99
|
backgroundColor={isSelected ? "cyan" : undefined}
|
|
100
|
+
flexShrink={0}
|
|
98
101
|
>
|
|
99
|
-
{isSelected ? "
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
</
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
102
|
+
<Text color={isSelected ? "black" : "white"}>
|
|
103
|
+
{isSelected ? "▶ " : " "}
|
|
104
|
+
</Text>
|
|
105
|
+
</Box>
|
|
106
|
+
<Box
|
|
107
|
+
backgroundColor={isSelected ? "cyan" : undefined}
|
|
108
|
+
flexGrow={1}
|
|
109
|
+
>
|
|
110
|
+
<Text
|
|
111
|
+
color={isSelected ? "black" : "white"}
|
|
112
|
+
wrap="truncate-end"
|
|
113
|
+
>
|
|
114
|
+
{session.id} | {lastActiveAt} | {session.latestTotalTokens}{" "}
|
|
115
|
+
tokens
|
|
116
|
+
</Text>
|
|
117
|
+
</Box>
|
|
108
118
|
</Box>
|
|
119
|
+
{isSelected && session.firstMessage && (
|
|
120
|
+
<Box marginLeft={2} width="100%">
|
|
121
|
+
<Text dimColor italic wrap="truncate-end">
|
|
122
|
+
{session.firstMessage.replace(/\n/g, "\\n")}
|
|
123
|
+
</Text>
|
|
124
|
+
</Box>
|
|
125
|
+
)}
|
|
109
126
|
</Box>
|
|
110
127
|
);
|
|
111
128
|
})}
|
|
112
129
|
</Box>
|
|
113
130
|
|
|
114
|
-
{sessions.length >
|
|
131
|
+
{sessions.length > MAX_VISIBLE_ITEMS && (
|
|
115
132
|
<Box>
|
|
116
133
|
<Text dimColor>
|
|
117
134
|
... showing {displaySessions.length} of {sessions.length} sessions
|