fix(table): correct display width for spacing marks, unskewing list columns - #25
Merged
Merged
Conversation
…olumns A Thai tab title pushed its URL/WIN/FLAGS cells two columns right of every other row in `tabbrew tabs list`. Not a pad-on-.length bug — table.ts already measured with Bun.stringWidth. Bun's mark table is itself wrong, in both directions: า ำ Thai/Lao SARA AA / AM (Lo) Bun 0 terminal 1 -> over-pads, shifts right ि Indic spacing matras (Mc) Bun 0 terminal 1 -> over-pads harakat Arabic/Hebrew (Mn) Bun 1 terminal 0 -> under-pads, shifts left The reported title carries two U+0E32, hence exactly two columns of drift. width() now walks grapheme clusters and applies two rules: a combining mark (Mn/Me/Cf/Cc) is always 0, and a spacing character is never 0. Emoji clusters are still delegated to Bun, which gets ZWJ/flag/skin-tone sequences right, and ASCII takes an s.length fast path. Fixing this one function fixes colWidth, padEnd, padEndLink and truncate — so both `tabs list` and `docs list` align without touching either command. Also replace fmtTab's `title.slice(0, 33)` in the preview renderer with the existing grapheme-aware truncate(): a code-unit cut split an emoji's surrogate pair into a replacement char and orphaned Thai vowels from their consonants. Adds bun test (built into Bun — still zero deps) with src/table.test.ts, wires `bun run test` into CI, and updates the two CLAUDE.md claims this invalidates. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
The bug
A Thai tab title pushed its
URL/WIN/FLAGScells two columns right of every other row intabbrew tabs list:This is not a pad-on-
.lengthbug —table.tsalready measured withBun.stringWidth.Bun.stringWidthis itself wrong, and in both directions:าำThai /າຳLao — SARA AA/AM, categoryLo\p{Mc}(e.g.ि, 77 codepoints)\p{Mn}(104 codepoints)The reported title carries two U+0E32 (
า), hence exactly two columns of drift — confirmed by measuring the screenshot's pixel offset against the character cell width.The fix
width()now walks grapheme clusters and applies two rules:Mn/Me/Cf/Cc) is always 0 — fixes the Arabic over-count;Math.max(1, Bun.stringWidth(ch))) — fixes Thai/Lao/Indic.Bun.stringWidthstill supplies the2for everything wide, and whole emoji clusters are delegated to it untouched, since it gets ZWJ sequences, regional-indicator flags and skin-tone modifiers right today. ASCII takes ans.lengthfast path (~every cell), and the existing ANSI-stripping behavior is preserved.width()is the single chokepoint forcolWidth/padEnd/padEndLink/truncate, so this fixes bothtabs listanddocs listwithout touching either command file.Also: grapheme-safe truncation in the preview renderer
fmtTab(src/tabbrew-script/render.ts) truncated withtitle.slice(0, 33)on code units, which splits an emoji's surrogate pair and orphans Thai vowels from their consonants:Replaced with the existing grapheme-aware
truncate()fromtable.ts— reuse, not a new helper.Tests
Introduces
bun test— built into Bun, so this holds the repo's zero-runtime-deps line.src/table.test.tscovers Thai/Lao, Arabic, Devanagari, CJK, ZWJ/flag/skin-tone emoji, NFC-vs-NFD, ANSI, and grapheme-safe truncation. Wired intopackage.jsonandci.ymlbetweentypecheckandbuild.CLAUDE.mdupdated — it claimed "no test runner configured" and that width was measured "viaBun.stringWidth".Verification
Against the real
~/.config/tabbrew/tabs.jsonfrom the report, all 14 rows now start theWINcolumn at display column 93 (the Thai row previously sat at 95).docs list, which has its own Thai titles, is uniform at column 71.Confirm in a real terminal, not through a pipe — the bug is only visible when a terminal does the rendering.
🤖 Generated with Claude Code