Powerline prompts render as smudged/banded segments: font-rasterized separators + unclamped fallback glyphs
- Dominant language
- TypeScript
- Stars
- 2.8k
- Forks
- 172
- PR merge metrics
- No merged PRs in 30d
Description
## Symptom
Embedding ghostty-web (0.4.0) with a starship Powerline prompt (segment backgrounds, fading-arrow separators `U+E0B0`, rounded caps `U+E0B4/E0B6`, Nerd icons), the prompt renders as soft "watercolor" blobs with ragged edges and horizontal banding inside segments — while plain ASCII output stays crisp. Reproduced on a 2x display at 13px with JetBrains Mono + Symbols Nerd Font Mono.
## Root causes (two, compounding)
**1. Powerline separators are font-rasterized.**
`renderCellText` draws `U+E0B0-U+E0B7` via `fillText` like any glyph. Two physical problems:
- the diagonal edges keep anti-aliasing fringe on both sides, which visually smears against adjacent solid segment backgrounds;
- the glyph's ink height is smaller than the cell height (font ascent/descent box ≠ cell box), so the neighboring background bleeds through above/below every separator — with stacked "fading arrow" themes this shows as banding stripes.
This is why WezTerm and xterm.js (`customGlyphs`) both special-case these codepoints and draw them as exact vector geometry covering the cell box, bypassing the font entirely.
**2. Oversized fallback glyphs are not clamped to their cells.**
Cell metrics come from the primary font's `M`, but glyphs served by fallback fonts carry their own advance — e.g. Symbols Nerd Font Mono icons advance 1em (13px = 1.67 cells at a 7.8px cell). `fillText` never scales, so the ink bleeds into neighbor cells. Since rows repaint by background fill (no `clearRect`), the cursor-row repaint shaves that overhanging ink asymmetrically every blink — accumulating the ragged artifacts.
`renderCellText` already computes the cell budget (`cellWidth = metrics.width * cell.width`) but doesn't pass it to `fillText`, whose 4th argument (`maxWidth`) does exactly this clamping.
## Fix
PR incoming that (a) draws `U+E0B0-U+E0B7` as vector paths (solid triangles / angle outlines / half-circles) using the cell's fillStyle, and (b) passes `cellWidth` as `fillText`'s `maxWidth` for everything else. Verified pixel-level against WezTerm rendering the same prompt.
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.