feat(vscode): render read-tool images inline in chat view - #12010
Conversation
The read tool already produces base64 data URL attachments for image files and threads them through SSE into the webview part store, but the tool renderer never received them. Thread part.state.attachments into ToolProps and render image attachments inline below the read tool card, reusing the existing user-attachment image pattern with click-to-open preview. No backend, schema, or SDK changes needed.
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Small, well-scoped change confined to Files Reviewed (3 files)
Reviewed by claude-sonnet-5 · Input: 34 · Output: 9.2K · Cached: 700.5K Review guidance: REVIEW.md from base branch |
…-rendering feat(vscode): render read-tool images inline in chat view
Problem
When the agent reads an image file via the
readtool, the image bytes never appear in the chat view. The user only sees a text line (Read <filename>) with no visual confirmation of what was read, even though the agent itself receives the image as vision input. CLI is out of scope and much more difficult.Why this happens
The data pipeline already exists end-to-end but stops short of rendering:
readtool (packages/opencode/src/tool/read.ts:327-333) base64-encodes image files and returns them asFilePartattachments with adata:URL.ToolStateCompleted.attachments, they flow over SSE (message.part.updated), land in the webview part store (context/session.tsx:1635), and survive session reload (they are not touched by compaction orstripPartMetadata).PART_MAPPING["tool"]renderer passesinput/metadata/output/statusinto each tool component but notpart.state.attachments, so thereadrenderer never sees the image data. The webview CSP already allowsimg-src ... data: https:, and user-attached images already render with<img src={file.url}>atmessage-part.tsx:836.So the only missing piece is a renderer.
What this change does
Three additions to
packages/kilo-ui/src/components/message-part.tsx(Kilo-owned, no upstream markers):attachments?: FilePart[]toToolProps.part.state.attachmentsthrough theDynamiccomponent inPART_MAPPING["tool"].readtool renderer, filter image attachments (mime.startsWith("image/")with aurl) and render them inline as<img>thumbnails withloading="lazy"anddecoding="async", clickable to open the existingImagePreviewdialog (the same pattern already shipping for user-attached images and image diffs).Plus matching CSS in
message-part.cssfor thetool-read-imagesslots (200px max thumbnail, rounded border, hover highlight,object-fit: contain).No backend, schema, SDK, or extension-host changes. The
FilePartschema (mime+ base64url) is already a first-classParttype and already carries images for both user-attached and tool-read paths.Scope and limits
readtool only.webfetchalso emits image attachments but is left for a follow-up if desired.PartTable.data, so the webview still shows them after compaction.SUPPORTED_IMAGE_MIMESin the read tool isjpeg/png/gif/webponly, so no SVG data URL ever reaches the renderer. Even if one did,<img>-loaded SVGs are sandboxed by the browser (scripts do not execute).image.normalize()(Photon WASM, 2000x2000 / ~5MB cap) before the data URL reaches the client, so each inline image is a bounded decode identical to the existing user-attachment and image-diff rendering paths.