wave-code 0.0.4 → 0.0.6

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.
Files changed (102) hide show
  1. package/README.md +2 -2
  2. package/dist/components/ChatInterface.d.ts.map +1 -1
  3. package/dist/components/ChatInterface.js +4 -24
  4. package/dist/components/CommandSelector.js +4 -4
  5. package/dist/components/DiffViewer.d.ts +1 -1
  6. package/dist/components/DiffViewer.d.ts.map +1 -1
  7. package/dist/components/DiffViewer.js +15 -15
  8. package/dist/components/FileSelector.js +2 -2
  9. package/dist/components/InputBox.d.ts.map +1 -1
  10. package/dist/components/InputBox.js +46 -101
  11. package/dist/components/Markdown.d.ts +6 -0
  12. package/dist/components/Markdown.d.ts.map +1 -0
  13. package/dist/components/Markdown.js +22 -0
  14. package/dist/components/MessageItem.d.ts +9 -0
  15. package/dist/components/MessageItem.d.ts.map +1 -0
  16. package/dist/components/MessageItem.js +15 -0
  17. package/dist/components/MessageList.d.ts +1 -1
  18. package/dist/components/MessageList.d.ts.map +1 -1
  19. package/dist/components/MessageList.js +33 -32
  20. package/dist/components/SubagentBlock.d.ts +1 -2
  21. package/dist/components/SubagentBlock.d.ts.map +1 -1
  22. package/dist/components/SubagentBlock.js +29 -20
  23. package/dist/components/ToolResultDisplay.js +5 -5
  24. package/dist/contexts/useChat.d.ts +1 -0
  25. package/dist/contexts/useChat.d.ts.map +1 -1
  26. package/dist/contexts/useChat.js +29 -2
  27. package/dist/hooks/useInputManager.d.ts +93 -0
  28. package/dist/hooks/useInputManager.d.ts.map +1 -0
  29. package/dist/hooks/useInputManager.js +332 -0
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +17 -10
  32. package/dist/managers/InputManager.d.ts +171 -0
  33. package/dist/managers/InputManager.d.ts.map +1 -0
  34. package/dist/managers/InputManager.js +826 -0
  35. package/dist/print-cli.d.ts +8 -0
  36. package/dist/print-cli.d.ts.map +1 -0
  37. package/dist/print-cli.js +128 -0
  38. package/dist/utils/constants.d.ts +1 -1
  39. package/dist/utils/constants.js +1 -1
  40. package/dist/utils/fileSearch.d.ts +20 -0
  41. package/dist/utils/fileSearch.d.ts.map +1 -0
  42. package/dist/utils/fileSearch.js +102 -0
  43. package/dist/utils/logger.js +3 -3
  44. package/dist/utils/usageSummary.d.ts +33 -0
  45. package/dist/utils/usageSummary.d.ts.map +1 -0
  46. package/dist/utils/usageSummary.js +154 -0
  47. package/package.json +10 -6
  48. package/src/components/ChatInterface.tsx +13 -43
  49. package/src/components/CommandSelector.tsx +5 -5
  50. package/src/components/DiffViewer.tsx +18 -16
  51. package/src/components/FileSelector.tsx +2 -2
  52. package/src/components/InputBox.tsx +78 -169
  53. package/src/components/Markdown.tsx +29 -0
  54. package/src/components/MessageItem.tsx +104 -0
  55. package/src/components/MessageList.tsx +142 -198
  56. package/src/components/SubagentBlock.tsx +56 -73
  57. package/src/components/ToolResultDisplay.tsx +6 -6
  58. package/src/contexts/useChat.tsx +34 -2
  59. package/src/hooks/useInputManager.ts +461 -0
  60. package/src/index.ts +20 -10
  61. package/src/managers/InputManager.ts +1132 -0
  62. package/src/print-cli.ts +160 -0
  63. package/src/utils/constants.ts +1 -1
  64. package/src/utils/fileSearch.ts +133 -0
  65. package/src/utils/logger.ts +3 -3
  66. package/src/utils/usageSummary.ts +234 -0
  67. package/dist/hooks/useBashHistorySelector.d.ts +0 -15
  68. package/dist/hooks/useBashHistorySelector.d.ts.map +0 -1
  69. package/dist/hooks/useBashHistorySelector.js +0 -61
  70. package/dist/hooks/useCommandSelector.d.ts +0 -24
  71. package/dist/hooks/useCommandSelector.d.ts.map +0 -1
  72. package/dist/hooks/useCommandSelector.js +0 -98
  73. package/dist/hooks/useFileSelector.d.ts +0 -16
  74. package/dist/hooks/useFileSelector.d.ts.map +0 -1
  75. package/dist/hooks/useFileSelector.js +0 -174
  76. package/dist/hooks/useImageManager.d.ts +0 -13
  77. package/dist/hooks/useImageManager.d.ts.map +0 -1
  78. package/dist/hooks/useImageManager.js +0 -46
  79. package/dist/hooks/useInputHistory.d.ts +0 -11
  80. package/dist/hooks/useInputHistory.d.ts.map +0 -1
  81. package/dist/hooks/useInputHistory.js +0 -64
  82. package/dist/hooks/useInputKeyboardHandler.d.ts +0 -83
  83. package/dist/hooks/useInputKeyboardHandler.d.ts.map +0 -1
  84. package/dist/hooks/useInputKeyboardHandler.js +0 -507
  85. package/dist/hooks/useInputState.d.ts +0 -14
  86. package/dist/hooks/useInputState.d.ts.map +0 -1
  87. package/dist/hooks/useInputState.js +0 -57
  88. package/dist/hooks/useMemoryTypeSelector.d.ts +0 -9
  89. package/dist/hooks/useMemoryTypeSelector.d.ts.map +0 -1
  90. package/dist/hooks/useMemoryTypeSelector.js +0 -27
  91. package/dist/plain-cli.d.ts +0 -7
  92. package/dist/plain-cli.d.ts.map +0 -1
  93. package/dist/plain-cli.js +0 -44
  94. package/src/hooks/useBashHistorySelector.ts +0 -77
  95. package/src/hooks/useCommandSelector.ts +0 -131
  96. package/src/hooks/useFileSelector.ts +0 -227
  97. package/src/hooks/useImageManager.ts +0 -64
  98. package/src/hooks/useInputHistory.ts +0 -74
  99. package/src/hooks/useInputKeyboardHandler.ts +0 -778
  100. package/src/hooks/useInputState.ts +0 -66
  101. package/src/hooks/useMemoryTypeSelector.ts +0 -40
  102. package/src/plain-cli.ts +0 -60
package/README.md CHANGED
@@ -51,8 +51,8 @@ export LOG_FILE="/path/to/your/logfile.log"
51
51
  # Maximum log file size (optional, defaults to 10MB)
52
52
  export LOG_MAX_FILE_SIZE="10485760"
53
53
 
54
- # Token limit (optional, defaults to 64000)
55
- export TOKEN_LIMIT="64000"
54
+ # Token limit (optional, defaults to 96000)
55
+ export TOKEN_LIMIT="96000"
56
56
 
