fix: warn and skip insertion when Box is nested inside Text

Detect invalid <Box> inside <Text> nesting at the DOM insert level and
emit a dev warning instead of crashing the WASM yoga engine. The box
insertion is skipped to prevent layout corruption.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Yunfei He
2026-05-26 01:04:16 +08:00
parent 45b9ed3d9d
commit 37c4905bbc
3 changed files with 47 additions and 3 deletions
+25
View File
@@ -67,6 +67,16 @@ const STYLE_PROPS = new Set([
"overflowY",
]);
/** Walk up the DOM tree to check if we're inside a text or virtual-text context. */
function isInsideTextContext(node: TuiContainer): boolean {
let current: TuiContainer | null = node;
while (current) {
if (current.type === "text" || current.type === "virtual-text") return true;
current = current.parent;
}
return false;
}
export function buildNodeOps(options: TtyRendererOptions): RendererOptions<TuiNode, TuiNode> {
const { onCommit } = options;
@@ -132,6 +142,21 @@ export function buildNodeOps(options: TtyRendererOptions): RendererOptions<TuiNo
}
const parentC = parent as TuiContainer;
// Dev warning: <Box> inside <Text> is invalid (matches Ink's validation).
// Inserting a box into a text context corrupts the yoga WASM layout engine.
if (
process.env["NODE_ENV"] !== "production" &&
child.type === "box" &&
isInsideTextContext(parentC)
) {
// eslint-disable-next-line no-console
console.warn(
"[vue-tui] A <Box> cannot be nested inside a <Text> component. " +
"Wrap it in a sibling <Box> instead.",
);
return; // Skip insertion to prevent WASM crash
}
// Move semantics: if the child is already mounted (Vue's keyed reorder
// emits insert(existingChild, parent, newAnchor) without a prior remove),
// detach it from its current DOM and yoga positions before re-inserting.