Skip to content

fix(renderer): column-align wide-glyph cells in libghostty-vt snapshots#118

Merged
ThomasK33 merged 2 commits into
mainfrom
fix/wide-char-cell-column-alignment
Jun 3, 2026
Merged

fix(renderer): column-align wide-glyph cells in libghostty-vt snapshots#118
ThomasK33 merged 2 commits into
mainfrom
fix/wide-char-cell-column-alignment

Conversation

@ThomasK33
Copy link
Copy Markdown
Member

Fixes #112.

Problem

Wide characters (CJK/emoji, native cell width: 2) misaligned per-cell rendering: in a line containing a wide glyph, every cell after the glyph shifted one column left, and the dashboard cursor-cell highlight landed on the wrong cell.

I verified the report against the source and reproduced it against the real native engine (in-process, no host needed):

"rocket 🚀 done" — native cell records:
  col=6 width=1 " "
  col=7 width=2 "🚀"     ← width-2 glyph
  col=9 width=1 " "       ← NOTE: no record for col 8
  col=10 width=1 "d" ...

The native engine emits one width-2 record and no record for the trailing column. mapNativeCells packed one array entry per record and dropped the native col/width, so the packed array was one entry short and cells[col] no longer matched terminal column col past the glyph.

Root cause

Fix (no protocol-schema change)

Pack cells column-indexed in mapNativeCells: place each record at its true col and emit an empty spacer for every trailing column a wide glyph covers (and defensively for any gap). The spacer carries the glyph's styling so the trailing half shades correctly. This reuses the exact col/width the native engine already provides (no text-width measurement needed) and matches the ghostty-web backend, which already emits one cell per column — so the two renderers are now consistent and the consumer's index-as-column assumption is correct. visibleLines text was already right.

This deliberately avoids option (a) from the issue (adding col/width to the public SnapshotCell schema); that remains a possible future addition if a consumer ever needs explicit width.

Verification

  • Producer (real native engine): a new availability-guarded unit test drives LibghosttyVtBackend.snapshot({includeCells:true}) — the same path agent-tty snapshot --include-cells --renderer libghostty-vt and the dashboard use — feeding a real 🚀 and asserting the glyph sits at its true column with an empty spacer after it.
  • Producer (mock records): asserts both the emoji case and a two-wide-glyph CJK case (A漢字BB stays at true col 5, previously off-by-2).
  • Consumer: a projectLiveView test asserts a wide-glyph row renders column-aligned and the cursor highlights its true column (the bug highlighted the next cell).
  • npm run typecheck, npm run lint, npm run format:check, full unit suite (1212 passed), and npm run build all pass locally. The e2e/integration host-dependent suites run in CI.

Existing cell tests used only ASCII/width-1 records (and the projection test helper built cells by code-point index), which is why this slipped through.

🤖 Generated with Claude Code

ThomasK33 and others added 2 commits June 3, 2026 19:49
mapNativeCells packed one SnapshotCell per native cell record and dropped
the native col/width, so a width-2 glyph (CJK/emoji) became a single entry
with no spacer for its trailing column. Index-as-column consumers — the
Session Dashboard projection (which pins libghostty-vt) and its cursor-cell
highlight — then shifted every cell after the glyph one column left.

Pack cells column-indexed instead: place each record at its true column and
emit an empty spacer for a wide glyph's trailing column (and any gap),
mirroring the ghostty-web backend, which already emits one cell per column.
No protocol-schema change; visibleLines text was already correct.

Adds regression tests for the producer (mocked records plus the real native
engine, guarded by availability) and the dashboard projection.

Closes #112

Change-Id: Ie1e757983e51ec60ff26ae423ad07a0865000828
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: Ib18c233c8a00d8b9ef3d46b48e39cc4de0b7abbf
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33
Copy link
Copy Markdown
Member Author

@codex review

@ThomasK33
Copy link
Copy Markdown
Member Author

@claude review

@ThomasK33 ThomasK33 merged commit 32da67b into main Jun 3, 2026
21 of 23 checks passed
@ThomasK33 ThomasK33 deleted the fix/wide-char-cell-column-alignment branch June 3, 2026 17:59
@ThomasK33 ThomasK33 mentioned this pull request Jun 3, 2026
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.

Wide characters (CJK/emoji) misalign per-cell rendering and cursor highlight in SnapshotCell consumers

1 participant