{"version":3,"file":"index.cjs","names":["current: HTMLElement | null","groups: { top: number; items: HeadingPosition[] }[]","containers: HTMLElement[]","rafId: number"],"sources":["../src/utils/parse.ts","../src/utils/calculate.ts","../src/ui/button.ts","../src/core/toc.ts"],"sourcesContent":["export const parseHeading = (htmlElement: HTMLElement, exceptLevel?: number[]) => {\n    const levels = [1, 2, 3, 4, 5, 6].filter((level) => !exceptLevel?.includes(level))\n    const selector = levels.map((level) => `h${level}`).join(', ')\n    const headings = htmlElement.querySelectorAll(selector)\n    return headings\n}\n","import type { HeadingPosition } from '../types/position'\n\nexport const calculateHeadingPositions = (headings: NodeListOf<Element>, scrollOffset = 0): HeadingPosition[] => {\n    const documentHeight = document.documentElement.scrollHeight\n    const viewportHeight = window.innerHeight\n    const maxScroll = documentHeight - viewportHeight\n    const scrollbarHeight = viewportHeight\n\n    return Array.from(headings).map((heading) => {\n        const element = heading as HTMLElement\n        let offsetTop = 0\n        let current: HTMLElement | null = element\n\n        while (current) {\n            offsetTop += current.offsetTop\n            current = current.offsetParent as HTMLElement | null\n        }\n\n        const scrollRatio = maxScroll > 0 ? Math.min((offsetTop + scrollOffset) / documentHeight, 1) : 0\n        const buttonTop = scrollRatio * scrollbarHeight\n\n        return {\n            element: heading,\n            scrollY: offsetTop,\n            fixedTop: buttonTop,\n        }\n    })\n}\n","import type { ButtonOptions, HeadingPosition } from '../types/position'\n\nexport const renderButtons = (positions: HeadingPosition[], options?: ButtonOptions): HTMLElement[] => {\n    const buttonHeight = options?.buttonHeight || 16\n    const minGap = buttonHeight\n    const groups: { top: number; items: HeadingPosition[] }[] = []\n\n    positions.forEach((pos) => {\n        const existingGroup = groups.find((g) => Math.abs(g.top - pos.fixedTop) < minGap)\n        if (existingGroup) {\n            existingGroup.items.push(pos)\n        } else {\n            groups.push({ top: pos.fixedTop, items: [pos] })\n        }\n    })\n\n    groups.sort((a, b) => a.top - b.top)\n\n    for (let i = 1; i < groups.length; i++) {\n        const prev = groups[i - 1]!\n        const curr = groups[i]!\n        const prevBottom = prev.top + prev.items.length * (buttonHeight + 4) - 4\n\n        if (curr.top < prevBottom + minGap) {\n            curr.top = prevBottom + minGap\n        }\n    }\n\n    const containers: HTMLElement[] = []\n    const offset = options?.rightOffset || 0\n\n    groups.forEach((group) => {\n        const container = document.createElement('div')\n        container.setAttribute('data-bscroll-toc', 'true')\n        container.style.position = 'fixed'\n        container.style.top = `${group.top}px`\n        container.style.right = `${offset}px`\n        container.style.display = 'flex'\n        container.style.flexDirection = 'column'\n        container.style.alignItems = 'flex-end'\n        container.style.gap = '4px'\n\n        group.items.forEach((position) => {\n            const button = createButton(position, options)\n\n            button.addEventListener('click', () => {\n                const scrollOffset = options?.scrollOffset || 0\n                window.scrollTo({\n                    top: position.scrollY + scrollOffset,\n                    behavior: 'smooth',\n                })\n\n                if (options?.onClick) {\n                    options.onClick(position)\n                }\n            })\n\n            container.appendChild(button)\n        })\n\n        containers.push(container)\n    })\n\n    return containers\n}\n\nconst createButton = (position: HeadingPosition, options?: ButtonOptions) => {\n    const button = document.createElement('button')\n    button.textContent = position.element.textContent || ''\n    button.className = options?.className || ''\n\n    return button\n}\n","import { parseHeading } from '../utils/parse'\nimport { calculateHeadingPositions } from '../utils/calculate'\nimport { renderButtons } from '../ui/button'\nimport type { ButtonOptions } from '../types/position'\n\nconst activeInstances = new Map<HTMLElement, {\n    containers: HTMLElement[]\n    rafId: number\n    cleanup: () => void\n}>()\n\nlet navigationHandlerInitialized = false\n\nconst initializeNavigationHandler = () => {\n    if (navigationHandlerInitialized || typeof window === 'undefined' || typeof history === 'undefined') return\n    navigationHandlerInitialized = true\n\n    const cleanupDisconnectedInstances = () => {\n        activeInstances.forEach((instance, element) => {\n            if (!element.isConnected || !document.contains(element)) {\n                instance.cleanup()\n            }\n        })\n    }\n\n    window.addEventListener('popstate', cleanupDisconnectedInstances)\n\n    const wrapHistoryMethod = (method: 'pushState' | 'replaceState') => {\n        const original = history[method]\n        history[method] = function (...args: Parameters<typeof original>) {\n            cleanupDisconnectedInstances()\n            return original.apply(this, args)\n        }\n    }\n\n    wrapHistoryMethod('pushState')\n    wrapHistoryMethod('replaceState')\n}\n\nexport const setScrollToc = (htmlElement?: HTMLElement, options?: ButtonOptions) => {\n    if (!htmlElement) return () => {}\n    if (typeof document === 'undefined') return () => {}\n\n    const body = document.querySelector('body')\n    if (!body) return () => {}\n\n    initializeNavigationHandler()\n\n    const prev = activeInstances.get(htmlElement)\n    if (prev) {\n        prev.cleanup()\n    }\n\n    const headings = parseHeading(htmlElement, options?.exceptLevel)\n    const positions = calculateHeadingPositions(headings, options?.scrollOffset)\n    const containers = renderButtons(positions, options)\n\n    containers.forEach((container) => {\n        body.appendChild(container)\n    })\n\n    let rafId: number\n\n    const cleanup = () => {\n        cancelAnimationFrame(rafId)\n        containers.forEach((c) => c.remove())\n        activeInstances.delete(htmlElement)\n    }\n\n    const check = () => {\n        if (!htmlElement.isConnected || !document.contains(htmlElement)) {\n            cleanup()\n            return\n        }\n        rafId = requestAnimationFrame(check)\n    }\n\n    rafId = requestAnimationFrame(check)\n\n    activeInstances.set(htmlElement, { containers, rafId, cleanup })\n\n    return cleanup\n}\n"],"mappings":"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"}