@ox-content/vite-plugin 0.0.1-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/environment.ts","../src/highlight.ts","../src/mermaid.ts","../src/transform.ts","../src/docs.ts","../src/nav-generator.ts","../src/ssg.ts","../src/search.ts"],"sourcesContent":["/**\n * Vite Plugin for Ox Content\n *\n * Uses Vite's Environment API for SSG-focused Markdown processing.\n * Provides separate environments for client and server rendering.\n */\n\nimport * as path from 'path';\nimport type { Plugin, ViteDevServer, ResolvedConfig } from 'vite';\nimport { createMarkdownEnvironment } from './environment';\nimport { transformMarkdown } from './transform';\nimport { extractDocs, generateMarkdown, writeDocs, resolveDocsOptions } from './docs';\nimport { buildSsg, resolveSsgOptions } from './ssg';\nimport { resolveSearchOptions, buildSearchIndex, writeSearchIndex, generateSearchModule } from './search';\nimport type { OxContentOptions, ResolvedOptions } from './types';\n\nexport type { OxContentOptions } from './types';\nexport type {\n DocsOptions,\n ResolvedDocsOptions,\n DocEntry,\n ParamDoc,\n ReturnDoc,\n ExtractedDocs,\n SsgOptions,\n ResolvedSsgOptions,\n SearchOptions,\n ResolvedSearchOptions,\n SearchDocument,\n SearchResult,\n} from './types';\n\n/**\n * Creates the Ox Content Vite plugin.\n *\n * @example\n * ```ts\n * // vite.config.ts\n * import { defineConfig } from 'vite';\n * import { oxContent } from 'vite-plugin-ox-content';\n *\n * export default defineConfig({\n * plugins: [\n * oxContent({\n * srcDir: 'docs',\n * gfm: true,\n * }),\n * ],\n * });\n * ```\n */\nexport function oxContent(options: OxContentOptions = {}): Plugin[] {\n const resolvedOptions = resolveOptions(options);\n let config: ResolvedConfig;\n let _server: ViteDevServer | undefined;\n\n const mainPlugin: Plugin = {\n name: 'ox-content',\n\n configResolved(resolvedConfig) {\n config = resolvedConfig;\n },\n\n configureServer(devServer) {\n _server = devServer;\n\n // Add middleware for serving Markdown files\n devServer.middlewares.use(async (req, res, next) => {\n const url = req.url;\n if (!url || !url.endsWith('.md')) {\n return next();\n }\n\n // Let Vite handle the transformation\n next();\n });\n },\n\n resolveId(id) {\n // Handle virtual modules for Markdown imports\n if (id.startsWith('virtual:ox-content/')) {\n return '\\0' + id;\n }\n\n // Resolve .md files\n if (id.endsWith('.md')) {\n return id;\n }\n\n return null;\n },\n\n async load(id) {\n // Handle virtual modules\n if (id.startsWith('\\0virtual:ox-content/')) {\n const path = id.slice('\\0virtual:ox-content/'.length);\n return generateVirtualModule(path, resolvedOptions);\n }\n\n return null;\n },\n\n async transform(code, id) {\n if (!id.endsWith('.md')) {\n return null;\n }\n\n // Transform Markdown to JavaScript module\n const result = await transformMarkdown(code, id, resolvedOptions);\n\n return {\n code: result.code,\n map: null,\n };\n },\n\n // Hot Module Replacement support\n async handleHotUpdate({ file, server }) {\n if (file.endsWith('.md')) {\n // Notify client about the update\n server.ws.send({\n type: 'custom',\n event: 'ox-content:update',\n data: { file },\n });\n\n // Return empty array to prevent default HMR\n // We handle it ourselves\n const modules = server.moduleGraph.getModulesByFile(file);\n return modules ? Array.from(modules) : [];\n }\n },\n };\n\n // Environment API plugin for SSG\n const environmentPlugin: Plugin = {\n name: 'ox-content:environment',\n\n config() {\n return {\n environments: {\n // Markdown processing environment\n markdown: createMarkdownEnvironment(resolvedOptions),\n },\n };\n },\n };\n\n // Docs generation plugin (builtin, opt-out)\n const docsPlugin: Plugin = {\n name: 'ox-content:docs',\n\n async buildStart() {\n const docsOptions = resolvedOptions.docs;\n if (!docsOptions || !docsOptions.enabled) {\n return;\n }\n\n // Generate docs at build start\n const root = config?.root || process.cwd();\n const srcDirs = docsOptions.src.map((src) => path.resolve(root, src));\n const outDir = path.resolve(root, docsOptions.out);\n\n try {\n const extracted = await extractDocs(srcDirs, docsOptions);\n\n if (extracted.length > 0) {\n const generated = generateMarkdown(extracted, docsOptions);\n await writeDocs(generated, outDir, extracted, docsOptions);\n\n console.log(\n `[ox-content] Generated ${Object.keys(generated).length} documentation files to ${docsOptions.out}`\n );\n }\n } catch (err) {\n console.warn('[ox-content] Failed to generate documentation:', err);\n }\n },\n\n configureServer(devServer) {\n const docsOptions = resolvedOptions.docs;\n if (!docsOptions || !docsOptions.enabled) {\n return;\n }\n\n // Watch source directories for changes\n const root = config?.root || process.cwd();\n const srcDirs = docsOptions.src.map((src) => path.resolve(root, src));\n\n for (const srcDir of srcDirs) {\n devServer.watcher.add(srcDir);\n }\n\n // Regenerate docs on file changes\n devServer.watcher.on('change', async (file) => {\n const isSourceFile = srcDirs.some(\n (srcDir) =>\n file.startsWith(srcDir) &&\n (file.endsWith('.ts') || file.endsWith('.tsx'))\n );\n\n if (isSourceFile) {\n const outDir = path.resolve(root, docsOptions.out);\n\n try {\n const extracted = await extractDocs(srcDirs, docsOptions);\n if (extracted.length > 0) {\n const generated = generateMarkdown(extracted, docsOptions);\n await writeDocs(generated, outDir, extracted, docsOptions);\n }\n } catch {\n // Ignore errors during dev\n }\n }\n });\n },\n };\n\n // SSG plugin (builtin, opt-in by default)\n const ssgPlugin: Plugin = {\n name: 'ox-content:ssg',\n\n async closeBundle() {\n const ssgOptions = resolvedOptions.ssg;\n if (!ssgOptions.enabled) {\n return;\n }\n\n const root = config?.root || process.cwd();\n\n try {\n const result = await buildSsg(resolvedOptions, root);\n\n if (result.files.length > 0) {\n console.log(\n `[ox-content] Generated ${result.files.length} HTML files`\n );\n }\n\n if (result.errors.length > 0) {\n for (const error of result.errors) {\n console.warn(`[ox-content] ${error}`);\n }\n }\n } catch (err) {\n console.error('[ox-content] SSG build failed:', err);\n }\n },\n };\n\n // Search plugin\n let searchIndexJson = '';\n const searchPlugin: Plugin = {\n name: 'ox-content:search',\n\n resolveId(id) {\n if (id === 'virtual:ox-content/search') {\n return '\\0virtual:ox-content/search';\n }\n return null;\n },\n\n async load(id) {\n if (id === '\\0virtual:ox-content/search') {\n const searchOptions = resolvedOptions.search;\n if (!searchOptions.enabled) {\n return 'export const search = () => []; export const searchOptions = { enabled: false }; export default { search, searchOptions };';\n }\n\n const indexPath = resolvedOptions.base + 'search-index.json';\n return generateSearchModule(searchOptions, indexPath);\n }\n return null;\n },\n\n async buildStart() {\n const searchOptions = resolvedOptions.search;\n if (!searchOptions.enabled) {\n return;\n }\n\n const root = config?.root || process.cwd();\n const srcDir = path.resolve(root, resolvedOptions.srcDir);\n\n try {\n searchIndexJson = await buildSearchIndex(srcDir, resolvedOptions.base);\n console.log('[ox-content] Search index built');\n } catch (err) {\n console.warn('[ox-content] Failed to build search index:', err);\n }\n },\n\n async closeBundle() {\n const searchOptions = resolvedOptions.search;\n if (!searchOptions.enabled || !searchIndexJson) {\n return;\n }\n\n const root = config?.root || process.cwd();\n const outDir = path.resolve(root, resolvedOptions.outDir);\n\n try {\n await writeSearchIndex(searchIndexJson, outDir);\n console.log('[ox-content] Search index written to', path.join(outDir, 'search-index.json'));\n } catch (err) {\n console.warn('[ox-content] Failed to write search index:', err);\n }\n },\n };\n\n return [mainPlugin, environmentPlugin, docsPlugin, ssgPlugin, searchPlugin];\n}\n\n/**\n * Resolves plugin options with defaults.\n */\nfunction resolveOptions(options: OxContentOptions): ResolvedOptions {\n return {\n srcDir: options.srcDir ?? 'docs',\n outDir: options.outDir ?? 'dist',\n base: options.base ?? '/',\n ssg: resolveSsgOptions(options.ssg),\n gfm: options.gfm ?? true,\n footnotes: options.footnotes ?? true,\n tables: options.tables ?? true,\n taskLists: options.taskLists ?? true,\n strikethrough: options.strikethrough ?? true,\n highlight: options.highlight ?? false,\n highlightTheme: options.highlightTheme ?? 'github-dark',\n mermaid: options.mermaid ?? false,\n frontmatter: options.frontmatter ?? true,\n toc: options.toc ?? true,\n tocMaxDepth: options.tocMaxDepth ?? 3,\n ogImage: options.ogImage ?? false,\n ogImageOptions: options.ogImageOptions ?? {},\n transformers: options.transformers ?? [],\n docs: resolveDocsOptions(options.docs),\n search: resolveSearchOptions(options.search),\n };\n}\n\n/**\n * Generates virtual module content.\n */\nfunction generateVirtualModule(\n path: string,\n options: ResolvedOptions\n): string {\n if (path === 'config') {\n return `export default ${JSON.stringify(options)};`;\n }\n\n if (path === 'runtime') {\n return `\n export function useMarkdown() {\n return {\n render: (content) => {\n // Client-side rendering if needed\n return content;\n },\n };\n }\n `;\n }\n\n return 'export default {};';\n}\n\n// Re-export types and utilities\nexport { createMarkdownEnvironment } from './environment';\nexport { transformMarkdown } from './transform';\nexport { extractDocs, generateMarkdown, writeDocs, resolveDocsOptions } from './docs';\nexport { buildSsg, resolveSsgOptions, DEFAULT_HTML_TEMPLATE } from './ssg';\nexport { resolveSearchOptions, buildSearchIndex, writeSearchIndex } from './search';\nexport * from './types';\n","/**\n * Vite Environment API integration for Ox Content.\n *\n * Creates a dedicated environment for Markdown processing,\n * enabling SSG-style rendering with separate client/server contexts.\n */\n\nimport type { EnvironmentOptions } from 'vite';\nimport type { ResolvedOptions } from './types';\n\n/**\n * Creates the Markdown processing environment configuration.\n *\n * This environment is used for:\n * - Server-side rendering of Markdown files\n * - Static site generation\n * - Pre-rendering at build time\n *\n * @example\n * ```ts\n * // In your vite.config.ts\n * export default defineConfig({\n * environments: {\n * markdown: createMarkdownEnvironment({\n * srcDir: 'docs',\n * gfm: true,\n * }),\n * },\n * });\n * ```\n */\nexport function createMarkdownEnvironment(\n options: ResolvedOptions\n): EnvironmentOptions {\n return {\n // Consumer type for this environment\n consumer: 'server',\n\n // Build configuration\n build: {\n // Output to a separate directory\n outDir: `${options.outDir}/.markdown`,\n\n // Emit assets for SSG\n emitAssets: true,\n\n // Create manifest for asset tracking\n manifest: true,\n\n // SSR-like externalization\n rollupOptions: {\n external: [\n // Externalize Node.js built-ins\n /^node:/,\n // Externalize native modules\n /\\.node$/,\n ],\n },\n },\n\n // Resolve configuration\n resolve: {\n // Handle .md files\n extensions: ['.md', '.markdown'],\n\n // Conditions for module resolution\n conditions: ['markdown', 'node', 'import'],\n\n // Don't dedupe - each environment gets its own modules\n dedupe: [],\n },\n\n // Optimize dependencies\n optimizeDeps: {\n // Include ox-content dependencies\n include: [],\n // Exclude native modules\n exclude: ['@ox-content/napi'],\n },\n };\n}\n\n/**\n * Environment-specific module transformer.\n *\n * This is called during the transform phase to process\n * Markdown files within the environment context.\n */\nexport interface EnvironmentTransformContext {\n /**\n * Current environment name.\n */\n environment: string;\n\n /**\n * Whether we're in development mode.\n */\n isDev: boolean;\n\n /**\n * Whether this is a server-side render.\n */\n isSSR: boolean;\n\n /**\n * The resolved Vite config.\n */\n config: unknown;\n}\n\n/**\n * Creates environment-aware transform options.\n */\nexport function createTransformOptions(\n ctx: EnvironmentTransformContext,\n options: ResolvedOptions\n): ResolvedOptions {\n return {\n ...options,\n // Adjust options based on environment\n highlight: ctx.isSSR ? options.highlight : false,\n ogImage: ctx.isSSR ? options.ogImage : false,\n };\n}\n\n/**\n * Runs pre-render for SSG.\n *\n * This function is called during build to pre-render all Markdown files.\n */\nexport async function prerender(\n files: string[],\n _options: ResolvedOptions\n): Promise<Map<string, string>> {\n const results = new Map<string, string>();\n\n for (const file of files) {\n // In production, this would use the Ox Content parser\n // For now, we just mark the file as needing processing\n results.set(file, `/* Pre-rendered: ${file} */`);\n }\n\n return results;\n}\n\n/**\n * Environment plugin factory.\n *\n * Creates plugins specific to the Markdown environment.\n */\nexport function createEnvironmentPlugins(_options: ResolvedOptions) {\n return [\n {\n name: 'ox-content:markdown-env',\n\n // Only apply to markdown environment\n applyToEnvironment(name: string) {\n return name === 'markdown';\n },\n\n // Transform within the environment\n transform(code: string, id: string) {\n if (!id.endsWith('.md')) {\n return null;\n }\n\n // Environment-specific transformation\n return {\n code: `\n // Transformed in markdown environment\n ${code}\n `,\n };\n },\n },\n ];\n}\n","/**\n * Syntax highlighting with Shiki via rehype.\n */\n\nimport { unified } from 'unified';\nimport rehypeParse from 'rehype-parse';\nimport rehypeStringify from 'rehype-stringify';\nimport type { Root, Element } from 'hast';\nimport { createHighlighter, type Highlighter, type BundledTheme } from 'shiki';\n\n// Cached highlighter instance\nlet highlighterPromise: Promise<Highlighter> | null = null;\n\n/**\n * Get or create the Shiki highlighter.\n */\nasync function getHighlighter(theme: string): Promise<Highlighter> {\n if (!highlighterPromise) {\n highlighterPromise = createHighlighter({\n themes: [theme as BundledTheme],\n langs: [\n 'javascript',\n 'typescript',\n 'jsx',\n 'tsx',\n 'vue',\n 'svelte',\n 'html',\n 'css',\n 'scss',\n 'json',\n 'yaml',\n 'markdown',\n 'bash',\n 'shell',\n 'rust',\n 'python',\n 'go',\n 'java',\n 'c',\n 'cpp',\n 'sql',\n 'graphql',\n 'diff',\n 'toml',\n ],\n });\n }\n return highlighterPromise;\n}\n\n/**\n * Rehype plugin for syntax highlighting with Shiki.\n */\nfunction rehypeShikiHighlight(options: { theme: string }) {\n const { theme } = options;\n\n return async (tree: Root) => {\n const highlighter = await getHighlighter(theme);\n\n // Find all pre > code elements\n const visit = async (node: Root | Element) => {\n if ('children' in node) {\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i];\n\n if (child.type === 'element' && child.tagName === 'pre') {\n const codeElement = child.children.find(\n (c): c is Element => c.type === 'element' && c.tagName === 'code'\n );\n\n if (codeElement) {\n // Extract language from class\n const className = codeElement.properties?.className;\n let lang = 'text';\n\n if (Array.isArray(className)) {\n const langClass = className.find(\n (c: string | number) => typeof c === 'string' && c.startsWith('language-')\n );\n if (langClass && typeof langClass === 'string') {\n lang = langClass.replace('language-', '');\n }\n }\n\n // Get code text\n const codeText = getTextContent(codeElement);\n\n // Highlight with Shiki\n try {\n const highlighted = highlighter.codeToHtml(codeText, {\n lang: lang as any,\n theme: theme as BundledTheme,\n });\n\n // Parse the highlighted HTML and replace the pre element\n const parsed = unified()\n .use(rehypeParse, { fragment: true })\n .parse(highlighted);\n\n // Replace the pre element with the highlighted one\n if (parsed.children[0]) {\n node.children[i] = parsed.children[0] as Element;\n }\n } catch {\n // If highlighting fails, keep the original\n }\n }\n } else if (child.type === 'element') {\n await visit(child);\n }\n }\n }\n };\n\n await visit(tree);\n };\n}\n\n/**\n * Extract text content from a hast node.\n */\nfunction getTextContent(node: Element | Root): string {\n let text = '';\n\n if ('children' in node) {\n for (const child of node.children) {\n if (child.type === 'text') {\n text += child.value;\n } else if (child.type === 'element') {\n text += getTextContent(child);\n }\n }\n }\n\n return text;\n}\n\n/**\n * Apply syntax highlighting to HTML using Shiki.\n */\nexport async function highlightCode(\n html: string,\n theme: string = 'github-dark'\n): Promise<string> {\n const result = await unified()\n .use(rehypeParse, { fragment: true })\n .use(rehypeShikiHighlight, { theme })\n .use(rehypeStringify)\n .process(html);\n\n return String(result);\n}\n","/**\n * Mermaid diagram support.\n *\n * Transforms mermaid code blocks into SVG diagrams.\n * Uses client-side rendering with a wrapper element.\n */\n\nimport { unified } from 'unified';\nimport rehypeParse from 'rehype-parse';\nimport rehypeStringify from 'rehype-stringify';\nimport type { Root, Element } from 'hast';\n\n/**\n * Extract text content from a hast node.\n */\nfunction getTextContent(node: Element | Root): string {\n let text = '';\n\n if ('children' in node) {\n for (const child of node.children) {\n if (child.type === 'text') {\n text += child.value;\n } else if (child.type === 'element') {\n text += getTextContent(child);\n }\n }\n }\n\n return text;\n}\n\n/**\n * Rehype plugin to transform mermaid code blocks.\n *\n * Replaces ```mermaid blocks with a wrapper element\n * that can be rendered client-side.\n */\nfunction rehypeMermaid() {\n return (tree: Root) => {\n const visit = (node: Root | Element) => {\n if ('children' in node) {\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i];\n\n if (child.type === 'element' && child.tagName === 'pre') {\n const codeElement = child.children.find(\n (c): c is Element => c.type === 'element' && c.tagName === 'code'\n );\n\n if (codeElement) {\n // Check if this is a mermaid code block\n const className = codeElement.properties?.className;\n let isMermaid = false;\n\n if (Array.isArray(className)) {\n isMermaid = className.some(\n (c: string | number) => typeof c === 'string' && c.includes('mermaid')\n );\n }\n\n if (isMermaid) {\n const mermaidCode = getTextContent(codeElement);\n\n // Replace with mermaid wrapper\n const wrapper: Element = {\n type: 'element',\n tagName: 'div',\n properties: {\n className: ['ox-mermaid'],\n 'data-mermaid': mermaidCode,\n },\n children: [\n {\n type: 'element',\n tagName: 'pre',\n properties: {\n className: ['ox-mermaid-source'],\n },\n children: [\n {\n type: 'text',\n value: mermaidCode,\n },\n ],\n },\n ],\n };\n\n node.children[i] = wrapper;\n }\n }\n } else if (child.type === 'element') {\n visit(child);\n }\n }\n }\n };\n\n visit(tree);\n };\n}\n\n/**\n * Transform mermaid code blocks in HTML.\n *\n * Creates wrapper elements that can be rendered client-side\n * by the mermaid runtime.\n */\nexport async function transformMermaid(html: string): Promise<string> {\n const result = await unified()\n .use(rehypeParse, { fragment: true })\n .use(rehypeMermaid)\n .use(rehypeStringify)\n .process(html);\n\n return String(result);\n}\n\n/**\n * Client-side mermaid initialization script.\n *\n * This script should be included in the page to render\n * mermaid diagrams.\n */\nexport const mermaidClientScript = `\n<script type=\"module\">\n import mermaid from 'https://esm.sh/mermaid@11/dist/mermaid.esm.min.mjs';\n\n mermaid.initialize({\n startOnLoad: false,\n theme: 'dark',\n themeVariables: {\n primaryColor: '#bd34fe',\n primaryTextColor: '#fff',\n primaryBorderColor: '#7c3aed',\n lineColor: '#41d1ff',\n secondaryColor: '#1a1a2e',\n tertiaryColor: '#161618',\n },\n });\n\n async function renderMermaidDiagrams() {\n const elements = document.querySelectorAll('.ox-mermaid');\n\n for (const el of elements) {\n const code = el.dataset.mermaid;\n if (!code) continue;\n\n try {\n const id = 'mermaid-' + Math.random().toString(36).slice(2, 9);\n const { svg } = await mermaid.render(id, code);\n\n // Replace content with rendered SVG\n el.innerHTML = svg;\n el.classList.add('ox-mermaid-rendered');\n } catch (err) {\n console.error('Mermaid render error:', err);\n el.classList.add('ox-mermaid-error');\n }\n }\n }\n\n // Render on load and on HMR updates\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', renderMermaidDiagrams);\n } else {\n renderMermaidDiagrams();\n }\n\n // Re-render on content updates (for SPA navigation)\n if (import.meta.hot) {\n import.meta.hot.on('ox-content:update', renderMermaidDiagrams);\n }\n</script>\n`;\n\n/**\n * CSS styles for mermaid diagrams.\n */\nexport const mermaidStyles = `\n<style>\n .ox-mermaid {\n margin: 1.5rem 0;\n padding: 1rem;\n background: var(--code-bg, #161618);\n border-radius: 8px;\n border: 1px solid var(--border-color, #2e2e32);\n overflow-x: auto;\n }\n\n .ox-mermaid-source {\n display: none;\n }\n\n .ox-mermaid-rendered .ox-mermaid-source {\n display: none;\n }\n\n .ox-mermaid svg {\n max-width: 100%;\n height: auto;\n }\n\n .ox-mermaid-error {\n color: #f87171;\n padding: 1rem;\n }\n\n .ox-mermaid-error .ox-mermaid-source {\n display: block;\n }\n</style>\n`;\n","/**\n * Markdown Transformation Engine\n *\n * This module handles the complete transformation pipeline for Markdown files,\n * converting raw Markdown content into JavaScript modules that can be imported\n * by web applications. The transformation process includes:\n *\n * 1. **Parsing**: Uses Rust-based parser via NAPI bindings for high performance\n * 2. **Rendering**: Converts parsed AST to semantic HTML\n * 3. **Enhancement**: Applies syntax highlighting, Mermaid diagram rendering, etc.\n * 4. **Code Generation**: Generates JavaScript/TypeScript module code\n *\n * The generated modules export:\n * - `html`: Rendered HTML content\n * - `frontmatter`: Parsed YAML metadata\n * - `toc`: Hierarchical table of contents\n * - `render`: Client-side render function for dynamic updates\n *\n * @example\n * ```typescript\n * import { transformMarkdown } from './transform';\n *\n * const content = await transformMarkdown(\n * '# Hello\\n\\nWorld',\n * 'path/to/file.md',\n * resolvedOptions\n * );\n *\n * console.log(content.html); // '<h1>Hello</h1><p>World</p>'\n * console.log(content.toc); // [{ depth: 1, text: 'Hello', slug: 'hello', children: [] }]\n * ```\n */\n\nimport type { ResolvedOptions, TransformResult, TocEntry } from './types';\nimport { highlightCode } from './highlight';\nimport { transformMermaid } from './mermaid';\n\n/**\n * NAPI bindings for Rust-based Markdown processing.\n *\n * Provides access to compiled Rust functions for high-performance\n * Markdown parsing and rendering operations.\n */\ninterface NapiBindings {\n /**\n * Simple Markdown parser and renderer in one step.\n * Faster for simple use cases but lacks advanced features.\n *\n * @param source - Raw Markdown content\n * @param options - Parser configuration (GFM flag)\n * @returns Rendered HTML and parsing errors\n */\n parseAndRender: (source: string, options?: { gfm?: boolean }) => { html: string; errors: string[] };\n\n /**\n * Full-featured Markdown transformation pipeline.\n * Handles frontmatter extraction, TOC generation, and advanced parsing.\n *\n * @param source - Raw Markdown content (may include frontmatter)\n * @param options - Comprehensive transformation options\n * @returns Transformed result with HTML, metadata, and TOC\n */\n transform: (source: string, options?: JsTransformOptions) => {\n html: string;\n frontmatter: string;\n toc: { depth: number; text: string; slug: string }[];\n errors: string[];\n };\n\n /**\n * Generates an OG image as SVG.\n *\n * @param data - OG image data (title, description, etc.)\n * @param config - Optional OG image configuration\n * @returns SVG string\n */\n generateOgImageSvg: (data: OgImageData, config?: OgImageConfig) => string;\n}\n\n/**\n * OG image data for generating social media preview images.\n */\nexport interface OgImageData {\n /** Page title */\n title: string;\n /** Page description */\n description?: string;\n /** Site name */\n siteName?: string;\n /** Author name */\n author?: string;\n}\n\n/**\n * OG image configuration.\n */\nexport interface OgImageConfig {\n /** Image width in pixels */\n width?: number;\n /** Image height in pixels */\n height?: number;\n /** Background color (hex) */\n backgroundColor?: string;\n /** Text color (hex) */\n textColor?: string;\n /** Title font size */\n titleFontSize?: number;\n /** Description font size */\n descriptionFontSize?: number;\n}\n\n/**\n * Options for Rust-based Markdown transformation.\n *\n * Controls which Markdown extensions and features are enabled\n * during parsing and rendering.\n */\ninterface JsTransformOptions {\n /**\n * Enable GitHub Flavored Markdown extensions.\n * Includes tables, task lists, strikethrough, and autolinks.\n * @default false\n */\n gfm?: boolean;\n\n /**\n * Enable footnotes syntax ([^1]: definition).\n * @default false\n */\n footnotes?: boolean;\n\n /**\n * Enable task list syntax (- [ ] unchecked, - [x] checked).\n * @default false\n */\n taskLists?: boolean;\n\n /**\n * Enable table rendering (GFM extension).\n * Requires GFM to be enabled for full functionality.\n * @default false\n */\n tables?: boolean;\n\n /**\n * Enable strikethrough syntax (~~text~~).\n * Requires GFM to be enabled.\n * @default false\n */\n strikethrough?: boolean;\n\n /**\n * Enable automatic link conversion (URLs become clickable).\n * @default false\n */\n autolinks?: boolean;\n\n /**\n * Maximum heading depth for table of contents.\n * Headings deeper than this level are excluded from TOC.\n * @default 3\n * @min 1\n * @max 6\n */\n tocMaxDepth?: number;\n\n /**\n * Convert `.md` links to `.html` links for SSG output.\n * @default false\n */\n convertMdLinks?: boolean;\n\n /**\n * Base URL for absolute link conversion (e.g., \"/\" or \"/docs/\").\n * @default \"/\"\n */\n baseUrl?: string;\n}\n\n/**\n * Cached NAPI bindings instance.\n * Loaded on first use and reused for subsequent transformations.\n * @internal\n */\nlet napiBindings: NapiBindings | null | undefined;\n\n/**\n * Flag to prevent repeated NAPI loading attempts.\n * Set to true after first load attempt (success or failure).\n * @internal\n */\nlet napiLoadAttempted = false;\n\n/**\n * Lazily loads and caches NAPI bindings.\n *\n * This function uses lazy loading to defer the import of NAPI bindings\n * until they're actually needed. The bindings are loaded only once and\n * cached for subsequent uses. If loading fails (e.g., bindings not built),\n * the failure is cached to avoid repeated load attempts.\n *\n * ## Performance Considerations\n *\n * The first call to this function may have a slight performance penalty\n * due to module loading. Subsequent calls use the cached result and are\n * essentially zero-cost.\n *\n * ## Error Handling\n *\n * If NAPI bindings are not available (not built, wrong architecture, etc.),\n * this function returns `null`. The caller should handle this gracefully\n * or provide fallback behavior.\n *\n * @returns Promise resolving to NAPI bindings or null if unavailable\n *\n * @example\n * ```typescript\n * // Simple check with fallback\n * const napi = await loadNapiBindings();\n * if (!napi) {\n * console.warn('NAPI bindings not available, using fallback');\n * return fallbackRender(content);\n * }\n *\n * // Use Rust implementation\n * const result = napi.transform(content, { gfm: true });\n * ```\n *\n * @internal\n */\nasync function loadNapiBindings(): Promise<NapiBindings | null> {\n // Return cached result (success or failure)\n if (napiLoadAttempted) {\n return napiBindings ?? null;\n }\n\n // Mark attempt as made to prevent retry loops\n napiLoadAttempted = true;\n\n try {\n // Dynamic import to handle cases where NAPI isn't built\n const mod = await import('@ox-content/napi');\n napiBindings = mod;\n return mod;\n } catch (error) {\n // NAPI not available (not built, missing dependencies, etc.)\n // Log for debugging but don't throw - allow graceful degradation\n if (process.env.DEBUG) {\n console.debug('[ox-content] NAPI bindings load failed:', error);\n }\n napiBindings = null;\n return null;\n }\n}\n\n/**\n * Transforms Markdown content into a JavaScript module.\n *\n * This is the primary entry point for transforming Markdown files. It handles\n * the complete transformation pipeline including parsing, rendering, syntax\n * highlighting, and code generation.\n *\n * ## Pipeline Steps\n *\n * 1. **Parse & Render**: Uses Rust-based parser via NAPI for high performance\n * 2. **Extract Metadata**: Parses YAML frontmatter and generates table of contents\n * 3. **Enhance HTML**: Applies syntax highlighting and Mermaid diagram rendering\n * 4. **Generate Code**: Creates importable JavaScript module\n *\n * ## Generated Module Exports\n *\n * - `html` (string): Rendered HTML content with all enhancements applied\n * - `frontmatter` (object): Parsed YAML frontmatter as JavaScript object\n * - `toc` (array): Hierarchical table of contents entries\n * - `render` (function): Client-side render function for dynamic updates\n *\n * ## Markdown Features Supported\n *\n * The supported features depend on parser options:\n * - **Commonmark**: Headings, paragraphs, lists, code blocks, links, images\n * - **GFM Extensions**: Tables, task lists, strikethrough, autolinks\n * - **Enhancements**: Syntax highlighting, Mermaid diagrams, TOC generation\n * - **Metadata**: YAML frontmatter parsing\n *\n * ## Performance\n *\n * Uses Rust-based parsing via NAPI bindings for optimal performance. Falls back\n * gracefully if Rust bindings are unavailable.\n *\n * @param source - Raw Markdown source code (may include YAML frontmatter)\n * @param filePath - File path for source attribution and relative link resolution\n * @param options - Resolved plugin options controlling transformation behavior\n *\n * @returns Promise resolving to transformation result with HTML and metadata\n *\n * @throws Error if NAPI bindings are unavailable (can be handled gracefully)\n *\n * @example\n * ```typescript\n * import { transformMarkdown } from './transform';\n * import { resolveOptions } from './index';\n *\n * // Transform a Markdown file with YAML frontmatter\n * const markdown = `---\n * title: Getting Started\n * author: john\n * ---\n *\n * # Getting Started\n *\n * Welcome! This guide explains [transformMarkdown] function.\n *\n * ## Installation\n *\n * \\`\\`\\`bash\n * npm install vite-plugin-ox-content\n * \\`\\`\\`\n * `;\n *\n * const options = resolveOptions({\n * highlight: true,\n * highlightTheme: 'github-dark',\n * toc: true,\n * gfm: true,\n * mermaid: true,\n * });\n *\n * const result = await transformMarkdown(markdown, 'docs/getting-started.md', options);\n *\n * // Generated module exports\n * console.log(result.html); // Rendered HTML with syntax highlighting\n * console.log(result.frontmatter); // { title: 'Getting Started', author: 'john' }\n * console.log(result.toc); // [{ depth: 1, text: 'Getting Started', ... }]\n * console.log(result.code); // ES module export statement\n * ```\n */\n/**\n * SSG-specific transform options.\n */\nexport interface SsgTransformOptions {\n /** Convert `.md` links to `.html` links */\n convertMdLinks?: boolean;\n /** Base URL for absolute link conversion */\n baseUrl?: string;\n}\n\nexport async function transformMarkdown(\n source: string,\n filePath: string,\n options: ResolvedOptions,\n ssgOptions?: SsgTransformOptions\n): Promise<TransformResult> {\n const napi = await loadNapiBindings();\n\n if (!napi) {\n throw new Error('[ox-content] NAPI bindings not available. Please ensure @ox-content/napi is built.');\n }\n\n // Use Rust-based transformation\n const result = napi.transform(source, {\n gfm: options.gfm,\n footnotes: options.footnotes,\n taskLists: options.taskLists,\n tables: options.tables,\n strikethrough: options.strikethrough,\n tocMaxDepth: options.tocMaxDepth,\n convertMdLinks: ssgOptions?.convertMdLinks,\n baseUrl: ssgOptions?.baseUrl,\n });\n\n if (result.errors.length > 0) {\n console.warn('[ox-content] Transform warnings:', result.errors);\n }\n\n let html = result.html;\n let frontmatter: Record<string, unknown>;\n\n try {\n frontmatter = JSON.parse(result.frontmatter);\n } catch {\n frontmatter = {};\n }\n\n // Convert flat TOC from Rust to nested TOC\n const flatToc: TocEntry[] = result.toc.map(item => ({\n ...item,\n children: [],\n }));\n const toc = options.toc ? buildTocTree(flatToc) : [];\n\n // Apply syntax highlighting if enabled\n if (options.highlight) {\n html = await highlightCode(html, options.highlightTheme);\n }\n\n // Transform mermaid diagrams if enabled\n if (options.mermaid) {\n html = await transformMermaid(html);\n }\n\n // Generate JavaScript module code\n const code = generateModuleCode(html, frontmatter, toc, filePath, options);\n\n return {\n code,\n html,\n frontmatter,\n toc,\n };\n}\n\n/**\n * Builds nested TOC tree from flat list.\n */\nfunction buildTocTree(entries: TocEntry[]): TocEntry[] {\n const root: TocEntry[] = [];\n const stack: TocEntry[] = [];\n\n for (const entry of entries) {\n // Pop stack until we find a parent with smaller depth\n while (stack.length > 0 && stack[stack.length - 1].depth >= entry.depth) {\n stack.pop();\n }\n\n if (stack.length === 0) {\n root.push(entry);\n } else {\n stack[stack.length - 1].children.push(entry);\n }\n\n stack.push(entry);\n }\n\n return root;\n}\n\n/**\n * Generates the JavaScript module code.\n */\nfunction generateModuleCode(\n html: string,\n frontmatter: Record<string, unknown>,\n toc: TocEntry[],\n filePath: string,\n _options: ResolvedOptions\n): string {\n const htmlJson = JSON.stringify(html);\n const frontmatterJson = JSON.stringify(frontmatter);\n const tocJson = JSON.stringify(toc);\n\n return `\n// Generated by vite-plugin-ox-content\n// Source: ${filePath}\n\n/**\n * Rendered HTML content.\n */\nexport const html = ${htmlJson};\n\n/**\n * Parsed frontmatter.\n */\nexport const frontmatter = ${frontmatterJson};\n\n/**\n * Table of contents.\n */\nexport const toc = ${tocJson};\n\n/**\n * Default export with all data.\n */\nexport default {\n html,\n frontmatter,\n toc,\n};\n\n// HMR support\nif (import.meta.hot) {\n import.meta.hot.accept((newModule) => {\n if (newModule) {\n // Trigger re-render with new content\n import.meta.hot.invalidate();\n }\n });\n}\n`;\n}\n\n/**\n * Extracts imports from Markdown content.\n *\n * Supports importing components for interactive islands.\n */\nexport function extractImports(content: string): string[] {\n const importRegex = /^import\\s+.+\\s+from\\s+['\"](.+)['\"]/gm;\n const imports: string[] = [];\n let match;\n\n while ((match = importRegex.exec(content)) !== null) {\n imports.push(match[1]);\n }\n\n return imports;\n}\n\n/**\n * Generates an OG image SVG using the Rust-based generator.\n *\n * This function uses the Rust NAPI bindings to generate SVG-based\n * OG images for social media previews. The SVG can be served directly\n * or converted to PNG/JPEG for broader compatibility.\n *\n * In the future, custom JS templates can be provided to override\n * the default Rust-based template.\n *\n * @param data - OG image data (title, description, etc.)\n * @param config - Optional OG image configuration\n * @returns SVG string or null if NAPI bindings are unavailable\n */\nexport async function generateOgImageSvg(\n data: OgImageData,\n config?: OgImageConfig\n): Promise<string | null> {\n const napi = await loadNapiBindings();\n if (!napi) {\n return null;\n }\n\n // Convert config to NAPI format (camelCase to snake_case)\n const napiConfig = config\n ? {\n width: config.width,\n height: config.height,\n backgroundColor: config.backgroundColor,\n textColor: config.textColor,\n titleFontSize: config.titleFontSize,\n descriptionFontSize: config.descriptionFontSize,\n }\n : undefined;\n\n return napi.generateOgImageSvg(data, napiConfig);\n}\n","/**\n * Source Documentation Extraction and Generation\n *\n * This module provides comprehensive tools for extracting JSDoc/TSDoc comments\n * from TypeScript/JavaScript source files and automatically generating Markdown\n * documentation.\n *\n * ## Features\n *\n * - **Automatic Extraction**: Parses JSDoc comments from functions, classes, interfaces, and types\n * - **Flexible Filtering**: Include/exclude patterns for selective documentation\n * - **Markdown Generation**: Converts extracted docs to organized Markdown files\n * - **Navigation Generation**: Auto-generates sidebar navigation metadata\n * - **GitHub Links**: Includes clickable links to source code on GitHub\n *\n * ## Supported JSDoc Tags\n *\n * - `@param {type} name - description` - Function parameter documentation\n * - `@returns {type} description` - Return value documentation\n * - `@example` - Code examples (multi-line blocks)\n * - `@private` - Mark item as private (excluded from docs if private=false)\n * - `@default value` - Default parameter value\n * - Custom tags are preserved in the `tags` field\n *\n * ## Usage Flow\n *\n * 1. Call `extractDocs()` to parse source files\n * 2. Call `generateMarkdown()` to create Markdown content\n * 3. Call `writeDocs()` to write files to output directory\n * 4. Generated nav.ts can be imported for sidebar navigation\n *\n * @example\n * ```typescript\n * import { extractDocs, generateMarkdown, writeDocs } from './docs';\n *\n * const docsOptions = {\n * enabled: true,\n * src: ['./src'],\n * out: './docs/api',\n * include: ['**\\/*.ts'],\n * exclude: ['**\\/*.test.ts'],\n * groupBy: 'file',\n * githubUrl: 'https://github.com/user/project',\n * };\n *\n * const extracted = await extractDocs(['./src'], docsOptions);\n * const markdown = generateMarkdown(extracted, docsOptions);\n * await writeDocs(markdown, './docs/api', extracted, docsOptions);\n * ```\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport type { ResolvedDocsOptions, ExtractedDocs, DocEntry, ParamDoc } from './types';\nimport { generateNavMetadata, generateNavCode } from './nav-generator';\n\n/**\n * Regex pattern for matching JSDoc comment blocks.\n *\n * Matches `/** ... */` comments that start at the beginning of a line\n * (with optional leading whitespace). This pattern avoids false matches\n * with `/**` inside strings like glob patterns.\n *\n * @internal\n */\nconst JSDOC_BLOCK = /^[ \\t]*\\/\\*\\*\\s*([\\s\\S]*?)\\s*\\*\\//gm;\n\n/**\n * Regex pattern for matching function declarations.\n * Matches: `function name`, `export function name`, `async function name`\n * @internal\n */\nconst FUNCTION_DECL = /(?:export\\s+)?(?:async\\s+)?function\\s+(\\w+)/;\n\n/**\n * Regex pattern for matching const arrow/async functions.\n * Matches: `const name = () => {}`, `const name = async () => {}`\n * @internal\n */\nconst CONST_FUNC = /(?:export\\s+)?const\\s+(\\w+)\\s*=\\s*(?:async\\s*)?\\(/;\n\n/**\n * Regex pattern for matching class declarations.\n * Matches: `class Name`, `export class Name`\n * @internal\n */\nconst CLASS_DECL = /(?:export\\s+)?class\\s+(\\w+)/;\n\n/**\n * Regex pattern for matching interface declarations.\n * Matches: `interface Name`, `export interface Name`\n * @internal\n */\nconst INTERFACE_DECL = /(?:export\\s+)?interface\\s+(\\w+)/;\n\n/**\n * Regex pattern for matching type alias declarations.\n * Matches: `type Name = ...`, `export type Name = ...`\n * @internal\n */\nconst TYPE_DECL = /(?:export\\s+)?type\\s+(\\w+)/;\n\n/**\n * Extracts JSDoc documentation from source files in specified directories.\n *\n * This function recursively searches directories for source files matching\n * the include/exclude patterns, then extracts all documented items (functions,\n * classes, interfaces, types) from those files.\n *\n * ## Process\n *\n * 1. **File Discovery**: Recursively walks directories, applying filters\n * 2. **File Reading**: Loads each matching file's content\n * 3. **JSDoc Extraction**: Parses JSDoc comments using regex patterns\n * 4. **Declaration Matching**: Pairs JSDoc comments with source declarations\n * 5. **Result Collection**: Aggregates extracted documentation by file\n *\n * ## Include/Exclude Patterns\n *\n * Patterns support:\n * - `**` - Match any directory structure\n * - `*` - Match any filename\n * - Standard glob patterns (e.g., `**\\/*.test.ts`)\n *\n * ## Performance Considerations\n *\n * - Uses filesystem I/O which can be slow for large codebases\n * - Consider using more specific include patterns to reduce file scanning\n * - Results are not cached; call once per build/dev session\n *\n * @param srcDirs - Array of source directory paths to scan\n * @param options - Documentation extraction options (filters, grouping, etc.)\n *\n * @returns Promise resolving to array of extracted documentation by file.\n * Each ExtractedDocs object contains file path and array of DocEntry items.\n *\n * @example\n * ```typescript\n * const docs = await extractDocs(\n * ['./packages/vite-plugin/src'],\n * {\n * enabled: true,\n * src: [],\n * out: 'docs',\n * include: ['**\\/*.ts'],\n * exclude: ['**\\/*.test.ts', '**\\/*.spec.ts'],\n * format: 'markdown',\n * private: false,\n * toc: true,\n * groupBy: 'file',\n * generateNav: true,\n * }\n * );\n *\n * // Returns:\n * // [\n * // {\n * // file: '/path/to/transform.ts',\n * // entries: [\n * // { name: 'transformMarkdown', kind: 'function', ... },\n * // { name: 'loadNapiBindings', kind: 'function', ... },\n * // ]\n * // },\n * // ...\n * // ]\n * ```\n */\nexport async function extractDocs(\n srcDirs: string[],\n options: ResolvedDocsOptions\n): Promise<ExtractedDocs[]> {\n const results: ExtractedDocs[] = [];\n\n for (const srcDir of srcDirs) {\n const files = await findFiles(srcDir, options);\n\n for (const file of files) {\n const content = await fs.promises.readFile(file, 'utf-8');\n const entries = extractFromContent(content, file, options);\n\n if (entries.length > 0) {\n results.push({ file, entries });\n }\n }\n }\n\n return results;\n}\n\n/**\n * Recursively finds all source files matching include/exclude patterns.\n *\n * @internal\n */\nasync function findFiles(dir: string, options: ResolvedDocsOptions): Promise<string[]> {\n const files: string[] = [];\n\n async function walk(currentDir: string) {\n let entries;\n try {\n entries = await fs.promises.readdir(currentDir, { withFileTypes: true });\n } catch {\n return;\n }\n\n for (const entry of entries) {\n const fullPath = path.join(currentDir, entry.name);\n\n if (entry.isDirectory()) {\n if (!isExcluded(fullPath, options.exclude)) {\n await walk(fullPath);\n }\n } else if (entry.isFile()) {\n if (isIncluded(fullPath, options.include) && !isExcluded(fullPath, options.exclude)) {\n files.push(fullPath);\n }\n }\n }\n }\n\n await walk(dir);\n return files;\n}\n\nfunction isIncluded(file: string, patterns: string[]): boolean {\n return patterns.some((pattern) => {\n if (pattern.includes('**')) {\n const ext = pattern.split('.').pop();\n return file.endsWith(`.${ext}`);\n }\n return file.endsWith(pattern.replace('*', ''));\n });\n}\n\nfunction isExcluded(file: string, patterns: string[]): boolean {\n return patterns.some((pattern) => {\n if (pattern.includes('node_modules')) {\n return file.includes('node_modules');\n }\n if (pattern.includes('.test.') || pattern.includes('.spec.')) {\n return file.includes('.test.') || file.includes('.spec.');\n }\n return false;\n });\n}\n\n/**\n * Extracts documentation entries from file content.\n */\nfunction extractFromContent(\n content: string,\n file: string,\n options: ResolvedDocsOptions\n): DocEntry[] {\n const entries: DocEntry[] = [];\n\n let match: RegExpExecArray | null;\n JSDOC_BLOCK.lastIndex = 0;\n\n while ((match = JSDOC_BLOCK.exec(content)) !== null) {\n const jsdocContent = match[1];\n const jsdocEnd = match.index + match[0].length;\n\n const afterJsdoc = content.slice(jsdocEnd).trim();\n const lineNumber = content.slice(0, match.index).split('\\n').length;\n\n const entry = parseJsdocBlock(jsdocContent, afterJsdoc, file, lineNumber);\n\n if (entry && (options.private || !entry.private)) {\n entries.push(entry);\n }\n }\n\n return entries;\n}\n\n/**\n * Extracts the complete function signature for display.\n *\n * Captures the full function declaration from `export/async/function name(...): ReturnType`\n * or `export const name = (...): ReturnType => {}`, handling multi-line signatures.\n *\n * @param signature - Multi-line function declaration text\n * @returns Cleaned function signature or undefined if not found\n *\n * @internal\n */\nfunction extractFunctionSignature(signature: string): string | undefined {\n // Match function declarations: export/async function, export const, etc.\n // Capture everything from the start until the opening brace or arrow\n const match = signature.match(\n /(?:export\\s+)?(?:async\\s+)?(?:function\\s+\\w+|\\w+\\s*=\\s*(?:async\\s*)?\\()\\([^{]*?\\)(?:\\s*:\\s*[^{;]+)?/s\n );\n\n if (match) {\n let sig = match[0].trim();\n // Clean up excessive whitespace while preserving structure\n // Replace multiple spaces with single space, but keep newlines in readable format\n sig = sig\n .split('\\n')\n .map(line => line.trim())\n .filter(line => line)\n .join('\\n ');\n return sig;\n }\n\n return undefined;\n}\n\n/**\n * Extracts parameter and return types from a TypeScript function signature.\n *\n * Parses function signatures to extract:\n * - Parameter names and their type annotations\n * - Return type annotation\n *\n * Handles various function declaration styles:\n * - `function name(param: type): ReturnType`\n * - `const name = (param: type): ReturnType => {}`\n * - `export async function name(param: type): Promise<ReturnType>`\n *\n * @param signature - Multi-line function signature text\n * @param params - Array of parameter docs with names already extracted\n * @returns Object with extracted parameter types and return type\n *\n * @internal\n */\nfunction extractTypesFromSignature(\n signature: string,\n params: ParamDoc[]\n): { paramTypes: string[]; returnType?: string } {\n const paramTypes: string[] = [];\n\n // Extract the parameter list from the signature\n // Match everything between the first `(` and the closing `)` before `=>` or `{`\n const paramListMatch = signature.match(/\\(([^)]*)\\)(?:\\s*:\\s*([^{=>]+))?/s);\n\n if (paramListMatch && paramListMatch[1]) {\n const paramListStr = paramListMatch[1];\n\n // Split by comma, but be careful about nested generics\n const paramParts = splitParameters(paramListStr);\n\n for (const part of paramParts) {\n const trimmed = part.trim();\n if (!trimmed) continue;\n\n // Extract type from \"name: type\" or \"name: type = default\"\n // Handle nested generics properly\n const typeMatch = /:\\s*(.+?)(?:\\s*=|$)/.exec(trimmed);\n if (typeMatch) {\n let typeStr = typeMatch[1].trim();\n // Remove trailing equals and everything after it (default value)\n if (typeStr.includes('=')) {\n typeStr = typeStr.split('=')[0].trim();\n }\n paramTypes.push(typeStr);\n }\n }\n }\n\n // Extract return type\n let returnType: string | undefined;\n\n // Look for return type annotation `: Type` or `: Promise<Type>`\n // This comes after the closing parenthesis\n // Need to handle nested angle brackets in generics\n const returnTypeMatch = signature.match(/\\)\\s*:\\s*(.+?)(?={|$)/);\n if (returnTypeMatch) {\n returnType = returnTypeMatch[1].trim();\n }\n\n return {\n paramTypes,\n returnType,\n };\n}\n\n/**\n * Splits function parameters while respecting nested angle brackets (generics).\n *\n * Handles cases like:\n * - `a: string, b: number` → `[\"a: string\", \"b: number\"]`\n * - `a: Promise<string>, b: Record<string, any>` → `[\"a: Promise<string>\", \"b: Record<string, any>\"]`\n *\n * @param paramListStr - String containing all parameters\n * @returns Array of individual parameter strings\n *\n * @internal\n */\nfunction splitParameters(paramListStr: string): string[] {\n const parts: string[] = [];\n let current = '';\n let depth = 0; // Track nested angle brackets\n\n for (const char of paramListStr) {\n if (char === '<') {\n depth++;\n current += char;\n } else if (char === '>') {\n depth--;\n current += char;\n } else if (char === ',' && depth === 0) {\n parts.push(current);\n current = '';\n } else {\n current += char;\n }\n }\n\n if (current) {\n parts.push(current);\n }\n\n return parts;\n}\n\n/**\n * Parses a JSDoc block and the following declaration.\n * Only matches if the declaration is immediately after the JSDoc (with only whitespace/keywords between).\n */\nfunction parseJsdocBlock(\n jsdoc: string,\n declaration: string,\n file: string,\n line: number\n): DocEntry | null {\n const params: ParamDoc[] = [];\n const examples: string[] = [];\n const tags: Record<string, string> = {};\n let description = '';\n let returns: { type: string; description: string } | undefined;\n let isPrivate = false;\n\n // Split lines and remove JSDoc markers but preserve indentation for code examples\n const rawLines = jsdoc.split('\\n').map((l) => l.replace(/^\\s*\\*\\s?/, ''));\n const cleanedLines = rawLines.map((l) => l.trim()).filter((l) => l);\n\n let currentExample = '';\n let inExample = false;\n let rawLineIndex = 0;\n\n for (const lineText of cleanedLines) {\n // Find the corresponding raw line to get original indentation for examples\n while (rawLineIndex < rawLines.length && rawLines[rawLineIndex].trim() !== lineText) {\n rawLineIndex++;\n }\n const rawLine = rawLineIndex < rawLines.length ? rawLines[rawLineIndex] : lineText;\n rawLineIndex++;\n\n if (lineText.startsWith('@')) {\n if (inExample) {\n examples.push(currentExample.trim());\n currentExample = '';\n inExample = false;\n }\n\n const tagMatch = /@(\\w+)\\s*(?:\\{([^}]*)\\})?(.*)/.exec(lineText);\n if (tagMatch) {\n const [, tagName, tagType, tagRest] = tagMatch;\n\n switch (tagName) {\n case 'param':\n const paramMatch = /(\\w+)\\s*-?\\s*(.*)/.exec(tagRest.trim());\n if (paramMatch) {\n params.push({\n name: paramMatch[1],\n type: tagType || 'unknown',\n description: paramMatch[2],\n });\n }\n break;\n case 'returns':\n case 'return':\n returns = {\n type: tagType || 'unknown',\n description: tagRest.trim(),\n };\n break;\n case 'example':\n inExample = true;\n break;\n case 'private':\n isPrivate = true;\n break;\n default:\n tags[tagName] = tagRest.trim();\n }\n }\n } else if (inExample) {\n // Use raw line to preserve indentation in code examples\n currentExample += rawLine + '\\n';\n } else if (!description) {\n description = lineText;\n } else {\n description += '\\n' + lineText;\n }\n }\n\n if (inExample && currentExample) {\n examples.push(currentExample.trim());\n }\n\n // Only look at the first few lines after the JSDoc to find the declaration\n // This prevents module-level JSDoc from matching distant declarations\n const firstFewLines = declaration.split('\\n').slice(0, 5).join('\\n');\n\n let name = '';\n let kind: DocEntry['kind'] = 'function';\n\n // Use anchored patterns to match at the start (after optional whitespace/keywords)\n const ANCHORED_FUNCTION = /^(?:export\\s+)?(?:async\\s+)?function\\s+(\\w+)/;\n const ANCHORED_CONST_FUNC = /^(?:export\\s+)?const\\s+(\\w+)\\s*=\\s*(?:async\\s*)?\\(/;\n const ANCHORED_CLASS = /^(?:export\\s+)?class\\s+(\\w+)/;\n const ANCHORED_INTERFACE = /^(?:export\\s+)?interface\\s+(\\w+)/;\n const ANCHORED_TYPE = /^(?:export\\s+)?type\\s+(\\w+)/;\n\n let declMatch: RegExpExecArray | null;\n\n if ((declMatch = ANCHORED_FUNCTION.exec(firstFewLines))) {\n name = declMatch[1];\n kind = 'function';\n } else if ((declMatch = ANCHORED_CONST_FUNC.exec(firstFewLines))) {\n name = declMatch[1];\n kind = 'function';\n } else if ((declMatch = ANCHORED_CLASS.exec(firstFewLines))) {\n name = declMatch[1];\n kind = 'class';\n } else if ((declMatch = ANCHORED_INTERFACE.exec(firstFewLines))) {\n name = declMatch[1];\n kind = 'interface';\n } else if ((declMatch = ANCHORED_TYPE.exec(firstFewLines))) {\n name = declMatch[1];\n kind = 'type';\n }\n\n if (!name) return null;\n\n // Extract full signature and types from function signature if needed\n let signature: string | undefined;\n if (kind === 'function') {\n const signatureTypes = extractTypesFromSignature(firstFewLines, params);\n\n // Update params with extracted types if JSDoc types were missing\n if (signatureTypes.paramTypes.length > 0) {\n for (let i = 0; i < params.length && i < signatureTypes.paramTypes.length; i++) {\n if (params[i].type === 'unknown') {\n params[i].type = signatureTypes.paramTypes[i];\n }\n }\n }\n\n // Update return type if JSDoc return type was missing\n if (signatureTypes.returnType && (!returns || returns.type === 'unknown')) {\n if (returns) {\n returns.type = signatureTypes.returnType;\n } else {\n returns = {\n type: signatureTypes.returnType,\n description: '',\n };\n }\n }\n\n // Extract the complete function signature\n signature = extractFunctionSignature(firstFewLines);\n }\n\n return {\n name,\n kind,\n description,\n params: params.length > 0 ? params : undefined,\n returns,\n examples: examples.length > 0 ? examples : undefined,\n tags: Object.keys(tags).length > 0 ? tags : undefined,\n private: isPrivate,\n file,\n line,\n signature,\n };\n}\n\n/**\n * Generates Markdown documentation from extracted docs.\n */\nexport function generateMarkdown(\n docs: ExtractedDocs[],\n options: ResolvedDocsOptions\n): Record<string, string> {\n const result: Record<string, string> = {};\n const symbolMap = buildSymbolMap(docs);\n\n if (options.groupBy === 'file') {\n const docToFile = new Map<ExtractedDocs, string>();\n\n for (const doc of docs) {\n let fileName = path.basename(doc.file, path.extname(doc.file));\n // Avoid conflict with the main index.md\n if (fileName === 'index') {\n fileName = 'index-module';\n }\n docToFile.set(doc, fileName);\n\n const markdown = generateFileMarkdown(doc, options, fileName, symbolMap);\n result[`${fileName}.md`] = markdown;\n }\n\n result['index.md'] = generateIndex(docs, docToFile);\n } else {\n const byKind = new Map<string, DocEntry[]>();\n\n for (const doc of docs) {\n for (const entry of doc.entries) {\n const existing = byKind.get(entry.kind) || [];\n existing.push(entry);\n byKind.set(entry.kind, existing);\n }\n }\n\n for (const [kind, entries] of byKind) {\n result[`${kind}s.md`] = generateCategoryMarkdown(kind, entries, options, symbolMap);\n }\n\n result['index.md'] = generateCategoryIndex(byKind);\n }\n\n return result;\n}\n\nfunction generateFileMarkdown(\n doc: ExtractedDocs,\n options: ResolvedDocsOptions,\n currentFileName: string,\n symbolMap: Map<string, SymbolLocation>\n): string {\n const displayName = path.basename(doc.file);\n let md = `# ${displayName}\\n\\n`;\n\n // Add source link if githubUrl is provided\n if (options.githubUrl) {\n const sourceLink = generateSourceLink(doc.file, options.githubUrl);\n if (sourceLink) {\n md += sourceLink + '\\n\\n';\n }\n }\n\n // Pass symbol map for cross-file link resolution\n for (const entry of doc.entries) {\n md += generateEntryMarkdown(entry, options, currentFileName, symbolMap);\n }\n\n return md;\n}\n\nfunction generateEntryMarkdown(\n entry: DocEntry,\n options?: ResolvedDocsOptions,\n currentFileName?: string,\n symbolMap?: Map<string, SymbolLocation>\n): string {\n let md = `## ${entry.name}\\n\\n`;\n\n md += `\\`${entry.kind}\\`\\n\\n`;\n\n if (entry.description) {\n // Convert symbol links [SymbolName] to markdown links\n const processedDescription = currentFileName && symbolMap\n ? convertSymbolLinks(entry.description, currentFileName, symbolMap)\n : entry.description;\n md += `${processedDescription}\\n\\n`;\n }\n\n // Add source link if githubUrl is provided\n if (options?.githubUrl) {\n const sourceLink = generateSourceLink(entry.file, options.githubUrl, entry.line);\n if (sourceLink) {\n md += sourceLink + '\\n\\n';\n }\n }\n\n // Add function signature if available\n if (entry.signature && entry.kind === 'function') {\n md += '```typescript\\n';\n md += entry.signature + '\\n';\n md += '```\\n\\n';\n }\n\n if (entry.params && entry.params.length > 0) {\n md += '### Parameters\\n\\n';\n md += '| Name | Type | Description |\\n';\n md += '|------|------|-------------|\\n';\n for (const param of entry.params) {\n md += `| \\`${param.name}\\` | \\`${param.type}\\` | ${param.description} |\\n`;\n }\n md += '\\n';\n }\n\n if (entry.returns) {\n md += '### Returns\\n\\n';\n md += `\\`${entry.returns.type}\\` - ${entry.returns.description}\\n\\n`;\n }\n\n if (entry.examples && entry.examples.length > 0) {\n md += '### Examples\\n\\n';\n for (const example of entry.examples) {\n md += '```ts\\n';\n md += example.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n md += '\\n```\\n\\n';\n }\n }\n\n md += '---\\n\\n';\n\n return md;\n}\n\nfunction generateIndex(docs: ExtractedDocs[], docToFile?: Map<ExtractedDocs, string>): string {\n let md = '# API Documentation\\n\\n';\n md += 'Generated by [Ox Content](https://github.com/ubugeeei/ox-content)\\n\\n';\n\n md += '## Modules\\n\\n';\n\n for (const doc of docs) {\n const displayName = path.basename(doc.file, path.extname(doc.file));\n let fileName = displayName;\n\n if (docToFile && docToFile.has(doc)) {\n fileName = docToFile.get(doc)!;\n } else if (fileName === 'index') {\n fileName = 'index-module';\n }\n\n md += `### [${displayName}](./${fileName}.md)\\n\\n`;\n\n for (const entry of doc.entries) {\n const desc = entry.description?.slice(0, 80) || '';\n const ellipsis = entry.description && entry.description.length > 80 ? '...' : '';\n md += `- \\`${entry.kind}\\` **${entry.name}** - ${desc}${ellipsis}\\n`;\n }\n md += '\\n';\n }\n\n return md;\n}\n\nfunction generateCategoryMarkdown(\n kind: string,\n entries: DocEntry[],\n options: ResolvedDocsOptions,\n symbolMap: Map<string, SymbolLocation>\n): string {\n const categoryFileName = `${kind}s`;\n let md = `# ${kind.charAt(0).toUpperCase() + kind.slice(1)}s\\n\\n`;\n\n for (const entry of entries) {\n md += generateEntryMarkdown(entry, options, categoryFileName, symbolMap);\n }\n\n return md;\n}\n\nfunction generateCategoryIndex(byKind: Map<string, DocEntry[]>): string {\n let md = '# API Documentation\\n\\n';\n md += 'Generated by [Ox Content](https://github.com/ubugeeei/ox-content)\\n\\n';\n\n for (const [kind, entries] of byKind) {\n const kindTitle = kind.charAt(0).toUpperCase() + kind.slice(1) + 's';\n md += `## [${kindTitle}](./${kind}s.md)\\n\\n`;\n\n for (const entry of entries) {\n const desc = entry.description?.slice(0, 60) || '';\n md += `- **${entry.name}** - ${desc}...\\n`;\n }\n md += '\\n';\n }\n\n return md;\n}\n\n/**\n * Symbol location info for cross-file linking.\n */\ninterface SymbolLocation {\n name: string;\n file: string;\n fileName: string;\n}\n\n/**\n * Converts symbol links [SymbolName] to markdown links.\n *\n * Processes description text to convert cargo-docs-style symbol references\n * `[SymbolName]` into clickable markdown links pointing to the appropriate\n * documentation page.\n *\n * ## Examples\n *\n * Input: \"See [transformMarkdown] for usage\" (same file)\n * Output: \"See [transformMarkdown](#transformmarkdown) for usage\"\n *\n * Input: \"Uses [NavItem] interface\" (different file: types.ts)\n * Output: \"Uses [NavItem](./types.md#navitem) interface\"\n *\n * @param text - Description text containing symbol references\n * @param currentFileName - Current file name (without extension) for same-file detection\n * @param symbolMap - Map of symbol names to their file locations\n * @returns Text with symbol references converted to markdown links\n *\n * @internal\n */\nfunction convertSymbolLinks(\n text: string,\n currentFileName: string,\n symbolMap: Map<string, SymbolLocation>\n): string {\n // Match [SymbolName] pattern where SymbolName starts with uppercase or underscore\n // Negative lookahead (?!\\() ensures we don't match [Name] that's already part of [Name](url)\n return text.replace(/\\[([A-Z_]\\w*)\\](?!\\()/g, (match, symbolName) => {\n const location = symbolMap.get(symbolName);\n if (!location) {\n // Symbol not found, keep original text\n return match;\n }\n\n if (location.fileName === currentFileName) {\n // Same file - use anchor only\n return `[${symbolName}](#${symbolName.toLowerCase()})`;\n } else {\n // Different file - use cross-file link\n return `[${symbolName}](./${location.fileName}.md#${symbolName.toLowerCase()})`;\n }\n });\n}\n\n/**\n * Builds a map of all symbols to their file locations.\n */\nfunction buildSymbolMap(docs: ExtractedDocs[]): Map<string, SymbolLocation> {\n const map = new Map<string, SymbolLocation>();\n\n for (const doc of docs) {\n let fileName = path.basename(doc.file, path.extname(doc.file));\n if (fileName === 'index') {\n fileName = 'index-module';\n }\n\n for (const entry of doc.entries) {\n map.set(entry.name, {\n name: entry.name,\n file: doc.file,\n fileName,\n });\n }\n }\n\n return map;\n}\n\n/**\n * Writes generated documentation to the output directory.\n */\nexport async function writeDocs(\n docs: Record<string, string>,\n outDir: string,\n extractedDocs?: ExtractedDocs[],\n options?: ResolvedDocsOptions\n): Promise<void> {\n await fs.promises.mkdir(outDir, { recursive: true });\n\n for (const [fileName, content] of Object.entries(docs)) {\n const filePath = path.join(outDir, fileName);\n await fs.promises.writeFile(filePath, content, 'utf-8');\n }\n\n // Generate and write navigation metadata if enabled\n if (extractedDocs && options?.generateNav && options.groupBy === 'file') {\n const navItems = generateNavMetadata(extractedDocs, '/api');\n const navCode = generateNavCode(navItems, 'apiNav');\n const navFilePath = path.join(outDir, 'nav.ts');\n await fs.promises.writeFile(navFilePath, navCode, 'utf-8');\n }\n}\n\n/**\n * Resolves docs options with defaults.\n */\n/**\n * Generates a GitHub source link for a file and optional line number.\n *\n * @param filePath - Full path to the source file\n * @param githubUrl - Base GitHub repository URL\n * @param lineNumber - Optional line number to link to\n * @returns Markdown link to source code\n */\nfunction generateSourceLink(filePath: string, githubUrl: string, lineNumber?: number): string {\n // Convert absolute path to relative path from repository root\n // Match common project directory patterns: npm/, packages/, crates/, src/\n const relativePath = filePath.replace(/^.*?\\/(npm|packages|crates|src)\\//, '$1/');\n\n const fragment = lineNumber ? `#L${lineNumber}` : '';\n const link = `${githubUrl}/blob/main/${relativePath}${fragment}`;\n\n return `**[Source](${link})**`;\n}\n\nexport function resolveDocsOptions(\n options: import('./types').DocsOptions | false | undefined\n): ResolvedDocsOptions | false {\n if (options === false) {\n return false;\n }\n\n const opts = options || {};\n\n return {\n enabled: opts.enabled ?? true,\n src: opts.src ?? ['./src'],\n out: opts.out ?? 'docs/api',\n include: opts.include ?? ['**/*.ts', '**/*.tsx'],\n exclude: opts.exclude ?? ['**/*.test.*', '**/*.spec.*', 'node_modules'],\n format: opts.format ?? 'markdown',\n private: opts.private ?? false,\n toc: false,\n groupBy: opts.groupBy ?? 'file',\n githubUrl: opts.githubUrl,\n generateNav: opts.generateNav ?? true,\n };\n}\n","/**\n * Navigation Metadata Generator for API Documentation\n *\n * This module provides utilities for generating sidebar navigation structures\n * from extracted documentation. It automatically:\n *\n * - **Extracts file information**: Gets display names and file paths\n * - **Formats names**: Converts technical names to readable titles\n * - **Generates TypeScript**: Creates importable nav.ts files\n * - **Maintains hierarchy**: Supports nested navigation structures\n *\n * ## Generated Navigation Format\n *\n * The generated navigation is TypeScript-based for type safety and IDE support:\n *\n * ```typescript\n * export const apiNav: NavItem[] = [\n * { title: 'Overview', path: '/api/index' },\n * { title: 'Transform', path: '/api/transform' },\n * { title: 'Types', path: '/api/types' },\n * // ... auto-generated from documentation\n * ] as const;\n * ```\n *\n * ## Integration\n *\n * The generated nav.ts file can be imported directly:\n *\n * ```typescript\n * // In your Vue/React component\n * import { apiNav } from '../api/nav';\n *\n * const apiItems = apiNav.map(item => ({\n * ...item,\n * file: () => import(`../api/${item.path.split('/').pop()}.md`)\n * }));\n * ```\n *\n * @example\n * ```typescript\n * import { generateNavMetadata, generateNavCode } from './nav-generator';\n *\n * const extracted = [\n * { file: 'transform.ts', entries: [...] },\n * { file: 'types.ts', entries: [...] },\n * ];\n *\n * const navItems = generateNavMetadata(extracted);\n * // => [\n * // { title: 'Transform', path: '/api/transform' },\n * // { title: 'Types', path: '/api/types' },\n * // ]\n *\n * const code = generateNavCode(navItems);\n * // => TypeScript code ready to write to nav.ts\n * ```\n */\n\nimport path from 'path';\nimport type { ExtractedDocs, NavItem } from './types';\n\n/**\n * Generates sidebar navigation metadata from extracted documentation.\n *\n * Takes an array of extracted documentation and produces a flat navigation\n * structure suitable for sidebar menus. Items are:\n * - Sorted alphabetically by display name\n * - Formatted with readable titles\n * - Prefixed with the specified base path\n *\n * ## Naming Conventions\n *\n * - `transform.ts` → `{ title: 'Transform', path: '/api/transform' }`\n * - `nav-generator.ts` → `{ title: 'Nav Generator', path: '/api/nav-generator' }`\n * - `index.ts` or `index-module.ts` → `{ title: 'Overview', path: '/api/index' }`\n * - `types.ts` → `{ title: 'Types', path: '/api/types' }`\n *\n * ## Sorting\n *\n * Items are sorted alphabetically by display title for consistent ordering.\n * Special item 'Overview' sorts naturally with others (O comes after most letters).\n *\n * ## Path Generation\n *\n * The generated paths are used to import corresponding Markdown files:\n * - Path `/api/transform` → Import from `../api/transform.md`\n * - Path `/api/index` → Import from `../api/index.md`\n *\n * @param docs - Array of extracted documentation (file + entries)\n * @param basePath - Base path prefix for navigation URLs (default: '/api')\n * Use '/api' for main API docs, '/helpers' for utilities, etc.\n *\n * @returns Array of navigation items ready to use or export to TypeScript\n *\n * @example\n * ```typescript\n * const navItems = generateNavMetadata(\n * [\n * { file: 'transform.ts', entries: [...] },\n * { file: 'docs.ts', entries: [...] },\n * { file: 'types.ts', entries: [...] },\n * ],\n * '/api'\n * );\n *\n * // Returns:\n * // [\n * // { title: 'Docs', path: '/api/docs' },\n * // { title: 'Transform', path: '/api/transform' },\n * // { title: 'Types', path: '/api/types' },\n * // ]\n * ```\n *\n * @see generateNavCode For converting these items to TypeScript code\n */\nexport function generateNavMetadata(docs: ExtractedDocs[], basePath: string = '/api'): NavItem[] {\n // Sort docs by filename for consistent ordering\n const sortedDocs = [...docs].sort((a, b) => {\n const aName = getDocDisplayName(a.file);\n const bName = getDocDisplayName(b.file);\n return aName.localeCompare(bName);\n });\n\n return sortedDocs.map((doc) => ({\n title: getDocDisplayName(doc.file),\n path: `${basePath}/${getDocFileName(doc.file)}`,\n }));\n}\n\n/**\n * Gets the human-readable display name for a documentation file.\n *\n * Transforms file paths and names into proper title case:\n * - Extracts base name (e.g., 'transform.ts' → 'transform')\n * - Converts kebab-case to Title Case (e.g., 'nav-generator' → 'Nav Generator')\n * - Converts camelCase to Title Case (e.g., 'transformMarkdown' → 'Transform Markdown')\n * - Handles special cases (index → 'Overview')\n *\n * ## Examples\n *\n * - `'/path/to/transform.ts'` → `'Transform'`\n * - `'nav-generator.ts'` → `'Nav Generator'`\n * - `'index.ts'` → `'Overview'`\n * - `'index-module.ts'` → `'Overview'`\n * - `'myFunction.ts'` → `'My Function'` (with camelCase handling)\n *\n * @param filePath - Full or relative file path\n * @returns Formatted display name suitable for UI labels\n *\n * @internal\n */\nfunction getDocDisplayName(filePath: string): string {\n const fileName = path.basename(filePath, path.extname(filePath));\n\n // Handle special cases\n if (fileName === 'index' || fileName === 'index-module') {\n return 'Overview';\n }\n\n // Convert kebab-case and snake_case to Title Case\n // Also handles camelCase transitions\n return fileName\n .replace(/[-_]([a-z])/g, (_, char) => ' ' + char.toUpperCase())\n .replace(/^[a-z]/, (char) => char.toUpperCase());\n}\n\n/**\n * Gets the file name (without extension) for use in navigation paths.\n *\n * This handles filename conflicts that may occur during generation:\n * - Preserves most names as-is\n * - Special handling for index files to maintain consistency\n *\n * @param filePath - Source file path\n * @returns File name without extension, ready for URL paths\n *\n * @internal\n */\nfunction getDocFileName(filePath: string): string {\n const fileName = path.basename(filePath, path.extname(filePath));\n\n // Handle filename conflicts\n // The docs generator renames 'index' to 'index-module' to avoid conflicts\n if (fileName === 'index') {\n return 'index';\n }\n\n return fileName;\n}\n\n/**\n * Generates TypeScript code for navigation metadata export.\n *\n * Creates a complete, self-contained TypeScript file that:\n * - Defines the NavItem interface\n * - Exports navigation items as a const\n * - Uses `as const` for type-safe literal types\n * - Includes auto-generation notice\n *\n * The generated code is production-ready and suitable for direct import\n * in Vue, React, or vanilla TypeScript applications.\n *\n * ## Generated Code Example\n *\n * ```typescript\n * export interface NavItem {\n * title: string;\n * path: string;\n * children?: NavItem[];\n * }\n *\n * export const apiNav: NavItem[] = [\n * { \"title\": \"Docs\", \"path\": \"/api/docs\" },\n * { \"title\": \"Transform\", \"path\": \"/api/transform\" },\n * // ...\n * ] as const;\n * ```\n *\n * ## Features\n *\n * - **Type Safety**: Includes NavItem interface definition\n * - **Readonly**: Uses `as const` to ensure immutability\n * - **IDE Support**: Full IntelliSense and autocomplete\n * - **Self-Documenting**: Includes notice that file is auto-generated\n *\n * @param navItems - Array of navigation items to export\n * @param exportName - Name of the exported const (default: 'apiNav')\n * Use custom names for different navigation sections\n *\n * @returns Complete TypeScript source code as string,\n * ready to write to a .ts file\n *\n * @example\n * ```typescript\n * const navItems = [\n * { title: 'Home', path: '/api/index' },\n * { title: 'Transform', path: '/api/transform' },\n * ];\n *\n * const code = generateNavCode(navItems, 'apiNav');\n * await fs.promises.writeFile('docs/api/nav.ts', code, 'utf-8');\n * ```\n *\n * @see generateNavMetadata For generating NavItem arrays from extracted docs\n */\nexport function generateNavCode(navItems: NavItem[], exportName: string = 'apiNav'): string {\n const json = JSON.stringify(navItems, null, 2);\n return `/**\n * Auto-generated API documentation navigation.\n * This file is automatically generated by the docs plugin.\n * Do not edit manually.\n */\n\nexport interface NavItem {\n title: string;\n path: string;\n children?: NavItem[];\n}\n\nexport const ${exportName}: NavItem[] = ${json} as const;\n`;\n}\n","/**\n * SSG (Static Site Generation) module for ox-content\n */\n\nimport * as fs from 'fs/promises';\nimport * as path from 'path';\nimport { glob } from 'glob';\nimport { transformMarkdown, generateOgImageSvg } from './transform';\nimport type { OgImageData, OgImageConfig } from './transform';\nimport type {\n ResolvedOptions,\n ResolvedSsgOptions,\n SsgOptions,\n TocEntry,\n} from './types';\n\n/**\n * Navigation item for SSG.\n */\nexport interface SsgNavItem {\n title: string;\n path: string;\n href: string;\n children?: SsgNavItem[];\n}\n\n/**\n * Page data for SSG.\n */\nexport interface SsgPageData {\n title: string;\n description?: string;\n content: string;\n toc: TocEntry[];\n frontmatter: Record<string, unknown>;\n path: string;\n href: string;\n}\n\n/**\n * Default HTML template for SSG pages with navigation.\n */\nexport const DEFAULT_HTML_TEMPLATE = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>{{title}}{{#siteName}} - {{siteName}}{{/siteName}}</title>\n {{#description}}<meta name=\"description\" content=\"{{description}}\">{{/description}}\n <!-- Open Graph -->\n <meta property=\"og:type\" content=\"website\">\n <meta property=\"og:title\" content=\"{{title}}{{#siteName}} - {{siteName}}{{/siteName}}\">\n {{#description}}<meta property=\"og:description\" content=\"{{description}}\">{{/description}}\n {{#ogImage}}<meta property=\"og:image\" content=\"{{ogImage}}\">{{/ogImage}}\n <!-- Twitter Card -->\n <meta name=\"twitter:card\" content=\"summary_large_image\">\n <meta name=\"twitter:title\" content=\"{{title}}{{#siteName}} - {{siteName}}{{/siteName}}\">\n {{#description}}<meta name=\"twitter:description\" content=\"{{description}}\">{{/description}}\n {{#ogImage}}<meta name=\"twitter:image\" content=\"{{ogImage}}\">{{/ogImage}}\n <style>\n :root {\n --sidebar-width: 260px;\n --header-height: 60px;\n --max-content-width: 960px;\n --font-sans: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n --font-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;\n --color-bg: #ffffff;\n --color-bg-alt: #f8f9fa;\n --color-text: #1a1a1a;\n --color-text-muted: #666666;\n --color-border: #e5e7eb;\n --color-primary: #b7410e;\n --color-primary-hover: #ce5937;\n --color-code-bg: #1e293b;\n --color-code-text: #e2e8f0;\n }\n [data-theme=\"dark\"] {\n --color-bg: #0f172a;\n --color-bg-alt: #1e293b;\n --color-text: #e2e8f0;\n --color-text-muted: #94a3b8;\n --color-border: #334155;\n --color-primary: #e67e4d;\n --color-primary-hover: #f4a07a;\n --color-code-bg: #0f172a;\n --color-code-text: #e2e8f0;\n }\n @media (prefers-color-scheme: dark) {\n :root:not([data-theme=\"light\"]) {\n --color-bg: #0f172a;\n --color-bg-alt: #1e293b;\n --color-text: #e2e8f0;\n --color-text-muted: #94a3b8;\n --color-border: #334155;\n --color-primary: #e67e4d;\n --color-primary-hover: #f4a07a;\n --color-code-bg: #0f172a;\n --color-code-text: #e2e8f0;\n }\n }\n * { box-sizing: border-box; margin: 0; padding: 0; }\n html { scroll-behavior: smooth; }\n body {\n font-family: var(--font-sans);\n line-height: 1.7;\n color: var(--color-text);\n background: var(--color-bg);\n }\n a { color: var(--color-primary); text-decoration: none; }\n a:hover { color: var(--color-primary-hover); text-decoration: underline; }\n\n /* Header */\n .header {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n height: var(--header-height);\n background: var(--color-bg);\n border-bottom: 1px solid var(--color-border);\n display: flex;\n align-items: center;\n padding: 0 1.5rem;\n z-index: 100;\n }\n .header-title {\n font-size: 1.25rem;\n font-weight: 600;\n color: var(--color-text);\n }\n .header-title:hover { text-decoration: none; }\n .menu-toggle {\n display: none;\n background: none;\n border: none;\n cursor: pointer;\n padding: 0.5rem;\n margin-right: 0.75rem;\n }\n .menu-toggle svg { display: block; }\n .menu-toggle path { stroke: var(--color-text); }\n .header-actions { margin-left: auto; display: flex; align-items: center; gap: 0.5rem; }\n .search-button {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.5rem 0.75rem;\n background: var(--color-bg-alt);\n border: 1px solid var(--color-border);\n border-radius: 6px;\n color: var(--color-text-muted);\n cursor: pointer;\n font-size: 0.875rem;\n transition: border-color 0.15s, color 0.15s;\n }\n .search-button:hover { border-color: var(--color-primary); color: var(--color-text); }\n .search-button svg { width: 16px; height: 16px; }\n .search-button kbd {\n padding: 0.125rem 0.375rem;\n background: var(--color-bg);\n border: 1px solid var(--color-border);\n border-radius: 4px;\n font-family: var(--font-mono);\n font-size: 0.75rem;\n }\n @media (max-width: 640px) {\n .search-button span, .search-button kbd { display: none; }\n .search-button { padding: 0.5rem; }\n }\n .search-modal-overlay {\n display: none;\n position: fixed;\n inset: 0;\n z-index: 200;\n background: rgba(0,0,0,0.6);\n backdrop-filter: blur(4px);\n justify-content: center;\n padding-top: 10vh;\n }\n .search-modal-overlay.open { display: flex; }\n .search-modal {\n width: 100%;\n max-width: 560px;\n margin: 0 1rem;\n background: var(--color-bg);\n border: 1px solid var(--color-border);\n border-radius: 12px;\n overflow: hidden;\n box-shadow: 0 25px 50px -12px rgba(0,0,0,0.4);\n max-height: 70vh;\n display: flex;\n flex-direction: column;\n }\n .search-header {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 1rem;\n border-bottom: 1px solid var(--color-border);\n }\n .search-header svg { flex-shrink: 0; color: var(--color-text-muted); }\n .search-input {\n flex: 1;\n background: none;\n border: none;\n outline: none;\n font-size: 1rem;\n color: var(--color-text);\n }\n .search-input::placeholder { color: var(--color-text-muted); }\n .search-close {\n padding: 0.25rem 0.5rem;\n background: var(--color-bg-alt);\n border: 1px solid var(--color-border);\n border-radius: 4px;\n color: var(--color-text-muted);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n cursor: pointer;\n }\n .search-results {\n flex: 1;\n overflow-y: auto;\n padding: 0.5rem;\n }\n .search-result {\n display: block;\n padding: 0.75rem 1rem;\n border-radius: 8px;\n color: var(--color-text);\n text-decoration: none;\n }\n .search-result:hover, .search-result.selected { background: var(--color-bg-alt); text-decoration: none; }\n .search-result-title { font-weight: 600; font-size: 0.875rem; margin-bottom: 0.25rem; }\n .search-result-snippet { font-size: 0.8125rem; color: var(--color-text-muted); }\n .search-empty { padding: 2rem 1rem; text-align: center; color: var(--color-text-muted); }\n .search-footer {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 1rem;\n padding: 0.75rem 1rem;\n border-top: 1px solid var(--color-border);\n background: var(--color-bg-alt);\n font-size: 0.75rem;\n color: var(--color-text-muted);\n }\n .search-footer kbd {\n padding: 0.125rem 0.375rem;\n background: var(--color-bg);\n border: 1px solid var(--color-border);\n border-radius: 4px;\n font-family: var(--font-mono);\n }\n .theme-toggle {\n background: none;\n border: none;\n cursor: pointer;\n padding: 0.5rem;\n border-radius: 6px;\n color: var(--color-text-muted);\n transition: background 0.15s, color 0.15s;\n }\n .theme-toggle:hover { background: var(--color-bg-alt); color: var(--color-text); }\n .theme-toggle svg { display: block; width: 20px; height: 20px; }\n .theme-toggle .icon-sun { display: none; }\n .theme-toggle .icon-moon { display: block; }\n [data-theme=\"dark\"] .theme-toggle .icon-sun { display: block; }\n [data-theme=\"dark\"] .theme-toggle .icon-moon { display: none; }\n @media (prefers-color-scheme: dark) {\n :root:not([data-theme=\"light\"]) .theme-toggle .icon-sun { display: block; }\n :root:not([data-theme=\"light\"]) .theme-toggle .icon-moon { display: none; }\n }\n\n /* Layout */\n .layout {\n display: flex;\n padding-top: var(--header-height);\n min-height: 100vh;\n }\n\n /* Sidebar */\n .sidebar {\n position: fixed;\n top: var(--header-height);\n left: 0;\n bottom: 0;\n width: var(--sidebar-width);\n background: var(--color-bg-alt);\n border-right: 1px solid var(--color-border);\n overflow-y: auto;\n padding: 1.5rem 1rem;\n }\n .nav-section { margin-bottom: 1.5rem; }\n .nav-title {\n font-size: 0.75rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--color-text-muted);\n margin-bottom: 0.5rem;\n padding: 0 0.75rem;\n }\n .nav-list { list-style: none; }\n .nav-item { margin: 0.125rem 0; }\n .nav-link {\n display: block;\n padding: 0.5rem 0.75rem;\n border-radius: 6px;\n color: var(--color-text);\n font-size: 0.875rem;\n transition: background 0.15s;\n }\n .nav-link:hover {\n background: var(--color-border);\n text-decoration: none;\n }\n .nav-link.active {\n background: var(--color-primary);\n color: white;\n }\n\n /* Main content */\n .main {\n flex: 1;\n margin-left: var(--sidebar-width);\n padding: 2rem;\n min-width: 0;\n overflow-x: hidden;\n }\n .content {\n max-width: var(--max-content-width);\n margin: 0 auto;\n overflow-wrap: break-word;\n word-wrap: break-word;\n word-break: break-word;\n }\n\n /* TOC (right sidebar) */\n .toc {\n position: fixed;\n top: calc(var(--header-height) + 2rem);\n right: 2rem;\n width: 200px;\n font-size: 0.8125rem;\n }\n .toc-title {\n font-weight: 600;\n margin-bottom: 0.75rem;\n color: var(--color-text-muted);\n }\n .toc-list { list-style: none; }\n .toc-item { margin: 0.375rem 0; }\n .toc-link {\n color: var(--color-text-muted);\n display: block;\n padding-left: calc((var(--depth, 1) - 1) * 0.75rem);\n }\n .toc-link:hover { color: var(--color-primary); }\n @media (max-width: 1200px) { .toc { display: none; } }\n\n /* Typography */\n .content h1 { font-size: 2.25rem; margin-bottom: 1rem; line-height: 1.2; }\n .content h2 { font-size: 1.5rem; margin-top: 2.5rem; margin-bottom: 1rem; padding-bottom: 0.5rem; border-bottom: 1px solid var(--color-border); }\n .content h3 { font-size: 1.25rem; margin-top: 2rem; margin-bottom: 0.75rem; }\n .content h4 { font-size: 1rem; margin-top: 1.5rem; margin-bottom: 0.5rem; }\n .content p { margin-bottom: 1rem; }\n .content ul, .content ol { margin: 1rem 0; padding-left: 1.5rem; }\n .content li { margin: 0.375rem 0; }\n .content blockquote {\n border-left: 4px solid var(--color-primary);\n padding: 0.5rem 1rem;\n margin: 1rem 0;\n background: var(--color-bg-alt);\n border-radius: 0 6px 6px 0;\n }\n .content code {\n font-family: var(--font-mono);\n font-size: 0.875em;\n background: var(--color-bg-alt);\n padding: 0.2em 0.4em;\n border-radius: 4px;\n word-break: break-all;\n }\n .content pre {\n background: var(--color-code-bg);\n color: var(--color-code-text);\n padding: 1rem 1.25rem;\n border-radius: 8px;\n overflow-x: auto;\n margin: 1.5rem 0;\n line-height: 1.5;\n }\n .content pre code {\n background: transparent;\n padding: 0;\n font-size: 0.8125rem;\n }\n .content table {\n width: 100%;\n border-collapse: collapse;\n margin: 1.5rem 0;\n font-size: 0.875rem;\n }\n .content th, .content td {\n border: 1px solid var(--color-border);\n padding: 0.75rem 1rem;\n text-align: left;\n }\n .content th { background: var(--color-bg-alt); font-weight: 600; }\n .content img { max-width: 100%; height: auto; border-radius: 8px; display: block; }\n .content img[alt*=\"Logo\"] { max-width: 200px; display: block; margin: 1rem 0; }\n .content img[alt*=\"Architecture\"] { max-width: 600px; }\n .content img[alt*=\"Benchmark\"] { max-width: 680px; }\n .content hr { border: none; border-top: 1px solid var(--color-border); margin: 2rem 0; }\n\n /* Responsive */\n @media (max-width: 768px) {\n .menu-toggle { display: block; }\n .sidebar {\n transform: translateX(-100%);\n transition: transform 0.3s ease;\n z-index: 99;\n width: 280px;\n }\n .sidebar.open { transform: translateX(0); }\n .main { margin-left: 0; padding: 1rem 0.75rem; }\n .content { padding: 0 0.25rem; }\n .content h1 { font-size: 1.5rem; line-height: 1.3; margin-bottom: 0.75rem; }\n .content h2 { font-size: 1.2rem; margin-top: 2rem; }\n .content h3 { font-size: 1.1rem; }\n .content p { font-size: 0.9375rem; margin-bottom: 0.875rem; }\n .content ul, .content ol { padding-left: 1.25rem; font-size: 0.9375rem; }\n .content pre {\n padding: 0.75rem;\n font-size: 0.75rem;\n margin: 1rem -0.75rem;\n border-radius: 0;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n }\n .content code { font-size: 0.8125em; }\n .content table {\n display: block;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n font-size: 0.8125rem;\n margin: 1rem -0.75rem;\n width: calc(100% + 1.5rem);\n }\n .content th, .content td { padding: 0.5rem 0.75rem; white-space: nowrap; }\n .content img { margin: 1rem 0; }\n .content img[alt*=\"Logo\"] { max-width: 150px; }\n .content img[alt*=\"Architecture\"] { max-width: 100%; }\n .content img[alt*=\"Benchmark\"] { max-width: 100%; }\n .content blockquote { padding: 0.5rem 0.75rem; margin: 1rem 0; font-size: 0.9375rem; }\n .header { padding: 0 1rem; }\n .header-title { font-size: 1rem; }\n .header-title img { width: 24px; height: 24px; }\n .overlay {\n display: none;\n position: fixed;\n inset: 0;\n background: rgba(0,0,0,0.5);\n z-index: 98;\n }\n .overlay.open { display: block; }\n }\n\n /* Extra small devices */\n @media (max-width: 480px) {\n .main { padding: 0.75rem 0.5rem; }\n .content h1 { font-size: 1.35rem; }\n .content pre { font-size: 0.6875rem; padding: 0.625rem; }\n .content table { font-size: 0.75rem; }\n .content th, .content td { padding: 0.375rem 0.5rem; }\n }\n </style>\n</head>\n<body>\n <header class=\"header\">\n <button class=\"menu-toggle\" aria-label=\"Toggle menu\">\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-width=\"2\" stroke-linecap=\"round\">\n <path d=\"M3 12h18M3 6h18M3 18h18\"/>\n </svg>\n </button>\n <a href=\"{{base}}index.html\" class=\"header-title\">\n <img src=\"{{base}}logo.svg\" alt=\"\" width=\"28\" height=\"28\" style=\"margin-right: 8px; vertical-align: middle;\" />\n {{siteName}}\n </a>\n <div class=\"header-actions\">\n <button class=\"search-button\" aria-label=\"Search\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\">\n <circle cx=\"11\" cy=\"11\" r=\"8\"/><path d=\"m21 21-4.3-4.3\"/>\n </svg>\n <span>Search</span>\n <kbd>/</kbd>\n </button>\n <button class=\"theme-toggle\" aria-label=\"Toggle theme\">\n <svg class=\"icon-sun\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"5\"/><path d=\"M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42\"/>\n </svg>\n <svg class=\"icon-moon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\">\n <path d=\"M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z\"/>\n </svg>\n </button>\n </div>\n </header>\n <div class=\"search-modal-overlay\">\n <div class=\"search-modal\">\n <div class=\"search-header\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\">\n <circle cx=\"11\" cy=\"11\" r=\"8\"/><path d=\"m21 21-4.3-4.3\"/>\n </svg>\n <input type=\"text\" class=\"search-input\" placeholder=\"Search documentation...\" />\n <button class=\"search-close\">Esc</button>\n </div>\n <div class=\"search-results\"></div>\n <div class=\"search-footer\">\n <span><kbd>↑</kbd><kbd>↓</kbd> to navigate</span>\n <span><kbd>Enter</kbd> to select</span>\n <span><kbd>Esc</kbd> to close</span>\n </div>\n </div>\n </div>\n <div class=\"overlay\"></div>\n <div class=\"layout\">\n <aside class=\"sidebar\">\n <nav>\n{{navigation}}\n </nav>\n </aside>\n <main class=\"main\">\n <article class=\"content\">\n{{content}}\n </article>\n </main>\n{{#hasToc}}\n <aside class=\"toc\">\n <div class=\"toc-title\">On this page</div>\n <ul class=\"toc-list\">\n{{toc}}\n </ul>\n </aside>\n{{/hasToc}}\n </div>\n <script>\n // Menu toggle\n const toggle = document.querySelector('.menu-toggle');\n const sidebar = document.querySelector('.sidebar');\n const overlay = document.querySelector('.overlay');\n if (toggle && sidebar && overlay) {\n const close = () => { sidebar.classList.remove('open'); overlay.classList.remove('open'); };\n toggle.addEventListener('click', () => {\n sidebar.classList.toggle('open');\n overlay.classList.toggle('open');\n });\n overlay.addEventListener('click', close);\n sidebar.querySelectorAll('a').forEach(a => a.addEventListener('click', close));\n }\n\n // Theme toggle\n const themeToggle = document.querySelector('.theme-toggle');\n const getPreferredTheme = () => {\n const stored = localStorage.getItem('theme');\n if (stored) return stored;\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n };\n const setTheme = (theme) => {\n document.documentElement.setAttribute('data-theme', theme);\n localStorage.setItem('theme', theme);\n };\n // Initialize theme\n setTheme(getPreferredTheme());\n if (themeToggle) {\n themeToggle.addEventListener('click', () => {\n const current = document.documentElement.getAttribute('data-theme') || getPreferredTheme();\n setTheme(current === 'dark' ? 'light' : 'dark');\n });\n }\n\n // Search functionality\n const searchButton = document.querySelector('.search-button');\n const searchOverlay = document.querySelector('.search-modal-overlay');\n const searchInput = document.querySelector('.search-input');\n const searchResults = document.querySelector('.search-results');\n const searchClose = document.querySelector('.search-close');\n let searchIndex = null;\n let selectedIndex = 0;\n let results = [];\n\n const openSearch = () => {\n searchOverlay.classList.add('open');\n searchInput.focus();\n };\n const closeSearch = () => {\n searchOverlay.classList.remove('open');\n searchInput.value = '';\n searchResults.innerHTML = '';\n selectedIndex = 0;\n results = [];\n };\n\n // Load search index\n const loadSearchIndex = async () => {\n if (searchIndex) return;\n try {\n const res = await fetch('{{base}}search-index.json');\n searchIndex = await res.json();\n } catch (e) {\n console.warn('Failed to load search index:', e);\n }\n };\n\n // Tokenize query\n const tokenize = (text) => {\n const tokens = [];\n let current = '';\n for (const char of text) {\n const isCjk = /[\\\\u4E00-\\\\u9FFF\\\\u3400-\\\\u4DBF\\\\u3040-\\\\u309F\\\\u30A0-\\\\u30FF\\\\uAC00-\\\\uD7AF]/.test(char);\n if (isCjk) {\n if (current) { tokens.push(current.toLowerCase()); current = ''; }\n tokens.push(char);\n } else if (/[a-zA-Z0-9_]/.test(char)) {\n current += char;\n } else if (current) {\n tokens.push(current.toLowerCase());\n current = '';\n }\n }\n if (current) tokens.push(current.toLowerCase());\n return tokens;\n };\n\n // Perform search\n const performSearch = async (query) => {\n if (!query.trim()) {\n searchResults.innerHTML = '';\n results = [];\n return;\n }\n await loadSearchIndex();\n if (!searchIndex) {\n searchResults.innerHTML = '<div class=\"search-empty\">Search index not available</div>';\n return;\n }\n\n const tokens = tokenize(query);\n if (!tokens.length) {\n searchResults.innerHTML = '';\n results = [];\n return;\n }\n\n const k1 = 1.2, b = 0.75;\n const docScores = new Map();\n\n for (let i = 0; i < tokens.length; i++) {\n const token = tokens[i];\n const isLast = i === tokens.length - 1;\n let matchingTerms = [];\n if (isLast && token.length >= 2) {\n matchingTerms = Object.keys(searchIndex.index).filter(t => t.startsWith(token));\n } else if (searchIndex.index[token]) {\n matchingTerms = [token];\n }\n\n for (const term of matchingTerms) {\n const postings = searchIndex.index[term] || [];\n const df = searchIndex.df[term] || 1;\n const idf = Math.log((searchIndex.doc_count - df + 0.5) / (df + 0.5) + 1.0);\n\n for (const posting of postings) {\n const doc = searchIndex.documents[posting.doc_idx];\n if (!doc) continue;\n const boost = posting.field === 'Title' ? 10 : posting.field === 'Heading' ? 5 : 1;\n const tf = posting.tf;\n const docLen = doc.body.length;\n const score = idf * ((tf * (k1 + 1)) / (tf + k1 * (1 - b + b * docLen / searchIndex.avg_dl))) * boost;\n\n if (!docScores.has(posting.doc_idx)) {\n docScores.set(posting.doc_idx, { score: 0, matches: new Set() });\n }\n const entry = docScores.get(posting.doc_idx);\n entry.score += score;\n entry.matches.add(term);\n }\n }\n }\n\n results = Array.from(docScores.entries())\n .map(([docIdx, data]) => {\n const doc = searchIndex.documents[docIdx];\n let snippet = '';\n if (doc.body) {\n const bodyLower = doc.body.toLowerCase();\n let firstPos = -1;\n for (const match of data.matches) {\n const pos = bodyLower.indexOf(match);\n if (pos !== -1 && (firstPos === -1 || pos < firstPos)) firstPos = pos;\n }\n const start = Math.max(0, firstPos - 50);\n const end = Math.min(doc.body.length, start + 150);\n snippet = doc.body.slice(start, end);\n if (start > 0) snippet = '...' + snippet;\n if (end < doc.body.length) snippet += '...';\n }\n return { ...doc, score: data.score, snippet };\n })\n .sort((a, b) => b.score - a.score)\n .slice(0, 10);\n\n selectedIndex = 0;\n renderResults();\n };\n\n const renderResults = () => {\n if (!results.length) {\n searchResults.innerHTML = '<div class=\"search-empty\">No results found</div>';\n return;\n }\n searchResults.innerHTML = results.map((r, i) =>\n '<a href=\"' + r.url + '\" class=\"search-result' + (i === selectedIndex ? ' selected' : '') + '\">' +\n '<div class=\"search-result-title\">' + r.title + '</div>' +\n (r.snippet ? '<div class=\"search-result-snippet\">' + r.snippet + '</div>' : '') +\n '</a>'\n ).join('');\n };\n\n // Event listeners\n if (searchButton) searchButton.addEventListener('click', openSearch);\n if (searchClose) searchClose.addEventListener('click', closeSearch);\n if (searchOverlay) searchOverlay.addEventListener('click', (e) => { if (e.target === searchOverlay) closeSearch(); });\n\n let searchTimeout = null;\n if (searchInput) {\n searchInput.addEventListener('input', () => {\n if (searchTimeout) clearTimeout(searchTimeout);\n searchTimeout = setTimeout(() => performSearch(searchInput.value), 150);\n });\n searchInput.addEventListener('keydown', (e) => {\n if (e.key === 'Escape') closeSearch();\n else if (e.key === 'ArrowDown') {\n e.preventDefault();\n if (selectedIndex < results.length - 1) { selectedIndex++; renderResults(); }\n } else if (e.key === 'ArrowUp') {\n e.preventDefault();\n if (selectedIndex > 0) { selectedIndex--; renderResults(); }\n } else if (e.key === 'Enter' && results[selectedIndex]) {\n e.preventDefault();\n window.location.href = results[selectedIndex].url;\n }\n });\n }\n\n // Global keyboard shortcut (/ or Cmd+K)\n document.addEventListener('keydown', (e) => {\n if ((e.key === '/' && !(e.target instanceof HTMLInputElement)) ||\n ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k')) {\n e.preventDefault();\n openSearch();\n }\n });\n </script>\n</body>\n</html>`;\n\n/**\n * Bare HTML template (no navigation, no styles).\n */\nexport const BARE_HTML_TEMPLATE = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>{{title}}</title>\n</head>\n<body>\n{{content}}\n</body>\n</html>`;\n\n/**\n * Resolves SSG options with defaults.\n */\nexport function resolveSsgOptions(ssg: SsgOptions | boolean | undefined): ResolvedSsgOptions {\n if (ssg === false) {\n return {\n enabled: false,\n extension: '.html',\n clean: false,\n bare: false,\n generateOgImage: false,\n };\n }\n\n if (ssg === true || ssg === undefined) {\n return {\n enabled: true,\n extension: '.html',\n clean: false,\n bare: false,\n generateOgImage: false,\n };\n }\n\n return {\n enabled: ssg.enabled ?? true,\n extension: ssg.extension ?? '.html',\n clean: ssg.clean ?? false,\n bare: ssg.bare ?? false,\n siteName: ssg.siteName,\n ogImage: ssg.ogImage,\n generateOgImage: ssg.generateOgImage ?? false,\n siteUrl: ssg.siteUrl,\n };\n}\n\n/**\n * Simple mustache-like template rendering.\n */\nfunction renderTemplate(template: string, data: Record<string, unknown>): string {\n let result = template;\n\n // Handle conditionals: {{#key}}content{{/key}}\n result = result.replace(/\\{\\{#(\\w+)\\}\\}([\\s\\S]*?)\\{\\{\\/\\1\\}\\}/g, (_, key, content) => {\n return data[key] ? content : '';\n });\n\n // Handle simple replacements: {{key}}\n result = result.replace(/\\{\\{(\\w+)\\}\\}/g, (_, key) => {\n const value = data[key];\n return value !== undefined && value !== null ? String(value) : '';\n });\n\n return result;\n}\n\n/**\n * Extracts title from content or frontmatter.\n */\nfunction extractTitle(\n content: string,\n frontmatter: Record<string, unknown>\n): string {\n if (frontmatter.title && typeof frontmatter.title === 'string') {\n return frontmatter.title;\n }\n\n const h1Match = content.match(/<h1[^>]*>([^<]+)<\\/h1>/i);\n if (h1Match) {\n return h1Match[1].trim();\n }\n\n return 'Untitled';\n}\n\n/**\n * Generates navigation HTML from nav groups.\n */\nfunction generateNavHtml(navGroups: NavGroup[], currentPath: string): string {\n return navGroups\n .map((group) => {\n const items = group.items\n .map((item) => {\n const isActive = item.path === currentPath;\n const activeClass = isActive ? ' active' : '';\n return ` <li class=\"nav-item\"><a href=\"${item.href}\" class=\"nav-link${activeClass}\">${item.title}</a></li>`;\n })\n .join('\\n');\n\n return ` <div class=\"nav-section\">\n <div class=\"nav-title\">${group.title}</div>\n <ul class=\"nav-list\">\n${items}\n </ul>\n </div>`;\n })\n .join('\\n');\n}\n\n/**\n * Generates TOC HTML from toc entries.\n */\nfunction generateTocHtml(toc: TocEntry[]): string {\n const flattenToc = (entries: TocEntry[], depth = 1): string[] => {\n const items: string[] = [];\n for (const entry of entries) {\n items.push(\n ` <li class=\"toc-item\"><a href=\"#${entry.slug}\" class=\"toc-link\" style=\"--depth: ${depth}\">${entry.text}</a></li>`\n );\n if (entry.children && entry.children.length > 0) {\n items.push(...flattenToc(entry.children, depth + 1));\n }\n }\n return items;\n };\n return flattenToc(toc).join('\\n');\n}\n\n/**\n * Generates bare HTML page (no navigation, no styles).\n */\nexport function generateBareHtmlPage(\n content: string,\n title: string\n): string {\n return renderTemplate(BARE_HTML_TEMPLATE, {\n title,\n content,\n });\n}\n\n/**\n * Generates HTML page with navigation using Rust NAPI bindings.\n */\nexport async function generateHtmlPage(\n pageData: SsgPageData,\n navGroups: NavGroup[],\n siteName: string,\n base: string,\n ogImage?: string\n): Promise<string> {\n const mod = await import('@ox-content/napi');\n\n // Convert TocEntry to the format expected by Rust\n const tocForRust = pageData.toc.map((entry) => ({\n depth: entry.depth,\n text: entry.text,\n slug: entry.slug,\n }));\n\n // Convert NavGroup to the format expected by Rust\n const navGroupsForRust = navGroups.map((group) => ({\n title: group.title,\n items: group.items.map((item) => ({\n title: item.title,\n path: item.path,\n href: item.href,\n })),\n }));\n\n return mod.generateSsgHtml(\n {\n title: pageData.title,\n description: pageData.description,\n content: pageData.content,\n toc: tocForRust,\n path: pageData.path,\n },\n navGroupsForRust,\n {\n siteName,\n base,\n ogImage,\n }\n );\n}\n\n/**\n * Converts a markdown file path to its corresponding HTML output path.\n */\nexport function getOutputPath(\n inputPath: string,\n srcDir: string,\n outDir: string,\n extension: string\n): string {\n const relativePath = path.relative(srcDir, inputPath);\n const baseName = relativePath.replace(/\\.(?:md|markdown)$/i, extension);\n\n if (baseName.endsWith(`index${extension}`)) {\n return path.join(outDir, baseName);\n }\n\n const dirName = baseName.replace(new RegExp(`\\\\${extension}$`), '');\n return path.join(outDir, dirName, `index${extension}`);\n}\n\n/**\n * Converts a markdown file path to a relative URL path.\n */\nfunction getUrlPath(inputPath: string, srcDir: string): string {\n const relativePath = path.relative(srcDir, inputPath);\n const baseName = relativePath.replace(/\\.(?:md|markdown)$/i, '');\n\n if (baseName === 'index' || baseName.endsWith('/index')) {\n return baseName.replace(/\\/?index$/, '') || '/';\n }\n\n return baseName;\n}\n\n/**\n * Converts a markdown file path to an href.\n */\nfunction getHref(inputPath: string, srcDir: string, base: string, extension: string): string {\n const urlPath = getUrlPath(inputPath, srcDir);\n if (urlPath === '/' || urlPath === '') {\n return `${base}index${extension}`;\n }\n return `${base}${urlPath}/index${extension}`;\n}\n\n/**\n * Gets the OG image output path for a given markdown file.\n */\nfunction getOgImagePath(inputPath: string, srcDir: string, outDir: string): string {\n const relativePath = path.relative(srcDir, inputPath);\n const baseName = relativePath.replace(/\\.(?:md|markdown)$/i, '');\n\n if (baseName === 'index' || baseName.endsWith('/index')) {\n const dirPath = baseName.replace(/\\/?index$/, '') || '';\n return path.join(outDir, dirPath, 'og-image.svg');\n }\n\n return path.join(outDir, baseName, 'og-image.svg');\n}\n\n/**\n * Gets the OG image URL for use in meta tags.\n * If siteUrl is provided, returns an absolute URL (required for SNS sharing).\n */\nfunction getOgImageUrl(inputPath: string, srcDir: string, base: string, siteUrl?: string): string {\n const urlPath = getUrlPath(inputPath, srcDir);\n let relativePath: string;\n if (urlPath === '/' || urlPath === '') {\n relativePath = `${base}og-image.svg`;\n } else {\n relativePath = `${base}${urlPath}/og-image.svg`;\n }\n\n // Return absolute URL if siteUrl is provided\n if (siteUrl) {\n const cleanSiteUrl = siteUrl.replace(/\\/$/, '');\n return `${cleanSiteUrl}${relativePath}`;\n }\n\n return relativePath;\n}\n\n/**\n * Gets display title from file path.\n */\nfunction getDisplayTitle(filePath: string): string {\n const fileName = path.basename(filePath, path.extname(filePath));\n\n if (fileName === 'index') {\n const dirName = path.basename(path.dirname(filePath));\n if (dirName && dirName !== '.') {\n return formatTitle(dirName);\n }\n return 'Home';\n }\n\n return formatTitle(fileName);\n}\n\n/**\n * Formats a file/dir name as a title.\n */\nfunction formatTitle(name: string): string {\n return name\n .replace(/[-_]([a-z])/g, (_, char) => ' ' + char.toUpperCase())\n .replace(/^[a-z]/, (char) => char.toUpperCase());\n}\n\n/**\n * Collects all markdown files from the source directory.\n */\nexport async function collectMarkdownFiles(srcDir: string): Promise<string[]> {\n const pattern = path.join(srcDir, '**/*.{md,markdown}');\n const files = await glob(pattern, {\n nodir: true,\n ignore: ['**/node_modules/**', '**/dist/**', '**/.git/**'],\n });\n return files.sort();\n}\n\n/**\n * Navigation group for hierarchical navigation.\n */\ninterface NavGroup {\n title: string;\n items: SsgNavItem[];\n}\n\n/**\n * Builds navigation items from markdown files, grouped by directory.\n */\nfunction buildNavItems(\n markdownFiles: string[],\n srcDir: string,\n base: string,\n extension: string\n): NavGroup[] {\n const groups = new Map<string, SsgNavItem[]>();\n\n // Define the order of groups (api at the bottom)\n const groupOrder = ['', 'examples', 'packages', 'api'];\n\n for (const file of markdownFiles) {\n const relativePath = path.relative(srcDir, file);\n const parts = relativePath.split(path.sep);\n\n // Determine group: first directory or '' for root files\n let groupKey = '';\n if (parts.length > 1) {\n groupKey = parts[0];\n }\n\n if (!groups.has(groupKey)) {\n groups.set(groupKey, []);\n }\n\n const urlPath = getUrlPath(file, srcDir);\n\n // Use \"Overview\" for root index.md, otherwise use getDisplayTitle\n let title: string;\n if (urlPath === '/' || urlPath === '') {\n title = 'Overview';\n } else {\n title = getDisplayTitle(file);\n }\n\n groups.get(groupKey)!.push({\n title,\n path: urlPath,\n href: getHref(file, srcDir, base, extension),\n });\n }\n\n // Sort items within each group: index files first, then alphabetically\n const sortItems = (items: SsgNavItem[]) => {\n return items.sort((a, b) => {\n // Root index (Overview) comes first\n const aIsRoot = a.path === '/' || a.path === '';\n const bIsRoot = b.path === '/' || b.path === '';\n if (aIsRoot && !bIsRoot) return -1;\n if (!aIsRoot && bIsRoot) return 1;\n // Otherwise, maintain alphabetical order by title\n return a.title.localeCompare(b.title);\n });\n };\n\n // Convert to array and sort by group order\n const result: NavGroup[] = [];\n\n for (const key of groupOrder) {\n const items = groups.get(key);\n if (items && items.length > 0) {\n result.push({\n title: key === '' ? 'Guide' : formatTitle(key),\n items: sortItems(items),\n });\n groups.delete(key);\n }\n }\n\n // Add any remaining groups\n for (const [key, items] of groups) {\n if (items.length > 0) {\n result.push({\n title: formatTitle(key),\n items: sortItems(items),\n });\n }\n }\n\n return result;\n}\n\n/**\n * Builds all markdown files to static HTML.\n */\nexport async function buildSsg(\n options: ResolvedOptions,\n root: string\n): Promise<{ files: string[]; errors: string[] }> {\n const ssgOptions = options.ssg;\n if (!ssgOptions.enabled) {\n return { files: [], errors: [] };\n }\n\n const srcDir = path.resolve(root, options.srcDir);\n const outDir = path.resolve(root, options.outDir);\n const base = options.base.endsWith('/') ? options.base : options.base + '/';\n const generatedFiles: string[] = [];\n const errors: string[] = [];\n\n // Clean output directory if requested\n if (ssgOptions.clean) {\n try {\n await fs.rm(outDir, { recursive: true, force: true });\n } catch {\n // Ignore if directory doesn't exist\n }\n }\n\n // Collect markdown files\n const markdownFiles = await collectMarkdownFiles(srcDir);\n\n // Build navigation\n const navItems = buildNavItems(markdownFiles, srcDir, base, ssgOptions.extension);\n\n // Get site name from options or package.json\n let siteName = ssgOptions.siteName ?? 'Documentation';\n if (!ssgOptions.siteName) {\n try {\n const pkgPath = path.join(root, 'package.json');\n const pkg = JSON.parse(await fs.readFile(pkgPath, 'utf-8'));\n if (pkg.name) {\n siteName = formatTitle(pkg.name);\n }\n } catch {\n // Use default\n }\n }\n\n // Process each file\n for (const inputPath of markdownFiles) {\n try {\n const content = await fs.readFile(inputPath, 'utf-8');\n // Pass SSG options to transform for .md -> .html link conversion in Rust\n const result = await transformMarkdown(content, inputPath, options, {\n convertMdLinks: true,\n baseUrl: base,\n });\n\n const title = extractTitle(result.html, result.frontmatter);\n const description = result.frontmatter.description as string | undefined;\n\n // Determine OG image URL for this page\n let pageOgImage = ssgOptions.ogImage; // fallback to static URL\n\n // Generate per-page OG image if enabled (uses Rust)\n if (ssgOptions.generateOgImage && !ssgOptions.bare) {\n const ogImageData: OgImageData = {\n title,\n description,\n siteName,\n author: result.frontmatter.author as string | undefined,\n };\n\n // Use OG image options from the main options if available\n const ogImageConfig: OgImageConfig | undefined = options.ogImageOptions\n ? {\n width: options.ogImageOptions.width,\n height: options.ogImageOptions.height,\n backgroundColor: options.ogImageOptions.background,\n textColor: options.ogImageOptions.textColor,\n }\n : undefined;\n\n const svg = await generateOgImageSvg(ogImageData, ogImageConfig);\n if (svg) {\n const ogImageOutputPath = getOgImagePath(inputPath, srcDir, outDir);\n await fs.mkdir(path.dirname(ogImageOutputPath), { recursive: true });\n await fs.writeFile(ogImageOutputPath, svg, 'utf-8');\n generatedFiles.push(ogImageOutputPath);\n\n // Use per-page OG image URL (absolute if siteUrl is provided)\n pageOgImage = getOgImageUrl(inputPath, srcDir, base, ssgOptions.siteUrl);\n }\n }\n\n // Generate HTML based on bare option\n let html: string;\n if (ssgOptions.bare) {\n html = generateBareHtmlPage(result.html, title);\n } else {\n const pageData: SsgPageData = {\n title,\n description,\n content: result.html,\n toc: result.toc,\n frontmatter: result.frontmatter,\n path: getUrlPath(inputPath, srcDir),\n href: getHref(inputPath, srcDir, base, ssgOptions.extension),\n };\n html = await generateHtmlPage(pageData, navItems, siteName, base, pageOgImage);\n }\n\n // Write output file\n const outputPath = getOutputPath(\n inputPath,\n srcDir,\n outDir,\n ssgOptions.extension\n );\n\n await fs.mkdir(path.dirname(outputPath), { recursive: true });\n await fs.writeFile(outputPath, html, 'utf-8');\n\n generatedFiles.push(outputPath);\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n errors.push(`Failed to process ${inputPath}: ${errorMessage}`);\n }\n }\n\n return { files: generatedFiles, errors };\n}\n","/**\n * Full-text search functionality for Ox Content.\n *\n * Generates search index at build time and provides client-side search.\n */\n\nimport * as fs from 'fs/promises';\nimport * as path from 'path';\nimport type { SearchOptions, ResolvedSearchOptions, SearchDocument } from './types';\n\n// Import Rust bindings\nlet oxContent: typeof import('@ox-content/napi') | null = null;\n\nasync function getOxContent() {\n if (!oxContent) {\n try {\n oxContent = await import('@ox-content/napi');\n } catch {\n console.warn('[ox-content] Native bindings not available, search disabled');\n return null;\n }\n }\n return oxContent;\n}\n\n/**\n * Resolves search options with defaults.\n */\nexport function resolveSearchOptions(\n options: SearchOptions | boolean | undefined\n): ResolvedSearchOptions {\n if (options === false) {\n return {\n enabled: false,\n limit: 10,\n prefix: true,\n placeholder: 'Search documentation...',\n hotkey: '/',\n };\n }\n\n const opts = typeof options === 'object' ? options : {};\n\n return {\n enabled: opts.enabled ?? true,\n limit: opts.limit ?? 10,\n prefix: opts.prefix ?? true,\n placeholder: opts.placeholder ?? 'Search documentation...',\n hotkey: opts.hotkey ?? '/',\n };\n}\n\n/**\n * Collects all Markdown files from a directory.\n */\nasync function collectMarkdownFiles(dir: string): Promise<string[]> {\n const files: string[] = [];\n\n async function walk(currentDir: string) {\n try {\n const entries = await fs.readdir(currentDir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(currentDir, entry.name);\n\n if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules') {\n await walk(fullPath);\n } else if (entry.isFile() && entry.name.endsWith('.md')) {\n files.push(fullPath);\n }\n }\n } catch {\n // Ignore errors\n }\n }\n\n await walk(dir);\n return files;\n}\n\n/**\n * Builds the search index from Markdown files.\n */\nexport async function buildSearchIndex(\n srcDir: string,\n base: string\n): Promise<string> {\n const napi = await getOxContent();\n\n if (!napi) {\n return JSON.stringify({ documents: [], index: {}, df: {}, avg_dl: 0, doc_count: 0 });\n }\n\n const files = await collectMarkdownFiles(srcDir);\n const documents: SearchDocument[] = [];\n\n for (const file of files) {\n try {\n const content = await fs.readFile(file, 'utf-8');\n const relativePath = path.relative(srcDir, file);\n const url = base + relativePath.replace(/\\.md$/, '').replace(/\\\\/g, '/');\n const id = relativePath.replace(/\\.md$/, '').replace(/\\\\/g, '/');\n\n // Use Rust bindings to extract search content (if available)\n const extractSearchContent = (napi as any).extractSearchContent;\n if (!extractSearchContent) {\n console.warn('[ox-content] Search not available: extractSearchContent not implemented');\n return '[]';\n }\n const doc = extractSearchContent(content, id, url, { gfm: true });\n\n documents.push({\n id: doc.id,\n title: doc.title,\n url: doc.url,\n body: doc.body,\n headings: doc.headings,\n code: doc.code,\n });\n } catch {\n // Skip files that can't be processed\n }\n }\n\n // Build the index using Rust bindings (if available)\n const buildSearchIndex = (napi as any).buildSearchIndex;\n if (!buildSearchIndex) {\n console.warn('[ox-content] Search not available: buildSearchIndex not implemented');\n return JSON.stringify(documents);\n }\n return buildSearchIndex(documents);\n}\n\n/**\n * Writes the search index to a file.\n */\nexport async function writeSearchIndex(\n indexJson: string,\n outDir: string\n): Promise<void> {\n const indexPath = path.join(outDir, 'search-index.json');\n\n // Ensure output directory exists\n await fs.mkdir(outDir, { recursive: true });\n\n // Write the index\n await fs.writeFile(indexPath, indexJson, 'utf-8');\n}\n\n/**\n * Client-side search module code.\n * This is injected into the bundle as a virtual module.\n */\nexport function generateSearchModule(\n options: ResolvedSearchOptions,\n indexPath: string\n): string {\n return `\n// Search module generated by ox-content\nconst searchOptions = ${JSON.stringify(options)};\n\nlet searchIndex = null;\nlet indexPromise = null;\n\n// Tokenizer for queries\nfunction tokenizeQuery(text) {\n const tokens = [];\n let current = '';\n\n for (const char of text) {\n const isCjk = /[\\\\u4E00-\\\\u9FFF\\\\u3400-\\\\u4DBF\\\\u3040-\\\\u309F\\\\u30A0-\\\\u30FF\\\\uAC00-\\\\uD7AF]/.test(char);\n\n if (isCjk) {\n if (current) {\n tokens.push(current.toLowerCase());\n current = '';\n }\n tokens.push(char);\n } else if (/[a-zA-Z0-9_]/.test(char)) {\n current += char;\n } else if (current) {\n tokens.push(current.toLowerCase());\n current = '';\n }\n }\n\n if (current) {\n tokens.push(current.toLowerCase());\n }\n\n return tokens;\n}\n\n// BM25 scoring\nfunction computeIdf(df, docCount) {\n return Math.log((docCount - df + 0.5) / (df + 0.5) + 1.0);\n}\n\nfunction getFieldBoost(field) {\n switch (field) {\n case 'Title': return 10.0;\n case 'Heading': return 5.0;\n case 'Body': return 1.0;\n case 'Code': return 0.5;\n default: return 1.0;\n }\n}\n\n// Load the index\nasync function loadIndex() {\n if (searchIndex) return searchIndex;\n if (indexPromise) return indexPromise;\n\n indexPromise = fetch('${indexPath}')\n .then(res => res.json())\n .then(data => {\n searchIndex = data;\n return data;\n })\n .catch(err => {\n console.error('[ox-content] Failed to load search index:', err);\n return null;\n });\n\n return indexPromise;\n}\n\n// Search function\nexport async function search(query, options = {}) {\n const index = await loadIndex();\n\n if (!index || !query.trim()) {\n return [];\n }\n\n const limit = options.limit ?? searchOptions.limit;\n const prefix = options.prefix ?? searchOptions.prefix;\n const tokens = tokenizeQuery(query);\n\n if (tokens.length === 0) {\n return [];\n }\n\n const k1 = 1.2;\n const b = 0.75;\n const docScores = new Map();\n\n for (let i = 0; i < tokens.length; i++) {\n const token = tokens[i];\n const isLast = i === tokens.length - 1;\n\n // Find matching terms\n let matchingTerms = [];\n if (prefix && isLast && token.length >= 2) {\n matchingTerms = Object.keys(index.index).filter(term => term.startsWith(token));\n } else if (index.index[token]) {\n matchingTerms = [token];\n }\n\n for (const term of matchingTerms) {\n const postings = index.index[term] || [];\n const df = index.df[term] || 1;\n const idf = computeIdf(df, index.doc_count);\n\n for (const posting of postings) {\n const doc = index.documents[posting.doc_idx];\n if (!doc) continue;\n\n const docLen = doc.body.length;\n const tf = posting.tf;\n const boost = getFieldBoost(posting.field);\n\n const score = idf * ((tf * (k1 + 1.0)) / (tf + k1 * (1.0 - b + b * docLen / index.avg_dl))) * boost;\n\n if (!docScores.has(posting.doc_idx)) {\n docScores.set(posting.doc_idx, { score: 0, matches: new Set() });\n }\n const entry = docScores.get(posting.doc_idx);\n entry.score += score;\n entry.matches.add(term);\n }\n }\n }\n\n // Convert to results\n const results = Array.from(docScores.entries())\n .map(([docIdx, data]) => {\n const doc = index.documents[docIdx];\n const matches = Array.from(data.matches);\n\n // Generate snippet\n let snippet = '';\n if (doc.body) {\n const bodyLower = doc.body.toLowerCase();\n let firstPos = -1;\n for (const match of matches) {\n const pos = bodyLower.indexOf(match);\n if (pos !== -1 && (firstPos === -1 || pos < firstPos)) {\n firstPos = pos;\n }\n }\n\n const start = Math.max(0, firstPos - 50);\n const end = Math.min(doc.body.length, start + 150);\n snippet = doc.body.slice(start, end);\n if (start > 0) snippet = '...' + snippet;\n if (end < doc.body.length) snippet = snippet + '...';\n }\n\n return {\n id: doc.id,\n title: doc.title,\n url: doc.url,\n score: data.score,\n matches,\n snippet,\n };\n })\n .sort((a, b) => b.score - a.score)\n .slice(0, limit);\n\n return results;\n}\n\nexport { searchOptions };\nexport default { search, searchOptions, loadIndex };\n`;\n}\n"],"mappings":";AAOA,YAAYA,WAAU;;;ACwBf,SAAS,0BACd,SACoB;AACpB,SAAO;AAAA;AAAA,IAEL,UAAU;AAAA;AAAA,IAGV,OAAO;AAAA;AAAA,MAEL,QAAQ,GAAG,QAAQ,MAAM;AAAA;AAAA,MAGzB,YAAY;AAAA;AAAA,MAGZ,UAAU;AAAA;AAAA,MAGV,eAAe;AAAA,QACb,UAAU;AAAA;AAAA,UAER;AAAA;AAAA,UAEA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA;AAAA,IAGA,SAAS;AAAA;AAAA,MAEP,YAAY,CAAC,OAAO,WAAW;AAAA;AAAA,MAG/B,YAAY,CAAC,YAAY,QAAQ,QAAQ;AAAA;AAAA,MAGzC,QAAQ,CAAC;AAAA,IACX;AAAA;AAAA,IAGA,cAAc;AAAA;AAAA,MAEZ,SAAS,CAAC;AAAA;AAAA,MAEV,SAAS,CAAC,kBAAkB;AAAA,IAC9B;AAAA,EACF;AACF;;;AC5EA,SAAS,eAAe;AACxB,OAAO,iBAAiB;AACxB,OAAO,qBAAqB;AAE5B,SAAS,yBAA8D;AAGvE,IAAI,qBAAkD;AAKtD,eAAe,eAAe,OAAqC;AACjE,MAAI,CAAC,oBAAoB;AACvB,yBAAqB,kBAAkB;AAAA,MACrC,QAAQ,CAAC,KAAqB;AAAA,MAC9B,OAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAKA,SAAS,qBAAqB,SAA4B;AACxD,QAAM,EAAE,MAAM,IAAI;AAElB,SAAO,OAAO,SAAe;AAC3B,UAAM,cAAc,MAAM,eAAe,KAAK;AAG9C,UAAM,QAAQ,OAAO,SAAyB;AAC5C,UAAI,cAAc,MAAM;AACtB,iBAAS,IAAI,GAAG,IAAI,KAAK,SAAS,QAAQ,KAAK;AAC7C,gBAAM,QAAQ,KAAK,SAAS,CAAC;AAE7B,cAAI,MAAM,SAAS,aAAa,MAAM,YAAY,OAAO;AACvD,kBAAM,cAAc,MAAM,SAAS;AAAA,cACjC,CAAC,MAAoB,EAAE,SAAS,aAAa,EAAE,YAAY;AAAA,YAC7D;AAEA,gBAAI,aAAa;AAEf,oBAAM,YAAY,YAAY,YAAY;AAC1C,kBAAI,OAAO;AAEX,kBAAI,MAAM,QAAQ,SAAS,GAAG;AAC5B,sBAAM,YAAY,UAAU;AAAA,kBAC1B,CAAC,MAAuB,OAAO,MAAM,YAAY,EAAE,WAAW,WAAW;AAAA,gBAC3E;AACA,oBAAI,aAAa,OAAO,cAAc,UAAU;AAC9C,yBAAO,UAAU,QAAQ,aAAa,EAAE;AAAA,gBAC1C;AAAA,cACF;AAGA,oBAAM,WAAW,eAAe,WAAW;AAG3C,kBAAI;AACF,sBAAM,cAAc,YAAY,WAAW,UAAU;AAAA,kBACnD;AAAA,kBACA;AAAA,gBACF,CAAC;AAGD,sBAAM,SAAS,QAAQ,EACpB,IAAI,aAAa,EAAE,UAAU,KAAK,CAAC,EACnC,MAAM,WAAW;AAGpB,oBAAI,OAAO,SAAS,CAAC,GAAG;AACtB,uBAAK,SAAS,CAAC,IAAI,OAAO,SAAS,CAAC;AAAA,gBACtC;AAAA,cACF,QAAQ;AAAA,cAER;AAAA,YACF;AAAA,UACF,WAAW,MAAM,SAAS,WAAW;AACnC,kBAAM,MAAM,KAAK;AAAA,UACnB;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,UAAM,MAAM,IAAI;AAAA,EAClB;AACF;AAKA,SAAS,eAAe,MAA8B;AACpD,MAAI,OAAO;AAEX,MAAI,cAAc,MAAM;AACtB,eAAW,SAAS,KAAK,UAAU;AACjC,UAAI,MAAM,SAAS,QAAQ;AACzB,gBAAQ,MAAM;AAAA,MAChB,WAAW,MAAM,SAAS,WAAW;AACnC,gBAAQ,eAAe,KAAK;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAKA,eAAsB,cACpB,MACA,QAAgB,eACC;AACjB,QAAM,SAAS,MAAM,QAAQ,EAC1B,IAAI,aAAa,EAAE,UAAU,KAAK,CAAC,EACnC,IAAI,sBAAsB,EAAE,MAAM,CAAC,EACnC,IAAI,eAAe,EACnB,QAAQ,IAAI;AAEf,SAAO,OAAO,MAAM;AACtB;;;ACjJA,SAAS,WAAAC,gBAAe;AACxB,OAAOC,kBAAiB;AACxB,OAAOC,sBAAqB;AAM5B,SAASC,gBAAe,MAA8B;AACpD,MAAI,OAAO;AAEX,MAAI,cAAc,MAAM;AACtB,eAAW,SAAS,KAAK,UAAU;AACjC,UAAI,MAAM,SAAS,QAAQ;AACzB,gBAAQ,MAAM;AAAA,MAChB,WAAW,MAAM,SAAS,WAAW;AACnC,gBAAQA,gBAAe,KAAK;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAQA,SAAS,gBAAgB;AACvB,SAAO,CAAC,SAAe;AACrB,UAAM,QAAQ,CAAC,SAAyB;AACtC,UAAI,cAAc,MAAM;AACtB,iBAAS,IAAI,GAAG,IAAI,KAAK,SAAS,QAAQ,KAAK;AAC7C,gBAAM,QAAQ,KAAK,SAAS,CAAC;AAE7B,cAAI,MAAM,SAAS,aAAa,MAAM,YAAY,OAAO;AACvD,kBAAM,cAAc,MAAM,SAAS;AAAA,cACjC,CAAC,MAAoB,EAAE,SAAS,aAAa,EAAE,YAAY;AAAA,YAC7D;AAEA,gBAAI,aAAa;AAEf,oBAAM,YAAY,YAAY,YAAY;AAC1C,kBAAI,YAAY;AAEhB,kBAAI,MAAM,QAAQ,SAAS,GAAG;AAC5B,4BAAY,UAAU;AAAA,kBACpB,CAAC,MAAuB,OAAO,MAAM,YAAY,EAAE,SAAS,SAAS;AAAA,gBACvE;AAAA,cACF;AAEA,kBAAI,WAAW;AACb,sBAAM,cAAcA,gBAAe,WAAW;AAG9C,sBAAM,UAAmB;AAAA,kBACvB,MAAM;AAAA,kBACN,SAAS;AAAA,kBACT,YAAY;AAAA,oBACV,WAAW,CAAC,YAAY;AAAA,oBACxB,gBAAgB;AAAA,kBAClB;AAAA,kBACA,UAAU;AAAA,oBACR;AAAA,sBACE,MAAM;AAAA,sBACN,SAAS;AAAA,sBACT,YAAY;AAAA,wBACV,WAAW,CAAC,mBAAmB;AAAA,sBACjC;AAAA,sBACA,UAAU;AAAA,wBACR;AAAA,0BACE,MAAM;AAAA,0BACN,OAAO;AAAA,wBACT;AAAA,sBACF;AAAA,oBACF;AAAA,kBACF;AAAA,gBACF;AAEA,qBAAK,SAAS,CAAC,IAAI;AAAA,cACrB;AAAA,YACF;AAAA,UACF,WAAW,MAAM,SAAS,WAAW;AACnC,kBAAM,KAAK;AAAA,UACb;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,UAAM,IAAI;AAAA,EACZ;AACF;AAQA,eAAsB,iBAAiB,MAA+B;AACpE,QAAM,SAAS,MAAMH,SAAQ,EAC1B,IAAIC,cAAa,EAAE,UAAU,KAAK,CAAC,EACnC,IAAI,aAAa,EACjB,IAAIC,gBAAe,EACnB,QAAQ,IAAI;AAEf,SAAO,OAAO,MAAM;AACtB;;;ACoEA,IAAI;AAOJ,IAAI,oBAAoB;AAuCxB,eAAe,mBAAiD;AAE9D,MAAI,mBAAmB;AACrB,WAAO,gBAAgB;AAAA,EACzB;AAGA,sBAAoB;AAEpB,MAAI;AAEF,UAAM,MAAM,MAAM,OAAO,kBAAkB;AAC3C,mBAAe;AACf,WAAO;AAAA,EACT,SAAS,OAAO;AAGd,QAAI,QAAQ,IAAI,OAAO;AACrB,cAAQ,MAAM,2CAA2C,KAAK;AAAA,IAChE;AACA,mBAAe;AACf,WAAO;AAAA,EACT;AACF;AA6FA,eAAsB,kBACpB,QACA,UACA,SACA,YAC0B;AAC1B,QAAM,OAAO,MAAM,iBAAiB;AAEpC,MAAI,CAAC,MAAM;AACT,UAAM,IAAI,MAAM,oFAAoF;AAAA,EACtG;AAGA,QAAM,SAAS,KAAK,UAAU,QAAQ;AAAA,IACpC,KAAK,QAAQ;AAAA,IACb,WAAW,QAAQ;AAAA,IACnB,WAAW,QAAQ;AAAA,IACnB,QAAQ,QAAQ;AAAA,IAChB,eAAe,QAAQ;AAAA,IACvB,aAAa,QAAQ;AAAA,IACrB,gBAAgB,YAAY;AAAA,IAC5B,SAAS,YAAY;AAAA,EACvB,CAAC;AAED,MAAI,OAAO,OAAO,SAAS,GAAG;AAC5B,YAAQ,KAAK,oCAAoC,OAAO,MAAM;AAAA,EAChE;AAEA,MAAI,OAAO,OAAO;AAClB,MAAI;AAEJ,MAAI;AACF,kBAAc,KAAK,MAAM,OAAO,WAAW;AAAA,EAC7C,QAAQ;AACN,kBAAc,CAAC;AAAA,EACjB;AAGA,QAAM,UAAsB,OAAO,IAAI,IAAI,WAAS;AAAA,IAClD,GAAG;AAAA,IACH,UAAU,CAAC;AAAA,EACb,EAAE;AACF,QAAM,MAAM,QAAQ,MAAM,aAAa,OAAO,IAAI,CAAC;AAGnD,MAAI,QAAQ,WAAW;AACrB,WAAO,MAAM,cAAc,MAAM,QAAQ,cAAc;AAAA,EACzD;AAGA,MAAI,QAAQ,SAAS;AACnB,WAAO,MAAM,iBAAiB,IAAI;AAAA,EACpC;AAGA,QAAM,OAAO,mBAAmB,MAAM,aAAa,KAAK,UAAU,OAAO;AAEzE,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAKA,SAAS,aAAa,SAAiC;AACrD,QAAM,OAAmB,CAAC;AAC1B,QAAM,QAAoB,CAAC;AAE3B,aAAW,SAAS,SAAS;AAE3B,WAAO,MAAM,SAAS,KAAK,MAAM,MAAM,SAAS,CAAC,EAAE,SAAS,MAAM,OAAO;AACvE,YAAM,IAAI;AAAA,IACZ;AAEA,QAAI,MAAM,WAAW,GAAG;AACtB,WAAK,KAAK,KAAK;AAAA,IACjB,OAAO;AACL,YAAM,MAAM,SAAS,CAAC,EAAE,SAAS,KAAK,KAAK;AAAA,IAC7C;AAEA,UAAM,KAAK,KAAK;AAAA,EAClB;AAEA,SAAO;AACT;AAKA,SAAS,mBACP,MACA,aACA,KACA,UACA,UACQ;AACR,QAAM,WAAW,KAAK,UAAU,IAAI;AACpC,QAAM,kBAAkB,KAAK,UAAU,WAAW;AAClD,QAAM,UAAU,KAAK,UAAU,GAAG;AAElC,SAAO;AAAA;AAAA,aAEI,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA,sBAKC,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA,6BAKD,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA,qBAKvB,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqB5B;AAiCA,eAAsB,mBACpB,MACA,QACwB;AACxB,QAAM,OAAO,MAAM,iBAAiB;AACpC,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,EACT;AAGA,QAAM,aAAa,SACf;AAAA,IACE,OAAO,OAAO;AAAA,IACd,QAAQ,OAAO;AAAA,IACf,iBAAiB,OAAO;AAAA,IACxB,WAAW,OAAO;AAAA,IAClB,eAAe,OAAO;AAAA,IACtB,qBAAqB,OAAO;AAAA,EAC9B,IACA;AAEJ,SAAO,KAAK,mBAAmB,MAAM,UAAU;AACjD;;;AC5eA,YAAY,QAAQ;AACpB,YAAYE,WAAU;;;ACMtB,OAAO,UAAU;AAyDV,SAAS,oBAAoB,MAAuB,WAAmB,QAAmB;AAE/F,QAAM,aAAa,CAAC,GAAG,IAAI,EAAE,KAAK,CAAC,GAAG,MAAM;AAC1C,UAAM,QAAQ,kBAAkB,EAAE,IAAI;AACtC,UAAM,QAAQ,kBAAkB,EAAE,IAAI;AACtC,WAAO,MAAM,cAAc,KAAK;AAAA,EAClC,CAAC;AAED,SAAO,WAAW,IAAI,CAAC,SAAS;AAAA,IAC9B,OAAO,kBAAkB,IAAI,IAAI;AAAA,IACjC,MAAM,GAAG,QAAQ,IAAI,eAAe,IAAI,IAAI,CAAC;AAAA,EAC/C,EAAE;AACJ;AAwBA,SAAS,kBAAkB,UAA0B;AACnD,QAAM,WAAW,KAAK,SAAS,UAAU,KAAK,QAAQ,QAAQ,CAAC;AAG/D,MAAI,aAAa,WAAW,aAAa,gBAAgB;AACvD,WAAO;AAAA,EACT;AAIA,SAAO,SACJ,QAAQ,gBAAgB,CAAC,GAAG,SAAS,MAAM,KAAK,YAAY,CAAC,EAC7D,QAAQ,UAAU,CAAC,SAAS,KAAK,YAAY,CAAC;AACnD;AAcA,SAAS,eAAe,UAA0B;AAChD,QAAM,WAAW,KAAK,SAAS,UAAU,KAAK,QAAQ,QAAQ,CAAC;AAI/D,MAAI,aAAa,SAAS;AACxB,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAyDO,SAAS,gBAAgB,UAAqB,aAAqB,UAAkB;AAC1F,QAAM,OAAO,KAAK,UAAU,UAAU,MAAM,CAAC;AAC7C,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,eAYM,UAAU,iBAAiB,IAAI;AAAA;AAE9C;;;ADpMA,IAAM,cAAc;AAsGpB,eAAsB,YACpB,SACA,SAC0B;AAC1B,QAAM,UAA2B,CAAC;AAElC,aAAW,UAAU,SAAS;AAC5B,UAAM,QAAQ,MAAM,UAAU,QAAQ,OAAO;AAE7C,eAAW,QAAQ,OAAO;AACxB,YAAM,UAAU,MAAS,YAAS,SAAS,MAAM,OAAO;AACxD,YAAM,UAAU,mBAAmB,SAAS,MAAM,OAAO;AAEzD,UAAI,QAAQ,SAAS,GAAG;AACtB,gBAAQ,KAAK,EAAE,MAAM,QAAQ,CAAC;AAAA,MAChC;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAOA,eAAe,UAAU,KAAa,SAAiD;AACrF,QAAM,QAAkB,CAAC;AAEzB,iBAAe,KAAK,YAAoB;AACtC,QAAI;AACJ,QAAI;AACF,gBAAU,MAAS,YAAS,QAAQ,YAAY,EAAE,eAAe,KAAK,CAAC;AAAA,IACzE,QAAQ;AACN;AAAA,IACF;AAEA,eAAW,SAAS,SAAS;AAC3B,YAAM,WAAgB,WAAK,YAAY,MAAM,IAAI;AAEjD,UAAI,MAAM,YAAY,GAAG;AACvB,YAAI,CAAC,WAAW,UAAU,QAAQ,OAAO,GAAG;AAC1C,gBAAM,KAAK,QAAQ;AAAA,QACrB;AAAA,MACF,WAAW,MAAM,OAAO,GAAG;AACzB,YAAI,WAAW,UAAU,QAAQ,OAAO,KAAK,CAAC,WAAW,UAAU,QAAQ,OAAO,GAAG;AACnF,gBAAM,KAAK,QAAQ;AAAA,QACrB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,KAAK,GAAG;AACd,SAAO;AACT;AAEA,SAAS,WAAW,MAAc,UAA6B;AAC7D,SAAO,SAAS,KAAK,CAAC,YAAY;AAChC,QAAI,QAAQ,SAAS,IAAI,GAAG;AAC1B,YAAM,MAAM,QAAQ,MAAM,GAAG,EAAE,IAAI;AACnC,aAAO,KAAK,SAAS,IAAI,GAAG,EAAE;AAAA,IAChC;AACA,WAAO,KAAK,SAAS,QAAQ,QAAQ,KAAK,EAAE,CAAC;AAAA,EAC/C,CAAC;AACH;AAEA,SAAS,WAAW,MAAc,UAA6B;AAC7D,SAAO,SAAS,KAAK,CAAC,YAAY;AAChC,QAAI,QAAQ,SAAS,cAAc,GAAG;AACpC,aAAO,KAAK,SAAS,cAAc;AAAA,IACrC;AACA,QAAI,QAAQ,SAAS,QAAQ,KAAK,QAAQ,SAAS,QAAQ,GAAG;AAC5D,aAAO,KAAK,SAAS,QAAQ,KAAK,KAAK,SAAS,QAAQ;AAAA,IAC1D;AACA,WAAO;AAAA,EACT,CAAC;AACH;AAKA,SAAS,mBACP,SACA,MACA,SACY;AACZ,QAAM,UAAsB,CAAC;AAE7B,MAAI;AACJ,cAAY,YAAY;AAExB,UAAQ,QAAQ,YAAY,KAAK,OAAO,OAAO,MAAM;AACnD,UAAM,eAAe,MAAM,CAAC;AAC5B,UAAM,WAAW,MAAM,QAAQ,MAAM,CAAC,EAAE;AAExC,UAAM,aAAa,QAAQ,MAAM,QAAQ,EAAE,KAAK;AAChD,UAAM,aAAa,QAAQ,MAAM,GAAG,MAAM,KAAK,EAAE,MAAM,IAAI,EAAE;AAE7D,UAAM,QAAQ,gBAAgB,cAAc,YAAY,MAAM,UAAU;AAExE,QAAI,UAAU,QAAQ,WAAW,CAAC,MAAM,UAAU;AAChD,cAAQ,KAAK,KAAK;AAAA,IACpB;AAAA,EACF;AAEA,SAAO;AACT;AAaA,SAAS,yBAAyB,WAAuC;AAGvE,QAAM,QAAQ,UAAU;AAAA,IACtB;AAAA,EACF;AAEA,MAAI,OAAO;AACT,QAAI,MAAM,MAAM,CAAC,EAAE,KAAK;AAGxB,UAAM,IACH,MAAM,IAAI,EACV,IAAI,UAAQ,KAAK,KAAK,CAAC,EACvB,OAAO,UAAQ,IAAI,EACnB,KAAK,MAAM;AACd,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAoBA,SAAS,0BACP,WACA,QAC+C;AAC/C,QAAM,aAAuB,CAAC;AAI9B,QAAM,iBAAiB,UAAU,MAAM,mCAAmC;AAE1E,MAAI,kBAAkB,eAAe,CAAC,GAAG;AACvC,UAAM,eAAe,eAAe,CAAC;AAGrC,UAAM,aAAa,gBAAgB,YAAY;AAE/C,eAAW,QAAQ,YAAY;AAC7B,YAAM,UAAU,KAAK,KAAK;AAC1B,UAAI,CAAC,QAAS;AAId,YAAM,YAAY,sBAAsB,KAAK,OAAO;AACpD,UAAI,WAAW;AACb,YAAI,UAAU,UAAU,CAAC,EAAE,KAAK;AAEhC,YAAI,QAAQ,SAAS,GAAG,GAAG;AACzB,oBAAU,QAAQ,MAAM,GAAG,EAAE,CAAC,EAAE,KAAK;AAAA,QACvC;AACA,mBAAW,KAAK,OAAO;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAGA,MAAI;AAKJ,QAAM,kBAAkB,UAAU,MAAM,uBAAuB;AAC/D,MAAI,iBAAiB;AACnB,iBAAa,gBAAgB,CAAC,EAAE,KAAK;AAAA,EACvC;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,EACF;AACF;AAcA,SAAS,gBAAgB,cAAgC;AACvD,QAAM,QAAkB,CAAC;AACzB,MAAI,UAAU;AACd,MAAI,QAAQ;AAEZ,aAAW,QAAQ,cAAc;AAC/B,QAAI,SAAS,KAAK;AAChB;AACA,iBAAW;AAAA,IACb,WAAW,SAAS,KAAK;AACvB;AACA,iBAAW;AAAA,IACb,WAAW,SAAS,OAAO,UAAU,GAAG;AACtC,YAAM,KAAK,OAAO;AAClB,gBAAU;AAAA,IACZ,OAAO;AACL,iBAAW;AAAA,IACb;AAAA,EACF;AAEA,MAAI,SAAS;AACX,UAAM,KAAK,OAAO;AAAA,EACpB;AAEA,SAAO;AACT;AAMA,SAAS,gBACP,OACA,aACA,MACA,MACiB;AACjB,QAAM,SAAqB,CAAC;AAC5B,QAAM,WAAqB,CAAC;AAC5B,QAAM,OAA+B,CAAC;AACtC,MAAI,cAAc;AAClB,MAAI;AACJ,MAAI,YAAY;AAGhB,QAAM,WAAW,MAAM,MAAM,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,aAAa,EAAE,CAAC;AACxE,QAAM,eAAe,SAAS,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC;AAElE,MAAI,iBAAiB;AACrB,MAAI,YAAY;AAChB,MAAI,eAAe;AAEnB,aAAW,YAAY,cAAc;AAEnC,WAAO,eAAe,SAAS,UAAU,SAAS,YAAY,EAAE,KAAK,MAAM,UAAU;AACnF;AAAA,IACF;AACA,UAAM,UAAU,eAAe,SAAS,SAAS,SAAS,YAAY,IAAI;AAC1E;AAEA,QAAI,SAAS,WAAW,GAAG,GAAG;AAC5B,UAAI,WAAW;AACb,iBAAS,KAAK,eAAe,KAAK,CAAC;AACnC,yBAAiB;AACjB,oBAAY;AAAA,MACd;AAEA,YAAM,WAAW,gCAAgC,KAAK,QAAQ;AAC9D,UAAI,UAAU;AACZ,cAAM,CAAC,EAAE,SAAS,SAAS,OAAO,IAAI;AAEtC,gBAAQ,SAAS;AAAA,UACf,KAAK;AACH,kBAAM,aAAa,oBAAoB,KAAK,QAAQ,KAAK,CAAC;AAC1D,gBAAI,YAAY;AACd,qBAAO,KAAK;AAAA,gBACV,MAAM,WAAW,CAAC;AAAA,gBAClB,MAAM,WAAW;AAAA,gBACjB,aAAa,WAAW,CAAC;AAAA,cAC3B,CAAC;AAAA,YACH;AACA;AAAA,UACF,KAAK;AAAA,UACL,KAAK;AACH,sBAAU;AAAA,cACR,MAAM,WAAW;AAAA,cACjB,aAAa,QAAQ,KAAK;AAAA,YAC5B;AACA;AAAA,UACF,KAAK;AACH,wBAAY;AACZ;AAAA,UACF,KAAK;AACH,wBAAY;AACZ;AAAA,UACF;AACE,iBAAK,OAAO,IAAI,QAAQ,KAAK;AAAA,QACjC;AAAA,MACF;AAAA,IACF,WAAW,WAAW;AAEpB,wBAAkB,UAAU;AAAA,IAC9B,WAAW,CAAC,aAAa;AACvB,oBAAc;AAAA,IAChB,OAAO;AACL,qBAAe,OAAO;AAAA,IACxB;AAAA,EACF;AAEA,MAAI,aAAa,gBAAgB;AAC/B,aAAS,KAAK,eAAe,KAAK,CAAC;AAAA,EACrC;AAIA,QAAM,gBAAgB,YAAY,MAAM,IAAI,EAAE,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI;AAEnE,MAAI,OAAO;AACX,MAAI,OAAyB;AAG7B,QAAM,oBAAoB;AAC1B,QAAM,sBAAsB;AAC5B,QAAM,iBAAiB;AACvB,QAAM,qBAAqB;AAC3B,QAAM,gBAAgB;AAEtB,MAAI;AAEJ,MAAK,YAAY,kBAAkB,KAAK,aAAa,GAAI;AACvD,WAAO,UAAU,CAAC;AAClB,WAAO;AAAA,EACT,WAAY,YAAY,oBAAoB,KAAK,aAAa,GAAI;AAChE,WAAO,UAAU,CAAC;AAClB,WAAO;AAAA,EACT,WAAY,YAAY,eAAe,KAAK,aAAa,GAAI;AAC3D,WAAO,UAAU,CAAC;AAClB,WAAO;AAAA,EACT,WAAY,YAAY,mBAAmB,KAAK,aAAa,GAAI;AAC/D,WAAO,UAAU,CAAC;AAClB,WAAO;AAAA,EACT,WAAY,YAAY,cAAc,KAAK,aAAa,GAAI;AAC1D,WAAO,UAAU,CAAC;AAClB,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,KAAM,QAAO;AAGlB,MAAI;AACJ,MAAI,SAAS,YAAY;AACvB,UAAM,iBAAiB,0BAA0B,eAAe,MAAM;AAGtE,QAAI,eAAe,WAAW,SAAS,GAAG;AACxC,eAAS,IAAI,GAAG,IAAI,OAAO,UAAU,IAAI,eAAe,WAAW,QAAQ,KAAK;AAC9E,YAAI,OAAO,CAAC,EAAE,SAAS,WAAW;AAChC,iBAAO,CAAC,EAAE,OAAO,eAAe,WAAW,CAAC;AAAA,QAC9C;AAAA,MACF;AAAA,IACF;AAGA,QAAI,eAAe,eAAe,CAAC,WAAW,QAAQ,SAAS,YAAY;AACzE,UAAI,SAAS;AACX,gBAAQ,OAAO,eAAe;AAAA,MAChC,OAAO;AACL,kBAAU;AAAA,UACR,MAAM,eAAe;AAAA,UACrB,aAAa;AAAA,QACf;AAAA,MACF;AAAA,IACF;AAGA,gBAAY,yBAAyB,aAAa;AAAA,EACpD;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ,OAAO,SAAS,IAAI,SAAS;AAAA,IACrC;AAAA,IACA,UAAU,SAAS,SAAS,IAAI,WAAW;AAAA,IAC3C,MAAM,OAAO,KAAK,IAAI,EAAE,SAAS,IAAI,OAAO;AAAA,IAC5C,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAKO,SAAS,iBACd,MACA,SACwB;AACxB,QAAM,SAAiC,CAAC;AACxC,QAAM,YAAY,eAAe,IAAI;AAErC,MAAI,QAAQ,YAAY,QAAQ;AAC9B,UAAM,YAAY,oBAAI,IAA2B;AAEjD,eAAW,OAAO,MAAM;AACtB,UAAI,WAAgB,eAAS,IAAI,MAAW,cAAQ,IAAI,IAAI,CAAC;AAE7D,UAAI,aAAa,SAAS;AACxB,mBAAW;AAAA,MACb;AACA,gBAAU,IAAI,KAAK,QAAQ;AAE3B,YAAM,WAAW,qBAAqB,KAAK,SAAS,UAAU,SAAS;AACvE,aAAO,GAAG,QAAQ,KAAK,IAAI;AAAA,IAC7B;AAEA,WAAO,UAAU,IAAI,cAAc,MAAM,SAAS;AAAA,EACpD,OAAO;AACL,UAAM,SAAS,oBAAI,IAAwB;AAE3C,eAAW,OAAO,MAAM;AACtB,iBAAW,SAAS,IAAI,SAAS;AAC/B,cAAM,WAAW,OAAO,IAAI,MAAM,IAAI,KAAK,CAAC;AAC5C,iBAAS,KAAK,KAAK;AACnB,eAAO,IAAI,MAAM,MAAM,QAAQ;AAAA,MACjC;AAAA,IACF;AAEA,eAAW,CAAC,MAAM,OAAO,KAAK,QAAQ;AACpC,aAAO,GAAG,IAAI,MAAM,IAAI,yBAAyB,MAAM,SAAS,SAAS,SAAS;AAAA,IACpF;AAEA,WAAO,UAAU,IAAI,sBAAsB,MAAM;AAAA,EACnD;AAEA,SAAO;AACT;AAEA,SAAS,qBACP,KACA,SACA,iBACA,WACQ;AACR,QAAM,cAAmB,eAAS,IAAI,IAAI;AAC1C,MAAI,KAAK,KAAK,WAAW;AAAA;AAAA;AAGzB,MAAI,QAAQ,WAAW;AACrB,UAAM,aAAa,mBAAmB,IAAI,MAAM,QAAQ,SAAS;AACjE,QAAI,YAAY;AACd,YAAM,aAAa;AAAA,IACrB;AAAA,EACF;AAGA,aAAW,SAAS,IAAI,SAAS;AAC/B,UAAM,sBAAsB,OAAO,SAAS,iBAAiB,SAAS;AAAA,EACxE;AAEA,SAAO;AACT;AAEA,SAAS,sBACP,OACA,SACA,iBACA,WACQ;AACR,MAAI,KAAK,MAAM,MAAM,IAAI;AAAA;AAAA;AAEzB,QAAM,KAAK,MAAM,IAAI;AAAA;AAAA;AAErB,MAAI,MAAM,aAAa;AAErB,UAAM,uBAAuB,mBAAmB,YAC5C,mBAAmB,MAAM,aAAa,iBAAiB,SAAS,IAChE,MAAM;AACV,UAAM,GAAG,oBAAoB;AAAA;AAAA;AAAA,EAC/B;AAGA,MAAI,SAAS,WAAW;AACtB,UAAM,aAAa,mBAAmB,MAAM,MAAM,QAAQ,WAAW,MAAM,IAAI;AAC/E,QAAI,YAAY;AACd,YAAM,aAAa;AAAA,IACrB;AAAA,EACF;AAGA,MAAI,MAAM,aAAa,MAAM,SAAS,YAAY;AAChD,UAAM;AACN,UAAM,MAAM,YAAY;AACxB,UAAM;AAAA,EACR;AAEA,MAAI,MAAM,UAAU,MAAM,OAAO,SAAS,GAAG;AAC3C,UAAM;AACN,UAAM;AACN,UAAM;AACN,eAAW,SAAS,MAAM,QAAQ;AAChC,YAAM,OAAO,MAAM,IAAI,UAAU,MAAM,IAAI,QAAQ,MAAM,WAAW;AAAA;AAAA,IACtE;AACA,UAAM;AAAA,EACR;AAEA,MAAI,MAAM,SAAS;AACjB,UAAM;AACN,UAAM,KAAK,MAAM,QAAQ,IAAI,QAAQ,MAAM,QAAQ,WAAW;AAAA;AAAA;AAAA,EAChE;AAEA,MAAI,MAAM,YAAY,MAAM,SAAS,SAAS,GAAG;AAC/C,UAAM;AACN,eAAW,WAAW,MAAM,UAAU;AACpC,YAAM;AACN,YAAM,QAAQ,QAAQ,cAAc,EAAE,EAAE,QAAQ,WAAW,EAAE;AAC7D,YAAM;AAAA,IACR;AAAA,EACF;AAEA,QAAM;AAEN,SAAO;AACT;AAEA,SAAS,cAAc,MAAuB,WAAgD;AAC5F,MAAI,KAAK;AACT,QAAM;AAEN,QAAM;AAEN,aAAW,OAAO,MAAM;AACtB,UAAM,cAAmB,eAAS,IAAI,MAAW,cAAQ,IAAI,IAAI,CAAC;AAClE,QAAI,WAAW;AAEf,QAAI,aAAa,UAAU,IAAI,GAAG,GAAG;AACnC,iBAAW,UAAU,IAAI,GAAG;AAAA,IAC9B,WAAW,aAAa,SAAS;AAC/B,iBAAW;AAAA,IACb;AAEA,UAAM,QAAQ,WAAW,OAAO,QAAQ;AAAA;AAAA;AAExC,eAAW,SAAS,IAAI,SAAS;AAC/B,YAAM,OAAO,MAAM,aAAa,MAAM,GAAG,EAAE,KAAK;AAChD,YAAM,WAAW,MAAM,eAAe,MAAM,YAAY,SAAS,KAAK,QAAQ;AAC9E,YAAM,OAAO,MAAM,IAAI,QAAQ,MAAM,IAAI,QAAQ,IAAI,GAAG,QAAQ;AAAA;AAAA,IAClE;AACA,UAAM;AAAA,EACR;AAEA,SAAO;AACT;AAEA,SAAS,yBACP,MACA,SACA,SACA,WACQ;AACR,QAAM,mBAAmB,GAAG,IAAI;AAChC,MAAI,KAAK,KAAK,KAAK,OAAO,CAAC,EAAE,YAAY,IAAI,KAAK,MAAM,CAAC,CAAC;AAAA;AAAA;AAE1D,aAAW,SAAS,SAAS;AAC3B,UAAM,sBAAsB,OAAO,SAAS,kBAAkB,SAAS;AAAA,EACzE;AAEA,SAAO;AACT;AAEA,SAAS,sBAAsB,QAAyC;AACtE,MAAI,KAAK;AACT,QAAM;AAEN,aAAW,CAAC,MAAM,OAAO,KAAK,QAAQ;AACpC,UAAM,YAAY,KAAK,OAAO,CAAC,EAAE,YAAY,IAAI,KAAK,MAAM,CAAC,IAAI;AACjE,UAAM,OAAO,SAAS,OAAO,IAAI;AAAA;AAAA;AAEjC,eAAW,SAAS,SAAS;AAC3B,YAAM,OAAO,MAAM,aAAa,MAAM,GAAG,EAAE,KAAK;AAChD,YAAM,OAAO,MAAM,IAAI,QAAQ,IAAI;AAAA;AAAA,IACrC;AACA,UAAM;AAAA,EACR;AAEA,SAAO;AACT;AAiCA,SAAS,mBACP,MACA,iBACA,WACQ;AAGR,SAAO,KAAK,QAAQ,0BAA0B,CAAC,OAAO,eAAe;AACnE,UAAM,WAAW,UAAU,IAAI,UAAU;AACzC,QAAI,CAAC,UAAU;AAEb,aAAO;AAAA,IACT;AAEA,QAAI,SAAS,aAAa,iBAAiB;AAEzC,aAAO,IAAI,UAAU,MAAM,WAAW,YAAY,CAAC;AAAA,IACrD,OAAO;AAEL,aAAO,IAAI,UAAU,OAAO,SAAS,QAAQ,OAAO,WAAW,YAAY,CAAC;AAAA,IAC9E;AAAA,EACF,CAAC;AACH;AAKA,SAAS,eAAe,MAAoD;AAC1E,QAAM,MAAM,oBAAI,IAA4B;AAE5C,aAAW,OAAO,MAAM;AACtB,QAAI,WAAgB,eAAS,IAAI,MAAW,cAAQ,IAAI,IAAI,CAAC;AAC7D,QAAI,aAAa,SAAS;AACxB,iBAAW;AAAA,IACb;AAEA,eAAW,SAAS,IAAI,SAAS;AAC/B,UAAI,IAAI,MAAM,MAAM;AAAA,QAClB,MAAM,MAAM;AAAA,QACZ,MAAM,IAAI;AAAA,QACV;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;AAKA,eAAsB,UACpB,MACA,QACA,eACA,SACe;AACf,QAAS,YAAS,MAAM,QAAQ,EAAE,WAAW,KAAK,CAAC;AAEnD,aAAW,CAAC,UAAU,OAAO,KAAK,OAAO,QAAQ,IAAI,GAAG;AACtD,UAAM,WAAgB,WAAK,QAAQ,QAAQ;AAC3C,UAAS,YAAS,UAAU,UAAU,SAAS,OAAO;AAAA,EACxD;AAGA,MAAI,iBAAiB,SAAS,eAAe,QAAQ,YAAY,QAAQ;AACvE,UAAM,WAAW,oBAAoB,eAAe,MAAM;AAC1D,UAAM,UAAU,gBAAgB,UAAU,QAAQ;AAClD,UAAM,cAAmB,WAAK,QAAQ,QAAQ;AAC9C,UAAS,YAAS,UAAU,aAAa,SAAS,OAAO;AAAA,EAC3D;AACF;AAaA,SAAS,mBAAmB,UAAkB,WAAmB,YAA6B;AAG5F,QAAM,eAAe,SAAS,QAAQ,qCAAqC,KAAK;AAEhF,QAAM,WAAW,aAAa,KAAK,UAAU,KAAK;AAClD,QAAM,OAAO,GAAG,SAAS,cAAc,YAAY,GAAG,QAAQ;AAE9D,SAAO,cAAc,IAAI;AAC3B;AAEO,SAAS,mBACd,SAC6B;AAC7B,MAAI,YAAY,OAAO;AACrB,WAAO;AAAA,EACT;AAEA,QAAM,OAAO,WAAW,CAAC;AAEzB,SAAO;AAAA,IACL,SAAS,KAAK,WAAW;AAAA,IACzB,KAAK,KAAK,OAAO,CAAC,OAAO;AAAA,IACzB,KAAK,KAAK,OAAO;AAAA,IACjB,SAAS,KAAK,WAAW,CAAC,WAAW,UAAU;AAAA,IAC/C,SAAS,KAAK,WAAW,CAAC,eAAe,eAAe,cAAc;AAAA,IACtE,QAAQ,KAAK,UAAU;AAAA,IACvB,SAAS,KAAK,WAAW;AAAA,IACzB,KAAK;AAAA,IACL,SAAS,KAAK,WAAW;AAAA,IACzB,WAAW,KAAK;AAAA,IAChB,aAAa,KAAK,eAAe;AAAA,EACnC;AACF;;;AE55BA,YAAYC,SAAQ;AACpB,YAAYC,WAAU;AACtB,SAAS,YAAY;AAoCd,IAAM,wBAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA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","names":["path","unified","rehypeParse","rehypeStringify","getTextContent","path","fs","path","fs","path","collectMarkdownFiles","buildSearchIndex","oxContent","path"]}
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@ox-content/vite-plugin",
3
+ "version": "0.0.1-alpha.0",
4
+ "description": "Vite plugin for Ox Content - High-performance Markdown processing with Environment API",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "scripts": {
18
+ "build": "tsup",
19
+ "dev": "tsup --watch",
20
+ "typecheck": "tsc --noEmit"
21
+ },
22
+ "peerDependencies": {
23
+ "vite": "catalog:"
24
+ },
25
+ "dependencies": {
26
+ "@ox-content/napi": "workspace:*",
27
+ "glob": "^11.0.0",
28
+ "unified": "^11.0.5",
29
+ "rehype-parse": "^9.0.1",
30
+ "rehype-stringify": "^10.0.1",
31
+ "shiki": "^1.24.0",
32
+ "mermaid": "^11.4.0"
33
+ },
34
+ "devDependencies": {
35
+ "@types/hast": "^3.0.4",
36
+ "@types/node": "catalog:",
37
+ "tsup": "catalog:",
38
+ "typescript": "catalog:",
39
+ "vite": "catalog:"
40
+ },
41
+ "keywords": [
42
+ "vite",
43
+ "vite-plugin",
44
+ "markdown",
45
+ "ox-content",
46
+ "ssg",
47
+ "environment-api"
48
+ ],
49
+ "license": "MIT",
50
+ "author": "ubugeeei",
51
+ "repository": {
52
+ "type": "git",
53
+ "url": "https://github.com/ubugeeei/ox-content.git",
54
+ "directory": "npm/vite-plugin-ox-content"
55
+ },
56
+ "publishConfig": {
57
+ "access": "public"
58
+ }
59
+ }