wave-code 0.0.5 → 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 (57) 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 +21 -50
  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 -33
  20. package/dist/components/SubagentBlock.d.ts +0 -1
  21. package/dist/components/SubagentBlock.d.ts.map +1 -1
  22. package/dist/components/SubagentBlock.js +29 -30
  23. package/dist/components/ToolResultDisplay.js +5 -5
  24. package/dist/contexts/useChat.d.ts +2 -2
  25. package/dist/contexts/useChat.d.ts.map +1 -1
  26. package/dist/contexts/useChat.js +18 -9
  27. package/dist/hooks/useInputManager.d.ts +3 -1
  28. package/dist/hooks/useInputManager.d.ts.map +1 -1
  29. package/dist/hooks/useInputManager.js +15 -2
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +9 -2
  32. package/dist/managers/InputManager.d.ts +3 -1
  33. package/dist/managers/InputManager.d.ts.map +1 -1
  34. package/dist/managers/InputManager.js +44 -24
  35. package/dist/print-cli.d.ts +1 -0
  36. package/dist/print-cli.d.ts.map +1 -1
  37. package/dist/print-cli.js +88 -23
  38. package/dist/utils/usageSummary.d.ts +6 -0
  39. package/dist/utils/usageSummary.d.ts.map +1 -1
  40. package/dist/utils/usageSummary.js +72 -0
  41. package/package.json +10 -6
  42. package/src/components/ChatInterface.tsx +13 -43
  43. package/src/components/CommandSelector.tsx +5 -5
  44. package/src/components/DiffViewer.tsx +18 -16
  45. package/src/components/FileSelector.tsx +2 -2
  46. package/src/components/InputBox.tsx +22 -74
  47. package/src/components/Markdown.tsx +29 -0
  48. package/src/components/MessageItem.tsx +104 -0
  49. package/src/components/MessageList.tsx +142 -202
  50. package/src/components/SubagentBlock.tsx +56 -84
  51. package/src/components/ToolResultDisplay.tsx +5 -5
  52. package/src/contexts/useChat.tsx +22 -13
  53. package/src/hooks/useInputManager.ts +21 -3
  54. package/src/index.ts +12 -2
  55. package/src/managers/InputManager.ts +55 -25
  56. package/src/print-cli.ts +103 -21
  57. package/src/utils/usageSummary.ts +109 -0
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,KAAiC,MAAM,OAAO,CAAC;AAWtD,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,CAqO5C,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,5 +1,5 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useCallback, useEffect } from "react";
2
+ import { useEffect, useMemo } from "react";
3
3
  import { Box, Text } from "ink";
4
4
  import { useInput } from "ink";
5
5
  import { FileSelector } from "./FileSelector.js";
@@ -12,37 +12,34 @@ import { useInputManager } from "../hooks/useInputManager.js";
12
12
  export const INPUT_PLACEHOLDER_TEXT = "Type your message (use @ to reference files, / for commands, ! for bash history, # to add memory)...";
13
13
  export const INPUT_PLACEHOLDER_TEXT_PREFIX = INPUT_PLACEHOLDER_TEXT.substring(0, 10);
