Open
Conversation
New module `lib/src/lib/mouse-selection.ts` holds mouse-reporting, bracketed-paste, override, selection, and hint-token state per terminal. Mirrors the subscribeToSessionStateChanges / getSnapshot pattern in terminal-registry.ts so it plugs into useSyncExternalStore. Enforces two spec rules at the store level: override can't be activated while mouse reporting is off, and mouse reporting going to none auto-ends any active override. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ET modes New `mouse-mode-observer.ts` registers parser hooks on every xterm terminal that fire on DECSET (CSI ? ... h) and DECRST (CSI ? ... l), returning false so xterm still handles the sequence. A queueMicrotask then syncs our store from `terminal.modes.mouseTrackingMode` and `terminal.modes.bracketedPasteMode` once xterm's own handler has run. terminal-registry wires the observer into setupTerminalEntry and calls removeMouseSelectionState in destroyTerminal. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Used downstream for choosing Cmd vs Ctrl paste/copy keybindings. Guards against undefined navigator so it's safe in node test envs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
TerminalPaneHeader now subscribes to the mouse-selection store and renders a CursorClickIcon between the title and the alarm bell whenever the inside program has requested mouse reporting. When an override is active, a Prohibit glyph is composited on top and the tooltip/aria-label switch to the "restore" wording from spec §1.2. Clicking toggles a temporary override on, or ends any active override. Four Storybook states: hidden, reporting on, temporary override, permanent override. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Portal banner anchored below the No-Mouse icon shows while the override is temporary, with Make-permanent and Cancel buttons that toggle the store. Kept in sync with scroll/resize like TodoAlarmDialog. Per spec §2.4 the banner is mouse-only. The existing TemporaryOverride story in MouseHeaderIcon.stories.tsx now exercises the banner as well. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds beginDrag / updateDrag / endDrag / isDragging state helpers to mouse-selection.ts and wires capture-phase mousedown/mousemove/ mouseup listeners in setupTerminalEntry. Classifies each click per spec §6.1 state matrix: terminal owns the drag if mouse reporting is off, an override is active, or the mousedown originated in scrollback. Otherwise the event is left alone to bubble to xterm and forward to the inside program. This story intentionally does NOT stopPropagation — we observe events and build selection state while xterm's default visible selection still works. C.2 adds the overlay and takes over event handling fully. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New SelectionOverlay component renders the current selection as absolute-positioned rectangles above the xterm grid: one per row for linewise, one total for block. Uses the page's --vscode-terminal-selectionBackground for the fill color so it matches the theme. Terminal overlay dimensions (cols, rows, viewportY, baseY, element size) are exposed via getTerminalOverlayDims. A global renderTick bumped by terminal.onRender / onResize lets the overlay re-measure and reposition on scroll or resize without each pane needing its own subscription plumbing. computeRects is tested exhaustively for normalization, multi-row linewise, block, and viewport clipping. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
setDragAlt flips the in-progress selection between linewise and block shape when Alt is pressed or released without the mouse moving. Window-level keydown/keyup listeners in setupTerminalEntry call it while a drag is active. SelectionOverlay renders a "Hold Alt for block selection" hint above the drag-end cell while dragging. The hint is clamped inside the overlay bounds and hidden once the drag ends. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…lipboard New modules: - rewrap.ts: Copy Rewrapped transform — strips box-drawing frame lines, strips leading/trailing border chars from remaining lines, joins wrapped paragraphs with spaces, preserves blank-line paragraph separators. Table-driven tests cover frames, tables, paragraph unwrap, edge cases. - selection-text.ts: extract the cells covered by a Selection from an xterm buffer, handling linewise and block shapes (block is rectangular slab, not rewrapped). - clipboard.ts: copyRaw / copyRewrapped / doPaste glue over navigator.clipboard, keyed by terminal id. doPaste wraps the text in bracketed-paste markers when the inside program has enabled bracketed paste. getTerminalInstance exposed from terminal-registry for features that need direct buffer access. Copy/paste UI and keyboard shortcuts follow in D.1/D.3/F.1-F.2. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New SelectionPopup shown after mouse-up on an active selection with Copy Raw and Copy Rewrapped buttons. Labels switch Cmd/Ctrl based on IS_MAC. Dismissed by Esc, click-outside, or a completed copy. Pond's keydown handler gets an early branch that intercepts Cmd/Ctrl+C (and +Shift) before the passthrough-mode short-circuit when the selected pane has a finalized terminal selection. This is the only place where Ctrl+C is prevented from reaching the inside program — spec §4.2's narrow rule. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cmd+V / Cmd+Shift+V on macOS and Ctrl+V / Ctrl+Shift+V on other platforms are intercepted in Pond's keydown handler and routed to doPaste, which reads the clipboard and writes it to the PTY wrapped in bracketed-paste markers when the inside program has opted in (already implemented in D.2a). Ctrl+V on macOS is intentionally NOT intercepted and still reaches the inside program as the raw 0x16 control byte (spec §8.2.1). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New smart-token.ts exposes detectTokenAt(line, col): finds the whitespace-delimited token at the given position, tests it against URL/path/error-location patterns, and strips unlikely- trailing characters (periods, commas, unmatched brackets) per spec §5.1. Matched pairs like the trailing `)` in wikipedia-style URLs are preserved. Table-driven tests cover each listed pattern, trailing-punctuation rules, non-matches, and position sensitivity within a token. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The mousemove listener in terminal-registry reads the buffer line under the drag cursor, runs detectTokenAt, and stores any matching URL/path as the hintToken. SelectionOverlay now shows both the "Hold Alt" hint and, when a token is detected, "Press e to select the full URL/path". extendSelectionToToken in mouse-selection respects drag direction: the anchor stays, the end jumps to whichever token boundary is farther from the anchor, so left-to-right and right-to-left drags both feel natural. Pond's keydown handler gets a mid-drag branch that handles e (extend) and Esc (cancel), both consumed so they don't reach the inside program. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After a drag ends, setupTerminalEntry snapshots the selected text as a baseline. Each subsequent terminal.onRender re-extracts the current text over the same coordinates and cancels the selection if anything differs. terminal.onResize also cancels unconditionally, per spec §3.4. While a drag is in progress the baseline is held at null so the active selection isn't fighting the user's own updates. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When the mouse router decides the terminal should handle a drag, it now calls preventDefault + stopPropagation on mousedown, mousemove, and mouseup. That prevents xterm's built-in text selection from starting in parallel with ours — which would have left two highlighted regions on top of each other. Propagation is only stopped when the terminal owns the drag; program-bound events (mouse reporting on, no override, live region) still flow through to xterm untouched. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the new spec to the AGENTS.md index with the usual one-line summary and the list of files it covers, following the existing entries' format. Stories C.5 (auto-scroll during drag), F.3 (right-click / Edit menu paste), and the Copy Rewrapped heuristic refinement stay deferred to follow-ups per spec §9. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…handler blocks in Pond, remove unused BOX_RUN_GLOBAL, flatten token hint logic
…ike block) and add try/catch for clipboard API errors
…ive endCol conversion
…c §2.1, fix stale comment
… drag per spec §3.6
Replace the per-row translucent-fill divs with a single SVG path that traces the perimeter of the whole selection. For multi-row linewise selections the path walks the right edges down, the bottom of the last row, then the left edges up in reverse — so width transitions between rows produce proper outside corners instead of stacked-rectangle double-lines. Border color prefers --vscode-focusBorder (typically fully opaque), falls back to --vscode-terminal-foreground, then selectionBackground, then a cornflower default. This renders cleanly across themes where the translucent fill previously looked bad. New rectsToPath helper has coverage for empty, single-rect, two-row Z, three-row with full-width middle, and block shapes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Both computeCell (mouse hit-testing) and computeRects (overlay) were deriving cell size from element.width / cols and element.height / rows. xterm actually renders into `.xterm-screen` inside the element with a few pixels of padding around it, so each cell was (padding / rows) px taller than reality. The per-cell error is negligible but it accumulated linearly — the selection border drifted out of alignment as row count grew. getTerminalOverlayDims now queries `.xterm-screen`, exposing the measured cellWidth / cellHeight plus gridLeft / gridTop offsets. computeCell uses the same helper so mouse and highlight can't drift apart. SelectionOverlay translates its SVG path by (gridLeft, gridTop) and positions the mid-drag hint with the same offsets. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three fixes to the Alt hint and copy popup: 1. Never flip sides when the preferred side would clip off-screen. The old fallback put the hint INSIDE the selection for drag-up near the viewport top, and bounced in and out as the mouse crossed the fit-check threshold. Now we clamp to the same-side viewport edge instead. 2. Add one cellHeight of gap on the drag-down side so the line adjacent to the selection stays visible. The drag-up side already felt like two lines of clearance because the hint's own height extends it away from the selection; shifting drag-down by one row matches that visual weight. 3. The hint label reads "Hold Opt for block selection" on macOS and "Hold Alt for block selection" elsewhere, driven by IS_MAC from lib/platform. Spec §3.3 updated to match. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Popup outer container: drop px-1.5 py-0.5 and gap-1; add items-stretch + overflow-hidden so button hover backgrounds paint edge-to-edge and get clipped by the container's rounded corners. Buttons: drop border-border and inner p-0; use px-1.5 py-0.5 so the popup height equals the Alt hint height and hover fills the button's full hit area. New stories file lib/src/stories/TextSelection.stories.tsx mounts a real TerminalPane with SCENARIO_LS_OUTPUT and drives selection state directly via setSelection. Six variants: linewise outline, block outline, Alt hint for drag-up and drag-down, copy popup for drag-up and drag-down. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drag-up used to compute `top` by subtracting a per-element height estimate, so the popup (~32px est) sat 12px closer to the selection than the hint (~44px est). Now both elements anchor their BOTTOM edge via CSS `bottom`, so heights cancel out and the popup lands exactly where the hint was. Symmetric with drag-down, which already top-anchors via a height-free formula. Also shift the drag-up anchor up by one full cell, so the row adjacent to the selection stays visible on both sides — matching the +2-row offset on the drag-down top-anchored path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The separate "Copied!" confirmation card shifted position because it had different padding and content than the popup it replaced. Now the popup stays mounted and the pressed button signals the press in situ: accent background + text color, inline check mark where the shortcut label was, plus a 260ms back-eased scale pop. Uses the existing flashCopy state machine so both the Cmd+C keyboard path and the button click look identical. Respects prefers-reduced-motion. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The shortcut label is now kept in the DOM as an invisible span when the button is flashed, so it reserves its width. The check icon is overlaid via absolute positioning + flex centering inside that reserved box. Button size no longer jumps between the normal and the just-pressed state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Deploying mouseterm with
|
| Latest commit: |
25c3d23
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://89b467d4.mouseterm.pages.dev |
| Branch Preview URL: | https://mouse-and-clipboard.mouseterm.pages.dev |
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.
This adds our new mouse and clipboard behavior. Still TODO is update the tutorial to demonstrate (and test) it all, particularly for TUI's which have opted-in to mouse handling.