From ddac248d26a313d0badc8cdffaafd4de947639b6 Mon Sep 17 00:00:00 2001 From: Yunfei He Date: Sun, 24 May 2026 23:16:58 +0800 Subject: [PATCH] fix(runtime): set Static yoga node to display:none in dynamic layout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Static content is rendered via paintIsolated (separate channel), but its yoga node was still participating in the dynamic frame layout calculation, adding blank lines equal to the Static content height between the last Static item and the dynamic content below. Setting display:none on the Static yoga node removes it from the dynamic layout. paintIsolated is unaffected — it reparents children into a temporary yoga tree. --- .../integration/components/static.test.tsx | 25 +++++++++++++++++++ packages/runtime/src/host/yoga.ts | 5 ++++ 2 files changed, 30 insertions(+) diff --git a/packages/runtime-tests/integration/components/static.test.tsx b/packages/runtime-tests/integration/components/static.test.tsx index 01713c7..e01bb82 100644 --- a/packages/runtime-tests/integration/components/static.test.tsx +++ b/packages/runtime-tests/integration/components/static.test.tsx @@ -130,3 +130,28 @@ test("Static flush clears the dynamic frame first (non-debug mode)", async () => app.unmount(); }); + +test("Static items do not add blank lines to the dynamic frame", async () => { + const items = ref([]); + + const App = defineComponent(() => () => ( + + + {{ + default: ({ item }: { item: string }) => {item}, + }} + + [live] + + )); + + const { lastFrame } = await render(App); + expect(lastFrame()).toBe("[live]"); + + items.value = ["a", "b", "c"]; + await nextTick(); + + // The dynamic frame should ONLY contain [live], no blank lines from Static + const frame = lastFrame()!; + expect(frame).toBe("[live]"); +}); diff --git a/packages/runtime/src/host/yoga.ts b/packages/runtime/src/host/yoga.ts index 9b6c996..721e7a7 100644 --- a/packages/runtime/src/host/yoga.ts +++ b/packages/runtime/src/host/yoga.ts @@ -43,6 +43,11 @@ function hasYoga(node: TuiNode): node is YogaCarrier { export function attachYoga(node: YogaCarrier): void { node.yoga = createYogaNode(); + // Static nodes are painted via a separate channel (paintIsolated), so they + // must not occupy space in the dynamic frame's yoga layout. + if (node.type === "static") { + (node.yoga as YogaNode).setDisplay(Yoga.DISPLAY_NONE); + } } export function detachYoga(node: YogaCarrier): void {