Is your feature request related to a problem?
A text run can't carry pre-formatted terminal output. Every non-printable byte in the content is mapped to U+FFFD before it reaches the screen, so any embedded escape sequence is destroyed. Rendering text(ESC + "[32mgreen" + ESC + "[0m") produces �[32mgreen�[0m—the structural ESC bytes become replacement glyphs and the rest leaks through as literal characters, so the terminal never interprets the color. Captured subprocess output, a pre-styled string, or any text that already contains escapes can't pass through intact.
Describe the solution you'd like
An opt-in passthrough flag that emits the run's bytes verbatim—no U+FFFD substitution and no per-run SGR wrapper—so embedded escapes reach the terminal unchanged:
text(preStyled, { passthrough: true });
Width accounting should still count only the printable cells (ignoring the zero-width escape bytes) so layout stays correct.
Describe alternatives you've considered
Stripping escapes in JS and re-expressing them via text({ color }) loses anything we don't model (cursor moves, OSC, mixed styling) and re-implements an ANSI parser on the JS side. The point of passthrough is to hand the renderer bytes it shouldn't touch.
Additional context
Failing test case on nm/repro/text-passthrough (test · diff).
Today emit_ch maps non-printables to U+FFFD (src/clayterm.c:155) and render_text wraps each run in its own SGR; passthrough needs a flag on the text op (ops.ts) that the text decode in reduce (src/clayterm.c:562) threads to a verbatim-copy path.
Is your feature request related to a problem?
A
textrun can't carry pre-formatted terminal output. Every non-printable byte in the content is mapped toU+FFFDbefore it reaches the screen, so any embedded escape sequence is destroyed. Renderingtext(ESC + "[32mgreen" + ESC + "[0m")produces�[32mgreen�[0m—the structuralESCbytes become replacement glyphs and the rest leaks through as literal characters, so the terminal never interprets the color. Captured subprocess output, a pre-styled string, or any text that already contains escapes can't pass through intact.Describe the solution you'd like
An opt-in
passthroughflag that emits the run's bytes verbatim—noU+FFFDsubstitution and no per-run SGR wrapper—so embedded escapes reach the terminal unchanged:Width accounting should still count only the printable cells (ignoring the zero-width escape bytes) so layout stays correct.
Describe alternatives you've considered
Stripping escapes in JS and re-expressing them via
text({ color })loses anything we don't model (cursor moves, OSC, mixed styling) and re-implements an ANSI parser on the JS side. The point of passthrough is to hand the renderer bytes it shouldn't touch.Additional context
Failing test case on
nm/repro/text-passthrough(test · diff).Today
emit_chmaps non-printables toU+FFFD(src/clayterm.c:155) andrender_textwraps each run in its own SGR; passthrough needs a flag on thetextop (ops.ts) that the text decode inreduce(src/clayterm.c:562) threads to a verbatim-copy path.