Skip to content

fix(src/ui.zig): render UTF-8 in the sidebar and status row#49

Merged
kylecarbs merged 2 commits into
mainfrom
fix/ui-utf8-sidebar
Jun 12, 2026
Merged

fix(src/ui.zig): render UTF-8 in the sidebar and status row#49
kylecarbs merged 2 commits into
mainfrom
fix/ui-utf8-sidebar

Conversation

@kylecarbs

Copy link
Copy Markdown
Member

Fixes #48

The boo ui sidebar and status row replaced every non-ASCII byte with ?, so UTF-8 text (e.g. a Mandarin window title, 3 bytes per character) rendered as a run of question marks.

Changes

  • appendClipped now decodes UTF-8 and passes valid printable sequences through intact, clipping by display columns instead of bytes: East Asian wide/fullwidth characters count as two columns, combining marks as zero (compact wcwidth-style table). Rows keep their exact width, so the sidebar never bleeds into the separator or viewport; a wide character that would straddle the clip boundary is dropped and padded.
  • Control bytes (C0/DEL/C1) and invalid UTF-8 still render as ?, preserving the old guarantee that no raw control bytes reach the row being composed.
  • Updated the stale Entry.title comment ("sanitized to printable ASCII"): the daemon only strips control bytes, titles may contain any UTF-8.

Session content in the viewport already rendered through libghostty's formatter and was unaffected; a regression test now pins that down too.

Testing

  • New unit tests: UTF-8 passthrough, column-based clipping of wide chars, zero-width combining marks, control/invalid byte replacement, a Mandarin sidebar title row, and CJK passthrough in appendTermRow.
  • zig build test-all (unit + PTY integration) passes; zig fmt clean.
  • End-to-end: set a 你好世界 OSC title in a live session; the daemon reports it intact in boo ls and the sidebar renders it.
Investigation notes
  • Session names are validated to [a-zA-Z0-9._-], so the non-ASCII text in the sidebar comes from window titles (OSC 2) and status-row text; appendClipped was the single choke point that mangled them.
  • The daemon's title sanitization (daemon.zig) only strips bytes < 0x20 and 0x7f, so multi-byte UTF-8 already arrived intact at the UI.
  • ghostty-vt does not export its unicode width table through lib_vt.zig, so the fix uses a compact wcwidth-style approximation covering the common zero-width and wide ranges; only sidebar/status chrome is measured with it, session content goes through libghostty.

This PR was generated by Coder Agents on behalf of @kylecarbs.

The sidebar and status row replaced every non-ASCII byte with '?',
so UTF-8 text such as a Mandarin window title rendered as a run of
question marks. Decode UTF-8 in appendClipped and pass valid
printable sequences through intact, clipping by display columns:
East Asian wide characters take two columns and combining marks
take none, so rows keep their exact width and never bleed into the
separator. Control bytes and invalid sequences still render as '?'.

Session content in the viewport already rendered through
libghostty's formatter and was unaffected.

Fixes #48
ghostty-vt computes exact cell widths internally, but the table is
generated at build time and not exported through the module, so the
sidebar's chrome measurement cannot reuse it.
@kylecarbs kylecarbs merged commit 4e32027 into main Jun 12, 2026
5 checks passed
This was referenced Jun 12, 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.

[BUG] cannot show mandarin

1 participant