From a0abf4a79f3523ae00736c375864c42b3f1bdebe Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Sat, 10 Jan 2026 23:55:44 +0700 Subject: [PATCH] feat(app): add Read tool call display with file path and content MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add ReadToolCallSchema to parse Read tool calls (file_path, offset, limit) - Display file path immediately from input (before result arrives) - Show offset/limit range when specified - Display file content in scrollable area when result completes 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- .../app/src/components/tool-call-sheet.tsx | 37 +++++++++++++++++++ packages/app/src/utils/tool-call-parsers.ts | 36 +++++++++++++++++- 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/packages/app/src/components/tool-call-sheet.tsx b/packages/app/src/components/tool-call-sheet.tsx index e31b5afeb..2a3de9613 100644 --- a/packages/app/src/components/tool-call-sheet.tsx +++ b/packages/app/src/components/tool-call-sheet.tsx @@ -218,6 +218,39 @@ function ToolCallSheetContent({ data, onClose }: ToolCallSheetContentProps) { ) : null} ); + } else if (toolCallDisplay.type === "read") { + // Read tool: show file path and content + sections.push( + + File + + {toolCallDisplay.filePath} + + {(toolCallDisplay.offset !== undefined || toolCallDisplay.limit !== undefined) ? ( + + {toolCallDisplay.offset !== undefined ? `Offset: ${toolCallDisplay.offset}` : ""} + {toolCallDisplay.offset !== undefined && toolCallDisplay.limit !== undefined ? " • " : ""} + {toolCallDisplay.limit !== undefined ? `Limit: ${toolCallDisplay.limit}` : ""} + + ) : null} + {toolCallDisplay.content ? ( + + + {toolCallDisplay.content} + + + ) : null} + + ); } else { // Generic tool: show input/output as key-value pairs if (toolCallDisplay.input.length > 0) { @@ -414,6 +447,10 @@ const styles = StyleSheet.create((theme) => ({ fontFamily: Fonts.mono, fontSize: theme.fontSize.xs, }, + rangeText: { + color: theme.colors.mutedForeground, + fontSize: theme.fontSize.xs, + }, diffContainer: { borderWidth: theme.borderWidth[1], borderColor: theme.colors.border, diff --git a/packages/app/src/utils/tool-call-parsers.ts b/packages/app/src/utils/tool-call-parsers.ts index 52a5407e4..523ba691b 100644 --- a/packages/app/src/utils/tool-call-parsers.ts +++ b/packages/app/src/utils/tool-call-parsers.ts @@ -967,6 +967,40 @@ const EditToolCallSchema = z }; }); +// Read input: { file_path: string, offset?: number, limit?: number } +const ReadInputSchema = z.object({ + file_path: z.string(), + offset: z.number().optional(), + limit: z.number().optional(), +}).passthrough(); + +// Read result: { type: "file_read", filePath: string, content: string } +const ReadResultSchema = z.object({ + type: z.literal("file_read"), + filePath: z.string(), + content: z.string(), +}).passthrough(); + +// Read tool call display schema +const ReadToolCallSchema = z + .object({ + input: ReadInputSchema, + result: z.unknown(), + }) + .transform((data): { type: "read"; filePath: string; content: string; offset?: number; limit?: number } => { + const filePath = data.input.file_path; + const resultParsed = ReadResultSchema.safeParse(data.result); + const content = resultParsed.success ? resultParsed.data.content : ""; + + return { + type: "read", + filePath, + content, + offset: data.input.offset, + limit: data.input.limit, + }; + }); + // Generic tool call display schema (fallback) const GenericToolCallSchema = z .object({ @@ -984,7 +1018,7 @@ const GenericToolCallSchema = z }; }); -const ToolCallDisplaySchema = z.union([ShellToolCallSchema, EditToolCallSchema, GenericToolCallSchema]); +const ToolCallDisplaySchema = z.union([ShellToolCallSchema, EditToolCallSchema, ReadToolCallSchema, GenericToolCallSchema]); export type ToolCallDisplay = z.infer;