diff --git a/docs/architecture/notes.md b/docs/architecture/notes.md index 27975a8..1aa38a0 100644 --- a/docs/architecture/notes.md +++ b/docs/architecture/notes.md @@ -47,6 +47,11 @@ 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. +**The affixes stay in the note area.** A configured prefix/suffix wraps the number in +the note's own `text` only — the anchor keeps the bare number, which is where +LibreOffice draws its Before/After text too (probed; Word has no such setting, so the +DOCX carries none anywhere). + **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 diff --git a/src/lib/editor/extensions/notes.ts b/src/lib/editor/extensions/notes.ts index 4887303..5c79614 100644 --- a/src/lib/editor/extensions/notes.ts +++ b/src/lib/editor/extensions/notes.ts @@ -25,10 +25,13 @@ export function newNoteId(): string { // The label a note shows: its own custom mark where a file gave it one, else the // running number in the class's own format, wrapped in the configured prefix/suffix. -export function noteLabel(index: number, kind: NoteKind, settings: NoteSettings, custom?: string | null): string { +// The affixes belong to the note area only — the anchor stays the bare number, which +// is where LibreOffice draws its Before/After text too (probed). +export function noteLabel(index: number, kind: NoteKind, settings: NoteSettings, custom?: string | null, affixes = true): string { if (custom) return custom; const s = settings[kind]; - return `${s.prefix}${formatOrdinal(s.startAt + index, s.numFormat)}${s.suffix}`; + const num = formatOrdinal(s.startAt + index, s.numFormat); + return affixes ? `${s.prefix}${num}${s.suffix}` : num; } declare module '@tiptap/core' { @@ -225,6 +228,7 @@ export function setNoteAnchorPages(next: ReadonlyMap): boolean { // since the endnote list has no page of its own to count on. export function noteLabels( refs: NoteRefInfo[], settings: NoteSettings, pages: ReadonlyMap = anchorPages, + affixes = true, ): Map { const seen = new Map(); const out = new Map(); @@ -234,7 +238,7 @@ export function noteLabels( : restart === 'page' && ref.kind === 'footnote' ? `${ref.kind}:p${pages.get(ref.id) ?? 0}` : ref.kind; const index = seen.get(bucket) ?? 0; - out.set(ref.id, noteLabel(index, ref.kind, settings)); + out.set(ref.id, noteLabel(index, ref.kind, settings, null, affixes)); seen.set(bucket, index + 1); } return out; @@ -427,17 +431,21 @@ function syncStructure(state: EditorState): Transaction | null { function syncNumbers(state: EditorState, settings: NoteSettings): Transaction | null { const refs = collectNoteRefs(state.doc); const labels = noteLabels(refs, settings); + // The anchor shows the bare number: the prefix/suffix are note-area text only. + const anchorLabels = noteLabels(refs, settings, anchorPages, false); const section = findNoteSection(state.doc); // A note the file numbered by hand keeps its own mark, and the anchor shows the same // one — the two are one mark drawn twice, not two. section?.node.forEach((child) => { - if (child.attrs.label) labels.set(String(child.attrs.id ?? ''), String(child.attrs.label)); + if (!child.attrs.label) return; + labels.set(String(child.attrs.id ?? ''), String(child.attrs.label)); + anchorLabels.set(String(child.attrs.id ?? ''), String(child.attrs.label)); }); const tr = state.tr; let changed = false; for (const ref of refs) { - const label = labels.get(ref.id) ?? ''; + const label = anchorLabels.get(ref.id) ?? ''; if (state.doc.nodeAt(ref.pos)?.attrs.text === label) continue; tr.setNodeAttribute(ref.pos, 'text', label); changed = true;