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
@@ -5,7 +5,7 @@ import type { DiffBlock } from "wave-agent-sdk";
5
5
 
6
6
  interface DiffViewerProps {
7
7
  block: DiffBlock;
8
- isExpanded?: boolean;
8
+ isStatic?: boolean;
9
9
  }
10
10
 
11
11
  // Render word-level diff
@@ -48,7 +48,7 @@ const renderWordLevelDiff = (removedLine: string, addedLine: string) => {
48
48
 
49
49
  export const DiffViewer: React.FC<DiffViewerProps> = ({
50
50
  block,
51
- isExpanded = false,
51
+ isStatic = true,
52
52
  }) => {
53
53
  const { diffResult } = block;
54
54
 
@@ -246,21 +246,22 @@ export const DiffViewer: React.FC<DiffViewerProps> = ({
246
246
  // Handle remaining deleted lines at the end
247
247
  flushPendingLines();
248
248
 
249
- // Only limit displayed lines in collapsed state
250
- if (!isExpanded) {
251
- const MAX_DISPLAY_LINES = 50;
252
- if (lines.length > MAX_DISPLAY_LINES) {
253
- const truncatedLines = lines.slice(0, MAX_DISPLAY_LINES);
254
- truncatedLines.push({
255
- content: `... (${lines.length - MAX_DISPLAY_LINES} more lines truncated, press Ctrl+O to expand)`,
256
- type: "separator",
257
- });
258
- return truncatedLines;
259
- }
249
+ return lines;
250
+ }, [diffResult]);
251
+
252
+ // Truncate to last 10 lines for non-static items
253
+ const displayLines = useMemo(() => {
254
+ if (isStatic) {
255
+ return diffLines;
260
256
  }
261
257
 
262
- return lines;
263
- }, [diffResult, isExpanded]);
258
+ const MAX_LINES = 10;
259
+ if (diffLines.length <= MAX_LINES) {
260
+ return diffLines;
261
+ }
262
+
263
+ return diffLines.slice(-MAX_LINES);
264
+ }, [diffLines, isStatic]);
264
265
 
265
266
  if (!diffResult || diffResult.length === 0) {
266
267
  return (
@@ -270,11 +271,12 @@ export const DiffViewer: React.FC<DiffViewerProps> = ({
270
271
  );
271
272
  }
272
273
 
274
+ // Show traditional diff view
273
275
  return (
274
276
  <Box flexDirection="column">
275
277
  <Box flexDirection="column">
276
278
  <Box flexDirection="column">
277
- {diffLines.map((line, index) => {
279
+ {displayLines.map((line, index) => {
278
280
  // If has word-level diff, render special effects
279
281
  if (line.wordDiff) {
280
282
  const prefix = line.type === "removed" ? "- " : "+ ";
@@ -22,7 +22,7 @@ export const FileSelector: React.FC<FileSelectorProps> = ({
22
22
  const [selectedIndex, setSelectedIndex] = useState(0);
23
23
 
24
24
  useInput((input, key) => {
25
- if (key.return) {
25
+ if (key.return || key.tab) {
26
26
  if (files.length > 0 && selectedIndex < files.length) {
27
27
  onSelect(files[selectedIndex].path);
28
28
  }
@@ -126,7 +126,7 @@ export const FileSelector: React.FC<FileSelectorProps> = ({
126
126
 
127
127
  <Box marginTop={1}>
128
128
  <Text dimColor>
129
- Use ↑↓ to navigate, Enter to select, Escape to cancel
129
+ Use ↑↓ to navigate, Enter/Tab to select, Escape to cancel
130
130
  </Text>
131
131
  <Text dimColor>
132
132
  File {selectedIndex + 1} of {files.length}
@@ -1,19 +1,14 @@
1
- import React, { useState, useCallback } from "react";
1
+ import React, { useEffect, useMemo } from "react";
2
2
  import { Box, Text } from "ink";
3
+ import { useInput } from "ink";
3
4
  import { FileSelector } from "./FileSelector.js";
4
5
  import { CommandSelector } from "./CommandSelector.js";
5
6
  import { BashHistorySelector } from "./BashHistorySelector.js";
6
7
  import { MemoryTypeSelector } from "./MemoryTypeSelector.js";
7
8
  import { BashShellManager } from "./BashShellManager.js";
8
9
  import { McpManager } from "./McpManager.js";
9
- import { useInputState } from "../hooks/useInputState.js";
10
- import { useFileSelector } from "../hooks/useFileSelector.js";
11
- import { useCommandSelector } from "../hooks/useCommandSelector.js";
12
- import { useBashHistorySelector } from "../hooks/useBashHistorySelector.js";
13
- import { useMemoryTypeSelector } from "../hooks/useMemoryTypeSelector.js";
14
- import { useInputHistory } from "../hooks/useInputHistory.js";
15
- import { useInputKeyboardHandler } from "../hooks/useInputKeyboardHandler.js";
16
- import { useImageManager } from "../hooks/useImageManager.js";
10
+ import { useInputManager } from "../hooks/useInputManager.js";
11
+
17
12
  import type { McpServerStatus, SlashCommand } from "wave-agent-sdk";
18
13
 
19
14
  export const INPUT_PLACEHOLDER_TEXT =
@@ -58,172 +53,83 @@ export const InputBox: React.FC<InputBoxProps> = ({
58
53
  slashCommands = [],
59
54
  hasSlashCommand = () => false,
60
55
  }) => {
61
- // Get current working directory
62
- const currentWorkdir = workdir || process.cwd();
63
- // Bash shell manager state
64
- const [showBashManager, setShowBashManager] = useState(false);
65
- // MCP manager state
66
- const [showMcpManager, setShowMcpManager] = useState(false);
67
- // Basic input state
56
+ // Get current working directory - memoized to avoid repeated process.cwd() calls
57
+ const currentWorkdir = useMemo(() => workdir || process.cwd(), [workdir]);
58
+
59
+ // Input manager with all input state and functionality (including images)
68
60
  const {
69
61
  inputText,
70
- setInputText,
71
62
  cursorPosition,
72
- setCursorPosition,
73
- insertTextAtCursor,
74
- deleteCharAtCursor,
75
- clearInput,
76
- moveCursorLeft,
77
- moveCursorRight,
78
- moveCursorToStart,
79
- moveCursorToEnd,
80
- } = useInputState();
81
-
82
- // File selector functionality
83
- const {
63
+ // Image management
64
+ attachedImages,
65
+ clearImages,
66
+ // File selector
84
67
  showFileSelector,
85
68
  filteredFiles,
86
- searchQuery,
87
- activateFileSelector,
88
- handleFileSelect: handleFileSelectorSelect,
69
+ fileSearchQuery: searchQuery,
70
+ handleFileSelect,
89
71
  handleCancelFileSelect,
90
- updateSearchQuery,
91
- checkForAtDeletion,
92
- atPosition,
93
- } = useFileSelector();
94
-
95
- // Command selector functionality
96
- const {
72
+ // Command selector
97
73
  showCommandSelector,
98
74
  commandSearchQuery,
99
- activateCommandSelector,
100
- handleCommandSelect: handleCommandSelectorSelect,
101
- handleCommandInsert: handleCommandSelectorInsert,
75
+ handleCommandSelect,
76
+ handleCommandInsert,
102
77
  handleCancelCommandSelect,
103
- updateCommandSearchQuery,
104
- checkForSlashDeletion,
105
- slashPosition,
106
- } = useCommandSelector({
107
- onShowBashManager: () => setShowBashManager(true),
108
- onShowMcpManager: () => setShowMcpManager(true),
109
- sendMessage: async (content: string) => {
110
- await sendMessage(content);
111
- },
112
- hasSlashCommand,
113
- });
114
-
115
- // Bash history selector functionality
116
- const {
78
+ // Bash history selector
117
79
  showBashHistorySelector,
118
80
  bashHistorySearchQuery,
119
- activateBashHistorySelector,
120
- handleBashHistorySelect: handleBashHistorySelectorSelect,
121
- handleBashHistoryExecute,
81
+ handleBashHistorySelect,
122
82
  handleCancelBashHistorySelect,
123
- updateBashHistorySearchQuery,
124
- checkForExclamationDeletion,
125
- exclamationPosition,
126
- } = useBashHistorySelector();
127
-
128
- // Memory type selector functionality
129
- const {
83
+ // Memory type selector
130
84
  showMemoryTypeSelector,
131
85
  memoryMessage,
132
- activateMemoryTypeSelector,
133
- handleMemoryTypeSelect: handleMemoryTypeSelectorSelect,
86
+ handleMemoryTypeSelect,
134
87
  handleCancelMemoryTypeSelect,
135
- } = useMemoryTypeSelector();
88
+ // Bash/MCP Manager
89
+ showBashManager,
90
+ showMcpManager,
91
+ setShowBashManager,
92
+ setShowMcpManager,
93
+ // Input history
94
+ setUserInputHistory,
95
+ // Complex handlers combining multiple operations
96
+ handleBashHistoryExecuteAndSend,
97
+ // Main handler
98
+ handleInput,
99
+ // Manager ready state
100
+ isManagerReady,
101
+ } = useInputManager({
102
+ onSendMessage: sendMessage,
103
+ onHasSlashCommand: hasSlashCommand,
104
+ onSaveMemory: saveMemory,
105
+ onAbortMessage: abortMessage,
106
+ });
136
107
 
137
- // Input history functionality
138
- const { resetHistoryNavigation, navigateHistory } = useInputHistory({
139
- userInputHistory,
108
+ // Set user input history when it changes
109
+ useEffect(() => {
110
+ setUserInputHistory(userInputHistory);
111
+ }, [userInputHistory, setUserInputHistory]);
112
+
113
+ // Use the InputManager's unified input handler
114
+ useInput(async (input, key) => {
115
+ await handleInput(
116
+ input,
117
+ key,
118
+ attachedImages,
119
+ isLoading,
120
+ isCommandRunning,
121
+ clearImages,
122
+ );
140
123
  });
141
124
 
142
- // Image management functionality (includes clipboard paste)
143
- const { attachedImages, clearImages, handlePasteImage } =
144
- useImageManager(insertTextAtCursor);
125
+ // These methods are already memoized in useInputManager, no need to wrap again
145
126
 
146
- // Keyboard handling
147
- const {
148
- handleFileSelect,
149
- handleCommandSelect,
150
- handleBashHistorySelect,
151
- handleBashHistoryExecute: keyboardHandleBashHistoryExecute,
152
- handleMemoryTypeSelect,
153
- } = useInputKeyboardHandler({
154
- inputText,
155
- setInputText,
156
- cursorPosition,
157
- setCursorPosition,
158
- moveCursorLeft,
159
- moveCursorRight,
160
- moveCursorToStart,
161
- moveCursorToEnd,
162
- deleteCharAtCursor,
163
- insertTextAtCursor,
164
- clearInput,
165
- resetHistoryNavigation,
166
- navigateHistory,
167
- handlePasteImage,
168
- attachedImages,
169
- clearImages,
170
- showFileSelector,
171
- activateFileSelector,
172
- handleFileSelect: handleFileSelectorSelect,
173
- handleCancelFileSelect,
174
- updateSearchQuery,
175
- checkForAtDeletion,
176
- atPosition,
177
- showCommandSelector,
178
- activateCommandSelector,
179
- handleCommandSelect: handleCommandSelectorSelect,
180
- handleCommandInsert: handleCommandSelectorInsert,
181
- handleCancelCommandSelect,
182
- updateCommandSearchQuery,
183
- checkForSlashDeletion,
184
- slashPosition,
185
- showBashHistorySelector,
186
- activateBashHistorySelector,
187
- handleBashHistorySelect: handleBashHistorySelectorSelect,
188
- handleBashHistoryExecute,
189
- handleCancelBashHistorySelect,
190
- updateBashHistorySearchQuery,
191
- checkForExclamationDeletion,
192
- exclamationPosition,
193
- showMemoryTypeSelector,
194
- activateMemoryTypeSelector,
195
- handleMemoryTypeSelect: handleMemoryTypeSelectorSelect,
196
- showBashManager,
197
- showMcpManager,
198
- isCommandRunning,
199
- isLoading,
200
- sendMessage,
201
- abortMessage,
202
- saveMemory,
203
- });
127
+ // These methods are already memoized in useInputManager and combine multiple operations
204
128
 
205
129
  const isPlaceholder = !inputText;
206
130
  const placeholderText = INPUT_PLACEHOLDER_TEXT;
207
131
 
208
- // Create adapter function for CommandSelector
209
- const handleCommandInsert = useCallback(
210
- (command: string) => {
211
- const result = handleCommandSelectorInsert(
212
- command,
213
- inputText,
214
- cursorPosition,
215
- );
216
- setInputText(result.newInput);
217
- setCursorPosition(result.newCursorPosition);
218
- },
219
- [
220
- handleCommandSelectorInsert,
221
- inputText,
222
- cursorPosition,
223
- setInputText,
224
- setCursorPosition,
225
- ],
226
- );
132
+ // handleCommandSelectorInsert is already memoized in useInputManager, no need to wrap again
227
133
 
228
134
  // Split text into three parts: before cursor, cursor position, after cursor
229
135
  const displayText = isPlaceholder ? placeholderText : inputText;
@@ -235,8 +141,13 @@ export const InputBox: React.FC<InputBoxProps> = ({
235
141
  // Always show cursor, allow user to continue input during loading
236
142
  const shouldShowCursor = true;
237
143
 
144
+ // Only show the Box after InputManager is created on first mount
145
+ if (!isManagerReady) {
146
+ return null;
147
+ }
148
+
238
149
  return (
239
- <Box flexDirection="column" width={"100%"}>
150
+ <Box flexDirection="column">
240
151
  {showFileSelector && (
241
152
  <FileSelector
242
153
  files={filteredFiles}
@@ -261,7 +172,7 @@ export const InputBox: React.FC<InputBoxProps> = ({
261
172
  searchQuery={bashHistorySearchQuery}
262
173
  workdir={currentWorkdir}
263
174
  onSelect={handleBashHistorySelect}
264
- onExecute={keyboardHandleBashHistoryExecute}
175
+ onExecute={handleBashHistoryExecuteAndSend}
265
176
  onCancel={handleCancelBashHistorySelect}
266
177
  />
267
178
  )}
@@ -288,21 +199,19 @@ export const InputBox: React.FC<InputBoxProps> = ({
288
199
  )}
289
200
  {showBashManager || showMcpManager || (
290
201
  <Box borderStyle="single" borderColor="gray" paddingX={1}>
291
- <Box width="100%" flexDirection="row" justifyContent="space-between">
292
- <Text color={isPlaceholder ? "gray" : "white"}>
293
- {shouldShowCursor ? (
294
- <>
295
- {beforeCursor}
296
- <Text backgroundColor="white" color="black">
297
- {atCursor}
298
- </Text>
299
- {afterCursor}
300
- </>
301
- ) : (
302
- displayText
303
- )}
304
- </Text>
305
- </Box>
202
+ <Text color={isPlaceholder ? "gray" : "white"}>
203
+ {shouldShowCursor ? (
204
+ <>
205
+ {beforeCursor}
206
+ <Text backgroundColor="white" color="black">
207
+ {atCursor}
208
+ </Text>
209
+ {afterCursor}
210
+ </>
211
+ ) : (
212
+ displayText
213
+ )}
214
+ </Text>
306
215
  </Box>
307
216
  )}
308
217
  </Box>
@@ -0,0 +1,29 @@
1
+ import React, { useMemo } from "react";
2
+ import { Text } from "ink";
3
+ import { marked } from "marked";
4
+ import TerminalRenderer from "marked-terminal";
5
+
6
+ export interface MarkdownProps {
7
+ children: string;
8
+ }
9
+
10
+ // Markdown component using marked-terminal with proper unescape option
11
+ export const Markdown = React.memo(({ children }: MarkdownProps) => {
12
+ const result = useMemo(() => {
13
+ // Configure marked with TerminalRenderer using default options
14
+ marked.setOptions({
15
+ renderer: new TerminalRenderer({
16
+ // Use official unescape option to handle HTML entities
17
+ unescape: true,
18
+ }),
19
+ });
20
+
21
+ const output = marked(children);
22
+ return typeof output === "string" ? output.trim() : "";
23
+ }, [children]);
24
+
25
+ return <Text>{result}</Text>;
26
+ });
27
+
28
+ // Add display name for debugging
29
+ Markdown.displayName = "Markdown";
@@ -0,0 +1,104 @@
1
+ import React from "react";
2
+ import { Box, Text } from "ink";
3
+ import type { Message } from "wave-agent-sdk";
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 { Markdown } from "./Markdown.js";
12
+
13
+ export interface MessageItemProps {
14
+ message: Message;
15
+ isExpanded: boolean;
16
+ shouldShowHeader: boolean;
17
+ isStatic?: boolean;
18
+ }
19
+
20
+ export const MessageItem = ({
21
+ message,
22
+ isExpanded,
23
+ shouldShowHeader,
24
+ isStatic = true,
25
+ }: MessageItemProps) => {
26
+ if (message.blocks.length === 0) return null;
27
+ return (
28
+ <Box flexDirection="column" gap={1} marginTop={1}>
29
+ {shouldShowHeader && (
30
+ <Box>
31
+ <Text color={message.role === "user" ? "cyan" : "green"} bold>
32
+ {message.role === "user" ? "👤 You" : "🤖 Assistant"}
33
+ </Text>
34
+ </Box>
35
+ )}
36
+
37
+ <Box flexDirection="column" gap={1}>
38
+ {message.blocks.map((block, blockIndex) => (
39
+ <Box key={blockIndex}>
40
+ {block.type === "text" && block.content.trim() && (
41
+ <Box>
42
+ {block.customCommandContent && (
43
+ <Text color="cyan" bold>
44
+ ⚡{" "}
45
+ </Text>
46
+ )}
47
+ {block.source === MessageSource.HOOK && (
48
+ <Text color="magenta" bold>
49
+ 🔗{" "}
50
+ </Text>
51
+ )}
52
+ {isStatic ? (
53
+ <Markdown>{block.content}</Markdown>
54
+ ) : (
55
+ <Text>{block.content.split("\n").slice(-10).join("\n")}</Text>
56
+ )}
57
+ </Box>
58
+ )}
59
+
60
+ {block.type === "error" && (
61
+ <Box>
62
+ <Text color="red">❌ Error: {block.content}</Text>
63
+ </Box>
64
+ )}
65
+
66
+ {block.type === "diff" && (
67
+ <DiffViewer block={block} isStatic={isStatic} />
68
+ )}
69
+
70
+ {block.type === "command_output" && (
71
+ <CommandOutputDisplay block={block} isExpanded={isExpanded} />
72
+ )}
73
+
74
+ {block.type === "tool" && (
75
+ <ToolResultDisplay block={block} isExpanded={isExpanded} />
76
+ )}
77
+
78
+ {block.type === "image" && (
79
+ <Box>
80
+ <Text color="magenta" bold>
81
+ 📷 Image
82
+ </Text>
83
+ {block.imageUrls && block.imageUrls.length > 0 && (
84
+ <Text color="gray" dimColor>
85
+ {" "}
86
+ ({block.imageUrls.length})
87
+ </Text>
88
+ )}
89
+ </Box>
90
+ )}
91
+
92
+ {block.type === "memory" && <MemoryDisplay block={block} />}
93
+
94
+ {block.type === "compress" && (
95
+ <CompressDisplay block={block} isExpanded={isExpanded} />
96
+ )}
97
+
98
+ {block.type === "subagent" && <SubagentBlock block={block} />}
99
+ </Box>
100
+ ))}
101
+ </Box>
102
+ </Box>
103
+ );
104
+ };