fix(layout): per-section footer constraints for multi-section docs (SD-1837)#2022
Conversation
…ring (SD-1837) Footer tables now respect per-section margins in multi-section documents. Previously, all footers were measured using the first section's content width, causing table overflow on sections with different margins. Key changes: - Add margins/pageSize to SectionMetadata for per-section constraint computation - Refactor layoutPerRIdHeaderFooters to compute per-section constraints using composite keys (rId::sN) for section-specific measurements - Handle pct-based table widths by pre-expanding constraints - Add rescaleColumnWidths to all table fragment creation sites (SD-1859) - Unwrap unhandled w:fldSimple fields (FILENAME, etc.) to render cached text
Visual diffs detectedPixel differences were found in visual tests. This is not blocking — reproduce locally with |
Fix multiple issues with footer editing in presentation mode: - Fix hover tooltip only showing on first page by detecting page index from DOM via elementsFromPoint in normalizeClientPoint - Fix double-click on footer selecting body text by properly preventing default browser behavior on pointerdown in footer regions - Fix clicking on page N redirecting to page 1 by keeping Y as global layout coordinates in normalizeClientPoint (only X is adjusted for page centering) and computing page-local Y separately from the page element's bounding rect for header/footer hit testing - Fix switching between footer editors on different pages by cleaning up the previous editing session in enterMode before setting up the new one - Fix footer table layout collapsing by setting table-layout:fixed on tables in the footer ProseMirror editor element - Fix wrong total page count in footer by reading body layout page count via getBodyPageCount dependency instead of header layout - Fix page-number NodeView reading incorrect totalPageCount property
There was a problem hiding this comment.
Pull request overview
This PR fixes multi-section header/footer layout and editing issues by making header/footer measurement and hit-testing section-aware, improving pointer normalization across pages, and correcting page-number/token handling in header/footer contexts.
Changes:
- Add per-section header/footer layout constraints (including handling of table width overflow and section-specific margins/page sizes).
- Fix header/footer editing interactions by improving pointer normalization (page detection, global vs page-local Y) and event handling to avoid native selection/scroll issues.
- Improve page-number/field rendering: correct total page count sourcing, refresh NodeView text when editor options change, and expand OOXML field preprocessing (e.g.,
fldSimple, legacyw:pgNum).
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/super-editor/src/extensions/pagination/pagination-helpers.js | Adds a scoped class to header/footer ProseMirror roots to apply header/footer-specific table CSS. |
| packages/super-editor/src/extensions/page-number/page-number.test.js | Updates tests to use editor.options.totalPageCount when present. |
| packages/super-editor/src/extensions/page-number/page-number.js | Prefers totalPageCount option, and refreshes NodeView text in update(). |
| packages/super-editor/src/core/super-converter/field-references/preProcessPageFieldsOnly.test.js | Updates/adds tests for fldSimple unwrapping and legacy w:pgNum conversion. |
| packages/super-editor/src/core/super-converter/field-references/preProcessPageFieldsOnly.js | Adds fldSimple handling changes and legacy w:pgNum → sd:autoPageNumber. |
| packages/super-editor/src/core/presentation-editor/types.ts | Extends HeaderFooterRegion with section-aware displayPageNumber. |
| packages/super-editor/src/core/presentation-editor/pointer-events/EditorInputManager.ts | Passes pageIndex/pageLocalY through normalization + hit testing; prevents native selection on H/F pointerdown. |
| packages/super-editor/src/core/presentation-editor/header-footer/HeaderFooterSessionManager.ts | Improves hit testing with optional known page index/local Y; fixes editor session switching cleanup; sources total pages from body layout. |
| packages/super-editor/src/core/presentation-editor/dom/PointerNormalization.ts | Keeps Y in global layout coords; detects page via elementsFromPoint and computes pageLocalY from DOM rect. |
| packages/super-editor/src/core/presentation-editor/dom/PointerNormalization.test.ts | Updates expectations for new normalization return shape. |
| packages/super-editor/src/core/presentation-editor/PresentationEditor.ts | Wires new hit-test callback signature and provides getBodyPageCount. |
| packages/super-editor/src/core/header-footer/HeaderFooterRegistry.ts | Forces page-number DOM refresh after setOptions() updates in header/footer editors. |
| packages/super-editor/src/core/header-footer/HeaderFooterPerRidLayout.ts | Implements per-section header/footer measurement with composite keys and table-width-aware constraints. |
| packages/super-editor/src/core/header-footer/EditorOverlayManager.ts | Allows overlay overflow for footer table overflow behavior. |
| packages/super-editor/src/assets/styles/elements/prosemirror.css | Adds header/footer-scoped table CSS overrides. |
| packages/layout-engine/pm-adapter/src/sections/analysis.ts | Publishes section margins and pageSize into SectionMetadata. |
| packages/layout-engine/pm-adapter/src/sections/analysis.test.ts | Updates expected SectionMetadata to include nullable margins/pageSize. |
| packages/layout-engine/layout-engine/src/layout-table.ts | Adds proportional rescaling of table column widths when clamped to narrower sections. |
| packages/layout-engine/layout-engine/src/layout-table.test.ts | Adds tests for the new column-width rescaling behavior. |
| packages/layout-engine/layout-bridge/src/incrementalLayout.ts | Extends HeaderFooterLayoutResult to include effectiveWidth. |
| packages/layout-engine/contracts/src/index.ts | Extends contracts for SectionMetadata (margins/pageSize) and TableFragment (columnWidths). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
packages/super-editor/src/core/presentation-editor/header-footer/HeaderFooterSessionManager.ts
Outdated
Show resolved
Hide resolved
packages/super-editor/src/core/presentation-editor/header-footer/HeaderFooterSessionManager.ts
Outdated
Show resolved
Hide resolved
packages/super-editor/src/core/super-converter/field-references/preProcessPageFieldsOnly.js
Show resolved
Hide resolved
packages/super-editor/src/core/presentation-editor/PresentationEditor.ts
Outdated
Show resolved
Hide resolved
packages/super-editor/src/core/header-footer/HeaderFooterPerRidLayout.ts
Show resolved
Hide resolved
packages/super-editor/src/core/presentation-editor/dom/PointerNormalization.test.ts
Show resolved
Hide resolved
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2bca00577f
ℹ️ 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".
packages/super-editor/src/core/presentation-editor/header-footer/HeaderFooterSessionManager.ts
Show resolved
Hide resolved
Via L3 deep analysis · sensitive risk |
|
@tupizz mind resolving conflicts and resolving checking copilot comments? Resolve them if not applicable or let's fix pls! Thank you! |
…D-1837) Conflict resolutions: - layout-table.ts: remove duplicate private rescaleColumnWidths in favor of main's exported version; remove duplicate columnWidths properties left by auto-merge at fragment creation sites - contracts/index.ts: remove duplicate columnWidths field in TableFragment type - layout-table.test.ts: use main's test name and assertion for no-clamping case (expect undefined); include main's boundary-consistency test - prosemirror.css: scope header/footer table rule under .sd-editor-scoped to match SD-1850 scoping convention Reviewer comment fixes: - PresentationEditor.ts: add pageLocalY? to #normalizeClientPoint return type so downstream consumers can see the field in TypeScript - HeaderFooterSessionManager.ts: use region.pageNumber directly instead of parseInt(displayPageNumber) which silently produces NaN for non-decimal page numbering schemes (Roman numerals, letters) - HeaderFooterSessionManager.ts: correct misleading hitTestRegion JSDoc to accurately describe that y is global layout coordinates, not page-local
|
🎉 This PR is included in superdoc v1.17.0-next.8 The release is available on GitHub release |
|
🎉 This PR is included in superdoc-cli v0.2.0-next.3 The release is available on GitHub release |
# [1.17.0](v1.16.0...v1.17.0) (2026-02-28) ### Bug Fixes * active track change ([#2163](#2163)) ([108c14d](108c14d)) * add currentTotalPages getter and pagination-update event ([#2202](#2202)) ([95b4579](95b4579)), closes [#958](#958) * always call resolveComment after custom TC bubble handlers (SD-2049) ([#2204](#2204)) ([34fb4e0](34fb4e0)) * backward replace insert text ([#2172](#2172)) ([66f0849](66f0849)) * **collaboration:** deduplicate updateYdocDocxData during replaceFile (SD-1920) ([#2162](#2162)) ([52962fc](52962fc)) * **comments:** cross-page collision avoidance for floating comment bubbles (SD-1998) ([#2180](#2180)) ([6cfbeca](6cfbeca)) * **comments:** remove synchronous dispatch from plugin apply() (SD-1940) ([#2157](#2157)) ([887175b](887175b)) * **css:** scope ProseMirror CSS to prevent bleeding into host apps (SD-1850) ([#2134](#2134)) ([b9d98fa](b9d98fa)) * document-api improvements, plan mode, query.match, mutations ([6221580](6221580)) * **document-api:** delete table cell fix ([#2209](#2209)) ([5e5c43f](5e5c43f)) * **document-api:** distribute columns command fixes ([#2207](#2207)) ([8f4eaf7](8f4eaf7)) * **document-api:** fix cell shading in document api ([#2215](#2215)) ([456f60e](456f60e)) * **document-api:** insert table cell ([#2210](#2210)) ([357ee90](357ee90)) * **document-api:** plan-engine reliability fixes and error diagnostics ([#2185](#2185)) ([abfd81b](abfd81b)) * **document-api:** split table cell command ([#2217](#2217)) ([0b3e2b4](0b3e2b4)) * **document-api:** split table command ([#2214](#2214)) ([ec31699](ec31699)) * **editor:** render styles applied inside SDT fields (SD-2011) ([#2188](#2188)) ([9c34be3](9c34be3)) * **editor:** selection highlight flickers when dragging across mark boundaries (SD-2024) ([#2205](#2205)) ([ba03e76](ba03e76)) * extract duplicate block identity normalization from docxImporter ([7f7ff93](7f7ff93)) * improve backspace behavior near run boundaries for tracked changes ([#2175](#2175)) ([6c9c7a3](6c9c7a3)) * **layout:** per-section footer constraints for multi-section docs (SD-1837) ([#2022](#2022)) ([e11acc5](e11acc5)) * normalize review namespace into trackChanges, harden input validation ([33e907b](33e907b)) * outside click for toolbar dropdown ([#2174](#2174)) ([5f859c7](5f859c7)) * preserve line spacing and indentation on Google Docs paste ([#2183](#2183)) ([b9a7357](b9a7357)), closes [#2151](#2151) * **shapes:** render grouped DrawingML shapes with custom geometry (SD-1877) ❇️ ([#2105](#2105)) ([14985a5](14985a5)) * support cell spacing ([#1879](#1879)) ([1639967](1639967)) * **tables:** expand auto-width tables to fill available page width ([#2109](#2109)) ([15f36bc](15f36bc)) * text highlight on export ([#2189](#2189)) ([9cbd022](9cbd022)) * track highlight changes ([#2192](#2192)) ([e164625](e164625)) * undo/redo actions ([#2161](#2161)) ([495e92f](495e92f)) ### Features * allow custom accept/reject handlers for TC bubbles ([#1921](#1921)) ([e30abf6](e30abf6)) * **document-api:** add format operations font size alignment color font family ([#2179](#2179)) ([f19c688](f19c688)) * **document-api:** add plan-based mutation engine with query.match and style capture ([#2160](#2160)) ([365293a](365293a)) * **document-api:** doc default initial styles ([#2184](#2184)) ([f25e41f](f25e41f)) * **document-api:** include anchored text in comments list response ([#2177](#2177)) ([b3a2912](b3a2912)) * **document-api:** inline formatting parity core end-to-end ([#2197](#2197)) ([b405b03](b405b03)) * **document-api:** inline formatting rpr parity ([#2198](#2198)) ([41ab771](41ab771)) * **document-api:** section commands ([#2199](#2199)) ([ec4abe3](ec4abe3)) * **document-api:** support deleting entire block nodes not only text ([#2181](#2181)) ([2897246](2897246)) * **document-api:** table of contents commands ([#2200](#2200)) ([baa72c4](baa72c4)) * **document-api:** tables namespace and commands ([#2182](#2182)) ([b80ee31](b80ee31)) * **markdown:** add markdown override to sdk, improve conversion ([#2196](#2196)) ([04a1c71](04a1c71)) * preserve w:view setting through DOCX round-trip ([#2190](#2190)) ([48b4210](48b4210)), closes [#2070](#2070) * **track-changes:** clear comment bubbles when bulk accept or reject TCs ([#2159](#2159)) ([27fbe8e](27fbe8e)) ### Performance Improvements * **comments:** batch tracked change comment creation on load ([#2166](#2166)) ([0c2eca5](0c2eca5)) * **comments:** batch tracked change creation and virtualize floating bubbles (SD-1997) ([#2168](#2168)) ([70fd7d9](70fd7d9))
Demo on the bugs and proposed solution
CleanShot.2026-02-13.at.16.18.54.mp4
Summary
Fix per-section footer constraints for multi-section documents and fix footer editing interaction bugs.
Per-section footer layout (SD-1837)
fldSimplefields (e.g., PAGE, NUMPAGES) in footers now render correctly during layoutFooter editing: hover tooltip on all pages
normalizeClientPointdid not detect which page the pointer was over, sohitTestRegionalways derived page index 0 from the Y coordinateelementsFromPointinnormalizeClientPointand passpageIndex+pageLocalY(computed from the page element's bounding rect) tohitTestRegionFooter editing: double-click selecting body text
pointerdownwas not callingevent.preventDefault()when the click landed on a footer region, allowing the browser to perform native text selectionevent.preventDefault()in#handlePointerDownwhen a header/footer region is detectedFooter editing: clicking page N redirecting to page 1
normalizeClientPointwas subtractinggetPageOffsetYfrom the Y coordinate, making it page-local. But all downstream consumers (clickToPosition, selection handling) expect global layout Yyas global layout coordinates innormalizeClientPoint. ComputepageLocalYseparately from the page element'sgetBoundingClientRectand pass it only to the header/footer hit testing pathFooter editing: switching pages scrolls to wrong page
#handleClickInHeaderFooterModeconsumed thepointerdownevent withoutpreventDefault, allowing native browser scroll. Additionally,#enterModedid not clean up the previous editing session before creating a new onepreventDefault). Clean up the previous session (disable editor, hide overlay) at the start of#enterModeFooter editing: table layout collapse
table-layout: autoinprosemirror.csscauses browsers to ignore explicit<col>widths fromcreateColGroup()table-layout: fixedon tables in the footer ProseMirror editor element after creationFooter editing: wrong total page count
HeaderFooterSessionManagerreadthis.#headerLayoutResults[0].layout.pages.length(the header/footer's own layout, always 1 page) instead of the body layout. Additionally,page-number.jsread a non-existentparentEditor.currentTotalPagespropertygetBodyPageCountdependency to read the actual body page count. Updatepage-number.jsto readeditor.options.totalPageCountfirst (set during editor creation)Footer editing: fldSimple field preprocessing
fldSimplefields withPAGEorNUMPAGESinstructions were not preprocessed for standalone renderingfldSimpleelements inpreProcessPageFieldsOnlyalongside existingfldChar-based field processingTest plan
pnpm --filter super-editor test— all 5547 tests pass