Copy off a terminal with a finger - #68
Open
karngyan wants to merge 2 commits into
Open
Conversation
A phone could read the terminal and never lift anything out of it. The line telling you to run `claude --resume <id>` was on screen and there was no way to get the id into the prompt below it. Nothing was broken. The screen is painted to a canvas, so there is no text on the page for the operating system to offer Copy over, and `user-select` is off across xterm besides — the browser has no idea the glyphs it drew are characters. Only flue knows that, so only flue can offer it. A long press selects the word under the finger and a drag widens the range, either direction, across as many rows as the finger travels; a menu offers Copy, Paste and Cancel at whichever end of the terminal the hand is not covering. Words break on whitespace alone, which is not what a text editor would do and is what a terminal wants: paths, URLs, hashes and session ids are made of the punctuation an editor breaks on, and a rule that stopped at the hyphen would turn a UUID into six presses. The range is xterm's own — `select` takes a length that wraps past the end of a row, which is the only way the public API can express a multi-row range, and it means the renderer draws the selection without flue owning a pixel of it. What flue owns is the gesture: the press has to be told apart from the scroll that shares the surface with it, so a finger that travels more than the slop before the hold elapses is a scroll, and once a selection begins the scroll path is abandoned for the rest of the gesture. Two smaller things fall out of it. The compatibility mousedown behind a press that never moved is swallowed, because xterm answers a mousedown by starting a selection of its own and would clear this one a frame after making it. And the transparent textarea from the release before this one is gone — it was there to give a long press some editable text to land on, and it was also what drew a focus ring around the whole terminal and risked iOS zooming the page on every tap. flue owns the press now, so the platform is told to keep its callout to itself. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The press only opened the menu when it found a word, so the one place a paste is actually wanted — the empty prompt somebody is trying to paste into — was the one place nothing happened. Copy is the only verb in that menu that needs a selection. Paste never did, and hiding both behind one is what left the terminal with no way to paste into it at all. So the press always opens the menu now, and Copy is left out rather than greyed when there is nothing under the finger to copy: a press on blank space is a press asking to paste, and the shortest menu that answers it is the right one. Whichever verb the press was asking for is the one that reads as loud. A press on blank space also leaves the scroll alone. There is no range for a drag to widen, so the scrollback is the only thing left for the finger to do. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The gap
A phone could read the terminal and never lift anything out of it. The line saying to run
claude --resume <id>was on screen with no way to get the id into the prompt below it.Nothing was broken, and no control was missing. The screen is painted to a canvas, so there is no text on the page for the OS to offer Copy over, and
user-selectis off across.xtermbesides — the browser has no idea the glyphs it drew are characters. Only flue knows that, so only flue can offer it.The gesture
Long press selects the word under the finger. Drag widens the range, either direction, across as many rows as the finger travels. A menu offers Copy, Paste and Cancel at whichever end of the terminal the hand is not covering.
Words break on whitespace alone. That is not what a text editor would do and is what a terminal wants: paths, URLs, hashes, flags and session ids are made of the punctuation an editor breaks on, and a rule that stopped at the hyphen would turn a UUID into six presses.
The range is xterm's own.
select(col, row, length)takes a length that wraps past the end of a row, which is the only way the public API can express a multi-row range — and it means the renderer draws the selection without flue owning a pixel of it.What flue owns is telling the press apart from the scroll that shares the surface with it. A finger that travels more than
PRESS_SLOPbeforeLONG_PRESS_MSelapses is a scroll; once a selection begins the scroll path is abandoned for the rest of the gesture, because a drag that both graded a range and moved what was under it would be unusable.Two things that fall out of it
The compatibility mousedown is swallowed. A touch nothing cancelled is replayed as a mouse, and xterm answers a mousedown by starting a selection of its own — clearing this one a frame after making it. A press that never moved has no touchmove to cancel, so the mouse event is the only place left to stop it. Capture phase on the inset, which is an ancestor of the element xterm listens on.
The transparent textarea is gone. It was there to give a long press some editable text to land on. It was also what drew the blue focus ring around the whole terminal, and what risked iOS zooming the page on every tap. flue owns the press now, so
-webkit-touch-callout: nonetells the platform to keep its own callout out of the way.Tests
make test— 1353 web, 163 relay, every Go package greenmake lintNew coverage: word bounds including the CJK-width case, extending forward across a row boundary and backwards from the anchored word, the fallback when a drag returns inside the anchor, and viewport-relative rows against a scrolled buffer;
cellAtcancelling the scale a mirroring view is drawn at; and in the view — the press, the scroll that beats it to it, the drag that widens instead of scrolling, the lift that starts no glide, the swallowed mouse replay, Copy, Paste, the refusal message, and the menu placement.Needs a device check
I can't drive a real phone from here, so the gesture itself is unverified on hardware. Worth checking: a long press selects, a drag widens it, Copy actually reaches the system clipboard, Paste lands in the prompt, and iOS shows no callout of its own over the menu.
🤖 Generated with Claude Code