Skip to content

feat(page-number): chapter-prefixed section page numbering (SD-3029)#3620

Merged
harbournick merged 20 commits into
luccas/sd-3349-bug-numbering-issuesfrom
luccas/sd-3029-feature-section-page-numbering
Jun 4, 2026
Merged

feat(page-number): chapter-prefixed section page numbering (SD-3029)#3620
harbournick merged 20 commits into
luccas/sd-3349-bug-numbering-issuesfrom
luccas/sd-3029-feature-section-page-numbering

Conversation

@luccas-harbour
Copy link
Copy Markdown
Contributor

Summary

Adds end-to-end support for Word's chapter-prefixed page numbering (w:pgNumType/@w:chapStyle + @w:chapSep). When a section enables it, PAGE fields render as <chapter>-<page> (e.g. 1-1, 12-3), where the chapter number is sourced from the nearest preceding numbered Heading marker and the separator is one of hyphen | period | colon | emDash | enDash.

The feature threads through the entire pipeline — DOCX import/export, contracts, the v1 layout-adapter, the layout engine, the layout bridge, the DOM painter, the live editor extensions, and the Document API.

Changes by layer

Contracts (layout-engine/contracts)

  • New PageNumberChapterSeparator type and two formatting helpers: formatChapterPageNumberText (joins a chapter prefix + separator to a page component) and formatSectionPageNumberText (formats the page number then applies the chapter prefix). Hyphen maps to a non-breaking hyphen (\u2011) to match Word.
  • SectionNumbering extended with chapterStyle / chapterSeparator; format now reuses PageNumberFormat. SectionBreakBlock.numbering reuses SectionNumbering.
  • Page, HeaderFooterPage, ResolvedPage, and ResolvedHeaderFooterPage carry pageNumberFormat, pageNumberChapterText, and pageNumberChapterSeparator.
  • ParagraphAttrs gains headingLevel and listLevelOrdinal, used to identify chapter markers.

Import / Export (super-editor)

  • Section extraction and the document-api sections-xml read/write helpers parse and round-trip w:chapStyle / w:chapSep. Section signature equality now compares the new chapter fields so changes invalidate correctly.
  • The layout-adapter resolves headingLevel from direct outlineLvl, built-in "Heading N" style names, or the style's outlineLvl, and captures listLevelOrdinal from structured numbering paths.

Layout engine (pageNumbering.ts)

  • buildChapterContextByPage walks layout pages in order, tracks the active chapter marker per heading level (clearing deeper levels when a higher one changes), and maps each page to its resolved chapter prefix. Marker text is normalized and validated; empty Heading 1 markers fall back to a structured ordinal, but nested prefixes are never synthesized.
  • computeDisplayPageNumber and resolvePageNumberTokens produce chapter-prefixed display text. Token resolution now returns undefined when nothing changed and preserves token metadata across convergence passes (cloneBlockWithResolvedTokens no longer strips tokens).

Layout bridge (incrementalLayout.ts, layoutHeaderFooter.ts, resolveHeaderFooterTokens.ts)

  • Chapter context is cached per layout signature and applied to body pages before header/footer resolution. The page resolver now surfaces format + chapter fields to header/footer layout.
  • Header/footer prelayout uses a conservative page resolver that reserves width for the longest possible chapter prefix, preventing under-measured header/footer height. Header height pre-measurement now routes through the cached layout path.
  • Bucketing is disabled when any page has a chapter prefix or section-aware display text (since 1-1 and 12-1 differ in width within one digit bucket), forcing per-page measurement.
  • Convergence loop now converges on resolved output rather than early-exiting on stable page count.

Painters (painters/dom) — renderer.ts, text-run.ts, and shape text resolution render chapter-prefixed PAGE fields; the page-context signature includes the new fields.

