feat(book): full title→heading chain in injectBookSectionDefaults#33
Open
mmcky wants to merge 1 commit into
Open
feat(book): full title→heading chain in injectBookSectionDefaults#33mmcky wants to merge 1 commit into
mmcky wants to merge 1 commit into
Conversation
Wires `title.enabled`, `heading_2.enabled`, and `heading_3.enabled` per `section:` tag in `injectBookSectionDefaults` so the chapter-prefix machinery in enumerate.ts composes without authors needing project-level workarounds. Background: `enumerate.ts`'s book-mode auto-prefix only fires when the page-title heading is itself numbered (it provides `this.enumerator` for figures/theorems/sub-headings to prepend). The page-title heading in turn requires `numbering.title.enabled: true`. PR #22 wired `heading_1.enabled` per section but missed `title`, so chapter pages rendered `Figure 1` / `Section 1` instead of `Figure 1.1` / `Section 1.1` on a default `numbering.book: true` config. Authors hit this on pandoc- generated chapter files (LaTeX `\chapter{}` → MD `# Title`) where they cannot add per-page frontmatter. This patch: - chapters / appendices: seeds `title.enabled ??= true`, `heading_2.enabled ??= true`, `heading_3.enabled ??= true` alongside the existing `heading_1` defaults. - frontmatter / backmatter: seeds those same keys to `??= false` so a project-level `heading_2.enabled: true` (often used so chapters number correctly) doesn't leak through and number the preface's `##` headings. - Changes the legacy hard `h1.enabled = false` on frontmatter/backmatter to `??= false` for symmetry — a page that explicitly wants its title numbered (e.g. "Chapter 0: Preface") can now override the section default. The hard assignment had made this impossible. All assignments use `??=` so the layered precedence (page > project > section default > hardcoded) is preserved. Adds 10 new tests covering the new wiring and the per-page override paths. Closes #25. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR updates injectBookSectionDefaults in myst-cli to seed book-mode numbering defaults per TOC section: tag so that chapter-prefix enumeration composes correctly when a page’s first H1 is absorbed as the page title (e.g., pandoc chapter conversions).
Changes:
- Extend section-default injection to cover
numbering.title,numbering.heading_2, andnumbering.heading_3(in addition to existingheading_1) forchapters/appendices, and seed disabled defaults forfrontmatter/backmatter. - Add unit tests covering the new defaults and override behavior.
- Add a changeset documenting the patch release.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| packages/myst-cli/src/process/mdast.ts | Extends injectBookSectionDefaults to seed title + heading_2/3 defaults by book section. |
| packages/myst-cli/src/process/bookSection.spec.ts | Adds tests validating the new seeding/override behavior. |
| .changeset/book-section-defaults.md | Declares a myst-cli patch release and describes the behavior change. |
Comments suppressed due to low confidence (1)
packages/myst-cli/src/process/bookSection.spec.ts:200
- This test (and its comment) uses
heading_1.start: 0to illustrate a “Chapter 0: Preface”, but the frontmatter validator enforcesstart >= 1for numbering items (myst-frontmattervalidateNumberingItem,min: 1). In the real pipeline a page-levelstart: 0will be rejected/ignored, so this example doesn’t reflect a supported override unless validation rules are changed.
test('page override wins on a frontmatter page: enabling title is preserved', () => {
// The other direction: an author has a `Preface` they want numbered
// as part of the chapter sequence ("Chapter 0: Preface"). They write
// `numbering.heading_1: { enabled: true, start: 0, label: "Chapter %s" }`.
// The section default uses ??=, so the page wins and the preface
// gets numbered despite being tagged `section: frontmatter`.
//
// This is the symmetry fix: the legacy `h1.enabled = false` (hard
// assignment) made this impossible.
const fm: PageFrontmatter = {
numbering: {
book: { enabled: true },
heading_1: { enabled: true, start: 0, label: 'Chapter %s' },
title: { enabled: true },
},
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+153
to
+162
| // For frontmatter / backmatter we seed `false` so a project-level | ||
| // `numbering.heading_2.enabled: true` (often needed to make chapter | ||
| // pages work) does not leak through and number the preface's `##` | ||
| // headings as `1, 2, 3`. | ||
| // | ||
| // All assignments use `??=` so per-page and per-project overrides | ||
| // always win — this layer is *defaults*, not mandates. | ||
| numbering.title ??= {}; | ||
| numbering.heading_2 ??= {}; | ||
| numbering.heading_3 ??= {}; |
Comment on lines
+149
to
+158
| test('section defaults seed false for frontmatter even when project enables h2', () => { | ||
| // Before #25's fix, a project enabling heading_2 (a workaround for | ||
| // chapter prefixing) would leak through to frontmatter pages and | ||
| // number the preface's `##` headings as 1, 2, 3. Now the section | ||
| // default explicitly seeds heading_2.enabled = false for frontmatter, | ||
| // so the preface stays unnumbered regardless of project config. | ||
| // | ||
| // (Project-level numbering merges happen after this function runs in | ||
| // the full pipeline, but the section-default false acts as the page- | ||
| // local override — closer to the page beats the project setting.) |
| "myst-cli": patch | ||
| --- | ||
|
|
||
| Book mode: `injectBookSectionDefaults` now seeds the full `title → heading_1 → heading_2 → heading_3` chain per section tag, fixing chapter-prefix composition. With `numbering.book: true` and `section: chapters` (or `appendices`) on a TOC entry, figures, sections, and theorems on chapter pages now render as `1.1`, `1.1.1`, etc. instead of the broken flat `1`, `1`. The matching `section: frontmatter` / `backmatter` branch seeds `false` for the same chain so preface / back-matter pages stay unnumbered even when a project enables those depths for chapters. All assignments use `??=` so per-page and per-project overrides always win — including the previously-impossible case of enabling numbering on a frontmatter page. Closes QuantEcon/mystmd#25. |
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.
Summary
Closes #25. Wires the full
title → heading_1 → heading_2 → heading_3chain persection:tag ininjectBookSectionDefaultsso the chapter-prefix machinery inenumerate.tscomposes without authors needing project-level or per-page frontmatter workarounds.The problem (from #25)
In a project using just
numbering.book: trueandsection:tags:…the
enumerate.tsauto-prefix machinery never fires on chapter pages. The page-title H1 (which pandoc-generated chapter files produce as# Introduction) doesn't increment a counter unlessnumbering.title.enabled: true, sothis.enumerator(the chapter prefix) is undefined and figures / sections / theorems render flat:Figure 1instead ofFigure 1.1,Section 1instead of1.1, etc.The existing workaround required setting
title.enabledandheading_2.enabledat project level — but that then leaked into frontmatter/backmatter pages, numbering## Acknowledgementson the preface as "1". Per-page frontmatter to disable it was the only way out, and isn't viable for book-dp1 where chapter files are pandoc-generated and forbidden to hand-edit.The fix
In
injectBookSectionDefaults(a ~30-line function), wire the matching chain per section:section:tagchapters/appendicestitle.enabled ??= true,heading_2.enabled ??= true,heading_3.enabled ??= true(alongside existingheading_1wiring)frontmatter/backmattertitle.enabled ??= false,heading_2.enabled ??= false,heading_3.enabled ??= false(matching existingheading_1disable)Also changes the legacy hard
h1.enabled = falseon frontmatter/backmatter to??= falsefor symmetry. The hard assignment had made it impossible for an author to override (e.g. opting aPrefacepage back into the chapter sequence). With??=the layered precedence is uniform: page frontmatter > project config > section default > hardcoded fallback.Tests added
10 new tests in
bookSection.spec.ts:All 19
bookSection.spec.tstests pass; full myst-cli suite 293/293 green.Precedence preserved
This change only adds defaults, never overrides. The mystmd inheritance model (overrides cascade down the document tree) is fully honored:
myst.ymlAll assignments use
??=so any higher layer's setting flows through untouched.Test plan
bookSection.spec.ts(19 tests) passmyst-clifull suite (293 tests) pass1.1,1.1.1,Figure 1.1, etc. with justnumbering.book: trueinmyst.yml🤖 Generated with Claude Code