Skip to content

fix(composer): say when a pasted image cannot go to this agent (#1250 row 7) - #1426

Merged
Juliusolsson05 merged 5 commits into
mainfrom
fix/image-paste-unsupported-provider
Sep 27, 2026
Merged

Juliusolsson05 merged 5 commits into
mainfrom
fix/image-paste-unsupported-provider

Conversation

@Juliusolsson05

@Juliusolsson05 Juliusolsson05 commented Sep 27, 2026 •

Copy link
Copy Markdown
Owner

Refs #1250 (row 7). #1250 is a batch issue, so the other rows stay open.

Problem

Only Claude takes pasted images (supportsImageAttachments). In a Codex, OpenCode, Grok or Pi composer, pasting a screenshot inserted nothing and said nothing. useClaudeImagePaste.handlePaste returned { handledImages: false } at once, and the textarea's default paste of an image-only clipboard inserts no text.

Change

  • For a provider whose composer cannot take images, handlePaste shows a pane toast, "Pasted images can't be sent to from this composer.", when the paste carries:
    • an image file item (with text too, the text still pastes and the toast says "…only the text was pasted.");
    • a data-URL <img> in text/html with no text;
    • or, with nothing else in the event, an image only navigator.clipboard.read() shows.
  • Paste-to-focus (a paste landing outside the textarea) hands an EMPTY event (no text, HTML or items) to the same handler, so an async-only image is said there too, and Claude can attach it. Ordinary text is never routed there.
  • A web-page copy (<img> beside its text) and plain text stay silent. The wording names this composer because Grok's own TUI (Terminal view) attaches clipboard images.
  • It still returns handledImages: false, so text routing (the textarea and usePasteToFocus) is unchanged.

Plan: docs/plans/2026-09-27-image-paste-unsupported-provider.md.

Tests

New useClaudeImagePaste.renderer.test.tsx: the real hook and real capabilities, with the RECORDED PNG from testing/fixtures/image-reads/claude-user-attachment.json as the pasted file (the clipboard event is the one stubbed edge).

  • Codex, OpenCode, Grok and Pi, image only: each names its provider (red on main), with handledImages: false.
  • Image plus text: the "only the text was pasted" toast. HTML-only image: the toast. Async-only image: the toast.
  • Plain text and a web-page copy: no toast. A non-image file, a string item, and no clipboard data: no toast. Claude, image only: no such toast, and the image is taken.
  • usePasteToFocus.asyncImage.renderer.test.tsx: an empty paste reaches the handler; text never does.

Verification

  • rm -rf .tsc-out; npx tsc -b: clean.
  • NODE_ENV=test npx vitest run src/renderer/src/workspace/tile-tree/TileLeaf: 201/201.
  • Review round 1: the disposition is in the PR comments.
  • The app was not launched.

🤖 Generated with Claude Code

Juliusolsson05 and others added 2 commits September 27, 2026 05:58
… row 7)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…row 7)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Juliusolsson05 Juliusolsson05 added type:bug Something works wrong class:C3-silent-failure The app knows it failed and does not say sev:P3 Minor labels Sep 27, 2026
…-only pastes; composer wording (#1426 review)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Juliusolsson05

Copy link
Copy Markdown
Owner Author

Review disposition, round 1: reviewers a and b (head 3d5b768f)

Verdicts: a FIX-BEFORE-MERGE, b FIX-BEFORE-MERGE. c's report is pending.

Finding Disposition
b1: an image file plus text dropped the image silently ("see attached" with no attachment) Fixed: the text still pastes, and the toast says "…only the text was pasted." Only an HTML <img> beside text (a web-page copy) stays silent.
a2 / b2: an image arriving only as a data-URL <img> in text/html was silent Fixed: it counts when there is no text.
a3: an image only the async clipboard API shows was silent Fixed: with nothing else in the event, readImagesFromClipboard is probed. Paste-to-focus's documented miss of this shape is unchanged.
b3: the wording blamed the provider (Grok's own TUI takes images) Fixed: "Pasted images can't be sent to from this composer."
a4: provider wording was untested Fixed: Codex, OpenCode, Grok and Pi are each asserted.
a1: Terminal-view panes stay silent Declined. In Terminal view the paste goes to the provider's own TUI, which owns image handling (Grok's attaches clipboard images). The app's capability flag describes its composer, not the TUI.

Also: parseImagesFromHtml uses querySelectorAll('img') instead of doc.images. It is identical in a browser, and the happy-dom test DOM lacks images on a parsed document.

Verification:

  • npx tsc -b: clean.
  • workspace/tile-tree/TileLeaf: 198/198.
  • Seven of the nine new cases are red on the previous head; the two silent cases (plain text, web-page copy) pass there by design.

🤖 Generated with Claude Code

…ke (#1426 review c)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Juliusolsson05

Copy link
Copy Markdown
Owner Author

Reviewer c (MERGE-READY, on the round-0 head) and verification b (MERGE-READY, no findings) at 5b030704. c's test gaps are pinned. A non-image file, a type-less file, a string item typed image/*, and a null clipboardData all stay silent, which kills M1, M2 and M5. The Claude case now asserts the image is taken. c's M7 (hard-coded label) was already killed by the per-provider cases. The plan's Claude wording is corrected. Verification a is still running.

🤖 Generated with Claude Code

…er (#1426 verification a)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Juliusolsson05

Copy link
Copy Markdown
Owner Author

Verification a: FIX-BEFORE-MERGE, fixed in c32d4bae. An async-only image pasted through paste-to-focus (focus off the textarea) stayed silent, because that path decided "no image" synchronously. An EMPTY paste event (no text, no HTML, no items) now goes to the shared handler, which probes the async clipboard; this also lets Claude attach such an image. Ordinary text never takes the branch, and a test pins that it never reaches the handler. The new test is red on the previous head. TileLeaf suite: 201/201; tsc is clean.

Round count: round 1 and verification are done, so the review is capped. c and verify b were MERGE-READY. The PR waits for CI and the manager check.

🤖 Generated with Claude Code

@Juliusolsson05

Copy link
Copy Markdown
Owner Author

Manager verification at c32d4bae: MERGE-READY; the a1 decline is accepted. Residuals:

  • The .catch(() => []) around readImagesFromClipboard in the unsupported-composer branch is not pinned: removing it survives the suite. With it gone, a rejected async clipboard read would surface as an unhandled rejection instead of a silent no-op. It is left as a residual rather than moving the verified head.
  • The native image behaviour of each provider TUI (Terminal view) is not checked here. Grok's is documented as attaching clipboard images.

The head is unchanged, so READY follows exact-head CI and merge-gate.sh --member.

🤖 Generated with Claude Code

@Juliusolsson05
Juliusolsson05 merged commit 9895a9e into main Sep 27, 2026
2 checks passed
@Juliusolsson05
Juliusolsson05 deleted the fix/image-paste-unsupported-provider branch September 27, 2026 15:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

class:C3-silent-failure The app knows it failed and does not say sev:P3 Minor type:bug Something works wrong

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant