import React, { memo } from 'react' // --- Types --- export interface Heading { level: number text: string /** Position in document (character offset from markdown source) */ pos: number } // --- Heading Parser --- const HEADING_RE = /^(#{1,6})\s+(.+)$/gm /** * Parse headings from raw markdown content using regex. */ export function parseHeadings(markdown: string): Heading[] { const headings: Heading[] = [] let match: RegExpExecArray | null // Reset regex state HEADING_RE.lastIndex = 0 while ((match = HEADING_RE.exec(markdown)) !== null) { const level = match[1].length const text = match[2].trim() headings.push({ level, text, pos: match.index }) } return headings } // --- Component --- interface OutlinePanelProps { headings: Heading[] onNavigate: (pos: number) => void activeHeadingIndex: number | null } interface OutlineItemProps { heading: Heading isActive: boolean onNavigate: (pos: number) => void } const OutlineItem = memo(function OutlineItem({ heading, isActive, onNavigate }: OutlineItemProps) { return ( ) }) export const OutlinePanel = memo(function OutlinePanel({ headings, onNavigate, activeHeadingIndex }: OutlinePanelProps) { if (headings.length === 0) { return (