Live editor (super-editor extensions) — the page-number node, header/footer editor managers, per-RId layout, story editor factory, presentation editor, and session manager all plumb currentPageChapterNumberText / currentPageChapterSeparator so the editable header/footer surfaces match the painted layout. (#getPageNumberFormatForDomNode also guards against a missing view.)

Document APIsections.setPageNumbering accepts chapterStyle and chapterSeparator. The input requirement relaxed from oneOf to anyOf (any one of start/format/chapterStyle/chapterSeparator); schemas, types, validation, and generated reference docs updated.

Tests

New/expanded coverage across the stack: pageNumbering, resolvePageTokens, resolvePageNumberTokens, header/footer prelayout, bucketing, page-token convergence, per-RId layout, registry, paragraph heading-level extraction, section extraction/breaks, sections-xml round-trip, the page-number extension, and the Document API sections suite.

Notes

  • Builds on a series of follow-up fixes in this branch (prelayout token measurement, two-digit page tokens, stale chapter editor context, per-RId chapter context, no-break hyphen, heading-ordinal fallback).
  • Marked MVP: chapter prefix is derived from a single visible numbered-heading marker; multi-level prefix synthesis from structured ordinals is intentionally avoided.

@luccas-harbour luccas-harbour requested a review from a team as a code owner June 3, 2026 14:00
@linear-code
Copy link
Copy Markdown

linear-code Bot commented Jun 3, 2026

SD-3029

Copy link
Copy Markdown

@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: b4c1326be1

ℹ️ 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".

Comment thread packages/layout-engine/layout-bridge/src/layoutHeaderFooter.ts Outdated
@luccas-harbour luccas-harbour self-assigned this Jun 3, 2026
@luccas-harbour luccas-harbour force-pushed the luccas/sd-3349-bug-numbering-issues branch from 789c091 to 25957c8 Compare June 3, 2026 14:45
@luccas-harbour luccas-harbour force-pushed the luccas/sd-3029-feature-section-page-numbering branch from b3bce5f to ec1fc52 Compare June 3, 2026 14:45
Extend section page numbering with `chapterStyle` (w:chapStyle) and
`chapterSeparator` (w:chapSep) support across the stack:

- document-api: add fields to the setPageNumbering contract (anyOf now
  accepts chapterStyle/chapterSeparator), schemas, types, and validation
- layout contracts: extend SectionNumbering with chapter fields and
  reuse PageNumberFormat/PageNumberChapterSeparator types
- layout-adapter: extract w:chapStyle/w:chapSep from pgNumType, validate
  positive integers and known separators, and compare them in section
  signatures
- sections-xml helpers: read/write chapter attributes on w:pgNumType
- regenerate Document API reference docs
Resolve and paint chapter number prefixes (e.g. "1-1", "3:V") for
sections that enable chapter numbering, deriving the chapter from the
nearest numbered Heading N marker.

- contracts: add formatSectionPageNumberText helper, chapter/format
  fields on Page and HeaderFooterPage, and headingLevel/listLevelOrdinal
  paragraph attrs
- pageNumbering: add buildChapterContextByPage to track the active
  chapter per physical page, normalizeChapterMarkerText to accept only
  clean single-token markers, and thread chapter context through
  computeDisplayPageNumber
- layout-bridge: build/cache chapter context across PAGE-token
  convergence, apply it to body pages, and disable header/footer digit
  bucketing when chapter prefixes can vary the rendered width
- resolvePageTokens / text-run / renderer: preserve the chapter prefix
  when a run-local PAGE format switch applies
- layout-adapter: resolve built-in heading level from style metadata
  (incl. localized names) and expose the structured list ordinal
@luccas-harbour luccas-harbour force-pushed the luccas/sd-3349-bug-numbering-issues branch from 25957c8 to e2a88d6 Compare June 3, 2026 16:58
@luccas-harbour luccas-harbour force-pushed the luccas/sd-3029-feature-section-page-numbering branch from ec1fc52 to c95dfa0 Compare June 3, 2026 16:58
@harbournick harbournick merged commit 1de645b into luccas/sd-3349-bug-numbering-issues Jun 4, 2026
40 checks passed
@harbournick harbournick deleted the luccas/sd-3029-feature-section-page-numbering branch June 4, 2026 00:58
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.

3 participants