Final round-2 test-only batch (behaviors already at parity with Ink reconciler.tsx,
measure-text.tsx, flex-*.tsx, overflow.tsx, build-output.ts):
- build-output: every package.json export target resolves on disk (runtime/cli/testing)
+ the .d.mts declaration sibling for the typed libraries (runtime/testing, not cli).
- reconciler: keyed insert-between [a,c]→[a,b,c]; replace a colored <Text> child with a
plain string; setElementText A→B + the text-context guard; marginLeft removal reset.
- measure: empty <Text> contributes height 0 in a column; non-zero left (marginLeft=5 →
5,1); measureTextNatural trailing/only-newline heights.
- flex: alignSelf='auto' == default + alignSelf removal resets to AUTO; the two
space-around known-yoga-bug cases converted from test.skip to test.fails (they assert
the DESIRED output and flip to a real failure if yoga ever fixes the bug); the documented
flexDirection/flexWrap removal-reset divergence (was comment-only) now has a visual lock.
- overflow: out-of-bounds writes produce Ink's exact clipped frame (sparse past-width cell,
filtered hole) — tightened from toBeDefined().
- components: inline + top-level non-empty fragment in <Text>; the previously-skipped
ST-terminated OSC-8 hyperlink hard-wrap now passes ('abcde\nfghij') — un-skipped as a lock.
Codex-reviewed GENUINE.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
vue-tui left `display` out of RESETTABLE_PROPS, so a removed/undefined `display` persisted
its prior value (a removed display="none" stayed hidden). Adds `display` to RESETTABLE_PROPS
-- the setter already maps undefined -> DISPLAY_FLEX -- so a withdrawn `display` returns to
the Box default (visible), per render = f(current props), like flexDirection/flexWrap (G19).
Deliberate, documented divergence from Ink (which hides on a present-undefined `display` via
DISPLAY_NONE, and persists on omitted) -- recorded in ink-divergences.md. The reset is
consistent across the visual and screen-reader paths (both read yoga's display state).
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A string flexBasis was forwarded to yoga's setFlexBasis, so a bare numeric string like
"3" became 3 absolute cells; Ink coerces ANY string to a percent via Number.parseInt ->
setFlexBasisPercent (styles.ts:547-555). "3" now means 3% (at width 6 the box collapses
to 0 and the sibling takes the row), matching Ink. The setter branch is now structurally
identical to Ink -- number -> absolute, string -> percent, anything else (incl. a
non-number/non-string value Vue's prop validation only warns about) -> setFlexBasisAuto()
instead of throwing.
A separate, pre-existing downstream divergence (zero/negative flexBasis% wraps the
sibling in Ink) is documented with a skipped test -- not caused or fixed here.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(runtime): reset yoga props to default on dynamic removal (Ink parity, G19)
Removed style props now reset to the yoga default (margin/padding/min/gap/flexGrow→0, flexShrink→1, flexBasis→auto, flexDirection→ROW, flexWrap→NO_WRAP, alignItems→STRETCH, alignSelf→AUTO, justifyContent→FLEX_START, position→RELATIVE) instead of keeping a stale value — matches Ink's reconciler diff + styles.ts.
The fix threads the previous prop value (prev) from patchProp into applyYogaProp so that resets only fire on genuine removals (prev is a real value, not null/undefined from Vue's initial-mount or never-set patches). RESETTABLE_PROPS is extended with all newly resettable keys.
Follow-up blocker fixes:
- marginX/marginY/paddingX/paddingY now map to Yoga.EDGE_HORIZONTAL/EDGE_VERTICAL (matching Ink styles.ts) instead of concrete EDGE_START/END/TOP/BOTTOM. They compose with the specific edges per yoga precedence, so removing an axis shorthand no longer clobbers a surviving marginLeft/etc.
- applyYogaProp and all setters now treat null the same as undefined (value == null) for the removal/reset path. Vue's host renderer passes next=null (not undefined) when a key disappears from a spread props object (e.g. Static spreads style into host props), which previously bypassed the reset and forwarded raw null into yoga (NaN/0 corruption).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore(parity): ledger — G19 pr-open, reconcile G18 merged
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Distributes across text, static, transform, newline-spacer,
error-handling, and prop-reset test files.
Tests requiring features not yet in vue-tui are marked test.todo:
- <Transform> inside <Text> (paint pass limitation)
- multi-line transform (transform nodes not yoga carriers)
- text/box nesting validation (no runtime validation yet)
- complex Static rerender patterns (Ink-specific API)
Also fixes wrap="hard" to use wordWrap:false so text breaks at exact
character boundaries rather than word boundaries, matching Ink behavior.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>