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.
@@ -1 +1 @@
1
- {"version":3,"file":"MarketplaceAddForm.d.ts","sourceRoot":"","sources":["../../src/components/MarketplaceAddForm.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAIxC,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EA+CtC,CAAC"}
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
- setSource((prev) => {
17
- if (prev.trim()) {
18
- actions.addMarketplace(prev.trim());
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,KAAmB,MAAM,OAAO,CAAC;AAExC,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,CA0ThD,CAAC"}
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
- setViewMode((prevMode) => {
49
- if (prevMode === "list") {
50
- setSelectedIndex((prevIndex) => {
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
- setViewMode((prev) => {
65
- if (prev === "detail") {
66
- return "list";
67
- }
63
+ if (viewModeRef.current === "detail") {
64
+ setViewMode("list");
65
+ }
66
+ else {
68
67
  onCancel();
69
- return prev;
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
- setSelectedIndex((prev) => {
84
- const server = servers[prev];
85
- if (server &&
86
- (server.status === "disconnected" || server.status === "error")) {
87
- handleConnect(server.name);
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
- setSelectedIndex((prev) => {
95
- const server = servers[prev];
96
- if (server && server.status === "connected") {
97
- handleDisconnect(server.name);
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,6CAiHpB,CAAC"}
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,KAAmB,MAAM,OAAO,CAAC;AAUxC,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EA+JhC,CAAC"}
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
- setSelectedActionIndex((prev) => {
37
- const action = INSTALLED_ACTIONS[prev].id;
38
- if (action === "uninstall") {
39
- actions.uninstallPlugin(plugin.name, plugin.marketplace);
40
- }
41
- else {
42
- actions.updatePlugin(plugin.name, plugin.marketplace);
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
- setSelectedScopeIndex((prev) => {
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":"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"}
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;AAYxB,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IAEvB,UAAU,EAAE,OAAO,CAAC;IACpB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,oBAAoB,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACjD,cAAc,EAAE,KAAK,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACnD,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACtC,CAAC,CAAC;IAEH,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,CACX,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,EAClD,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KACjC,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9C,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAE1B,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,mBAAmB,EAAE,MAAM,MAAM,EAAE,CAAC;IACpC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAElC,UAAU,EAAE,eAAe,EAAE,CAAC;IAC9B,gBAAgB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3D,mBAAmB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAE9D,eAAe,EAAE,cAAc,EAAE,CAAC;IAElC,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,uBAAuB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK;QAC3C,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,GAAG,IAAI,CAAC;IACT,kBAAkB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;IAEhD,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,eAAe,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;IAEhD,cAAc,EAAE,cAAc,CAAC;IAC/B,iBAAiB,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IAClD,kBAAkB,EAAE,OAAO,CAAC;IAE5B,qBAAqB,EAAE,OAAO,CAAC;IAC/B,uBAAuB,EAAE,OAAO,CAAC;IACjC,cAAc,CAAC,EAAE;QACf,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAChC,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,oBAAoB,CAAC,EAAE,OAAO,CAAC;QAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,gBAAgB,EAAE,CAChB,QAAQ,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,eAAe,CAAC,EAAE,MAAM,EACxB,oBAAoB,CAAC,EAAE,OAAO,EAC9B,WAAW,CAAC,EAAE,MAAM,KACjB,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACjC,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,0BAA0B,EAAE,CAAC,QAAQ,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACnE,wBAAwB,EAAE,MAAM,IAAI,CAAC;IAErC,qBAAqB,EAAE,MAAM,IAAI,CAAC;IAElC,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,IAAI,CAAC;IAE3B,kBAAkB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,oBAAoB,EAAE,MAAM,OAAO,CAAC;QAClC,QAAQ,EAAE,OAAO,EAAE,CAAC;QACpB,UAAU,EAAE,MAAM,EAAE,CAAC;KACtB,CAAC,CAAC;IAEH,gBAAgB,EAAE,MAAM,OAAO,gBAAgB,EAAE,aAAa,CAAC;IAC/D,cAAc,EAAE,MAAM,OAAO,gBAAgB,EAAE,WAAW,CAAC;IAC3D,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAID,eAAO,MAAM,OAAO,uBAMnB,CAAC;AAEF,MAAM,WAAW,iBAAkB,SAAQ,YAAY;IACrD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CA6nBpD,CAAC"}
@@ -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, cloneMessage, } from "wave-agent-sdk";
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
- setIsExpanded((prev) => {
424
- const newExpanded = !prev;
425
- isExpandedRef.current = newExpanded;
426
- if (newExpanded) {
427
- // Transitioning to EXPANDED: Freeze the current view
428
- // Cancel any pending throttled updates to avoid overwriting the frozen state
429
- throttledSetMessages.cancel();
430
- // Deep copy the last message to ensure it doesn't update if the agent is still writing to it
431
- setMessages((currentMessages) => {
432
- if (currentMessages.length === 0)
433
- return currentMessages;
434
- const lastMessage = currentMessages[currentMessages.length - 1];
435
- const frozenLastMessage = cloneMessage(lastMessage);
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
- return newExpanded;
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.7",
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.7"
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
- setSource((prev) => {
16
- if (prev.trim()) {
17
- actions.addMarketplace(prev.trim());
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
- setViewMode((prevMode) => {
70
- if (prevMode === "list") {
71
- setSelectedIndex((prevIndex) => {
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
- setViewMode((prev) => {
87
- if (prev === "detail") {
88
- return "list";
89
- }
88
+ if (viewModeRef.current === "detail") {
89
+ setViewMode("list");
90
+ } else {
90
91
  onCancel();
91
- return prev;
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
- setSelectedIndex((prev) => {
109
- const server = servers[prev];
110
- if (
111
- server &&
112
- (server.status === "disconnected" || server.status === "error")
113
- ) {
114
- handleConnect(server.name);
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
- setSelectedIndex((prev) => {
123
- const server = servers[prev];
124
- if (server && server.status === "connected") {
125
- handleDisconnect(server.name);
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
  });
@@ -65,6 +65,7 @@ export const MessageList = React.memo(
65
65
  const { block } = item;
66
66
  const isDynamic =
67
67
  !forceStatic &&
68
+ !isExpanded &&
68
69
  (messagesWithRunningBlocks.has(item.messageIndex) || isRunning(block));
69
70
  return { ...item, isDynamic };
70
71
  });
@@ -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
- setSelectedActionIndex((prev) => {
55
- const action = INSTALLED_ACTIONS[prev].id;
56
- if (action === "uninstall") {
57
- actions.uninstallPlugin(plugin.name, plugin.marketplace);
58
- } else {
59
- actions.updatePlugin(plugin.name, plugin.marketplace);
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
- setSelectedScopeIndex((prev) => {
65
- actions.installPlugin(
66
- plugin.name,
67
- plugin.marketplace,
68
- SCOPES[prev].id,
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
  });
@@ -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
- setIsExpanded((prev) => {
694
- const newExpanded = !prev;
695
- isExpandedRef.current = newExpanded;
696
- if (newExpanded) {
697
- // Transitioning to EXPANDED: Freeze the current view
698
- // Cancel any pending throttled updates to avoid overwriting the frozen state
699
- throttledSetMessages.cancel();
700
- // Deep copy the last message to ensure it doesn't update if the agent is still writing to it
701
- setMessages((currentMessages) => {
702
- if (currentMessages.length === 0) return currentMessages;
703
- const lastMessage = currentMessages[currentMessages.length - 1];
704
- const frozenLastMessage = cloneMessage(lastMessage);
705
- return [...currentMessages.slice(0, -1), frozenLastMessage];
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
- return newExpanded;
714
- });
706
+ }
715
707
  // Force remount to re-render Static items with new isExpanded state
716
708
  requestRemount();
717
709
  }