Skip to content

feat(terminal): paste clipboard images into SSH sessions as a file path (#630) - #639

Merged
kipavy merged 1 commit into
devfrom
feat/terminal-image-paste
Oct 14, 2026
Merged

kipavy merged 1 commit into
devfrom
feat/terminal-image-paste

Conversation

@kipavy

@kipavy kipavy commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

Takes over #630 by @ghecko with the changes from its review. His setting, translations, shared paste helper and terminal hookup are kept, and the squash commit credits him as co-author.

What

When the clipboard holds an image and no text, pasting into an SSH session uploads the image to the host and types its path, followed by a space. Paste works with Ctrl+V, Ctrl+Shift+V, right-click, and now also Cmd+V on macOS. Tools on the host such as Claude Code then attach it.

  • Uploads that take more than 250 ms show a bottom-right progress toast with a thumbnail, host, size, dimensions, percent and Cancel.
  • On success the toast shows the file and offers "Delete from host". The very first paste says instead that the image is on the host for 7 days, readable only by you, and links to the setting.
  • On failure the toast names the host and the classified error (e.g. "prod-api: Permission denied") and offers Retry, which re-sends the same image.
  • Pastes queue per session, so two quick pastes type their paths in order.
  • Nothing uploads when the session doesn't accept input (e.g. control handed to a guest). While input is broadcast to several panes, paste refuses with a toast.
  • The setting description now notes that the path points to the first host if you SSH further.

How

  • paste_image_read reads the clipboard in Rust, encodes PNG with png (already in the tree) and returns the size, dimensions and a small thumbnail. It keeps the last four images by id for Retry. No webview clipboard-image permission is needed.
  • paste_image_upload opens SFTP on the session's existing connection. It ensures ~/.cache/voltius/pastes exists and is owner-only, and prunes pastes older than 7 days. It writes <name>.part in 64 KB chunks with sftp-progress-<id> events and a cancel token, then renames to the final name. A failed or cancelled write deletes the .part file.
  • Progress toasts can now carry a thumbnail, a detail line and an action once finished. A finished one keeps its own duration instead of the fixed 2 s.
  • sessionAcceptsInput is shared with writeToSession.
  • Compared with feat(terminal): paste clipboard images into SSH sessions as a file path #630, this drops sftp_write_bytes and its header plumbing, the FileBackend::write_file(&[u8]) change, the canvas re-encode, and clipboard-manager:allow-read-image.

Testing

  • Rust: commands::sftp::paste tests cover the file name, prune rules, thumbnail aspect and Retry memory. cargo clippy --workspace --all-targets -- -D warnings is clean.
  • Frontend: terminalImagePaste.test.ts covers no image, toggle off, no input, broadcast, fast and slow uploads, progress, Cancel, first use, failure with Retry, and queue order. terminalClipboard.test.ts adds macOS Cmd+V. tsc is clean. The full vitest sweep (802 files) passed apart from two known 5 s timeouts under load, which pass when run on their own.
  • In the headless app against an OpenSSH host, with real clipboard images:
    • First and later pastes.
    • Delete from host (file gone).
    • A 3840×2360 screenshot over a link throttled to 2 Mbit/s, with live progress and Cancel at 32% (no file or .part left).
    • Upload failure with Retry.
    • Two pastes in a row.
  • Known limit: the X11 clipboard read gives up after about 4 s on huge transfers (a 13 MB noise PNG). Real screenshots, including 4K at 2 MB, read fine.

…th (#630)

A program on the SSH host, such as Claude Code, can't reach this machine's
clipboard. Pasting an image (Ctrl+V, Ctrl+Shift+V, right-click, or Cmd+V
on macOS) when the clipboard holds no text now uploads it next to the
session's shell and types its path, followed by a space.

paste_image_read takes the image from the clipboard in Rust, encodes it
as PNG and keeps the last few by id, so Retry sends the same picture even
if the clipboard changed. paste_image_upload opens SFTP on the session's
own SSH connection, writes ~/.cache/voltius/pastes/voltius-paste-<time>.png
as a .part file with sftp-progress events and a cancel token, renames it
when complete, makes the folder owner-only and prunes pastes older than
7 days.

Uploads over 250 ms show a bottom-right progress toast with a thumbnail,
host, size, dimensions and Cancel. Success shows the file with "Delete
from host"; the first paste instead says where the image went and links
to the setting. Failures name the host with the classified error and
offer Retry. Pastes queue per session so paths land in paste order.
Nothing uploads when the session doesn't accept input, and broadcast
input refuses with a toast.

Progress toasts can now carry a thumbnail, a detail line and an action
once finished, and a finished one keeps its own duration.

Built on ghecko's PR #630: the setting, its translations, the shared
paste helper and the terminal hookup are his.

Co-authored-by: ghecko <13104020+ghecko@users.noreply.github.com>
@kipavy
kipavy merged commit 9201604 into dev Oct 14, 2026
5 checks passed
@kipavy
kipavy deleted the feat/terminal-image-paste branch October 14, 2026 14:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant