Merge pull request #12010 from Kilo-Org/research-inline-image-rendering

feat(vscode): render read-tool images inline in chat view
This commit is contained in:
Marius
2026-07-07 15:01:18 +02:00
committed by GitHub
3 changed files with 59 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"kilo-code": minor
---
Render images inline in the chat view when the agent reads an image file. Images appear below the read tool card and can be clicked to open a full-size preview.
@@ -412,6 +412,35 @@ html[data-theme="kilo-vscode"] [data-component="bash-output"] {
margin-bottom: 0px;
}
[data-slot="tool-read-images"] {
display: flex;
flex-wrap: wrap;
gap: 8px;
padding-top: 8px;
max-width: min(100%, 64ch);
}
[data-slot="tool-read-image"] {
max-width: 200px;
max-height: 200px;
border-radius: var(--radius-sm, 6px);
overflow: hidden;
border: 1px solid var(--border-weak-base);
cursor: pointer;
transition: border-color 0.15s ease;
&:hover {
border-color: var(--border-strong-base);
}
}
[data-slot="tool-read-image-img"] {
display: block;
max-width: 100%;
max-height: 200px;
object-fit: contain;
}
[data-component="todos"] {
[data-slot="message-part-todo-hidden"] {
padding: 2px 0 2px 28px;
@@ -995,6 +995,7 @@ export interface ToolProps {
callID?: string
output?: string
status?: string
attachments?: FilePart[]
hideDetails?: boolean
defaultOpen?: boolean
forceOpen?: boolean
@@ -1243,6 +1244,8 @@ PART_MAPPING["tool"] = function ToolPartDisplay(props) {
// @ts-expect-error
output={part.state.output}
status={part.state.status}
// @ts-expect-error
attachments={part.state.attachments}
hideDetails={props.hideDetails}
defaultOpen={props.defaultOpen}
animate
@@ -1746,6 +1749,7 @@ ToolRegistry.register({
render(props) {
const data = useData()
const i18n = useI18n()
const dialog = useDialog()
const args: string[] = []
if (props.input.offset) args.push("offset=" + props.input.offset)
if (props.input.limit) args.push("limit=" + props.input.limit)
@@ -1755,6 +1759,10 @@ ToolRegistry.register({
return value.filter((p): p is string => typeof p === "string")
})
const pending = createMemo(() => busy(props.status))
const images = createMemo(() =>
(props.attachments ?? []).filter((f) => f.mime.startsWith("image/") && f.url),
)
const preview = (url: string, alt?: string) => dialog.show(() => <ImagePreview src={url} alt={alt} />)
return (
<>
<BasicTool
@@ -1783,6 +1791,23 @@ ToolRegistry.register({
/>
)}
</For>
<Show when={images().length > 0}>
<div data-slot="tool-read-images">
<For each={images()}>
{(file) => (
<div data-slot="tool-read-image" onClick={() => preview(file.url, file.filename)}>
<img
data-slot="tool-read-image-img"
src={file.url}
alt={file.filename ?? i18n.t("ui.message.attachment.alt")}
loading="lazy"
decoding="async"
/>
</div>
)}
</For>
</div>
</Show>
</>
)
},