57
57
  ```
58
58
 
@@ -1 +1 @@
1
- {"version":3,"file":"ChatInterface.d.ts","sourceRoot":"","sources":["../../src/components/ChatInterface.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4B,MAAM,OAAO,CAAC;AAOjD,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAgFjC,CAAC"}
1
+ {"version":3,"file":"ChatInterface.d.ts","sourceRoot":"","sources":["../../src/components/ChatInterface.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAM1B,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAmDjC,CAAC"}
@@ -1,31 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useRef, useEffect } from "react";
3
2
  import { Box } from "ink";
4
3
  import { MessageList } from "./MessageList.js";
5
4
  import { InputBox } from "./InputBox.js";
6
5
  import { useChat } from "../contexts/useChat.js";
7
6
  export const ChatInterface = () => {
8
- const { messages, isLoading, isCommandRunning, userInputHistory, isCompressing, sendMessage, abortMessage, saveMemory, mcpServers, connectMcpServer, disconnectMcpServer, isExpanded, latestTotalTokens, slashCommands, hasSlashCommand, } = useChat();
9
- // Create a ref to store messages in expanded mode
10
- const expandedMessagesRef = useRef([]);
11
- useEffect(() => {
12
- // Only sync when collapsed
13
- if (!isExpanded) {
14
- expandedMessagesRef.current = messages.map((message, index) => {
15
- // If it's the last message, deep copy its blocks
16
- if (index === messages.length - 1) {
17
- return {
18
- ...message,
19
- blocks: message.blocks.map((block) => ({ ...block })),
20
- };
21
- }
22
- return message;
23
- });
24
- }
25
- }, [isExpanded, messages]);
26
- return (_jsxs(Box, { flexDirection: "column", height: "100%", children: [_jsx(Box, { flexGrow: 1, flexDirection: "column", paddingX: 1, children: isExpanded ? (
27
- // Expanded mode uses messages from ref, loading and tokens are hardcoded to false and 0
28
- _jsx(MessageList, { messages: expandedMessagesRef.current, isLoading: false, isCommandRunning: false, latestTotalTokens: 0, isExpanded: true })) : (
29
- // Normal mode uses real-time state
30
- _jsx(MessageList, { messages: messages, isLoading: isLoading, isCommandRunning: isCommandRunning, isCompressing: isCompressing, latestTotalTokens: latestTotalTokens, isExpanded: false })) }), !isExpanded && (_jsx(InputBox, { isLoading: isLoading, isCommandRunning: isCommandRunning, userInputHistory: userInputHistory, sendMessage: sendMessage, abortMessage: abortMessage, saveMemory: saveMemory, mcpServers: mcpServers, connectMcpServer: connectMcpServer, disconnectMcpServer: disconnectMcpServer, slashCommands: slashCommands, hasSlashCommand: hasSlashCommand }))] }));
7
+ const { messages, isLoading, isCommandRunning, userInputHistory, isCompressing, sendMessage, abortMessage, saveMemory, mcpServers, connectMcpServer, disconnectMcpServer, isExpanded, sessionId, latestTotalTokens, slashCommands, hasSlashCommand, } = useChat();
8
+ if (!sessionId)
9
+ return null;
10
+ return (_jsxs(Box, { flexDirection: "column", height: "100%", paddingY: 1, children: [_jsx(MessageList, { messages: messages, isLoading: isLoading, isCommandRunning: isCommandRunning, isCompressing: isCompressing, latestTotalTokens: latestTotalTokens, isExpanded: isExpanded }, String(isExpanded) + sessionId), !isExpanded && (_jsx(InputBox, { isLoading: isLoading, isCommandRunning: isCommandRunning, userInputHistory: userInputHistory, sendMessage: sendMessage, abortMessage: abortMessage, saveMemory: saveMemory, mcpServers: mcpServers, connectMcpServer: connectMcpServer, disconnectMcpServer: disconnectMcpServer, slashCommands: slashCommands, hasSlashCommand: hasSlashCommand }))] }));
31
11
  };
@@ -22,12 +22,12 @@ export const CommandSelector = ({ searchQuery, onSelect, onInsert, onCancel, com
22
22
  const allCommands = [...commands, ...AVAILABLE_COMMANDS];
23
23
  // Filter command list
24
24
  const filteredCommands = allCommands.filter((command) => !searchQuery ||
25
- command.name.toLowerCase().includes(searchQuery.toLowerCase()));
25
+ command.id.toLowerCase().includes(searchQuery.toLowerCase()));
26
26
  useInput((input, key) => {
27
27
  if (key.return) {
28
28
  if (filteredCommands.length > 0 &&
29
29
  selectedIndex < filteredCommands.length) {
30
- const selectedCommand = filteredCommands[selectedIndex].name;
30
+ const selectedCommand = filteredCommands[selectedIndex].id;
31
31
  onSelect(selectedCommand);
32
32
  }
33
33
  return;
@@ -35,7 +35,7 @@ export const CommandSelector = ({ searchQuery, onSelect, onInsert, onCancel, com
35
35
  if (key.tab && onInsert) {
36
36
  if (filteredCommands.length > 0 &&
37
37
  selectedIndex < filteredCommands.length) {
38
- const selectedCommand = filteredCommands[selectedIndex].name;
38
+ const selectedCommand = filteredCommands[selectedIndex].id;
39
39
  onInsert(selectedCommand);
40
40
  }
41
41
  return;
@@ -56,5 +56,5 @@ export const CommandSelector = ({ searchQuery, onSelect, onInsert, onCancel, com
56
56
  if (filteredCommands.length === 0) {
57
57
  return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "yellow", padding: 1, marginBottom: 1, children: [_jsxs(Text, { color: "yellow", children: ["No commands found for \"", searchQuery, "\""] }), _jsx(Text, { dimColor: true, children: "Press Escape to cancel" })] }));
58
58
  }
59
- return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "magenta", padding: 1, gap: 1, marginBottom: 1, children: [_jsx(Box, { children: _jsxs(Text, { color: "magenta", bold: true, children: ["Command Selector ", searchQuery && `(filtering: "${searchQuery}")`] }) }), filteredCommands.map((command, index) => (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: index === selectedIndex ? "black" : "white", backgroundColor: index === selectedIndex ? "magenta" : undefined, children: [index === selectedIndex ? "▶ " : " ", "/", command.name] }), index === selectedIndex && (_jsx(Box, { marginLeft: 4, children: _jsx(Text, { color: "gray", dimColor: true, children: command.description }) }))] }, command.name))), _jsx(Box, { children: _jsxs(Text, { dimColor: true, children: ["\u2191\u2193 navigate \u2022 Enter execute \u2022 ", onInsert ? "Tab insert • " : "", "Esc cancel"] }) })] }));
59
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "magenta", padding: 1, gap: 1, marginBottom: 1, children: [_jsx(Box, { children: _jsxs(Text, { color: "magenta", bold: true, children: ["Command Selector ", searchQuery && `(filtering: "${searchQuery}")`] }) }), filteredCommands.map((command, index) => (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: index === selectedIndex ? "black" : "white", backgroundColor: index === selectedIndex ? "magenta" : undefined, children: [index === selectedIndex ? "▶ " : " ", "/", command.id] }), index === selectedIndex && (_jsx(Box, { marginLeft: 4, children: _jsx(Text, { color: "gray", dimColor: true, children: command.description }) }))] }, command.id))), _jsx(Box, { children: _jsxs(Text, { dimColor: true, children: ["\u2191\u2193 navigate \u2022 Enter execute \u2022 ", onInsert ? "Tab insert • " : "", "Esc cancel"] }) })] }));
60
60
  };
@@ -2,7 +2,7 @@ import React from "react";
2
2
  import type { DiffBlock } from "wave-agent-sdk";
3
3
  interface DiffViewerProps {
4
4
  block: DiffBlock;
5
- isExpanded?: boolean;
5
+ isStatic?: boolean;
6
6
  }
7
7
  export declare const DiffViewer: React.FC<DiffViewerProps>;
8
8
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"DiffViewer.d.ts","sourceRoot":"","sources":["../../src/components/DiffViewer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkB,MAAM,OAAO,CAAC;AAGvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,UAAU,eAAe;IACvB,KAAK,EAAE,SAAS,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAwCD,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAgRhD,CAAC"}
1
+ {"version":3,"file":"DiffViewer.d.ts","sourceRoot":"","sources":["../../src/components/DiffViewer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkB,MAAM,OAAO,CAAC;AAGvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,UAAU,eAAe;IACvB,KAAK,EAAE,SAAS,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAwCD,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAkRhD,CAAC"}
@@ -22,7 +22,7 @@ const renderWordLevelDiff = (removedLine, addedLine) => {
22
22
  });
23
23
  return { removedParts, addedParts };
24
24
  };
25
- export const DiffViewer = ({ block, isExpanded = false, }) => {
25
+ export const DiffViewer = ({ block, isStatic = true, }) => {
26
26
  const { diffResult } = block;
27
27
  const diffLines = useMemo(() => {
28
28
  if (!diffResult)
@@ -183,24 +183,24 @@ export const DiffViewer = ({ block, isExpanded = false, }) => {
183
183
  });
184
184
  // Handle remaining deleted lines at the end
185
185
  flushPendingLines();
186
- // Only limit displayed lines in collapsed state
187
- if (!isExpanded) {
188
- const MAX_DISPLAY_LINES = 50;
189
- if (lines.length > MAX_DISPLAY_LINES) {
190
- const truncatedLines = lines.slice(0, MAX_DISPLAY_LINES);
191
- truncatedLines.push({
192
- content: `... (${lines.length - MAX_DISPLAY_LINES} more lines truncated, press Ctrl+O to expand)`,
193
- type: "separator",
194
- });
195
- return truncatedLines;
196
- }
197
- }
198
186
  return lines;
199
- }, [diffResult, isExpanded]);
187
+ }, [diffResult]);
188
+ // Truncate to last 10 lines for non-static items
189
+ const displayLines = useMemo(() => {
190
+ if (isStatic) {
191
+ return diffLines;
192
+ }
193
+ const MAX_LINES = 10;
194
+ if (diffLines.length <= MAX_LINES) {
195
+ return diffLines;
196
+ }
197
+ return diffLines.slice(-MAX_LINES);
198
+ }, [diffLines, isStatic]);
200
199
  if (!diffResult || diffResult.length === 0) {
201
200
  return (_jsx(Box, { flexDirection: "column", children: _jsx(Text, { color: "gray", children: "No changes detected" }) }));
202
201
  }
203
- return (_jsx(Box, { flexDirection: "column", children: _jsx(Box, { flexDirection: "column", children: _jsx(Box, { flexDirection: "column", children: diffLines.map((line, index) => {
202
+ // Show traditional diff view
203
+ return (_jsx(Box, { flexDirection: "column", children: _jsx(Box, { flexDirection: "column", children: _jsx(Box, { flexDirection: "column", children: displayLines.map((line, index) => {
204
204
  // If has word-level diff, render special effects
205
205
  if (line.wordDiff) {
206
206
  const prefix = line.type === "removed" ? "- " : "+ ";
@@ -4,7 +4,7 @@ import { Box, Text, useInput } from "ink";
4
4
  export const FileSelector = ({ files, searchQuery, onSelect, onCancel, }) => {
5
5
  const [selectedIndex, setSelectedIndex] = useState(0);
6
6
  useInput((input, key) => {
7
- if (key.return) {
7
+ if (key.return || key.tab) {
8
8
  if (files.length > 0 && selectedIndex < files.length) {
9
9
  onSelect(files[selectedIndex].path);
10
10
  }
@@ -44,5 +44,5 @@ export const FileSelector = ({ files, searchQuery, onSelect, onCancel, }) => {
44
44
  const isSelected = actualIndex === selectedIndex;
45
45
  const icon = fileItem.type === "directory" ? "📁" : "📄";
46
46
  return (_jsx(Box, { children: _jsxs(Text, { color: isSelected ? "black" : "white", backgroundColor: isSelected ? "cyan" : undefined, children: [" ", icon, " ", fileItem.path] }) }, fileItem.path));
47
- }), endIndex < files.length && (_jsxs(Text, { dimColor: true, children: ["... ", files.length - endIndex, " more files below"] })), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { dimColor: true, children: "Use \u2191\u2193 to navigate, Enter to select, Escape to cancel" }), _jsxs(Text, { dimColor: true, children: ["File ", selectedIndex + 1, " of ", files.length] })] })] }));
47
+ }), endIndex < files.length && (_jsxs(Text, { dimColor: true, children: ["... ", files.length - endIndex, " more files below"] })), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { dimColor: true, children: "Use \u2191\u2193 to navigate, Enter/Tab to select, Escape to cancel" }), _jsxs(Text, { dimColor: true, children: ["File ", selectedIndex + 1, " of ", files.length] })] })] }));
48
48
  };
@@ -1 +1 @@
1
- {"version":3,"file":"InputBox.d.ts","sourceRoot":"","sources":["../../src/components/InputBox.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAgC,MAAM,OAAO,CAAC;AAgBrD,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEpE,eAAO,MAAM,sBAAsB,yGACqE,CAAC;AAEzG,eAAO,MAAM,6BAA6B,QAGzC,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,WAAW,CAAC,EAAE,CACZ,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,KAC/C,IAAI,CAAC;IACV,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAE1E,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;IAC/B,gBAAgB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5D,mBAAmB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAE/D,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;IAC/B,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;CAClD;AAED,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CAuQ5C,CAAC"}
1
+ {"version":3,"file":"InputBox.d.ts","sourceRoot":"","sources":["../../src/components/InputBox.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA6B,MAAM,OAAO,CAAC;AAWlD,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEpE,eAAO,MAAM,sBAAsB,yGACqE,CAAC;AAEzG,eAAO,MAAM,6BAA6B,QAGzC,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,WAAW,CAAC,EAAE,CACZ,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,KAC/C,IAAI,CAAC;IACV,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAE1E,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;IAC/B,gBAAgB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5D,mBAAmB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAE/D,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;IAC/B,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;CAClD;AAED,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CAiL5C,CAAC"}
@@ -1,118 +1,59 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState, useCallback } from "react";
2
+ import { useEffect, useMemo } from "react";
3
3
  import { Box, Text } from "ink";
4
+ import { useInput } from "ink";
4
5
  import { FileSelector } from "./FileSelector.js";
5
6
  import { CommandSelector } from "./CommandSelector.js";
6
7
  import { BashHistorySelector } from "./BashHistorySelector.js";
7
8
  import { MemoryTypeSelector } from "./MemoryTypeSelector.js";
8
9
  import { BashShellManager } from "./BashShellManager.js";
9
10
  import { McpManager } from "./McpManager.js";
10
- import { useInputState } from "../hooks/useInputState.js";
11
- import { useFileSelector } from "../hooks/useFileSelector.js";
12
- import { useCommandSelector } from "../hooks/useCommandSelector.js";
13
- import { useBashHistorySelector } from "../hooks/useBashHistorySelector.js";
14
- import { useMemoryTypeSelector } from "../hooks/useMemoryTypeSelector.js";
15
- import { useInputHistory } from "../hooks/useInputHistory.js";
16
- import { useInputKeyboardHandler } from "../hooks/useInputKeyboardHandler.js";
17
- import { useImageManager } from "../hooks/useImageManager.js";
11
+ import { useInputManager } from "../hooks/useInputManager.js";
18
12
  export const INPUT_PLACEHOLDER_TEXT = "Type your message (use @ to reference files, / for commands, ! for bash history, # to add memory)...";
19
13
  export const INPUT_PLACEHOLDER_TEXT_PREFIX = INPUT_PLACEHOLDER_TEXT.substring(0, 10);
20
14
  export const InputBox = ({ isLoading = false, isCommandRunning = false, workdir, userInputHistory = [], sendMessage = () => { }, abortMessage = () => { }, saveMemory = async () => { }, mcpServers = [], connectMcpServer = async () => false, disconnectMcpServer = async () => false, slashCommands = [], hasSlashCommand = () => false, }) => {
21
- // Get current working directory
22
- const currentWorkdir = workdir || process.cwd();
23
- // Bash shell manager state
24
- const [showBashManager, setShowBashManager] = useState(false);
25
- // MCP manager state
26
- const [showMcpManager, setShowMcpManager] = useState(false);
27
- // Basic input state
28
- const { inputText, setInputText, cursorPosition, setCursorPosition, insertTextAtCursor, deleteCharAtCursor, clearInput, moveCursorLeft, moveCursorRight, moveCursorToStart, moveCursorToEnd, } = useInputState();
29
- // File selector functionality
30
- const { showFileSelector, filteredFiles, searchQuery, activateFileSelector, handleFileSelect: handleFileSelectorSelect, handleCancelFileSelect, updateSearchQuery, checkForAtDeletion, atPosition, } = useFileSelector();
31
- // Command selector functionality
32
- const { showCommandSelector, commandSearchQuery, activateCommandSelector, handleCommandSelect: handleCommandSelectorSelect, handleCommandInsert: handleCommandSelectorInsert, handleCancelCommandSelect, updateCommandSearchQuery, checkForSlashDeletion, slashPosition, } = useCommandSelector({
33
- onShowBashManager: () => setShowBashManager(true),
34
- onShowMcpManager: () => setShowMcpManager(true),
35
- sendMessage: async (content) => {
36
- await sendMessage(content);
37
- },
38
- hasSlashCommand,
15
+ // Get current working directory - memoized to avoid repeated process.cwd() calls
16
+ const currentWorkdir = useMemo(() => workdir || process.cwd(), [workdir]);
17
+ // Input manager with all input state and functionality (including images)
18
+ const { inputText, cursorPosition,
19
+ // Image management
20
+ attachedImages, clearImages,
21
+ // File selector
22
+ showFileSelector, filteredFiles, fileSearchQuery: searchQuery, handleFileSelect, handleCancelFileSelect,
23
+ // Command selector
24
+ showCommandSelector, commandSearchQuery, handleCommandSelect, handleCommandInsert, handleCancelCommandSelect,
25
+ // Bash history selector
26
+ showBashHistorySelector, bashHistorySearchQuery, handleBashHistorySelect, handleCancelBashHistorySelect,
27
+ // Memory type selector
28
+ showMemoryTypeSelector, memoryMessage, handleMemoryTypeSelect, handleCancelMemoryTypeSelect,
29
+ // Bash/MCP Manager
30
+ showBashManager, showMcpManager, setShowBashManager, setShowMcpManager,
31
+ // Input history
32
+ setUserInputHistory,
33
+ // Complex handlers combining multiple operations
34
+ handleBashHistoryExecuteAndSend,
35
+ // Main handler
36
+ handleInput,
37
+ // Manager ready state
38
+ isManagerReady, } = useInputManager({
39
+ onSendMessage: sendMessage,
40
+ onHasSlashCommand: hasSlashCommand,
41
+ onSaveMemory: saveMemory,
42
+ onAbortMessage: abortMessage,
39
43
  });
40
- // Bash history selector functionality
41
- const { showBashHistorySelector, bashHistorySearchQuery, activateBashHistorySelector, handleBashHistorySelect: handleBashHistorySelectorSelect, handleBashHistoryExecute, handleCancelBashHistorySelect, updateBashHistorySearchQuery, checkForExclamationDeletion, exclamationPosition, } = useBashHistorySelector();
42
- // Memory type selector functionality
43
- const { showMemoryTypeSelector, memoryMessage, activateMemoryTypeSelector, handleMemoryTypeSelect: handleMemoryTypeSelectorSelect, handleCancelMemoryTypeSelect, } = useMemoryTypeSelector();
44
- // Input history functionality
45
- const { resetHistoryNavigation, navigateHistory } = useInputHistory({
46
- userInputHistory,
47
- });
48
- // Image management functionality (includes clipboard paste)
49
- const { attachedImages, clearImages, handlePasteImage } = useImageManager(insertTextAtCursor);
50
- // Keyboard handling
51
- const { handleFileSelect, handleCommandSelect, handleBashHistorySelect, handleBashHistoryExecute: keyboardHandleBashHistoryExecute, handleMemoryTypeSelect, } = useInputKeyboardHandler({
52
- inputText,
53
- setInputText,
54
- cursorPosition,
55
- setCursorPosition,
56
- moveCursorLeft,
57
- moveCursorRight,
58
- moveCursorToStart,
59
- moveCursorToEnd,
60
- deleteCharAtCursor,
61
- insertTextAtCursor,
62
- clearInput,
63
- resetHistoryNavigation,
64
- navigateHistory,
65
- handlePasteImage,
66
- attachedImages,
67
- clearImages,
68
- showFileSelector,
69
- activateFileSelector,
70
- handleFileSelect: handleFileSelectorSelect,
71
- handleCancelFileSelect,
72
- updateSearchQuery,
73
- checkForAtDeletion,
74
- atPosition,
75
- showCommandSelector,
76
- activateCommandSelector,
77
- handleCommandSelect: handleCommandSelectorSelect,
78
- handleCommandInsert: handleCommandSelectorInsert,
79
- handleCancelCommandSelect,
80
- updateCommandSearchQuery,
81
- checkForSlashDeletion,
82
- slashPosition,
83
- showBashHistorySelector,
84
- activateBashHistorySelector,
85
- handleBashHistorySelect: handleBashHistorySelectorSelect,
86
- handleBashHistoryExecute,
87
- handleCancelBashHistorySelect,
88
- updateBashHistorySearchQuery,
89
- checkForExclamationDeletion,
90
- exclamationPosition,
91
- showMemoryTypeSelector,
92
- activateMemoryTypeSelector,
93
- handleMemoryTypeSelect: handleMemoryTypeSelectorSelect,
94
- showBashManager,
95
- showMcpManager,
96
- isCommandRunning,
97
- isLoading,
98
- sendMessage,
99
- abortMessage,
100
- saveMemory,
44
+ // Set user input history when it changes
45
+ useEffect(() => {
46
+ setUserInputHistory(userInputHistory);
47
+ }, [userInputHistory, setUserInputHistory]);
48
+ // Use the InputManager's unified input handler
49
+ useInput(async (input, key) => {
50
+ await handleInput(input, key, attachedImages, isLoading, isCommandRunning, clearImages);
101
51
  });
52
+ // These methods are already memoized in useInputManager, no need to wrap again
53
+ // These methods are already memoized in useInputManager and combine multiple operations
102
54
  const isPlaceholder = !inputText;
103
55
  const placeholderText = INPUT_PLACEHOLDER_TEXT;
104
- // Create adapter function for CommandSelector
105
- const handleCommandInsert = useCallback((command) => {
106
- const result = handleCommandSelectorInsert(command, inputText, cursorPosition);
107
- setInputText(result.newInput);
108
- setCursorPosition(result.newCursorPosition);
109
- }, [
110
- handleCommandSelectorInsert,
111
- inputText,
112
- cursorPosition,
113
- setInputText,
114
- setCursorPosition,
115
- ]);
56
+ // handleCommandSelectorInsert is already memoized in useInputManager, no need to wrap again
116
57
  // Split text into three parts: before cursor, cursor position, after cursor
117
58
  const displayText = isPlaceholder ? placeholderText : inputText;
118
59
  const beforeCursor = displayText.substring(0, cursorPosition);
@@ -120,5 +61,9 @@ export const InputBox = ({ isLoading = false, isCommandRunning = false, workdir,
120
61
  const afterCursor = displayText.substring(cursorPosition + 1);
121
62
  // Always show cursor, allow user to continue input during loading
122
63
  const shouldShowCursor = true;
123
- return (_jsxs(Box, { flexDirection: "column", width: "100%", children: [showFileSelector && (_jsx(FileSelector, { files: filteredFiles, searchQuery: searchQuery, onSelect: handleFileSelect, onCancel: handleCancelFileSelect })), showCommandSelector && (_jsx(CommandSelector, { searchQuery: commandSearchQuery, onSelect: handleCommandSelect, onInsert: handleCommandInsert, onCancel: handleCancelCommandSelect, commands: slashCommands })), showBashHistorySelector && (_jsx(BashHistorySelector, { searchQuery: bashHistorySearchQuery, workdir: currentWorkdir, onSelect: handleBashHistorySelect, onExecute: keyboardHandleBashHistoryExecute, onCancel: handleCancelBashHistorySelect })), showMemoryTypeSelector && (_jsx(MemoryTypeSelector, { message: memoryMessage, onSelect: handleMemoryTypeSelect, onCancel: handleCancelMemoryTypeSelect })), showBashManager && (_jsx(BashShellManager, { onCancel: () => setShowBashManager(false) })), showMcpManager && (_jsx(McpManager, { onCancel: () => setShowMcpManager(false), servers: mcpServers, onConnectServer: connectMcpServer, onDisconnectServer: disconnectMcpServer })), showBashManager || showMcpManager || (_jsx(Box, { borderStyle: "single", borderColor: "gray", paddingX: 1, children: _jsx(Box, { width: "100%", flexDirection: "row", justifyContent: "space-between", children: _jsx(Text, { color: isPlaceholder ? "gray" : "white", children: shouldShowCursor ? (_jsxs(_Fragment, { children: [beforeCursor, _jsx(Text, { backgroundColor: "white", color: "black", children: atCursor }), afterCursor] })) : (displayText) }) }) }))] }));
64
+ // Only show the Box after InputManager is created on first mount
65
+ if (!isManagerReady) {
66
+ return null;
67
+ }
68
+ return (_jsxs(Box, { flexDirection: "column", children: [showFileSelector && (_jsx(FileSelector, { files: filteredFiles, searchQuery: searchQuery, onSelect: handleFileSelect, onCancel: handleCancelFileSelect })), showCommandSelector && (_jsx(CommandSelector, { searchQuery: commandSearchQuery, onSelect: handleCommandSelect, onInsert: handleCommandInsert, onCancel: handleCancelCommandSelect, commands: slashCommands })), showBashHistorySelector && (_jsx(BashHistorySelector, { searchQuery: bashHistorySearchQuery, workdir: currentWorkdir, onSelect: handleBashHistorySelect, onExecute: handleBashHistoryExecuteAndSend, onCancel: handleCancelBashHistorySelect })), showMemoryTypeSelector && (_jsx(MemoryTypeSelector, { message: memoryMessage, onSelect: handleMemoryTypeSelect, onCancel: handleCancelMemoryTypeSelect })), showBashManager && (_jsx(BashShellManager, { onCancel: () => setShowBashManager(false) })), showMcpManager && (_jsx(McpManager, { onCancel: () => setShowMcpManager(false), servers: mcpServers, onConnectServer: connectMcpServer, onDisconnectServer: disconnectMcpServer })), showBashManager || showMcpManager || (_jsx(Box, { borderStyle: "single", borderColor: "gray", paddingX: 1, children: _jsx(Text, { color: isPlaceholder ? "gray" : "white", children: shouldShowCursor ? (_jsxs(_Fragment, { children: [beforeCursor, _jsx(Text, { backgroundColor: "white", color: "black", children: atCursor }), afterCursor] })) : (displayText) }) }))] }));
124
69
  };
@@ -0,0 +1,6 @@
1
+ import React from "react";
2
+ export interface MarkdownProps {
3
+ children: string;
4
+ }
5
+ export declare const Markdown: React.MemoExoticComponent<({ children }: MarkdownProps) => import("react/jsx-runtime").JSX.Element>;
6
+ //# sourceMappingURL=Markdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Markdown.d.ts","sourceRoot":"","sources":["../../src/components/Markdown.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkB,MAAM,OAAO,CAAC;AAKvC,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAGD,eAAO,MAAM,QAAQ,2CAA6B,aAAa,6CAe7D,CAAC"}
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React, { useMemo } from "react";
3
+ import { Text } from "ink";
4
+ import { marked } from "marked";
5
+ import TerminalRenderer from "marked-terminal";
6
+ // Markdown component using marked-terminal with proper unescape option
7
+ export const Markdown = React.memo(({ children }) => {
8
+ const result = useMemo(() => {
9
+ // Configure marked with TerminalRenderer using default options
10
+ marked.setOptions({
11
+ renderer: new TerminalRenderer({
12
+ // Use official unescape option to handle HTML entities
13
+ unescape: true,
14
+ }),
15
+ });
16
+ const output = marked(children);
17
+ return typeof output === "string" ? output.trim() : "";
18
+ }, [children]);
19
+ return _jsx(Text, { children: result });
20
+ });
21
+ // Add display name for debugging
22
+ Markdown.displayName = "Markdown";
@@ -0,0 +1,9 @@
1
+ import type { Message } from "wave-agent-sdk";
2
+ export interface MessageItemProps {
3
+ message: Message;
4
+ isExpanded: boolean;
5
+ shouldShowHeader: boolean;
6
+ isStatic?: boolean;
7
+ }
8
+ export declare const MessageItem: ({ message, isExpanded, shouldShowHeader, isStatic, }: MessageItemProps) => import("react/jsx-runtime").JSX.Element | null;
9
+ //# sourceMappingURL=MessageItem.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MessageItem.d.ts","sourceRoot":"","sources":["../../src/components/MessageItem.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAU9C,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,eAAO,MAAM,WAAW,GAAI,sDAKzB,gBAAgB,mDA+ElB,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text } from "ink";
3
+ import { MessageSource } from "wave-agent-sdk";
4
+ import { DiffViewer } from "./DiffViewer.js";
5
+ import { CommandOutputDisplay } from "./CommandOutputDisplay.js";
6
+ import { ToolResultDisplay } from "./ToolResultDisplay.js";
7
+ import { MemoryDisplay } from "./MemoryDisplay.js";
8
+ import { CompressDisplay } from "./CompressDisplay.js";
9
+ import { SubagentBlock } from "./SubagentBlock.js";
10
+ import { Markdown } from "./Markdown.js";
11
+ export const MessageItem = ({ message, isExpanded, shouldShowHeader, isStatic = true, }) => {
12
+ if (message.blocks.length === 0)
13
+ return null;
14
+ return (_jsxs(Box, { flexDirection: "column", gap: 1, marginTop: 1, children: [shouldShowHeader && (_jsx(Box, { children: _jsx(Text, { color: message.role === "user" ? "cyan" : "green", bold: true, children: message.role === "user" ? "👤 You" : "🤖 Assistant" }) })), _jsx(Box, { flexDirection: "column", gap: 1, children: message.blocks.map((block, blockIndex) => (_jsxs(Box, { children: [block.type === "text" && block.content.trim() && (_jsxs(Box, { children: [block.customCommandContent && (_jsxs(Text, { color: "cyan", bold: true, children: ["\u26A1", " "] })), block.source === MessageSource.HOOK && (_jsxs(Text, { color: "magenta", bold: true, children: ["\uD83D\uDD17", " "] })), isStatic ? (_jsx(Markdown, { children: block.content })) : (_jsx(Text, { children: block.content.split("\n").slice(-10).join("\n") }))] })), block.type === "error" && (_jsx(Box, { children: _jsxs(Text, { color: "red", children: ["\u274C Error: ", block.content] }) })), block.type === "diff" && (_jsx(DiffViewer, { block: block, isStatic: isStatic })), block.type === "command_output" && (_jsx(CommandOutputDisplay, { block: block, isExpanded: isExpanded })), block.type === "tool" && (_jsx(ToolResultDisplay, { block: block, isExpanded: isExpanded })), block.type === "image" && (_jsxs(Box, { children: [_jsx(Text, { color: "magenta", bold: true, children: "\uD83D\uDCF7 Image" }), block.imageUrls && block.imageUrls.length > 0 && (_jsxs(Text, { color: "gray", dimColor: true, children: [" ", "(", block.imageUrls.length, ")"] }))] })), block.type === "memory" && _jsx(MemoryDisplay, { block: block }), block.type === "compress" && (_jsx(CompressDisplay, { block: block, isExpanded: isExpanded })), block.type === "subagent" && _jsx(SubagentBlock, { block: block })] }, blockIndex))) })] }));
15
+ };
@@ -8,5 +8,5 @@ export interface MessageListProps {
8
8
  latestTotalTokens?: number;
9
9
  isExpanded?: boolean;
10
10
  }
11
- export declare const MessageList: React.FC<MessageListProps>;
11
+ export declare const MessageList: React.MemoExoticComponent<({ messages, isLoading, isCommandRunning, isCompressing, latestTotalTokens, isExpanded, }: MessageListProps) => import("react/jsx-runtime").JSX.Element>;
12
12
  //# sourceMappingURL=MessageList.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../src/components/MessageList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkB,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAuG9C,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAyGlD,CAAC"}
1
+ {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../src/components/MessageList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAG9C,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,eAAO,MAAM,WAAW,uHAQnB,gBAAgB,6CA0IpB,CAAC"}
@@ -1,37 +1,38 @@
1
- import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
- import { useMemo } from "react";
3
- import { Box, Text } from "ink";
4
- import { DiffViewer } from "./DiffViewer.js";
5
- import { CommandOutputDisplay } from "./CommandOutputDisplay.js";
6
- import { ToolResultDisplay } from "./ToolResultDisplay.js";
7
- import { MemoryDisplay } from "./MemoryDisplay.js";
8
- import { CompressDisplay } from "./CompressDisplay.js";
9
- import { SubagentBlock } from "./SubagentBlock.js";
10
- import { usePagination } from "../hooks/usePagination.js";
11
- // Function to render a single message
12
- const renderMessageItem = (message, originalIndex, isExpanded, previousMessage) => {
13
- const shouldShowHeader = previousMessage?.role !== message.role;
14
- return (_jsxs(Box, { flexDirection: "column", children: [shouldShowHeader && (_jsx(Box, { children: _jsxs(Text, { color: message.role === "user" ? "cyan" : "green", bold: true, children: [message.role === "user" ? "👤 You" : "🤖 Assistant", _jsxs(Text, { color: "gray", dimColor: true, children: [" ", "#", originalIndex + 1] })] }) })), _jsx(Box, { marginLeft: 2, flexDirection: "column", gap: 1, marginTop: shouldShowHeader ? 1 : 0, children: message.blocks.map((block, blockIndex) => (_jsxs(Box, { children: [block.type === "text" && block.content.trim() && (_jsx(Box, { children: _jsx(Text, { children: block.content }) })), block.type === "error" && (_jsx(Box, { children: _jsxs(Text, { color: "red", children: ["\u274C Error: ", block.content] }) })), block.type === "diff" && (_jsx(DiffViewer, { block: block, isExpanded: isExpanded })), block.type === "command_output" && (_jsx(CommandOutputDisplay, { block: block, isExpanded: isExpanded })), block.type === "tool" && (_jsx(ToolResultDisplay, { block: block, isExpanded: isExpanded })), block.type === "image" && (_jsxs(Box, { children: [_jsx(Text, { color: "magenta", bold: true, children: "\uD83D\uDCF7 Image" }), block.imageUrls && block.imageUrls.length > 0 && (_jsxs(Text, { color: "gray", dimColor: true, children: [" ", "(", block.imageUrls.length, ")"] }))] })), block.type === "memory" && _jsx(MemoryDisplay, { block: block }), block.type === "compress" && (_jsx(CompressDisplay, { block: block, isExpanded: isExpanded })), block.type === "custom_command" && (_jsxs(Box, { children: [_jsx(Text, { color: "cyan", bold: true, children: "\u26A1" }), _jsx(Text, { children: block.originalInput || `/${block.commandName}` })] })), block.type === "subagent" && (_jsx(SubagentBlock, { block: block, isExpanded: isExpanded }))] }, blockIndex))) })] }, `message-${originalIndex}`));
15
- };
16
- export const MessageList = ({ messages, isLoading = false, isCommandRunning = false, isCompressing = false, latestTotalTokens = 0, isExpanded = false, }) => {
17
- // Use original messages for pagination calculation
18
- const { displayInfo } = usePagination(messages);
19
- // Get current page messages while preserving original index information
20
- const currentMessagesWithIndex = useMemo(() => {
21
- return messages
22
- .slice(displayInfo.startIndex, displayInfo.endIndex)
23
- .map((message, index) => ({
24
- message,
25
- originalIndex: displayInfo.startIndex + index,
26
- }));
27
- }, [messages, displayInfo.startIndex, displayInfo.endIndex]);
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import { Box, Text, Static } from "ink";
4
+ import { MessageItem } from "./MessageItem.js";
5
+ export const MessageList = React.memo(({ messages, isLoading = false, isCommandRunning = false, isCompressing = false, latestTotalTokens = 0, isExpanded = false, }) => {
28
6
  // Empty message state
29
7
  if (messages.length === 0) {
30
8
  return (_jsx(Box, { flexDirection: "column", paddingY: 1, children: _jsx(Text, { color: "gray", children: "Welcome to WAVE Code Assistant!" }) }));
31
9
  }
32
- return (_jsxs(Box, { flexDirection: "column", gap: 1, marginTop: 1, children: [_jsx(Box, { flexDirection: "column", gap: 1, children: currentMessagesWithIndex.map(({ message, originalIndex }) => {
10
+ // Limit messages when expanded to prevent long rendering times
11
+ const maxExpandedMessages = 20;
12
+ const shouldLimitMessages = isExpanded && messages.length > maxExpandedMessages;
13
+ const displayMessages = shouldLimitMessages
14
+ ? messages.slice(-maxExpandedMessages)
15
+ : messages;
16
+ const omittedCount = shouldLimitMessages
17
+ ? messages.length - maxExpandedMessages
18
+ : 0;
19
+ // Compute which messages to render statically vs dynamically
20
+ const shouldRenderLastDynamic = isLoading || isCommandRunning;
21
+ const staticMessages = shouldRenderLastDynamic
22
+ ? displayMessages.slice(0, -1)
23
+ : displayMessages;
24
+ const dynamicMessages = shouldRenderLastDynamic && displayMessages.length > 0
25
+ ? [displayMessages[displayMessages.length - 1]]
26
+ : [];
27
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 1, gap: 1, children: [omittedCount > 0 && (_jsx(Box, { children: _jsxs(Text, { color: "gray", dimColor: true, children: ["... ", omittedCount, " earlier message", omittedCount !== 1 ? "s" : "", " ", "omitted (showing latest ", maxExpandedMessages, ")"] }) })), _jsx(Static, { items: staticMessages, children: (message, key) => {
33
28
  // Get previous message
34
- const previousMessage = originalIndex > 0 ? messages[originalIndex - 1] : undefined;
35
- return renderMessageItem(message, originalIndex, isExpanded, previousMessage);
36
- }) }), !isExpanded && (isLoading || isCommandRunning || isCompressing) && (_jsxs(Box, { flexDirection: "column", gap: 1, children: [isLoading && (_jsxs(Box, { children: [_jsx(Text, { color: "yellow", children: "\uD83D\uDCAD AI is thinking... " }), _jsxs(Text, { color: "gray", dimColor: true, children: [" ", "|", " "] }), _jsx(Text, { color: "blue", bold: true, children: latestTotalTokens.toLocaleString() }), _jsxs(Text, { color: "gray", dimColor: true, children: [" ", "tokens |", " "] }), _jsx(Text, { color: "red", bold: true, children: "Esc" }), _jsxs(Text, { color: "gray", dimColor: true, children: [" ", "to abort"] })] })), isCommandRunning && (_jsx(Text, { color: "blue", children: "\uD83D\uDE80 Command is running..." })), isCompressing && (_jsx(Text, { color: "magenta", children: "\uD83D\uDDDC\uFE0F Compressing message history..." }))] })), messages.length > 0 && (_jsx(Box, { children: _jsxs(Box, { justifyContent: "space-between", width: "100%", children: [_jsxs(Box, { children: [_jsxs(Text, { color: "gray", children: ["Messages ", messages.length, " Page ", displayInfo.currentPage, "/", displayInfo.totalPages] }), _jsxs(Text, { color: "gray", dimColor: true, children: [" ", "\u2190 ", _jsx(Text, { color: "cyan", children: "Ctrl+U/D" }), " Navigate"] })] }), _jsxs(Text, { color: "gray", dimColor: true, children: [_jsx(Text, { color: "cyan", children: "Ctrl+O" }), " Toggle", " ", isExpanded ? "Collapse" : "Expand"] })] }) }))] }));
37
- };
29
+ const previousMessage = key > 0 ? staticMessages[key - 1] : undefined;
30
+ return (_jsx(MessageItem, { message: message, shouldShowHeader: previousMessage?.role !== message.role, isExpanded: isExpanded, isStatic: true }, key));
31
+ } }), dynamicMessages.map((message, index) => {
32
+ const messageIndex = staticMessages.length + index;
33
+ const previousMessage = messageIndex > 0 ? displayMessages[messageIndex - 1] : undefined;
34
+ return (_jsx(Box, { marginTop: -1, children: _jsx(MessageItem, { message: message, shouldShowHeader: previousMessage?.role !== message.role, isExpanded: isExpanded, isStatic: false }) }, `dynamic-${index}`));
35
+ }), (isLoading || isCommandRunning || isCompressing) && (_jsxs(Box, { flexDirection: "column", gap: 1, children: [isLoading && (_jsxs(Box, { children: [_jsx(Text, { color: "yellow", children: "\uD83D\uDCAD AI is thinking... " }), _jsxs(Text, { color: "gray", dimColor: true, children: ["|", " "] }), _jsx(Text, { color: "red", bold: true, children: "Esc" }), _jsxs(Text, { color: "gray", dimColor: true, children: [" ", "to abort"] })] })), isCommandRunning && (_jsx(Text, { color: "blue", children: "\uD83D\uDE80 Command is running..." })), isCompressing && (_jsx(Text, { color: "magenta", children: "\uD83D\uDDDC\uFE0F Compressing message history..." }))] })), messages.length > 0 && (_jsx(Box, { children: _jsxs(Box, { justifyContent: "space-between", width: "100%", children: [_jsx(Box, { children: _jsxs(Text, { color: "gray", children: ["Messages ", messages.length, latestTotalTokens > 0 && (_jsxs(_Fragment, { children: [_jsxs(Text, { color: "gray", dimColor: true, children: [" ", "|", " "] }), _jsx(Text, { color: "blue", bold: true, children: latestTotalTokens.toLocaleString() }), _jsxs(Text, { color: "gray", dimColor: true, children: [" ", "tokens"] })] }))] }) }), _jsxs(Text, { color: "gray", dimColor: true, children: [_jsx(Text, { color: "cyan", children: "Ctrl+O" }), " Toggle", " ", isExpanded ? "Collapse" : "Expand"] })] }) }))] }));
36
+ });
37
+ // Add display name for debugging
38
+ MessageList.displayName = "MessageList";
@@ -1,8 +1,7 @@
1
1
  import React from "react";
2
- import type { SubagentBlock as SubagentBlockType } from "wave-agent-sdk/src/types.js";
2
+ import type { SubagentBlock as SubagentBlockType } from "wave-agent-sdk";
3
3
  interface SubagentBlockProps {
4
4
  block: SubagentBlockType;
5
- isExpanded?: boolean;
6
5
  }
7
6
  export declare const SubagentBlock: React.FC<SubagentBlockProps>;
8
7
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"SubagentBlock.d.ts","sourceRoot":"","sources":["../../src/components/SubagentBlock.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EACV,aAAa,IAAI,iBAAiB,EAGnC,MAAM,6BAA6B,CAAC;AA4BrC,UAAU,kBAAkB;IAC1B,KAAK,EAAE,iBAAiB,CAAC;IACzB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAuFtD,CAAC"}
1
+ {"version":3,"file":"SubagentBlock.d.ts","sourceRoot":"","sources":["../../src/components/SubagentBlock.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,aAAa,IAAI,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAGzE,UAAU,kBAAkB;IAC1B,KAAK,EAAE,iBAAiB,CAAC;CAC1B;AAED,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAoGtD,CAAC"}
@@ -1,19 +1,10 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Box, Text } from "ink";
3
- import { ToolResultDisplay } from "./ToolResultDisplay.js";
4
- const MessageBlockRenderer = ({ block, isExpanded, }) => {
5
- switch (block.type) {
6
- case "text":
7
- return _jsx(Text, { children: block.content });
8
- case "error":
9
- return _jsxs(Text, { color: "red", children: ["\u274C Error: ", block.content] });
10
- case "tool":
11
- return _jsx(ToolResultDisplay, { block: block, isExpanded: isExpanded });
12
- default:
13
- return null;
14
- }
15
- };
16
- export const SubagentBlock = ({ block, isExpanded = false, }) => {
3
+ import { useChat } from "../contexts/useChat.js";
4
+ export const SubagentBlock = ({ block }) => {
5
+ const { subagentMessages } = useChat();
6
+ // Get messages for this subagent from context
7
+ const messages = subagentMessages[block.subagentId] || [];
17
8
  // Status indicator mapping
18
9
  const getStatusIndicator = (status) => {
19
10
  switch (status) {
@@ -30,9 +21,27 @@ export const SubagentBlock = ({ block, isExpanded = false, }) => {
30
21
  }
31
22
  };
32
23
  const statusInfo = getStatusIndicator(block.status);
33
- // Determine how many messages to show
34
- const messagesToShow = isExpanded
35
- ? block.messages.slice(-10) // Up to 10 most recent when expanded
36
- : block.messages.slice(-2); // Up to 2 most recent when collapsed
37
- return (_jsxs(Box, { borderStyle: "round", borderColor: "magenta", paddingX: 1, paddingY: 0, flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { flexDirection: "row", justifyContent: "space-between", alignItems: "center", children: [_jsxs(Box, { flexDirection: "row", alignItems: "center", children: [_jsxs(Text, { color: "cyan", children: ["\uD83E\uDD16 ", block.subagentName] }), _jsxs(Text, { color: statusInfo.color, dimColor: false, children: [" ", statusInfo.icon] })] }), !isExpanded && (_jsxs(Text, { color: "gray", dimColor: true, children: [block.messages.length, " messages"] }))] }), messagesToShow.length > 0 && (_jsx(Box, { flexDirection: "column", marginTop: 1, gap: 1, children: messagesToShow.map((message, index) => (_jsx(Box, { flexDirection: "column", marginBottom: 0, gap: 1, children: message.blocks.map((messageBlock, blockIndex) => (_jsx(Box, { flexDirection: "column", children: _jsx(MessageBlockRenderer, { block: messageBlock, isExpanded: isExpanded }) }, blockIndex))) }, index))) })), !isExpanded && block.messages.length > 2 && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "gray", dimColor: true, children: ["... and ", block.messages.length - 2, " more messages (Ctrl+O to expand)"] }) }))] }));
24
+ // Find the last 2 tool names and their compact params, and count total tools
25
+ const getLastTwoTools = () => {
26
+ const tools = [];
27
+ let totalToolCount = 0;
28
+ for (let i = messages.length - 1; i >= 0; i--) {
29
+ const message = messages[i];
30
+ for (let j = message.blocks.length - 1; j >= 0; j--) {
31
+ const messageBlock = message.blocks[j];
32
+ if (messageBlock.type === "tool" && messageBlock.name) {
33
+ totalToolCount++;
34
+ if (tools.length < 2) {
35
+ tools.push({
36
+ name: messageBlock.name,
37
+ compactParams: messageBlock.compactParams,
38
+ });
39
+ }
40
+ }
41
+ }
42
+ }
43
+ return { tools: tools.reverse(), totalToolCount }; // Reverse to show oldest first, newest last
44
+ };
45
+ const { tools: lastTwoTools, totalToolCount } = getLastTwoTools();
46
+ return (_jsxs(Box, { borderRight: false, borderTop: false, borderBottom: false, borderStyle: "classic", borderColor: "magenta", paddingX: 1, paddingY: 0, flexDirection: "column", marginBottom: 1, children: [_jsx(Box, { flexDirection: "row", gap: 1, children: _jsxs(Box, { flexDirection: "row", alignItems: "center", children: [_jsxs(Text, { color: "cyan", children: ["\uD83E\uDD16 ", block.subagentName] }), _jsxs(Text, { color: statusInfo.color, dimColor: false, children: [" ", statusInfo.icon] }), _jsxs(Text, { color: "gray", dimColor: true, children: [" ", "(", messages.length, " messages)"] })] }) }), lastTwoTools.length > 0 && (_jsxs(Box, { flexDirection: "column", marginTop: 1, gap: 1, children: [totalToolCount > 2 && (_jsx(Text, { color: "gray", dimColor: true, children: "..." })), lastTwoTools.map((tool, index) => (_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { color: "magenta", children: "\uD83D\uDD27 " }), _jsx(Text, { color: "white", children: tool.name }), tool.compactParams && (_jsxs(Text, { color: "gray", children: [" ", tool.compactParams] }))] }, index)))] }))] }));
38
47
  };