Skip to content

fix(client): copy-mode no longer blanks the window — paint a pane backdrop#84

Closed
phall1 wants to merge 1 commit into
mainfrom
fix/copy-mode-backdrop
Closed

fix(client): copy-mode no longer blanks the window — paint a pane backdrop#84
phall1 wants to merge 1 commit into
mainfrom
fix/copy-mode-backdrop

Conversation

@phall1

@phall1 phall1 commented Jun 8, 2026

Copy link
Copy Markdown
Owner

The bug

Entering copy-mode fully blanks the window. Copy-mode is a transparent overlay — it should show the live pane content with the selection highlighted on top — but the overlay paint system hands every overlay a fresh blank Buffer (correct for modal overlays like help/palette/prompts, which draw their own surface) and the driver \x1b[2J-clears before painting. Copy-mode's render() only styled the selected cells, so on a blank buffer it produced a blank window with just a status line.

Latent until phux-v6jw made copy-mode actually worth entering.

The fix

Give the copy-mode overlay a backdrop of the focused pane's visible glyphs and paint them under the highlight.

  • RenderOverlay gains two default-no-op hooks: backdrop_dims() -> Option<(u16,u16)> (None for modal overlays) and set_backdrop(grid). Only CopyModeOverlay overrides them — no churn on other overlays, no downcasting.
  • CopyModeOverlay stores backdrop: Vec<Vec<char>>; render() writes the pane glyph into every cell before applying selection/anchor styling.
  • The driver refreshes the backdrop (reads the focused pane's glyph grid via the renderer) right before each overlay paint, gated on backdrop_dims() — tracks entry, cursor moves, and live content updates; no-op for modal overlays.

Scope notes

  • Cosmetic only — copied text is still extracted from the engine (format_selection_alloc, phux-v6jw), never from these glyphs.
  • Cell colors under copy-mode aren't reproduced yet (plain glyphs) — a clear win over a blank screen; styled backdrop is a follow-up.

Test

render_paints_pane_backdrop_not_blank asserts the backdrop glyphs land in the buffer (window is not blank).

Gates

clippy --all-features -D warnings · cargo doc -D warnings · fmt · nextest.

🤖 Generated with Claude Code

…kdrop

Copy-mode is a *transparent* overlay: it must show the live pane content with
the selection highlighted on top. But the overlay paint system hands every
overlay a fresh blank Buffer (correct for modal overlays — help, palette,
prompts — which draw their own surface), and the driver clears the screen
before painting. Copy-mode's render() only styled the selected cells, so on a
blank buffer it produced a blank window with just a status line. The bug was
latent until phux-v6jw made copy-mode actually worth entering.

Fix: give the copy-mode overlay a backdrop of the focused pane's visible
glyphs and paint them under the highlight.

- RenderOverlay gains two default-no-op hooks: `backdrop_dims()` (None for
  modal overlays) and `set_backdrop(grid)`. Only CopyModeOverlay overrides
  them — no churn on the other overlays, no downcasting.
- CopyModeOverlay stores a `backdrop: Vec<Vec<char>>` and render() writes the
  pane glyph into every cell before applying the selection/anchor styling.
- The driver refreshes the backdrop (read the focused pane's glyph grid via
  the renderer) immediately before each overlay paint, gated on
  `backdrop_dims()` — so it tracks entry, cursor moves, and live content
  updates, and is a no-op for modal overlays / no overlay.

Cosmetic only: the copied text is still extracted from the engine
(format_selection_alloc, phux-v6jw), never from these glyphs. Cell styling
(colors) under copy-mode is not yet reproduced — plain glyphs — which is a
clear win over a blank screen; styled backdrop is a follow-up.

Test: render_paints_pane_backdrop_not_blank asserts the backdrop glyphs land
in the buffer (the window is not blank).

Gates: clippy --all-features -D warnings, cargo doc -D warnings, fmt, nextest.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@phall1

phall1 commented Jun 8, 2026

Copy link
Copy Markdown
Owner Author

Closing unmerged — the backdrop approach is the wrong layer (it re-paints a lossy copy of the screen). Replacing with the correct fix: render the selection highlight (reverse-video) inside the pane renderer so nothing on screen swaps and real colors are preserved. See follow-up PR.

@phall1 phall1 closed this Jun 8, 2026
phall1 added a commit that referenced this pull request Jun 8, 2026
Copy-mode was implemented as a modal overlay. Modal overlays (help, palette,
prompts) clear the screen (ED2) and repaint their own surface from a fresh
buffer — correct for them, wrong for copy-mode, which is a selection highlight
*over* the live pane. The result was that entering copy-mode wiped the real
content and repainted a degraded copy (blank, or — with the reverted backdrop
hack — color-stripped).

Render the selection where it belongs: inside the pane renderer. The cell-emit
loop already walks every cell with its real style/fg/bg; when a cell falls in
the copy-mode selection it now toggles `inverse` (reverse-video) before
emitting. So the pane renders exactly as it always does — true colors, true
content — and the selected cells just invert. Nothing on screen swaps.

- `render.rs`: `SelectionRect` (pane-local, linear) + a transient
  `TerminalRenderer::selection` the driver sets only around a copy-mode paint;
  the cell loop reverse-videos selected cells.
- copy-mode stops being a modal overlay: `RenderOverlay::copy_selection()`
  (default None; copy-mode returns Some) tells the driver to repaint the
  focused pane with the selection + a status line via `paint_active_overlay`,
  instead of the modal clear+overlay path. Copy-mode's own `render` is now a
  no-op; its key handling (arrows/Enter→OSC52 copy/Esc) is unchanged.

Reverse-video preserves each cell's colors (just inverted) and is theme-native.

Tests: `selection_emits_reverse_video_for_selected_cells` (real content +
SGR 7 on the selection, none without), `selection_rect_contains_is_linear`,
`copy_selection_tracks_normalized_range`.

Supersedes the closed backdrop PR #84.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
phall1 added a commit that referenced this pull request Jun 8, 2026
Copy-mode was implemented as a modal overlay. Modal overlays (help, palette,
prompts) clear the screen (ED2) and repaint their own surface from a fresh
buffer — correct for them, wrong for copy-mode, which is a selection highlight
*over* the live pane. The result was that entering copy-mode wiped the real
content and repainted a degraded copy (blank, or — with the reverted backdrop
hack — color-stripped).

Render the selection where it belongs: inside the pane renderer. The cell-emit
loop already walks every cell with its real style/fg/bg; when a cell falls in
the copy-mode selection it now toggles `inverse` (reverse-video) before
emitting. So the pane renders exactly as it always does — true colors, true
content — and the selected cells just invert. Nothing on screen swaps.

- `render.rs`: `SelectionRect` (pane-local, linear) + a transient
  `TerminalRenderer::selection` the driver sets only around a copy-mode paint;
  the cell loop reverse-videos selected cells.
- copy-mode stops being a modal overlay: `RenderOverlay::copy_selection()`
  (default None; copy-mode returns Some) tells the driver to repaint the
  focused pane with the selection + a status line via `paint_active_overlay`,
  instead of the modal clear+overlay path. Copy-mode's own `render` is now a
  no-op; its key handling (arrows/Enter→OSC52 copy/Esc) is unchanged.

Reverse-video preserves each cell's colors (just inverted) and is theme-native.

Tests: `selection_emits_reverse_video_for_selected_cells` (real content +
SGR 7 on the selection, none without), `selection_rect_contains_is_linear`,
`copy_selection_tracks_normalized_range`.

Supersedes the closed backdrop PR #84.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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