14
14
  export const InputBox = ({ isLoading = false, isCommandRunning = false, workdir, userInputHistory = [], sendMessage = () => { }, abortMessage = () => { }, saveMemory = async () => { }, mcpServers = [], connectMcpServer = async () => false, disconnectMcpServer = async () => false, slashCommands = [], hasSlashCommand = () => false, }) => {
15
- // Get current working directory
16
- const currentWorkdir = workdir || process.cwd();
17
- // Simple history navigation reset function
18
- const resetHistoryNavigation = useCallback(() => {
19
- // This will be handled by InputManager through callbacks
20
- }, []);
15
+ // Get current working directory - memoized to avoid repeated process.cwd() calls
16
+ const currentWorkdir = useMemo(() => workdir || process.cwd(), [workdir]);
21
17
  // Input manager with all input state and functionality (including images)
22
- const { inputText, cursorPosition, clearInput,
18
+ const { inputText, cursorPosition,
23
19
  // Image management
24
20
  attachedImages, clearImages,
25
21
  // File selector
26
- showFileSelector, filteredFiles, fileSearchQuery: searchQuery, handleFileSelect: handleFileSelectorSelect, handleCancelFileSelect,
22
+ showFileSelector, filteredFiles, fileSearchQuery: searchQuery, handleFileSelect, handleCancelFileSelect,
27
23
  // Command selector
28
- showCommandSelector, commandSearchQuery, handleCommandSelect: handleCommandSelectorSelect, handleCommandInsert: handleCommandSelectorInsert, handleCancelCommandSelect,
24
+ showCommandSelector, commandSearchQuery, handleCommandSelect, handleCommandInsert, handleCancelCommandSelect,
29
25
  // Bash history selector
30
- showBashHistorySelector, bashHistorySearchQuery, handleBashHistorySelect: handleBashHistorySelectorSelect, handleBashHistoryExecute, handleCancelBashHistorySelect,
26
+ showBashHistorySelector, bashHistorySearchQuery, handleBashHistorySelect, handleCancelBashHistorySelect,
31
27
  // Memory type selector
32
- showMemoryTypeSelector, memoryMessage, handleMemoryTypeSelect: handleMemoryTypeSelectorSelect, handleCancelMemoryTypeSelect,
28
+ showMemoryTypeSelector, memoryMessage, handleMemoryTypeSelect, handleCancelMemoryTypeSelect,
33
29
  // Bash/MCP Manager
34
30
  showBashManager, showMcpManager, setShowBashManager, setShowMcpManager,
35
31
  // Input history
36
32
  setUserInputHistory,
33
+ // Complex handlers combining multiple operations
34
+ handleBashHistoryExecuteAndSend,
37
35
  // Main handler
38
- handleInput, } = useInputManager({
39
- onShowBashManager: () => setShowBashManager(true),
40
- onShowMcpManager: () => setShowMcpManager(true),
36
+ handleInput,
37
+ // Manager ready state
38
+ isManagerReady, } = useInputManager({
41
39
  onSendMessage: sendMessage,
42
40
  onHasSlashCommand: hasSlashCommand,
43
41
  onSaveMemory: saveMemory,
44
42
  onAbortMessage: abortMessage,
45
- onResetHistoryNavigation: resetHistoryNavigation,
46
43
  });
47
44
  // Set user input history when it changes
48
45
  useEffect(() => {
@@ -52,41 +49,11 @@ export const InputBox = ({ isLoading = false, isCommandRunning = false, workdir,
52
49
  useInput(async (input, key) => {
53
50
  await handleInput(input, key, attachedImages, isLoading, isCommandRunning, clearImages);
54
51
  });
55
- // Handler functions for keyboard events
56
- const handleFileSelect = useCallback((filePath) => {
57
- handleFileSelectorSelect(filePath);
58
- }, [handleFileSelectorSelect]);
59
- const handleCommandSelect = useCallback((command) => {
60
- handleCommandSelectorSelect(command);
61
- }, [handleCommandSelectorSelect]);
62
- const handleBashHistorySelect = useCallback((command) => {
63
- handleBashHistorySelectorSelect(command);
64
- }, [handleBashHistorySelectorSelect]);
65
- const keyboardHandleBashHistoryExecute = useCallback((command) => {
66
- const commandToExecute = handleBashHistoryExecute(command);
67
- // Clear input box and execute command, ensure command starts with !
68
- const bashCommand = commandToExecute.startsWith("!")
69
- ? commandToExecute
70
- : `!${commandToExecute}`;
71
- clearInput();
72
- sendMessage(bashCommand);
73
- }, [handleBashHistoryExecute, clearInput, sendMessage]);
74
- const handleMemoryTypeSelect = useCallback(async (type) => {
75
- const currentMessage = inputText.trim();
76
- if (currentMessage.startsWith("#")) {
77
- await saveMemory(currentMessage, type);
78
- }
79
- // Call the handler function to close the selector
80
- handleMemoryTypeSelectorSelect(type);
81
- // Clear input box
82
- clearInput();
83
- }, [inputText, saveMemory, handleMemoryTypeSelectorSelect, clearInput]);
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
84
54
  const isPlaceholder = !inputText;
85
55
  const placeholderText = INPUT_PLACEHOLDER_TEXT;
86
- // Create adapter function for CommandSelector
87
- const handleCommandInsert = useCallback((command) => {
88
- handleCommandSelectorInsert(command);
89
- }, [handleCommandSelectorInsert]);
56
+ // handleCommandSelectorInsert is already memoized in useInputManager, no need to wrap again
90
57
  // Split text into three parts: before cursor, cursor position, after cursor
91
58
  const displayText = isPlaceholder ? placeholderText : inputText;
92
59
  const beforeCursor = displayText.substring(0, cursorPosition);
@@ -94,5 +61,9 @@ export const InputBox = ({ isLoading = false, isCommandRunning = false, workdir,
94
61
  const afterCursor = displayText.substring(cursorPosition + 1);
95
62
  // Always show cursor, allow user to continue input during loading
96
63
  const shouldShowCursor = true;
97
- 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(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) }) }))] }));
98
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;AA2G9C,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,38 +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 { MessageSource } from "wave-agent-sdk";
5
- import { DiffViewer } from "./DiffViewer.js";
6
- import { CommandOutputDisplay } from "./CommandOutputDisplay.js";
7
- import { ToolResultDisplay } from "./ToolResultDisplay.js";
8
- import { MemoryDisplay } from "./MemoryDisplay.js";
9
- import { CompressDisplay } from "./CompressDisplay.js";
10
- import { SubagentBlock } from "./SubagentBlock.js";
11
- import { usePagination } from "../hooks/usePagination.js";
12
- // Function to render a single message
13
- const renderMessageItem = (message, originalIndex, isExpanded, previousMessage) => {
14
- const shouldShowHeader = previousMessage?.role !== message.role;
15
- 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: _jsxs(Text, { children: [block.customCommandContent && (_jsxs(Text, { color: "cyan", bold: true, children: ["\u26A1", " "] })), block.source === MessageSource.HOOK && (_jsxs(Text, { color: "magenta", bold: true, children: ["\uD83D\uDD17", " "] })), 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 === "subagent" && (_jsx(SubagentBlock, { block: block, isExpanded: isExpanded }))] }, blockIndex))) })] }, `message-${originalIndex}`));
16
- };
17
- export const MessageList = ({ messages, isLoading = false, isCommandRunning = false, isCompressing = false, latestTotalTokens = 0, isExpanded = false, }) => {
18
- // Use original messages for pagination calculation
19
- const { displayInfo } = usePagination(messages);
20
- // Get current page messages while preserving original index information
21
- const currentMessagesWithIndex = useMemo(() => {
22
- return messages
23
- .slice(displayInfo.startIndex, displayInfo.endIndex)
24
- .map((message, index) => ({
25
- message,
26
- originalIndex: displayInfo.startIndex + index,
27
- }));
28
- }, [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, }) => {
29
6
  // Empty message state
30
7
  if (messages.length === 0) {
31
8
  return (_jsx(Box, { flexDirection: "column", paddingY: 1, children: _jsx(Text, { color: "gray", children: "Welcome to WAVE Code Assistant!" }) }));
32
9
  }
33
- 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) => {
34
28
  // Get previous message
35
- const previousMessage = originalIndex > 0 ? messages[originalIndex - 1] : undefined;
36
- return renderMessageItem(message, originalIndex, isExpanded, previousMessage);
37
- }) }), !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"] })] }) }))] }));
38
- };
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";
@@ -2,7 +2,6 @@ import React from "react";
2
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,gBAAgB,CAAC;AAuCxB,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,29 +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
- const truncateText = (text, maxLines) => {
6
- const lines = text.split("\n");
7
- if (lines.length <= maxLines) {
8
- return text;
9
- }
10
- return lines.slice(0, maxLines).join("\n") + "\n...";
11
- };
12
- switch (block.type) {
13
- case "text": {
14
- const maxLines = isExpanded ? 50 : 10;
15
- const truncatedContent = truncateText(block.content, maxLines);
16
- return _jsx(Text, { children: truncatedContent });
17
- }
18
- case "error":
19
- return _jsxs(Text, { color: "red", children: ["\u274C Error: ", block.content] });
20
- case "tool":
21
- return _jsx(ToolResultDisplay, { block: block, isExpanded: isExpanded });
22
- default:
23
- return null;
24
- }
25
- };
26
- 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] || [];
27
8
  // Status indicator mapping
