wave-code 0.7.0 → 0.7.2

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/dist/components/ChatInterface.d.ts.map +1 -1
  2. package/dist/components/ChatInterface.js +28 -15
  3. package/dist/components/ConfirmationDetails.d.ts +1 -0
  4. package/dist/components/ConfirmationDetails.d.ts.map +1 -1
  5. package/dist/components/ConfirmationDetails.js +6 -9
  6. package/dist/components/ConfirmationSelector.d.ts +1 -0
  7. package/dist/components/ConfirmationSelector.d.ts.map +1 -1
  8. package/dist/components/ConfirmationSelector.js +164 -117
  9. package/dist/components/DiffDisplay.d.ts +1 -0
  10. package/dist/components/DiffDisplay.d.ts.map +1 -1
  11. package/dist/components/DiffDisplay.js +92 -29
  12. package/dist/components/HistorySearch.d.ts.map +1 -1
  13. package/dist/components/HistorySearch.js +1 -1
  14. package/dist/components/Markdown.d.ts.map +1 -1
  15. package/dist/components/Markdown.js +3 -1
  16. package/dist/components/McpManager.d.ts.map +1 -1
  17. package/dist/components/McpManager.js +49 -52
  18. package/dist/components/MessageBlockItem.d.ts +9 -0
  19. package/dist/components/MessageBlockItem.d.ts.map +1 -0
  20. package/dist/components/MessageBlockItem.js +11 -0
  21. package/dist/components/MessageList.d.ts +2 -4
  22. package/dist/components/MessageList.d.ts.map +1 -1
  23. package/dist/components/MessageList.js +28 -23
  24. package/dist/components/PluginDetail.d.ts.map +1 -1
  25. package/dist/components/PluginDetail.js +19 -22
  26. package/dist/components/TaskList.d.ts.map +1 -1
  27. package/dist/components/TaskList.js +2 -5
  28. package/dist/components/ToolDisplay.d.ts.map +1 -1
  29. package/dist/components/ToolDisplay.js +1 -1
  30. package/dist/contexts/useChat.d.ts.map +1 -1
  31. package/dist/contexts/useChat.js +17 -2
  32. package/dist/utils/highlightUtils.d.ts +2 -0
  33. package/dist/utils/highlightUtils.d.ts.map +1 -0
  34. package/dist/utils/highlightUtils.js +69 -0
  35. package/dist/utils/toolParameterTransforms.d.ts +1 -1
  36. package/dist/utils/toolParameterTransforms.d.ts.map +1 -1
  37. package/dist/utils/toolParameterTransforms.js +10 -3
  38. package/package.json +4 -2
  39. package/src/components/ChatInterface.tsx +35 -23
  40. package/src/components/ConfirmationDetails.tsx +13 -9
  41. package/src/components/ConfirmationSelector.tsx +207 -128
  42. package/src/components/DiffDisplay.tsx +162 -59
  43. package/src/components/HistorySearch.tsx +1 -0
  44. package/src/components/Markdown.tsx +3 -1
  45. package/src/components/McpManager.tsx +51 -59
  46. package/src/components/MessageBlockItem.tsx +83 -0
  47. package/src/components/MessageList.tsx +55 -52
  48. package/src/components/PluginDetail.tsx +30 -31
  49. package/src/components/TaskList.tsx +2 -5
  50. package/src/components/ToolDisplay.tsx +5 -1
  51. package/src/contexts/useChat.tsx +18 -2
  52. package/src/utils/highlightUtils.ts +76 -0
  53. package/src/utils/toolParameterTransforms.ts +11 -2
  54. package/dist/components/MessageItem.d.ts +0 -8
  55. package/dist/components/MessageItem.d.ts.map +0 -1
  56. package/dist/components/MessageItem.js +0 -13
  57. package/src/components/MessageItem.tsx +0 -81
@@ -7,11 +7,13 @@ import { diffLines, diffWords } from "diff";
7
7
  interface DiffDisplayProps {
8
8
  toolName?: string;
9
9
  parameters?: string;
10
+ startLineNumber?: number;
10
11
  }
11
12
 
