Skip to content

Fix TUI duplicate top bar and crash with double-width (CJK) text at the right edge - #5500

Merged
Gabriel Dufresne (GabrielDuf) merged 3 commits into
mainfrom
fix/tui-wide-char-edge
Oct 9, 2026
Merged

Gabriel Dufresne (GabrielDuf) merged 3 commits into
mainfrom
fix/tui-wide-char-edge

Conversation

@GabrielDuf

@GabrielDuf Gabriel Dufresne (GabrielDuf) commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Cause

The TUI's page tab strip and bottom function bar sized their labels with string.Length. CJK translations take two terminal cells per character (zh_CN "帮助" is 2 chars but 4 cells), so both rows believed they fit while the real text ran to the right edge and a double-width glyph could land in the terminal's last column.

Consolonia 12.0.3.14 does not handle that:

  • DrawingContextImpl keeps a wide glyph whose first cell is inside the clip, and RenderTarget passes it to the device unchanged.
  • AnsiConsoleOutput.WritePixel writes two spaces before a wide glyph. In the last column that wraps the cursor: the terminal scrolls a line (or spills onto the next row) that Consolonia never tracks, leaving a stale copy of the tab strip on screen. That is the duplicate, unresponsive top bar in the issue.
  • On the next redraw, RenderToDevice reads one cell past the buffer edge and the render loop dies with IndexOutOfRangeException.

Changes

  • TuiChrome.Cells() measures text in terminal cells with Wcwidth, the same calculator Consolonia uses for glyph widths.
  • The tab-strip density choice, the function-bar key hints, the notification variants and the badges all use it.
  • When even number-only tabs do not fit, the selected tab now drops to its number too (previously it kept its full label), so on very narrow terminals no CJK glyph can reach the edge. The fitting logic moved to a static MainWindow.FitTabs so it can be tested.
  • Tests: TuiFunctionBarTests.DoubleWidthLabelsAreMeasuredInTerminalCells and the new TuiTabStripTests. Both new test sets fail on the previous behaviour.

Testing

  • Real console (conhost) with zh_CN fake data, switching tabs with Alt+digit at every width from 64 to 104 columns × 30 rows: the old build crashed at 73, 75, 77, 92, 94 and 96 columns and hung at 79 and 98; the fixed build was clean at all 41 widths.
  • Windows Terminal at 94 columns, by hand: the old build showed the tab label spilling onto the next row and crashed on Alt+1; the fixed build stays correct through the same key sequence.
  • dotnet format whitespace and style are clean for both TUI projects.

Not covered here

Other TUI layouts still measure with string.Length (menu dropdown padding, the package page's actions panel width, dialog widths, chips). None of them sits at the right edge of the terminal, so at worst they misalign rather than crash; they can be moved to TuiChrome.Cells in a follow-up.


Closes #5490

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The new width helper undercounts emoji rendered as double-width by Consolonia, leaving an edge-overflow path unresolved.

1 open finding
What changed in this PR

Fixes TUI edge rendering by measuring labels in terminal cells rather than UTF-16 length.

Changes:

  • Adds shared terminal-cell measurement.
  • Updates tab and function-bar fitting.
  • Adds CJK width regression tests.
File Description
src/​UniGetUI.Tui/​Views/​MainWindow.cs Adds testable tab-density fitting.
src/​UniGetUI.Tui/​Views/​Controls/​TuiFunctionBar.cs Measures hints, notifications, and badges by cells.
src/​UniGetUI.Tui/​Infrastructure/​TuiChrome.cs Adds shared cell-width calculation.
src/​UniGetUI.Tui.Tests/​TuiTabStripTests.cs Tests CJK tab fitting.
src/​UniGetUI.Tui.Tests/​TuiFunctionBarTests.cs Tests CJK function-bar fitting.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/UniGetUI.Tui/Infrastructure/TuiChrome.cs Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

The sizing changes address the reported failure and include focused CJK, emoji, and narrow-width regression coverage.

0 open findings

1 resolved since last review

🧠 Review effort: Balanced

@randy-but-a-ro randy-but-a-ro Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🤖 Pull request was approved automatically: the AI review is complete and all its review threads are resolved. 🎉

Integration Details
{
	"deliveryId": "d9888a70-c409-11f1-989f-19c35bf64653",
	"headSha": "f886c53234a5d23c1974007d0c5765bbaec3d7a2",
	"reviewer": "copilot-pull-request-reviewer[bot]"
}

@GabrielDuf
Gabriel Dufresne (GabrielDuf) merged commit 7da7f7c into main Oct 9, 2026
6 checks passed
@GabrielDuf
Gabriel Dufresne (GabrielDuf) deleted the fix/tui-wide-char-edge branch October 9, 2026 19:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] TUI: switching top-bar tabs draws a duplicate, unresponsive top bar

2 participants