Found while implementing #5470 (rewriting apps/console/src/sdui-tiers-preview.tsx's
page sources off Tailwind). Filing rather than fixing — outside that card's declared file
surface, and it is in packages/sdui-parser, not the console.
What happens
packages/sdui-parser/src/parse.ts:177, inside the children loop:
consttext=this.readTextRun();consttrimmed=text.replace(/\s+/g,' ').trim();if(trimmed)children.push(trimmed);
The collapse (\s+ → one space) is correct and matches HTML's own whitespace model.
The .trim() is not: it deletes the leading and trailing space of every text run,
including the space that separates that run from an adjacent inline element. HTML
collapses a whitespace run to one space; it does not remove it.
Measured
Rendered through the real PageRenderer in Chromium against
apps/console/sdui-tiers-preview.html (vite dev), reading textContent:
| source | rendered |
|---|
A <strong …>kind:'html'</strong> page — … | Akind:'html'page — … |
Full HTML tag set in the <em …>html</em> tier. | Full HTML tag set in thehtmltier. |
A trusted <a …>react tier</a> behind a flag. | A trustedreact tierbehind a flag. |
Present before and after#5470's change, byte-for-byte identical output — measured by
checking out apps/console/src/sdui-tiers-preview.tsx at 7e811687a (26 classNames in
its page sources, i.e. the pre-change file) and re-rendering the same page:
NEW "intro": "Akind:'html'page — native HTML tags and the blocks' structured props, …"
BASE "intro": "Akind:'html'page — plain native HTML tags, Tailwind classes, …"
So this is not a regression from that card — it is the tier's own behaviour, and it has
been visible in the repo's own worked example for as long as the example has existed.
Why it matters beyond the preview
kind:'html' is the tier the guide tells authors and AI agents to reach for by default
(content/docs/guide/react-pages.md §Choosing between them), and the tag set it advertises
is exactly the prose set — strong, em, a, code, b, i, abbr, q, cite
(packages/components/src/renderers/basic/html-elements.tsx). Any authored sentence with
emphasis or a link inside it renders with the words run together. It is silent: the page
renders, the structure is right, nothing reports anything.
What needs deciding
The fix is small but the exact rule is worth stating rather than guessing, because it is a
save-time parse that pins metadata:
- (a) Replace
.trim() with HTML's rule — collapse the run to a single space, and drop
a run only when it is entirely whitespace and sits between block-level boundaries.
Highest fidelity; needs a decision about what counts as a boundary in a schema tree that
has no block/inline distinction of its own. - (b) Narrower: keep one leading/trailing space whenever the run is adjacent to a
sibling element (rather than to the start/end of the parent). Mechanical, no boundary
taxonomy, slightly over-generous inside block containers like <ul>.
Either way this changes parser output for existing sources, so it wants a pin in
packages/sdui-parser/src/__tests__ and a look at whether any snapshot depends on the
current trimming.
Found while implementing #5470 (rewriting
apps/console/src/sdui-tiers-preview.tsx'spage sources off Tailwind). Filing rather than fixing — outside that card's declared file
surface, and it is in
packages/sdui-parser, not the console.What happens
packages/sdui-parser/src/parse.ts:177, inside the children loop:The collapse (
\s+→ one space) is correct and matches HTML's own whitespace model.The
.trim()is not: it deletes the leading and trailing space of every text run,including the space that separates that run from an adjacent inline element. HTML
collapses a whitespace run to one space; it does not remove it.
Measured
Rendered through the real
PageRendererin Chromium againstapps/console/sdui-tiers-preview.html(vite dev), readingtextContent:A <strong …>kind:'html'</strong> page — …Akind:'html'page — …Full HTML tag set in the <em …>html</em> tier.Full HTML tag set in thehtmltier.A trusted <a …>react tier</a> behind a flag.A trustedreact tierbehind a flag.Present before and after#5470's change, byte-for-byte identical output — measured by
checking out
apps/console/src/sdui-tiers-preview.tsxat7e811687a(26classNames inits page sources, i.e. the pre-change file) and re-rendering the same page:
So this is not a regression from that card — it is the tier's own behaviour, and it has
been visible in the repo's own worked example for as long as the example has existed.
Why it matters beyond the preview
kind:'html'is the tier the guide tells authors and AI agents to reach for by default(
content/docs/guide/react-pages.md§Choosing between them), and the tag set it advertisesis exactly the prose set —
strong,em,a,code,b,i,abbr,q,cite(
packages/components/src/renderers/basic/html-elements.tsx). Any authored sentence withemphasis or a link inside it renders with the words run together. It is silent: the page
renders, the structure is right, nothing reports anything.
What needs deciding
The fix is small but the exact rule is worth stating rather than guessing, because it is a
save-time parse that pins metadata:
.trim()with HTML's rule — collapse the run to a single space, and dropa run only when it is entirely whitespace and sits between block-level boundaries.
Highest fidelity; needs a decision about what counts as a boundary in a schema tree that
has no block/inline distinction of its own.
sibling element (rather than to the start/end of the parent). Mechanical, no boundary
taxonomy, slightly over-generous inside block containers like
<ul>.Either way this changes parser output for existing sources, so it wants a pin in
packages/sdui-parser/src/__tests__and a look at whether any snapshot depends on thecurrent trimming.