Skip to content

refactor(painters/dom): unify SDT container rendering (SD-2838)#3322

Merged
harbournick merged 38 commits into
luccas/sd-2838-unify-paragraph-renderingfrom
luccas/sd-2838-unify-sdt-rendering
May 26, 2026
Merged

refactor(painters/dom): unify SDT container rendering (SD-2838)#3322
harbournick merged 38 commits into
luccas/sd-2838-unify-paragraph-renderingfrom
luccas/sd-2838-unify-sdt-rendering

Conversation

@luccas-harbour

Copy link
Copy Markdown
Contributor

Summary

Consolidates SDT (Structured Document Tag) container rendering across paragraphs, tables, and nested tables into a single shared module, then fixes a long tail of nested/split-fragment chrome bugs that the unified pipeline made tractable.

Previously, the duplicate-suppression contract was implicit: cells were handed a raw tableSdt reference and each renderer reimplemented its own metadata comparisons. With nested tables, idless SDT controls, and split fragments, this produced missing borders, duplicated chrome, clipped labels, and chrome that disappeared across page splits. The new pipeline uses a key-based ancestor chain and an upward chrome-rendered callback, so every renderer (paragraph, table fragment, table cell, embedded table) makes the same decisions from the same data.

Changes

New shared module — painters/dom/src/sdt/container.ts (replaces utils/sdt-helpers.ts)

  • Renames applySdtContainerStylingapplySdtContainerChrome
  • Adds shouldRenderSdtContainerChrome, getSdtContainerKeyForBlock, getSdtSiblingBoundaries
  • Adds SdtAncestorOptions type — ancestorContainerKey(s) + ancestorContainerSdt(s) chains for explicit suppression

Contracts (contracts/src/sdt-container.ts) — new file

  • getSdtContainerKey, getSdtContainerMetadata, hasExplicitSdtContainerKey, isSdtContainerMetadata
  • layout-resolved drops the local sdtContainerKey.ts and imports from contracts (single source of truth)
  • idlessSdtContainerKeys WeakMap groups id-less sibling controls by identity, with an AIDEV-NOTE explaining why alias/title matching is unsafe

Renderer threading

  • renderTableFragment, renderTableRow, renderTableCell, renderEmbeddedTable, and renderParagraphContent all accept the ancestorContainerKey(s)/ancestorContainerSdt(s) chain and an onSdtContainerChrome callback
  • Cells/tables flip overflow: visible only when a descendant actually renders chrome, not preemptively based on metadata
  • sdtBoundary start/end flags are now correctly narrowed at split points (paragraph mid-line splits, embedded table mid-row splits)

Nested + split-fragment fixes (each is its own commit)

  • Continue SDT chrome across paragraph splits, partial embedded tables, and nested-table continuations
  • Suppress duplicate chrome when a nested table or cell already inherits the same container
  • Preserve the SDT ancestor chain through nested tables so id-less controls don't re-render
  • Skip media (drawings) at SDT boundaries
  • Use the rendered lock mode (not the resolved one) so the dataset matches what's visually shown
  • Allow nested SDT labels to overflow rather than being clipped
  • Merge idless SDT siblings into a single container instead of fragmenting

Tests

  • New contracts/src/sdt-container.test.ts (42 lines) and painters/dom/src/sdt/container.test.ts (200 lines)
  • renderTableCell.test.ts expanded with ~1.2k lines covering nested SDT cases
  • renderTableFragment.test.ts adds ~195 lines for ancestor/chrome-callback wiring

@luccas-harbour
luccas-harbour requested a review from a team as a code owner May 15, 2026 17:37
@linear-code

linear-code Bot commented May 15, 2026

Copy link
Copy Markdown

SD-2838

@luccas-harbour luccas-harbour self-assigned this May 15, 2026
@luccas-harbour
luccas-harbour force-pushed the luccas/sd-2838-unify-sdt-rendering branch from 8f91a95 to 1ce01ee Compare May 15, 2026 19:52
@harbournick
harbournick requested a review from VladaHarbour May 19, 2026 19:43
@luccas-harbour
luccas-harbour requested review from tupizz and removed request for a team and caio-pizzol May 20, 2026 18:45

