Fix TUI layout for halfwidth Japanese sound marks (#35962)

## What changed

- Use Ratatui-compatible terminal widths throughout TUI layout, preserving
  full-width counters for long lines.
- Wrap and truncate on grapheme boundaries so halfwidth kana sound marks and
  emoji sequences stay intact.
- Apply the corrected sizing to text input, selection popups, status and
  history cells, Markdown, hyperlinks, and scrollable cloud diffs.

## Testing

- Add focused unit and snapshot coverage for wrapping, truncation, cursor and
  mask alignment, popup columns, status output, session headers, Markdown
  tables, process output, and scrollable diffs.

GitOrigin-RevId: 294ec0bcff743c6bb16d55ec26308524ce741879
This commit is contained in:
Charlie Marsh authored and copyberry committed 2026-07-29 14:01:31 +00:00
1 parent cef3910ea4
commit 3725f02cf3
38 files changed
+1335 -245

No files matched your search

+36
View File
@@ -1525,6 +1525,42 @@ async fn status_snapshot_truncates_in_narrow_terminal() {
assert_snapshot!(sanitized);
}
#[tokio::test]
async fn status_snapshot_truncates_halfwidth_kana_in_narrow_terminal() {
let temp_home = TempDir::new().expect("temp home");
let mut config = test_config(&temp_home).await;
set_workspace_cwd(&mut config, test_path_buf("/workspace/tests").abs());
let account = StatusAccountDisplay::ChatGpt {
email: Some("ガパガパガパ@example.com".to_string()),
plan: Some("ガパ plan".to_string()),
};
let usage = TokenUsage::default();
let now = chrono::Local
.with_ymd_and_hms(2024, 1, 2, 3, 4, 5)
.single()
.expect("timestamp");
let composite = new_status_output(
&config,
Some(&account),
/*token_info*/ None,
&usage,
&None,
Some("ガパガパガパガパ thread".to_string()),
/*forked_from*/ None,
/*rate_limits*/ None,
/*plan_type*/ None,
now,
"ガパガパガパガパ-model",
Some("ガパ collaboration mode"),
/*reasoning_effort_override*/ None,
);
let rendered_lines = render_lines(&composite.display_lines(/*width*/ 42));
let sanitized = sanitize_directory(rendered_lines).join("\n");
assert_snapshot!(sanitized);
}
#[tokio::test]
async fn status_snapshot_shows_missing_limits_message() {
let temp_home = TempDir::new().expect("temp home");