fix(runtime): concatenate children in screen-reader <Transform> (Ink parity, G23) (#52)

* fix(runtime): concatenate children in screen-reader <Transform> (Ink parity, G23)

The screen-reader transform branch joined children with "\n", producing
newline-separated text where Ink concatenates. In Ink a <Transform> is an
`ink-text` node, so the screen-reader path squashes it via squashTextNodes
(squash-text-nodes.ts:42), which concatenates child text with "". Join with
"" instead of "\n" to match.

The transform node's own fn is intentionally NOT applied: verified empirically
against Ink 7.0.4 that squashTextNodes only applies the internal_transform of
*child* nodes (squash-text-nodes.ts:34-39), never of the top-level node handed
to it. A <Transform> directly under a <Box> is squashed as the top-level node,
so its own transform is skipped — yielding the bare concatenated children.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore(parity): ledger — G23 pr-open, reconcile G22 merged, log G23 spec correction

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Yunfei He
2026-05-30 07:19:55 +08:00
committed by GitHub
parent 0d847f0e48
commit c61ec5f0ca
3 changed files with 43 additions and 4 deletions
+15 -2
View File
@@ -103,7 +103,20 @@ export function renderScreenReaderOutput(node: TuiNode, options: ScreenReaderOpt
.filter(Boolean)
.join(separator);
} else if (node.type === "transform") {
// Transform nodes: render children, then no transform is applied in screen reader mode
// Transform nodes: CONCATENATE children with "" (not newline-join), matching
// Ink's squashTextNodes (squash-text-nodes.ts:42, `text += nodeText`). In Ink
// a <Transform> is an `ink-text` node, so the SR path squashes it via
// squashTextNodes which concatenates child text with "".
//
// The transform's OWN fn is intentionally NOT applied here. Verified
// empirically against Ink 7.0.4: squashTextNodes only applies the
// internal_transform of *child* nodes (squash-text-nodes.ts:34-39), never of
// the top-level node it is handed. When a <Transform> is a direct child of a
// <Box>, the SR renderer calls squashTextNodes(transformNode) directly, so
// the transform node's own internal_transform is skipped — yielding the bare
// concatenated children. A nested <Transform> inside a <Text> DOES get its
// transform applied (see squashTextContent above), because there it is a
// *child* being squashed by its parent text node.
const children = node.children;
output = children
.map((childNode) =>
@@ -113,7 +126,7 @@ export function renderScreenReaderOutput(node: TuiNode, options: ScreenReaderOpt
}),
)
.filter(Boolean)
.join("\n");
.join("");
}
// Add accessibility annotations