Skip to content
This repository was archived by the owner on Aug 20, 2026. It is now read-only.
This repository was archived by the owner on Aug 20, 2026. It is now read-only.

Add a canonical headingLevel field to the paragraph content model #13

Description

@Mearman

styleId on ContentParagraph carries the source format's raw style name (w:pStyle/@w:val for docx, e.g. 'Heading1') — kept for round-trip fidelity, not meant as a resolved signal. But it's already being used as one: markdown-codec's writer parses it back into a number (test md/heading-level-clamped, "styleId beyond Heading6 clamps to level 6"), and odf.js's odt reader already extracts the real numeric text:outline-level attribute and re-encodes it into a 'HeadingN'-shaped styleId string to match docx's convention (src/typed/odt/read.ts), rather than exposing the number it already has. Two packages have independently converged on treating styleId as an encoded heading level — the same "two places arrived at the same vocabulary" situation this package exists to fix for ContentDocument itself, one level down.

Proposed: add headingLevel?: number directly to ContentParagraphSchema (1-based), alongside the existing styleId. styleId keeps its current job (round-trip fidelity, whatever name the source format used); headingLevel becomes the one canonical, format-agnostic signal a consumer reads to build hierarchy from a flat block sequence — grouping everything under its nearest preceding heading, the way none of the current codecs' output supports doing today (a section is blocks: ContentBlock[], flat, in document order).

Each reader has its own real signal to resolve this from, not a shared name-matching pass:

  • odf.js: already has the number in hand (text:outline-level) before it currently throws it away by re-encoding into styleId. Smallest change of the three.
  • ooxml.js: styleId is currently the raw w:pStyle/@w:val, unresolved — a custom style based on Heading2 keeps its own custom name, not 'Heading2'. The style cascade this package already walks for formatting should resolve headingLevel from the style's own w:pPr/w:outlineLvl (the actual OOXML mechanism for this, more reliable than matching the style name, and Word's built-in HeadingN styles set it directly).
  • markdown-codec: trivial — # depth is the heading level, no inference needed, and this is also where the promoted shared helper (below) applies on the write side.

Also promote the existing private styleId-to-level parser (currently only inside markdown-codec's writer) into a shared, exported helper here, for any producer that only has a raw style name to resolve from (a docx custom style not yet walked through the cascade above, for instance).

Companion to #12. Both are breaking changes to the same paragraph/content shape (#12 proposes folding ContentParagraph into FusedNode; this proposes adding a field to it), and this should land as part of the same CONTENT_FORMAT_VERSION bump as #12 rather than as a separate one — landing it first means #12 has to carry it forward into FusedNode's design, landing it after means every reader that populates it gets touched twice. Neither issue has any implementation started yet, so there's room to do this properly.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions