Source code for ralph.display.vt_normalizer
"""Utilities for normalizing VT/TUI output into stable semantic text.
The goal is not pixel-perfect replay. It is to collapse common terminal repaint noise
into a transcript surface that downstream Claude-interactive parsers can reason about
without being tightly coupled to one specific TUI paint pattern.
"""
from __future__ import annotations
import re
_ANSI_ESCAPE_RE = re.compile(r"\x1B(?:\[[0-?]*[ -/]*[@-~]|\][^\x1b\x07]*(?:\x07|\x1b\\)|[@-Z\\-_])")
[docs]
def normalize_vt_text(raw: str) -> str:
"""Strip ANSI control noise and collapse carriage-return repaints.
Carriage returns are treated as "rewrite the current line" markers instead of
semantic newlines so spinner updates and partial repaints do not create duplicate
transcript entries.
A lone ``\\r`` clears the current accumulated line (rewrite). However, ``\\r\\r\\n``
or ``\\r\\n`` at content boundaries is treated as a line break, not a double rewrite,
to avoid discarding menu prompts and other multi-line TUI content that uses CR as a
cheap line separator.
"""
ansi_free = _ANSI_ESCAPE_RE.sub("", raw)
current_line = ""
output: list[str] = []
index = 0
length = len(ansi_free)
while index < length:
char = ansi_free[index]
if char == "\r":
if index + 1 < length and ansi_free[index + 1] in ("\r", "\n"):
lookahead = ansi_free[index + 1]
if lookahead == "\n":
output.append(f"{current_line}\n")
current_line = ""
index += 2
continue
if lookahead == "\r" and index + 2 < length and ansi_free[index + 2] == "\n":
output.append(f"{current_line}\n")
current_line = ""
index += 3
continue
current_line = ""
index += 1
continue
if char == "\n":
output.append(f"{current_line}\n")
current_line = ""
index += 1
continue
if char == "\b":
current_line = current_line[:-1]
index += 1
continue
current_line += char
index += 1
if current_line:
output.append(current_line)
return "".join(output)
__all__ = ["normalize_vt_text"]