dock-rush 1.0.0 → 1.0.1

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/components/doc-page/markdown-viewer.tsx"],"sourcesContent":["import { Button } from '@/components/ui/button'\r\nimport { Skeleton } from '@/components/ui/skeleton'\r\nimport { Check, Copy } from 'lucide-react'\r\nimport * as React from 'react'\r\nimport ReactMarkdown from 'react-markdown'\r\nimport { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'\r\nimport {\r\n\toneDark,\r\n\toneLight,\r\n} from 'react-syntax-highlighter/dist/esm/styles/prism'\r\nimport rehypeRaw from 'rehype-raw'\r\nimport rehypeSanitize from 'rehype-sanitize'\r\nimport remarkGfm from 'remark-gfm'\r\n\r\ninterface MarkdownViewerProps {\r\n\treadonly filePath: string\r\n\treadonly folderPath: string\r\n\treadonly onPageSelect: (p: string) => void\r\n}\r\n\r\n// Функция для создания якорей из текста заголовка\r\nfunction toAnchor(children: React.ReactNode): string {\r\n\tconst regex = /[^\\w\\s-]/g\r\n\tconst spaceRegex = /\\s+/g\r\n\tconst dashRegex = /-+/g\r\n\r\n\tif (typeof children === 'string') {\r\n\t\treturn children\r\n\t\t\t.toLowerCase()\r\n\t\t\t.replace(regex, '')\r\n\t\t\t.replace(spaceRegex, '-')\r\n\t\t\t.replace(dashRegex, '-')\r\n\t\t\t.trim()\r\n\t}\r\n\tif (Array.isArray(children)) {\r\n\t\treturn children\r\n\t\t\t.map(child => {\r\n\t\t\t\tif (typeof child === 'string') return child\r\n\t\t\t\tif (React.isValidElement(child)) {\r\n\t\t\t\t\tconst childProps = child.props as { children?: React.ReactNode }\r\n\t\t\t\t\tif (childProps.children) {\r\n\t\t\t\t\t\treturn toAnchor(childProps.children)\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\treturn ''\r\n\t\t\t})\r\n\t\t\t.join('')\r\n\t\t\t.toLowerCase()\r\n\t\t\t.replace(regex, '')\r\n\t\t\t.replace(spaceRegex, '-')\r\n\t\t\t.replace(dashRegex, '-')\r\n\t\t\t.trim()\r\n\t}\r\n\treturn ''\r\n}\r\n\r\n// Компонент для блока кода с кнопкой копирования\r\nfunction CodeBlock({\r\n\tcodeString,\r\n\tlanguage,\r\n\tcopiedCode,\r\n\tsetCopiedCode,\r\n\tcodeTheme,\r\n}: {\r\n\treadonly codeString: string\r\n\treadonly language: string\r\n\treadonly copiedCode: string | null\r\n\treadonly setCopiedCode: (id: string | null) => void\r\n\treadonly codeTheme: any\r\n}) {\r\n\tconst codeId = React.useId()\r\n\tconst isCopied = copiedCode === codeId\r\n\r\n\treturn (\r\n\t\t<div className='my-6 overflow-hidden rounded-lg border border-border bg-card'>\r\n\t\t\t<div className='flex h-12 items-center justify-between border-b border-border bg-muted/50 px-4'>\r\n\t\t\t\t<span className='text-sm font-medium text-foreground'>{language}</span>\r\n\t\t\t\t<Button\r\n\t\t\t\t\tvariant='ghost'\r\n\t\t\t\t\tsize='sm'\r\n\t\t\t\t\tclassName='h-8 w-8 p-0'\r\n\t\t\t\t\tonClick={() => {\r\n\t\t\t\t\t\tnavigator.clipboard.writeText(codeString)\r\n\t\t\t\t\t\tsetCopiedCode(codeId)\r\n\t\t\t\t\t\tsetTimeout(() => setCopiedCode(null), 2000)\r\n\t\t\t\t\t}}\r\n\t\t\t\t>\r\n\t\t\t\t\t{isCopied ? (\r\n\t\t\t\t\t\t<Check className='h-4 w-4' />\r\n\t\t\t\t\t) : (\r\n\t\t\t\t\t\t<Copy className='h-4 w-4' />\r\n\t\t\t\t\t)}\r\n\t\t\t\t\t<span className='sr-only'>Copy code</span>\r\n\t\t\t\t</Button>\r\n\t\t\t</div>\r\n\t\t\t<SyntaxHighlighter\r\n\t\t\t\tstyle={codeTheme}\r\n\t\t\t\tlanguage={language}\r\n\t\t\t\tPreTag='div'\r\n\t\t\t\tshowLineNumbers={false}\r\n\t\t\t\tcustomStyle={{\r\n\t\t\t\t\tmargin: 0,\r\n\t\t\t\t\tpadding: '1rem',\r\n\t\t\t\t\tfontSize: '14px',\r\n\t\t\t\t\tlineHeight: '1.5',\r\n\t\t\t\t\tbackground: 'transparent',\r\n\t\t\t\t}}\r\n\t\t\t>\r\n\t\t\t\t{codeString}\r\n\t\t\t</SyntaxHighlighter>\r\n\t\t</div>\r\n\t)\r\n}\r\n\r\n// Функция для разрешения путей к изображениям\r\nfunction resolveImagePath(src: string, filePath: string, folderPath: string): string {\r\n\t// Абсолютные пути (http, https, data:) оставляем как есть\r\n\tif (\r\n\t\t!src ||\r\n\t\tsrc.startsWith('http://') ||\r\n\t\tsrc.startsWith('https://') ||\r\n\t\tsrc.startsWith('data:')\r\n\t) {\r\n\t\treturn src\r\n\t}\r\n\r\n\t// Если путь начинается с /, это абсолютный путь от корня сайта\r\n\t// Но нам нужно проверить, не находится ли он в папке документации\r\n\tif (src.startsWith('/')) {\r\n\t\t// Если путь уже начинается с folderPath, оставляем как есть\r\n\t\tconst cleanFolderPath = folderPath.replace(/^\\/+/, '').replace(/\\/+$/, '')\r\n\t\tif (src.startsWith(`/${cleanFolderPath}/`)) {\r\n\t\t\treturn src\r\n\t\t}\r\n\t\t// Иначе добавляем folderPath\r\n\t\tif (cleanFolderPath) {\r\n\t\t\treturn `/${cleanFolderPath}${src}`\r\n\t\t}\r\n\t\treturn src\r\n\t}\r\n\r\n\t// Относительные пути разрешаем относительно текущего файла\r\n\t// filePath имеет формат: \"2.0.2/ru/api/markdown-test.md\" (относительно folderPath)\r\n\tif (!filePath) {\r\n\t\t// Если filePath пустой, просто добавляем folderPath\r\n\t\tconst cleanFolderPath = folderPath.replace(/^\\/+/, '').replace(/\\/+$/, '')\r\n\t\tconst imageName = src.replace(/\\\\/g, '/').replace(/^\\.\\//, '')\r\n\t\tif (cleanFolderPath) {\r\n\t\t\treturn `/${cleanFolderPath}/${imageName}`\r\n\t\t}\r\n\t\treturn `/${imageName}`\r\n\t}\r\n\t\r\n\t// Убираем имя файла из пути, оставляем только директорию\r\n\tconst fileDir = filePath.replace(/[/\\\\][^/\\\\]*\\.md$/, '').replace(/\\\\/g, '/')\r\n\t\r\n\t// Нормализуем путь изображения (убираем начальные ./ если есть)\r\n\tconst imagePath = src.replace(/\\\\/g, '/').replace(/^\\.\\//, '')\r\n\tconst imageParts = imagePath.split('/').filter(Boolean)\r\n\t\r\n\t// Начинаем с директории файла (может быть пустой, если файл в корне)\r\n\tconst dirParts = fileDir ? fileDir.split('/').filter(Boolean) : []\r\n\r\n\t// Обрабатываем относительные пути (.., .)\r\n\tfor (const part of imageParts) {\r\n\t\tif (part === '..') {\r\n\t\t\tif (dirParts.length > 0) {\r\n\t\t\t\tdirParts.pop()\r\n\t\t\t}\r\n\t\t} else if (part !== '.') {\r\n\t\t\tdirParts.push(part)\r\n\t\t}\r\n\t}\r\n\r\n\t// Формируем путь относительно папки документации\r\n\tconst resolvedPath = dirParts.length > 0 ? dirParts.join('/') : ''\r\n\t\r\n\t// Добавляем folderPath в начало пути\r\n\t// В Vite файлы из public доступны по корневому пути\r\n\tconst cleanFolderPath = folderPath.replace(/^\\/+/, '').replace(/\\/+$/, '')\r\n\t\r\n\tif (cleanFolderPath) {\r\n\t\tif (resolvedPath) {\r\n\t\t\treturn `/${cleanFolderPath}/${resolvedPath}`\r\n\t\t}\r\n\t\t// Если resolvedPath пустой, но есть folderPath, возвращаем только folderPath\r\n\t\t// Это может произойти, если изображение указано как \"./\" или \"../\" до корня\r\n\t\treturn `/${cleanFolderPath}`\r\n\t}\r\n\t\r\n\treturn resolvedPath ? `/${resolvedPath}` : '/'\r\n}\r\n\r\nexport function MarkdownViewer({\r\n\tfilePath,\r\n\tfolderPath,\r\n\tonPageSelect,\r\n}: MarkdownViewerProps) {\r\n\tconst [content, setContent] = React.useState<string>('')\r\n\tconst [loading, setLoading] = React.useState(true)\r\n\tconst [error, setError] = React.useState<string | null>(null)\r\n\tconst [isDark, setIsDark] = React.useState(false)\r\n\tconst [copiedCode, setCopiedCode] = React.useState<string | null>(null)\r\n\r\n\tReact.useEffect(() => {\r\n\t\tconst checkTheme = () => {\r\n\t\t\tsetIsDark(document.documentElement.classList.contains('dark'))\r\n\t\t}\r\n\t\tcheckTheme()\r\n\t\tconst observer = new MutationObserver(checkTheme)\r\n\t\tobserver.observe(document.documentElement, {\r\n\t\t\tattributes: true,\r\n\t\t\tattributeFilter: ['class'],\r\n\t\t})\r\n\t\treturn () => observer.disconnect()\r\n\t}, [])\r\n\r\n\tconst codeTheme = isDark ? oneDark : oneLight\r\n\r\n\tReact.useEffect(() => {\r\n\t\tconst loadMarkdown = async () => {\r\n\t\t\tsetLoading(true)\r\n\t\t\tsetError(null)\r\n\t\t\tsetContent('')\r\n\r\n\t\t\t// Минимальная задержка для плавности анимации\r\n\t\t\tconst minDelay = 200\r\n\r\n\t\t\ttry {\r\n\t\t\t\tconst startTime = Date.now()\r\n\r\n\t\t\t\t// Загружаем markdown файл через API\r\n\t\t\t\tconst params = new URLSearchParams()\r\n\t\t\t\tparams.set('filePath', filePath)\r\n\t\t\t\tparams.set('folderPath', folderPath)\r\n\r\n\t\t\t\tconst response = await fetch(\r\n\t\t\t\t\t`/api/dock-rush-markdown?${params.toString()}`,\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\theaders: {\r\n\t\t\t\t\t\t\tAccept: 'application/json',\r\n\t\t\t\t\t\t},\r\n\t\t\t\t\t}\r\n\t\t\t\t)\r\n\r\n\t\t\t\tif (!response.ok) {\r\n\t\t\t\t\tthrow new Error(`HTTP ${response.status}: ${response.statusText}`)\r\n\t\t\t\t}\r\n\r\n\t\t\t\tconst data = await response.json()\r\n\r\n\t\t\t\t// Вычисляем оставшееся время для минимальной задержки\r\n\t\t\t\tconst elapsed = Date.now() - startTime\r\n\t\t\t\tconst remainingDelay = Math.max(0, minDelay - elapsed)\r\n\r\n\t\t\t\tawait new Promise(resolve => setTimeout(resolve, remainingDelay))\r\n\r\n\t\t\t\tif (data.success) {\r\n\t\t\t\t\tsetContent(data.content)\r\n\t\t\t\t\t// Небольшая задержка перед скрытием loading для плавности\r\n\t\t\t\t\tsetTimeout(() => {\r\n\t\t\t\t\t\tsetLoading(false)\r\n\t\t\t\t\t}, 100)\r\n\t\t\t\t} else {\r\n\t\t\t\t\tthrow new Error(data.error || 'Failed to load markdown')\r\n\t\t\t\t}\r\n\t\t\t} catch (err) {\r\n\t\t\t\tsetError(err instanceof Error ? err.message : 'Unknown error')\r\n\t\t\t\tsetLoading(false)\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (filePath) {\r\n\t\t\tloadMarkdown()\r\n\t\t}\r\n\t}, [filePath, folderPath])\r\n\r\n\tif (error) {\r\n\t\treturn (\r\n\t\t\t<div className='flex flex-col items-center justify-center p-12 animate-in fade-in-0 duration-300'>\r\n\t\t\t\t<p className='text-destructive'>Ошибка загрузки файла: {error}</p>\r\n\t\t\t</div>\r\n\t\t)\r\n\t}\r\n\r\n\treturn (\r\n\t\t<div className='relative mx-auto max-w-4xl px-6 py-8 h-full'>\r\n\t\t\t{/* Скелетон */}\r\n\t\t\t{loading && (\r\n\t\t\t\t<div className='flex flex-col gap-4 p-6 animate-in fade-in-0 duration-200'>\r\n\t\t\t\t\t<Skeleton className='h-8 w-3/4' />\r\n\t\t\t\t\t<Skeleton className='h-4 w-full' />\r\n\t\t\t\t\t<Skeleton className='h-4 w-full' />\r\n\t\t\t\t\t<Skeleton className='h-4 w-2/3' />\r\n\t\t\t\t\t<Skeleton className='h-4 w-full' />\r\n\t\t\t\t\t<Skeleton className='h-4 w-5/6' />\r\n\t\t\t\t\t<Skeleton className='h-4 w-full' />\r\n\t\t\t\t\t<Skeleton className='h-4 w-4/5' />\r\n\t\t\t\t</div>\r\n\t\t\t)}\r\n\r\n\t\t\t{/* Контент */}\r\n\t\t\t{!loading && content && (\r\n\t\t\t\t<div className='animate-in fade-in-0 duration-300'>\r\n\t\t\t\t\t<ReactMarkdown\r\n\t\t\t\tremarkPlugins={[remarkGfm]}\r\n\t\t\t\trehypePlugins={[rehypeRaw, rehypeSanitize]}\r\n\t\t\t\tcomponents={{\r\n\t\t\t\t\t// Заголовки в стиле shadcn\r\n\t\t\t\t\th1: ({ node, ...props }) => {\r\n\t\t\t\t\t\tconst anchor = toAnchor(props.children)\r\n\t\t\t\t\t\treturn (\r\n\t\t\t\t\t\t\t<h1\r\n\t\t\t\t\t\t\t\tid={anchor}\r\n\t\t\t\t\t\t\t\tclassName='mb-6 mt-8 scroll-mt-20 text-4xl font-bold tracking-tight'\r\n\t\t\t\t\t\t\t\t{...props}\r\n\t\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t)\r\n\t\t\t\t\t},\r\n\t\t\t\t\th2: ({ node, ...props }) => {\r\n\t\t\t\t\t\tconst anchor = toAnchor(props.children)\r\n\t\t\t\t\t\treturn (\r\n\t\t\t\t\t\t\t<h2\r\n\t\t\t\t\t\t\t\tid={anchor}\r\n\t\t\t\t\t\t\t\tclassName='mb-4 mt-8 scroll-mt-20 text-3xl font-semibold tracking-tight'\r\n\t\t\t\t\t\t\t\t{...props}\r\n\t\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t)\r\n\t\t\t\t\t},\r\n\t\t\t\t\th3: ({ node, ...props }) => {\r\n\t\t\t\t\t\tconst anchor = toAnchor(props.children)\r\n\t\t\t\t\t\treturn (\r\n\t\t\t\t\t\t\t<h3\r\n\t\t\t\t\t\t\t\tid={anchor}\r\n\t\t\t\t\t\t\t\tclassName='mb-3 mt-6 scroll-mt-20 text-2xl font-semibold tracking-tight'\r\n\t\t\t\t\t\t\t\t{...props}\r\n\t\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t)\r\n\t\t\t\t\t},\r\n\t\t\t\t\th4: ({ node, ...props }) => {\r\n\t\t\t\t\t\tconst anchor = toAnchor(props.children)\r\n\t\t\t\t\t\treturn (\r\n\t\t\t\t\t\t\t<h4\r\n\t\t\t\t\t\t\t\tid={anchor}\r\n\t\t\t\t\t\t\t\tclassName='mb-2 mt-4 scroll-mt-20 text-xl font-semibold tracking-tight'\r\n\t\t\t\t\t\t\t\t{...props}\r\n\t\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t)\r\n\t\t\t\t\t},\r\n\t\t\t\t\th5: ({ node, ...props }) => {\r\n\t\t\t\t\t\tconst anchor = toAnchor(props.children)\r\n\t\t\t\t\t\treturn (\r\n\t\t\t\t\t\t\t<h5\r\n\t\t\t\t\t\t\t\tid={anchor}\r\n\t\t\t\t\t\t\t\tclassName='mb-2 mt-4 scroll-mt-20 text-lg font-semibold'\r\n\t\t\t\t\t\t\t\t{...props}\r\n\t\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t)\r\n\t\t\t\t\t},\r\n\t\t\t\t\th6: ({ node, ...props }) => {\r\n\t\t\t\t\t\tconst anchor = toAnchor(props.children)\r\n\t\t\t\t\t\treturn (\r\n\t\t\t\t\t\t\t<h6\r\n\t\t\t\t\t\t\t\tid={anchor}\r\n\t\t\t\t\t\t\t\tclassName='mb-2 mt-4 scroll-mt-20 text-base font-semibold'\r\n\t\t\t\t\t\t\t\t{...props}\r\n\t\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t)\r\n\t\t\t\t\t},\r\n\t\t\t\t\t// Параграфы в стиле shadcn\r\n\t\t\t\t\tp: ({ node, children, ...props }: any) => {\r\n\t\t\t\t\t\t// Проверяем, есть ли внутри изображения или другие блочные элементы\r\n\t\t\t\t\t\tconst childrenArray = React.Children.toArray(children)\r\n\t\t\t\t\t\tconst hasBlockElements = childrenArray.some((child: unknown) => {\r\n\t\t\t\t\t\t\tif (React.isValidElement(child)) {\r\n\t\t\t\t\t\t\t\tif (child.type === 'figure' || child.type === 'img') {\r\n\t\t\t\t\t\t\t\t\treturn true\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\tconst childProps = child.props as { node?: any }\r\n\t\t\t\t\t\t\t\tif (childProps?.node) {\r\n\t\t\t\t\t\t\t\t\tconst childNode = childProps.node\r\n\t\t\t\t\t\t\t\t\tif (\r\n\t\t\t\t\t\t\t\t\t\tchildNode.type === 'element' &&\r\n\t\t\t\t\t\t\t\t\t\t(childNode.tagName === 'img' ||\r\n\t\t\t\t\t\t\t\t\t\t\tchildNode.tagName === 'figure')\r\n\t\t\t\t\t\t\t\t\t) {\r\n\t\t\t\t\t\t\t\t\t\treturn true\r\n\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\treturn false\r\n\t\t\t\t\t\t})\r\n\r\n\t\t\t\t\t\tif (hasBlockElements) {\r\n\t\t\t\t\t\t\tconst { ref, ...divProps } = props as any\r\n\t\t\t\t\t\t\treturn (\r\n\t\t\t\t\t\t\t\t<div\r\n\t\t\t\t\t\t\t\t\tclassName='mb-6 leading-7 text-muted-foreground'\r\n\t\t\t\t\t\t\t\t\t{...divProps}\r\n\t\t\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t\t\t{children}\r\n\t\t\t\t\t\t\t\t</div>\r\n\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\treturn (\r\n\t\t\t\t\t\t\t<p className='mb-6 leading-7 text-muted-foreground' {...props}>\r\n\t\t\t\t\t\t\t\t{children}\r\n\t\t\t\t\t\t\t</p>\r\n\t\t\t\t\t\t)\r\n\t\t\t\t\t},\r\n\t\t\t\t\t// Списки в стиле shadcn\r\n\t\t\t\t\tul: ({ node, ...props }) => (\r\n\t\t\t\t\t\t<ul\r\n\t\t\t\t\t\t\tclassName='my-6 ml-6 list-disc space-y-2 text-muted-foreground'\r\n\t\t\t\t\t\t\t{...props}\r\n\t\t\t\t\t\t/>\r\n\t\t\t\t\t),\r\n\t\t\t\t\tol: ({ node, ...props }) => (\r\n\t\t\t\t\t\t<ol\r\n\t\t\t\t\t\t\tclassName='my-6 ml-6 list-decimal space-y-2 text-muted-foreground'\r\n\t\t\t\t\t\t\t{...props}\r\n\t\t\t\t\t\t/>\r\n\t\t\t\t\t),\r\n\t\t\t\t\tli: ({ node, ...props }) => (\r\n\t\t\t\t\t\t<li className='leading-7 pl-2' {...props} />\r\n\t\t\t\t\t),\r\n\t\t\t\t\t// Чекбоксы для task lists\r\n\t\t\t\t\tinput: ({ node, ...props }: any) => {\r\n\t\t\t\t\t\tif (props.type === 'checkbox') {\r\n\t\t\t\t\t\t\treturn (\r\n\t\t\t\t\t\t\t\t<input\r\n\t\t\t\t\t\t\t\t\ttype='checkbox'\r\n\t\t\t\t\t\t\t\t\tclassName='mr-2 h-4 w-4 rounded border-border accent-primary'\r\n\t\t\t\t\t\t\t\t\tdisabled\r\n\t\t\t\t\t\t\t\t\t{...props}\r\n\t\t\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\treturn <input {...props} />\r\n\t\t\t\t\t},\r\n\t\t\t\t\t// Цитаты в стиле shadcn\r\n\t\t\t\t\tblockquote: ({ node, ...props }) => (\r\n\t\t\t\t\t\t<blockquote\r\n\t\t\t\t\t\t\tclassName='my-6 border-l-2 border-border bg-muted/50 pl-6 italic text-muted-foreground'\r\n\t\t\t\t\t\t\t{...props}\r\n\t\t\t\t\t\t/>\r\n\t\t\t\t\t),\r\n\t\t\t\t\t// Подсветка кода в стиле shadcn\r\n\t\t\t\t\tcode: ({ node, inline, className, children, ...props }: any) => {\r\n\t\t\t\t\t\tconst match = /language-(\\w+)/.exec(className || '')\r\n\t\t\t\t\t\tconst codeString = String(children).replace(/\\n$/, '')\r\n\r\n\t\t\t\t\t\tif (!inline && match) {\r\n\t\t\t\t\t\t\treturn (\r\n\t\t\t\t\t\t\t\t<CodeBlock\r\n\t\t\t\t\t\t\t\t\tcodeString={codeString}\r\n\t\t\t\t\t\t\t\t\tlanguage={match[1]}\r\n\t\t\t\t\t\t\t\t\tcopiedCode={copiedCode}\r\n\t\t\t\t\t\t\t\t\tsetCopiedCode={setCopiedCode}\r\n\t\t\t\t\t\t\t\t\tcodeTheme={codeTheme}\r\n\t\t\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\treturn (\r\n\t\t\t\t\t\t\t<code\r\n\t\t\t\t\t\t\t\tclassName='relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm font-semibold'\r\n\t\t\t\t\t\t\t\t{...props}\r\n\t\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t\t{children}\r\n\t\t\t\t\t\t\t</code>\r\n\t\t\t\t\t\t)\r\n\t\t\t\t\t},\r\n\t\t\t\t\tpre: ({ node, ...props }: any) => {\r\n\t\t\t\t\t\tconst { ref, ...divProps } = props\r\n\t\t\t\t\t\treturn <div {...divProps} />\r\n\t\t\t\t\t},\r\n\t\t\t\t\t// Изображения в стиле shadcn\r\n\t\t\t\t\timg: ({ src, alt, title }: any) => {\r\n\t\t\t\t\t\t// Разрешаем путь к изображению относительно текущего файла\r\n\t\t\t\t\t\tconst resolvedSrc = resolveImagePath(\r\n\t\t\t\t\t\t\tsrc || '',\r\n\t\t\t\t\t\t\tfilePath || '',\r\n\t\t\t\t\t\t\tfolderPath || 'docs'\r\n\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\treturn (\r\n\t\t\t\t\t\t\t<figure className='my-8'>\r\n\t\t\t\t\t\t\t\t<img\r\n\t\t\t\t\t\t\t\t\tsrc={resolvedSrc}\r\n\t\t\t\t\t\t\t\t\talt={alt}\r\n\t\t\t\t\t\t\t\t\ttitle={title}\r\n\t\t\t\t\t\t\t\t\tloading='lazy'\r\n\t\t\t\t\t\t\t\t\tclassName='rounded-lg border border-border bg-muted max-w-full h-auto'\r\n\t\t\t\t\t\t\t\t\tonError={(e) => {\r\n\t\t\t\t\t\t\t\t\t\t// Если изображение не загрузилось, показываем placeholder\r\n\t\t\t\t\t\t\t\t\t\tconst target = e.target as HTMLImageElement\r\n\t\t\t\t\t\t\t\t\t\ttarget.style.display = 'none'\r\n\t\t\t\t\t\t\t\t\t\tconst figure = target.closest('figure')\r\n\t\t\t\t\t\t\t\t\t\tif (figure && !figure.querySelector('.image-error')) {\r\n\t\t\t\t\t\t\t\t\t\t\tconst errorDiv = document.createElement('div')\r\n\t\t\t\t\t\t\t\t\t\t\terrorDiv.className = 'image-error p-4 text-center text-sm text-muted-foreground border border-dashed border-border rounded-lg'\r\n\t\t\t\t\t\t\t\t\t\t\terrorDiv.textContent = `Изображение не найдено: ${src} (путь: ${resolvedSrc})`\r\n\t\t\t\t\t\t\t\t\t\t\tfigure.appendChild(errorDiv)\r\n\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t}}\r\n\t\t\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t\t\t{alt && (\r\n\t\t\t\t\t\t\t\t\t<figcaption className='mt-3 text-center text-sm text-muted-foreground'>\r\n\t\t\t\t\t\t\t\t\t\t{alt}\r\n\t\t\t\t\t\t\t\t\t</figcaption>\r\n\t\t\t\t\t\t\t\t)}\r\n\t\t\t\t\t\t\t</figure>\r\n\t\t\t\t\t\t)\r\n\t\t\t\t\t},\r\n\t\t\t\t\t// Ссылки в стиле shadcn\r\n\t\t\t\t\ta: ({ href, children, ...props }: any) => {\r\n\t\t\t\t\t\t/* 1. внешние / якоря – оставляем как есть */\r\n\t\t\t\t\t\tif (\r\n\t\t\t\t\t\t\t!href ||\r\n\t\t\t\t\t\t\thref.startsWith('http') ||\r\n\t\t\t\t\t\t\thref.startsWith('#') ||\r\n\t\t\t\t\t\t\thref.startsWith('mailto:') ||\r\n\t\t\t\t\t\t\thref.startsWith('tel:')\r\n\t\t\t\t\t\t) {\r\n\t\t\t\t\t\t\treturn (\r\n\t\t\t\t\t\t\t\t<a\r\n\t\t\t\t\t\t\t\t\thref={href}\r\n\t\t\t\t\t\t\t\t\tclassName='font-medium text-primary underline underline-offset-4 transition-colors hover:text-primary/80'\r\n\t\t\t\t\t\t\t\t\ttarget='_blank'\r\n\t\t\t\t\t\t\t\t\trel='noopener noreferrer'\r\n\t\t\t\t\t\t\t\t\t{...props}\r\n\t\t\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t\t\t{children}\r\n\t\t\t\t\t\t\t\t</a>\r\n\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t/* 2. локальный .md – перехватываем */\r\n\t\t\t\t\t\tif (href.toLowerCase().endsWith('.md')) {\r\n\t\t\t\t\t\t\tconst handle = (e: React.MouseEvent) => {\r\n\t\t\t\t\t\t\t\te.preventDefault()\r\n\r\n\t\t\t\t\t\t\t\t/* 2а. выделяем папку текущего файла (убираем имя.md) */\r\n\t\t\t\t\t\t\t\tconst dir = filePath.replace(/[/\\\\][^/\\\\]*\\.md$/, '') // убирает \\installation.md\r\n\r\n\t\t\t\t\t\t\t\t/* 2b. нормализуем слеши → всегда \"/\" для единообразия */\r\n\t\t\t\t\t\t\t\tconst base = dir.replace(/\\\\/g, '/')\r\n\r\n\t\t\t\t\t\t\t\t/* 2c. резолвим относительную ссылку */\r\n\t\t\t\t\t\t\t\tconst rel = href.replace(/\\\\/g, '/')\r\n\t\t\t\t\t\t\t\tconst relParts = rel.split('/').filter(Boolean)\r\n\t\t\t\t\t\t\t\tconst baseParts = base.split('/').filter(Boolean)\r\n\r\n\t\t\t\t\t\t\t\tfor (const p of relParts) {\r\n\t\t\t\t\t\t\t\t\tif (p === '..') baseParts.pop()\r\n\t\t\t\t\t\t\t\t\telse if (p !== '.') baseParts.push(p)\r\n\t\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t\t\tconst absolute = baseParts.join('/')\r\n\t\t\t\t\t\t\t\t// onPageSelect теперь обновляет URL автоматически\r\n\t\t\t\t\t\t\t\tonPageSelect(absolute)\r\n\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t\treturn (\r\n\t\t\t\t\t\t\t\t<a\r\n\t\t\t\t\t\t\t\t\thref={href}\r\n\t\t\t\t\t\t\t\t\tonClick={handle}\r\n\t\t\t\t\t\t\t\t\tclassName='font-medium text-primary underline underline-offset-4 transition-colors hover:text-primary/80 cursor-pointer'\r\n\t\t\t\t\t\t\t\t\t{...props}\r\n\t\t\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t\t\t{children}\r\n\t\t\t\t\t\t\t\t</a>\r\n\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t/* 3. остальные внутренние ссылки – просто отдаём */\r\n\t\t\t\t\t\treturn (\r\n\t\t\t\t\t\t\t<a\r\n\t\t\t\t\t\t\t\thref={href}\r\n\t\t\t\t\t\t\t\tclassName='font-medium text-primary underline underline-offset-4 transition-colors hover:text-primary/80'\r\n\t\t\t\t\t\t\t\t{...props}\r\n\t\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t)\r\n\t\t\t\t\t},\r\n\t\t\t\t\t// Таблицы в стиле shadcn\r\n\t\t\t\t\ttable: ({ children }: any) => (\r\n\t\t\t\t\t\t<div className='my-6 w-full overflow-y-auto'>\r\n\t\t\t\t\t\t\t<table className='w-full border-collapse'>{children}</table>\r\n\t\t\t\t\t\t</div>\r\n\t\t\t\t\t),\r\n\t\t\t\t\tthead: ({ node, ...props }) => (\r\n\t\t\t\t\t\t<thead className='[&_tr]:border-b' {...props} />\r\n\t\t\t\t\t),\r\n\t\t\t\t\tth: ({ node, ...props }) => (\r\n\t\t\t\t\t\t<th\r\n\t\t\t\t\t\t\tclassName='h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0'\r\n\t\t\t\t\t\t\t{...props}\r\n\t\t\t\t\t\t/>\r\n\t\t\t\t\t),\r\n\t\t\t\t\ttd: ({ node, ...props }) => (\r\n\t\t\t\t\t\t<td\r\n\t\t\t\t\t\t\tclassName='p-4 align-middle [&:has([role=checkbox])]:pr-0'\r\n\t\t\t\t\t\t\t{...props}\r\n\t\t\t\t\t\t/>\r\n\t\t\t\t\t),\r\n\t\t\t\t\ttbody: ({ node, ...props }: any) => (\r\n\t\t\t\t\t\t<tbody className='[&_tr:last-child]:border-0' {...props} />\r\n\t\t\t\t\t),\r\n\t\t\t\t\ttr: ({ node, ...props }: any) => (\r\n\t\t\t\t\t\t<tr\r\n\t\t\t\t\t\t\tclassName='border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted'\r\n\t\t\t\t\t\t\t{...props}\r\n\t\t\t\t\t\t/>\r\n\t\t\t\t\t),\r\n\t\t\t\t\t// Горизонтальная линия в стиле shadcn\r\n\t\t\t\t\thr: ({ node, ...props }) => (\r\n\t\t\t\t\t\t<hr className='my-8 border-t border-border' {...props} />\r\n\t\t\t\t\t),\r\n\t\t\t\t\t// Форматирование текста в стиле shadcn\r\n\t\t\t\t\tstrong: ({ node, ...props }) => (\r\n\t\t\t\t\t\t<strong className='font-semibold text-foreground' {...props} />\r\n\t\t\t\t\t),\r\n\t\t\t\t\tem: ({ node, ...props }) => (\r\n\t\t\t\t\t\t<em className='italic text-foreground' {...props} />\r\n\t\t\t\t\t),\r\n\t\t\t\t\tdel: ({ node, ...props }) => (\r\n\t\t\t\t\t\t<del className='line-through text-muted-foreground' {...props} />\r\n\t\t\t\t\t),\r\n\t\t\t\t\t// Дополнительные HTML элементы\r\n\t\t\t\t\t// Поддержка <mark> для выделения\r\n\t\t\t\t\tmark: ({ node, ...props }: any) => (\r\n\t\t\t\t\t\t<mark\r\n\t\t\t\t\t\t\tclassName='bg-yellow-200 dark:bg-yellow-900/50 px-1 rounded'\r\n\t\t\t\t\t\t\t{...props}\r\n\t\t\t\t\t\t/>\r\n\t\t\t\t\t),\r\n\t\t\t\t\t// Поддержка <kbd> для клавиатурных сочетаний\r\n\t\t\t\t\tkbd: ({ node, ...props }: any) => (\r\n\t\t\t\t\t\t<kbd\r\n\t\t\t\t\t\t\tclassName='px-2 py-1 text-xs font-semibold text-foreground bg-muted border border-border rounded shadow-sm'\r\n\t\t\t\t\t\t\t{...props}\r\n\t\t\t\t\t\t/>\r\n\t\t\t\t\t),\r\n\t\t\t\t\t// Поддержка <abbr> для аббревиатур\r\n\t\t\t\t\tabbr: ({ node, title, ...props }: any) => (\r\n\t\t\t\t\t\t<abbr\r\n\t\t\t\t\t\t\ttitle={title}\r\n\t\t\t\t\t\t\tclassName='underline decoration-dotted cursor-help'\r\n\t\t\t\t\t\t\t{...props}\r\n\t\t\t\t\t\t/>\r\n\t\t\t\t\t),\r\n\t\t\t\t\t// Поддержка <sub> и <sup>\r\n\t\t\t\t\tsub: ({ node, ...props }: any) => (\r\n\t\t\t\t\t\t<sub className='text-xs align-sub' {...props} />\r\n\t\t\t\t\t),\r\n\t\t\t\t\tsup: ({ node, ...props }: any) => (\r\n\t\t\t\t\t\t<sup className='text-xs align-super' {...props} />\r\n\t\t\t\t\t),\r\n\t\t\t\t\t// Поддержка <details> и <summary>\r\n\t\t\t\t\tdetails: ({ node, ...props }: any) => (\r\n\t\t\t\t\t\t<details\r\n\t\t\t\t\t\t\tclassName='my-4 px-4 rounded-lg border border-border bg-muted/30'\r\n\t\t\t\t\t\t\t{...props}\r\n\t\t\t\t\t\t/>\r\n\t\t\t\t\t),\r\n\t\t\t\t\tsummary: ({ node, ...props }: any) => (\r\n\t\t\t\t\t\t<summary\r\n\t\t\t\t\t\t\tclassName='cursor-pointer px-4 py-2 font-semibold hover:bg-muted/50'\r\n\t\t\t\t\t\t\t{...props}\r\n\t\t\t\t\t\t/>\r\n\t\t\t\t\t),\r\n\t\t\t\t\t// Поддержка <dl>, <dt>, <dd> для списков определений\r\n\t\t\t\t\tdl: ({ node, ...props }: any) => (\r\n\t\t\t\t\t\t<dl className='my-4 space-y-2' {...props} />\r\n\t\t\t\t\t),\r\n\t\t\t\t\tdt: ({ node, ...props }: any) => (\r\n\t\t\t\t\t\t<dt className='font-semibold text-foreground' {...props} />\r\n\t\t\t\t\t),\r\n\t\t\t\t\tdd: ({ node, ...props }: any) => (\r\n\t\t\t\t\t\t<dd className='ml-6 text-muted-foreground' {...props} />\r\n\t\t\t\t\t),\r\n\t\t\t\t\t// Поддержка <video>\r\n\t\t\t\t\tvideo: ({ node, src, ...props }: any) => (\r\n\t\t\t\t\t\t<video\r\n\t\t\t\t\t\t\tsrc={src}\r\n\t\t\t\t\t\t\tcontrols\r\n\t\t\t\t\t\t\tclassName='my-4 w-full rounded-lg border border-border'\r\n\t\t\t\t\t\t\t{...props}\r\n\t\t\t\t\t\t/>\r\n\t\t\t\t\t),\r\n\t\t\t\t\t// Поддержка <iframe>\r\n\t\t\t\t\tiframe: ({ node, src, title, ...props }: any) => (\r\n\t\t\t\t\t\t<iframe\r\n\t\t\t\t\t\t\tsrc={src}\r\n\t\t\t\t\t\t\ttitle={title}\r\n\t\t\t\t\t\t\tclassName='my-4 w-full rounded-lg border border-border'\r\n\t\t\t\t\t\t\tallowFullScreen\r\n\t\t\t\t\t\t\t{...props}\r\n\t\t\t\t\t\t/>\r\n\t\t\t\t\t),\r\n\t\t\t\t\t// Поддержка <audio>\r\n\t\t\t\t\taudio: ({ node, src, ...props }: any) => (\r\n\t\t\t\t\t\t<audio src={src} controls className='my-4 w-full' {...props} />\r\n\t\t\t\t\t),\r\n\t\t\t\t\t// Поддержка <br>\r\n\t\t\t\t\tbr: ({ node, ...props }: any) => <br {...props} />,\r\n\t\t\t\t\t// Поддержка <div>\r\n\t\t\t\t\tdiv: ({ node, className, ...props }: any) => {\r\n\t\t\t\t\t\t// Сохраняем классы если они есть, добавляем базовые стили\r\n\t\t\t\t\t\tconst finalClassName = className ? `${className} my-2` : 'my-2'\r\n\t\t\t\t\t\treturn <div className={finalClassName} {...props} />\r\n\t\t\t\t\t},\r\n\t\t\t\t\t// Поддержка <span>\r\n\t\t\t\t\tspan: ({ node, ...props }: any) => <span {...props} />,\r\n\t\t\t\t\t// Поддержка <section>\r\n\t\t\t\t\tsection: ({ node, ...props }: any) => (\r\n\t\t\t\t\t\t<section className='my-6' {...props} />\r\n\t\t\t\t\t),\r\n\t\t\t\t\t// Поддержка <article>\r\n\t\t\t\t\tarticle: ({ node, ...props }: any) => (\r\n\t\t\t\t\t\t<article className='my-6' {...props} />\r\n\t\t\t\t\t),\r\n\t\t\t\t\t// Поддержка <aside>\r\n\t\t\t\t\taside: ({ node, ...props }: any) => (\r\n\t\t\t\t\t\t<aside\r\n\t\t\t\t\t\t\tclassName='my-4 border-l-4 border-primary pl-4 italic text-muted-foreground'\r\n\t\t\t\t\t\t\t{...props}\r\n\t\t\t\t\t\t/>\r\n\t\t\t\t\t),\r\n\t\t\t\t\t// Поддержка <address>\r\n\t\t\t\t\taddress: ({ node, ...props }: any) => (\r\n\t\t\t\t\t\t<address className='my-4 not-italic' {...props} />\r\n\t\t\t\t\t),\r\n\t\t\t\t\t// Поддержка <time>\r\n\t\t\t\t\ttime: ({ node, datetime, ...props }: any) => (\r\n\t\t\t\t\t\t<time\r\n\t\t\t\t\t\t\tdateTime={datetime}\r\n\t\t\t\t\t\t\tclassName='text-muted-foreground'\r\n\t\t\t\t\t\t\t{...props}\r\n\t\t\t\t\t\t/>\r\n\t\t\t\t\t),\r\n\t\t\t\t\t// Поддержка <small>\r\n\t\t\t\t\tsmall: ({ node, ...props }: any) => (\r\n\t\t\t\t\t\t<small className='text-sm text-muted-foreground' {...props} />\r\n\t\t\t\t\t),\r\n\t\t\t\t\t// Поддержка <ins> (вставленный текст)\r\n\t\t\t\t\tins: ({ node, ...props }: any) => (\r\n\t\t\t\t\t\t<ins\r\n\t\t\t\t\t\t\tclassName='no-underline bg-green-100 dark:bg-green-900/30 px-1 rounded'\r\n\t\t\t\t\t\t\t{...props}\r\n\t\t\t\t\t\t/>\r\n\t\t\t\t\t),\r\n\t\t\t\t\t// Поддержка <samp> (пример вывода)\r\n\t\t\t\t\tsamp: ({ node, ...props }: any) => (\r\n\t\t\t\t\t\t<samp\r\n\t\t\t\t\t\t\tclassName='font-mono bg-muted px-1 py-0.5 rounded text-sm'\r\n\t\t\t\t\t\t\t{...props}\r\n\t\t\t\t\t\t/>\r\n\t\t\t\t\t),\r\n\t\t\t\t\t// Поддержка <var> (переменные)\r\n\t\t\t\t\tvar: ({ node, ...props }: any) => (\r\n\t\t\t\t\t\t<var className='italic font-mono text-primary' {...props} />\r\n\t\t\t\t\t),\r\n\t\t\t\t\t// Поддержка <cite> (цитата источника)\r\n\t\t\t\t\tcite: ({ node, ...props }: any) => (\r\n\t\t\t\t\t\t<cite className='italic text-muted-foreground' {...props} />\r\n\t\t\t\t\t),\r\n\t\t\t\t\t// Поддержка <q> (короткая цитата)\r\n\t\t\t\t\tq: ({ node, ...props }: any) => (\r\n\t\t\t\t\t\t<q className='italic text-muted-foreground' {...props} />\r\n\t\t\t\t\t),\r\n\t\t\t\t\t// Поддержка <dfn> (определение термина)\r\n\t\t\t\t\tdfn: ({ node, ...props }: any) => (\r\n\t\t\t\t\t\t<dfn className='italic font-semibold' {...props} />\r\n\t\t\t\t\t),\r\n\t\t\t\t\t// Поддержка <bdi> и <bdo> (двунаправленный текст)\r\n\t\t\t\t\tbdi: ({ node, ...props }: any) => <bdi {...props} />,\r\n\t\t\t\t\tbdo: ({ node, dir, ...props }: any) => <bdo dir={dir} {...props} />,\r\n\t\t\t\t\t// Поддержка <wbr> (возможный перенос строки)\r\n\t\t\t\t\twbr: ({ node, ...props }: any) => <wbr {...props} />,\r\n\t\t\t\t\t// Поддержка <ruby>, <rt>, <rp> (руби-аннотации)\r\n\t\t\t\t\truby: ({ node, ...props }: any) => (\r\n\t\t\t\t\t\t<ruby className='text-base' {...props} />\r\n\t\t\t\t\t),\r\n\t\t\t\t\trt: ({ node, ...props }: any) => (\r\n\t\t\t\t\t\t<rt className='text-xs text-muted-foreground' {...props} />\r\n\t\t\t\t\t),\r\n\t\t\t\t\trp: ({ node, ...props }: any) => (\r\n\t\t\t\t\t\t<rp className='text-xs text-muted-foreground' {...props} />\r\n\t\t\t\t\t),\r\n\t\t\t\t\t// Поддержка <meter> (измеритель)\r\n\t\t\t\t\tmeter: ({ node, value, min, max, ...props }: any) => (\r\n\t\t\t\t\t\t<meter\r\n\t\t\t\t\t\t\tvalue={value}\r\n\t\t\t\t\t\t\tmin={min}\r\n\t\t\t\t\t\t\tmax={max}\r\n\t\t\t\t\t\t\tclassName='w-full h-2 rounded'\r\n\t\t\t\t\t\t\t{...props}\r\n\t\t\t\t\t\t/>\r\n\t\t\t\t\t),\r\n\t\t\t\t\t// Поддержка <progress> (прогресс-бар)\r\n\t\t\t\t\tprogress: ({ node, value, max, ...props }: any) => (\r\n\t\t\t\t\t\t<progress\r\n\t\t\t\t\t\t\tvalue={value}\r\n\t\t\t\t\t\t\tmax={max}\r\n\t\t\t\t\t\t\tclassName='w-full h-2 rounded'\r\n\t\t\t\t\t\t\t{...props}\r\n\t\t\t\t\t\t/>\r\n\t\t\t\t\t),\r\n\t\t\t\t}}\r\n\t\t\t>\r\n\t\t\t\t{content}\r\n\t\t\t</ReactMarkdown>\r\n\t\t\t\t</div>\r\n\t\t\t)}\r\n\t\t</div>\r\n\t)\r\n}\r\n"],"mappings":"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","names":["Check","Copy","React","ReactMarkdown","SyntaxHighlighter","oneDark","oneLight","rehypeRaw","rehypeSanitize","remarkGfm","jsx","jsxs","toAnchor","children","regex","spaceRegex","dashRegex","child","childProps","CodeBlock","codeString","language","copiedCode","setCopiedCode","codeTheme","codeId","Button","resolveImagePath","src","filePath","folderPath","cleanFolderPath","imageName","fileDir","imageParts","dirParts","part","resolvedPath","MarkdownViewer","onPageSelect","content","setContent","loading","setLoading","error","setError","isDark","setIsDark","checkTheme","observer","minDelay","startTime","params","response","data","elapsed","remainingDelay","resolve","err","Skeleton","node","props","anchor","childNode","ref","divProps","inline","className","match","alt","title","resolvedSrc","e","target","figure","errorDiv","href","base","relParts","baseParts","p","absolute","finalClassName","datetime","dir","value","min","max"]}
@@ -0,0 +1,4 @@
1
+ var J=Object.create;var E=Object.defineProperty;var V=Object.getOwnPropertyDescriptor;var U=Object.getOwnPropertyNames;var Y=Object.getPrototypeOf,q=Object.prototype.hasOwnProperty;var K=(e,n)=>{for(var o in n)E(e,o,{get:n[o],enumerable:!0})},k=(e,n,o,t)=>{if(n&&typeof n=="object"||typeof n=="function")for(let s of U(n))!q.call(e,s)&&s!==o&&E(e,s,{get:()=>n[s],enumerable:!(t=V(n,s))||t.enumerable});return e};var h=(e,n,o)=>(o=e!=null?J(Y(e)):{},k(n||!e||!e.__esModule?E(o,"default",{value:e,enumerable:!0}):o,e)),Q=e=>k(E({},"__esModule",{value:!0}),e);var oe={};K(oe,{ConsoleLogger:()=>v,FolderScanner:()=>I,dockRushScannerPlugin:()=>z});module.exports=Q(oe);var v=class{static log(n,o,t="tree"){switch(console.log(`
2
+ \u{1F4C1} ${o}
3
+ `),t){case"tree":this.logTree(n);break;case"list":this.logList(n);break;case"minimal":this.logMinimal(n);break}}static logTree(n){n.sort((o,t)=>o.type!==t.type?o.type==="directory"?-1:1:o.name.localeCompare(t.name)).forEach(o=>{let t=" ".repeat(o.depth),s=o.type==="directory"?"\u{1F4C1}":"\u{1F4C4}";console.log(`${t}${s} ${o.name}`)})}static logList(n){n.forEach((o,t)=>{let s=o.type==="directory"?"\u{1F4C1}":"\u{1F4C4}";console.log(`${t+1}. ${s} ${o.relativePath}`)})}static logMinimal(n){let o=n.filter(s=>s.type==="directory").length,t=n.length-o;console.log(`\u{1F4C1} ${o} directories, \u{1F4C4} ${t} files`)}};var T=h(require("fast-glob")),P=h(require("path"));function X(e){let n=/^\d+\.\d+\.\d+$/,o=new Set;for(let t of e)t.type==="directory"&&n.test(t.name)&&o.add(t.name);return Array.from(o).sort((t,s)=>{let a=t.split(".").map(Number),r=s.split(".").map(Number);for(let i=0;i<Math.max(a.length,r.length);i++){let d=a[i]??0,b=r[i]??0;if(d!==b)return b-d}return 0})}var I=class{async scan(n,o={}){try{let s=(await(0,T.default)("**/*",{cwd:n,onlyFiles:!1,stats:!0,absolute:!0,deep:o.deep??1/0,ignore:o.ignore,dot:!0})).map(r=>{let i=P.relative(n,r.path),d="isDirectory"in r.stats?r.stats.isDirectory():!1;return{name:P.basename(r.path),path:r.path,relativePath:i,type:d?"directory":"file",size:r.stats?.size,extension:d===!1?P.extname(r.path).toLowerCase().slice(1):void 0,depth:i.split(P.sep).length-1}}),a=X(s);return{success:!0,files:s,versions:a}}catch(t){return{success:!1,files:[],versions:[],error:t instanceof Error?t.message:"Unknown error"}}}};var x=h(require("fs/promises")),u=h(require("path"));var D=h(require("gray-matter")),_=h(require("fs/promises")),p=h(require("path"));var Z=h(require("gray-matter"));function N(e){return{title:typeof e.title=="string"?e.title:void 0,order:typeof e.order=="number"?e.order:typeof e.order=="string"?parseInt(e.order,10):void 0,icon:typeof e.icon=="string"?e.icon:void 0,hidden:typeof e.hidden=="boolean"?e.hidden:e.hidden==="true"?!0:void 0,searchable:typeof e.searchable=="boolean"?e.searchable:e.searchable==="false"?!1:void 0,lang:typeof e.lang=="string"?e.lang:void 0,tags:Array.isArray(e.tags)?e.tags.filter(n=>typeof n=="string"):void 0,layout:typeof e.layout=="string"?e.layout:void 0}}function M(e){return{title:typeof e.title=="string"?e.title:void 0,order:typeof e.order=="number"?e.order:typeof e.order=="string"?parseInt(e.order,10):void 0,icon:typeof e.icon=="string"?e.icon:void 0,hidden:typeof e.hidden=="boolean"?e.hidden:e.hidden==="true"?!0:void 0,searchable:typeof e.searchable=="boolean"?e.searchable:e.searchable==="false"?!1:void 0,dropdown:typeof e.dropdown=="string"&&["open","collapsed","always-open"].includes(e.dropdown)?e.dropdown:void 0}}function A(e){return{title:typeof e.title=="string"?e.title:void 0,order:typeof e.order=="number"?e.order:typeof e.order=="string"?parseInt(e.order,10):void 0,icon:typeof e.icon=="string"?e.icon:void 0,hidden:typeof e.hidden=="boolean"?e.hidden:e.hidden==="true"?!0:void 0,searchable:typeof e.searchable=="boolean"?e.searchable:e.searchable==="false"?!1:void 0,description:typeof e.description=="string"?e.description:void 0}}function B(e,n){return{type:"button",variant:typeof e.variant=="string"&&["link","page"].includes(e.variant)?e.variant:"page",title:typeof e.title=="string"?e.title:n,order:typeof e.order=="number"?e.order:typeof e.order=="string"?parseInt(e.order,10):void 0,icon:typeof e.icon=="string"?e.icon:void 0,hidden:typeof e.hidden=="boolean"?e.hidden:e.hidden==="true"?!0:void 0,searchable:typeof e.searchable=="boolean"?e.searchable===!1:(e.searchable==="false",!1),url:typeof e.url=="string"?e.url:void 0,style:typeof e.style=="string"&&["primary","secondary","ghost"].includes(e.style)?e.style:void 0,target:typeof e.target=="string"&&["_blank","_self"].includes(e.target)?e.target:void 0,position:typeof e.position=="string"&&["sidebar","header"].includes(e.position)?e.position:"sidebar"}}var L={dropdown:/^dropdown-settings\.md$/i,group:/^group-settings\.md$/i},ee=/\.button\.md$/;async function te(e){try{let n=await _.default.readFile(e,"utf-8"),{data:o={},content:t}=(0,D.default)(n);if(ee.test(p.default.basename(e))){let a=p.default.basename(e,".button.md").replace(/[-_]/g," ");return{frontmatter:B(o,a),content:t}}return{frontmatter:N(o),content:t}}catch{return{frontmatter:null,content:""}}}async function j(e,n){let o=n==="dropdown"?"dropdown-settings.md":"group-settings.md",t=p.default.join(e,o);try{let s=await _.default.readFile(t,"utf-8"),{data:a}=(0,D.default)(s);return n==="dropdown"?M(a):A(a)}catch{return null}}function ne(e,n){let o=n.replace(/\\/g,"/");for(let t of e)if(!(t.type!=="file"||p.default.dirname(t.relativePath).replace(/\\/g,"/")!==o)){if(L.dropdown.test(t.name))return{type:"dropdown",file:t};if(L.group.test(t.name))return{type:"group",file:t}}return{type:null,file:null}}async function G(e,n){let o=new Map;for(let s of e){if(s.type!=="file"||s.extension!=="md")continue;let a=p.default.isAbsolute(s.path)?s.path:p.default.join(n,s.path);try{let{frontmatter:r}=await te(a);r&&o.set(s.relativePath,r)}catch{}}let t=new Set;for(let s of e){if(s.type!=="directory")continue;let a=s.relativePath.replace(/\\/g,"/");if(t.has(a))continue;let r=ne(e,a);if(r.file){if(r.type==="dropdown"){let i=p.default.isAbsolute(s.path)?s.path:p.default.join(n,s.path),d=await j(i,"dropdown");d&&o.set(a,d)}else if(r.type==="group"){let i=p.default.isAbsolute(s.path)?s.path:p.default.join(n,s.path),d=await j(i,"group");d&&o.set(a,d)}}t.add(a)}return o}function W(e,n){e.middlewares.use(async(o,t,s)=>{try{if(!o.url)return s();let a=o.url.startsWith("/api/dock-rush-scan"),r=o.url.startsWith("/api/dock-rush-markdown");if(!a&&!r)return s();let i=new URL(o.url,`http://${o.headers.host||"localhost"}`);if(r){let d=i.searchParams.get("filePath"),b=i.searchParams.get("folderPath")||"docs";if(!d){t.statusCode=400,t.setHeader("Content-Type","application/json"),t.end(JSON.stringify({success:!1,error:"filePath parameter is required"}));return}let S=d.replaceAll("\\","/"),O=n.root??e.config?.root??process.cwd(),l;u.default.isAbsolute(S)?l=S:l=u.default.resolve(O,b,S);let w=u.default.resolve(O,b),$=u.default.normalize(l),y=u.default.normalize(w);if(!$.startsWith(y)){t.statusCode=403,t.setHeader("Content-Type","application/json"),t.end(JSON.stringify({success:!1,error:"Access denied: file outside docs folder"}));return}try{await x.default.access(l);let f=await x.default.readFile(l,"utf-8"),m=await x.default.stat(l),g=f,c={};if(f.match(/^---\s*\n([\s\S]*?)\n---\s*\n?/m)){let R=await import("gray-matter");try{c=R.default(f).data||{}}catch{}g=f.replace(/^---\s*\n[\s\S]*?\n---\s*\n?/m,"")}g=g.replace(/^\s*\n+/g,""),g=g.replace(/\n+\s*$/g,""),t.setHeader("Content-Type","application/json"),t.setHeader("Cache-Control","no-cache"),t.statusCode=200,t.end(JSON.stringify({success:!0,content:g,frontmatter:c,metadata:{path:l,relativePath:u.default.relative(w,l),name:u.default.basename(l),size:m.size,modified:m.mtime,created:m.birthtime}}))}catch(f){let m=f instanceof Error?f.message:"File not found";t.statusCode=404,t.setHeader("Content-Type","application/json"),t.end(JSON.stringify({success:!1,error:`File not found: ${m}`,requestedPath:l,normalizedPath:$,docsFolder:y}))}return}if(a){let d=i.searchParams.get("folderPath")??"",b=i.searchParams.get("console")==="true",S=i.searchParams.get("consoleFormat")||"tree",O=i.searchParams.getAll("ignore")||void 0;if(!d){t.statusCode=400,t.setHeader("Content-Type","application/json"),t.end(JSON.stringify({success:!1,error:"folderPath parameter is required",versions:[],files:[]}));return}let l=n.root??e.config?.root??process.cwd(),w=u.default.resolve(l,d),y=await new I().scan(w,{ignore:O});if(y.success){let f=await G(y.files,w),m=await Promise.all(y.files.map(async c=>{if(c.type==="file"&&c.extension==="md")try{let F=u.default.isAbsolute(c.path)?c.path:u.default.join(w,c.path),R=await x.default.readFile(F,"utf-8"),C=R;R.match(/^---\s*\n([\s\S]*?)\n---\s*\n?/m)&&(C=R.replace(/^---\s*\n[\s\S]*?\n---\s*\n?/m,""));let H=C.replace(/^\s*\n+/g,"").replace(/\n+\s*$/g,"").substring(0,1e4).toLowerCase();return{...c,content:H}}catch{return c}return c}));b&&v.log(m,w,S);let g={};for(let[c,F]of f.entries())g[c]=F;t.setHeader("Content-Type","application/json"),t.statusCode=200,t.end(JSON.stringify({success:!0,versions:y.versions,files:m,metadata:g}))}else t.setHeader("Content-Type","application/json"),t.statusCode=500,t.end(JSON.stringify({success:!1,error:y.error,versions:[],files:[]}))}}catch{t.headersSent||(t.statusCode=500,t.setHeader("Content-Type","application/json"),t.end(JSON.stringify({success:!1,error:"Internal server error"})))}})}function z(e={}){return{name:"dock-rush-folder-scanner",configureServer(n){W(n,e)},configurePreviewServer(n){W(n,e)}}}0&&(module.exports={ConsoleLogger,FolderScanner,dockRushScannerPlugin});
4
+ //# sourceMappingURL=plugin.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/plugin.ts","../src/lib/scanningFolder/logger.ts","../src/lib/scanningFolder/scanner.ts","../src/lib/scanningFolder/vitePlugin.ts","../src/lib/scanningFolder/metadata-reader.ts","../src/lib/documentation/frontmatter-parser.ts"],"sourcesContent":["export * from './lib/scanningFolder'\r\nexport {\r\n\tdockRushScannerPlugin,\r\n\ttype DockRushScannerPluginOptions,\r\n} from './lib/scanningFolder/vitePlugin'\r\n","import { ConsoleFormat, FileInfo } from './types'\r\n\r\nexport class ConsoleLogger {\r\n\tstatic log(\r\n\t\tfiles: FileInfo[],\r\n\t\tfolderPath: string,\r\n\t\tformat: ConsoleFormat = 'tree'\r\n\t) {\r\n\t\tconsole.log(`\\n📁 ${folderPath}\\n`)\r\n\r\n\t\tswitch (format) {\r\n\t\t\tcase 'tree':\r\n\t\t\t\tthis.logTree(files)\r\n\t\t\t\tbreak\r\n\t\t\tcase 'list':\r\n\t\t\t\tthis.logList(files)\r\n\t\t\t\tbreak\r\n\t\t\tcase 'minimal':\r\n\t\t\t\tthis.logMinimal(files)\r\n\t\t\t\tbreak\r\n\t\t}\r\n\t}\r\n\r\n\tprivate static logTree(files: FileInfo[]) {\r\n\t\tfiles\r\n\t\t\t.sort((a, b) => {\r\n\t\t\t\tif (a.type !== b.type) return a.type === 'directory' ? -1 : 1\r\n\t\t\t\treturn a.name.localeCompare(b.name)\r\n\t\t\t})\r\n\t\t\t.forEach(file => {\r\n\t\t\t\tconst indent = ' '.repeat(file.depth)\r\n\t\t\t\tconst icon = file.type === 'directory' ? '📁' : '📄'\r\n\t\t\t\tconsole.log(`${indent}${icon} ${file.name}`)\r\n\t\t\t})\r\n\t}\r\n\r\n\tprivate static logList(files: FileInfo[]) {\r\n\t\tfiles.forEach((file, i) => {\r\n\t\t\tconst icon = file.type === 'directory' ? '📁' : '📄'\r\n\t\t\tconsole.log(`${i + 1}. ${icon} ${file.relativePath}`)\r\n\t\t})\r\n\t}\r\n\r\n\tprivate static logMinimal(files: FileInfo[]) {\r\n\t\tconst dirs = files.filter(f => f.type === 'directory').length\r\n\t\tconst fileCount = files.length - dirs\r\n\t\tconsole.log(`📁 ${dirs} directories, 📄 ${fileCount} files`)\r\n\t}\r\n}\r\n","import fg from 'fast-glob'\r\nimport * as path from 'node:path'\r\nimport { FileInfo, ScanOptions, ScanResult } from './types'\r\n\r\n/**\r\n * Extracts version numbers from directory names that match semantic version patterns\r\n */\r\nfunction extractVersions(files: FileInfo[]): string[] {\r\n\tconst versionPattern = /^\\d+\\.\\d+\\.\\d+$/\r\n\tconst versions = new Set<string>()\r\n\r\n\tfor (const file of files) {\r\n\t\tif (file.type === 'directory' && versionPattern.test(file.name)) {\r\n\t\t\tversions.add(file.name)\r\n\t\t}\r\n\t}\r\n\r\n\treturn Array.from(versions).sort((a, b) => {\r\n\t\tconst aParts = a.split('.').map(Number)\r\n\t\tconst bParts = b.split('.').map(Number)\r\n\r\n\t\tfor (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {\r\n\t\t\tconst aPart = aParts[i] ?? 0\r\n\t\t\tconst bPart = bParts[i] ?? 0\r\n\t\t\tif (aPart !== bPart) {\r\n\t\t\t\treturn bPart - aPart // Sort descending (newest first)\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn 0\r\n\t})\r\n}\r\n\r\nexport class FolderScanner {\r\n\tasync scan(\r\n\t\tfolderPath: string,\r\n\t\toptions: ScanOptions = {}\r\n\t): Promise<ScanResult> {\r\n\t\ttry {\r\n\t\t\tconst entries = await fg('**/*', {\r\n\t\t\t\tcwd: folderPath,\r\n\t\t\t\tonlyFiles: false,\r\n\t\t\t\tstats: true,\r\n\t\t\t\tabsolute: true,\r\n\t\t\t\tdeep: options.deep ?? Infinity,\r\n\t\t\t\tignore: options.ignore,\r\n\t\t\t\tdot: true,\r\n\t\t\t})\r\n\r\n\t\t\tconst files = entries.map(entry => {\r\n\t\t\t\tconst relativePath = path.relative(folderPath, entry.path)\r\n\t\t\t\tconst isDirectory =\r\n\t\t\t\t\t'isDirectory' in entry.stats ? entry.stats.isDirectory() : false\r\n\r\n\t\t\t\treturn {\r\n\t\t\t\t\tname: path.basename(entry.path),\r\n\t\t\t\t\tpath: entry.path,\r\n\t\t\t\t\trelativePath,\r\n\t\t\t\t\ttype: isDirectory ? 'directory' : 'file',\r\n\t\t\t\t\tsize: entry.stats?.size,\r\n\t\t\t\t\textension:\r\n\t\t\t\t\t\tisDirectory === false\r\n\t\t\t\t\t\t\t? path.extname(entry.path).toLowerCase().slice(1)\r\n\t\t\t\t\t\t\t: undefined,\r\n\t\t\t\t\tdepth: relativePath.split(path.sep).length - 1,\r\n\t\t\t\t}\r\n\t\t\t}) as FileInfo[]\r\n\r\n\t\t\tconst versions = extractVersions(files)\r\n\r\n\t\t\treturn { success: true, files, versions: versions }\r\n\t\t} catch (error) {\r\n\t\t\treturn {\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\tfiles: [],\r\n\t\t\t\tversions: [],\r\n\t\t\t\terror: error instanceof Error ? error.message : 'Unknown error',\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n","import fs from 'node:fs/promises'\nimport path from 'node:path'\nimport { ConsoleLogger } from './logger'\nimport { readAllMetadata } from './metadata-reader'\nimport { FolderScanner } from './scanner'\nimport type { ConsoleFormat, FileInfo } from './types'\n\nexport interface DockRushScannerPluginOptions {\n\t/**\n\t * HTTP‑роут, по которому будет висеть endpoint сканера.\n\t * По умолчанию: `/api/dock-rush-scan`\n\t */\n\troute?: string\n\n\t/**\n\t * Корневая папка для разрешения относительного `folderPath`.\n\t * По умолчанию — `server.config.root` или `process.cwd()`.\n\t */\n\troot?: string\n}\n\n/**\n * Vite‑плагин, который обрабатывает запросы от клиентского компонента\n * и запускает `FolderScanner` на стороне Node.\n */\n// Общая функция для настройки middleware\nfunction setupMiddleware(server: any, options: DockRushScannerPluginOptions) {\n\t// ОБЪЕДИНЕННЫЙ обработчик для всех API\n\tserver.middlewares.use(async (req: any, res: any, next: any) => {\n\t\ttry {\n\t\t\tif (!req.url) return next()\n\n\t\t\t// Маршруты API\n\t\t\tconst isScanRequest = req.url.startsWith('/api/dock-rush-scan')\n\t\t\tconst isMarkdownRequest = req.url.startsWith('/api/dock-rush-markdown')\n\n\t\t\tif (!isScanRequest && !isMarkdownRequest) {\n\t\t\t\treturn next()\n\t\t\t}\n\n\t\t\tconst url = new URL(req.url, `http://${req.headers.host || 'localhost'}`)\n\n\t\t\t// MARKDOWN ENDPOINT\n\t\t\tif (isMarkdownRequest) {\n\t\t\t\tconst rawFilePath = url.searchParams.get('filePath')\n\t\t\t\tconst folderPath = url.searchParams.get('folderPath') || 'docs'\n\n\t\t\t\tif (!rawFilePath) {\n\t\t\t\t\tres.statusCode = 400\n\t\t\t\t\tres.setHeader('Content-Type', 'application/json')\n\t\t\t\t\tres.end(\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\terror: 'filePath parameter is required',\n\t\t\t\t\t\t})\n\t\t\t\t\t)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\t// Нормализуем путь: заменяем обратные слэши на прямые\n\t\t\t\tconst filePath = rawFilePath.replaceAll('\\\\', '/')\n\n\t\t\t\tconst root = options.root ?? server.config?.root ?? process.cwd()\n\n\t\t\t\t// ДВА варианта пути:\n\t\t\t\t// 1. Если filePath абсолютный (содержит полный путь)\n\t\t\t\tlet absoluteFilePath: string\n\n\t\t\t\tif (path.isAbsolute(filePath)) {\n\t\t\t\t\t// Это уже абсолютный путь от сканера\n\t\t\t\t\tabsoluteFilePath = filePath\n\t\t\t\t} else {\n\t\t\t\t\t// Это относительный путь\n\t\t\t\t\tabsoluteFilePath = path.resolve(root, folderPath, filePath)\n\t\t\t\t}\n\n\t\t\t\t// Проверка безопасности\n\t\t\t\tconst docsFolder = path.resolve(root, folderPath)\n\t\t\t\tconst normalizedTargetFile = path.normalize(absoluteFilePath)\n\t\t\t\tconst normalizedDocsFolder = path.normalize(docsFolder)\n\n\t\t\t\tif (!normalizedTargetFile.startsWith(normalizedDocsFolder)) {\n\t\t\t\t\tres.statusCode = 403\n\t\t\t\t\tres.setHeader('Content-Type', 'application/json')\n\t\t\t\t\tres.end(\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\terror: 'Access denied: file outside docs folder',\n\t\t\t\t\t\t})\n\t\t\t\t\t)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\t// Проверяем существование файла\n\t\t\t\t\tawait fs.access(absoluteFilePath)\n\n\t\t\t\t\tconst rawContent = await fs.readFile(absoluteFilePath, 'utf-8')\n\t\t\t\t\tconst stats = await fs.stat(absoluteFilePath)\n\n\t\t\t\t\t// Удаляем frontmatter блок от --- до --- включительно\n\t\t\t\t\tlet cleanContent = rawContent\n\t\t\t\t\tlet frontmatterData: Record<string, unknown> = {}\n\n\t\t\t\t\t// Проверяем, есть ли frontmatter в начале файла\n\t\t\t\t\tconst frontmatterMatch = rawContent.match(\n\t\t\t\t\t\t/^---\\s*\\n([\\s\\S]*?)\\n---\\s*\\n?/m\n\t\t\t\t\t)\n\n\t\t\t\t\tif (frontmatterMatch) {\n\t\t\t\t\t\t// Извлекаем frontmatter для парсинга\n\t\t\t\t\t\tconst matter = await import('gray-matter')\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst parsed = matter.default(rawContent)\n\t\t\t\t\t\t\tfrontmatterData = parsed.data || {}\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t// Если парсинг не удался, frontmatterData остается пустым\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Удаляем весь frontmatter блок (от первого --- до второго --- включительно)\n\t\t\t\t\t\tcleanContent = rawContent.replace(\n\t\t\t\t\t\t\t/^---\\s*\\n[\\s\\S]*?\\n---\\s*\\n?/m,\n\t\t\t\t\t\t\t''\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\n\t\t\t\t\t// Удаляем все пустые строки в начале (после frontmatter)\n\t\t\t\t\tcleanContent = cleanContent.replace(/^\\s*\\n+/g, '')\n\t\t\t\t\t// Удаляем все пустые строки в конце\n\t\t\t\t\tcleanContent = cleanContent.replace(/\\n+\\s*$/g, '')\n\n\t\t\t\t\tres.setHeader('Content-Type', 'application/json')\n\t\t\t\t\tres.setHeader('Cache-Control', 'no-cache')\n\t\t\t\t\tres.statusCode = 200\n\t\t\t\t\tres.end(\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\t\tcontent: cleanContent,\n\t\t\t\t\t\t\tfrontmatter: frontmatterData,\n\t\t\t\t\t\t\tmetadata: {\n\t\t\t\t\t\t\t\tpath: absoluteFilePath,\n\t\t\t\t\t\t\t\trelativePath: path.relative(docsFolder, absoluteFilePath),\n\t\t\t\t\t\t\t\tname: path.basename(absoluteFilePath),\n\t\t\t\t\t\t\t\tsize: stats.size,\n\t\t\t\t\t\t\t\tmodified: stats.mtime,\n\t\t\t\t\t\t\t\tcreated: stats.birthtime,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t})\n\t\t\t\t\t)\n\t\t\t\t} catch (fileError) {\n\t\t\t\t\tconst errorMessage =\n\t\t\t\t\t\tfileError instanceof Error ? fileError.message : 'File not found'\n\t\t\t\t\tres.statusCode = 404\n\t\t\t\t\tres.setHeader('Content-Type', 'application/json')\n\t\t\t\t\tres.end(\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\terror: `File not found: ${errorMessage}`,\n\t\t\t\t\t\t\trequestedPath: absoluteFilePath,\n\t\t\t\t\t\t\tnormalizedPath: normalizedTargetFile,\n\t\t\t\t\t\t\tdocsFolder: normalizedDocsFolder,\n\t\t\t\t\t\t})\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// SCAN ENDPOINT\n\t\t\tif (isScanRequest) {\n\t\t\t\tconst folderPath = url.searchParams.get('folderPath') ?? ''\n\t\t\t\tconst consoleEnabled = url.searchParams.get('console') === 'true'\n\t\t\t\tconst consoleFormat = (url.searchParams.get('consoleFormat') ||\n\t\t\t\t\t'tree') as ConsoleFormat\n\t\t\t\tconst ignore = url.searchParams.getAll('ignore') || undefined\n\n\t\t\t\tif (!folderPath) {\n\t\t\t\t\tres.statusCode = 400\n\t\t\t\t\tres.setHeader('Content-Type', 'application/json')\n\t\t\t\t\tres.end(\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\terror: 'folderPath parameter is required',\n\t\t\t\t\t\t\tversions: [],\n\t\t\t\t\t\t\tfiles: [],\n\t\t\t\t\t\t})\n\t\t\t\t\t)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconst root = options.root ?? server.config?.root ?? process.cwd()\n\t\t\t\tconst targetFolder = path.resolve(root, folderPath)\n\n\t\t\t\tconst scanner = new FolderScanner()\n\t\t\t\tconst result = await scanner.scan(targetFolder, { ignore })\n\n\t\t\t\tif (result.success) {\n\t\t\t\t\t// Читаем метаданные для всех файлов\n\t\t\t\t\tconst metadata = await readAllMetadata(result.files, targetFolder)\n\n\t\t\t\t\t// Читаем содержимое markdown файлов для поиска\n\t\t\t\t\tconst filesWithContent: FileInfo[] = await Promise.all(\n\t\t\t\t\t\tresult.files.map(async (file): Promise<FileInfo> => {\n\t\t\t\t\t\t\t// Читаем содержимое только для markdown файлов\n\t\t\t\t\t\t\tif (file.type === 'file' && file.extension === 'md') {\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tconst absolutePath = path.isAbsolute(file.path)\n\t\t\t\t\t\t\t\t\t\t? file.path\n\t\t\t\t\t\t\t\t\t\t: path.join(targetFolder, file.path)\n\n\t\t\t\t\t\t\t\t\tconst rawContent = await fs.readFile(absolutePath, 'utf-8')\n\n\t\t\t\t\t\t\t\t\t// Удаляем frontmatter и ограничиваем размер для индексации\n\t\t\t\t\t\t\t\t\tlet content = rawContent\n\t\t\t\t\t\t\t\t\tconst frontmatterMatch = rawContent.match(\n\t\t\t\t\t\t\t\t\t\t/^---\\s*\\n([\\s\\S]*?)\\n---\\s*\\n?/m\n\t\t\t\t\t\t\t\t\t)\n\n\t\t\t\t\t\t\t\t\tif (frontmatterMatch) {\n\t\t\t\t\t\t\t\t\t\tcontent = rawContent.replace(\n\t\t\t\t\t\t\t\t\t\t\t/^---\\s*\\n[\\s\\S]*?\\n---\\s*\\n?/m,\n\t\t\t\t\t\t\t\t\t\t\t''\n\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t// Ограничиваем размер содержимого для индексации (первые 10000 символов)\n\t\t\t\t\t\t\t\t\tconst indexedContent = content\n\t\t\t\t\t\t\t\t\t\t.replace(/^\\s*\\n+/g, '') // Убираем пустые строки в начале\n\t\t\t\t\t\t\t\t\t\t.replace(/\\n+\\s*$/g, '') // Убираем пустые строки в конце\n\t\t\t\t\t\t\t\t\t\t.substring(0, 10000)\n\t\t\t\t\t\t\t\t\t\t.toLowerCase()\n\n\t\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\t\t...file,\n\t\t\t\t\t\t\t\t\t\tcontent: indexedContent,\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t\t\treturn file\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn file\n\t\t\t\t\t\t})\n\t\t\t\t\t)\n\n\t\t\t\t\t// Log to console if enabled\n\t\t\t\t\tif (consoleEnabled) {\n\t\t\t\t\t\tConsoleLogger.log(filesWithContent, targetFolder, consoleFormat)\n\t\t\t\t\t}\n\n\t\t\t\t\t// Преобразуем Map в объект для JSON\n\t\t\t\t\tconst metadataObj: Record<string, unknown> = {}\n\t\t\t\t\tfor (const [key, value] of metadata.entries()) {\n\t\t\t\t\t\tmetadataObj[key] = value\n\t\t\t\t\t}\n\n\t\t\t\t\t// Return JSON response\n\t\t\t\t\tres.setHeader('Content-Type', 'application/json')\n\t\t\t\t\tres.statusCode = 200\n\t\t\t\t\tres.end(\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\t\tversions: result.versions,\n\t\t\t\t\t\t\tfiles: filesWithContent,\n\t\t\t\t\t\t\tmetadata: metadataObj,\n\t\t\t\t\t\t})\n\t\t\t\t\t)\n\t\t\t\t} else {\n\t\t\t\t\tres.setHeader('Content-Type', 'application/json')\n\t\t\t\t\tres.statusCode = 500\n\t\t\t\t\tres.end(\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\terror: result.error,\n\t\t\t\t\t\t\tversions: [],\n\t\t\t\t\t\t\tfiles: [],\n\t\t\t\t\t\t})\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (!res.headersSent) {\n\t\t\t\tres.statusCode = 500\n\t\t\t\tres.setHeader('Content-Type', 'application/json')\n\t\t\t\tres.end(\n\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\terror: 'Internal server error',\n\t\t\t\t\t})\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t})\n}\n\nexport function dockRushScannerPlugin(\n\toptions: DockRushScannerPluginOptions = {}\n) {\n\treturn {\n\t\tname: 'dock-rush-folder-scanner',\n\t\tconfigureServer(server: any) {\n\t\t\tsetupMiddleware(server, options)\n\t\t},\n\t\tconfigurePreviewServer(server: any) {\n\t\t\tsetupMiddleware(server, options)\n\t\t},\n\t}\n}\n","import matter from 'gray-matter'\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport {\n\tparseButtonSettings,\n\tparseDropdownSettings,\n\tparseGroupSettings,\n\tparsePageFrontmatter,\n} from '../documentation/frontmatter-parser'\nimport type {\n\tButtonSettings,\n\tDropdownSettings,\n\tGroupSettings,\n\tPageFrontmatter,\n} from '../documentation/types'\nimport type { FileInfo } from './types'\n\nconst SETTINGS_PATTERNS = {\n\tdropdown: /^dropdown-settings\\.md$/i,\n\tgroup: /^group-settings\\.md$/i,\n}\n\nconst BUTTON_PATTERN = /\\.button\\.md$/\n\n/**\n * Читает метаданные из markdown файла\n */\nexport async function readFileMetadata(filePath: string) {\n\ttry {\n\t\tconst content = await fs.readFile(filePath, 'utf-8')\n\n\t\t/* ➜ добавляем дефолт {} если фронт-маттер отсутствует */\n\t\tconst { data: rawFrontmatter = {}, content: markdownContent } =\n\t\t\tmatter(content)\n\n\t\tif (BUTTON_PATTERN.test(path.basename(filePath))) {\n\t\t\tconst defaultTitle = path\n\t\t\t\t.basename(filePath, '.button.md')\n\t\t\t\t.replace(/[-_]/g, ' ')\n\t\t\tconst buttonSettings = parseButtonSettings(rawFrontmatter, defaultTitle)\n\t\t\treturn { frontmatter: buttonSettings, content: markdownContent }\n\t\t}\n\n\t\tconst pageFrontmatter = parsePageFrontmatter(rawFrontmatter)\n\t\treturn { frontmatter: pageFrontmatter, content: markdownContent }\n\t} catch (error) {\n\t\treturn { frontmatter: null, content: '' }\n\t}\n}\n\n/**\n * Читает settings файл для папки (dropdown или group)\n */\nexport async function readSettingsFile(\n\tfolderPath: string,\n\ttype: 'dropdown' | 'group'\n): Promise<DropdownSettings | GroupSettings | null> {\n\tconst settingsFileName =\n\t\ttype === 'dropdown' ? 'dropdown-settings.md' : 'group-settings.md'\n\tconst settingsPath = path.join(folderPath, settingsFileName)\n\n\ttry {\n\t\tconst content = await fs.readFile(settingsPath, 'utf-8')\n\t\tconst { data } = matter(content)\n\n\t\tif (type === 'dropdown') {\n\t\t\treturn parseDropdownSettings(data)\n\t\t} else {\n\t\t\treturn parseGroupSettings(data)\n\t\t}\n\t} catch (error) {\n\t\t// Settings файл не найден - это нормально\n\t\treturn null\n\t}\n}\n\n/**\n * Находит settings файл в папке\n */\nexport function findSettingsFile(\n\tfiles: FileInfo[],\n\tfolderRelativePath: string\n): {\n\ttype: 'dropdown' | 'group' | null\n\tfile: FileInfo | null\n} {\n\tconst normalizedFolderPath = folderRelativePath.replace(/\\\\/g, '/')\n\n\tfor (const file of files) {\n\t\tif (file.type !== 'file') continue\n\n\t\tconst fileDir = path.dirname(file.relativePath).replace(/\\\\/g, '/')\n\t\tif (fileDir !== normalizedFolderPath) continue\n\n\t\tif (SETTINGS_PATTERNS.dropdown.test(file.name)) {\n\t\t\treturn { type: 'dropdown', file }\n\t\t}\n\t\tif (SETTINGS_PATTERNS.group.test(file.name)) {\n\t\t\treturn { type: 'group', file }\n\t\t}\n\t}\n\n\treturn { type: null, file: null }\n}\n\n/**\n * Читает метаданные для всех файлов в структуре\n */\nexport async function readAllMetadata(\n\tfiles: FileInfo[],\n\trootPath: string\n): Promise<\n\tMap<\n\t\tstring,\n\t\tPageFrontmatter | ButtonSettings | DropdownSettings | GroupSettings\n\t>\n> {\n\tconst metadataMap = new Map<\n\t\tstring,\n\t\tPageFrontmatter | ButtonSettings | DropdownSettings | GroupSettings\n\t>()\n\n\t// Читаем метаданные для всех markdown файлов\n\tfor (const file of files) {\n\t\tif (file.type !== 'file' || file.extension !== 'md') continue\n\n\t\tconst absolutePath = path.isAbsolute(file.path)\n\t\t\t? file.path\n\t\t\t: path.join(rootPath, file.path)\n\n\t\ttry {\n\t\t\tconst { frontmatter } = await readFileMetadata(absolutePath)\n\n\t\t\tif (frontmatter) {\n\t\t\t\tmetadataMap.set(file.relativePath, frontmatter)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Игнорируем ошибки чтения файлов\n\t\t}\n\t}\n\n\t// Читаем settings файлы для папок\n\tconst processedFolders = new Set<string>()\n\tfor (const file of files) {\n\t\tif (file.type !== 'directory') continue\n\n\t\tconst folderRelativePath = file.relativePath.replace(/\\\\/g, '/')\n\t\tif (processedFolders.has(folderRelativePath)) continue\n\n\t\tconst settingsFile = findSettingsFile(files, folderRelativePath)\n\t\tif (settingsFile.file) {\n\t\t\tif (settingsFile.type === 'dropdown') {\n\t\t\t\tconst absoluteFolderPath = path.isAbsolute(file.path)\n\t\t\t\t\t? file.path\n\t\t\t\t\t: path.join(rootPath, file.path)\n\t\t\t\tconst settings = await readSettingsFile(absoluteFolderPath, 'dropdown')\n\t\t\t\tif (settings) {\n\t\t\t\t\tmetadataMap.set(folderRelativePath, settings)\n\t\t\t\t}\n\t\t\t} else if (settingsFile.type === 'group') {\n\t\t\t\tconst absoluteFolderPath = path.isAbsolute(file.path)\n\t\t\t\t\t? file.path\n\t\t\t\t\t: path.join(rootPath, file.path)\n\t\t\t\tconst settings = await readSettingsFile(absoluteFolderPath, 'group')\n\t\t\t\tif (settings) {\n\t\t\t\t\tmetadataMap.set(folderRelativePath, settings)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprocessedFolders.add(folderRelativePath)\n\t}\n\n\treturn metadataMap\n}\n","import matter from 'gray-matter'\r\nimport { errorBuilder } from '../errorBuilder/error-builder'\r\nimport type {\r\n\tBaseMeta,\r\n\tButtonSettings,\r\n\tDropdownSettings,\r\n\tGroupSettings,\r\n\tLanguageSettings,\r\n\tPageFrontmatter,\r\n\tVersionSettings,\r\n} from './types'\r\n\r\n/**\r\n * Парсит frontmatter из markdown файла\r\n */\r\nexport function parseFrontmatter(content: string): {\r\n\tfrontmatter: Record<string, unknown>\r\n\tcontent: string\r\n} {\r\n\ttry {\r\n\t\tconst parsed = matter(content)\r\n\t\treturn {\r\n\t\t\tfrontmatter: parsed.data || {},\r\n\t\t\tcontent: parsed.content,\r\n\t\t}\r\n\t} catch (error) {\r\n\t\t// Если frontmatter некорректный, возвращаем весь контент как есть\r\n\t\terrorBuilder([], {\r\n\t\t\ttype: 'VERSION_FORMAT_ERROR',\r\n\t\t\treason: `frontmatter некорректный`,\r\n\t\t\tfix: `1`,\r\n\t\t})\r\n\t\treturn {\r\n\t\t\tfrontmatter: {},\r\n\t\t\tcontent,\r\n\t\t}\r\n\t}\r\n}\r\n\r\n/**\r\n * Парсит метаданные страницы из frontmatter\r\n */\r\nexport function parsePageFrontmatter(\r\n\tfrontmatter: Record<string, unknown>\r\n): PageFrontmatter {\r\n\treturn {\r\n\t\ttitle:\r\n\t\t\ttypeof frontmatter.title === 'string' ? frontmatter.title : undefined,\r\n\t\torder:\r\n\t\t\ttypeof frontmatter.order === 'number'\r\n\t\t\t\t? frontmatter.order\r\n\t\t\t\t: typeof frontmatter.order === 'string'\r\n\t\t\t\t? parseInt(frontmatter.order, 10)\r\n\t\t\t\t: undefined,\r\n\t\ticon: typeof frontmatter.icon === 'string' ? frontmatter.icon : undefined,\r\n\t\thidden:\r\n\t\t\ttypeof frontmatter.hidden === 'boolean'\r\n\t\t\t\t? frontmatter.hidden\r\n\t\t\t\t: frontmatter.hidden === 'true'\r\n\t\t\t\t? true\r\n\t\t\t\t: undefined,\r\n\t\tsearchable:\r\n\t\t\ttypeof frontmatter.searchable === 'boolean'\r\n\t\t\t\t? frontmatter.searchable\r\n\t\t\t\t: frontmatter.searchable === 'false'\r\n\t\t\t\t? false\r\n\t\t\t\t: undefined,\r\n\t\tlang: typeof frontmatter.lang === 'string' ? frontmatter.lang : undefined,\r\n\t\ttags: Array.isArray(frontmatter.tags)\r\n\t\t\t? frontmatter.tags.filter((t): t is string => typeof t === 'string')\r\n\t\t\t: undefined,\r\n\t\tlayout:\r\n\t\t\ttypeof frontmatter.layout === 'string' ? frontmatter.layout : undefined,\r\n\t}\r\n}\r\n\r\n/**\r\n * Парсит настройки dropdown из frontmatter\r\n */\r\nexport function parseDropdownSettings(\r\n\tfrontmatter: Record<string, unknown>\r\n): DropdownSettings {\r\n\treturn {\r\n\t\ttitle:\r\n\t\t\ttypeof frontmatter.title === 'string' ? frontmatter.title : undefined,\r\n\t\torder:\r\n\t\t\ttypeof frontmatter.order === 'number'\r\n\t\t\t\t? frontmatter.order\r\n\t\t\t\t: typeof frontmatter.order === 'string'\r\n\t\t\t\t? parseInt(frontmatter.order, 10)\r\n\t\t\t\t: undefined,\r\n\t\ticon: typeof frontmatter.icon === 'string' ? frontmatter.icon : undefined,\r\n\t\thidden:\r\n\t\t\ttypeof frontmatter.hidden === 'boolean'\r\n\t\t\t\t? frontmatter.hidden\r\n\t\t\t\t: frontmatter.hidden === 'true'\r\n\t\t\t\t? true\r\n\t\t\t\t: undefined,\r\n\t\tsearchable:\r\n\t\t\ttypeof frontmatter.searchable === 'boolean'\r\n\t\t\t\t? frontmatter.searchable\r\n\t\t\t\t: frontmatter.searchable === 'false'\r\n\t\t\t\t? false\r\n\t\t\t\t: undefined,\r\n\t\tdropdown:\r\n\t\t\ttypeof frontmatter.dropdown === 'string' &&\r\n\t\t\t['open', 'collapsed', 'always-open'].includes(frontmatter.dropdown)\r\n\t\t\t\t? (frontmatter.dropdown as 'open' | 'collapsed' | 'always-open')\r\n\t\t\t\t: undefined,\r\n\t}\r\n}\r\n\r\n/**\r\n * Парсит настройки group из frontmatter\r\n */\r\nexport function parseGroupSettings(\r\n\tfrontmatter: Record<string, unknown>\r\n): GroupSettings {\r\n\treturn {\r\n\t\ttitle:\r\n\t\t\ttypeof frontmatter.title === 'string' ? frontmatter.title : undefined,\r\n\t\torder:\r\n\t\t\ttypeof frontmatter.order === 'number'\r\n\t\t\t\t? frontmatter.order\r\n\t\t\t\t: typeof frontmatter.order === 'string'\r\n\t\t\t\t? parseInt(frontmatter.order, 10)\r\n\t\t\t\t: undefined,\r\n\t\ticon: typeof frontmatter.icon === 'string' ? frontmatter.icon : undefined,\r\n\t\thidden:\r\n\t\t\ttypeof frontmatter.hidden === 'boolean'\r\n\t\t\t\t? frontmatter.hidden\r\n\t\t\t\t: frontmatter.hidden === 'true'\r\n\t\t\t\t? true\r\n\t\t\t\t: undefined,\r\n\t\tsearchable:\r\n\t\t\ttypeof frontmatter.searchable === 'boolean'\r\n\t\t\t\t? frontmatter.searchable\r\n\t\t\t\t: frontmatter.searchable === 'false'\r\n\t\t\t\t? false\r\n\t\t\t\t: undefined,\r\n\t\tdescription:\r\n\t\t\ttypeof frontmatter.description === 'string'\r\n\t\t\t\t? frontmatter.description\r\n\t\t\t\t: undefined,\r\n\t}\r\n}\r\n\r\n/**\r\n * Парсит настройки кнопки из frontmatter\r\n */\r\nexport function parseButtonSettings(\r\n\tfrontmatter: Record<string, unknown>,\r\n\tdefaultTitle: string\r\n): ButtonSettings {\r\n\treturn {\r\n\t\ttype: 'button',\r\n\t\tvariant:\r\n\t\t\ttypeof frontmatter.variant === 'string' &&\r\n\t\t\t['link', 'page'].includes(frontmatter.variant)\r\n\t\t\t\t? (frontmatter.variant as 'link' | 'page')\r\n\t\t\t\t: 'page',\r\n\t\ttitle:\r\n\t\t\ttypeof frontmatter.title === 'string' ? frontmatter.title : defaultTitle,\r\n\t\torder:\r\n\t\t\ttypeof frontmatter.order === 'number'\r\n\t\t\t\t? frontmatter.order\r\n\t\t\t\t: typeof frontmatter.order === 'string'\r\n\t\t\t\t? parseInt(frontmatter.order, 10)\r\n\t\t\t\t: undefined,\r\n\t\ticon: typeof frontmatter.icon === 'string' ? frontmatter.icon : undefined,\r\n\t\thidden:\r\n\t\t\ttypeof frontmatter.hidden === 'boolean'\r\n\t\t\t\t? frontmatter.hidden\r\n\t\t\t\t: frontmatter.hidden === 'true'\r\n\t\t\t\t? true\r\n\t\t\t\t: undefined,\r\n\t\tsearchable:\r\n\t\t\ttypeof frontmatter.searchable === 'boolean'\r\n\t\t\t\t? frontmatter.searchable === false\r\n\t\t\t\t: frontmatter.searchable === 'false'\r\n\t\t\t\t? false\r\n\t\t\t\t: false, // Кнопки по умолчанию не участвуют в поиске\r\n\t\turl: typeof frontmatter.url === 'string' ? frontmatter.url : undefined,\r\n\t\tstyle:\r\n\t\t\ttypeof frontmatter.style === 'string' &&\r\n\t\t\t['primary', 'secondary', 'ghost'].includes(frontmatter.style)\r\n\t\t\t\t? (frontmatter.style as 'primary' | 'secondary' | 'ghost')\r\n\t\t\t\t: undefined,\r\n\t\ttarget:\r\n\t\t\ttypeof frontmatter.target === 'string' &&\r\n\t\t\t['_blank', '_self'].includes(frontmatter.target)\r\n\t\t\t\t? (frontmatter.target as '_blank' | '_self')\r\n\t\t\t\t: undefined,\r\n\t\tposition:\r\n\t\t\ttypeof frontmatter.position === 'string' &&\r\n\t\t\t['sidebar', 'header'].includes(frontmatter.position)\r\n\t\t\t\t? (frontmatter.position as 'sidebar' | 'header')\r\n\t\t\t\t: 'sidebar',\r\n\t}\r\n}\r\n\r\n/**\r\n * Парсит настройки версии из frontmatter\r\n */\r\nexport function parseVersionSettings(\r\n\tfrontmatter: Record<string, unknown>\r\n): VersionSettings {\r\n\treturn {\r\n\t\ttitle:\r\n\t\t\ttypeof frontmatter.title === 'string' ? frontmatter.title : undefined,\r\n\t\torder:\r\n\t\t\ttypeof frontmatter.order === 'number'\r\n\t\t\t\t? frontmatter.order\r\n\t\t\t\t: typeof frontmatter.order === 'string'\r\n\t\t\t\t? parseInt(frontmatter.order, 10)\r\n\t\t\t\t: undefined,\r\n\t\tdefault:\r\n\t\t\ttypeof frontmatter.default === 'boolean'\r\n\t\t\t\t? frontmatter.default\r\n\t\t\t\t: frontmatter.default === 'true',\r\n\t\tdeprecated:\r\n\t\t\ttypeof frontmatter.deprecated === 'boolean'\r\n\t\t\t\t? frontmatter.deprecated\r\n\t\t\t\t: frontmatter.deprecated === 'true',\r\n\t}\r\n}\r\n\r\n/**\r\n * Парсит настройки языка из frontmatter\r\n */\r\nexport function parseLanguageSettings(\r\n\tfrontmatter: Record<string, unknown>\r\n): LanguageSettings {\r\n\treturn {\r\n\t\tlabel:\r\n\t\t\ttypeof frontmatter.label === 'string' ? frontmatter.label : undefined,\r\n\t\torder:\r\n\t\t\ttypeof frontmatter.order === 'number'\r\n\t\t\t\t? frontmatter.order\r\n\t\t\t\t: typeof frontmatter.order === 'string'\r\n\t\t\t\t? parseInt(frontmatter.order, 10)\r\n\t\t\t\t: undefined,\r\n\t}\r\n}\r\n\r\n/**\r\n * Применяет значения по умолчанию к базовым метаданным\r\n */\r\nexport function applyBaseMetaDefaults(meta: Partial<BaseMeta>): BaseMeta {\r\n\treturn {\r\n\t\torder: meta.order ?? 100,\r\n\t\thidden: meta.hidden ?? false,\r\n\t\tsearchable: meta.searchable ?? true,\r\n\t\t...meta,\r\n\t}\r\n}\r\n\r\n/**\r\n * Наследует метаданные от родителя к дочернему элементу\r\n */\r\nexport function inheritMeta(\r\n\tchildMeta: Partial<BaseMeta>,\r\n\tparentMeta?: Partial<BaseMeta>\r\n): BaseMeta {\r\n\tif (!parentMeta) {\r\n\t\treturn applyBaseMetaDefaults(childMeta)\r\n\t}\r\n\r\n\t// Наследуем значения, если они не заданы в дочернем элементе\r\n\tconst inherited: Partial<BaseMeta> = {\r\n\t\ticon: childMeta.icon ?? parentMeta.icon,\r\n\t\t// hidden не наследуется вверх (если дочерний hidden, он остается hidden)\r\n\t\thidden: childMeta.hidden ?? false,\r\n\t\t// searchable наследуется, но если родитель false, то и дочерний false\r\n\t\tsearchable:\r\n\t\t\tchildMeta.searchable !== undefined\r\n\t\t\t\t? childMeta.searchable\r\n\t\t\t\t: parentMeta.searchable ?? true,\r\n\t}\r\n\r\n\treturn applyBaseMetaDefaults({\r\n\t\t...parentMeta,\r\n\t\t...inherited,\r\n\t\t...childMeta, // Дочерние значения имеют приоритет\r\n\t})\r\n}\r\n"],"mappings":"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","names":["plugin_exports","__export","ConsoleLogger","FolderScanner","dockRushScannerPlugin","__toCommonJS","ConsoleLogger","files","folderPath","format","a","b","file","indent","icon","i","dirs","f","fileCount","import_fast_glob","path","extractVersions","files","versionPattern","versions","file","a","b","aParts","bParts","aPart","bPart","FolderScanner","folderPath","options","fg","entry","relativePath","isDirectory","error","import_promises","import_node_path","import_gray_matter","import_promises","import_node_path","import_gray_matter","parsePageFrontmatter","frontmatter","t","parseDropdownSettings","parseGroupSettings","parseButtonSettings","defaultTitle","SETTINGS_PATTERNS","BUTTON_PATTERN","readFileMetadata","filePath","content","fs","rawFrontmatter","markdownContent","matter","path","defaultTitle","parseButtonSettings","parsePageFrontmatter","readSettingsFile","folderPath","type","settingsFileName","settingsPath","data","parseDropdownSettings","parseGroupSettings","findSettingsFile","files","folderRelativePath","normalizedFolderPath","file","readAllMetadata","rootPath","metadataMap","absolutePath","frontmatter","processedFolders","settingsFile","absoluteFolderPath","settings","setupMiddleware","server","options","req","res","next","isScanRequest","isMarkdownRequest","url","rawFilePath","folderPath","filePath","root","absoluteFilePath","path","docsFolder","normalizedTargetFile","normalizedDocsFolder","fs","rawContent","stats","cleanContent","frontmatterData","matter","fileError","errorMessage","consoleEnabled","consoleFormat","ignore","targetFolder","result","FolderScanner","metadata","readAllMetadata","filesWithContent","file","absolutePath","content","indexedContent","ConsoleLogger","metadataObj","key","value","dockRushScannerPlugin"]}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Базовые метаданные, общие для всех сущностей
3
+ */
4
+ interface BaseMeta {
5
+ title?: string;
6
+ order?: number;
7
+ icon?: string;
8
+ hidden?: boolean;
9
+ searchable?: boolean;
10
+ }
11
+ /**
12
+ * Метаданные страницы (Page)
13
+ */
14
+ interface PageFrontmatter extends BaseMeta {
15
+ lang?: string;
16
+ tags?: string[];
17
+ layout?: string;
18
+ }
19
+ /**
20
+ * Метаданные Dropdown (папка)
21
+ */
22
+ interface DropdownSettings extends BaseMeta {
23
+ dropdown?: 'open' | 'collapsed' | 'always-open';
24
+ }
25
+ /**
26
+ * Метаданные Group (group-*)
27
+ */
28
+ interface GroupSettings extends BaseMeta {
29
+ description?: string;
30
+ }
31
+ /**
32
+ * Метаданные кнопки (Button)
33
+ */
34
+ interface ButtonSettings extends BaseMeta {
35
+ type: 'button';
36
+ variant: 'link' | 'page';
37
+ title: string;
38
+ url?: string;
39
+ style?: 'primary' | 'secondary' | 'ghost';
40
+ target?: '_blank' | '_self';
41
+ position?: 'sidebar' | 'header';
42
+ }
43
+
44
+ interface ScanOptions {
45
+ pattern?: string | string[];
46
+ ignore?: string[];
47
+ deep?: number;
48
+ onlyFiles?: boolean;
49
+ onlyDirectories?: boolean;
50
+ stats?: boolean;
51
+ }
52
+ interface FileInfo {
53
+ name: string;
54
+ path: string;
55
+ relativePath: string;
56
+ type: 'file' | 'directory';
57
+ size?: number;
58
+ extension?: string;
59
+ depth: number;
60
+ originalRelativePath?: string;
61
+ content?: string;
62
+ }
63
+ interface FileMetadata {
64
+ [key: string]: PageFrontmatter | ButtonSettings | DropdownSettings | GroupSettings;
65
+ }
66
+ interface ScanResult {
67
+ success: boolean;
68
+ files: FileInfo[];
69
+ error?: string;
70
+ versions: string[];
71
+ metadata?: FileMetadata;
72
+ }
73
+ type ConsoleFormat = 'tree' | 'list' | 'minimal';
74
+
75
+ declare class ConsoleLogger {
76
+ static log(files: FileInfo[], folderPath: string, format?: ConsoleFormat): void;
77
+ private static logTree;
78
+ private static logList;
79
+ private static logMinimal;
80
+ }
81
+
82
+ declare class FolderScanner {
83
+ scan(folderPath: string, options?: ScanOptions): Promise<ScanResult>;
84
+ }
85
+
86
+ interface DockRushScannerPluginOptions {
87
+ /**
88
+ * HTTP‑роут, по которому будет висеть endpoint сканера.
89
+ * По умолчанию: `/api/dock-rush-scan`
90
+ */
91
+ route?: string;
92
+ /**
93
+ * Корневая папка для разрешения относительного `folderPath`.
94
+ * По умолчанию — `server.config.root` или `process.cwd()`.
95
+ */
96
+ root?: string;
97
+ }
98
+ declare function dockRushScannerPlugin(options?: DockRushScannerPluginOptions): {
99
+ name: string;
100
+ configureServer(server: any): void;
101
+ configurePreviewServer(server: any): void;
102
+ };
103
+
104
+ export { type ConsoleFormat, ConsoleLogger, type DockRushScannerPluginOptions, type FileInfo, type FileMetadata, FolderScanner, type ScanOptions, type ScanResult, dockRushScannerPlugin };
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Базовые метаданные, общие для всех сущностей
3
+ */
4
+ interface BaseMeta {
5
+ title?: string;
6
+ order?: number;
7
+ icon?: string;
8
+ hidden?: boolean;
9
+ searchable?: boolean;
10
+ }
11
+ /**
12
+ * Метаданные страницы (Page)
13
+ */
14
+ interface PageFrontmatter extends BaseMeta {
15
+ lang?: string;
16
+ tags?: string[];
17
+ layout?: string;
18
+ }
19
+ /**
20
+ * Метаданные Dropdown (папка)
21
+ */
22
+ interface DropdownSettings extends BaseMeta {
23
+ dropdown?: 'open' | 'collapsed' | 'always-open';
24
+ }
25
+ /**
26
+ * Метаданные Group (group-*)
27
+ */
28
+ interface GroupSettings extends BaseMeta {
29
+ description?: string;
30
+ }
31
+ /**
32
+ * Метаданные кнопки (Button)
33
+ */
34
+ interface ButtonSettings extends BaseMeta {
35
+ type: 'button';
36
+ variant: 'link' | 'page';
37
+ title: string;
38
+ url?: string;
39
+ style?: 'primary' | 'secondary' | 'ghost';
40
+ target?: '_blank' | '_self';
41
+ position?: 'sidebar' | 'header';
42
+ }
43
+
44
+ interface ScanOptions {
45
+ pattern?: string | string[];
46
+ ignore?: string[];
47
+ deep?: number;
48
+ onlyFiles?: boolean;
49
+ onlyDirectories?: boolean;
50
+ stats?: boolean;
51
+ }
52
+ interface FileInfo {
53
+ name: string;
54
+ path: string;
55
+ relativePath: string;
56
+ type: 'file' | 'directory';
57
+ size?: number;
58
+ extension?: string;
59
+ depth: number;
60
+ originalRelativePath?: string;
61
+ content?: string;
62
+ }
63
+ interface FileMetadata {
64
+ [key: string]: PageFrontmatter | ButtonSettings | DropdownSettings | GroupSettings;
65
+ }
66
+ interface ScanResult {
67
+ success: boolean;
68
+ files: FileInfo[];
69
+ error?: string;
70
+ versions: string[];
71
+ metadata?: FileMetadata;
72
+ }
73
+ type ConsoleFormat = 'tree' | 'list' | 'minimal';
74
+
75
+ declare class ConsoleLogger {
76
+ static log(files: FileInfo[], folderPath: string, format?: ConsoleFormat): void;
77
+ private static logTree;
78
+ private static logList;
79
+ private static logMinimal;
80
+ }
81
+
82
+ declare class FolderScanner {
83
+ scan(folderPath: string, options?: ScanOptions): Promise<ScanResult>;
84
+ }
85
+
86
+ interface DockRushScannerPluginOptions {
87
+ /**
88
+ * HTTP‑роут, по которому будет висеть endpoint сканера.
89
+ * По умолчанию: `/api/dock-rush-scan`
90
+ */
91
+ route?: string;
92
+ /**
93
+ * Корневая папка для разрешения относительного `folderPath`.
94
+ * По умолчанию — `server.config.root` или `process.cwd()`.
95
+ */
96
+ root?: string;
97
+ }
98
+ declare function dockRushScannerPlugin(options?: DockRushScannerPluginOptions): {
99
+ name: string;
100
+ configureServer(server: any): void;
101
+ configurePreviewServer(server: any): void;
102
+ };
103
+
104
+ export { type ConsoleFormat, ConsoleLogger, type DockRushScannerPluginOptions, type FileInfo, type FileMetadata, FolderScanner, type ScanOptions, type ScanResult, dockRushScannerPlugin };
@@ -0,0 +1,4 @@
1
+ var I=class{static log(o,n,t="tree"){switch(console.log(`
2
+ \u{1F4C1} ${n}
3
+ `),t){case"tree":this.logTree(o);break;case"list":this.logList(o);break;case"minimal":this.logMinimal(o);break}}static logTree(o){o.sort((n,t)=>n.type!==t.type?n.type==="directory"?-1:1:n.name.localeCompare(t.name)).forEach(n=>{let t=" ".repeat(n.depth),s=n.type==="directory"?"\u{1F4C1}":"\u{1F4C4}";console.log(`${t}${s} ${n.name}`)})}static logList(o){o.forEach((n,t)=>{let s=n.type==="directory"?"\u{1F4C1}":"\u{1F4C4}";console.log(`${t+1}. ${s} ${n.relativePath}`)})}static logMinimal(o){let n=o.filter(s=>s.type==="directory").length,t=o.length-n;console.log(`\u{1F4C1} ${n} directories, \u{1F4C4} ${t} files`)}};import j from"fast-glob";import*as m from"path";function G(e){let o=/^\d+\.\d+\.\d+$/,n=new Set;for(let t of e)t.type==="directory"&&o.test(t.name)&&n.add(t.name);return Array.from(n).sort((t,s)=>{let a=t.split(".").map(Number),r=s.split(".").map(Number);for(let i=0;i<Math.max(a.length,r.length);i++){let d=a[i]??0,P=r[i]??0;if(d!==P)return P-d}return 0})}var x=class{async scan(o,n={}){try{let s=(await j("**/*",{cwd:o,onlyFiles:!1,stats:!0,absolute:!0,deep:n.deep??1/0,ignore:n.ignore,dot:!0})).map(r=>{let i=m.relative(o,r.path),d="isDirectory"in r.stats?r.stats.isDirectory():!1;return{name:m.basename(r.path),path:r.path,relativePath:i,type:d?"directory":"file",size:r.stats?.size,extension:d===!1?m.extname(r.path).toLowerCase().slice(1):void 0,depth:i.split(m.sep).length-1}}),a=G(s);return{success:!0,files:s,versions:a}}catch(t){return{success:!1,files:[],versions:[],error:t instanceof Error?t.message:"Unknown error"}}}};import O from"fs/promises";import u from"path";import N from"gray-matter";import M from"fs/promises";import p from"path";import oe from"gray-matter";function E(e){return{title:typeof e.title=="string"?e.title:void 0,order:typeof e.order=="number"?e.order:typeof e.order=="string"?parseInt(e.order,10):void 0,icon:typeof e.icon=="string"?e.icon:void 0,hidden:typeof e.hidden=="boolean"?e.hidden:e.hidden==="true"?!0:void 0,searchable:typeof e.searchable=="boolean"?e.searchable:e.searchable==="false"?!1:void 0,lang:typeof e.lang=="string"?e.lang:void 0,tags:Array.isArray(e.tags)?e.tags.filter(o=>typeof o=="string"):void 0,layout:typeof e.layout=="string"?e.layout:void 0}}function $(e){return{title:typeof e.title=="string"?e.title:void 0,order:typeof e.order=="number"?e.order:typeof e.order=="string"?parseInt(e.order,10):void 0,icon:typeof e.icon=="string"?e.icon:void 0,hidden:typeof e.hidden=="boolean"?e.hidden:e.hidden==="true"?!0:void 0,searchable:typeof e.searchable=="boolean"?e.searchable:e.searchable==="false"?!1:void 0,dropdown:typeof e.dropdown=="string"&&["open","collapsed","always-open"].includes(e.dropdown)?e.dropdown:void 0}}function D(e){return{title:typeof e.title=="string"?e.title:void 0,order:typeof e.order=="number"?e.order:typeof e.order=="string"?parseInt(e.order,10):void 0,icon:typeof e.icon=="string"?e.icon:void 0,hidden:typeof e.hidden=="boolean"?e.hidden:e.hidden==="true"?!0:void 0,searchable:typeof e.searchable=="boolean"?e.searchable:e.searchable==="false"?!1:void 0,description:typeof e.description=="string"?e.description:void 0}}function _(e,o){return{type:"button",variant:typeof e.variant=="string"&&["link","page"].includes(e.variant)?e.variant:"page",title:typeof e.title=="string"?e.title:o,order:typeof e.order=="number"?e.order:typeof e.order=="string"?parseInt(e.order,10):void 0,icon:typeof e.icon=="string"?e.icon:void 0,hidden:typeof e.hidden=="boolean"?e.hidden:e.hidden==="true"?!0:void 0,searchable:typeof e.searchable=="boolean"?e.searchable===!1:(e.searchable==="false",!1),url:typeof e.url=="string"?e.url:void 0,style:typeof e.style=="string"&&["primary","secondary","ghost"].includes(e.style)?e.style:void 0,target:typeof e.target=="string"&&["_blank","_self"].includes(e.target)?e.target:void 0,position:typeof e.position=="string"&&["sidebar","header"].includes(e.position)?e.position:"sidebar"}}var k={dropdown:/^dropdown-settings\.md$/i,group:/^group-settings\.md$/i},W=/\.button\.md$/;async function z(e){try{let o=await M.readFile(e,"utf-8"),{data:n={},content:t}=N(o);if(W.test(p.basename(e))){let a=p.basename(e,".button.md").replace(/[-_]/g," ");return{frontmatter:_(n,a),content:t}}return{frontmatter:E(n),content:t}}catch{return{frontmatter:null,content:""}}}async function T(e,o){let n=o==="dropdown"?"dropdown-settings.md":"group-settings.md",t=p.join(e,n);try{let s=await M.readFile(t,"utf-8"),{data:a}=N(s);return o==="dropdown"?$(a):D(a)}catch{return null}}function H(e,o){let n=o.replace(/\\/g,"/");for(let t of e)if(!(t.type!=="file"||p.dirname(t.relativePath).replace(/\\/g,"/")!==n)){if(k.dropdown.test(t.name))return{type:"dropdown",file:t};if(k.group.test(t.name))return{type:"group",file:t}}return{type:null,file:null}}async function A(e,o){let n=new Map;for(let s of e){if(s.type!=="file"||s.extension!=="md")continue;let a=p.isAbsolute(s.path)?s.path:p.join(o,s.path);try{let{frontmatter:r}=await z(a);r&&n.set(s.relativePath,r)}catch{}}let t=new Set;for(let s of e){if(s.type!=="directory")continue;let a=s.relativePath.replace(/\\/g,"/");if(t.has(a))continue;let r=H(e,a);if(r.file){if(r.type==="dropdown"){let i=p.isAbsolute(s.path)?s.path:p.join(o,s.path),d=await T(i,"dropdown");d&&n.set(a,d)}else if(r.type==="group"){let i=p.isAbsolute(s.path)?s.path:p.join(o,s.path),d=await T(i,"group");d&&n.set(a,d)}}t.add(a)}return n}function B(e,o){e.middlewares.use(async(n,t,s)=>{try{if(!n.url)return s();let a=n.url.startsWith("/api/dock-rush-scan"),r=n.url.startsWith("/api/dock-rush-markdown");if(!a&&!r)return s();let i=new URL(n.url,`http://${n.headers.host||"localhost"}`);if(r){let d=i.searchParams.get("filePath"),P=i.searchParams.get("folderPath")||"docs";if(!d){t.statusCode=400,t.setHeader("Content-Type","application/json"),t.end(JSON.stringify({success:!1,error:"filePath parameter is required"}));return}let w=d.replaceAll("\\","/"),R=o.root??e.config?.root??process.cwd(),l;u.isAbsolute(w)?l=w:l=u.resolve(R,P,w);let b=u.resolve(R,P),C=u.normalize(l),h=u.normalize(b);if(!C.startsWith(h)){t.statusCode=403,t.setHeader("Content-Type","application/json"),t.end(JSON.stringify({success:!1,error:"Access denied: file outside docs folder"}));return}try{await O.access(l);let f=await O.readFile(l,"utf-8"),y=await O.stat(l),g=f,c={};if(f.match(/^---\s*\n([\s\S]*?)\n---\s*\n?/m)){let F=await import("gray-matter");try{c=F.default(f).data||{}}catch{}g=f.replace(/^---\s*\n[\s\S]*?\n---\s*\n?/m,"")}g=g.replace(/^\s*\n+/g,""),g=g.replace(/\n+\s*$/g,""),t.setHeader("Content-Type","application/json"),t.setHeader("Cache-Control","no-cache"),t.statusCode=200,t.end(JSON.stringify({success:!0,content:g,frontmatter:c,metadata:{path:l,relativePath:u.relative(b,l),name:u.basename(l),size:y.size,modified:y.mtime,created:y.birthtime}}))}catch(f){let y=f instanceof Error?f.message:"File not found";t.statusCode=404,t.setHeader("Content-Type","application/json"),t.end(JSON.stringify({success:!1,error:`File not found: ${y}`,requestedPath:l,normalizedPath:C,docsFolder:h}))}return}if(a){let d=i.searchParams.get("folderPath")??"",P=i.searchParams.get("console")==="true",w=i.searchParams.get("consoleFormat")||"tree",R=i.searchParams.getAll("ignore")||void 0;if(!d){t.statusCode=400,t.setHeader("Content-Type","application/json"),t.end(JSON.stringify({success:!1,error:"folderPath parameter is required",versions:[],files:[]}));return}let l=o.root??e.config?.root??process.cwd(),b=u.resolve(l,d),h=await new x().scan(b,{ignore:R});if(h.success){let f=await A(h.files,b),y=await Promise.all(h.files.map(async c=>{if(c.type==="file"&&c.extension==="md")try{let S=u.isAbsolute(c.path)?c.path:u.join(b,c.path),F=await O.readFile(S,"utf-8"),v=F;F.match(/^---\s*\n([\s\S]*?)\n---\s*\n?/m)&&(v=F.replace(/^---\s*\n[\s\S]*?\n---\s*\n?/m,""));let L=v.replace(/^\s*\n+/g,"").replace(/\n+\s*$/g,"").substring(0,1e4).toLowerCase();return{...c,content:L}}catch{return c}return c}));P&&I.log(y,b,w);let g={};for(let[c,S]of f.entries())g[c]=S;t.setHeader("Content-Type","application/json"),t.statusCode=200,t.end(JSON.stringify({success:!0,versions:h.versions,files:y,metadata:g}))}else t.setHeader("Content-Type","application/json"),t.statusCode=500,t.end(JSON.stringify({success:!1,error:h.error,versions:[],files:[]}))}}catch{t.headersSent||(t.statusCode=500,t.setHeader("Content-Type","application/json"),t.end(JSON.stringify({success:!1,error:"Internal server error"})))}})}function J(e={}){return{name:"dock-rush-folder-scanner",configureServer(o){B(o,e)},configurePreviewServer(o){B(o,e)}}}export{I as ConsoleLogger,x as FolderScanner,J as dockRushScannerPlugin};
4
+ //# sourceMappingURL=plugin.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/scanningFolder/logger.ts","../src/lib/scanningFolder/scanner.ts","../src/lib/scanningFolder/vitePlugin.ts","../src/lib/scanningFolder/metadata-reader.ts","../src/lib/documentation/frontmatter-parser.ts"],"sourcesContent":["import { ConsoleFormat, FileInfo } from './types'\r\n\r\nexport class ConsoleLogger {\r\n\tstatic log(\r\n\t\tfiles: FileInfo[],\r\n\t\tfolderPath: string,\r\n\t\tformat: ConsoleFormat = 'tree'\r\n\t) {\r\n\t\tconsole.log(`\\n📁 ${folderPath}\\n`)\r\n\r\n\t\tswitch (format) {\r\n\t\t\tcase 'tree':\r\n\t\t\t\tthis.logTree(files)\r\n\t\t\t\tbreak\r\n\t\t\tcase 'list':\r\n\t\t\t\tthis.logList(files)\r\n\t\t\t\tbreak\r\n\t\t\tcase 'minimal':\r\n\t\t\t\tthis.logMinimal(files)\r\n\t\t\t\tbreak\r\n\t\t}\r\n\t}\r\n\r\n\tprivate static logTree(files: FileInfo[]) {\r\n\t\tfiles\r\n\t\t\t.sort((a, b) => {\r\n\t\t\t\tif (a.type !== b.type) return a.type === 'directory' ? -1 : 1\r\n\t\t\t\treturn a.name.localeCompare(b.name)\r\n\t\t\t})\r\n\t\t\t.forEach(file => {\r\n\t\t\t\tconst indent = ' '.repeat(file.depth)\r\n\t\t\t\tconst icon = file.type === 'directory' ? '📁' : '📄'\r\n\t\t\t\tconsole.log(`${indent}${icon} ${file.name}`)\r\n\t\t\t})\r\n\t}\r\n\r\n\tprivate static logList(files: FileInfo[]) {\r\n\t\tfiles.forEach((file, i) => {\r\n\t\t\tconst icon = file.type === 'directory' ? '📁' : '📄'\r\n\t\t\tconsole.log(`${i + 1}. ${icon} ${file.relativePath}`)\r\n\t\t})\r\n\t}\r\n\r\n\tprivate static logMinimal(files: FileInfo[]) {\r\n\t\tconst dirs = files.filter(f => f.type === 'directory').length\r\n\t\tconst fileCount = files.length - dirs\r\n\t\tconsole.log(`📁 ${dirs} directories, 📄 ${fileCount} files`)\r\n\t}\r\n}\r\n","import fg from 'fast-glob'\r\nimport * as path from 'node:path'\r\nimport { FileInfo, ScanOptions, ScanResult } from './types'\r\n\r\n/**\r\n * Extracts version numbers from directory names that match semantic version patterns\r\n */\r\nfunction extractVersions(files: FileInfo[]): string[] {\r\n\tconst versionPattern = /^\\d+\\.\\d+\\.\\d+$/\r\n\tconst versions = new Set<string>()\r\n\r\n\tfor (const file of files) {\r\n\t\tif (file.type === 'directory' && versionPattern.test(file.name)) {\r\n\t\t\tversions.add(file.name)\r\n\t\t}\r\n\t}\r\n\r\n\treturn Array.from(versions).sort((a, b) => {\r\n\t\tconst aParts = a.split('.').map(Number)\r\n\t\tconst bParts = b.split('.').map(Number)\r\n\r\n\t\tfor (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {\r\n\t\t\tconst aPart = aParts[i] ?? 0\r\n\t\t\tconst bPart = bParts[i] ?? 0\r\n\t\t\tif (aPart !== bPart) {\r\n\t\t\t\treturn bPart - aPart // Sort descending (newest first)\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn 0\r\n\t})\r\n}\r\n\r\nexport class FolderScanner {\r\n\tasync scan(\r\n\t\tfolderPath: string,\r\n\t\toptions: ScanOptions = {}\r\n\t): Promise<ScanResult> {\r\n\t\ttry {\r\n\t\t\tconst entries = await fg('**/*', {\r\n\t\t\t\tcwd: folderPath,\r\n\t\t\t\tonlyFiles: false,\r\n\t\t\t\tstats: true,\r\n\t\t\t\tabsolute: true,\r\n\t\t\t\tdeep: options.deep ?? Infinity,\r\n\t\t\t\tignore: options.ignore,\r\n\t\t\t\tdot: true,\r\n\t\t\t})\r\n\r\n\t\t\tconst files = entries.map(entry => {\r\n\t\t\t\tconst relativePath = path.relative(folderPath, entry.path)\r\n\t\t\t\tconst isDirectory =\r\n\t\t\t\t\t'isDirectory' in entry.stats ? entry.stats.isDirectory() : false\r\n\r\n\t\t\t\treturn {\r\n\t\t\t\t\tname: path.basename(entry.path),\r\n\t\t\t\t\tpath: entry.path,\r\n\t\t\t\t\trelativePath,\r\n\t\t\t\t\ttype: isDirectory ? 'directory' : 'file',\r\n\t\t\t\t\tsize: entry.stats?.size,\r\n\t\t\t\t\textension:\r\n\t\t\t\t\t\tisDirectory === false\r\n\t\t\t\t\t\t\t? path.extname(entry.path).toLowerCase().slice(1)\r\n\t\t\t\t\t\t\t: undefined,\r\n\t\t\t\t\tdepth: relativePath.split(path.sep).length - 1,\r\n\t\t\t\t}\r\n\t\t\t}) as FileInfo[]\r\n\r\n\t\t\tconst versions = extractVersions(files)\r\n\r\n\t\t\treturn { success: true, files, versions: versions }\r\n\t\t} catch (error) {\r\n\t\t\treturn {\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\tfiles: [],\r\n\t\t\t\tversions: [],\r\n\t\t\t\terror: error instanceof Error ? error.message : 'Unknown error',\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n","import fs from 'node:fs/promises'\nimport path from 'node:path'\nimport { ConsoleLogger } from './logger'\nimport { readAllMetadata } from './metadata-reader'\nimport { FolderScanner } from './scanner'\nimport type { ConsoleFormat, FileInfo } from './types'\n\nexport interface DockRushScannerPluginOptions {\n\t/**\n\t * HTTP‑роут, по которому будет висеть endpoint сканера.\n\t * По умолчанию: `/api/dock-rush-scan`\n\t */\n\troute?: string\n\n\t/**\n\t * Корневая папка для разрешения относительного `folderPath`.\n\t * По умолчанию — `server.config.root` или `process.cwd()`.\n\t */\n\troot?: string\n}\n\n/**\n * Vite‑плагин, который обрабатывает запросы от клиентского компонента\n * и запускает `FolderScanner` на стороне Node.\n */\n// Общая функция для настройки middleware\nfunction setupMiddleware(server: any, options: DockRushScannerPluginOptions) {\n\t// ОБЪЕДИНЕННЫЙ обработчик для всех API\n\tserver.middlewares.use(async (req: any, res: any, next: any) => {\n\t\ttry {\n\t\t\tif (!req.url) return next()\n\n\t\t\t// Маршруты API\n\t\t\tconst isScanRequest = req.url.startsWith('/api/dock-rush-scan')\n\t\t\tconst isMarkdownRequest = req.url.startsWith('/api/dock-rush-markdown')\n\n\t\t\tif (!isScanRequest && !isMarkdownRequest) {\n\t\t\t\treturn next()\n\t\t\t}\n\n\t\t\tconst url = new URL(req.url, `http://${req.headers.host || 'localhost'}`)\n\n\t\t\t// MARKDOWN ENDPOINT\n\t\t\tif (isMarkdownRequest) {\n\t\t\t\tconst rawFilePath = url.searchParams.get('filePath')\n\t\t\t\tconst folderPath = url.searchParams.get('folderPath') || 'docs'\n\n\t\t\t\tif (!rawFilePath) {\n\t\t\t\t\tres.statusCode = 400\n\t\t\t\t\tres.setHeader('Content-Type', 'application/json')\n\t\t\t\t\tres.end(\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\terror: 'filePath parameter is required',\n\t\t\t\t\t\t})\n\t\t\t\t\t)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\t// Нормализуем путь: заменяем обратные слэши на прямые\n\t\t\t\tconst filePath = rawFilePath.replaceAll('\\\\', '/')\n\n\t\t\t\tconst root = options.root ?? server.config?.root ?? process.cwd()\n\n\t\t\t\t// ДВА варианта пути:\n\t\t\t\t// 1. Если filePath абсолютный (содержит полный путь)\n\t\t\t\tlet absoluteFilePath: string\n\n\t\t\t\tif (path.isAbsolute(filePath)) {\n\t\t\t\t\t// Это уже абсолютный путь от сканера\n\t\t\t\t\tabsoluteFilePath = filePath\n\t\t\t\t} else {\n\t\t\t\t\t// Это относительный путь\n\t\t\t\t\tabsoluteFilePath = path.resolve(root, folderPath, filePath)\n\t\t\t\t}\n\n\t\t\t\t// Проверка безопасности\n\t\t\t\tconst docsFolder = path.resolve(root, folderPath)\n\t\t\t\tconst normalizedTargetFile = path.normalize(absoluteFilePath)\n\t\t\t\tconst normalizedDocsFolder = path.normalize(docsFolder)\n\n\t\t\t\tif (!normalizedTargetFile.startsWith(normalizedDocsFolder)) {\n\t\t\t\t\tres.statusCode = 403\n\t\t\t\t\tres.setHeader('Content-Type', 'application/json')\n\t\t\t\t\tres.end(\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\terror: 'Access denied: file outside docs folder',\n\t\t\t\t\t\t})\n\t\t\t\t\t)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\t// Проверяем существование файла\n\t\t\t\t\tawait fs.access(absoluteFilePath)\n\n\t\t\t\t\tconst rawContent = await fs.readFile(absoluteFilePath, 'utf-8')\n\t\t\t\t\tconst stats = await fs.stat(absoluteFilePath)\n\n\t\t\t\t\t// Удаляем frontmatter блок от --- до --- включительно\n\t\t\t\t\tlet cleanContent = rawContent\n\t\t\t\t\tlet frontmatterData: Record<string, unknown> = {}\n\n\t\t\t\t\t// Проверяем, есть ли frontmatter в начале файла\n\t\t\t\t\tconst frontmatterMatch = rawContent.match(\n\t\t\t\t\t\t/^---\\s*\\n([\\s\\S]*?)\\n---\\s*\\n?/m\n\t\t\t\t\t)\n\n\t\t\t\t\tif (frontmatterMatch) {\n\t\t\t\t\t\t// Извлекаем frontmatter для парсинга\n\t\t\t\t\t\tconst matter = await import('gray-matter')\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst parsed = matter.default(rawContent)\n\t\t\t\t\t\t\tfrontmatterData = parsed.data || {}\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t// Если парсинг не удался, frontmatterData остается пустым\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Удаляем весь frontmatter блок (от первого --- до второго --- включительно)\n\t\t\t\t\t\tcleanContent = rawContent.replace(\n\t\t\t\t\t\t\t/^---\\s*\\n[\\s\\S]*?\\n---\\s*\\n?/m,\n\t\t\t\t\t\t\t''\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\n\t\t\t\t\t// Удаляем все пустые строки в начале (после frontmatter)\n\t\t\t\t\tcleanContent = cleanContent.replace(/^\\s*\\n+/g, '')\n\t\t\t\t\t// Удаляем все пустые строки в конце\n\t\t\t\t\tcleanContent = cleanContent.replace(/\\n+\\s*$/g, '')\n\n\t\t\t\t\tres.setHeader('Content-Type', 'application/json')\n\t\t\t\t\tres.setHeader('Cache-Control', 'no-cache')\n\t\t\t\t\tres.statusCode = 200\n\t\t\t\t\tres.end(\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\t\tcontent: cleanContent,\n\t\t\t\t\t\t\tfrontmatter: frontmatterData,\n\t\t\t\t\t\t\tmetadata: {\n\t\t\t\t\t\t\t\tpath: absoluteFilePath,\n\t\t\t\t\t\t\t\trelativePath: path.relative(docsFolder, absoluteFilePath),\n\t\t\t\t\t\t\t\tname: path.basename(absoluteFilePath),\n\t\t\t\t\t\t\t\tsize: stats.size,\n\t\t\t\t\t\t\t\tmodified: stats.mtime,\n\t\t\t\t\t\t\t\tcreated: stats.birthtime,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t})\n\t\t\t\t\t)\n\t\t\t\t} catch (fileError) {\n\t\t\t\t\tconst errorMessage =\n\t\t\t\t\t\tfileError instanceof Error ? fileError.message : 'File not found'\n\t\t\t\t\tres.statusCode = 404\n\t\t\t\t\tres.setHeader('Content-Type', 'application/json')\n\t\t\t\t\tres.end(\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\terror: `File not found: ${errorMessage}`,\n\t\t\t\t\t\t\trequestedPath: absoluteFilePath,\n\t\t\t\t\t\t\tnormalizedPath: normalizedTargetFile,\n\t\t\t\t\t\t\tdocsFolder: normalizedDocsFolder,\n\t\t\t\t\t\t})\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// SCAN ENDPOINT\n\t\t\tif (isScanRequest) {\n\t\t\t\tconst folderPath = url.searchParams.get('folderPath') ?? ''\n\t\t\t\tconst consoleEnabled = url.searchParams.get('console') === 'true'\n\t\t\t\tconst consoleFormat = (url.searchParams.get('consoleFormat') ||\n\t\t\t\t\t'tree') as ConsoleFormat\n\t\t\t\tconst ignore = url.searchParams.getAll('ignore') || undefined\n\n\t\t\t\tif (!folderPath) {\n\t\t\t\t\tres.statusCode = 400\n\t\t\t\t\tres.setHeader('Content-Type', 'application/json')\n\t\t\t\t\tres.end(\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\terror: 'folderPath parameter is required',\n\t\t\t\t\t\t\tversions: [],\n\t\t\t\t\t\t\tfiles: [],\n\t\t\t\t\t\t})\n\t\t\t\t\t)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconst root = options.root ?? server.config?.root ?? process.cwd()\n\t\t\t\tconst targetFolder = path.resolve(root, folderPath)\n\n\t\t\t\tconst scanner = new FolderScanner()\n\t\t\t\tconst result = await scanner.scan(targetFolder, { ignore })\n\n\t\t\t\tif (result.success) {\n\t\t\t\t\t// Читаем метаданные для всех файлов\n\t\t\t\t\tconst metadata = await readAllMetadata(result.files, targetFolder)\n\n\t\t\t\t\t// Читаем содержимое markdown файлов для поиска\n\t\t\t\t\tconst filesWithContent: FileInfo[] = await Promise.all(\n\t\t\t\t\t\tresult.files.map(async (file): Promise<FileInfo> => {\n\t\t\t\t\t\t\t// Читаем содержимое только для markdown файлов\n\t\t\t\t\t\t\tif (file.type === 'file' && file.extension === 'md') {\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tconst absolutePath = path.isAbsolute(file.path)\n\t\t\t\t\t\t\t\t\t\t? file.path\n\t\t\t\t\t\t\t\t\t\t: path.join(targetFolder, file.path)\n\n\t\t\t\t\t\t\t\t\tconst rawContent = await fs.readFile(absolutePath, 'utf-8')\n\n\t\t\t\t\t\t\t\t\t// Удаляем frontmatter и ограничиваем размер для индексации\n\t\t\t\t\t\t\t\t\tlet content = rawContent\n\t\t\t\t\t\t\t\t\tconst frontmatterMatch = rawContent.match(\n\t\t\t\t\t\t\t\t\t\t/^---\\s*\\n([\\s\\S]*?)\\n---\\s*\\n?/m\n\t\t\t\t\t\t\t\t\t)\n\n\t\t\t\t\t\t\t\t\tif (frontmatterMatch) {\n\t\t\t\t\t\t\t\t\t\tcontent = rawContent.replace(\n\t\t\t\t\t\t\t\t\t\t\t/^---\\s*\\n[\\s\\S]*?\\n---\\s*\\n?/m,\n\t\t\t\t\t\t\t\t\t\t\t''\n\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t// Ограничиваем размер содержимого для индексации (первые 10000 символов)\n\t\t\t\t\t\t\t\t\tconst indexedContent = content\n\t\t\t\t\t\t\t\t\t\t.replace(/^\\s*\\n+/g, '') // Убираем пустые строки в начале\n\t\t\t\t\t\t\t\t\t\t.replace(/\\n+\\s*$/g, '') // Убираем пустые строки в конце\n\t\t\t\t\t\t\t\t\t\t.substring(0, 10000)\n\t\t\t\t\t\t\t\t\t\t.toLowerCase()\n\n\t\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\t\t...file,\n\t\t\t\t\t\t\t\t\t\tcontent: indexedContent,\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t\t\treturn file\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn file\n\t\t\t\t\t\t})\n\t\t\t\t\t)\n\n\t\t\t\t\t// Log to console if enabled\n\t\t\t\t\tif (consoleEnabled) {\n\t\t\t\t\t\tConsoleLogger.log(filesWithContent, targetFolder, consoleFormat)\n\t\t\t\t\t}\n\n\t\t\t\t\t// Преобразуем Map в объект для JSON\n\t\t\t\t\tconst metadataObj: Record<string, unknown> = {}\n\t\t\t\t\tfor (const [key, value] of metadata.entries()) {\n\t\t\t\t\t\tmetadataObj[key] = value\n\t\t\t\t\t}\n\n\t\t\t\t\t// Return JSON response\n\t\t\t\t\tres.setHeader('Content-Type', 'application/json')\n\t\t\t\t\tres.statusCode = 200\n\t\t\t\t\tres.end(\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\t\tversions: result.versions,\n\t\t\t\t\t\t\tfiles: filesWithContent,\n\t\t\t\t\t\t\tmetadata: metadataObj,\n\t\t\t\t\t\t})\n\t\t\t\t\t)\n\t\t\t\t} else {\n\t\t\t\t\tres.setHeader('Content-Type', 'application/json')\n\t\t\t\t\tres.statusCode = 500\n\t\t\t\t\tres.end(\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\terror: result.error,\n\t\t\t\t\t\t\tversions: [],\n\t\t\t\t\t\t\tfiles: [],\n\t\t\t\t\t\t})\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (!res.headersSent) {\n\t\t\t\tres.statusCode = 500\n\t\t\t\tres.setHeader('Content-Type', 'application/json')\n\t\t\t\tres.end(\n\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\terror: 'Internal server error',\n\t\t\t\t\t})\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t})\n}\n\nexport function dockRushScannerPlugin(\n\toptions: DockRushScannerPluginOptions = {}\n) {\n\treturn {\n\t\tname: 'dock-rush-folder-scanner',\n\t\tconfigureServer(server: any) {\n\t\t\tsetupMiddleware(server, options)\n\t\t},\n\t\tconfigurePreviewServer(server: any) {\n\t\t\tsetupMiddleware(server, options)\n\t\t},\n\t}\n}\n","import matter from 'gray-matter'\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport {\n\tparseButtonSettings,\n\tparseDropdownSettings,\n\tparseGroupSettings,\n\tparsePageFrontmatter,\n} from '../documentation/frontmatter-parser'\nimport type {\n\tButtonSettings,\n\tDropdownSettings,\n\tGroupSettings,\n\tPageFrontmatter,\n} from '../documentation/types'\nimport type { FileInfo } from './types'\n\nconst SETTINGS_PATTERNS = {\n\tdropdown: /^dropdown-settings\\.md$/i,\n\tgroup: /^group-settings\\.md$/i,\n}\n\nconst BUTTON_PATTERN = /\\.button\\.md$/\n\n/**\n * Читает метаданные из markdown файла\n */\nexport async function readFileMetadata(filePath: string) {\n\ttry {\n\t\tconst content = await fs.readFile(filePath, 'utf-8')\n\n\t\t/* ➜ добавляем дефолт {} если фронт-маттер отсутствует */\n\t\tconst { data: rawFrontmatter = {}, content: markdownContent } =\n\t\t\tmatter(content)\n\n\t\tif (BUTTON_PATTERN.test(path.basename(filePath))) {\n\t\t\tconst defaultTitle = path\n\t\t\t\t.basename(filePath, '.button.md')\n\t\t\t\t.replace(/[-_]/g, ' ')\n\t\t\tconst buttonSettings = parseButtonSettings(rawFrontmatter, defaultTitle)\n\t\t\treturn { frontmatter: buttonSettings, content: markdownContent }\n\t\t}\n\n\t\tconst pageFrontmatter = parsePageFrontmatter(rawFrontmatter)\n\t\treturn { frontmatter: pageFrontmatter, content: markdownContent }\n\t} catch (error) {\n\t\treturn { frontmatter: null, content: '' }\n\t}\n}\n\n/**\n * Читает settings файл для папки (dropdown или group)\n */\nexport async function readSettingsFile(\n\tfolderPath: string,\n\ttype: 'dropdown' | 'group'\n): Promise<DropdownSettings | GroupSettings | null> {\n\tconst settingsFileName =\n\t\ttype === 'dropdown' ? 'dropdown-settings.md' : 'group-settings.md'\n\tconst settingsPath = path.join(folderPath, settingsFileName)\n\n\ttry {\n\t\tconst content = await fs.readFile(settingsPath, 'utf-8')\n\t\tconst { data } = matter(content)\n\n\t\tif (type === 'dropdown') {\n\t\t\treturn parseDropdownSettings(data)\n\t\t} else {\n\t\t\treturn parseGroupSettings(data)\n\t\t}\n\t} catch (error) {\n\t\t// Settings файл не найден - это нормально\n\t\treturn null\n\t}\n}\n\n/**\n * Находит settings файл в папке\n */\nexport function findSettingsFile(\n\tfiles: FileInfo[],\n\tfolderRelativePath: string\n): {\n\ttype: 'dropdown' | 'group' | null\n\tfile: FileInfo | null\n} {\n\tconst normalizedFolderPath = folderRelativePath.replace(/\\\\/g, '/')\n\n\tfor (const file of files) {\n\t\tif (file.type !== 'file') continue\n\n\t\tconst fileDir = path.dirname(file.relativePath).replace(/\\\\/g, '/')\n\t\tif (fileDir !== normalizedFolderPath) continue\n\n\t\tif (SETTINGS_PATTERNS.dropdown.test(file.name)) {\n\t\t\treturn { type: 'dropdown', file }\n\t\t}\n\t\tif (SETTINGS_PATTERNS.group.test(file.name)) {\n\t\t\treturn { type: 'group', file }\n\t\t}\n\t}\n\n\treturn { type: null, file: null }\n}\n\n/**\n * Читает метаданные для всех файлов в структуре\n */\nexport async function readAllMetadata(\n\tfiles: FileInfo[],\n\trootPath: string\n): Promise<\n\tMap<\n\t\tstring,\n\t\tPageFrontmatter | ButtonSettings | DropdownSettings | GroupSettings\n\t>\n> {\n\tconst metadataMap = new Map<\n\t\tstring,\n\t\tPageFrontmatter | ButtonSettings | DropdownSettings | GroupSettings\n\t>()\n\n\t// Читаем метаданные для всех markdown файлов\n\tfor (const file of files) {\n\t\tif (file.type !== 'file' || file.extension !== 'md') continue\n\n\t\tconst absolutePath = path.isAbsolute(file.path)\n\t\t\t? file.path\n\t\t\t: path.join(rootPath, file.path)\n\n\t\ttry {\n\t\t\tconst { frontmatter } = await readFileMetadata(absolutePath)\n\n\t\t\tif (frontmatter) {\n\t\t\t\tmetadataMap.set(file.relativePath, frontmatter)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Игнорируем ошибки чтения файлов\n\t\t}\n\t}\n\n\t// Читаем settings файлы для папок\n\tconst processedFolders = new Set<string>()\n\tfor (const file of files) {\n\t\tif (file.type !== 'directory') continue\n\n\t\tconst folderRelativePath = file.relativePath.replace(/\\\\/g, '/')\n\t\tif (processedFolders.has(folderRelativePath)) continue\n\n\t\tconst settingsFile = findSettingsFile(files, folderRelativePath)\n\t\tif (settingsFile.file) {\n\t\t\tif (settingsFile.type === 'dropdown') {\n\t\t\t\tconst absoluteFolderPath = path.isAbsolute(file.path)\n\t\t\t\t\t? file.path\n\t\t\t\t\t: path.join(rootPath, file.path)\n\t\t\t\tconst settings = await readSettingsFile(absoluteFolderPath, 'dropdown')\n\t\t\t\tif (settings) {\n\t\t\t\t\tmetadataMap.set(folderRelativePath, settings)\n\t\t\t\t}\n\t\t\t} else if (settingsFile.type === 'group') {\n\t\t\t\tconst absoluteFolderPath = path.isAbsolute(file.path)\n\t\t\t\t\t? file.path\n\t\t\t\t\t: path.join(rootPath, file.path)\n\t\t\t\tconst settings = await readSettingsFile(absoluteFolderPath, 'group')\n\t\t\t\tif (settings) {\n\t\t\t\t\tmetadataMap.set(folderRelativePath, settings)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprocessedFolders.add(folderRelativePath)\n\t}\n\n\treturn metadataMap\n}\n","import matter from 'gray-matter'\r\nimport { errorBuilder } from '../errorBuilder/error-builder'\r\nimport type {\r\n\tBaseMeta,\r\n\tButtonSettings,\r\n\tDropdownSettings,\r\n\tGroupSettings,\r\n\tLanguageSettings,\r\n\tPageFrontmatter,\r\n\tVersionSettings,\r\n} from './types'\r\n\r\n/**\r\n * Парсит frontmatter из markdown файла\r\n */\r\nexport function parseFrontmatter(content: string): {\r\n\tfrontmatter: Record<string, unknown>\r\n\tcontent: string\r\n} {\r\n\ttry {\r\n\t\tconst parsed = matter(content)\r\n\t\treturn {\r\n\t\t\tfrontmatter: parsed.data || {},\r\n\t\t\tcontent: parsed.content,\r\n\t\t}\r\n\t} catch (error) {\r\n\t\t// Если frontmatter некорректный, возвращаем весь контент как есть\r\n\t\terrorBuilder([], {\r\n\t\t\ttype: 'VERSION_FORMAT_ERROR',\r\n\t\t\treason: `frontmatter некорректный`,\r\n\t\t\tfix: `1`,\r\n\t\t})\r\n\t\treturn {\r\n\t\t\tfrontmatter: {},\r\n\t\t\tcontent,\r\n\t\t}\r\n\t}\r\n}\r\n\r\n/**\r\n * Парсит метаданные страницы из frontmatter\r\n */\r\nexport function parsePageFrontmatter(\r\n\tfrontmatter: Record<string, unknown>\r\n): PageFrontmatter {\r\n\treturn {\r\n\t\ttitle:\r\n\t\t\ttypeof frontmatter.title === 'string' ? frontmatter.title : undefined,\r\n\t\torder:\r\n\t\t\ttypeof frontmatter.order === 'number'\r\n\t\t\t\t? frontmatter.order\r\n\t\t\t\t: typeof frontmatter.order === 'string'\r\n\t\t\t\t? parseInt(frontmatter.order, 10)\r\n\t\t\t\t: undefined,\r\n\t\ticon: typeof frontmatter.icon === 'string' ? frontmatter.icon : undefined,\r\n\t\thidden:\r\n\t\t\ttypeof frontmatter.hidden === 'boolean'\r\n\t\t\t\t? frontmatter.hidden\r\n\t\t\t\t: frontmatter.hidden === 'true'\r\n\t\t\t\t? true\r\n\t\t\t\t: undefined,\r\n\t\tsearchable:\r\n\t\t\ttypeof frontmatter.searchable === 'boolean'\r\n\t\t\t\t? frontmatter.searchable\r\n\t\t\t\t: frontmatter.searchable === 'false'\r\n\t\t\t\t? false\r\n\t\t\t\t: undefined,\r\n\t\tlang: typeof frontmatter.lang === 'string' ? frontmatter.lang : undefined,\r\n\t\ttags: Array.isArray(frontmatter.tags)\r\n\t\t\t? frontmatter.tags.filter((t): t is string => typeof t === 'string')\r\n\t\t\t: undefined,\r\n\t\tlayout:\r\n\t\t\ttypeof frontmatter.layout === 'string' ? frontmatter.layout : undefined,\r\n\t}\r\n}\r\n\r\n/**\r\n * Парсит настройки dropdown из frontmatter\r\n */\r\nexport function parseDropdownSettings(\r\n\tfrontmatter: Record<string, unknown>\r\n): DropdownSettings {\r\n\treturn {\r\n\t\ttitle:\r\n\t\t\ttypeof frontmatter.title === 'string' ? frontmatter.title : undefined,\r\n\t\torder:\r\n\t\t\ttypeof frontmatter.order === 'number'\r\n\t\t\t\t? frontmatter.order\r\n\t\t\t\t: typeof frontmatter.order === 'string'\r\n\t\t\t\t? parseInt(frontmatter.order, 10)\r\n\t\t\t\t: undefined,\r\n\t\ticon: typeof frontmatter.icon === 'string' ? frontmatter.icon : undefined,\r\n\t\thidden:\r\n\t\t\ttypeof frontmatter.hidden === 'boolean'\r\n\t\t\t\t? frontmatter.hidden\r\n\t\t\t\t: frontmatter.hidden === 'true'\r\n\t\t\t\t? true\r\n\t\t\t\t: undefined,\r\n\t\tsearchable:\r\n\t\t\ttypeof frontmatter.searchable === 'boolean'\r\n\t\t\t\t? frontmatter.searchable\r\n\t\t\t\t: frontmatter.searchable === 'false'\r\n\t\t\t\t? false\r\n\t\t\t\t: undefined,\r\n\t\tdropdown:\r\n\t\t\ttypeof frontmatter.dropdown === 'string' &&\r\n\t\t\t['open', 'collapsed', 'always-open'].includes(frontmatter.dropdown)\r\n\t\t\t\t? (frontmatter.dropdown as 'open' | 'collapsed' | 'always-open')\r\n\t\t\t\t: undefined,\r\n\t}\r\n}\r\n\r\n/**\r\n * Парсит настройки group из frontmatter\r\n */\r\nexport function parseGroupSettings(\r\n\tfrontmatter: Record<string, unknown>\r\n): GroupSettings {\r\n\treturn {\r\n\t\ttitle:\r\n\t\t\ttypeof frontmatter.title === 'string' ? frontmatter.title : undefined,\r\n\t\torder:\r\n\t\t\ttypeof frontmatter.order === 'number'\r\n\t\t\t\t? frontmatter.order\r\n\t\t\t\t: typeof frontmatter.order === 'string'\r\n\t\t\t\t? parseInt(frontmatter.order, 10)\r\n\t\t\t\t: undefined,\r\n\t\ticon: typeof frontmatter.icon === 'string' ? frontmatter.icon : undefined,\r\n\t\thidden:\r\n\t\t\ttypeof frontmatter.hidden === 'boolean'\r\n\t\t\t\t? frontmatter.hidden\r\n\t\t\t\t: frontmatter.hidden === 'true'\r\n\t\t\t\t? true\r\n\t\t\t\t: undefined,\r\n\t\tsearchable:\r\n\t\t\ttypeof frontmatter.searchable === 'boolean'\r\n\t\t\t\t? frontmatter.searchable\r\n\t\t\t\t: frontmatter.searchable === 'false'\r\n\t\t\t\t? false\r\n\t\t\t\t: undefined,\r\n\t\tdescription:\r\n\t\t\ttypeof frontmatter.description === 'string'\r\n\t\t\t\t? frontmatter.description\r\n\t\t\t\t: undefined,\r\n\t}\r\n}\r\n\r\n/**\r\n * Парсит настройки кнопки из frontmatter\r\n */\r\nexport function parseButtonSettings(\r\n\tfrontmatter: Record<string, unknown>,\r\n\tdefaultTitle: string\r\n): ButtonSettings {\r\n\treturn {\r\n\t\ttype: 'button',\r\n\t\tvariant:\r\n\t\t\ttypeof frontmatter.variant === 'string' &&\r\n\t\t\t['link', 'page'].includes(frontmatter.variant)\r\n\t\t\t\t? (frontmatter.variant as 'link' | 'page')\r\n\t\t\t\t: 'page',\r\n\t\ttitle:\r\n\t\t\ttypeof frontmatter.title === 'string' ? frontmatter.title : defaultTitle,\r\n\t\torder:\r\n\t\t\ttypeof frontmatter.order === 'number'\r\n\t\t\t\t? frontmatter.order\r\n\t\t\t\t: typeof frontmatter.order === 'string'\r\n\t\t\t\t? parseInt(frontmatter.order, 10)\r\n\t\t\t\t: undefined,\r\n\t\ticon: typeof frontmatter.icon === 'string' ? frontmatter.icon : undefined,\r\n\t\thidden:\r\n\t\t\ttypeof frontmatter.hidden === 'boolean'\r\n\t\t\t\t? frontmatter.hidden\r\n\t\t\t\t: frontmatter.hidden === 'true'\r\n\t\t\t\t? true\r\n\t\t\t\t: undefined,\r\n\t\tsearchable:\r\n\t\t\ttypeof frontmatter.searchable === 'boolean'\r\n\t\t\t\t? frontmatter.searchable === false\r\n\t\t\t\t: frontmatter.searchable === 'false'\r\n\t\t\t\t? false\r\n\t\t\t\t: false, // Кнопки по умолчанию не участвуют в поиске\r\n\t\turl: typeof frontmatter.url === 'string' ? frontmatter.url : undefined,\r\n\t\tstyle:\r\n\t\t\ttypeof frontmatter.style === 'string' &&\r\n\t\t\t['primary', 'secondary', 'ghost'].includes(frontmatter.style)\r\n\t\t\t\t? (frontmatter.style as 'primary' | 'secondary' | 'ghost')\r\n\t\t\t\t: undefined,\r\n\t\ttarget:\r\n\t\t\ttypeof frontmatter.target === 'string' &&\r\n\t\t\t['_blank', '_self'].includes(frontmatter.target)\r\n\t\t\t\t? (frontmatter.target as '_blank' | '_self')\r\n\t\t\t\t: undefined,\r\n\t\tposition:\r\n\t\t\ttypeof frontmatter.position === 'string' &&\r\n\t\t\t['sidebar', 'header'].includes(frontmatter.position)\r\n\t\t\t\t? (frontmatter.position as 'sidebar' | 'header')\r\n\t\t\t\t: 'sidebar',\r\n\t}\r\n}\r\n\r\n/**\r\n * Парсит настройки версии из frontmatter\r\n */\r\nexport function parseVersionSettings(\r\n\tfrontmatter: Record<string, unknown>\r\n): VersionSettings {\r\n\treturn {\r\n\t\ttitle:\r\n\t\t\ttypeof frontmatter.title === 'string' ? frontmatter.title : undefined,\r\n\t\torder:\r\n\t\t\ttypeof frontmatter.order === 'number'\r\n\t\t\t\t? frontmatter.order\r\n\t\t\t\t: typeof frontmatter.order === 'string'\r\n\t\t\t\t? parseInt(frontmatter.order, 10)\r\n\t\t\t\t: undefined,\r\n\t\tdefault:\r\n\t\t\ttypeof frontmatter.default === 'boolean'\r\n\t\t\t\t? frontmatter.default\r\n\t\t\t\t: frontmatter.default === 'true',\r\n\t\tdeprecated:\r\n\t\t\ttypeof frontmatter.deprecated === 'boolean'\r\n\t\t\t\t? frontmatter.deprecated\r\n\t\t\t\t: frontmatter.deprecated === 'true',\r\n\t}\r\n}\r\n\r\n/**\r\n * Парсит настройки языка из frontmatter\r\n */\r\nexport function parseLanguageSettings(\r\n\tfrontmatter: Record<string, unknown>\r\n): LanguageSettings {\r\n\treturn {\r\n\t\tlabel:\r\n\t\t\ttypeof frontmatter.label === 'string' ? frontmatter.label : undefined,\r\n\t\torder:\r\n\t\t\ttypeof frontmatter.order === 'number'\r\n\t\t\t\t? frontmatter.order\r\n\t\t\t\t: typeof frontmatter.order === 'string'\r\n\t\t\t\t? parseInt(frontmatter.order, 10)\r\n\t\t\t\t: undefined,\r\n\t}\r\n}\r\n\r\n/**\r\n * Применяет значения по умолчанию к базовым метаданным\r\n */\r\nexport function applyBaseMetaDefaults(meta: Partial<BaseMeta>): BaseMeta {\r\n\treturn {\r\n\t\torder: meta.order ?? 100,\r\n\t\thidden: meta.hidden ?? false,\r\n\t\tsearchable: meta.searchable ?? true,\r\n\t\t...meta,\r\n\t}\r\n}\r\n\r\n/**\r\n * Наследует метаданные от родителя к дочернему элементу\r\n */\r\nexport function inheritMeta(\r\n\tchildMeta: Partial<BaseMeta>,\r\n\tparentMeta?: Partial<BaseMeta>\r\n): BaseMeta {\r\n\tif (!parentMeta) {\r\n\t\treturn applyBaseMetaDefaults(childMeta)\r\n\t}\r\n\r\n\t// Наследуем значения, если они не заданы в дочернем элементе\r\n\tconst inherited: Partial<BaseMeta> = {\r\n\t\ticon: childMeta.icon ?? parentMeta.icon,\r\n\t\t// hidden не наследуется вверх (если дочерний hidden, он остается hidden)\r\n\t\thidden: childMeta.hidden ?? false,\r\n\t\t// searchable наследуется, но если родитель false, то и дочерний false\r\n\t\tsearchable:\r\n\t\t\tchildMeta.searchable !== undefined\r\n\t\t\t\t? childMeta.searchable\r\n\t\t\t\t: parentMeta.searchable ?? true,\r\n\t}\r\n\r\n\treturn applyBaseMetaDefaults({\r\n\t\t...parentMeta,\r\n\t\t...inherited,\r\n\t\t...childMeta, // Дочерние значения имеют приоритет\r\n\t})\r\n}\r\n"],"mappings":"AAEO,IAAMA,EAAN,KAAoB,CAC1B,OAAO,IACNC,EACAC,EACAC,EAAwB,OACvB,CAGD,OAFA,QAAQ,IAAI;AAAA,YAAQD,CAAU;AAAA,CAAI,EAE1BC,EAAQ,CACf,IAAK,OACJ,KAAK,QAAQF,CAAK,EAClB,MACD,IAAK,OACJ,KAAK,QAAQA,CAAK,EAClB,MACD,IAAK,UACJ,KAAK,WAAWA,CAAK,EACrB,KACF,CACD,CAEA,OAAe,QAAQA,EAAmB,CACzCA,EACE,KAAK,CAACG,EAAGC,IACLD,EAAE,OAASC,EAAE,KAAaD,EAAE,OAAS,YAAc,GAAK,EACrDA,EAAE,KAAK,cAAcC,EAAE,IAAI,CAClC,EACA,QAAQC,GAAQ,CAChB,IAAMC,EAAS,KAAK,OAAOD,EAAK,KAAK,EAC/BE,EAAOF,EAAK,OAAS,YAAc,YAAO,YAChD,QAAQ,IAAI,GAAGC,CAAM,GAAGC,CAAI,IAAIF,EAAK,IAAI,EAAE,CAC5C,CAAC,CACH,CAEA,OAAe,QAAQL,EAAmB,CACzCA,EAAM,QAAQ,CAACK,EAAMG,IAAM,CAC1B,IAAMD,EAAOF,EAAK,OAAS,YAAc,YAAO,YAChD,QAAQ,IAAI,GAAGG,EAAI,CAAC,KAAKD,CAAI,IAAIF,EAAK,YAAY,EAAE,CACrD,CAAC,CACF,CAEA,OAAe,WAAWL,EAAmB,CAC5C,IAAMS,EAAOT,EAAM,OAAOU,GAAKA,EAAE,OAAS,WAAW,EAAE,OACjDC,EAAYX,EAAM,OAASS,EACjC,QAAQ,IAAI,aAAMA,CAAI,2BAAoBE,CAAS,QAAQ,CAC5D,CACD,EChDA,OAAOC,MAAQ,YACf,UAAYC,MAAU,OAMtB,SAASC,EAAgBC,EAA6B,CACrD,IAAMC,EAAiB,kBACjBC,EAAW,IAAI,IAErB,QAAWC,KAAQH,EACdG,EAAK,OAAS,aAAeF,EAAe,KAAKE,EAAK,IAAI,GAC7DD,EAAS,IAAIC,EAAK,IAAI,EAIxB,OAAO,MAAM,KAAKD,CAAQ,EAAE,KAAK,CAACE,EAAGC,IAAM,CAC1C,IAAMC,EAASF,EAAE,MAAM,GAAG,EAAE,IAAI,MAAM,EAChCG,EAASF,EAAE,MAAM,GAAG,EAAE,IAAI,MAAM,EAEtC,QAAS,EAAI,EAAG,EAAI,KAAK,IAAIC,EAAO,OAAQC,EAAO,MAAM,EAAG,IAAK,CAChE,IAAMC,EAAQF,EAAO,CAAC,GAAK,EACrBG,EAAQF,EAAO,CAAC,GAAK,EAC3B,GAAIC,IAAUC,EACb,OAAOA,EAAQD,CAEjB,CACA,MAAO,EACR,CAAC,CACF,CAEO,IAAME,EAAN,KAAoB,CAC1B,MAAM,KACLC,EACAC,EAAuB,CAAC,EACF,CACtB,GAAI,CAWH,IAAMZ,GAVU,MAAMH,EAAG,OAAQ,CAChC,IAAKc,EACL,UAAW,GACX,MAAO,GACP,SAAU,GACV,KAAMC,EAAQ,MAAQ,IACtB,OAAQA,EAAQ,OAChB,IAAK,EACN,CAAC,GAEqB,IAAIC,GAAS,CAClC,IAAMC,EAAoB,WAASH,EAAYE,EAAM,IAAI,EACnDE,EACL,gBAAiBF,EAAM,MAAQA,EAAM,MAAM,YAAY,EAAI,GAE5D,MAAO,CACN,KAAW,WAASA,EAAM,IAAI,EAC9B,KAAMA,EAAM,KACZ,aAAAC,EACA,KAAMC,EAAc,YAAc,OAClC,KAAMF,EAAM,OAAO,KACnB,UACCE,IAAgB,GACR,UAAQF,EAAM,IAAI,EAAE,YAAY,EAAE,MAAM,CAAC,EAC9C,OACJ,MAAOC,EAAa,MAAW,KAAG,EAAE,OAAS,CAC9C,CACD,CAAC,EAEKZ,EAAWH,EAAgBC,CAAK,EAEtC,MAAO,CAAE,QAAS,GAAM,MAAAA,EAAO,SAAUE,CAAS,CACnD,OAASc,EAAO,CACf,MAAO,CACN,QAAS,GACT,MAAO,CAAC,EACR,SAAU,CAAC,EACX,MAAOA,aAAiB,MAAQA,EAAM,QAAU,eACjD,CACD,CACD,CACD,EC/EA,OAAOC,MAAQ,cACf,OAAOC,MAAU,OCDjB,OAAOC,MAAY,cACnB,OAAOC,MAAQ,cACf,OAAOC,MAAU,OCFjB,OAAOC,OAAY,cA0CZ,SAASC,EACfC,EACkB,CAClB,MAAO,CACN,MACC,OAAOA,EAAY,OAAU,SAAWA,EAAY,MAAQ,OAC7D,MACC,OAAOA,EAAY,OAAU,SAC1BA,EAAY,MACZ,OAAOA,EAAY,OAAU,SAC7B,SAASA,EAAY,MAAO,EAAE,EAC9B,OACJ,KAAM,OAAOA,EAAY,MAAS,SAAWA,EAAY,KAAO,OAChE,OACC,OAAOA,EAAY,QAAW,UAC3BA,EAAY,OACZA,EAAY,SAAW,OACvB,GACA,OACJ,WACC,OAAOA,EAAY,YAAe,UAC/BA,EAAY,WACZA,EAAY,aAAe,QAC3B,GACA,OACJ,KAAM,OAAOA,EAAY,MAAS,SAAWA,EAAY,KAAO,OAChE,KAAM,MAAM,QAAQA,EAAY,IAAI,EACjCA,EAAY,KAAK,OAAQC,GAAmB,OAAOA,GAAM,QAAQ,EACjE,OACH,OACC,OAAOD,EAAY,QAAW,SAAWA,EAAY,OAAS,MAChE,CACD,CAKO,SAASE,EACfF,EACmB,CACnB,MAAO,CACN,MACC,OAAOA,EAAY,OAAU,SAAWA,EAAY,MAAQ,OAC7D,MACC,OAAOA,EAAY,OAAU,SAC1BA,EAAY,MACZ,OAAOA,EAAY,OAAU,SAC7B,SAASA,EAAY,MAAO,EAAE,EAC9B,OACJ,KAAM,OAAOA,EAAY,MAAS,SAAWA,EAAY,KAAO,OAChE,OACC,OAAOA,EAAY,QAAW,UAC3BA,EAAY,OACZA,EAAY,SAAW,OACvB,GACA,OACJ,WACC,OAAOA,EAAY,YAAe,UAC/BA,EAAY,WACZA,EAAY,aAAe,QAC3B,GACA,OACJ,SACC,OAAOA,EAAY,UAAa,UAChC,CAAC,OAAQ,YAAa,aAAa,EAAE,SAASA,EAAY,QAAQ,EAC9DA,EAAY,SACb,MACL,CACD,CAKO,SAASG,EACfH,EACgB,CAChB,MAAO,CACN,MACC,OAAOA,EAAY,OAAU,SAAWA,EAAY,MAAQ,OAC7D,MACC,OAAOA,EAAY,OAAU,SAC1BA,EAAY,MACZ,OAAOA,EAAY,OAAU,SAC7B,SAASA,EAAY,MAAO,EAAE,EAC9B,OACJ,KAAM,OAAOA,EAAY,MAAS,SAAWA,EAAY,KAAO,OAChE,OACC,OAAOA,EAAY,QAAW,UAC3BA,EAAY,OACZA,EAAY,SAAW,OACvB,GACA,OACJ,WACC,OAAOA,EAAY,YAAe,UAC/BA,EAAY,WACZA,EAAY,aAAe,QAC3B,GACA,OACJ,YACC,OAAOA,EAAY,aAAgB,SAChCA,EAAY,YACZ,MACL,CACD,CAKO,SAASI,EACfJ,EACAK,EACiB,CACjB,MAAO,CACN,KAAM,SACN,QACC,OAAOL,EAAY,SAAY,UAC/B,CAAC,OAAQ,MAAM,EAAE,SAASA,EAAY,OAAO,EACzCA,EAAY,QACb,OACJ,MACC,OAAOA,EAAY,OAAU,SAAWA,EAAY,MAAQK,EAC7D,MACC,OAAOL,EAAY,OAAU,SAC1BA,EAAY,MACZ,OAAOA,EAAY,OAAU,SAC7B,SAASA,EAAY,MAAO,EAAE,EAC9B,OACJ,KAAM,OAAOA,EAAY,MAAS,SAAWA,EAAY,KAAO,OAChE,OACC,OAAOA,EAAY,QAAW,UAC3BA,EAAY,OACZA,EAAY,SAAW,OACvB,GACA,OACJ,WACC,OAAOA,EAAY,YAAe,UAC/BA,EAAY,aAAe,IAC3BA,EAAY,aAAe,QAC3B,IAEJ,IAAK,OAAOA,EAAY,KAAQ,SAAWA,EAAY,IAAM,OAC7D,MACC,OAAOA,EAAY,OAAU,UAC7B,CAAC,UAAW,YAAa,OAAO,EAAE,SAASA,EAAY,KAAK,EACxDA,EAAY,MACb,OACJ,OACC,OAAOA,EAAY,QAAW,UAC9B,CAAC,SAAU,OAAO,EAAE,SAASA,EAAY,MAAM,EAC3CA,EAAY,OACb,OACJ,SACC,OAAOA,EAAY,UAAa,UAChC,CAAC,UAAW,QAAQ,EAAE,SAASA,EAAY,QAAQ,EAC/CA,EAAY,SACb,SACL,CACD,CDtLA,IAAMM,EAAoB,CACzB,SAAU,2BACV,MAAO,uBACR,EAEMC,EAAiB,gBAKvB,eAAsBC,EAAiBC,EAAkB,CACxD,GAAI,CACH,IAAMC,EAAU,MAAMC,EAAG,SAASF,EAAU,OAAO,EAG7C,CAAE,KAAMG,EAAiB,CAAC,EAAG,QAASC,CAAgB,EAC3DC,EAAOJ,CAAO,EAEf,GAAIH,EAAe,KAAKQ,EAAK,SAASN,CAAQ,CAAC,EAAG,CACjD,IAAMO,EAAeD,EACnB,SAASN,EAAU,YAAY,EAC/B,QAAQ,QAAS,GAAG,EAEtB,MAAO,CAAE,YADcQ,EAAoBL,EAAgBI,CAAY,EACjC,QAASH,CAAgB,CAChE,CAGA,MAAO,CAAE,YADeK,EAAqBN,CAAc,EACpB,QAASC,CAAgB,CACjE,MAAgB,CACf,MAAO,CAAE,YAAa,KAAM,QAAS,EAAG,CACzC,CACD,CAKA,eAAsBM,EACrBC,EACAC,EACmD,CACnD,IAAMC,EACLD,IAAS,WAAa,uBAAyB,oBAC1CE,EAAeR,EAAK,KAAKK,EAAYE,CAAgB,EAE3D,GAAI,CACH,IAAMZ,EAAU,MAAMC,EAAG,SAASY,EAAc,OAAO,EACjD,CAAE,KAAAC,CAAK,EAAIV,EAAOJ,CAAO,EAE/B,OAAIW,IAAS,WACLI,EAAsBD,CAAI,EAE1BE,EAAmBF,CAAI,CAEhC,MAAgB,CAEf,OAAO,IACR,CACD,CAKO,SAASG,EACfC,EACAC,EAIC,CACD,IAAMC,EAAuBD,EAAmB,QAAQ,MAAO,GAAG,EAElE,QAAWE,KAAQH,EAIlB,GAHI,EAAAG,EAAK,OAAS,QAEFhB,EAAK,QAAQgB,EAAK,YAAY,EAAE,QAAQ,MAAO,GAAG,IAClDD,GAEhB,IAAIxB,EAAkB,SAAS,KAAKyB,EAAK,IAAI,EAC5C,MAAO,CAAE,KAAM,WAAY,KAAAA,CAAK,EAEjC,GAAIzB,EAAkB,MAAM,KAAKyB,EAAK,IAAI,EACzC,MAAO,CAAE,KAAM,QAAS,KAAAA,CAAK,EAI/B,MAAO,CAAE,KAAM,KAAM,KAAM,IAAK,CACjC,CAKA,eAAsBC,EACrBJ,EACAK,EAMC,CACD,IAAMC,EAAc,IAAI,IAMxB,QAAWH,KAAQH,EAAO,CACzB,GAAIG,EAAK,OAAS,QAAUA,EAAK,YAAc,KAAM,SAErD,IAAMI,EAAepB,EAAK,WAAWgB,EAAK,IAAI,EAC3CA,EAAK,KACLhB,EAAK,KAAKkB,EAAUF,EAAK,IAAI,EAEhC,GAAI,CACH,GAAM,CAAE,YAAAK,CAAY,EAAI,MAAM5B,EAAiB2B,CAAY,EAEvDC,GACHF,EAAY,IAAIH,EAAK,aAAcK,CAAW,CAEhD,MAAgB,CAEhB,CACD,CAGA,IAAMC,EAAmB,IAAI,IAC7B,QAAWN,KAAQH,EAAO,CACzB,GAAIG,EAAK,OAAS,YAAa,SAE/B,IAAMF,EAAqBE,EAAK,aAAa,QAAQ,MAAO,GAAG,EAC/D,GAAIM,EAAiB,IAAIR,CAAkB,EAAG,SAE9C,IAAMS,EAAeX,EAAiBC,EAAOC,CAAkB,EAC/D,GAAIS,EAAa,MAChB,GAAIA,EAAa,OAAS,WAAY,CACrC,IAAMC,EAAqBxB,EAAK,WAAWgB,EAAK,IAAI,EACjDA,EAAK,KACLhB,EAAK,KAAKkB,EAAUF,EAAK,IAAI,EAC1BS,EAAW,MAAMrB,EAAiBoB,EAAoB,UAAU,EAClEC,GACHN,EAAY,IAAIL,EAAoBW,CAAQ,CAE9C,SAAWF,EAAa,OAAS,QAAS,CACzC,IAAMC,EAAqBxB,EAAK,WAAWgB,EAAK,IAAI,EACjDA,EAAK,KACLhB,EAAK,KAAKkB,EAAUF,EAAK,IAAI,EAC1BS,EAAW,MAAMrB,EAAiBoB,EAAoB,OAAO,EAC/DC,GACHN,EAAY,IAAIL,EAAoBW,CAAQ,CAE9C,EAGDH,EAAiB,IAAIR,CAAkB,CACxC,CAEA,OAAOK,CACR,CDpJA,SAASO,EAAgBC,EAAaC,EAAuC,CAE5ED,EAAO,YAAY,IAAI,MAAOE,EAAUC,EAAUC,IAAc,CAC/D,GAAI,CACH,GAAI,CAACF,EAAI,IAAK,OAAOE,EAAK,EAG1B,IAAMC,EAAgBH,EAAI,IAAI,WAAW,qBAAqB,EACxDI,EAAoBJ,EAAI,IAAI,WAAW,yBAAyB,EAEtE,GAAI,CAACG,GAAiB,CAACC,EACtB,OAAOF,EAAK,EAGb,IAAMG,EAAM,IAAI,IAAIL,EAAI,IAAK,UAAUA,EAAI,QAAQ,MAAQ,WAAW,EAAE,EAGxE,GAAII,EAAmB,CACtB,IAAME,EAAcD,EAAI,aAAa,IAAI,UAAU,EAC7CE,EAAaF,EAAI,aAAa,IAAI,YAAY,GAAK,OAEzD,GAAI,CAACC,EAAa,CACjBL,EAAI,WAAa,IACjBA,EAAI,UAAU,eAAgB,kBAAkB,EAChDA,EAAI,IACH,KAAK,UAAU,CACd,QAAS,GACT,MAAO,gCACR,CAAC,CACF,EACA,MACD,CAGA,IAAMO,EAAWF,EAAY,WAAW,KAAM,GAAG,EAE3CG,EAAOV,EAAQ,MAAQD,EAAO,QAAQ,MAAQ,QAAQ,IAAI,EAI5DY,EAEAC,EAAK,WAAWH,CAAQ,EAE3BE,EAAmBF,EAGnBE,EAAmBC,EAAK,QAAQF,EAAMF,EAAYC,CAAQ,EAI3D,IAAMI,EAAaD,EAAK,QAAQF,EAAMF,CAAU,EAC1CM,EAAuBF,EAAK,UAAUD,CAAgB,EACtDI,EAAuBH,EAAK,UAAUC,CAAU,EAEtD,GAAI,CAACC,EAAqB,WAAWC,CAAoB,EAAG,CAC3Db,EAAI,WAAa,IACjBA,EAAI,UAAU,eAAgB,kBAAkB,EAChDA,EAAI,IACH,KAAK,UAAU,CACd,QAAS,GACT,MAAO,yCACR,CAAC,CACF,EACA,MACD,CAEA,GAAI,CAEH,MAAMc,EAAG,OAAOL,CAAgB,EAEhC,IAAMM,EAAa,MAAMD,EAAG,SAASL,EAAkB,OAAO,EACxDO,EAAQ,MAAMF,EAAG,KAAKL,CAAgB,EAGxCQ,EAAeF,EACfG,EAA2C,CAAC,EAOhD,GAJyBH,EAAW,MACnC,iCACD,EAEsB,CAErB,IAAMI,EAAS,KAAM,QAAO,aAAa,EACzC,GAAI,CAEHD,EADeC,EAAO,QAAQJ,CAAU,EACf,MAAQ,CAAC,CACnC,MAAgB,CAEhB,CAGAE,EAAeF,EAAW,QACzB,gCACA,EACD,CACD,CAGAE,EAAeA,EAAa,QAAQ,WAAY,EAAE,EAElDA,EAAeA,EAAa,QAAQ,WAAY,EAAE,EAElDjB,EAAI,UAAU,eAAgB,kBAAkB,EAChDA,EAAI,UAAU,gBAAiB,UAAU,EACzCA,EAAI,WAAa,IACjBA,EAAI,IACH,KAAK,UAAU,CACd,QAAS,GACT,QAASiB,EACT,YAAaC,EACb,SAAU,CACT,KAAMT,EACN,aAAcC,EAAK,SAASC,EAAYF,CAAgB,EACxD,KAAMC,EAAK,SAASD,CAAgB,EACpC,KAAMO,EAAM,KACZ,SAAUA,EAAM,MAChB,QAASA,EAAM,SAChB,CACD,CAAC,CACF,CACD,OAASI,EAAW,CACnB,IAAMC,EACLD,aAAqB,MAAQA,EAAU,QAAU,iBAClDpB,EAAI,WAAa,IACjBA,EAAI,UAAU,eAAgB,kBAAkB,EAChDA,EAAI,IACH,KAAK,UAAU,CACd,QAAS,GACT,MAAO,mBAAmBqB,CAAY,GACtC,cAAeZ,EACf,eAAgBG,EAChB,WAAYC,CACb,CAAC,CACF,CACD,CACA,MACD,CAGA,GAAIX,EAAe,CAClB,IAAMI,EAAaF,EAAI,aAAa,IAAI,YAAY,GAAK,GACnDkB,EAAiBlB,EAAI,aAAa,IAAI,SAAS,IAAM,OACrDmB,EAAiBnB,EAAI,aAAa,IAAI,eAAe,GAC1D,OACKoB,EAASpB,EAAI,aAAa,OAAO,QAAQ,GAAK,OAEpD,GAAI,CAACE,EAAY,CAChBN,EAAI,WAAa,IACjBA,EAAI,UAAU,eAAgB,kBAAkB,EAChDA,EAAI,IACH,KAAK,UAAU,CACd,QAAS,GACT,MAAO,mCACP,SAAU,CAAC,EACX,MAAO,CAAC,CACT,CAAC,CACF,EACA,MACD,CAEA,IAAMQ,EAAOV,EAAQ,MAAQD,EAAO,QAAQ,MAAQ,QAAQ,IAAI,EAC1D4B,EAAef,EAAK,QAAQF,EAAMF,CAAU,EAG5CoB,EAAS,MADC,IAAIC,EAAc,EACL,KAAKF,EAAc,CAAE,OAAAD,CAAO,CAAC,EAE1D,GAAIE,EAAO,QAAS,CAEnB,IAAME,EAAW,MAAMC,EAAgBH,EAAO,MAAOD,CAAY,EAG3DK,EAA+B,MAAM,QAAQ,IAClDJ,EAAO,MAAM,IAAI,MAAOK,GAA4B,CAEnD,GAAIA,EAAK,OAAS,QAAUA,EAAK,YAAc,KAC9C,GAAI,CACH,IAAMC,EAAetB,EAAK,WAAWqB,EAAK,IAAI,EAC3CA,EAAK,KACLrB,EAAK,KAAKe,EAAcM,EAAK,IAAI,EAE9BhB,EAAa,MAAMD,EAAG,SAASkB,EAAc,OAAO,EAGtDC,EAAUlB,EACWA,EAAW,MACnC,iCACD,IAGCkB,EAAUlB,EAAW,QACpB,gCACA,EACD,GAID,IAAMmB,EAAiBD,EACrB,QAAQ,WAAY,EAAE,EACtB,QAAQ,WAAY,EAAE,EACtB,UAAU,EAAG,GAAK,EAClB,YAAY,EAEd,MAAO,CACN,GAAGF,EACH,QAASG,CACV,CACD,MAAgB,CACf,OAAOH,CACR,CAED,OAAOA,CACR,CAAC,CACF,EAGIT,GACHa,EAAc,IAAIL,EAAkBL,EAAcF,CAAa,EAIhE,IAAMa,EAAuC,CAAC,EAC9C,OAAW,CAACC,EAAKC,CAAK,IAAKV,EAAS,QAAQ,EAC3CQ,EAAYC,CAAG,EAAIC,EAIpBtC,EAAI,UAAU,eAAgB,kBAAkB,EAChDA,EAAI,WAAa,IACjBA,EAAI,IACH,KAAK,UAAU,CACd,QAAS,GACT,SAAU0B,EAAO,SACjB,MAAOI,EACP,SAAUM,CACX,CAAC,CACF,CACD,MACCpC,EAAI,UAAU,eAAgB,kBAAkB,EAChDA,EAAI,WAAa,IACjBA,EAAI,IACH,KAAK,UAAU,CACd,QAAS,GACT,MAAO0B,EAAO,MACd,SAAU,CAAC,EACX,MAAO,CAAC,CACT,CAAC,CACF,CAEF,CACD,MAAgB,CACV1B,EAAI,cACRA,EAAI,WAAa,IACjBA,EAAI,UAAU,eAAgB,kBAAkB,EAChDA,EAAI,IACH,KAAK,UAAU,CACd,QAAS,GACT,MAAO,uBACR,CAAC,CACF,EAEF,CACD,CAAC,CACF,CAEO,SAASuC,EACfzC,EAAwC,CAAC,EACxC,CACD,MAAO,CACN,KAAM,2BACN,gBAAgBD,EAAa,CAC5BD,EAAgBC,EAAQC,CAAO,CAChC,EACA,uBAAuBD,EAAa,CACnCD,EAAgBC,EAAQC,CAAO,CAChC,CACD,CACD","names":["ConsoleLogger","files","folderPath","format","a","b","file","indent","icon","i","dirs","f","fileCount","fg","path","extractVersions","files","versionPattern","versions","file","a","b","aParts","bParts","aPart","bPart","FolderScanner","folderPath","options","entry","relativePath","isDirectory","error","fs","path","matter","fs","path","matter","parsePageFrontmatter","frontmatter","t","parseDropdownSettings","parseGroupSettings","parseButtonSettings","defaultTitle","SETTINGS_PATTERNS","BUTTON_PATTERN","readFileMetadata","filePath","content","fs","rawFrontmatter","markdownContent","matter","path","defaultTitle","parseButtonSettings","parsePageFrontmatter","readSettingsFile","folderPath","type","settingsFileName","settingsPath","data","parseDropdownSettings","parseGroupSettings","findSettingsFile","files","folderRelativePath","normalizedFolderPath","file","readAllMetadata","rootPath","metadataMap","absolutePath","frontmatter","processedFolders","settingsFile","absoluteFolderPath","settings","setupMiddleware","server","options","req","res","next","isScanRequest","isMarkdownRequest","url","rawFilePath","folderPath","filePath","root","absoluteFilePath","path","docsFolder","normalizedTargetFile","normalizedDocsFolder","fs","rawContent","stats","cleanContent","frontmatterData","matter","fileError","errorMessage","consoleEnabled","consoleFormat","ignore","targetFolder","result","FolderScanner","metadata","readAllMetadata","filesWithContent","file","absolutePath","content","indexedContent","ConsoleLogger","metadataObj","key","value","dockRushScannerPlugin"]}