fix(super-converter): normalize single-paragraph BIBLIOGRAPHY/INDEX/TOA field content (SD-3005)#3538
Merged
Merged
Conversation
β¦OA field content (SD-3005) When a BIBLIOGRAPHY, INDEX, or TOA field envelope sits inside one <w:p>, preProcessNodesForFldChar collects after-separate <w:r> runs as the field's nodesToCombine. The bibliography / index / tableOfAuthorities PM nodes declare content: 'paragraph+', so wrapping those loose runs directly inside <sd:bibliography> (etc.) violates the schema and crashes the editor on import β the document area renders blank and the console shows "Invalid content for node type bibliography". Add a shared normalizeFieldContentToParagraphs helper that groups adjacent non-w:p nodes into synthesized paragraphs, preserves existing w:p children as-is, and returns one empty paragraph for empty input. Wire it into the bibliography, index, and toa preprocessors so the three field families share one normalization contract. The empty-fallback behavior previously only existed in bibliography-preprocessor.js; aligning all three families on the new helper also fixes the same latent crash in single-paragraph INDEX (SD-3017) and TOA fields. Linear: SD-3005 (parent epic SD-3066)
e4fbca4 to
e883c50
Compare
Codecov Reportβ All modified and coverable lines are covered by tests. π’ Thoughts on this report? Let us know! |
There was a problem hiding this comment.
cubic analysis
No issues found across 7 files
Linked issue analysis
Linked issue: SD-3005: Feature: Bibliography
| Status | Acceptance criteria | Notes |
|---|---|---|
| β | Normalize single-paragraph BIBLIOGRAPHY field content into paragraph children to avoid PM schema errors (prevent import-time crash) | A new helper normalizeFieldContentToParagraphs was added and wired into bibliography-preprocessor, converting loose runs into synthesized nodes; tests exercise the single-paragraph case. |
| β | Apply same normalization to INDEX and TOA preprocessors to prevent the same crash class | Index and TOA preprocessors were updated to call the same helper and tests for INDEX/TOA single-paragraph cases were added. |
| β | Synthesize a single empty when the field has no rendered content | The helper returns a single empty paragraph for empty input and tests assert the synthesized empty paragraph for INDEX and TOA. |
| β | Preserve existing w:p children as-is for multi-paragraph fields | The normalization preserves existing nodes; tests validate multi-paragraph preservation behavior. |
| β | Preserve instruction text / switches and instructionTokens (e.g., "\l 1033") on sd:* nodes | Preprocessors continue to set the instruction attribute (and include instructionTokens when provided); tests assert instructionTokens/attribute presence and the PR notes browser verification preserving switches. |
| β | Add unit tests and ensure local test suites pass (no regressions in affected area) | Unit tests were added/updated for bibliography/index/toa preprocessors and the PR test plan reports all related vitest runs passing. |
| Browser verification against synthetic fixtures (multi-paragraph, single-paragraph, empty, switches preserved) | The PR description claims manual browser verification was performed and succeeded, but no runnable artifacts or verification logs are included in the diff/changeset. | |
| β | Layout-corpus comparison (pnpm layout:compare) to confirm render parity against corpus/golden docs | The PR test plan left the layout-corpus comparison unchecked and recommends gating it in CI; this comparison has not been completed in this PR. |
This was referenced May 29, 2026
β¦ty (SD-3066) (#3565) * fix(super-converter): bibliography/index/TOA field-code import fidelity (SD-3066) Real Word documents surfaced several import defects in the block field-code family (BIBLIOGRAPHY, INDEX, XE, TOA). This addresses them and unifies the shared shape behind one set of helpers. Fixes: - Multi-run instruction aggregation joined fragments with an injected separator space, corrupting instructions Word splits across runs (e.g. `XE " Building Standard "`). Join verbatim; literal spacing is preserved. - Table of Authorities was dropped on import: the v2 importer had no `sd:tableOfAuthorities` handler, so the node was silently discarded. Register tableOfAuthoritiesImporter alongside index/bibliography. - A content control wrapping a block field imported as an inline `structuredContent` node; inside a block-only documentPartObject this threw "Invalid content for node type documentPartObject" and the editor failed to mount. Classify an SDT whose content is a block field as structuredContentBlock. - Bibliography neither captured nor replayed instructionTokens (unlike index/toa), so split BIBLIOGRAPHY instructions did not round-trip. Add the attribute and wire it through preprocessor, encode and decode. Refactors (DRY/KISS): - Extract buildBlockFieldNode (shared by the bibliography/index/toa preprocessors) and wrapParagraphsAsComplexField (shared by their translator decoders), mirroring the existing inline-field helpers. - Centralize BLOCK_FIELD_XML_NAMES so the paragraph importer and SDT classifier agree on which sd:* nodes are block content. Adds REDβGREEN unit tests for each fix and the new shared helpers. Full super-converter suite passes (2990). Linear: SD-3066 (parent of SD-3005) * fix(pm-adapter): render bibliography/index/TOA inside documentPartObject (SD-3066) (#3566) Word wraps a generated bibliography (and other block fields) in a docPartObject SDT, sometimes via a nested content control. The handler only converted paragraph and tableOfContents children to flow blocks, so the field's entry paragraphs were silently dropped β the heading rendered but the entries did not. - documentPartObject now renders bibliography/index/tableOfAuthorities children via the shared paragraph-container handler, and structuredContentBlock children via their handler. - structuredContentBlock recurses block-field children (transparent wrapper), rendering their paragraphs. Section-counting invariant: findParagraphsWithSectPr recurses bibliography (so the handler advances currentParagraphIndex per entry) but not structuredContentBlock (so the scb path renders without advancing). Both paths were validated against the invariant; the prior code dropped entries AND under-counted, which could drift section breaks. Also extracts the shared handleParagraphContainerNode used by the bibliography, index and tableOfAuthorities handlers (previously three byte-identical copies). Regression tests cover both nesting shapes and the counter behavior. pm-adapter suite passes (302); layout corpus comparison shows no regressions attributable to this change. Linear: SD-3066 * feat(painter-dom): custom SDT styling variables under chrome:'none' (SD-3322) Under modules.contentControls.chrome:'none' the painter erased the SDT look entirely, so a consumer who wanted a custom field/clause appearance had to target the painted wrapper with !important and reach into internal state classes (.ProseMirror-selectednode, .sdt-group-hover) to keep it stable across hover and selection. That's the wrong "best practice" to teach. Make the chrome-none reset read a --sd-content-controls-custom-* variable layer with default-preserving fallbacks (0-width transparent border, no background / radius / padding). chrome:'none' stays visually empty by default - existing consumers see no change - but a consumer can now paint inline and block controls by setting variables on a data-sdt-* selector. The painter applies them across rest, hover, and selected, so the box stays stable (no jitter) and no !important or state-class selectors are needed. `border` is a full shorthand; block adds a `-border-left` accent rail; background vars cascade (hover from rest, selected from hover). - variables.css: document the custom-* surface; note the built-in chrome still uses the existing --sd-content-controls-* variables. - docs: add a "Style the controls in place" section to the custom-UI content controls guide. - test: assert the surface is wired and default-preserving; existing chrome-none selector + source-order tests are unchanged and still pass (painter-dom 1178/1178). * fix(painter-dom): custom hover wins on locked SDTs under chrome:'none' (SD-3322) The custom hover background was overridden for LOCKED controls under chrome:'none'. The base lock-hover rules (a built-in tint on inline, transparent on block) have equal specificity to the plain custom hover rules but come later in source order, so they won; the chrome-none lock-hover reset only reset z-index, not background. Re-assert the custom hover background in that reset block - it carries the extra .superdoc-cc-chrome-none class, so it outranks the base lock-hover rules. A locked control now follows --sd-content-controls-custom-*-hover-bg. With no custom var set the default is empty, so the built-in lock-hover tint no longer leaks under chrome:'none' for locked controls (consistently empty). Only the contract-templates demo has locked chrome-none controls, and it wants the custom hover, not the tint. Add a regression test asserting the custom hover vars are re-asserted after the base lock-hover rules (source order = it wins). painter-dom 1179/1179 green. * docs(theming): point chrome:'none' styling at the custom SDT variables (SD-3322) The content-controls theming table themes the built-in chrome. Add a one-line note that under chrome:'none' you style controls with the --sd-content-controls-custom-* variables instead, linking the custom UI guide. * demo/docs: contract-templates use the custom SDT styling variables (SD-3322) Rewrite the contract-templates demo's SDT styling onto SuperDoc's public --sd-content-controls-custom-* variables (from #3590), proving the new API in the real legal-template use case. The demo now styles its inline fields and block clauses with zero !important and zero internal state selectors (.ProseMirror-selectednode, .sdt-group-hover); the painter applies the variables across rest, hover, selected, and locked-hover. This is the copy-pasteable pattern for styling custom SDTs under chrome:'none'. - style.css: replace the per-state !important rules with one variable-setting rule per tag (inline + block); update the host-owned-styling comment. - test: add state coverage - the custom hover background drives a painted field (and wins over the built-in lock-hover tint), the border stays constant across states (no jitter), and no built-in label/chrome leaks. Demo suite 13/13. - docs (Document API > Content controls): correct the contentLocked wording (it rejects Document API content writes too, not just the editor); document the locked-template pattern (unlock -> write -> relock, incl. a locked parent for nested fields); add the single-use governed clause-library pattern alongside versioned reusable sections (kept - it's a valid pattern). - docs (Custom UI > Content controls): add a "Build a custom field system" walkthrough; describe the demo as a full custom contract-template UI. - README: note the demo styles through the public custom variables. Stacked on #3590 (the painter variable layer); retarget to main once it merges.
Contributor
|
π Docs preview: https://superdoc-tadeu-sd-3005-feature-bibliography.mintlify.app |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Note
π Stacked PRs β review bottom-up
documentPartObjectReview order: #3538 β #3565 β #3566. Each PR's base auto-retargets to
mainas the one below it merges.Summary
Single-paragraph BIBLIOGRAPHY / INDEX / TOA field codes (the typical Word output) currently crash SuperDoc on import: the editor never reaches
ready, the document area renders blank, and the console reportsInvalid content for node type bibliography. This PR fixes the root cause and lights up the same code path for INDEX (SD-3017) and TOA at the same time.Linear: SD-3005 (parent epic SD-3066)
Root cause
When a field envelope sits inside one
<w:p>,preProcessNodesForFldCharcollects the after-separate<w:r>runs as the field'snodesToCombine. The bibliography / index / tableOfAuthorities PM nodes declarecontent: 'paragraph+'. Wrapping loose runs directly inside<sd:bibliography>(and siblings) violates the schema βcontentMatchAtthrows βCommandServiceaborts β editor stuck ininitializedstate.PM-tree comparison:
bibliography { paragraph+ }.bibliography { run, run, ... }if PM allowed it.Fix
normalizeFieldContentToParagraphs(nodes)infld-preprocessors/normalize-field-content.js:w:pnodes into synthesized<w:p>wrappers<w:p>children as-is<w:p>for empty input (previously only the bibliography preprocessor's local fallback)bibliography-preprocessor.js,index-preprocessor.js,toa-preprocessor.js(one-line change each).Test plan
toa-preprocessor.test.js)pnpm --filter super-editor exec vitest run src/editors/v1/core/super-converter/field-references/fld-preprocessors/β 68/68 passpnpm --filter super-editor exec vitest run src/editors/v1/core/super-converterβ 2976/2976 pass, 336 test files, no regressions\l 1033switch preserved; italic runs preservedpnpm layout:compare) β recommend gating in CI rather than blocking this PRWhat is intentionally out of scope
This PR fixes the import-time crash. The remaining SD-3005 fidelity items (hanging indent, instruction-switch consumption like
\l 1033/\f category, regenerating fromcustomXml/item1.xml'sb:Sources) sit downstream of the crash and need a canonical Word-authored fixture in the corpus first. None of those are reproducible until the editor stops crashing on the typical Word output, which this PR addresses.