pi-mono/packages/tui/test
Petr Houška 2ce9d527d9
fix(tui): drop space after styled word at wrap boundary, reset strikethrough at line end (#11591)
Fixes #10925.

## Problem

`splitIntoTokensWithAnsi()` attaches pending escape codes to the next
visible character. The code that closes a styled word therefore lands on
the following space: `**commodo** consequat` tokenizes as
`\x1b[1mcommodo`, `\x1b[22m `, `consequat`.

`wrapSingleLine()` detected whitespace with `token.trim() === ""`, which
is false for `"\x1b[22m "`. When the styled word ended exactly at the
wrap width, that space was treated as a word and moved to the next line:

```
before: ["aaaa \x1b[1mbold", "\x1b[1m\x1b[22m next", "word"]   (width 9)
after:  ["aaaa \x1b[1mbold\x1b[22m", "next word"]
```

Because the closing code moved to the next line, the style also stayed
open into the right padding of the wrapped line. Separately,
`getLineEndReset()` closed only underline at line end, so strikethrough
that spans a wrap continued into the padding.

## Fix

- Detect whitespace tokens after stripping escape codes.
- When a whitespace token is dropped at a wrap, append its escape codes
to the wrapped line and update the tracker, so the style closes there
and is not re-opened on the next line.
- Close strikethrough (`\x1b[29m`) at line end, like underline.
`getActiveCodes()` re-opens it on the next line. Background is
unaffected.
- Whitespace wider than an empty line that carries codes (e.g.
background spaces followed by a trailing reset) keeps only the codes.
Without this, treating it as whitespace skipped the long-token path, and
the trailing reset blocked the final `trimEnd()`, leaving a line wider
than `width`.
- When a long word follows, the line before it is now trimmed before the
line-end reset is added. Otherwise the reset (now also emitted for
strikethrough) blocks the final `trimEnd()` and leaves trailing spaces.

## Tests

- `wrap-ansi.test.ts`: boundary closing code, dropped space with a color
reset, strikethrough reset and restore across a wrap, strikethrough
reset with background preserved.
- `markdown.test.ts`: padded Markdown with a background at an exact
boundary for bold and strikethrough.
- The new tests fail without the fix. With the old code, rendering the
issue text through `UserMessageComponent` (dark theme, padding 1) at
widths 40-120 produced 5 bad lines at width 78. With the fix it produces
none.
- Fuzzing over 1.2M random styled inputs with SGR and OSC 8 codes found
no line wider than `width`, and no case where the old wrapper laid out
text the same as the unstyled input but the new one does not.
- Benchmark (`wrapTextWithAnsi`, width 77, about 5-18k chars, median of
6 processes) against `main`: plain -5%, light styling -0.5%, heavy
styling +1%, long strikethrough +0%, all within noise. The whitespace
check scans character codes and stops at the first visible text instead
of allocating a stripped copy.
- `npm run check` passes. All `packages/tui` tests pass. `./test.sh`
shows no new failures; the remaining failures are environment-specific:
the clipboard tests also fail on `main` here, and the durable, env
(ssh), and chord failures are in packages that do not depend on
`pi-tui`.
2026-10-29 22:08:18 +01:00
..
fixtures fix(tui): restore terminal modes when the process exits without stop() 2026-10-20 19:52:54 +02:00
alt-screen-large-transcript-bench.ts feat(tui,coding-agent): report program status via OSC 7501 2026-10-07 19:05:41 +00:00
autocomplete-skill-slash.test.ts fix(tui): complete slash commands after leading whitespace (#10218) 2026-09-30 23:13:01 +02:00
autocomplete.test.ts fix(tui): escape regex metacharacters in single-segment fd queries (#11212) 2026-10-21 12:21:15 +02:00
bug-regression-isimageline-startswith-bug.test.ts chore: pin dependencies and use native TypeScript 2026-05-20 12:46:17 +02:00
chat-simple.ts fix(tui): normalize source filenames 2026-08-01 14:38:13 +02:00
colors.test.ts feat(coding-agent,tui): System theme (#10067) 2026-09-26 13:33:47 +02:00
editor-history-keybindings.test.ts feat(tui): add configurable prompt history actions 2026-08-05 21:34:21 +02:00
editor.test.ts fix(tui,coding-agent): handle selection page keys in all scrolling lists 2026-10-28 19:05:21 +01:00
exit-restore.test.ts fix(tui): restore terminal modes when the process exits without stop() 2026-10-20 19:52:54 +02:00
fuzzy.test.ts fix(tui,coding-agent): rank contiguous fuzzy matches and search session fields separately 2026-10-22 21:39:17 +02:00
image-test.ts feat: build with TypeScript 7 and run sources with plain node 2026-09-25 13:14:33 +02:00
input.test.ts feat(tui): search improvements (#8800) 2026-09-03 15:09:51 +02:00
key-tester.ts fix(tui): normalize source filenames 2026-08-01 14:38:13 +02:00
keybindings.test.ts fix(tui): keep Home/End for the editor in fullscreen mode 2026-10-05 09:53:54 +02:00
keys.test.ts fix(tui): recognize kitty F1-F12 sequences and ignore F13+ keys (#11468) 2026-10-27 09:16:36 +01:00
latex.test.ts fix(tui): improve LaTeX compatibility and layouts 2026-09-19 23:28:04 +02:00
layout.test.ts feat(tui): alt mode scrollbar but prettier (#8801) 2026-09-04 00:05:47 +02:00
markdown.test.ts fix(tui): drop space after styled word at wrap boundary, reset strikethrough at line end (#11591) 2026-10-29 22:08:18 +01:00
mouse-components.test.ts feat(coding-agent,tui): System theme (#10067) 2026-09-26 13:33:47 +02:00
native-clipboard-linux.test.ts feat(tui): Simplify clipboard handling (#9163) 2026-09-05 23:52:32 +02:00
native-module-path.test.ts feat(tui): Simplify clipboard handling (#9163) 2026-09-05 23:52:32 +02:00
native-platform.test.ts feat(tui): Simplify clipboard handling (#9163) 2026-09-05 23:52:32 +02:00
overlay-non-capturing.test.ts fix(tui): normalize source filenames 2026-08-01 14:38:13 +02:00
overlay-options.test.ts fix(tui): keep cursor visible when overlays close after stop 2026-09-25 13:30:50 +02:00
overlay-short-content.test.ts fix(tui): normalize source filenames 2026-08-01 14:38:13 +02:00
program-status.test.ts feat(tui,coding-agent): report program status via OSC 7501 2026-10-07 19:05:41 +00:00
regression-overlay-cjk-boundary.test.ts feat(tui): add alternate-screen renderer 2026-07-30 12:59:48 +02:00
regression-regional-indicator-width.test.ts chore: pin dependencies and use native TypeScript 2026-05-20 12:46:17 +02:00
regression-sigwinch-kill-eacces.test.ts fix(tui): wrap SIGWINCH self-signal so restricted seccomp policies do not crash startup (#8898) 2026-09-01 14:16:21 +02:00
regression-slice-by-column-ansi-order.test.ts fix(tui): preserve ANSI order at slice boundaries 2026-10-01 00:05:28 +02:00
render-churn-bench.ts feat(tui,coding-agent): report program status via OSC 7501 2026-10-07 19:05:41 +00:00
select-list.test.ts fix(tui,coding-agent): handle selection page keys in all scrolling lists 2026-10-28 19:05:21 +01:00
settings-list.test.ts fix(tui,coding-agent): handle selection page keys in all scrolling lists 2026-10-28 19:05:21 +01:00
stdin-buffer.test.ts fix(tui): keep delayed terminal replies out of keyboard input 2026-10-11 20:17:00 +02:00
tab-width.test.ts fix(tui): normalize source filenames 2026-08-01 14:38:13 +02:00
terminal-colors.test.ts fix(tui): keep delayed terminal replies out of keyboard input 2026-10-11 20:17:00 +02:00
terminal-image.test.ts feat(tui): enable Kitty images for Rex 2026-10-20 18:54:36 +02:00
terminal.test.ts fix(tui): treat ESC+Return with native Shift held as Shift+Enter 2026-10-29 20:17:54 +01:00
test-themes.ts chore: pin dependencies and use native TypeScript 2026-05-20 12:46:17 +02:00
truncate-to-width.test.ts fix(tui): close truncated OSC 8 links (#7657) 2026-08-05 13:50:37 +02:00
truncated-text.test.ts chore: pin dependencies and use native TypeScript 2026-05-20 12:46:17 +02:00
tui-alt-screen.test.ts fix(tui,coding-agent): handle selection page keys in all scrolling lists 2026-10-28 19:05:21 +01:00
tui-cell-size-input.test.ts fix(tui): normalize source filenames 2026-08-01 14:38:13 +02:00
tui-overlay-style-leak.test.ts fix(tui): normalize source filenames 2026-08-01 14:38:13 +02:00
tui-render.test.ts fix(tui): stop writing OSC 133 prompt markers in regular mode (#11565) 2026-10-29 14:00:16 +01:00
tui-shrink.test.ts fix(tui): normalize source filenames 2026-08-01 14:38:13 +02:00
viewport-overwrite-repro.ts feat: build with TypeScript 7 and run sources with plain node 2026-09-25 13:14:33 +02:00
virtual-terminal.ts feat(tui,coding-agent): report program status via OSC 7501 2026-10-07 19:05:41 +00:00
visible-width.test.ts fix(tui,coding-agent): reduce render cost of theme changes and streaming 2026-09-29 08:23:46 +02:00
wheel-scroll.test.ts feat(tui,coding-agent): configurable fullscreen wheel scrolling with auto acceleration 2026-09-29 09:57:53 +02:00
word-navigation.test.ts fix(tui): align input word segmentation with editor (#5068) 2026-05-27 20:50:04 +02:00
wrap-ansi.test.ts fix(tui): drop space after styled word at wrap boundary, reset strikethrough at line end (#11591) 2026-10-29 22:08:18 +01:00