12
13
  export const DiffDisplay: React.FC<DiffDisplayProps> = ({
13
14
  toolName,
14
15
  parameters,
16
+ startLineNumber,
15
17
  }) => {
16
18
  const showDiff =
17
19
  toolName && [WRITE_TOOL_NAME, EDIT_TOOL_NAME].includes(toolName);
@@ -21,12 +23,12 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
21
23
  if (!showDiff || !toolName || !parameters) return [];
22
24
  try {
23
25
  // Use local transformation with JSON parsing and type guards
24
- return transformToolBlockToChanges(toolName, parameters);
26
+ return transformToolBlockToChanges(toolName, parameters, startLineNumber);
25
27
  } catch (error) {
26
28
  console.warn("Error transforming tool block to changes:", error);
27
29
  return [];
28
30
  }
29
- }, [toolName, parameters, showDiff]);
31
+ }, [toolName, parameters, showDiff, startLineNumber]);
30
32
 
31
33
  // Render word-level diff between two lines of text
32
34
  const renderWordLevelDiff = (
@@ -100,6 +102,43 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
100
102
  try {
101
103
  if (changes.length === 0) return null;
102
104
 
105
+ const maxLineNum = changes.reduce((max, change) => {
106
+ const oldLines = (change.oldContent || "").split("\n").length;
107
+ const newLines = (change.newContent || "").split("\n").length;
108
+ const start = change.startLineNumber || 1;
109
+ // For Edit tool, the diff might show context lines before/after the change.
110
+ // The startLineNumber is the line where old_string starts.
111
+ // diffLines will include context lines if they are part of the change object.
112
+ // However, our transformEditParameters currently only puts old_string/new_string.
113
+ // If we ever support context lines in the Change object, we need to be careful.
114
+ return Math.max(max, start + oldLines, start + newLines);
115
+ }, 0);
116
+ const maxDigits = Math.max(2, maxLineNum.toString().length);
117
+
118
+ const renderLine = (
119
+ oldLineNum: number | null,
120
+ newLineNum: number | null,
121
+ prefix: string,
122
+ content: React.ReactNode,
123
+ color: string,
124
+ key: string,
125
+ ) => {
126
+ const formatNum = (num: number | null) =>
127
+ num === null
128
+ ? " ".repeat(maxDigits)
129
+ : num.toString().padStart(maxDigits);
130
+
131
+ return (
132
+ <Box key={key} flexDirection="row">
133
+ <Text color="gray">{formatNum(oldLineNum)} </Text>
134
+ <Text color="gray">{formatNum(newLineNum)} </Text>
135
+ <Text color="gray">| </Text>
136
+ <Text color={color}>{prefix}</Text>
137
+ <Text color={color}>{content}</Text>
138
+ </Box>
139
+ );
140
+ };
141
+
103
142
  const allElements: React.ReactNode[] = [];
104
143
 
105
144
  changes.forEach((change, changeIndex) => {
@@ -110,45 +149,46 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
110
149
  change.newContent || "",
111
150
  );
112
151
 
152
+ let oldLineNum = change.startLineNumber || 1;
153
+ let newLineNum = change.startLineNumber || 1;
154
+
113
155
  // Process line diffs
114
156
  const diffElements: React.ReactNode[] = [];
115
157
  lineDiffs.forEach((part, partIndex) => {
158
+ const lines = part.value.split("\n");
159
+ // diffLines might return a trailing empty string if the content ends with a newline
160
+ if (lines[lines.length - 1] === "") {
161
+ lines.pop();
162
+ }
163
+
116
164
  if (part.added) {
117
- const lines = part.value
118
- .split("\n")
119
- .filter((line) => line !== "");
120
165
  lines.forEach((line, lineIndex) => {
121
166
  diffElements.push(
122
- <Box
123
- key={`add-${changeIndex}-${partIndex}-${lineIndex}`}
124
- flexDirection="row"
125
- >
126
- <Text color="green">+</Text>
127
- <Text color="green">{line}</Text>
128
- </Box>,
167
+ renderLine(
168
+ null,
169
+ newLineNum++,
170
+ "+",
171
+ line,
172
+ "green",
173
+ `add-${changeIndex}-${partIndex}-${lineIndex}`,
174
+ ),
129
175
  );
130
176
  });
131
177
  } else if (part.removed) {
132
- const lines = part.value
133
- .split("\n")
134
- .filter((line) => line !== "");
135
178
  lines.forEach((line, lineIndex) => {
136
179
  diffElements.push(
137
- <Box
138
- key={`remove-${changeIndex}-${partIndex}-${lineIndex}`}
139
- flexDirection="row"
140
- >
141
- <Text color="red">-</Text>
142
- <Text color="red">{line}</Text>
143
- </Box>,
180
+ renderLine(
181
+ oldLineNum++,
182
+ null,
183
+ "-",
184
+ line,
185
+ "red",
186
+ `remove-${changeIndex}-${partIndex}-${lineIndex}`,
187
+ ),
144
188
  );
145
189
  });
146
190
  } else {
147
191
  // Context lines - show unchanged content
148
- const lines = part.value
149
- .split("\n")
150
- .filter((line) => line !== "");
151
-
152
192
  const isFirstBlock = partIndex === 0;
153
193
  const isLastBlock = partIndex === lineDiffs.length - 1;
154
194
 
@@ -159,6 +199,9 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
159
199
  if (isFirstBlock && !isLastBlock) {
160
200
  // First block: keep last 3
161
201
  if (lines.length > 3) {
202
+ const skipCount = lines.length - 3;
203
+ oldLineNum += skipCount;
204
+ newLineNum += skipCount;
162
205
  linesToDisplay = lines.slice(-3);
163
206
  showEllipsisTop = true;
164
207
  }
@@ -174,15 +217,12 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
174
217
  linesToDisplay = [...lines.slice(0, 3), ...lines.slice(-3)];
175
218
  showEllipsisTop = false; // We'll put ellipsis in the middle
176
219
  }
177
- } else if (isFirstBlock && isLastBlock) {
178
- // Only one block (no changes?) - keep all or apply a general limit
179
- // For now, let's keep all if it's the only block
180
220
  }
181
221
 
182
222
  if (showEllipsisTop) {
183
223
  diffElements.push(
184
224
  <Box key={`ellipsis-top-${changeIndex}-${partIndex}`}>
185
- <Text color="gray"> ...</Text>
225
+ <Text color="gray">{" ".repeat(maxDigits * 2 + 2)}...</Text>
186
226
  </Box>,
187
227
  );
188
228
  }
@@ -195,28 +235,39 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
195
235
  lines.length > 6 &&
196
236
  lineIndex === 3
197
237
  ) {
238
+ const skipCount = lines.length - 6;
239
+ oldLineNum += skipCount;
240
+ newLineNum += skipCount;
198
241
  diffElements.push(
199
242
  <Box key={`ellipsis-mid-${changeIndex}-${partIndex}`}>
200
- <Text color="gray"> ...</Text>
243
+ <Text color="gray">
244
+ {" ".repeat(maxDigits * 2 + 2)}...
245
+ </Text>
201
246
  </Box>,
202
247
  );
203
248
  }
204
249
 
205
250
  diffElements.push(
206
- <Box
207
- key={`context-${changeIndex}-${partIndex}-${lineIndex}`}
208
- flexDirection="row"
209
- >
210
- <Text color="white"> </Text>
211
- <Text color="white">{line}</Text>
212
- </Box>,
251
+ renderLine(
252
+ oldLineNum++,
253
+ newLineNum++,
254
+ " ",
255
+ line,
256
+ "white",
257
+ `context-${changeIndex}-${partIndex}-${lineIndex}`,
258
+ ),
213
259
  );
214
260
  });
215
261
 
216
262
  if (showEllipsisBottom) {
263
+ const skipCount = lines.length - linesToDisplay.length;
264
+ // We don't increment oldLineNum/newLineNum here because they are already incremented in the loop
265
+ // But we need to account for the lines we skipped at the end of this block
266
+ oldLineNum += skipCount;
267
+ newLineNum += skipCount;
217
268
  diffElements.push(
218
269
  <Box key={`ellipsis-bottom-${changeIndex}-${partIndex}`}>
219
- <Text color="gray"> ...</Text>
270
+ <Text color="gray">{" ".repeat(maxDigits * 2 + 2)}...</Text>
220
271
  </Box>,
221
272
  );
222
273
  }
@@ -235,6 +286,8 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
235
286
  ) {
236
287
  const removedText = extractTextFromElement(diffElements[0]);
237
288
  const addedText = extractTextFromElement(diffElements[1]);
289
+ const oldLineNumVal = extractOldLineNumFromElement(diffElements[0]);
290
+ const newLineNumVal = extractNewLineNumFromElement(diffElements[1]);
238
291
 
239
292
  if (removedText && addedText) {
240
293
  const { removedParts, addedParts } = renderWordLevelDiff(
@@ -244,19 +297,24 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
244
297
  );
245
298
 
246
299
  allElements.push(
247
- <Box
248
- key={`word-diff-removed-${changeIndex}`}
249
- flexDirection="row"
250
- >
251
- <Text color="red">-</Text>
252
- {removedParts}
253
- </Box>,
300
+ renderLine(
301
+ oldLineNumVal,
302
+ null,
303
+ "-",
304
+ removedParts,
305
+ "red",
306
+ `word-diff-removed-${changeIndex}`,
307
+ ),
254
308
  );
255
309
  allElements.push(
256
- <Box key={`word-diff-added-${changeIndex}`} flexDirection="row">
257
- <Text color="green">+</Text>
258
- {addedParts}
259
- </Box>,
310
+ renderLine(
311
+ null,
312
+ newLineNumVal,
313
+ "+",
314
+ addedParts,
315
+ "green",
316
+ `word-diff-added-${changeIndex}`,
317
+ ),
260
318
  );
261
319
  } else {
262
320
  allElements.push(...diffElements);
@@ -298,9 +356,6 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
298
356
  return (
299
357
  <Box flexDirection="column">
300
358
  <Box paddingLeft={2} borderLeft borderColor="cyan" flexDirection="column">
301
- <Text color="cyan" bold>
302
- Diff:
303
- </Text>
304
359
  {renderExpandedDiff()}
305
360
  </Box>
306
361
  </Box>
@@ -312,16 +367,64 @@ const extractTextFromElement = (element: React.ReactNode): string | null => {
312
367
  if (!React.isValidElement(element)) return null;
313
368
 
314
369
  // Navigate through Box -> Text structure
370
+ // Our new structure is: Box -> Text (old), Text (new), Text (|), Text (prefix), Text (content)
371
+ const children = (
372
+ element.props as unknown as { children?: React.ReactNode[] }
373
+ ).children;
374
+ if (Array.isArray(children) && children.length >= 5) {
375
+ const textElement = children[4]; // Fifth child should be the Text with content
376
+ if (React.isValidElement(textElement)) {
377
+ const textChildren = (textElement.props as Record<string, unknown>)
378
+ .children;
379
+ return Array.isArray(textChildren)
380
+ ? textChildren.join("")
381
+ : String(textChildren || "");
382
+ }
383
+ }
384
+ return null;
385
+ };
386
+
387
+ const extractOldLineNumFromElement = (
388
+ element: React.ReactNode,
389
+ ): number | null => {
390
+ if (!React.isValidElement(element)) return null;
391
+ const children = (
392
+ element.props as unknown as { children?: React.ReactNode[] }
393
+ ).children;
394
+ if (Array.isArray(children) && children.length >= 1) {
395
+ const textElement = children[0];
396
+ if (React.isValidElement(textElement)) {
397
+ const textChildren = (textElement.props as Record<string, unknown>)
398
+ .children;
399
+ const val = (
400
+ Array.isArray(textChildren)
401
+ ? textChildren.join("")
402
+ : String(textChildren || "")
403
+ ).trim();
404
+ return val ? parseInt(val, 10) : null;
405
+ }
406
+ }
407
+ return null;
408
+ };
409
+
410
+ const extractNewLineNumFromElement = (
411
+ element: React.ReactNode,
412
+ ): number | null => {
413
+ if (!React.isValidElement(element)) return null;
315
414
  const children = (
316
415
  element.props as unknown as { children?: React.ReactNode[] }
317
416
  ).children;
318
417
  if (Array.isArray(children) && children.length >= 2) {
319
- const textElement = children[1]; // Second child should be the Text with content
320
- if (
321
- React.isValidElement(textElement) &&
322
- (textElement.props as unknown as { children?: string }).children
323
- ) {
324
- return (textElement.props as unknown as { children: string }).children;
418
+ const textElement = children[1];
419
+ if (React.isValidElement(textElement)) {
420
+ const textChildren = (textElement.props as Record<string, unknown>)
421
+ .children;
422
+ const val = (
423
+ Array.isArray(textChildren)
424
+ ? textChildren.join("")
425
+ : String(textChildren || "")
426
+ ).trim();
427
+ return val ? parseInt(val, 10) : null;
325
428
  }
326
429
  }
327
430
  return null;
@@ -147,6 +147,7 @@ export const HistorySearch: React.FC<HistorySearchProps> = ({
147
147
  backgroundColor={isSelected ? "blue" : undefined}
148
148
  wrap="truncate-end"
149
149
  >
150
+ {isSelected ? "> " : " "}
150
151
  {entry.prompt.replace(/\n/g, " ")}
151
152
  </Text>
152
153
  </Box>
@@ -2,6 +2,7 @@ import React, { useMemo } from "react";
2
2
  import { Box, Text } from "ink";
3
3
  import { Renderer, marked, type Tokens } from "marked";
4
4
  import chalk from "chalk";
5
+ import { highlightToAnsi } from "../utils/highlightUtils.js";
5
6
 
6
7
  export interface MarkdownProps {
7
8
  children: string;
@@ -21,7 +22,8 @@ class AnsiRenderer extends Renderer<string> {
21
22
  override code({ text, lang }: Tokens.Code): string {
22
23
  const prefix = lang ? `\`\`\`${lang}` : "```";
23
24
  const suffix = "```";
24
- return `\n${chalk.gray(prefix)}\n${text}\n${chalk.gray(suffix)}\n`;
25
+ const highlighted = highlightToAnsi(text, lang);
26
+ return `\n${chalk.gray(prefix)}\n${highlighted}\n${chalk.gray(suffix)}\n`;
25
27
  }
26
28
 
27
29
  override blockquote({ tokens }: Tokens.Blockquote): string {
@@ -65,76 +65,68 @@ export const McpManager: React.FC<McpManagerProps> = ({
65
65
  };
66
66
 
67
67
  useInput((input, key) => {
68
- if (viewMode === "list") {
69
- // List mode navigation
70
- if (key.return) {
71
- if (servers.length > 0 && selectedIndex < servers.length) {
72
- setViewMode("detail");
68
+ if (key.return) {
69
+ setViewMode((prevMode) => {
70
+ if (prevMode === "list") {
71
+ setSelectedIndex((prevIndex) => {
72
+ if (servers.length > 0 && prevIndex < servers.length) {
73
+ // We can't call setViewMode here because we're already in a setViewMode call
74
+ // But we can return the new mode from the outer setViewMode
75
+ }
76
+ return prevIndex;
77
+ });
78
+ return "detail";
73
79
  }
74
- return;
75
- }
80
+ return prevMode;
81
+ });
82
+ return;
83
+ }
76
84
 
77
- if (key.escape) {
85
+ if (key.escape) {
86
+ setViewMode((prev) => {
87
+ if (prev === "detail") {
88
+ return "list";
89
+ }
78
90
  onCancel();
79
- return;
80
- }
81
-
82
- if (key.upArrow) {
83
- setSelectedIndex(Math.max(0, selectedIndex - 1));
84
- return;
85
- }
86
-
87
- if (key.downArrow) {
88
- setSelectedIndex(Math.min(servers.length - 1, selectedIndex + 1));
89
- return;
90
- }
91
+ return prev;
92
+ });
93
+ return;
94
+ }
91
95
 
92
- // Hotkeys for server actions
93
- if (
94
- input === "c" &&
95
- servers.length > 0 &&
96
- selectedIndex < servers.length
97
- ) {
98
- const server = servers[selectedIndex];
99
- if (server.status === "disconnected" || server.status === "error") {
100
- handleConnect(server.name);
101
- }
102
- return;
103
- }
96
+ if (key.upArrow) {
97
+ setSelectedIndex((prev) => Math.max(0, prev - 1));
98
+ return;
99
+ }
104
100
 
105
- if (
106
- input === "d" &&
107
- servers.length > 0 &&
108
- selectedIndex < servers.length
109
- ) {
110
- const server = servers[selectedIndex];
111
- if (server.status === "connected") {
112
- handleDisconnect(server.name);
113
- }
114
- return;
115
- }
116
- } else if (viewMode === "detail") {
117
- // Detail mode navigation
118
- if (key.escape) {
119
- setViewMode("list");
120
- return;
121
- }
101
+ if (key.downArrow) {
102
+ setSelectedIndex((prev) => Math.min(servers.length - 1, prev + 1));
103
+ return;
104
+ }
122
105
 
123
- if (selectedServer) {
106
+ // Hotkeys for server actions
107
+ if (input === "c") {
108
+ setSelectedIndex((prev) => {
109
+ const server = servers[prev];
124
110
  if (
125
- input === "c" &&
126
- (selectedServer.status === "disconnected" ||
127
- selectedServer.status === "error")
111
+ server &&
112
+ (server.status === "disconnected" || server.status === "error")
128
113
  ) {
129
- handleConnect(selectedServer.name);
130
- return;
114
+ handleConnect(server.name);
131
115
  }
116
+ return prev;
117
+ });
118
+ return;
119
+ }
132
120
 
133
- if (input === "d" && selectedServer.status === "connected") {
134
- handleDisconnect(selectedServer.name);
135
- return;
121
+ if (input === "d") {
122
+ setSelectedIndex((prev) => {
123
+ const server = servers[prev];
124
+ if (server && server.status === "connected") {
125
+ handleDisconnect(server.name);
136
126
  }
137
- }
127
+ return prev;
128
+ });
129
+ return;
138
130
  }
139
131
  });
140
132
 
@@ -0,0 +1,83 @@
1
+ import React from "react";
2
+ import { Box, Text } from "ink";
3
+ import type { Message, MessageBlock } from "wave-agent-sdk";
4
+ import { MessageSource } from "wave-agent-sdk";
5
+ import { CommandOutputDisplay } from "./CommandOutputDisplay.js";
6
+ import { ToolDisplay } from "./ToolDisplay.js";
7
+ import { CompressDisplay } from "./CompressDisplay.js";
8
+ import { ReasoningDisplay } from "./ReasoningDisplay.js";
9
+ import { Markdown } from "./Markdown.js";
10
+
11
+ export interface MessageBlockItemProps {
12
+ block: MessageBlock;
13
+ message: Message;
14
+ isExpanded: boolean;
15
+ paddingTop?: number;
16
+ }
17
+
18
+ export const MessageBlockItem = ({
19
+ block,
20
+ message,
21
+ isExpanded,
22
+ paddingTop = 0,
23
+ }: MessageBlockItemProps) => {
24
+ return (
25
+ <Box flexDirection="column" paddingTop={paddingTop}>
26
+ {block.type === "text" && block.content.trim() && (
27
+ <Box>
28
+ {block.customCommandContent && (
29
+ <Text color="cyan" bold>
30
+ ${" "}
31
+ </Text>
32
+ )}
33
+ {block.source === MessageSource.HOOK && (
34
+ <Text color="magenta" bold>
35
+ ~{" "}
36
+ </Text>
37
+ )}
38
+ {message.role === "user" ? (
39
+ <Text backgroundColor="gray" color="white">
40
+ {block.content}
41
+ </Text>
42
+ ) : (
43
+ <Markdown>{block.content}</Markdown>
44
+ )}
45
+ </Box>
46
+ )}
47
+
48
+ {block.type === "error" && (
49
+ <Box>
50
+ <Text color="red">Error: {block.content}</Text>
51
+ </Box>
52
+ )}
53
+
54
+ {block.type === "command_output" && (
55
+ <CommandOutputDisplay block={block} isExpanded={isExpanded} />
56
+ )}
57
+
58
+ {block.type === "tool" && (
59
+ <ToolDisplay block={block} isExpanded={isExpanded} />
60
+ )}
61
+
62
+ {block.type === "image" && (
63
+ <Box>
64
+ <Text color="magenta" bold>
65
+ # Image
66
+ </Text>
67
+ {block.imageUrls && block.imageUrls.length > 0 && (
68
+ <Text color="gray" dimColor>
69
+ {" "}
70
+ ({block.imageUrls.length})
71
+ </Text>
72
+ )}
73
+ </Box>
74
+ )}
75
+
76
+ {block.type === "compress" && (
77
+ <CompressDisplay block={block} isExpanded={isExpanded} />
78
+ )}
79
+
80
+ {block.type === "reasoning" && <ReasoningDisplay block={block} />}
81
+ </Box>
82
+ );
83
+ };