From b2f8f02b1499c5ddb580aac469f41cf761ac9035 Mon Sep 17 00:00:00 2001 From: leineveber Date: Thu, 9 Apr 2026 12:48:39 +0300 Subject: [PATCH 1/2] fix(transient-render-engine): preserve nested inline boundary spaces Keep boundary spaces inside named inline wrappers until the parent phrasing node collapses sibling whitespace. Add a regression test for nested inline tags that previously rendered "foobar" instead of "foo bar". Made-with: Cursor --- .../src/flow/__tests__/collapse.test.ts | 13 +++++++++++++ .../src/tree/TPhrasingCtor.ts | 8 ++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/transient-render-engine/src/flow/__tests__/collapse.test.ts b/packages/transient-render-engine/src/flow/__tests__/collapse.test.ts index 21bf4329..944cf9f2 100644 --- a/packages/transient-render-engine/src/flow/__tests__/collapse.test.ts +++ b/packages/transient-render-engine/src/flow/__tests__/collapse.test.ts @@ -37,6 +37,19 @@ describe('collapse function', () => { const ttree = makeTTree('foo bar'); expect(ttree).toMatchSnapshot(); }); + it('should preserve boundary spaces wrapped in nested inline phrasing tags', () => { + const ttree = makeTTree( + 'foo bar' + ); + const [firstSpan, secondSpan] = ttree.children; + expect(firstSpan.tagName).toBe('span'); + expect(firstSpan.children).toHaveLength(2); + expect((firstSpan.children[0] as TTextImpl).data).toBe('foo'); + expect((firstSpan.children[1] as TTextImpl).data).toBe(' '); + expect(secondSpan.tagName).toBe('span'); + expect(secondSpan.children).toHaveLength(1); + expect((secondSpan.children[0] as TTextImpl).data).toBe('bar'); + }); it('should handle nested anchors', () => { const ttree = makeTTree(nestedHyperlinksSource); expect(ttree).toMatchSnapshot(); diff --git a/packages/transient-render-engine/src/tree/TPhrasingCtor.ts b/packages/transient-render-engine/src/tree/TPhrasingCtor.ts index bcf61313..5ad6bacb 100644 --- a/packages/transient-render-engine/src/tree/TPhrasingCtor.ts +++ b/packages/transient-render-engine/src/tree/TPhrasingCtor.ts @@ -48,8 +48,12 @@ TPhrasingCtor.prototype.collapseChildren = function collapseChildren() { } previous = childK; }); - this.trimLeft(); - this.trimRight(); + // Preserve boundary spaces for named inline wrappers (e.g. styled spans) so + // their parent phrasing container can collapse sibling boundaries correctly. + if (this.tagName === null) { + this.trimLeft(); + this.trimRight(); + } return null; }; From 6f21b3fd46b679486aae7023c9ba09eedf0de983 Mon Sep 17 00:00:00 2001 From: leineveber Date: Mon, 20 Apr 2026 20:38:42 +0300 Subject: [PATCH 2/2] test(transient-render-engine): snapshot nested inline whitespace --- .../__snapshots__/collapse.test.ts.snap | 12 ++++++++++++ .../src/flow/__tests__/collapse.test.ts | 18 ++++++++++-------- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/packages/transient-render-engine/src/flow/__tests__/__snapshots__/collapse.test.ts.snap b/packages/transient-render-engine/src/flow/__tests__/__snapshots__/collapse.test.ts.snap index 33a016c4..5152de51 100644 --- a/packages/transient-render-engine/src/flow/__tests__/__snapshots__/collapse.test.ts.snap +++ b/packages/transient-render-engine/src/flow/__tests__/__snapshots__/collapse.test.ts.snap @@ -140,6 +140,18 @@ exports[`collapse function should not collapse when white-space CSS property is `; +exports[`collapse function should preserve boundary spaces wrapped in nested inline phrasing tags 1`] = ` + + + + + + + + + +`; + exports[`collapse function should remove children from TPhrasing nodes which are empty after timming 1`] = ` diff --git a/packages/transient-render-engine/src/flow/__tests__/collapse.test.ts b/packages/transient-render-engine/src/flow/__tests__/collapse.test.ts index 944cf9f2..2769cee7 100644 --- a/packages/transient-render-engine/src/flow/__tests__/collapse.test.ts +++ b/packages/transient-render-engine/src/flow/__tests__/collapse.test.ts @@ -41,14 +41,16 @@ describe('collapse function', () => { const ttree = makeTTree( 'foo bar' ); - const [firstSpan, secondSpan] = ttree.children; - expect(firstSpan.tagName).toBe('span'); - expect(firstSpan.children).toHaveLength(2); - expect((firstSpan.children[0] as TTextImpl).data).toBe('foo'); - expect((firstSpan.children[1] as TTextImpl).data).toBe(' '); - expect(secondSpan.tagName).toBe('span'); - expect(secondSpan.children).toHaveLength(1); - expect((secondSpan.children[0] as TTextImpl).data).toBe('bar'); + expect(ttree).toMatchSnapshot(); + }); + it('should collapse consecutive boundary spaces wrapped in nested inline phrasing tags', () => { + const twoSpaces = makeTTree( + 'foo bar' + ); + const oneSpace = makeTTree( + 'foo bar' + ); + expect(twoSpaces).toEqual(oneSpace); }); it('should handle nested anchors', () => { const ttree = makeTTree(nestedHyperlinksSource);