Agent pane: image viewer for chat images

Merged
TE
10d ago
+2371-32

Summary

Images in a reply can now be opened. A reply's inline image (a data URL, as the pane already draws) is a button: a click opens a viewer over the pane. The viewer shows the image's name, its place among the chat's images ("2 of 3"), Copy and Close.

  • Zoom works with a pinch or Command-scroll about the pointer, a double click (fit and 2x), or +, - and 0. A zoomed image pans by dragging or scrolling.
  • The arrows (buttons and arrow keys) go through every image in the chat, oldest first, each source once. Images inside code are skipped, as are data URLs over the existing 2 MB cap and images past a reply's 8 MB budget.
  • Copy puts the image on the clipboard as PNG. An SVG is drawn to a canvas first.
  • Escape, Close, or a click beside the fitted image closes the viewer, and focus goes back to the image that opened it. Switching chats closes it too.

This is step 3 of the in-thread visuals work (cx-ncc.39). Reveal in Finder and open in a browser pane need file-backed images and host ops, so they come later with the per-chat gallery. References from other apps are in cmux-app-screenshots references/visual-artifacts-2026-10-06/.

The pane mock (CMUX_NEXT_AGENT_PANE_MOCK=1) gives "Make sample images for the docs" three neutral images: a chart, a gradient and a diagram.

Testing

  • bun test test/ui-rules.test.ts test/gallery-coverage.test.ts test/gallery-env.test.ts test/gallery-theme.test.ts test/pane-english.test.ts src/agent-session/acpmux in webviews/: 1130 pass, 2 fail. Both failures are files from feat-cmux-next, not this PR: optchat-inspector/* (ui-rules and gallery coverage) and gallery/frame/liveErrors.ts (ui-rules).
  • New: conversation/imageViewer.test.tsx. It covers:
    • which images a chat has (order, dedupe, code and oversized skipped), exactly as the reply draws them
    • long data URLs, which the reply swaps for short refs, are listed; images past the reply's 8 MB budget are not
    • the zoom math (the point under the pointer stays put, the range is clamped, fitted recenters)
    • an inline image opening the viewer, and staying a plain image where there is no viewer
    • the viewer's name, position, arrows and Escape; it is modal and starts on Close
    • the image keys still work once focus moves to the dialog itself, and Control-scroll zooms
    • one image showing no arrows, with + and 0 zooming
  • Red then green: the data URL refs and budget (c4c5fabc216, 302a63a8be9), and the keys and pinch inside ui/Dialog (896ae91af76, 0ef1688351b).
  • bun run typecheck, vp lint and vp fmt --check are clean, and scripts/cmux-next/build-agent-pane-web.sh --check reports the bundle current.
  • CI: web / react-apps-check fails on the diff viewer's JavaScript budget (1549515 bytes against 1500000). feat-cmux-next's own committed bundle fails with the same bytes, and this PR doesn't touch it.
  • Not run: no app build or mini capture, per the web-UI rule. The clipboard copy has no automated test (WebKit clipboard needs a user gesture).

Changelog

Added: Click an image in a reply to open it in a viewer with zoom, arrows through the chat's images and copy

Proof

Before (feat-cmux-next): an image in a reply is a plain <img>, with nothing to click. Base gallery: /live/?entry=agent-pane.reply-image#/agent-pane.reply-image/click-to-load

After: gallery entry agent-pane.image-viewer (new in this PR), which draws the real pane on the gallery's mock host. Each image is a button that opens the viewer. Live branch preview on the team gallery host, which follows this branch (its sync is stuck on a deleted branch, so it shows this branch before the latest base merge; the viewer is unchanged since):

  • /wt/feat-cmux-next-image-viewer/?entry=agent-pane.image-viewer#/agent-pane.image-viewer/reply-images: a reply with three images, each a button that opens the viewer
  • /wt/feat-cmux-next-image-viewer/?entry=agent-pane.image-viewer#/agent-pane.image-viewer/across-turns: images in two replies, which the viewer's arrows go through

The opened viewer needs a click, so its states (open, next, zoomed) arrive as play steps with the next base merge.

Checklist

  • Behavior changes have added or updated tests, or Testing says why not
  • UI, settings, menu, schema, help-text or user-facing docs change: localization audited. Nine new pane strings (image.*) are in all 21 locales, with non-English marked needs_review like the pane's other strings.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.


Summary by cubic

Adds an image viewer to the agent pane: clicking a reply image opens it full-pane with zoom, pan, navigation, copy, and keyboard support.

The viewer shows the image name, its position among the chat's images ("2 of 3"), and Copy and Close buttons. Zoom is anchored to the pointer and works via pinch, Command-scroll, double click (fit and 2x), plus +, -, and 0; zoomed images pan by dragging or scrolling. Arrows and arrow keys step through every image in the chat, oldest first, each source once, skipping images inside code blocks and data URLs over the 2 MB cap. Copy places the image on the clipboard as PNG; SVGs are drawn to a canvas first.

The viewer is built on the shared ui/Dialog (Base UI), so it traps focus, makes the pane behind inert, closes on Escape, and returns focus to the source image on close; switching chats closes it too. The image list comes from Markdown.tsx's own parser so it matches exactly what the reply draws. The gallery entry agent-pane.image-viewer covers a reply with three images and images across two replies, with the opened viewer (open, next, zoomed) as play steps and anchors that prove nothing behind the viewer moves.

This branch also carries unrelated merges from feat-cmux-next (cmux VM agent tools, a mesh M2 proof, and a merge that drops the committed web bundles and keeps the string catalog in base's format); none is part of this PR's work.


[!NOTE]
Low Risk
Agent-pane UI and clipboard helpers only; no auth, persistence, or host protocol changes beyond existing markdown image handling.

Overview
Adds a full-pane image viewer for inline data-URL images in assistant replies. Clicking an image in the transcript (now wrapped in a button when the viewer is available) opens a modal overlay with title, position in the chat gallery, copy-to-clipboard as PNG, and prev/next across all deduplicated images collected from the session.

chatImages and exported inlineImages mirror Markdown rendering rules (skip fenced code, budgets, footnotes) so navigation matches what users actually see. App.tsx holds viewer state, provides ImageViewerContext (disabled in Quick Composer), and clears the viewer on session change.

The ImageViewer supports zoom/pan (pinch, Ctrl/Cmd-scroll, double-click, +/-/0), keyboard arrows, and extends Dialog with onKeyDown for those shortcuts. Styling, icons, gallery fixtures, mock session sample images, i18n keys, and imageViewer.test.tsx accompany the feature.

Reviewed by Cursor Bugbot for commit f3a58a00998b9453f2764c613afea7a73b81ce16. Bugbot is set up for automated code reviews on this repo. Configure here.