Skip to content

fix(layout): per-section footer constraints for multi-section docs (SD-1837)#2022

Merged
harbournick merged 6 commits intomainfrom
tadeu/sd-1837-footer-per-section-margins
Feb 25, 2026
Merged

fix(layout): per-section footer constraints for multi-section docs (SD-1837)#2022
harbournick merged 6 commits intomainfrom
tadeu/sd-1837-footer-per-section-margins

Conversation

@tupizz
Copy link
Contributor

@tupizz tupizz commented Feb 13, 2026

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)

  • Footer layout constraints now use per-section page margins instead of global defaults, so sections with different bottom margins render footers at the correct position
  • fldSimple fields (e.g., PAGE, NUMPAGES) in footers now render correctly during layout

Footer editing: hover tooltip on all pages

  • Bug: "Double click to edit footer" tooltip only appeared on page 1
  • Root cause: normalizeClientPoint did not detect which page the pointer was over, so hitTestRegion always derived page index 0 from the Y coordinate
  • Fix: Detect the page element via elementsFromPoint in normalizeClientPoint and pass pageIndex + pageLocalY (computed from the page element's bounding rect) to hitTestRegion

Footer editing: double-click selecting body text

  • Bug: Double-clicking a footer sometimes selected text from the document body instead of entering footer edit mode
  • Root cause: pointerdown was not calling event.preventDefault() when the click landed on a footer region, allowing the browser to perform native text selection
  • Fix: Call event.preventDefault() in #handlePointerDown when a header/footer region is detected

Footer editing: clicking page N redirecting to page 1

  • Bug: Clicking on any page would redirect/scroll to page 1
  • Root cause: normalizeClientPoint was subtracting getPageOffsetY from the Y coordinate, making it page-local. But all downstream consumers (clickToPosition, selection handling) expect global layout Y
  • Fix: Keep y as global layout coordinates in normalizeClientPoint. Compute pageLocalY separately from the page element's getBoundingClientRect and pass it only to the header/footer hit testing path

Footer editing: switching pages scrolls to wrong page

  • Bug: When already editing a footer on page 13 and double-clicking page 16's footer, the view scrolled back to page 13
  • Root cause: #handleClickInHeaderFooterMode consumed the pointerdown event without preventDefault, allowing native browser scroll. Additionally, #enterMode did not clean up the previous editing session before creating a new one
  • Fix: Let clicks on H/F regions fall through to the existing footer region handler (which properly calls preventDefault). Clean up the previous session (disable editor, hide overlay) at the start of #enterMode

Footer editing: table layout collapse

  • Bug: Footer table renders as single line of text instead of preserving column widths when editing
  • Root cause: Global CSS table-layout: auto in prosemirror.css causes browsers to ignore explicit <col> widths from createColGroup()
  • Fix: Set table-layout: fixed on tables in the footer ProseMirror editor element after creation

Footer editing: wrong total page count

  • Bug: Footer shows "Page 1 of 1" instead of the correct total page count
  • Root cause: HeaderFooterSessionManager read this.#headerLayoutResults[0].layout.pages.length (the header/footer's own layout, always 1 page) instead of the body layout. Additionally, page-number.js read a non-existent parentEditor.currentTotalPages property
  • Fix: Add getBodyPageCount dependency to read the actual body page count. Update page-number.js to read editor.options.totalPageCount first (set during editor creation)

Footer editing: fldSimple field preprocessing

  • Bug: fldSimple fields with PAGE or NUMPAGES instructions were not preprocessed for standalone rendering
  • Fix: Add handling for fldSimple elements in preProcessPageFieldsOnly alongside existing fldChar-based field processing

Test plan

  • Upload a multi-section DOCX with different page margins per section
  • Verify footer renders at the correct position for each section
  • Hover over footers on pages 1, 2, and later pages — tooltip should appear on all
  • Double-click footer on any page — should enter editing mode without selecting body text
  • While editing a footer, double-click a footer on a different page — should switch correctly
  • Verify footer table preserves column layout when editing
  • Verify page count shows correct total (e.g., "Page 1 of 61")
  • Run pnpm --filter super-editor test — all 5547 tests pass

…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
@linear
Copy link

linear bot commented Feb 13, 2026

@github-actions
Copy link
Contributor

github-actions bot commented Feb 13, 2026

Visual diffs detected

Pixel differences were found in visual tests. This is not blocking — reproduce locally with cd tests/visual && pnpm docs:download && pnpm test to review diffs.

@tupizz tupizz self-assigned this Feb 13, 2026
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
@tupizz tupizz marked this pull request as ready for review February 13, 2026 19:25
Copilot AI review requested due to automatic review settings February 13, 2026 19:25
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

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, legacy w: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:pgNumsd: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.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 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".

@github-actions
Copy link
Contributor

⚠️ AI Risk Review — potential issues found

  • Painter renderTableFragment may not consume fragment.columnWidths (uses measure.columnWidths at lines 333/371), causing rescaled widths to be ignored during rendering
  • HeaderFooterPerRidLayout ~350-line refactor has no test coverage despite complex width resolution logic

Via L3 deep analysis · sensitive risk

@harbournick
Copy link
Collaborator

@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
Copy link
Collaborator

@harbournick harbournick left a comment

Choose a reason for hiding this comment

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

LGTM

@harbournick harbournick merged commit e11acc5 into main Feb 25, 2026
8 checks passed
@harbournick harbournick deleted the tadeu/sd-1837-footer-per-section-margins branch February 25, 2026 21:48
@superdoc-bot
Copy link
Contributor

superdoc-bot bot commented Feb 25, 2026

🎉 This PR is included in superdoc v1.17.0-next.8

The release is available on GitHub release

@superdoc-bot
Copy link
Contributor

superdoc-bot bot commented Feb 25, 2026

🎉 This PR is included in superdoc-cli v0.2.0-next.3

The release is available on GitHub release

harbournick pushed a commit that referenced this pull request Feb 28, 2026
# [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))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants