fix(runtime): hard-wrap at width 0 must use wordWrap:false (Ink parity) (#141)
`<Text wrap="hard">` measured at width 0 dropped one blank row per interior word
boundary, so it measured a shorter height than Ink. Ink's wrap-text.ts uses
`{hard:true, wordWrap:false}` for `hard` mode and `{hard:true}` for `wrap` mode;
vue-tui's width-0 path (wrapZeroWidthAnsi) always used the `wrap` options
regardless of mode.
Thread the wrap mode into wrapZeroWidthAnsi and select
`{hard:true, trim:false, wordWrap:false}` for `hard` (vs `{hard:true, trim:false}`
for `wrap`) at width 0, matching Ink. The non-zero `hard` branch already used
wordWrap:false, so this makes the width-0 path consistent with it. The re-styling
loop is unchanged (extra blank rows pass through as empty strings).
width-0 hard "a b c" now measures 8 rows (['','a',' ','','b',' ','','c']) like
Ink, not 6. `wrap` mode and all non-zero widths are byte-identical.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -346,6 +346,32 @@ test("hard wrap with long word", async () => {
|
||||
expect(lastFrame()).toBe("aaaaa\naaaaa");
|
||||
});
|
||||
|
||||
test("hard wrap at width 0 measures one row per grapheme PLUS a blank row per interior word boundary (Ink parity)", async () => {
|
||||
// Ink wrap-text.ts uses wordWrap:false for `hard` mode: at width 0 that inserts an extra
|
||||
// blank row before each interior word's first grapheme, so "a b c" measures height 8
|
||||
// (["","a"," ","","b"," ","","c"]) — NOT the height-6 `wrap`-mode layout. The 0-width
|
||||
// Box is the tallest child, so the row sibling "X" sits on the FIRST row, and the column
|
||||
// grows to 8 rows. If `hard` were (wrongly) measured with `wrap` structure, the box would
|
||||
// be 6 rows tall.
|
||||
const { lastFrame } = await render(
|
||||
defineComponent(() => () => (
|
||||
<Box flexDirection="row">
|
||||
<Box width={0}>
|
||||
<Text wrap="hard">a b c</Text>
|
||||
</Box>
|
||||
<Text>X</Text>
|
||||
</Box>
|
||||
)),
|
||||
{ columns: 100 },
|
||||
);
|
||||
const lines = stripAnsi(lastFrame()!).split("\n");
|
||||
// 8 rows total (the 0-width hard-wrapped Text dictates the column height).
|
||||
expect(lines.length).toBe(8);
|
||||
// The 0-width column contributes no visible columns, so each row is just the sibling's
|
||||
// contribution on row 0 ("X") and empty rows below — confirming height 8, not 6.
|
||||
expect(lines[0]).toContain("X");
|
||||
});
|
||||
|
||||
test("don't hard wrap text if there is enough space", async () => {
|
||||
const { lastFrame } = await render(
|
||||
defineComponent(() => () => (
|
||||
|
||||
Reference in New Issue
Block a user