Skip to content

fix: clipboard image paste on Linux (WebKitGTK)#12909

Closed
ryan-mt wants to merge 3 commits into
anomalyco:devfrom
ryan-mt:fix/clipboard-image-paste-linux
Closed

fix: clipboard image paste on Linux (WebKitGTK)#12909
ryan-mt wants to merge 3 commits into
anomalyco:devfrom
ryan-mt:fix/clipboard-image-paste-linux

Conversation

@ryan-mt

@ryan-mt ryan-mt commented Feb 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Fix clipboard image paste not working on Linux desktop (WebKitGTK)
  • Fix unregistered tools (MCP tools, background tasks) showing no expandable output
  • Fix edit tool diff not rendering: correct filediff.path → filediff.file (matches FileDiff schema)
  • Add ErrorBoundary + text fallback around diff component for when @pierre/diffs fails
  • Add try-catch with console.warn in Diff component render effect
  • Fix task agent showing empty content when child session data is unavailable — show output text as fallback
  • Add error logging for clipboard read failures to aid debugging

Details

Clipboard paste (Linux/WebKitGTK)

WebKitGTK Bug #218519: clipboardData.items returns empty for images. The native readClipboardImage() fallback was gated behind !plainText, preventing it from running when clipboard also contained text (common with screenshot tools). Moved native clipboard attempt before plainText check.

Edit tool diff

  • <Show when={props.metadata.filediff?.path}> referenced nonexistent path field — FileDiff schema uses file. Fixed to filediff?.file.
  • Added ErrorBoundary around the Dynamic diff component with a text-based fallback when the shadow DOM / web worker based diff rendering fails.
  • Added try-catch in the Diff component's createEffect render loop so FileDiff.render() failures don't silently swallow errors.

Task agent empty state

Task tool always rendered wrapper divs even when childToolParts() was empty (child session data not in store). Now conditionally renders: shows tool list when parts exist, falls back to props.output text when empty.

GenericTool output

GenericTool (fallback for unregistered tools) was ignoring the output prop entirely. Now passes through output, hideDetails, defaultOpen, forceOpen, locked and renders output in a collapsible pre block.

Test plan

  • Screenshot tool (Spectacle/Flameshot) → copy image → Ctrl+V in prompt → image attaches
  • Copy text with image in clipboard → Ctrl+V → image attaches (not just text)
  • Edit tool shows diff content when expanded
  • Task agent shows output when child session data is unavailable
  • MCP/unregistered tools show expandable output
  • Verify no TypeScript errors across all packages

WebKitGTK does not expose clipboard images via DataTransfer API
(WebKit Bug #218519), so the browser path never finds image items.

Previously, the native clipboard fallback (readClipboardImage) was
only attempted when no plain text existed in the clipboard. This
caused image paste to fail when the clipboard contained both image
and text data (common with screenshot tools like Spectacle that
include file paths, or when copying images from browsers).

Changes:
- Move native clipboard read before plain text check so images
  always take priority over text on desktop (consistent with how
  Chromium handles clipboard images via DataTransfer)
- Add try-catch around readClipboardImage call to prevent silent
  crashes from propagating
- Add console.warn logging for each failure point in the RGBA-to-PNG
  conversion pipeline to aid debugging on affected systems
Copilot AI review requested due to automatic review settings February 10, 2026 03:19
@github-actions

Copy link
Copy Markdown
Contributor

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

@github-actions

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

No duplicate PRs found

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes Linux desktop (WebKitGTK) clipboard image paste by prioritizing a native clipboard image read path before falling back to plain text, and adds additional logging around the desktop RGBA→PNG conversion path to aid debugging.

Changes:

  • Update paste handling to always attempt readClipboardImage() on desktop before inserting text from the clipboard.
  • Add warning logs and additional error handling around the desktop clipboard image read + canvas conversion pipeline.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
packages/desktop/src/index.tsx Adds warning logs and a try/catch around canvas conversion when building a PNG File from native clipboard RGBA bytes.
packages/app/src/components/prompt-input/attachments.ts Changes paste logic to attempt native clipboard image reads first (even when text is present), with fallback to plain text.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +367 to +370
const image = await readImage().catch((e) => {
console.warn("[clipboard] readImage failed:", e)
return null
})

Copilot AI Feb 10, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

readImage() failures were previously swallowed (returned null) and are now always console.warned. Since readClipboardImage is now attempted even when pasting plain text, this risks producing warnings in the common/expected “clipboard has no image” case and can spam logs. Consider filtering/suppressing known expected errors (e.g., “no image available”), gating these warnings behind import.meta.env.DEV, or downgrading to a debug-level logger for expected failures.

Copilot uses AI. Check for mistakes.
Comment on lines +388 to +390
const imageData = ctx.createImageData(size.width, size.height)
imageData.data.set(bytes)
ctx.putImageData(imageData, 0, 0)

Copilot AI Feb 10, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before calling imageData.data.set(bytes), consider validating that bytes.length === size.width * size.height * 4 and returning null (with a log) when it doesn’t match. Relying on a thrown RangeError for control flow makes this path harder to reason about and produces less actionable diagnostics (you lose the expected vs actual lengths).

Copilot uses AI. Check for mistakes.
Comment on lines +79 to +89
// Desktop: try native clipboard for images before falling back to text.
// WebKitGTK does not expose clipboard images via DataTransfer (WebKit Bug #218519),
// so the browser path above will miss them. Always attempt the native read here
// regardless of whether text is also present in the clipboard.
if (input.readClipboardImage) {
try {
const file = await input.readClipboardImage()
if (file) {
await addImageAttachment(file)
return
}

Copilot AI Feb 10, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change introduces new paste-precedence behavior (always attempt native clipboard image read before falling back to text, even when text is present). There are bun:test unit tests in this directory, but none covering handlePaste; please add tests for (1) native image success when clipboard also has text, and (2) native read failure falling back to plain text.

Copilot uses AI. Check for mistakes.
Test User added 2 commits February 9, 2026 19:33
GenericTool was ignoring the output prop entirely, so any tool not
in the registry (MCP tools, background_output, custom tools) would
show only the tool name with no expandable content.

Update GenericTool to:
- Accept output, defaultOpen, forceOpen, and locked props
- Pass output as collapsible children to BasicTool
- Render output in a scrollable pre-formatted text block
- Use the same data-component="tool-output" as registered tools

This makes the expand arrow appear when output exists, and clicking
it reveals the tool's output content.
- Fix edit tool Show condition: filediff.path → filediff.file (matches
  FileDiff schema which uses 'file' not 'path')
- Add ErrorBoundary around Diff component in edit tool with text fallback
  when @pierre/diffs fails to render (shadow DOM/worker issues)
- Add try-catch in Diff component's render effect with console.warn and
  basic text fallback on failure
- Fix task tool empty content: only render tool list wrapper when
  childToolParts has items, show props.output as fallback text when
  child session data is unavailable
- Import ErrorBoundary from solid-js
@ryan-mt ryan-mt closed this by deleting the head repository Feb 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants