/** * A heading's anchor id — the fragment that in-document links point at. * * Imported HTML carries it on the heading tag itself (Google Docs writes * `

` and links its table of contents to `#h.2y1ok8y7pef0`). * Blok stores it on the header block as `data.anchor` and renders it back as the * heading element's `id`, so those links keep resolving after the round trip. * * DOM-free on purpose: the view renderer reads block data without a document. */ /** * Validate an anchor coming from block data or from pasted HTML. * * A fragment cannot contain whitespace and an empty one addresses nothing, so * both are rejected rather than written out as a broken id. * * Cross-heading uniqueness is explicitly OUT of scope, matching `anchorIds`: * two headings carrying the same anchor produce the same id, and consumers that * need uniqueness must dedup themselves. * * @param value - the raw anchor from block data or an `id` attribute * @returns the trimmed anchor, or undefined when it cannot address anything */ export function normalizeHeadingAnchor(value: unknown): string | undefined { if (typeof value !== 'string') { return undefined; } const trimmed = value.trim(); if (trimmed === '' || /\s/.test(trimmed)) { return undefined; } return trimmed; }