From 3566a20ebbded754070fce66af48690d1a904879 Mon Sep 17 00:00:00 2001 From: Adam Malczewski Date: Sat, 27 Jun 2026 04:18:59 +0900 Subject: feat(vision): image paste + transcript image rendering + vision badge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vision & vision-handoff frontend (consumes the backend's additive wire@0.12.0 / transport-contract@0.22.0 image types — no version bump). Contracts mirrored: - .dispatch/wire.reference.md: ImageChunk added to the Chunk union + ImageChunk/ImageInput interfaces. - .dispatch/transport-contract.reference.md: ChatRequest.images, ModelMetadata.vision, + ImageChunk/ImageInput re-exports. Core (core/chunks): - conformance: assertChunkExhaustive handles the new 'image' variant (the guard caught it — its purpose). - appendUserMessage(state, text, images?) echoes a [text, image, ...] user run; the user-message event dedup scans the trailing user run (not just the last chunk) so an image-bearing echo doesn't duplicate the text; applyHistory's during-gen dedup matches a multi-chunk echo by content equality (chunkContentEquals + trailingRun helpers). UI: - ChatView renders user 'image' chunks as lazy bubbles; a non-vision model's persisted [image, analysis-text] both render. read_image tool renders generically (no special-casing). - Composer: clipboard paste / file picker / drag-drop of images -> base64 data URLs, thumbnail previews with remove, forwarded on chat.send (omitted when none). Image-only sends allowed; steering (chat.queue) never forwards images. - ModelSelector: vision badge (isVisionModel) marks vision-capable models; indicator shows native-vision vs vision-handoff hint. Store wiring: ChatStore.send + AppStore.send + App.svelte handleSend thread images through; chat.send still omits cwd (only images added). Verification: svelte-check 0/0; vitest 901/901 (run twice, +34 new); biome clean; vite build OK. See backend-handoff.md §2j. Not merged or pushed. --- src/features/chat/ui/ChatView.svelte | 12 +- src/features/chat/ui/Composer.svelte | 232 +++++++++++++++++++++++++++--- src/features/chat/ui/ModelSelector.svelte | 46 +++++- 3 files changed, 269 insertions(+), 21 deletions(-) (limited to 'src/features/chat/ui') diff --git a/src/features/chat/ui/ChatView.svelte b/src/features/chat/ui/ChatView.svelte index e72f639..8081951 100644 --- a/src/features/chat/ui/ChatView.svelte +++ b/src/features/chat/ui/ChatView.svelte @@ -95,11 +95,21 @@ {#snippet chunkRow(rendered: RenderedChunk)} {#if rendered.role === "user"} - +
{#if rendered.chunk.type === "text"}

{rendered.chunk.text}

+ {:else if rendered.chunk.type === "image"} + {rendered.chunk.mimeType {/if}
diff --git a/src/features/chat/ui/Composer.svelte b/src/features/chat/ui/Composer.svelte index 96b4b3a..7898448 100644 --- a/src/features/chat/ui/Composer.svelte +++ b/src/features/chat/ui/Composer.svelte @@ -1,8 +1,19 @@
@@ -44,7 +61,32 @@ aria-label="Model selector" > {#each keyModels as model (model)} - + {/each} + {#if selectedVision} +
+ + Vision — this model sees images natively +
+ {:else} +
+ Pasted images are auto-described (vision handoff) +
+ {/if}
-- cgit v1.2.3