Conversation
…s (SD-2557)
When a Word document stores a `w:sectPr` on a paragraph immediately before a
TOC (or other `docPartObj`) SDT, the section break was dropped. The TOC
ended up rendering on the same page as the prior section's content instead
of starting a new page as Word does.
Root cause in pm-adapter:
- `findParagraphsWithSectPr` (sections/analysis.ts) recurses into `index`,
`bibliography`, and `tableOfAuthorities` to count their children as
paragraphs, but NOT into `documentPartObject` / `tableOfContents`.
- As a result, section ranges treat a TOC SDT as a single opaque unit —
its children don't occupy paragraph indices.
- When processing flow, `handleParagraphNode` / `handleIndexNode` /
`handleBibliographyNode` / `handleTableOfAuthoritiesNode` each emit a
pending section break before the paragraph whose index matches
`nextSection.startParagraphIndex`.
- `handleDocumentPartObjectNode` and `handleTableOfContentsNode` did NOT
run this check, so the deferred break only fired on the next body
paragraph AFTER the SDT. The SDT's content rendered in the PREVIOUS
section, with no page break before it.
Fix:
- Add `emitPendingSectionBreakForParagraph(sectionState, nextBlockId,
blocks, recordBlockKind)` helper in sections/breaks.ts that centralizes
the "check, emit, advance" pattern.
- Call the helper at the top of `handleDocumentPartObjectNode` and
`handleTableOfContentsNode` — once per SDT. Since the SDT's children
don't affect `currentParagraphIndex` (`findParagraphsWithSectPr` skips
them), the check fires correctly at the SDT boundary: if the SDT sits
at a section boundary, the nextPage break is emitted so the SDT renders
on a new page.
- No changes to section-range computation — counting stays consistent.
Verified against both fixtures from the issue (Highstreet Proposal Sample,
Heffernan Proposal Sample): cover stays on its own page, TOC starts on a
new page, matching Word.
Tests:
- 3 new unit tests in document-part-object.test.ts covering:
- Section break emitted at SDT boundary
- No emission when SDT is not at a section boundary
- No-op when sectionState is undefined
- 1740 pm-adapter tests pass (up from 1737), 604 layout-engine tests pass.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…ld emission (SD-2557)
The initial fix only handled the sectPr BEFORE a TOC docPartObj (on the
empty paragraph that precedes the SDT). It missed the SECOND sectPr that
Word commonly stores on the trailing empty paragraph INSIDE the TOC SDT —
which signals "TOC section ends here, next section starts on new page".
Because `findParagraphsWithSectPr` did not recurse into `documentPartObject`
or `tableOfContents`, that inner sectPr was never discovered, so no
section-range boundary was built between the TOC and the following body
content. SuperDoc rendered the TOC and the first body section stacked on
the same page (just one page later than before the first fix).
Complete fix:
1. `findParagraphsWithSectPr` (sections/analysis.ts) now recurses into
`documentPartObject` and `tableOfContents` in addition to `index` /
`bibliography` / `tableOfAuthorities`. This lets section-range analysis
see sectPrs stored anywhere inside a TOC SDT.
2. `handleDocumentPartObjectNode` (non-TOC branch) emits the pending
section break before each child paragraph and advances
`currentParagraphIndex` — matching the pattern in `handleIndexNode`,
`handleBibliographyNode`, and `handleTableOfAuthoritiesNode`.
3. `processTocChildren` (toc.ts) accepts `sectionState` via its context
arg and performs the same per-child emit + increment dance as the
paragraph handlers. This handles the TOC branch of
`handleDocumentPartObjectNode` and the nested `tableOfContents`
recursion path.
With all three changes, the Highstreet fixture now renders exactly like
Word:
- Page 1: cover
- Page 2: TOC alone
- Page 3: ABOUT US body
- Page 4: ON BEHALF OF HIGHSTREET
- Page 5: WORKERS COMPENSATION
Tests:
- 4 new tests in document-part-object.test.ts (non-TOC emission,
non-boundary no-op, undefined state, sectionState passthrough to
processTocChildren)
- 1741/1741 pm-adapter, 604/604 layout-engine, 11377/11377 super-editor
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 158dd29d64
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
…fore-tocdocpartobj-content-is
|
@luccas-harbour instead of the painter PRs, mind reviewing this one pls? thank you! |
…557) Collapse handleTableOfContentsNode into a delegation to processTocChildren so direct `tableOfContents` nodes share the same code path as the `documentPartObject` TOC gallery. Keeps the section-range counting contract from findParagraphsWithSectPr consistent across both paths: every TOC child paragraph advances sectionState.currentParagraphIndex, so deferred section breaks fire at the correct boundary. - Gate applyTocMetadata sdt fabrication on gallery — a direct tableOfContents PM node has no enclosing w:sdt, so we no longer invent a docPartObject SDT metadata entry on its entries. - Forward themeColors through processTocChildren to the paragraph converter; also pass it in from handleDocumentPartObjectNode. - Add regression tests for per-child index advancement, sdt gating, and themeColors pass-through. Addresses PR #2872 review feedback.
|
LGTM @harbournick |
|
🎉 This PR is included in superdoc v1.28.0-next.13 The release is available on GitHub release |
|
🎉 This PR is included in superdoc-cli v0.8.0-next.13 The release is available on GitHub release |
|
🎉 This PR is included in superdoc-sdk v1.6.0-next.49 |
|
🎉 This PR is included in superdoc v1.29.0 The release is available on GitHub release |
|
🎉 This PR is included in @superdoc-dev/mcp v0.3.0-next.1 The release is available on GitHub release |
|
🎉 This PR is included in superdoc-sdk v1.8.0-next.1 |
|
🎉 This PR is included in esign v2.6.0 The release is available on GitHub release |
|
🎉 This PR is included in template-builder v1.7.0 The release is available on GitHub release |
|
🎉 This PR is included in superdoc-cli v0.8.0 The release is available on GitHub release |
|
🎉 This PR is included in superdoc-sdk v1.8.0 |
|
🎉 This PR is included in @superdoc-dev/mcp v0.3.0 The release is available on GitHub release |
Summary
When a Word document stores a
w:sectPron a paragraph immediately before a TOC (or otherdocPartObj) SDT, the nextPage section break was dropped during pm-adapter conversion. The TOC rendered on the same page as the prior section's content instead of starting a new page as Word does.Linear: SD-2557
Root cause
In
packages/layout-engine/pm-adapter/:findParagraphsWithSectPr(sections/analysis.ts) recurses intoindex,bibliography, andtableOfAuthoritiesto count their children as paragraphs, but NOT intodocumentPartObject/tableOfContents.handleParagraphNode,handleIndexNode,handleBibliographyNode, andhandleTableOfAuthoritiesNodeeach emit a pending section break before the paragraph whose index matchesnextSection.startParagraphIndex.handleDocumentPartObjectNodeandhandleTableOfContentsNodedid NOT run this check — so the deferred break only fired on the next body paragraph AFTER the SDT, leaving the SDT's content in the previous section with no page break before it.Fix
Add an
emitPendingSectionBreakForParagraph(sectionState, nextBlockId, blocks, recordBlockKind)helper insections/breaks.tsthat centralizes the "check, emit, advance" pattern, then call it at the top ofhandleDocumentPartObjectNodeandhandleTableOfContentsNode— once per SDT. Since the SDT's children don't affectcurrentParagraphIndex(findParagraphsWithSectPrskips them), the check fires correctly at the SDT boundary: if the SDT sits at a section boundary, the nextPage break is emitted so the SDT renders on a new page.No changes to section-range computation — counting stays consistent.
Verification
Both fixtures from the Linear issue:
Side-by-side page-by-page PDF available at
/tmp/sd-2557-fixtures/SD-2557-comparison.pdf.Test plan
document-part-object.test.ts:@superdoc/pm-adaptertests pass (up from 1737)@superdoc/layout-enginetests passDemo
Related