Skip to content

fix: hold a paste or file drop until the window's frame returns - #637

Open
leftos wants to merge 4 commits into
penso:mainfrom
leftos:fix/hold-paste-until-frame
Open

leftos wants to merge 4 commits into
penso:mainfrom
leftos:fix/hold-paste-until-frame

Conversation

@leftos

@leftos leftos commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

🤖 Posted by Claude Code on behalf of @leftos.

Summary

Follow-up to #582. That PR's description covers this change, but the commit reached its branch after the merge, so it never landed on main.

A window whose frame does not match its size (another client sized the tab) refuses input until its own frame returns. #582 made a paste or a file drop take the tab back. But only typed keys were held while the window waited, so the first paste or drop in an idle window was thrown away and the user had to repeat it.

Keys, pastes, and drops now share one hold queue (window/pending_input.rs) and are delivered in the order the user made them once the frame matches.

  • A held paste keeps only the action and reads the clipboard when it is delivered. No image is held in memory, and the clipboard is not read on the UI thread.
  • A file drop is held for the pane under the pointer, or, when no frame is shown, for the drop point, which is hit-tested once a frame arrives.
  • Ctrl-V on a remote pane is held the same way.
  • A held paste or drop whose clipboard is read, or whose image is prepared, in the background stops delivery until that work ends. Input held after it waits, and so do keys, pastes and drops made in the meantime, which join the back of the queue. Once the paste is queued, that input is sent in order; if the preparation fails, times out, is cancelled, or turns into a remote file copy, it is discarded with its count. A held Enter never runs a command without the image pasted before it.
  • A paste or drop refused at the hold cap discards only the input held during the gap, so input typed after the frame returns follows the background pastes on every platform.
  • Held pastes and drops follow the rules of held keys: they are dropped with an error if focus moves first, cleared on reconnect, and capped at four per wait, past which they are refused with an error. A held send that fails says how many held inputs it discarded.
  • Nothing held after a failure is sent. Every held delivery returns a typed result (Delivery: Queued, Copying, Refused). Anything but Queued ends the batch, and the input held after it is discarded with its count, so a held Enter never runs a command without what was pasted or dropped before it. That covers:
    • a refused paste or drop (bad paths, image busy, another copy running, a reservation refused);
    • a drop or file paste that starts a remote file copy, whose paths arrive only when the copy completes ("Input not sent: the files are still copying (N held inputs discarded)");
    • a fifth paste or drop refused at the cap;
    • a held paste delivered while a menu is open or a focus click is pending ("Paste not sent: a menu is open").
  • On macOS a held paste always reads the clipboard in the background reader. A live paste or drop sends held input before itself. An empty drop does nothing.

Known limitation: on macOS, Cmd-V of files copied in Finder onto a remote pane resolves in the background after its slot is reserved, so a copy it starts cannot stop keys already delivered behind it.

Validation

At f81762f, on main at 7c64479:

  • check-commits (upstream/main..HEAD): passed
  • just ci on Windows 11 x86_64: format-check, check-file-size, check-sorted, lint (all features and no default features), test (default and all features; herdr-gpui 3709 and 3752 passed, 0 failed) passed. test-mockup-wait failed: that is the Windows-only failure in test_wait.py, and this PR touches nothing it covers.
  • WSL Ubuntu, Linux x86_64. This covers the cfg(unix) lifecycle tests, held_paste among them, which Windows does not build.
    • cargo clippy --workspace --all-targets -D warnings, all features and no default features: passed (all features re-run after the last change).
    • cargo test --workspace, default and all features, before the last change: the only failure was orchestrator::tests::service::the_worker_shows_runs_another_application_writes, a snapshot wait that timed out under load in code this PR does not touch; it passes run alone. After the last change, window::pending_input and lifecycle_tests::held_paste: 26 passed, 0 failed.
  • macOS 27.0.1, Apple silicon: cargo test -p herdr-gpui over window::pending_input, lifecycle_tests::held_paste, window::images and window::file_drop: 36 passed, 0 failed. This found the cap-mark case above before the last change.
  • The four window/pending_input/tests/background.rs tests for a held input behind a preparing paste, and the one for input after a refused cap mark, failed before their changes.
  • The eight endpoint::lifecycle_tests::held_paste tests failed before the change (nothing was held).
  • window/pending_input/tests.rs builds and runs on Windows too. Its tests failed before their fixes: a key held after a refused drop, a key held after a remote copy, a paste refused while a menu is open, a fifth paste refused at the cap, a live drop and a live paste after held keys, and live-drop focus. The red runs were shown by mutation for the fixes written before their tests.

Manual QA

  • Two machines on one tab: let one window go idle and resize the tab from the other machine, then paste text into the idle window. The tab comes back and the text arrives once, after the resize. Repeat with an image paste and with a file dropped on a pane.
  • Same setup on an SSH pane: type cat , drop two files, press Enter, then let the tab come back. The paths are not followed by a stray Enter, and the status says the held input was discarded while the files copy.
  • macOS: the same paste on a frame another client sized goes through the native background clipboard reader once delivered. No automated run covers that path: WSL takes the Linux path, and Windows does not build the lifecycle tests.

@leftos

leftos commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor Author

🤖 Posted by Claude Code on behalf of @leftos.

Local just ci checks at 602a976, which adds one test fix to the merge of main at 0ca50aa.

Windows 11 x86_64:

  • check-commits (upstream/main..HEAD): passed
  • format-check: passed
  • check-file-size: passed
  • check-sorted: passed
  • lint, all features and no default features: passed
  • test, default features and all features: passed, 0 failed in all 46 test binaries
  • test-mockup-wait: failed. This is the Windows-only failure in test_wait.py (test: run the mockup wait tests on Windows #643), and this PR touches nothing it covers.

WSL (Linux x86_64) at 602a976:

  • clippy, all features: passed
  • endpoint::lifecycle_tests: 125 passed, including every held_paste test

602a976 fixes the six held_paste failures on the Linux and macOS CI jobs. Since 1c6f35d on main, a snapshot that keeps the focus and changes only metadata no longer holds input, so the tests that opened their gap that way waited for a hold that never came. Their gap snapshot now also republishes the host's key bindings, which still holds input until the matching surface arrives.

Not run at 602a976: WSL clippy with no default features, and macOS. Before the merge, at f81762f, macOS window::pending_input, lifecycle_tests::held_paste, window::images and window::file_drop passed (36 tests).

The Windows native GUI jobs fail on main too (0ca50aa and later): the native_sidebar paint probe measures the child label at 179px or 127px against an expected 161px. This PR does not touch the sidebar.

The merge needed two fits to main: the screenshot-thumbnail branch upstream added to paste_files (7aeae2d) returns its Delivery, and the WSL drop tests compare paste_files against Delivery::Refused instead of reading it as a bool.

@greptile-apps

greptile-apps Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 4/5

[Medium impact] Changes how keyboard, paste, and file drop input queue during frame synchronization gaps.

Fix the background paste failure path before merging so a held Enter cannot run without the paste.

Findings

  1. P1 Enter follows a failed paste ▶

Summary

Keys, pastes, and file drops now share a queue while the window waits for its frame.

  • Pastes and keys keep their order while a frame is missing.
  • File drops wait for a frame and keep their intended pane.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Key, paste, or file drop] --> B{Frame ready?}
    B -->|No| C[Hold input in order]
    B -->|Yes| D[Flush held input]
    C --> D
    D --> E{Delivery result}
    E -->|Queued| F[Deliver next held input]
    E -->|Copying or refused| G[Discard remaining held input]
    F --> H[Background paste can still fail]
Loading

Reviews (2) · Last reviewed commit: "fix: hold a paste or file drop until the..." · Reviewed by Greptile

Comment thread crates/herdr-gpui/src/window/file_drop.rs Outdated
Comment thread crates/herdr-gpui/src/window/pending_input.rs Outdated
@leftos
leftos force-pushed the fix/hold-paste-until-frame branch from df12b69 to f8e299c Compare October 17, 2026 15:46
leftos added a commit to leftos/herdr-gpui that referenced this pull request Oct 17, 2026
Brings leftos/patches up to PR penso#637 at f8e299c: the review round on
"hold a paste or file drop until the window's frame returns". A held
paste or drop that is refused, starts a remote file copy, or is refused
at the cap ends the batch, and the input held after it is discarded with
its count. A held paste delivered while a menu is open is refused with a
reason, and a live paste or drop sends held input before itself.
leftos added a commit to leftos/herdr-gpui that referenced this pull request Oct 17, 2026
The plain Ctrl+V branch from PR penso#555 called paste_terminal_clipboard,
which PR penso#637's review round made test-only. Call paste_item, the
function it wrapped; the branch ignored its result either way.
@leftos
leftos force-pushed the fix/hold-paste-until-frame branch from f8e299c to 338e4f1 Compare October 17, 2026 17:03
@leftos
leftos marked this pull request as ready for review October 17, 2026 17:06
@leftos
leftos requested a review from penso as a code owner October 17, 2026 17:06
Comment thread crates/herdr-gpui/src/window/images.rs Outdated
preparing: true,
_task: task,
});
Delivery::Queued

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Enter follows a failed paste

When a user holds an image paste followed by Enter, start_image_preparation returns Delivery::Queued before the background work finishes. flush_pending_input then releases Enter. If preparation fails—for example, a clipboard GIF exceeds the upload limit—the connection skips the empty paste slot and sends Enter without the image.

Keep the remaining held input pending until preparation succeeds, and discard it if preparation fails.

Knowledge Base Used: Input, clipboard, and port forwarding

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

🤖 Posted by Claude Code on behalf of @leftos.

Fixed in f81762f. A held delivery that prepares in the background now returns Delivery::Preparing, which stops the flush and keeps the input behind it held. When the paste is queued, that input is flushed in order under the usual frame, focus and menu rules. When preparation fails, times out, is cancelled, or becomes a remote file copy, it is discarded with its count. Input made while waiting joins the back of the queue. window/pending_input/tests/background.rs covers each case, including an SVG image the bridge refuses, and each test failed before the change.

The previous commit let a paste or a file drop take a tab back from
another window, but what was pasted or dropped was still thrown away: a
window whose frame does not match its size refuses input, and only typed
keys were held for later. The first paste or drop in an idle window was
lost, and the user had to repeat it.

Keys, pastes, and drops now share one hold queue, so they are delivered
in the order the user made them once the frame matches. A held paste
keeps only the action and reads the clipboard when it is delivered, so
no image is kept in memory and nothing is read on the UI thread. A file
drop is held for the pane under the pointer, or for the drop point when
no frame is shown, which is hit-tested once one arrives. Ctrl-V on a
remote pane is held the same way.

Held pastes and drops follow the rules of held keys: they are dropped
with an error if focus moves first, cleared on reconnect, and capped at
four per gap, past which they are refused with an error. A held send
that fails now says how many held inputs it discarded.

A held paste or drop that is refused, or one that starts a remote file
copy whose paths arrive only once it completes, ends the batch: the
input held after it is discarded with the count, so a held Enter never
runs a command without what was pasted before it. A fifth paste or
drop refused at the cap ends the batch the same way. A held paste
delivered while a menu is open or a focus click is pending is refused
with a reason, and on macOS a held paste always reads the clipboard in
the background. A live paste or drop sends held input before itself.
@leftos
leftos force-pushed the fix/hold-paste-until-frame branch from 338e4f1 to f81762f Compare October 17, 2026 19:07
leftos added a commit to leftos/herdr-gpui that referenced this pull request Oct 17, 2026
…eturns)

# Conflicts:
#	crates/herdr-gpui/src/window/file_drop.rs
#	crates/herdr-gpui/src/window/pending_input.rs
leftos added a commit to leftos/herdr-gpui that referenced this pull request Oct 17, 2026
Reverts a29bbc0. Joining upstream's WSL drop hold with penso#637's held-input
queue is left to the maintainers; the build keeps the penso#637 merge's minimal
compile fix (a WSL drop reports Queued).
@penso penso added bug Something isn't working P1 High priority: crashes, blocked core workflows, or significant input regressions labels Oct 19, 2026
…l-frame

# Conflicts:
#	crates/herdr-gpui/src/window/file_drop.rs
#	crates/herdr-gpui/src/window/pending_input.rs
The merge of upstream main brought tests that still read paste_files as
a bool; it now returns Delivery, so they compare against Refused.
Since the metadata-update change on main, a newer snapshot that keeps
the same focus and changes only metadata keeps input flowing, so the
held-paste tests that opened their gap that way never saw input pause
and timed out on every Unix CI job. The gap's snapshot now also
republishes the host's key bindings, which still holds input until the
matching surface arrives.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working P1 High priority: crashes, blocked core workflows, or significant input regressions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants