Skip to content

fix(tui): proper Unicode width handling and combining mark support#26

Open
PTFOPlayer wants to merge 5 commits into
masterfrom
fix/unicode-width-combining-marks
Open

fix(tui): proper Unicode width handling and combining mark support#26
PTFOPlayer wants to merge 5 commits into
masterfrom
fix/unicode-width-combining-marks

Conversation

@PTFOPlayer

@PTFOPlayer PTFOPlayer commented Jun 13, 2026

Copy link
Copy Markdown
Owner

No description provided.

- Fix .max(1) bug that made zero-width combining mark handling dead code
  in screen.rs (write_str, write_str_wrapped, write_str_wrapped_clipped,
  write_str_wrapped_skip_clipped), conversation.rs (line_height_for_text),
  and input_bar.rs (input_rows). Combining marks were treated as width-1
  instead of being merged into the previous cell.

- Remove unused unicode-segmentation dependency from Cargo.toml

- Clean up dead code: unused has_newline variable and empty if block
  in input_bar.rs

- Dynamic input bar height based on content, resizable sidebar width,
  Unicode-aware text wrapping and truncation across all TUI widgets
Add a  field to  to track continuation cells for wide
characters that occupy 2 columns. When a wide character is written,
the following column is marked as a continuation cell so the renderer
skips it (the terminal already drew it as part of the wide character).

Key changes:
- Cell struct gains  field, defaulting to false
- New  constructor for continuation cells
- Screen's write_str/write_str_centered/write_str_right all place
  continuation cells after wide characters
- Diff renderer skips continuation cells and tracks cursor position
  based on actual character display width
- All widget code that manually sets cell chars now resets
- Input bar handles empty content case to properly clear background
- Add unit tests for wide character rendering and diff tracking
Three related bugs fixed:

1. ConfirmPrompt line_height overcounted: each diff line was counted
   using line_height_for_text (which accounts for wrapping), but the
   actual rendering truncates each diff line to 1 row. Changed to count
   each diff line as exactly 1 row, matching the rendering behavior.

2. Question rendering ignored wrapped row tracking: write_str_wrapped_clipped
   returns the last row written, but the code only did current_row += 1.
   If a question or answer wrapped to multiple lines, subsequent items
   would overlap. Now uses the return value to advance current_row
   correctly.

3. left_margin vs wrap_indent mismatch: User, Assistant, and System text
   rendering used left_margin=area.x (wrapping to left edge) but
   line_height_for_text used wrap_indent equal to the prefix width.
   This caused line_height to overestimate when text wrapped. Fixed all
   rendering calls to use left_margin = area.x + prefix_width, matching
   the wrap_indent used in height calculation.

Also added 3 new tests:
- test_confirm_prompt_line_height_matches_rendering
- test_confirm_prompt_does_not_create_stairs
- test_question_wrapping_height_matches
- Fix dim interface and ghost text bugs: add \x1b[0m reset before each
  cell's style in render_diff() to prevent ANSI attributes (dim, bold,
  background colors) from leaking into subsequent cells

- Move streaming/thinking spinner from conversation area to input bar:
  - Add streaming state to InputBarWidget with animated spinner frames
  - Show '⠋ Thinking…' / '⠋ Responding…' in input bar during streaming
  - Block all keyboard input during streaming except Ctrl+C and Ctrl+D
  - Remove SpinnerWidget from TuiApp (no longer needed separately)

- Clean up unused SpinnerWidget import and field from app.rs
…lp dismiss

- Extract WrapConfig struct to consolidate the three duplicated
  wrapping implementations (write_str_wrapped, write_str_wrapped_clipped,
  write_str_wrapped_skip_clipped) into a single write_wrapped method.
  The original methods are now thin convenience wrappers.
- Add dismiss_help() helper to replace repeated help_visible/help_scroll
  reset pairs in the help overlay event handler.
- Improve ANSI render_diff to deduplicate style attributes and emit
  fewer escape sequences by tracking active fg/bg/style state.
- Clean up else-if chains in help key handling for clarity.
- Add comprehensive unit tests for wrapping, clipping, scrolling,
  style deduplication, and space preservation.
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.

1 participant