28
9
  const getStatusIndicator = (status) => {
29
10
  switch (status) {
@@ -40,9 +21,27 @@ export const SubagentBlock = ({ block, isExpanded = false, }) => {
40
21
  }
41
22
  };
42
23
  const statusInfo = getStatusIndicator(block.status);
43
- // Determine how many messages to show
44
- const messagesToShow = isExpanded
45
- ? block.messages.slice(-10) // Up to 10 most recent when expanded
46
- : block.messages.slice(-2); // Up to 2 most recent when collapsed
47
- 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)))] }))] }));
48
47
  };
@@ -1,11 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Text } from "ink";
3
3
  export const ToolResultDisplay = ({ block, isExpanded = false, }) => {
4
- const { parameters, result, compactParams, isRunning, success, error, name } = block;
4
+ const { parameters, result, compactParams, stage, success, error, name } = block;
5
5
  // Directly use compactParams
6
6
  // (no change needed as we destructured it above)
7
7
  const getStatusColor = () => {
8
- if (isRunning)
8
+ if (stage === "running")
9
9
  return "yellow";
10
10
  if (success)
11
11
  return "green";
@@ -14,12 +14,12 @@ export const ToolResultDisplay = ({ block, isExpanded = false, }) => {
14
14
  return "gray"; // Unknown state or no state information
15
15
  };
16
16
  const getStatusText = () => {
17
- if (isRunning)
17
+ if (stage === "running")
18
18
  return "🔄";
19
19
  if (success)
20
20
  return "";
21
21
  if (error || success === false)
22
- return "❌ Failed";
22
+ return "❌";
23
23
  return ""; // Don't display text for unknown state
24
24
  };
25
25
  const hasImages = () => {
@@ -48,5 +48,5 @@ export const ToolResultDisplay = ({ block, isExpanded = false, }) => {
48
48
  return null;
49
49
  };
50
50
  const shortResult = getShortResult();
51
- return (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsxs(Box, { children: [_jsx(Text, { color: "magenta", children: "\uD83D\uDD27 " }), _jsx(Text, { color: "white", children: toolName }), !isExpanded && compactParams && (_jsxs(Text, { color: "gray", children: [" (", compactParams, ")"] })), _jsxs(Text, { color: getStatusColor(), children: [" ", getStatusText()] }), hasImages() && _jsxs(Text, { color: "blue", children: [" ", getImageIndicator()] })] }), !isExpanded && shortResult && !error && (_jsx(Box, { paddingLeft: 2, borderLeft: true, borderColor: "gray", flexDirection: "column", children: shortResult.split("\n").map((line, index) => (_jsx(Text, { color: "white", children: line }, index))) })), isExpanded && parameters && (_jsxs(Box, { paddingLeft: 2, borderLeft: true, borderColor: "gray", flexDirection: "column", children: [_jsx(Text, { color: "cyan", bold: true, children: "Parameters:" }), _jsx(Text, { color: "gray", children: parameters })] })), isExpanded && result && (_jsx(Box, { flexDirection: "column", children: _jsxs(Box, { paddingLeft: 2, borderLeft: true, borderColor: "green", flexDirection: "column", children: [_jsx(Text, { color: "cyan", bold: true, children: "Result:" }), _jsx(Text, { color: "white", children: result })] }) })), error && (_jsx(Box, { children: _jsxs(Text, { color: "red", children: ["Error: ", typeof error === "string" ? error : String(error)] }) }))] }));
51
+ return (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsxs(Box, { children: [_jsx(Text, { color: "magenta", children: "\uD83D\uDD27 " }), _jsx(Text, { color: "white", children: toolName }), !isExpanded && compactParams && (_jsxs(Text, { color: "gray", children: [" ", compactParams] })), _jsxs(Text, { color: getStatusColor(), children: [" ", getStatusText()] }), hasImages() && _jsxs(Text, { color: "blue", children: [" ", getImageIndicator()] })] }), !isExpanded && shortResult && !error && (_jsx(Box, { paddingLeft: 2, borderLeft: true, borderColor: "gray", flexDirection: "column", children: shortResult.split("\n").map((line, index) => (_jsx(Text, { color: "white", children: line }, index))) })), isExpanded && parameters && (_jsxs(Box, { paddingLeft: 2, borderLeft: true, borderColor: "gray", flexDirection: "column", children: [_jsx(Text, { color: "cyan", bold: true, children: "Parameters:" }), _jsx(Text, { color: "gray", children: parameters })] })), isExpanded && result && (_jsx(Box, { flexDirection: "column", children: _jsxs(Box, { paddingLeft: 2, borderLeft: true, borderColor: "green", flexDirection: "column", children: [_jsx(Text, { color: "cyan", bold: true, children: "Result:" }), _jsx(Text, { color: "white", children: result })] }) })), error && (_jsx(Box, { children: _jsxs(Text, { color: "red", children: ["Error: ", typeof error === "string" ? error : String(error)] }) }))] }));
52
52
  };