From bf564fbab4a2557cd4d16a4e9bd4d397416a9fcd Mon Sep 17 00:00:00 2001 From: Steffen Becker Date: Fri, 14 Aug 2026 23:11:10 +0200 Subject: [PATCH] fix: footnote counting per page/chapter and collecting at the end really work MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both were selectable in the dialog and changed nothing — they only rode the file formats, so an imported document numbered and placed its notes wrong. noteLabels now counts within a bucket (class, chapter, or the page pageBreaks publishes through setNoteAnchorPages); a collected footnote leaves position: absolute and opens its own page, as LibreOffice does. Counting and position move to the footnote tab, the only place LibreOffice offers them. The separator's own alignment moves the line alone — probed, notes stay left. --- docs/architecture/notes.md | 25 ++++++++++--- src/lib/components/NoteOptionsDialog.svelte | 18 +++++---- src/lib/editor/extensions/notes.ts | 41 +++++++++++++++++---- src/lib/editor/extensions/pageBreaks.ts | 33 ++++++++++++----- src/lib/storage/noteSettings.ts | 4 ++ src/styles/editor.css | 5 +++ tests/unit/notes.test.ts | 22 ++++++++++- 7 files changed, 116 insertions(+), 32 deletions(-) diff --git a/docs/architecture/notes.md b/docs/architecture/notes.md index 8768c1e..27975a8 100644 --- a/docs/architecture/notes.md +++ b/docs/architecture/notes.md @@ -47,16 +47,27 @@ note it was copied from; an anchor that ends up *inside* a note is dropped, and one there is refused (LibreOffice and Word refuse it too). A settings change is no document change, so the dialog's path carries the `RESYNC_NOTES` meta. +**Restarting the count.** `noteLabels` counts within a bucket: the class alone (document-wide), +its chapter (`collectNoteRefs` counts the level-1 headings above each anchor), or its page — +footnotes only, as in LibreOffice, since the endnote list has no page of its own. The page is +the one thing numbering cannot see, so `pageBreaks.ts` publishes its `anchorPages` through +`setNoteAnchorPages` and fires `RESYNC_NOTES` when the map moves; the renumber may rewrap, so +it costs one more pass off the same `MAX_CONVERGE_PASSES` budget. The dialog shows counting +and position on the footnote tab only, which is where LibreOffice keeps them. + ## Placement (`pageBreaks.ts`) A footnote is out of the flow (`position: absolute`, `left`/`right` = the page margins) -and `pageBreaks.ts` gives it its `top`. Endnotes stay in flow and the first one carries -`forceBreakBefore`, because LibreOffice starts the endnote list on a new page (probed). +and `pageBreaks.ts` gives it its `top`. A **collected** note stays in flow and the first one +carries `forceBreakBefore`, because LibreOffice opens a new page for the list (probed). +Which of the two a footnote is comes from `position: 'document'` → the `data-collect-footnotes` +attribute (`applyNoteVars`) → `editor.css`, and pagination reads it back off the note's own +computed `position` rather than the setting: the box is the truth either way. The reservation runs **inside** one pagination pass — reading it back across passes would break the file's own rule that a pass must not read its own last answer: -1. `collectLeaves` skips footnote notes and walks the section for the endnotes. +1. `collectLeaves` skips the notes that are out of flow and walks the section for the rest. 2. Each footnote's height is measured once: out of flow at a fixed width, it does not depend on the page it lands on. 3. `placeLeaves(reserved)` takes a per-page reservation off each `contentEnd`. A bounded @@ -69,9 +80,11 @@ break the file's own rule that a pass must not read its own last answer: frozen layout leaves every footnote on the page it used to belong to, without the position an edit above the section leaves the decorations on a stale one. -**Deliberate ceiling:** a note is never split across pages. A page whose notes outgrow its -content area keeps one line of body text and lets them overflow. Splitting the note, as -LibreOffice does, is the upgrade path (`ponytail:` comment at the cap). +**Deliberate ceilings:** a note is never split across pages — a page whose notes outgrow its +content area keeps one line of body text and lets them overflow, and splitting the note as +LibreOffice does is the upgrade path. Collected notes take **one** page in anchor order; +LibreOffice gives each class a page of its own, which needs a section order that is not the +anchor order the exports read. Both carry a `ponytail:` comment at the cap. ## LibreOffice's defaults, as probed diff --git a/src/lib/components/NoteOptionsDialog.svelte b/src/lib/components/NoteOptionsDialog.svelte index 39e73ad..2f0df65 100644 --- a/src/lib/components/NoteOptionsDialog.svelte +++ b/src/lib/components/NoteOptionsDialog.svelte @@ -76,15 +76,17 @@ { const v = num(e); if (v >= 1) patch({ startAt: Math.round(v) }); }} /> - + {#if kind === 'footnote'} +