Skip to content

Commit 96acb8a

Browse files
Fix transcript mode rendering issue when showing tab chars (#4911)
There's a weird rendering issue with transcript mode: Tab chars bleed through when scrolling up/down. e.g. `nl -ba ...` adds tab chars to each line, which make scrolling look glitchy in transcript mode. Before: https://github.com/user-attachments/assets/631ee7fc-6083-4d35-aaf0-a0b08e734470 After: https://github.com/user-attachments/assets/bbba6111-4bfc-4862-8357-0f51aa2a21ac
1 parent 687a13b commit 96acb8a

File tree

1 file changed

+20
-1
lines changed
  • codex-rs/ansi-escape/src

1 file changed

+20
-1
lines changed

codex-rs/ansi-escape/src/lib.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,30 @@ use ansi_to_tui::IntoText;
33
use ratatui::text::Line;
44
use ratatui::text::Text;
55

6+
// Expand tabs in a best-effort way for transcript rendering.
7+
// Tabs can interact poorly with left-gutter prefixes in our TUI and CLI
8+
// transcript views (e.g., `nl` separates line numbers from content with a tab).
9+
// Replacing tabs with spaces avoids odd visual artifacts without changing
10+
// semantics for our use cases.
11+
fn expand_tabs(s: &str) -> std::borrow::Cow<'_, str> {
12+
if s.contains('\t') {
13+
// Keep it simple: replace each tab with 4 spaces.
14+
// We do not try to align to tab stops since most usages (like `nl`)
15+
// look acceptable with a fixed substitution and this avoids stateful math
16+
// across spans.
17+
std::borrow::Cow::Owned(s.replace('\t', " "))
18+
} else {
19+
std::borrow::Cow::Borrowed(s)
20+
}
21+
}
22+
623
/// This function should be used when the contents of `s` are expected to match
724
/// a single line. If multiple lines are found, a warning is logged and only the
825
/// first line is returned.
926
pub fn ansi_escape_line(s: &str) -> Line<'static> {
10-
let text = ansi_escape(s);
27+
// Normalize tabs to spaces to avoid odd gutter collisions in transcript mode.
28+
let s = expand_tabs(s);
29+
let text = ansi_escape(&s);
1130
match text.lines.as_slice() {
1231
[] => "".into(),
1332
[only] => only.clone(),

0 commit comments

Comments
 (0)