feat(render): draw the cursor in screenshots - #96
Open
Ayman Bagabas (aymanbagabas) wants to merge 6 commits into
Open
feat(render): draw the cursor in screenshots#96Ayman Bagabas (aymanbagabas) wants to merge 6 commits into
Ayman Bagabas (aymanbagabas) wants to merge 6 commits into
Conversation
A screenshot showed the grid but never where the terminal was about to write, so a reader could not tell an editor's caret position, whether a program had hidden the cursor, or which mode it was in. The emulator now reports visibility (`DECTCEM`) and shape (`DECSCUSR`), and the renderer draws the cursor after the text pass: a block fills the cell, an underline sits on its bottom edge, and a bar on its left. A block redraws the character beneath it in the cell's background color, so it stays readable rather than being swallowed. The redraw goes through the same path as the text pass, which keeps two cases right that a naive redraw gets wrong: a double-width character is covered across both of its cells instead of being clipped and squashed into one, and a nerd font glyph comes back as a vector glyph rather than as a character the text font has no glyph for. Blink is deliberately not represented; a screenshot is a single moment, and a blinking cursor is drawn in the half of the cycle where it shows. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Ayman Bagabas <ayman.bagabas@gmail.com>
Brings main's move of the request types out of the core crate down the stack. The screenshot keeps main's signature and error handling, and holds the state guard across both the emulator borrow and the cursor lookup so a single lock covers the whole render. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Ayman Bagabas <ayman.bagabas@gmail.com>
This was referenced Aug 5, 2026
This was referenced Aug 5, 2026
Ayman Bagabas (aymanbagabas)
requested review from
cpendery (cpendery)
and
a lite review from Copilot
August 5, 2026 18:01
Copilot started reviewing on behalf of
Ayman Bagabas (aymanbagabas)
August 5, 2026 18:03
View session
There was a problem hiding this comment.
Pull request overview
Adds cursor rendering to SVG screenshots so captures reflect cursor visibility, position, shape, and color, aligning screenshots more closely with real terminal behavior and improving debugging/visual verification.
Changes:
- Extend the
Emulatorcontract with cursor visibility and shape reporting (DECTCEM/DECSCUSR). - Render the cursor (block/underline/bar) as an overlay pass in the SVG renderer, including block “redraw under cursor” behavior.
- Thread cursor visibility/position through screenshot generation and add conformance + renderer tests.
Show a summary per file
| File | Description |
|---|---|
| crates/shell-use/src/terminal/emu.rs | Adds CursorShape and new Emulator APIs for cursor visibility/shape. |
| crates/shell-use/src/terminal/conformance.rs | Adds conformance tests for cursor hide/show and shape selection sequences. |
| crates/shell-use/src/terminal/alacritty.rs | Implements new cursor APIs by mapping Alacritty terminal mode/style to CursorShape + visibility. |
| crates/shell-use/src/render/svg.rs | Adds cursor drawing pass to SVG output and expands renderer test coverage for cursor behavior. |
| crates/shell-use/src/engine.rs | Computes cursor position within full/visible row sets and passes it into SVG rendering. |
Review details
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 5/5 changed files
- Comments generated: 2
- Review effort level: Lite
A full render is as long as the scrollback, which a profile sets and does not cap. Offsetting the cursor past the history counted that in a `u16`, so a session with a scrollback deeper than 65535 rows wrapped: with `scrollback = 70000` and 68004 rows the cursor drew on row 2467, which is 68000 - 65536 and looks like a real line. The row is a `usize` now, since it indexes rows rather than the screen. The line documenting `render_svg` was also left behind above `CURSOR_THICKNESS` when that constant was added between them, so it documented the constant. `render_svg` carries its own description, so the stray line is gone. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Ayman Bagabas <ayman.bagabas@gmail.com>
Member
Author
|
For anyone hitting red CI on this one: the two failures seen here were both flakes, not this PR.
Both are instances of the class tracked in #98. Worth noting the |
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.
Third of a three PR stack: #94 → #95 → #96, based on #95 so the diff here is only this layer.
A screenshot showed the grid but never showed where the terminal was about to write. You could not tell an editor's caret position, whether a program had hidden the cursor, or which mode it was in.
Before / after
The same session, screenshotted by the same command. The cursor is parked mid-word on
The:The emulator now reports visibility (
DECTCEM,CSI ?25 handl) and shape (DECSCUSR,CSI Ps SP q). The renderer draws the cursor after the text pass: a block fills the cell, an underline sits on its bottom edge, a bar on its left.Visibility matters as much as position. Full-screen programs hide the cursor while repainting, so a screenshot that ignored
DECTCEMwould show one parked wherever the last write happened to land.The cursor never hides content
A block redraws the character beneath it in the cell background, the way a terminal does. The redraw goes through the same path as the text pass, which keeps right two cases a naive redraw gets wrong:
日)The cursor follows a color a program sets
OSC 12sets the cursor color andOSC 112resets it, both from #95. The cursor had nothing to paint until this PR, so this is where they become visible. One session, screenshotted after each step, cursor parked on theq:OSC 12sets#ff8700OSC 112resetsThe default cursor color follows the profile foreground, so the block is the same grey as the text. The
qstays legible in all three.Notes for review
Emulator::cursoris relative to the visible screen, soscreenshot --fulloffsets it past the scrollback above.HollowBlock(what it draws for an unfocused window) maps toBlock, since a headless terminal has no notion of focus. ItsHiddenshape means the same as the mode being off.