wave-code 0.12.7 → 0.12.8
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/components/MarketplaceAddForm.d.ts.map +1 -1
- package/dist/components/MarketplaceAddForm.js +9 -7
- package/dist/components/McpManager.d.ts.map +1 -1
- package/dist/components/McpManager.js +27 -35
- package/dist/components/MessageList.d.ts.map +1 -1
- package/dist/components/MessageList.js +1 -0
- package/dist/components/PluginDetail.d.ts.map +1 -1
- package/dist/components/PluginDetail.js +18 -15
- package/dist/contexts/useChat.d.ts.map +1 -1
- package/dist/contexts/useChat.js +15 -24
- package/package.json +2 -2
- package/src/components/MarketplaceAddForm.tsx +10 -7
- package/src/components/McpManager.tsx +31 -37
- package/src/components/MessageList.tsx +1 -0
- package/src/components/PluginDetail.tsx +24 -18
- package/src/contexts/useChat.tsx +14 -22
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MarketplaceAddForm.d.ts","sourceRoot":"","sources":["../../src/components/MarketplaceAddForm.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"MarketplaceAddForm.d.ts","sourceRoot":"","sources":["../../src/components/MarketplaceAddForm.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAI3D,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAkDtC,CAAC"}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from "react";
|
|
2
|
+
import { useState, useRef, useEffect } from "react";
|
|
3
3
|
import { Box, Text, useInput } from "ink";
|
|
4
4
|
import { usePluginManagerContext } from "../contexts/PluginManagerContext.js";
|
|
5
5
|
export const MarketplaceAddForm = () => {
|
|
6
6
|
const { state, actions } = usePluginManagerContext();
|
|
7
7
|
const [source, setSource] = useState("");
|
|
8
|
+
const sourceRef = useRef(source);
|
|
9
|
+
// Keep ref in sync with state
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
sourceRef.current = source;
|
|
12
|
+
}, [source]);
|
|
8
13
|
useInput((input, key) => {
|
|
9
14
|
if (key.escape) {
|
|
10
15
|
actions.setView("MARKETPLACES");
|
|
@@ -13,12 +18,9 @@ export const MarketplaceAddForm = () => {
|
|
|
13
18
|
return;
|
|
14
19
|
}
|
|
15
20
|
else if (key.return) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
return prev;
|
|
21
|
-
});
|
|
21
|
+
if (sourceRef.current.trim()) {
|
|
22
|
+
actions.addMarketplace(sourceRef.current.trim());
|
|
23
|
+
}
|
|
22
24
|
}
|
|
23
25
|
else if (key.backspace || key.delete) {
|
|
24
26
|
setSource((prev) => prev.slice(0, -1));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"McpManager.d.ts","sourceRoot":"","sources":["../../src/components/McpManager.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"McpManager.d.ts","sourceRoot":"","sources":["../../src/components/McpManager.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAE3D,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,eAAe,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1D,kBAAkB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC9D;AAED,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAoThD,CAAC"}
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from "react";
|
|
2
|
+
import { useState, useRef, useEffect } from "react";
|
|
3
3
|
import { Box, Text, useInput } from "ink";
|
|
4
4
|
export const McpManager = ({ onCancel, servers, onConnectServer, onDisconnectServer, }) => {
|
|
5
5
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
6
6
|
const [viewMode, setViewMode] = useState("list");
|
|
7
|
+
// Keep ref in sync with state to avoid stale closures in useInput
|
|
8
|
+
const selectedIndexRef = useRef(selectedIndex);
|
|
9
|
+
const viewModeRef = useRef(viewMode);
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
selectedIndexRef.current = selectedIndex;
|
|
12
|
+
}, [selectedIndex]);
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
viewModeRef.current = viewMode;
|
|
15
|
+
}, [viewMode]);
|
|
7
16
|
// Dynamically calculate selectedServer based on selectedIndex and servers
|
|
8
17
|
const selectedServer = viewMode === "detail" &&
|
|
9
18
|
servers.length > 0 &&
|
|
@@ -45,29 +54,18 @@ export const McpManager = ({ onCancel, servers, onConnectServer, onDisconnectSer
|
|
|
45
54
|
};
|
|
46
55
|
useInput((input, key) => {
|
|
47
56
|
if (key.return) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if (servers.length > 0 && prevIndex < servers.length) {
|
|
52
|
-
// We can't call setViewMode here because we're already in a setViewMode call
|
|
53
|
-
// But we can return the new mode from the outer setViewMode
|
|
54
|
-
}
|
|
55
|
-
return prevIndex;
|
|
56
|
-
});
|
|
57
|
-
return "detail";
|
|
58
|
-
}
|
|
59
|
-
return prevMode;
|
|
60
|
-
});
|
|
57
|
+
if (viewModeRef.current === "list") {
|
|
58
|
+
setViewMode("detail");
|
|
59
|
+
}
|
|
61
60
|
return;
|
|
62
61
|
}
|
|
63
62
|
if (key.escape) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
63
|
+
if (viewModeRef.current === "detail") {
|
|
64
|
+
setViewMode("list");
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
68
67
|
onCancel();
|
|
69
|
-
|
|
70
|
-
});
|
|
68
|
+
}
|
|
71
69
|
return;
|
|
72
70
|
}
|
|
73
71
|
if (key.upArrow) {
|
|
@@ -80,24 +78,18 @@ export const McpManager = ({ onCancel, servers, onConnectServer, onDisconnectSer
|
|
|
80
78
|
}
|
|
81
79
|
// Hotkeys for server actions
|
|
82
80
|
if (input === "c") {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
return prev;
|
|
90
|
-
});
|
|
81
|
+
const server = servers[selectedIndexRef.current];
|
|
82
|
+
if (server &&
|
|
83
|
+
(server.status === "disconnected" || server.status === "error")) {
|
|
84
|
+
handleConnect(server.name);
|
|
85
|
+
}
|
|
91
86
|
return;
|
|
92
87
|
}
|
|
93
88
|
if (input === "d") {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
return prev;
|
|
100
|
-
});
|
|
89
|
+
const server = servers[selectedIndexRef.current];
|
|
90
|
+
if (server && server.status === "connected") {
|
|
91
|
+
handleDisconnect(server.name);
|
|
92
|
+
}
|
|
101
93
|
return;
|
|
102
94
|
}
|
|
103
95
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../src/components/MessageList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAE,OAAO,EAAgB,MAAM,gBAAgB,CAAC;AAG5D,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,WAAW,uFAOnB,gBAAgB,
|
|
1
|
+
{"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../src/components/MessageList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAE,OAAO,EAAgB,MAAM,gBAAgB,CAAC;AAG5D,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,WAAW,uFAOnB,gBAAgB,6CAkHpB,CAAC"}
|
|
@@ -30,6 +30,7 @@ export const MessageList = React.memo(({ messages, isExpanded = false, forceStat
|
|
|
30
30
|
const blocksWithStatus = allBlocks.map((item) => {
|
|
31
31
|
const { block } = item;
|
|
32
32
|
const isDynamic = !forceStatic &&
|
|
33
|
+
!isExpanded &&
|
|
33
34
|
(messagesWithRunningBlocks.has(item.messageIndex) || isRunning(block));
|
|
34
35
|
return { ...item, isDynamic };
|
|
35
36
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PluginDetail.d.ts","sourceRoot":"","sources":["../../src/components/PluginDetail.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"PluginDetail.d.ts","sourceRoot":"","sources":["../../src/components/PluginDetail.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAU3D,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAqKhC,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from "react";
|
|
2
|
+
import { useState, useRef, useEffect } from "react";
|
|
3
3
|
import { Box, Text, useInput } from "ink";
|
|
4
4
|
import { usePluginManagerContext } from "../contexts/PluginManagerContext.js";
|
|
5
5
|
const SCOPES = [
|
|
@@ -11,6 +11,15 @@ export const PluginDetail = () => {
|
|
|
11
11
|
const { state, discoverablePlugins, installedPlugins, actions } = usePluginManagerContext();
|
|
12
12
|
const [selectedScopeIndex, setSelectedScopeIndex] = useState(0);
|
|
13
13
|
const [selectedActionIndex, setSelectedActionIndex] = useState(0);
|
|
14
|
+
// Keep refs in sync with state to avoid stale closures in useInput
|
|
15
|
+
const selectedScopeIndexRef = useRef(selectedScopeIndex);
|
|
16
|
+
const selectedActionIndexRef = useRef(selectedActionIndex);
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
selectedScopeIndexRef.current = selectedScopeIndex;
|
|
19
|
+
}, [selectedScopeIndex]);
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
selectedActionIndexRef.current = selectedActionIndex;
|
|
22
|
+
}, [selectedActionIndex]);
|
|
14
23
|
const plugin = discoverablePlugins.find((p) => `${p.name}@${p.marketplace}` === state.selectedId) ||
|
|
15
24
|
installedPlugins.find((p) => `${p.name}@${p.marketplace}` === state.selectedId);
|
|
16
25
|
const INSTALLED_ACTIONS = [
|
|
@@ -33,22 +42,16 @@ export const PluginDetail = () => {
|
|
|
33
42
|
}
|
|
34
43
|
else if (key.return && plugin && !state.isLoading) {
|
|
35
44
|
if (isInstalledAndEnabled) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
return prev;
|
|
45
|
-
});
|
|
45
|
+
const action = INSTALLED_ACTIONS[selectedActionIndexRef.current].id;
|
|
46
|
+
if (action === "uninstall") {
|
|
47
|
+
actions.uninstallPlugin(plugin.name, plugin.marketplace);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
actions.updatePlugin(plugin.name, plugin.marketplace);
|
|
51
|
+
}
|
|
46
52
|
}
|
|
47
53
|
else {
|
|
48
|
-
|
|
49
|
-
actions.installPlugin(plugin.name, plugin.marketplace, SCOPES[prev].id);
|
|
50
|
-
return prev;
|
|
51
|
-
});
|
|
54
|
+
actions.installPlugin(plugin.name, plugin.marketplace, SCOPES[selectedScopeIndexRef.current].id);
|
|
52
55
|
}
|
|
53
56
|
}
|
|
54
57
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useChat.d.ts","sourceRoot":"","sources":["../../src/contexts/useChat.tsx"],"names":[],"mappings":"AAAA,OAAO,KAQN,MAAM,OAAO,CAAC;AAGf,OAAO,KAAK,EACV,OAAO,EACP,eAAe,EACf,cAAc,EACd,IAAI,EACJ,YAAY,EACZ,kBAAkB,EAClB,cAAc,EACf,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"useChat.d.ts","sourceRoot":"","sources":["../../src/contexts/useChat.tsx"],"names":[],"mappings":"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"}
|
package/dist/contexts/useChat.js
CHANGED
|
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { createContext, useContext, useCallback, useRef, useEffect, useState, useMemo, } from "react";
|
|
3
3
|
import { useInput, useStdout } from "ink";
|
|
4
4
|
import { useAppConfig } from "./useAppConfig.js";
|
|
5
|
-
import { Agent, OPERATION_CANCELLED_BY_USER,
|
|
5
|
+
import { Agent, OPERATION_CANCELLED_BY_USER, } from "wave-agent-sdk";
|
|
6
6
|
import { logger } from "../utils/logger.js";
|
|
7
7
|
import { throttle } from "../utils/throttle.js";
|
|
8
8
|
import { displayUsageSummary } from "../utils/usageSummary.js";
|
|
@@ -420,30 +420,21 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
|
|
|
420
420
|
useInput((input, key) => {
|
|
421
421
|
if (key.ctrl && input === "o") {
|
|
422
422
|
// Clear terminal screen when expanded state changes
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
return [...currentMessages.slice(0, -1), frozenLastMessage];
|
|
437
|
-
});
|
|
438
|
-
}
|
|
439
|
-
else {
|
|
440
|
-
// Transitioning to COLLAPSED: Restore from agent's actual state
|
|
441
|
-
if (agentRef.current) {
|
|
442
|
-
setMessages([...agentRef.current.messages]);
|
|
443
|
-
}
|
|
423
|
+
// Use ref to get the current value to avoid stale closure
|
|
424
|
+
const nextExpanded = !isExpandedRef.current;
|
|
425
|
+
setIsExpanded(nextExpanded);
|
|
426
|
+
isExpandedRef.current = nextExpanded;
|
|
427
|
+
if (nextExpanded) {
|
|
428
|
+
// Transitioning to EXPANDED: Freeze the current view
|
|
429
|
+
// Cancel any pending throttled updates to avoid overwriting the frozen state
|
|
430
|
+
throttledSetMessages.cancel();
|
|
431
|
+
}
|
|
432
|
+
else {
|
|
433
|
+
// Transitioning to COLLAPSED: Restore from agent's actual state
|
|
434
|
+
if (agentRef.current) {
|
|
435
|
+
setMessages([...agentRef.current.messages]);
|
|
444
436
|
}
|
|
445
|
-
|
|
446
|
-
});
|
|
437
|
+
}
|
|
447
438
|
// Force remount to re-render Static items with new isExpanded state
|
|
448
439
|
requestRemount();
|
|
449
440
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wave-code",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.8",
|
|
4
4
|
"description": "CLI-based code assistant powered by AI, built with React and Ink",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"semver": "^7.7.4",
|
|
43
43
|
"yargs": "^17.7.2",
|
|
44
44
|
"zod": "^3.23.8",
|
|
45
|
-
"wave-agent-sdk": "0.12.
|
|
45
|
+
"wave-agent-sdk": "0.12.8"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/react": "^19.1.8",
|
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
import React, { useState } from "react";
|
|
1
|
+
import React, { useState, useRef, useEffect } from "react";
|
|
2
2
|
import { Box, Text, useInput } from "ink";
|
|
3
3
|
import { usePluginManagerContext } from "../contexts/PluginManagerContext.js";
|
|
4
4
|
|
|
5
5
|
export const MarketplaceAddForm: React.FC = () => {
|
|
6
6
|
const { state, actions } = usePluginManagerContext();
|
|
7
7
|
const [source, setSource] = useState("");
|
|
8
|
+
const sourceRef = useRef(source);
|
|
9
|
+
|
|
10
|
+
// Keep ref in sync with state
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
sourceRef.current = source;
|
|
13
|
+
}, [source]);
|
|
8
14
|
|
|
9
15
|
useInput((input, key) => {
|
|
10
16
|
if (key.escape) {
|
|
@@ -12,12 +18,9 @@ export const MarketplaceAddForm: React.FC = () => {
|
|
|
12
18
|
} else if (state.isLoading) {
|
|
13
19
|
return;
|
|
14
20
|
} else if (key.return) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
return prev;
|
|
20
|
-
});
|
|
21
|
+
if (sourceRef.current.trim()) {
|
|
22
|
+
actions.addMarketplace(sourceRef.current.trim());
|
|
23
|
+
}
|
|
21
24
|
} else if (key.backspace || key.delete) {
|
|
22
25
|
setSource((prev) => prev.slice(0, -1));
|
|
23
26
|
} else if (input.length === 1) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState } from "react";
|
|
1
|
+
import React, { useState, useRef, useEffect } from "react";
|
|
2
2
|
import { Box, Text, useInput } from "ink";
|
|
3
3
|
import { McpServerStatus } from "wave-agent-sdk";
|
|
4
4
|
|
|
@@ -18,6 +18,18 @@ export const McpManager: React.FC<McpManagerProps> = ({
|
|
|
18
18
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
19
19
|
const [viewMode, setViewMode] = useState<"list" | "detail">("list");
|
|
20
20
|
|
|
21
|
+
// Keep ref in sync with state to avoid stale closures in useInput
|
|
22
|
+
const selectedIndexRef = useRef(selectedIndex);
|
|
23
|
+
const viewModeRef = useRef(viewMode);
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
selectedIndexRef.current = selectedIndex;
|
|
27
|
+
}, [selectedIndex]);
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
viewModeRef.current = viewMode;
|
|
31
|
+
}, [viewMode]);
|
|
32
|
+
|
|
21
33
|
// Dynamically calculate selectedServer based on selectedIndex and servers
|
|
22
34
|
const selectedServer =
|
|
23
35
|
viewMode === "detail" &&
|
|
@@ -66,30 +78,18 @@ export const McpManager: React.FC<McpManagerProps> = ({
|
|
|
66
78
|
|
|
67
79
|
useInput((input, key) => {
|
|
68
80
|
if (key.return) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
if (servers.length > 0 && prevIndex < servers.length) {
|
|
73
|
-
// We can't call setViewMode here because we're already in a setViewMode call
|
|
74
|
-
// But we can return the new mode from the outer setViewMode
|
|
75
|
-
}
|
|
76
|
-
return prevIndex;
|
|
77
|
-
});
|
|
78
|
-
return "detail";
|
|
79
|
-
}
|
|
80
|
-
return prevMode;
|
|
81
|
-
});
|
|
81
|
+
if (viewModeRef.current === "list") {
|
|
82
|
+
setViewMode("detail");
|
|
83
|
+
}
|
|
82
84
|
return;
|
|
83
85
|
}
|
|
84
86
|
|
|
85
87
|
if (key.escape) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
88
|
+
if (viewModeRef.current === "detail") {
|
|
89
|
+
setViewMode("list");
|
|
90
|
+
} else {
|
|
90
91
|
onCancel();
|
|
91
|
-
|
|
92
|
-
});
|
|
92
|
+
}
|
|
93
93
|
return;
|
|
94
94
|
}
|
|
95
95
|
|
|
@@ -105,27 +105,21 @@ export const McpManager: React.FC<McpManagerProps> = ({
|
|
|
105
105
|
|
|
106
106
|
// Hotkeys for server actions
|
|
107
107
|
if (input === "c") {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
)
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
return prev;
|
|
117
|
-
});
|
|
108
|
+
const server = servers[selectedIndexRef.current];
|
|
109
|
+
if (
|
|
110
|
+
server &&
|
|
111
|
+
(server.status === "disconnected" || server.status === "error")
|
|
112
|
+
) {
|
|
113
|
+
handleConnect(server.name);
|
|
114
|
+
}
|
|
118
115
|
return;
|
|
119
116
|
}
|
|
120
117
|
|
|
121
118
|
if (input === "d") {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
return prev;
|
|
128
|
-
});
|
|
119
|
+
const server = servers[selectedIndexRef.current];
|
|
120
|
+
if (server && server.status === "connected") {
|
|
121
|
+
handleDisconnect(server.name);
|
|
122
|
+
}
|
|
129
123
|
return;
|
|
130
124
|
}
|
|
131
125
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState } from "react";
|
|
1
|
+
import React, { useState, useRef, useEffect } from "react";
|
|
2
2
|
import { Box, Text, useInput } from "ink";
|
|
3
3
|
import { usePluginManagerContext } from "../contexts/PluginManagerContext.js";
|
|
4
4
|
|
|
@@ -14,6 +14,18 @@ export const PluginDetail: React.FC = () => {
|
|
|
14
14
|
const [selectedScopeIndex, setSelectedScopeIndex] = useState(0);
|
|
15
15
|
const [selectedActionIndex, setSelectedActionIndex] = useState(0);
|
|
16
16
|
|
|
17
|
+
// Keep refs in sync with state to avoid stale closures in useInput
|
|
18
|
+
const selectedScopeIndexRef = useRef(selectedScopeIndex);
|
|
19
|
+
const selectedActionIndexRef = useRef(selectedActionIndex);
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
selectedScopeIndexRef.current = selectedScopeIndex;
|
|
23
|
+
}, [selectedScopeIndex]);
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
selectedActionIndexRef.current = selectedActionIndex;
|
|
27
|
+
}, [selectedActionIndex]);
|
|
28
|
+
|
|
17
29
|
const plugin =
|
|
18
30
|
discoverablePlugins.find(
|
|
19
31
|
(p) => `${p.name}@${p.marketplace}` === state.selectedId,
|
|
@@ -51,24 +63,18 @@ export const PluginDetail: React.FC = () => {
|
|
|
51
63
|
);
|
|
52
64
|
} else if (key.return && plugin && !state.isLoading) {
|
|
53
65
|
if (isInstalledAndEnabled) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
return prev;
|
|
62
|
-
});
|
|
66
|
+
const action = INSTALLED_ACTIONS[selectedActionIndexRef.current].id;
|
|
67
|
+
if (action === "uninstall") {
|
|
68
|
+
actions.uninstallPlugin(plugin.name, plugin.marketplace);
|
|
69
|
+
} else {
|
|
70
|
+
actions.updatePlugin(plugin.name, plugin.marketplace);
|
|
71
|
+
}
|
|
63
72
|
} else {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
);
|
|
70
|
-
return prev;
|
|
71
|
-
});
|
|
73
|
+
actions.installPlugin(
|
|
74
|
+
plugin.name,
|
|
75
|
+
plugin.marketplace,
|
|
76
|
+
SCOPES[selectedScopeIndexRef.current].id,
|
|
77
|
+
);
|
|
72
78
|
}
|
|
73
79
|
}
|
|
74
80
|
});
|
package/src/contexts/useChat.tsx
CHANGED
|
@@ -23,7 +23,6 @@ import {
|
|
|
23
23
|
AgentCallbacks,
|
|
24
24
|
type ToolPermissionContext,
|
|
25
25
|
OPERATION_CANCELLED_BY_USER,
|
|
26
|
-
cloneMessage,
|
|
27
26
|
} from "wave-agent-sdk";
|
|
28
27
|
import { logger } from "../utils/logger.js";
|
|
29
28
|
import { throttle } from "../utils/throttle.js";
|
|
@@ -690,28 +689,21 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
690
689
|
useInput((input, key) => {
|
|
691
690
|
if (key.ctrl && input === "o") {
|
|
692
691
|
// Clear terminal screen when expanded state changes
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
});
|
|
707
|
-
} else {
|
|
708
|
-
// Transitioning to COLLAPSED: Restore from agent's actual state
|
|
709
|
-
if (agentRef.current) {
|
|
710
|
-
setMessages([...agentRef.current.messages]);
|
|
711
|
-
}
|
|
692
|
+
// Use ref to get the current value to avoid stale closure
|
|
693
|
+
const nextExpanded = !isExpandedRef.current;
|
|
694
|
+
setIsExpanded(nextExpanded);
|
|
695
|
+
isExpandedRef.current = nextExpanded;
|
|
696
|
+
|
|
697
|
+
if (nextExpanded) {
|
|
698
|
+
// Transitioning to EXPANDED: Freeze the current view
|
|
699
|
+
// Cancel any pending throttled updates to avoid overwriting the frozen state
|
|
700
|
+
throttledSetMessages.cancel();
|
|
701
|
+
} else {
|
|
702
|
+
// Transitioning to COLLAPSED: Restore from agent's actual state
|
|
703
|
+
if (agentRef.current) {
|
|
704
|
+
setMessages([...agentRef.current.messages]);
|
|
712
705
|
}
|
|
713
|
-
|
|
714
|
-
});
|
|
706
|
+
}
|
|
715
707
|
// Force remount to re-render Static items with new isExpanded state
|
|
716
708
|
requestRemount();
|
|
717
709
|
}
|