// src/lib/lexical-headings.ts import { slugify } from './slugify' export type Heading = { id: string; text: string; level: number; children: Heading[] } type Node = { type: string; tag?: string; children?: Node[]; text?: string } function textOf(node: { children?: Node[] }): string { return (node.children ?? []).map((c) => c.text ?? '').join('') } export function anchorFor(node: { children?: Node[] }): string { return slugify(textOf(node)) } export function extractHeadingsFromLexical(value: { root: { children: Node[] } }): Heading[] { const flat: Heading[] = [] for (const node of value.root.children) { if (node.type !== 'heading' || !node.tag) continue const level = Number(node.tag.replace('h', '')) if (level < 2 || level > 6) continue const text = textOf(node) flat.push({ id: slugify(text), text, level, children: [] }) } // build nesting const root: Heading[] = [] for (const h of flat) { let inserted = false for (let i = root.length - 1; i >= 0; i--) { if (root[i].level < h.level) { let cursor = root[i] while (cursor.children.length && cursor.children[cursor.children.length - 1].level < h.level) { cursor = cursor.children[cursor.children.length - 1] } cursor.children.push(h) inserted = true break } } if (!inserted) root.push(h) } return root }