@VladaHarbour VladaHarbour left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a minor suggestion
Approve

Comment thread packages/layout-engine/contracts/src/sdt-container.ts
@luccas-harbour
luccas-harbour force-pushed the luccas/sd-2838-unify-sdt-rendering branch 2 times, most recently from 9c587c2 to 6308dac Compare May 21, 2026 19:07
…odule

Move sdt-helpers from utils/ into a dedicated sdt/container.ts module
and rename applySdtContainerStyling → applySdtContainerChrome. Introduce
shouldRenderSdtContainerChrome, getSdtContainerKeyForBlock, and
getSdtSiblingBoundaries so renderer.ts and renderTableCell.ts can share
the same boundary/suppression logic instead of inlining metadata
comparisons. Table cells now receive an ancestorTableSdtKey rather than
the raw tableSdt object, making the duplicate-suppression contract
explicit and key-based.
Move SDT boundary computation, dataset application, snapshot
collection, and inline wrapper helpers out of renderer.ts into
dedicated modules under sdt/. Renames runs/sdt.ts to sdt/inline.ts
and re-exports remain stable. No behavior change.
Extract image <img> element creation, clip-path resolution, and
hyperlink-anchor wrapping into shared helpers under `painters/dom/src/images/`,
and route the renderer's block/drawing image paths plus renderTableCell's
flowing, drawing, and anchored image paths through them. Eliminates five
near-identical copies of the same img setup (objectFit, cover→left-top,
clip-path, filters, hyperlink wrap) so behavior stays consistent across all
image surfaces. Adds coverage for clip-path, filters, and hyperlinks on both
flowing and anchored images inside table cells (SD-2838).
Removes the now-dead deriveBlockVersion machinery from renderer.ts
(paragraph/image/drawing/table version hashing, SDT metadata helpers,
list-marker guards) along with renderedBlockImageVersion in
image-block.ts. No remaining call sites consume these helpers after
the image-block rendering unification.
Move image fragment and drawing image creation out of renderer.ts into
dedicated modules under images/. Renderer now delegates:

- renderImageFragment → images/image-fragment.ts (with the
  buildImageGeometryTransform / applyImageGeometryTransform helpers it
  owned)
- createDrawingImageElement, createShapeGroupImageElement, and the
  inline shape-text image element creation → images/drawing-image.ts

No behavior change — pure extraction. Trims ~157 lines from renderer.ts.
Move image-clip-path and image-selectors (plus their tests) from
utils/ into the images/ directory alongside image-block, drawing-image,
hyperlink, and image-fragment. Update import paths in callers. No
behavior change.
Updates the comment to reflect that the painter-dom copy has been removed —
this is now the single source of truth, not a duplicate to keep in sync.
@luccas-harbour
luccas-harbour force-pushed the luccas/sd-2838-unify-paragraph-rendering branch from 3572c77 to 5e1e81b Compare May 22, 2026 18:49
@luccas-harbour
luccas-harbour force-pushed the luccas/sd-2838-unify-sdt-rendering branch from 6308dac to 01c2e2f Compare May 22, 2026 18:49
@github-actions

Copy link
Copy Markdown
Contributor

Agent docs audit

Found deterministic findings on 1 changed agent-doc item(s).

packages/layout-engine/AGENTS.md (162 lines)

  • 7 broken path ref(s)

Broken path refs:

  • features/feature-registry.ts
  • features/paragraph-borders/
  • src/incrementalLayout.ts
  • src/index.ts
  • src/internal.ts
  • src/renderer.ts
  • table/renderTableCell.ts

Deterministic L1 only: no AI, no Bash, no secrets. Semantic L2/L3 audit runs weekly on main. Policy: agent-docs-policy.md.

@luccas-harbour

Copy link
Copy Markdown
Contributor Author

The broken paths in AGENTS.md were fixed in the top-most PR in this commit.

…-block-rendering

refactor(painters/dom): unify image block rendering (SD-2838)
@harbournick
harbournick merged commit e01b30e into luccas/sd-2838-unify-paragraph-rendering May 26, 2026
6 checks passed
@harbournick
harbournick deleted the luccas/sd-2838-unify-sdt-rendering branch May 26, 2026 18:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants