Skip to content

Conversation

@neurosnap
Copy link
Contributor

@neurosnap neurosnap commented Jan 2, 2026

The VT formatter was treating cells without text as blank and emitting
them as plain spaces, losing any background color styling. This caused
TUIs like htop to lose their background colors when rehydrating terminal
state (e.g., after detach/reattach in zmx).

For styled formats (VT/HTML), cells with background colors or style_id
are now emitted with proper SGR sequences and a space character instead
of being accumulated as unstyled blanks.

Adds handling for bg_color_palette and bg_color_rgb content tags which
were previously unreachable.

I used amp to construct the test case and the code to make the test pass. Please see the amp thread.

I tested my branch against the test cases where it was previously broken (nvtop, htop, senpai).

Reference: https://ampcode.com/threads/T-019b7a35-c3f3-73fc-adfa-00bbe9dbda3c

VT

BEFORE

screenshot_1767373196

AFTER

screenshot_1767373113

HTML

BEFORE

screenshot_1767373647

AFTER

screenshot_1767373657

The issue is in ghostty_src/src/terminal/formatter.zig#L1117-L1129:

- Cells without text are treated as "blank" (line 1117-1119) - this includes cells that only have background colors
- When blank cells are emitted, they're plain spaces (line 1129) - writer.splatByteAll(' ', blank_cells) outputs spaces without any SGR styling
- Background-only cells (bg_color_palette, bg_color_rgb) are marked unreachable (lines 1233-1235) because the code assumes hasText() already filtered them

This means when htop draws a row like:

`[green bg]CPU: 45%[red bg]          [default]`

The trailing cells with red background but no text get accumulated as blanks and emitted as plain spaces - losing the background color.
The VT formatter was treating cells without text as blank and emitting
them as plain spaces, losing any background color styling. This caused
TUIs like htop to lose their background colors when rehydrating terminal
state (e.g., after detach/reattach in zmx).

For styled formats (VT/HTML), cells with background colors or style_id
are now emitted with proper SGR sequences and a space character instead
of being accumulated as unstyled blanks.

Adds handling for bg_color_palette and bg_color_rgb content tags which
were previously unreachable.

Reference: https://ampcode.com/threads/T-019b7a35-c3f3-73fc-adfa-00bbe9dbda3c
@neurosnap neurosnap requested a review from a team as a code owner January 2, 2026 01:28
@mitchellh mitchellh added this to the 1.3.0 milestone Jan 2, 2026
Copy link
Contributor

@mitchellh mitchellh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, sorry for the delay, I wanted to clean stuff up and now did. The main thing is that I wanted to use Zig's named blocks and early exit to clean up some logic. And also we could extract styling out to above the content tag now because we use it for all content tags.

This eliminated the emitColorSgr function which was a bit redundant and we can now reuse logic.

Tests were good and untouched.

@mitchellh mitchellh merged commit e9ea94d into ghostty-org:main Jan 7, 2026
11 checks passed
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.

2 participants