docmk 1.0.3 → 1.0.5
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.
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/package.json +6 -1
- package/src/builder/vite-dev.ts +4 -1
- package/src/cli/commands/dev.ts +3 -3
- package/.claude/skills/pdf/SKILL.md +0 -89
- package/.claude/skills/web-scraping/SKILL.md +0 -78
- package/CLAUDE.md +0 -90
- package/final-site/assets/main-B4orIFxK.css +0 -1
- package/final-site/assets/main-CSoKXua6.js +0 -25
- package/final-site/favicon.svg +0 -4
- package/final-site/index.html +0 -26
- package/final-site/robots.txt +0 -4
- package/final-site/sitemap.xml +0 -14
- package/my-docs/api/README.md +0 -152
- package/my-docs/api/advanced.md +0 -260
- package/my-docs/getting-started/README.md +0 -24
- package/my-docs/tutorials/README.md +0 -272
- package/my-docs/tutorials/customization.md +0 -492
- package/postcss.config.js +0 -6
- package/site/assets/main-BZUsYUCF.css +0 -1
- package/site/assets/main-q6laQtCD.js +0 -114
- package/site/favicon.svg +0 -4
- package/site/index.html +0 -23
- package/site/robots.txt +0 -4
- package/site/sitemap.xml +0 -34
- package/site-output/assets/main-B4orIFxK.css +0 -1
- package/site-output/assets/main-CSoKXua6.js +0 -25
- package/site-output/favicon.svg +0 -4
- package/site-output/index.html +0 -26
- package/site-output/robots.txt +0 -4
- package/site-output/sitemap.xml +0 -14
- package/tailwind.config.js +0 -65
- package/test-build/assets/main-C2ARPC0e.css +0 -1
- package/test-build/assets/main-CHIQpV3B.js +0 -25
- package/test-build/favicon.svg +0 -4
- package/test-build/index.html +0 -47
- package/test-build/robots.txt +0 -4
- package/test-build/sitemap.xml +0 -19
- package/test-dist/assets/main-B4orIFxK.css +0 -1
- package/test-dist/assets/main-CSoKXua6.js +0 -25
- package/test-dist/favicon.svg +0 -4
- package/test-dist/index.html +0 -26
- package/test-dist/robots.txt +0 -4
- package/test-dist/sitemap.xml +0 -14
- package/tsconfig.json +0 -30
- package/tsup.config.ts +0 -13
- package/vite.config.ts +0 -21
package/dist/index.js
CHANGED
|
@@ -386,12 +386,13 @@ async function createViteDevServer(options) {
|
|
|
386
386
|
}
|
|
387
387
|
});
|
|
388
388
|
await server.listen();
|
|
389
|
+
const actualPort = server.config.server.port;
|
|
389
390
|
const originalClose = server.close.bind(server);
|
|
390
391
|
server.close = async () => {
|
|
391
392
|
await watcher?.close();
|
|
392
393
|
return originalClose();
|
|
393
394
|
};
|
|
394
|
-
return server;
|
|
395
|
+
return { server, actualPort };
|
|
395
396
|
}
|
|
396
397
|
|
|
397
398
|
// src/cli/commands/dev.ts
|
|
@@ -412,12 +413,12 @@ async function devCommand(options) {
|
|
|
412
413
|
skillsDir: sourceDir,
|
|
413
414
|
outputDir: "dist"
|
|
414
415
|
});
|
|
415
|
-
const server = await createViteDevServer({
|
|
416
|
+
const { server, actualPort } = await createViteDevServer({
|
|
416
417
|
port,
|
|
417
418
|
skillsDir: sourceDir,
|
|
418
419
|
config
|
|
419
420
|
});
|
|
420
|
-
console.log(`\u{1F389} Dev server running at http://localhost:${
|
|
421
|
+
console.log(`\u{1F389} Dev server running at http://localhost:${actualPort}`);
|
|
421
422
|
process.on("SIGINT", async () => {
|
|
422
423
|
console.log("\\n\u{1F44B} Shutting down dev server...");
|
|
423
424
|
await server.close();
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/scanner/index.ts","../src/parser/index.ts","../src/cli/index.ts","../src/cli/commands/dev.ts","../src/builder/vite-dev.ts","../src/cli/commands/build.ts","../src/builder/index.ts","../src/cli/commands/preview.ts"],"sourcesContent":["import fs from 'fs/promises'\nimport path from 'path'\nimport { SkillDirectory, SkillFile } from '../types/index.js'\n\nexport async function scanSkillsDirectory(sourceDir: string): Promise<SkillDirectory[]> {\n try {\n await fs.access(sourceDir)\n } catch {\n throw new Error(`Source directory not found: ${sourceDir}`)\n }\n\n const entries = await fs.readdir(sourceDir, { withFileTypes: true })\n const directories: SkillDirectory[] = []\n\n for (const entry of entries) {\n if (entry.isDirectory()) {\n const dirPath = path.join(sourceDir, entry.name)\n const skillDirectory = await scanDirectory(dirPath, entry.name)\n directories.push(skillDirectory)\n }\n }\n\n return directories\n}\n\nasync function scanDirectory(dirPath: string, name: string): Promise<SkillDirectory> {\n const entries = await fs.readdir(dirPath, { withFileTypes: true })\n const children: (SkillDirectory | SkillFile)[] = []\n let skillFile: SkillFile | undefined\n\n for (const entry of entries) {\n const entryPath = path.join(dirPath, entry.name)\n\n if (entry.isDirectory()) {\n const subDir = await scanDirectory(entryPath, entry.name)\n children.push(subDir)\n } else if (entry.isFile() && entry.name.endsWith('.md')) {\n const file = await parseMarkdownFile(entryPath, entry.name)\n \n if (entry.name === 'SKILL.md') {\n skillFile = file\n } else {\n children.push(file)\n }\n }\n }\n\n return {\n path: dirPath,\n name,\n children,\n skillFile\n }\n}\n\nasync function parseMarkdownFile(filePath: string, fileName: string): Promise<SkillFile> {\n const content = await fs.readFile(filePath, 'utf-8')\n const stats = await fs.stat(filePath)\n\n // Basic frontmatter extraction\n const frontmatterMatch = content.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n/)\n const frontmatter: Record<string, any> = {}\n let markdownContent = content\n\n if (frontmatterMatch) {\n // Remove frontmatter from content\n markdownContent = content.slice(frontmatterMatch[0].length)\n \n // Parse frontmatter key-value pairs\n const fmLines = frontmatterMatch[1].split(/\\r?\\n/)\n for (const line of fmLines) {\n const colonIndex = line.indexOf(':')\n if (colonIndex > 0) {\n const key = line.slice(0, colonIndex).trim()\n const value = line.slice(colonIndex + 1).trim().replace(/^[\"']|[\"']$/g, '')\n if (key && value) {\n frontmatter[key] = value\n }\n }\n }\n }\n\n return {\n path: filePath,\n name: fileName,\n title: frontmatter.title || fileName.replace('.md', ''),\n description: frontmatter.description,\n content: markdownContent,\n frontmatter,\n lastModified: stats.mtime.getTime()\n }\n}\n\nexport async function watchSkillsDirectory(\n sourceDir: string, \n callback: (directories: SkillDirectory[]) => void\n) {\n const chokidar = await import('chokidar')\n \n const watcher = chokidar.watch(sourceDir, {\n ignored: /node_modules/,\n persistent: true,\n ignoreInitial: true\n })\n\n let debounceTimer: NodeJS.Timeout\n\n const handleChange = () => {\n clearTimeout(debounceTimer)\n debounceTimer = setTimeout(async () => {\n try {\n const directories = await scanSkillsDirectory(sourceDir)\n callback(directories)\n } catch (error) {\n console.error('Error rescanning source directory:', error)\n }\n }, 300)\n }\n\n watcher.on('add', handleChange)\n watcher.on('change', handleChange)\n watcher.on('unlink', handleChange)\n watcher.on('addDir', handleChange)\n watcher.on('unlinkDir', handleChange)\n\n return watcher\n}","import matter from 'gray-matter'\nimport MarkdownIt from 'markdown-it'\nimport { createHighlighter, Highlighter } from 'shiki'\nimport { SkillDirectory, SkillFile, SiteConfig, DocGenConfig, Navigation } from '../types/index.js'\n\nlet highlighter: Highlighter | null = null\n\nasync function getHighlighter() {\n if (!highlighter) {\n highlighter = await createHighlighter({\n themes: ['github-dark', 'github-light'],\n langs: ['javascript', 'typescript', 'bash', 'shell', 'json', 'html', 'css', 'vue', 'jsx', 'tsx', 'python', 'markdown', 'yaml', 'sql', 'go', 'rust', 'java', 'c', 'cpp']\n })\n }\n return highlighter\n}\n\nconst md = new MarkdownIt({\n html: true,\n linkify: true,\n typographer: true,\n highlight: function (str: string, lang: string) {\n // Synchronous fallback - actual highlighting done in enhanceFileContent\n return `<pre class=\"shiki-pending\" data-lang=\"${lang || 'text'}\"><code>${md.utils.escapeHtml(str)}</code></pre>`\n }\n})\n\nexport async function parseSkillsToConfig(\n directories: SkillDirectory[],\n siteConfig: SiteConfig\n): Promise<DocGenConfig> {\n const files: SkillFile[] = []\n const navigation = await generateNavigation(directories)\n\n // Collect all files for search indexing\n collectAllFiles(directories, files)\n\n // Enhance files with parsed content\n for (const file of files) {\n await enhanceFileContent(file)\n }\n\n return {\n siteConfig,\n navigation,\n files,\n directories\n }\n}\n\nfunction collectAllFiles(items: (SkillDirectory | SkillFile)[], files: SkillFile[]) {\n for (const item of items) {\n if ('content' in item) {\n // It's a SkillFile\n files.push(item)\n } else {\n // It's a SkillDirectory\n if (item.skillFile) {\n files.push(item.skillFile)\n }\n collectAllFiles(item.children, files)\n }\n }\n}\n\nasync function enhanceFileContent(file: SkillFile) {\n try {\n let content = file.content\n\n // Update title and description from frontmatter if available\n file.title = file.frontmatter.title || file.title\n file.description = file.frontmatter.description || file.description\n\n // Remove duplicate h1 if it matches frontmatter title\n if (file.frontmatter.title) {\n // Match first h1 heading in content (may have leading whitespace/newlines)\n const h1Match = content.match(/^\\s*#\\s+(.+)$/m)\n if (h1Match) {\n const h1Text = h1Match[1].trim()\n // If h1 matches title, remove it to avoid duplication\n if (h1Text === file.frontmatter.title) {\n content = content.replace(/^\\s*#\\s+.+\\n*/, '')\n }\n }\n }\n\n // Render markdown to HTML\n let html = md.render(content)\n \n // Apply syntax highlighting with shiki\n html = await highlightCodeBlocks(html)\n file.frontmatter.html = html\n\n // Extract headings for TOC\n const headings = extractHeadings(content)\n file.frontmatter.headings = headings\n\n } catch (error) {\n console.warn(`Failed to enhance content for ${file.path}:`, error)\n try {\n file.frontmatter.html = md.render(file.content)\n } catch (e) {\n console.error(`Failed to render markdown for ${file.path}:`, e)\n }\n }\n}\n\nasync function highlightCodeBlocks(html: string): Promise<string> {\n const hl = await getHighlighter()\n \n // Find all pending shiki code blocks and highlight them\n const codeBlockRegex = /<pre class=\"shiki-pending\" data-lang=\"([^\"]*)\"[^>]*><code>([^]*?)<\\/code><\\/pre>/g\n \n const matches = [...html.matchAll(codeBlockRegex)]\n \n for (const match of matches) {\n const [fullMatch, lang, escapedCode] = match\n // Unescape HTML entities\n const code = escapedCode\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/&/g, '&')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\")\n \n try {\n const validLang = hl.getLoadedLanguages().includes(lang) ? lang : 'text'\n const highlighted = hl.codeToHtml(code, {\n lang: validLang,\n theme: 'github-dark'\n })\n html = html.replace(fullMatch, highlighted)\n } catch (e) {\n // Keep original if highlighting fails\n console.warn(`Failed to highlight ${lang}:`, e)\n }\n }\n \n return html\n}\n\nfunction extractHeadings(content: string) {\n const headings: Array<{ level: number; text: string; anchor: string }> = []\n const lines = content.split('\\n')\n\n for (const line of lines) {\n const match = line.match(/^(#{1,6})\\\\s+(.+)$/)\n if (match) {\n const level = match[1].length\n const text = match[2].trim()\n const anchor = text.toLowerCase()\n .replace(/[^\\\\w\\\\s-]/g, '')\n .replace(/\\\\s+/g, '-')\n .trim()\n\n headings.push({ level, text, anchor })\n }\n }\n\n return headings\n}\n\nasync function generateNavigation(directories: SkillDirectory[]): Promise<Navigation[]> {\n const navigation: Navigation[] = []\n\n for (const dir of directories) {\n const navItem: Navigation = {\n text: dir.skillFile?.title || formatDirName(dir.name),\n link: dir.skillFile ? getFileRoute(dir.skillFile) : undefined\n }\n\n if (dir.children.length > 0) {\n navItem.children = []\n \n for (const child of dir.children) {\n if ('content' in child) {\n // It's a file\n navItem.children.push({\n text: child.title || formatFileName(child.name),\n link: getFileRoute(child)\n })\n } else {\n // It's a subdirectory\n const subNav = await generateNavigation([child])\n navItem.children.push(...subNav)\n }\n }\n }\n\n navigation.push(navItem)\n }\n\n return navigation\n}\n\nfunction getFileRoute(file: SkillFile): string {\n // Import the utility function from client utils\n // For now, use inline implementation\n const filePath = file.path\n \n // Try common patterns\n const patterns = ['my-docs', 'docs', 'documentation', '.claude/skills']\n let relativePath = ''\n \n for (const pattern of patterns) {\n const index = filePath.indexOf(pattern)\n if (index !== -1) {\n relativePath = filePath.slice(index + pattern.length)\n break\n }\n }\n \n if (!relativePath) {\n // Fallback: use last 2 segments\n const segments = filePath.split('/').filter(Boolean)\n if (segments.length >= 2) {\n relativePath = '/' + segments.slice(-2).join('/')\n } else {\n return '/'\n }\n }\n \n const segments = relativePath.split('/').filter(Boolean)\n \n if (segments.length === 0) return '/'\n \n // Remove file extension\n const lastSegment = segments[segments.length - 1]\n if (lastSegment.endsWith('.md')) {\n segments[segments.length - 1] = lastSegment.slice(0, -3)\n }\n \n // SKILL.md and README.md should map to parent directory route\n const finalSegment = segments[segments.length - 1]\n if (finalSegment === 'SKILL' || finalSegment === 'README') {\n segments.pop()\n }\n \n return '/' + segments.join('/')\n}\n\nfunction formatDirName(name: string): string {\n return name.split('-').map(word => \n word.charAt(0).toUpperCase() + word.slice(1)\n ).join(' ')\n}\n\nfunction formatFileName(name: string): string {\n const baseName = name.replace('.md', '')\n return formatDirName(baseName)\n}\n\nexport { md as markdownRenderer }","#!/usr/bin/env node\n\nimport { Command } from 'commander'\nimport { devCommand } from './commands/dev.js'\nimport { buildCommand } from './commands/build.js'\nimport { previewCommand } from './commands/preview.js'\n\nconst program = new Command()\n\nprogram\n .name('docmk')\n .description('CLI tool for generating documentation from any directory')\n .version('1.0.0')\n .argument('[directory]', 'Source directory path (starts dev server)', './docs')\n .option('-p, --port <port>', 'Port to run dev server on', '3000')\n .action((directory, options) => {\n // Default action: start dev server\n devCommand({ dir: directory, port: options.port })\n })\n\nprogram\n .command('dev')\n .description('Start development server')\n .option('-p, --port <port>', 'Port to run dev server on', '3000')\n .option('-d, --dir <directory>', 'Source directory path', './docs')\n .action(devCommand)\n\nprogram\n .command('build')\n .description('Build static documentation site')\n .option('-d, --dir <directory>', 'Source directory path', './docs')\n .option('-o, --output <directory>', 'Output directory', 'dist')\n .action(buildCommand)\n\nprogram\n .command('preview')\n .description('Preview built documentation site')\n .option('-p, --port <port>', 'Port to run preview server on', '4173')\n .option('-o, --output <directory>', 'Built site directory', 'dist')\n .action(previewCommand)\n\nprogram.parse()","import path from 'path'\nimport { createViteDevServer } from '../../builder/vite-dev.js'\nimport { scanSkillsDirectory } from '../../scanner/index.js'\nimport { parseSkillsToConfig } from '../../parser/index.js'\n\ninterface DevOptions {\n port: string\n dir: string\n}\n\nexport async function devCommand(options: DevOptions) {\n console.log('🚀 Starting DocGen development server...')\n \n const sourceDir = path.resolve(process.cwd(), options.dir)\n const port = parseInt(options.port, 10)\n \n try {\n // Check if source directory exists\n console.log(`📁 Scanning source directory: ${sourceDir}`)\n const directories = await scanSkillsDirectory(sourceDir)\n \n console.log(`✅ Found ${directories.length} directories`)\n \n // Parse to config\n const config = await parseSkillsToConfig(directories, {\n title: 'Documentation',\n description: 'Documentation generated from source directory',\n baseUrl: '/',\n skillsDir: sourceDir,\n outputDir: 'dist'\n })\n \n // Start Vite dev server\n const server = await createViteDevServer({\n port,\n skillsDir: sourceDir,\n config\n })\n \n console.log(`🎉 Dev server running at http://localhost:${port}`)\n \n // Handle graceful shutdown\n process.on('SIGINT', async () => {\n console.log('\\\\n👋 Shutting down dev server...')\n await server.close()\n process.exit(0)\n })\n \n } catch (error) {\n console.error('❌ Failed to start dev server:', error)\n process.exit(1)\n }\n}","import { createServer, ViteDevServer } from 'vite'\nimport vue from '@vitejs/plugin-vue'\nimport path from 'path'\nimport { fileURLToPath } from 'url'\nimport { DocGenConfig } from '../types/index.js'\nimport { scanSkillsDirectory, watchSkillsDirectory } from '../scanner/index.js'\nimport { parseSkillsToConfig } from '../parser/index.js'\n\n// Get the package root directory (where src/client is located)\nconst __dirname = path.dirname(fileURLToPath(import.meta.url))\nconst packageRoot = path.resolve(__dirname, '../..')\n\ninterface DevServerOptions {\n port: number\n skillsDir: string\n config: DocGenConfig\n}\n\nexport async function createViteDevServer(options: DevServerOptions): Promise<ViteDevServer> {\n let currentConfig = options.config\n\n const server = await createServer({\n root: path.resolve(packageRoot, 'src/client'),\n server: {\n port: options.port,\n host: 'localhost'\n },\n publicDir: false, // Disable public dir in dev mode\n plugins: [\n vue(),\n // Custom plugin to inject config and handle API routes\n {\n name: 'docgen-dev',\n configureServer(server) {\n // API endpoint for configuration\n server.middlewares.use('/api/config', (req, res, next) => {\n if (req.method === 'GET') {\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify(currentConfig))\n } else {\n next()\n }\n })\n\n // SPA fallback - return index.html for all non-API routes\n return () => {\n server.middlewares.use((req, res, next) => {\n // Skip API routes and vite's own routes\n if (req.url?.startsWith('/api/') || req.url?.startsWith('/@') || req.url?.includes('.html')) {\n return next()\n }\n // For all other routes, serve index.html for SPA routing\n if (req.url && !req.url.includes('.')) {\n req.url = '/index.html'\n }\n next()\n })\n }\n },\n transformIndexHtml: {\n order: 'pre',\n handler(html) {\n // Inject lightweight config placeholder\n // Full config will be loaded via API to avoid HTML parsing issues\n const configScript = `\n <script>\n // Config will be loaded from /api/config\n globalThis.__DOCGEN_CONFIG__ = null;\n </script>\n `\n return html.replace('<head>', `<head>${configScript}`)\n }\n }\n }\n ],\n resolve: {\n alias: {\n '@': path.resolve(packageRoot, 'src/client')\n }\n },\n define: {\n __DOCGEN_CONFIG__: JSON.stringify(currentConfig)\n }\n })\n\n // Watch skills directory for changes\n const watcher = await watchSkillsDirectory(options.skillsDir, async (directories) => {\n console.log('📝 Skills directory changed, updating configuration...')\n \n try {\n currentConfig = await parseSkillsToConfig(directories, currentConfig.siteConfig)\n \n // Notify all connected clients to reload\n server.ws.send({\n type: 'full-reload'\n })\n \n console.log('✅ Configuration updated')\n } catch (error) {\n console.error('❌ Failed to update configuration:', error)\n \n // Send error to clients\n server.ws.send({\n type: 'error',\n err: {\n message: `Failed to update configuration: ${error.message}`,\n stack: error.stack\n }\n })\n }\n })\n\n // Start the server\n await server.listen()\n\n // Extend server with cleanup method\n const originalClose = server.close.bind(server)\n server.close = async () => {\n await watcher?.close()\n return originalClose()\n }\n\n return server\n}\n\nexport async function createProductionBuild() {\n // This will be implemented in the builder\n}","import path from 'path'\nimport { buildSite } from '../../builder/index.js'\nimport { scanSkillsDirectory } from '../../scanner/index.js'\nimport { parseSkillsToConfig } from '../../parser/index.js'\n\ninterface BuildOptions {\n dir: string\n output: string\n}\n\nexport async function buildCommand(options: BuildOptions) {\n console.log('🏗️ Building DocGen documentation site...')\n \n const sourceDir = path.resolve(process.cwd(), options.dir)\n const outputDir = path.resolve(process.cwd(), options.output)\n \n try {\n // Scan source directory\n console.log(`📁 Scanning source directory: ${sourceDir}`)\n const directories = await scanSkillsDirectory(sourceDir)\n \n console.log(`✅ Found ${directories.length} directories`)\n \n // Parse to config\n const config = await parseSkillsToConfig(directories, {\n title: 'Documentation',\n description: 'Documentation generated from source directory',\n baseUrl: '/',\n skillsDir: sourceDir,\n outputDir\n })\n \n // Build site\n console.log(`📦 Building to: ${outputDir}`)\n await buildSite({\n input: sourceDir,\n output: outputDir,\n mode: 'production'\n }, config)\n \n console.log('✅ Build completed successfully!')\n console.log(`📂 Built files are in: ${outputDir}`)\n \n } catch (error) {\n console.error('❌ Build failed:', error)\n process.exit(1)\n }\n}","import { build } from 'vite'\nimport vue from '@vitejs/plugin-vue'\nimport path from 'path'\nimport { fileURLToPath } from 'url'\nimport fs from 'fs/promises'\nimport { DocGenConfig, BuildOptions } from '../types/index.js'\n\n// Get the package root directory\nconst __dirname = path.dirname(fileURLToPath(import.meta.url))\nconst packageRoot = path.resolve(__dirname, '../..')\n\nexport async function buildSite(options: BuildOptions, config: DocGenConfig) {\n console.log('🏗️ Building documentation site...')\n\n // Ensure output directory exists\n await fs.mkdir(options.output, { recursive: true })\n\n // Write config to a temporary file for the build process\n const configPath = path.join(process.cwd(), 'temp-config.json')\n await fs.writeFile(configPath, JSON.stringify(config, null, 2))\n\n try {\n // Build the client application\n await build({\n root: path.resolve(packageRoot, 'src/client'),\n base: config.siteConfig.baseUrl,\n build: {\n outDir: options.output,\n emptyOutDir: true,\n rollupOptions: {\n input: {\n main: path.resolve(packageRoot, 'src/client/index.html')\n }\n }\n },\n plugins: [\n vue(),\n // Plugin to inject config during build\n {\n name: 'docgen-build',\n transformIndexHtml: {\n order: 'pre',\n handler(html) {\n // Encode config as base64 with proper UTF-8 handling\n const configJson = JSON.stringify(config)\n // Use encodeURIComponent to handle UTF-8 properly before base64\n const utf8Encoded = unescape(encodeURIComponent(configJson))\n const configBase64 = Buffer.from(utf8Encoded, 'binary').toString('base64')\n // Decode: atob -> decodeURIComponent(escape()) to restore UTF-8\n const configScript = `<script>globalThis.__DOCGEN_CONFIG__=JSON.parse(decodeURIComponent(escape(atob(\"${configBase64}\"))));</script>`\n return html.replace('</head>', `${configScript}\\n</head>`)\n }\n }\n }\n ],\n resolve: {\n alias: {\n '@': path.resolve(packageRoot, 'src/client')\n }\n },\n define: {\n __DOCGEN_CONFIG__: JSON.stringify(config)\n }\n })\n \n // Generate additional static files\n await generateSitemap(options.output, config)\n await generateRobotsTxt(options.output, config)\n await copyAssets(options.output)\n \n console.log('✅ Build completed successfully!')\n \n } finally {\n // Clean up temporary config file\n try {\n await fs.unlink(configPath)\n } catch {\n // Ignore if file doesn't exist\n }\n }\n}\n\nasync function generateSitemap(outputDir: string, config: DocGenConfig) {\n console.log('📄 Generating sitemap...')\n \n const baseUrl = config.siteConfig.baseUrl.replace(/\\/$/, '')\n const urls: string[] = []\n \n // Add home page\n urls.push(`${baseUrl}/`)\n \n // Add all skill pages\n for (const file of config.files) {\n const route = getFileRoute(file.path, config.siteConfig.skillsDir)\n if (route !== '/') {\n urls.push(`${baseUrl}${route}`)\n }\n }\n \n const sitemap = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n${urls.map(url => ` <url>\n <loc>${url}</loc>\n <lastmod>${new Date().toISOString().split('T')[0]}</lastmod>\n <changefreq>weekly</changefreq>\n <priority>0.8</priority>\n </url>`).join('\\\\n')}\n</urlset>`\n \n await fs.writeFile(path.join(outputDir, 'sitemap.xml'), sitemap)\n}\n\nasync function generateRobotsTxt(outputDir: string, config: DocGenConfig) {\n console.log('🤖 Generating robots.txt...')\n \n const baseUrl = config.siteConfig.baseUrl.replace(/\\/$/, '')\n const robotsTxt = `User-agent: *\nAllow: /\n\nSitemap: ${baseUrl}/sitemap.xml`\n \n await fs.writeFile(path.join(outputDir, 'robots.txt'), robotsTxt)\n}\n\nasync function copyAssets(outputDir: string) {\n console.log('📁 Copying static assets...')\n \n // Create a simple favicon if it doesn't exist\n const faviconPath = path.join(outputDir, 'favicon.ico')\n \n try {\n await fs.access(faviconPath)\n } catch {\n // Create a simple SVG favicon\n const faviconSvg = `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\">\n <rect width=\"32\" height=\"32\" rx=\"4\" fill=\"#3182ce\"/>\n <text x=\"16\" y=\"22\" text-anchor=\"middle\" fill=\"white\" font-family=\"system-ui\" font-size=\"18\" font-weight=\"bold\">D</text>\n</svg>`\n \n await fs.writeFile(path.join(outputDir, 'favicon.svg'), faviconSvg)\n }\n}\n\nfunction getFileRoute(filePath: string, baseDir: string): string {\n // Normalize paths for comparison\n const normalizedFilePath = path.normalize(filePath)\n const normalizedBaseDir = path.normalize(baseDir)\n \n // Check if file is within base directory\n if (!normalizedFilePath.startsWith(normalizedBaseDir)) {\n return '/'\n }\n \n // Get relative path from base directory\n const relativePath = normalizedFilePath.slice(normalizedBaseDir.length)\n const segments = relativePath.split(path.sep).filter(Boolean)\n \n if (segments.length === 0) return '/'\n \n const lastSegment = segments[segments.length - 1]\n if (lastSegment.endsWith('.md')) {\n segments[segments.length - 1] = lastSegment.slice(0, -3)\n }\n \n // SKILL.md and README.md should map to parent directory route\n const finalSegment = segments[segments.length - 1]\n if (finalSegment === 'SKILL' || finalSegment === 'README') {\n segments.pop()\n }\n\n return '/' + segments.join('/')\n}\n\nexport async function buildForProduction(sourceDir: string, outputDir: string): Promise<DocGenConfig> {\n const { scanSkillsDirectory } = await import('../scanner/index.js')\n const { parseSkillsToConfig } = await import('../parser/index.js')\n \n const directories = await scanSkillsDirectory(sourceDir)\n const config = await parseSkillsToConfig(directories, {\n title: 'Documentation',\n description: 'Documentation generated from source directory',\n baseUrl: '/',\n skillsDir: sourceDir,\n outputDir\n })\n \n await buildSite({\n input: sourceDir,\n output: outputDir,\n mode: 'production'\n }, config)\n \n return config\n}","import path from 'path'\nimport fs from 'fs/promises'\nimport sirv from 'sirv'\nimport { createServer } from 'http'\n\ninterface PreviewOptions {\n port: string\n output: string\n}\n\nexport async function previewCommand(options: PreviewOptions) {\n console.log('👀 Starting DocGen preview server...')\n \n const outputDir = path.resolve(process.cwd(), options.output)\n const port = parseInt(options.port, 10)\n \n try {\n // Check if build directory exists\n try {\n await fs.access(outputDir)\n } catch {\n console.error(`❌ Build directory not found: ${outputDir}`)\n console.log('💡 Run \"docgen build\" first to generate the static site')\n process.exit(1)\n }\n \n // Create static file server\n const serve = sirv(outputDir, {\n single: true, // SPA mode\n dev: false,\n setHeaders: (res, pathname) => {\n if (pathname.endsWith('.html') || pathname === '/') {\n res.setHeader('Content-Type', 'text/html; charset=utf-8')\n }\n }\n })\n \n const server = createServer(serve)\n \n server.listen(port, () => {\n console.log(`🎉 Preview server running at http://localhost:${port}`)\n console.log(`📂 Serving files from: ${outputDir}`)\n })\n \n // Handle graceful shutdown\n process.on('SIGINT', () => {\n console.log('\\\\n👋 Shutting down preview server...')\n server.close(() => {\n process.exit(0)\n })\n })\n \n } catch (error) {\n console.error('❌ Failed to start preview server:', error)\n process.exit(1)\n }\n}"],"mappings":"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","names":["segments","path","path","server","path","path","vue","path","fileURLToPath","fs","__dirname","packageRoot","getFileRoute","path","path","fs","createServer"]}
|
|
1
|
+
{"version":3,"sources":["../src/scanner/index.ts","../src/parser/index.ts","../src/cli/index.ts","../src/cli/commands/dev.ts","../src/builder/vite-dev.ts","../src/cli/commands/build.ts","../src/builder/index.ts","../src/cli/commands/preview.ts"],"sourcesContent":["import fs from 'fs/promises'\nimport path from 'path'\nimport { SkillDirectory, SkillFile } from '../types/index.js'\n\nexport async function scanSkillsDirectory(sourceDir: string): Promise<SkillDirectory[]> {\n try {\n await fs.access(sourceDir)\n } catch {\n throw new Error(`Source directory not found: ${sourceDir}`)\n }\n\n const entries = await fs.readdir(sourceDir, { withFileTypes: true })\n const directories: SkillDirectory[] = []\n\n for (const entry of entries) {\n if (entry.isDirectory()) {\n const dirPath = path.join(sourceDir, entry.name)\n const skillDirectory = await scanDirectory(dirPath, entry.name)\n directories.push(skillDirectory)\n }\n }\n\n return directories\n}\n\nasync function scanDirectory(dirPath: string, name: string): Promise<SkillDirectory> {\n const entries = await fs.readdir(dirPath, { withFileTypes: true })\n const children: (SkillDirectory | SkillFile)[] = []\n let skillFile: SkillFile | undefined\n\n for (const entry of entries) {\n const entryPath = path.join(dirPath, entry.name)\n\n if (entry.isDirectory()) {\n const subDir = await scanDirectory(entryPath, entry.name)\n children.push(subDir)\n } else if (entry.isFile() && entry.name.endsWith('.md')) {\n const file = await parseMarkdownFile(entryPath, entry.name)\n \n if (entry.name === 'SKILL.md') {\n skillFile = file\n } else {\n children.push(file)\n }\n }\n }\n\n return {\n path: dirPath,\n name,\n children,\n skillFile\n }\n}\n\nasync function parseMarkdownFile(filePath: string, fileName: string): Promise<SkillFile> {\n const content = await fs.readFile(filePath, 'utf-8')\n const stats = await fs.stat(filePath)\n\n // Basic frontmatter extraction\n const frontmatterMatch = content.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n/)\n const frontmatter: Record<string, any> = {}\n let markdownContent = content\n\n if (frontmatterMatch) {\n // Remove frontmatter from content\n markdownContent = content.slice(frontmatterMatch[0].length)\n \n // Parse frontmatter key-value pairs\n const fmLines = frontmatterMatch[1].split(/\\r?\\n/)\n for (const line of fmLines) {\n const colonIndex = line.indexOf(':')\n if (colonIndex > 0) {\n const key = line.slice(0, colonIndex).trim()\n const value = line.slice(colonIndex + 1).trim().replace(/^[\"']|[\"']$/g, '')\n if (key && value) {\n frontmatter[key] = value\n }\n }\n }\n }\n\n return {\n path: filePath,\n name: fileName,\n title: frontmatter.title || fileName.replace('.md', ''),\n description: frontmatter.description,\n content: markdownContent,\n frontmatter,\n lastModified: stats.mtime.getTime()\n }\n}\n\nexport async function watchSkillsDirectory(\n sourceDir: string, \n callback: (directories: SkillDirectory[]) => void\n) {\n const chokidar = await import('chokidar')\n \n const watcher = chokidar.watch(sourceDir, {\n ignored: /node_modules/,\n persistent: true,\n ignoreInitial: true\n })\n\n let debounceTimer: NodeJS.Timeout\n\n const handleChange = () => {\n clearTimeout(debounceTimer)\n debounceTimer = setTimeout(async () => {\n try {\n const directories = await scanSkillsDirectory(sourceDir)\n callback(directories)\n } catch (error) {\n console.error('Error rescanning source directory:', error)\n }\n }, 300)\n }\n\n watcher.on('add', handleChange)\n watcher.on('change', handleChange)\n watcher.on('unlink', handleChange)\n watcher.on('addDir', handleChange)\n watcher.on('unlinkDir', handleChange)\n\n return watcher\n}","import matter from 'gray-matter'\nimport MarkdownIt from 'markdown-it'\nimport { createHighlighter, Highlighter } from 'shiki'\nimport { SkillDirectory, SkillFile, SiteConfig, DocGenConfig, Navigation } from '../types/index.js'\n\nlet highlighter: Highlighter | null = null\n\nasync function getHighlighter() {\n if (!highlighter) {\n highlighter = await createHighlighter({\n themes: ['github-dark', 'github-light'],\n langs: ['javascript', 'typescript', 'bash', 'shell', 'json', 'html', 'css', 'vue', 'jsx', 'tsx', 'python', 'markdown', 'yaml', 'sql', 'go', 'rust', 'java', 'c', 'cpp']\n })\n }\n return highlighter\n}\n\nconst md = new MarkdownIt({\n html: true,\n linkify: true,\n typographer: true,\n highlight: function (str: string, lang: string) {\n // Synchronous fallback - actual highlighting done in enhanceFileContent\n return `<pre class=\"shiki-pending\" data-lang=\"${lang || 'text'}\"><code>${md.utils.escapeHtml(str)}</code></pre>`\n }\n})\n\nexport async function parseSkillsToConfig(\n directories: SkillDirectory[],\n siteConfig: SiteConfig\n): Promise<DocGenConfig> {\n const files: SkillFile[] = []\n const navigation = await generateNavigation(directories)\n\n // Collect all files for search indexing\n collectAllFiles(directories, files)\n\n // Enhance files with parsed content\n for (const file of files) {\n await enhanceFileContent(file)\n }\n\n return {\n siteConfig,\n navigation,\n files,\n directories\n }\n}\n\nfunction collectAllFiles(items: (SkillDirectory | SkillFile)[], files: SkillFile[]) {\n for (const item of items) {\n if ('content' in item) {\n // It's a SkillFile\n files.push(item)\n } else {\n // It's a SkillDirectory\n if (item.skillFile) {\n files.push(item.skillFile)\n }\n collectAllFiles(item.children, files)\n }\n }\n}\n\nasync function enhanceFileContent(file: SkillFile) {\n try {\n let content = file.content\n\n // Update title and description from frontmatter if available\n file.title = file.frontmatter.title || file.title\n file.description = file.frontmatter.description || file.description\n\n // Remove duplicate h1 if it matches frontmatter title\n if (file.frontmatter.title) {\n // Match first h1 heading in content (may have leading whitespace/newlines)\n const h1Match = content.match(/^\\s*#\\s+(.+)$/m)\n if (h1Match) {\n const h1Text = h1Match[1].trim()\n // If h1 matches title, remove it to avoid duplication\n if (h1Text === file.frontmatter.title) {\n content = content.replace(/^\\s*#\\s+.+\\n*/, '')\n }\n }\n }\n\n // Render markdown to HTML\n let html = md.render(content)\n \n // Apply syntax highlighting with shiki\n html = await highlightCodeBlocks(html)\n file.frontmatter.html = html\n\n // Extract headings for TOC\n const headings = extractHeadings(content)\n file.frontmatter.headings = headings\n\n } catch (error) {\n console.warn(`Failed to enhance content for ${file.path}:`, error)\n try {\n file.frontmatter.html = md.render(file.content)\n } catch (e) {\n console.error(`Failed to render markdown for ${file.path}:`, e)\n }\n }\n}\n\nasync function highlightCodeBlocks(html: string): Promise<string> {\n const hl = await getHighlighter()\n \n // Find all pending shiki code blocks and highlight them\n const codeBlockRegex = /<pre class=\"shiki-pending\" data-lang=\"([^\"]*)\"[^>]*><code>([^]*?)<\\/code><\\/pre>/g\n \n const matches = [...html.matchAll(codeBlockRegex)]\n \n for (const match of matches) {\n const [fullMatch, lang, escapedCode] = match\n // Unescape HTML entities\n const code = escapedCode\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/&/g, '&')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\")\n \n try {\n const validLang = hl.getLoadedLanguages().includes(lang) ? lang : 'text'\n const highlighted = hl.codeToHtml(code, {\n lang: validLang,\n theme: 'github-dark'\n })\n html = html.replace(fullMatch, highlighted)\n } catch (e) {\n // Keep original if highlighting fails\n console.warn(`Failed to highlight ${lang}:`, e)\n }\n }\n \n return html\n}\n\nfunction extractHeadings(content: string) {\n const headings: Array<{ level: number; text: string; anchor: string }> = []\n const lines = content.split('\\n')\n\n for (const line of lines) {\n const match = line.match(/^(#{1,6})\\\\s+(.+)$/)\n if (match) {\n const level = match[1].length\n const text = match[2].trim()\n const anchor = text.toLowerCase()\n .replace(/[^\\\\w\\\\s-]/g, '')\n .replace(/\\\\s+/g, '-')\n .trim()\n\n headings.push({ level, text, anchor })\n }\n }\n\n return headings\n}\n\nasync function generateNavigation(directories: SkillDirectory[]): Promise<Navigation[]> {\n const navigation: Navigation[] = []\n\n for (const dir of directories) {\n const navItem: Navigation = {\n text: dir.skillFile?.title || formatDirName(dir.name),\n link: dir.skillFile ? getFileRoute(dir.skillFile) : undefined\n }\n\n if (dir.children.length > 0) {\n navItem.children = []\n \n for (const child of dir.children) {\n if ('content' in child) {\n // It's a file\n navItem.children.push({\n text: child.title || formatFileName(child.name),\n link: getFileRoute(child)\n })\n } else {\n // It's a subdirectory\n const subNav = await generateNavigation([child])\n navItem.children.push(...subNav)\n }\n }\n }\n\n navigation.push(navItem)\n }\n\n return navigation\n}\n\nfunction getFileRoute(file: SkillFile): string {\n // Import the utility function from client utils\n // For now, use inline implementation\n const filePath = file.path\n \n // Try common patterns\n const patterns = ['my-docs', 'docs', 'documentation', '.claude/skills']\n let relativePath = ''\n \n for (const pattern of patterns) {\n const index = filePath.indexOf(pattern)\n if (index !== -1) {\n relativePath = filePath.slice(index + pattern.length)\n break\n }\n }\n \n if (!relativePath) {\n // Fallback: use last 2 segments\n const segments = filePath.split('/').filter(Boolean)\n if (segments.length >= 2) {\n relativePath = '/' + segments.slice(-2).join('/')\n } else {\n return '/'\n }\n }\n \n const segments = relativePath.split('/').filter(Boolean)\n \n if (segments.length === 0) return '/'\n \n // Remove file extension\n const lastSegment = segments[segments.length - 1]\n if (lastSegment.endsWith('.md')) {\n segments[segments.length - 1] = lastSegment.slice(0, -3)\n }\n \n // SKILL.md and README.md should map to parent directory route\n const finalSegment = segments[segments.length - 1]\n if (finalSegment === 'SKILL' || finalSegment === 'README') {\n segments.pop()\n }\n \n return '/' + segments.join('/')\n}\n\nfunction formatDirName(name: string): string {\n return name.split('-').map(word => \n word.charAt(0).toUpperCase() + word.slice(1)\n ).join(' ')\n}\n\nfunction formatFileName(name: string): string {\n const baseName = name.replace('.md', '')\n return formatDirName(baseName)\n}\n\nexport { md as markdownRenderer }","#!/usr/bin/env node\n\nimport { Command } from 'commander'\nimport { devCommand } from './commands/dev.js'\nimport { buildCommand } from './commands/build.js'\nimport { previewCommand } from './commands/preview.js'\n\nconst program = new Command()\n\nprogram\n .name('docmk')\n .description('CLI tool for generating documentation from any directory')\n .version('1.0.0')\n .argument('[directory]', 'Source directory path (starts dev server)', './docs')\n .option('-p, --port <port>', 'Port to run dev server on', '3000')\n .action((directory, options) => {\n // Default action: start dev server\n devCommand({ dir: directory, port: options.port })\n })\n\nprogram\n .command('dev')\n .description('Start development server')\n .option('-p, --port <port>', 'Port to run dev server on', '3000')\n .option('-d, --dir <directory>', 'Source directory path', './docs')\n .action(devCommand)\n\nprogram\n .command('build')\n .description('Build static documentation site')\n .option('-d, --dir <directory>', 'Source directory path', './docs')\n .option('-o, --output <directory>', 'Output directory', 'dist')\n .action(buildCommand)\n\nprogram\n .command('preview')\n .description('Preview built documentation site')\n .option('-p, --port <port>', 'Port to run preview server on', '4173')\n .option('-o, --output <directory>', 'Built site directory', 'dist')\n .action(previewCommand)\n\nprogram.parse()","import path from 'path'\nimport { createViteDevServer } from '../../builder/vite-dev.js'\nimport { scanSkillsDirectory } from '../../scanner/index.js'\nimport { parseSkillsToConfig } from '../../parser/index.js'\n\ninterface DevOptions {\n port: string\n dir: string\n}\n\nexport async function devCommand(options: DevOptions) {\n console.log('🚀 Starting DocGen development server...')\n \n const sourceDir = path.resolve(process.cwd(), options.dir)\n const port = parseInt(options.port, 10)\n \n try {\n // Check if source directory exists\n console.log(`📁 Scanning source directory: ${sourceDir}`)\n const directories = await scanSkillsDirectory(sourceDir)\n \n console.log(`✅ Found ${directories.length} directories`)\n \n // Parse to config\n const config = await parseSkillsToConfig(directories, {\n title: 'Documentation',\n description: 'Documentation generated from source directory',\n baseUrl: '/',\n skillsDir: sourceDir,\n outputDir: 'dist'\n })\n \n // Start Vite dev server\n const { server, actualPort } = await createViteDevServer({\n port,\n skillsDir: sourceDir,\n config\n })\n\n console.log(`🎉 Dev server running at http://localhost:${actualPort}`)\n \n // Handle graceful shutdown\n process.on('SIGINT', async () => {\n console.log('\\\\n👋 Shutting down dev server...')\n await server.close()\n process.exit(0)\n })\n \n } catch (error) {\n console.error('❌ Failed to start dev server:', error)\n process.exit(1)\n }\n}","import { createServer, ViteDevServer } from 'vite'\nimport vue from '@vitejs/plugin-vue'\nimport path from 'path'\nimport { fileURLToPath } from 'url'\nimport { DocGenConfig } from '../types/index.js'\nimport { scanSkillsDirectory, watchSkillsDirectory } from '../scanner/index.js'\nimport { parseSkillsToConfig } from '../parser/index.js'\n\n// Get the package root directory (where src/client is located)\nconst __dirname = path.dirname(fileURLToPath(import.meta.url))\nconst packageRoot = path.resolve(__dirname, '../..')\n\ninterface DevServerOptions {\n port: number\n skillsDir: string\n config: DocGenConfig\n}\n\nexport async function createViteDevServer(options: DevServerOptions): Promise<ViteDevServer> {\n let currentConfig = options.config\n\n const server = await createServer({\n root: path.resolve(packageRoot, 'src/client'),\n server: {\n port: options.port,\n host: 'localhost'\n },\n publicDir: false, // Disable public dir in dev mode\n plugins: [\n vue(),\n // Custom plugin to inject config and handle API routes\n {\n name: 'docgen-dev',\n configureServer(server) {\n // API endpoint for configuration\n server.middlewares.use('/api/config', (req, res, next) => {\n if (req.method === 'GET') {\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify(currentConfig))\n } else {\n next()\n }\n })\n\n // SPA fallback - return index.html for all non-API routes\n return () => {\n server.middlewares.use((req, res, next) => {\n // Skip API routes and vite's own routes\n if (req.url?.startsWith('/api/') || req.url?.startsWith('/@') || req.url?.includes('.html')) {\n return next()\n }\n // For all other routes, serve index.html for SPA routing\n if (req.url && !req.url.includes('.')) {\n req.url = '/index.html'\n }\n next()\n })\n }\n },\n transformIndexHtml: {\n order: 'pre',\n handler(html) {\n // Inject lightweight config placeholder\n // Full config will be loaded via API to avoid HTML parsing issues\n const configScript = `\n <script>\n // Config will be loaded from /api/config\n globalThis.__DOCGEN_CONFIG__ = null;\n </script>\n `\n return html.replace('<head>', `<head>${configScript}`)\n }\n }\n }\n ],\n resolve: {\n alias: {\n '@': path.resolve(packageRoot, 'src/client')\n }\n },\n define: {\n __DOCGEN_CONFIG__: JSON.stringify(currentConfig)\n }\n })\n\n // Watch skills directory for changes\n const watcher = await watchSkillsDirectory(options.skillsDir, async (directories) => {\n console.log('📝 Skills directory changed, updating configuration...')\n \n try {\n currentConfig = await parseSkillsToConfig(directories, currentConfig.siteConfig)\n \n // Notify all connected clients to reload\n server.ws.send({\n type: 'full-reload'\n })\n \n console.log('✅ Configuration updated')\n } catch (error) {\n console.error('❌ Failed to update configuration:', error)\n \n // Send error to clients\n server.ws.send({\n type: 'error',\n err: {\n message: `Failed to update configuration: ${error.message}`,\n stack: error.stack\n }\n })\n }\n })\n\n // Start the server\n await server.listen()\n\n // Get actual port (in case requested port was taken)\n const actualPort = server.config.server.port\n\n // Extend server with cleanup method\n const originalClose = server.close.bind(server)\n server.close = async () => {\n await watcher?.close()\n return originalClose()\n }\n\n return { server, actualPort }\n}\n\nexport async function createProductionBuild() {\n // This will be implemented in the builder\n}","import path from 'path'\nimport { buildSite } from '../../builder/index.js'\nimport { scanSkillsDirectory } from '../../scanner/index.js'\nimport { parseSkillsToConfig } from '../../parser/index.js'\n\ninterface BuildOptions {\n dir: string\n output: string\n}\n\nexport async function buildCommand(options: BuildOptions) {\n console.log('🏗️ Building DocGen documentation site...')\n \n const sourceDir = path.resolve(process.cwd(), options.dir)\n const outputDir = path.resolve(process.cwd(), options.output)\n \n try {\n // Scan source directory\n console.log(`📁 Scanning source directory: ${sourceDir}`)\n const directories = await scanSkillsDirectory(sourceDir)\n \n console.log(`✅ Found ${directories.length} directories`)\n \n // Parse to config\n const config = await parseSkillsToConfig(directories, {\n title: 'Documentation',\n description: 'Documentation generated from source directory',\n baseUrl: '/',\n skillsDir: sourceDir,\n outputDir\n })\n \n // Build site\n console.log(`📦 Building to: ${outputDir}`)\n await buildSite({\n input: sourceDir,\n output: outputDir,\n mode: 'production'\n }, config)\n \n console.log('✅ Build completed successfully!')\n console.log(`📂 Built files are in: ${outputDir}`)\n \n } catch (error) {\n console.error('❌ Build failed:', error)\n process.exit(1)\n }\n}","import { build } from 'vite'\nimport vue from '@vitejs/plugin-vue'\nimport path from 'path'\nimport { fileURLToPath } from 'url'\nimport fs from 'fs/promises'\nimport { DocGenConfig, BuildOptions } from '../types/index.js'\n\n// Get the package root directory\nconst __dirname = path.dirname(fileURLToPath(import.meta.url))\nconst packageRoot = path.resolve(__dirname, '../..')\n\nexport async function buildSite(options: BuildOptions, config: DocGenConfig) {\n console.log('🏗️ Building documentation site...')\n\n // Ensure output directory exists\n await fs.mkdir(options.output, { recursive: true })\n\n // Write config to a temporary file for the build process\n const configPath = path.join(process.cwd(), 'temp-config.json')\n await fs.writeFile(configPath, JSON.stringify(config, null, 2))\n\n try {\n // Build the client application\n await build({\n root: path.resolve(packageRoot, 'src/client'),\n base: config.siteConfig.baseUrl,\n build: {\n outDir: options.output,\n emptyOutDir: true,\n rollupOptions: {\n input: {\n main: path.resolve(packageRoot, 'src/client/index.html')\n }\n }\n },\n plugins: [\n vue(),\n // Plugin to inject config during build\n {\n name: 'docgen-build',\n transformIndexHtml: {\n order: 'pre',\n handler(html) {\n // Encode config as base64 with proper UTF-8 handling\n const configJson = JSON.stringify(config)\n // Use encodeURIComponent to handle UTF-8 properly before base64\n const utf8Encoded = unescape(encodeURIComponent(configJson))\n const configBase64 = Buffer.from(utf8Encoded, 'binary').toString('base64')\n // Decode: atob -> decodeURIComponent(escape()) to restore UTF-8\n const configScript = `<script>globalThis.__DOCGEN_CONFIG__=JSON.parse(decodeURIComponent(escape(atob(\"${configBase64}\"))));</script>`\n return html.replace('</head>', `${configScript}\\n</head>`)\n }\n }\n }\n ],\n resolve: {\n alias: {\n '@': path.resolve(packageRoot, 'src/client')\n }\n },\n define: {\n __DOCGEN_CONFIG__: JSON.stringify(config)\n }\n })\n \n // Generate additional static files\n await generateSitemap(options.output, config)\n await generateRobotsTxt(options.output, config)\n await copyAssets(options.output)\n \n console.log('✅ Build completed successfully!')\n \n } finally {\n // Clean up temporary config file\n try {\n await fs.unlink(configPath)\n } catch {\n // Ignore if file doesn't exist\n }\n }\n}\n\nasync function generateSitemap(outputDir: string, config: DocGenConfig) {\n console.log('📄 Generating sitemap...')\n \n const baseUrl = config.siteConfig.baseUrl.replace(/\\/$/, '')\n const urls: string[] = []\n \n // Add home page\n urls.push(`${baseUrl}/`)\n \n // Add all skill pages\n for (const file of config.files) {\n const route = getFileRoute(file.path, config.siteConfig.skillsDir)\n if (route !== '/') {\n urls.push(`${baseUrl}${route}`)\n }\n }\n \n const sitemap = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n${urls.map(url => ` <url>\n <loc>${url}</loc>\n <lastmod>${new Date().toISOString().split('T')[0]}</lastmod>\n <changefreq>weekly</changefreq>\n <priority>0.8</priority>\n </url>`).join('\\\\n')}\n</urlset>`\n \n await fs.writeFile(path.join(outputDir, 'sitemap.xml'), sitemap)\n}\n\nasync function generateRobotsTxt(outputDir: string, config: DocGenConfig) {\n console.log('🤖 Generating robots.txt...')\n \n const baseUrl = config.siteConfig.baseUrl.replace(/\\/$/, '')\n const robotsTxt = `User-agent: *\nAllow: /\n\nSitemap: ${baseUrl}/sitemap.xml`\n \n await fs.writeFile(path.join(outputDir, 'robots.txt'), robotsTxt)\n}\n\nasync function copyAssets(outputDir: string) {\n console.log('📁 Copying static assets...')\n \n // Create a simple favicon if it doesn't exist\n const faviconPath = path.join(outputDir, 'favicon.ico')\n \n try {\n await fs.access(faviconPath)\n } catch {\n // Create a simple SVG favicon\n const faviconSvg = `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\">\n <rect width=\"32\" height=\"32\" rx=\"4\" fill=\"#3182ce\"/>\n <text x=\"16\" y=\"22\" text-anchor=\"middle\" fill=\"white\" font-family=\"system-ui\" font-size=\"18\" font-weight=\"bold\">D</text>\n</svg>`\n \n await fs.writeFile(path.join(outputDir, 'favicon.svg'), faviconSvg)\n }\n}\n\nfunction getFileRoute(filePath: string, baseDir: string): string {\n // Normalize paths for comparison\n const normalizedFilePath = path.normalize(filePath)\n const normalizedBaseDir = path.normalize(baseDir)\n \n // Check if file is within base directory\n if (!normalizedFilePath.startsWith(normalizedBaseDir)) {\n return '/'\n }\n \n // Get relative path from base directory\n const relativePath = normalizedFilePath.slice(normalizedBaseDir.length)\n const segments = relativePath.split(path.sep).filter(Boolean)\n \n if (segments.length === 0) return '/'\n \n const lastSegment = segments[segments.length - 1]\n if (lastSegment.endsWith('.md')) {\n segments[segments.length - 1] = lastSegment.slice(0, -3)\n }\n \n // SKILL.md and README.md should map to parent directory route\n const finalSegment = segments[segments.length - 1]\n if (finalSegment === 'SKILL' || finalSegment === 'README') {\n segments.pop()\n }\n\n return '/' + segments.join('/')\n}\n\nexport async function buildForProduction(sourceDir: string, outputDir: string): Promise<DocGenConfig> {\n const { scanSkillsDirectory } = await import('../scanner/index.js')\n const { parseSkillsToConfig } = await import('../parser/index.js')\n \n const directories = await scanSkillsDirectory(sourceDir)\n const config = await parseSkillsToConfig(directories, {\n title: 'Documentation',\n description: 'Documentation generated from source directory',\n baseUrl: '/',\n skillsDir: sourceDir,\n outputDir\n })\n \n await buildSite({\n input: sourceDir,\n output: outputDir,\n mode: 'production'\n }, config)\n \n return config\n}","import path from 'path'\nimport fs from 'fs/promises'\nimport sirv from 'sirv'\nimport { createServer } from 'http'\n\ninterface PreviewOptions {\n port: string\n output: string\n}\n\nexport async function previewCommand(options: PreviewOptions) {\n console.log('👀 Starting DocGen preview server...')\n \n const outputDir = path.resolve(process.cwd(), options.output)\n const port = parseInt(options.port, 10)\n \n try {\n // Check if build directory exists\n try {\n await fs.access(outputDir)\n } catch {\n console.error(`❌ Build directory not found: ${outputDir}`)\n console.log('💡 Run \"docgen build\" first to generate the static site')\n process.exit(1)\n }\n \n // Create static file server\n const serve = sirv(outputDir, {\n single: true, // SPA mode\n dev: false,\n setHeaders: (res, pathname) => {\n if (pathname.endsWith('.html') || pathname === '/') {\n res.setHeader('Content-Type', 'text/html; charset=utf-8')\n }\n }\n })\n \n const server = createServer(serve)\n \n server.listen(port, () => {\n console.log(`🎉 Preview server running at http://localhost:${port}`)\n console.log(`📂 Serving files from: ${outputDir}`)\n })\n \n // Handle graceful shutdown\n process.on('SIGINT', () => {\n console.log('\\\\n👋 Shutting down preview server...')\n server.close(() => {\n process.exit(0)\n })\n })\n \n } catch (error) {\n console.error('❌ Failed to start preview server:', error)\n process.exit(1)\n }\n}"],"mappings":"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","names":["segments","path","path","server","path","path","vue","path","fileURLToPath","fs","__dirname","packageRoot","getFileRoute","path","path","fs","createServer"]}
|
package/package.json
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "docmk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "A CLI tool for scanning directories and generating static documentation websites",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"docmk": "./bin/docmk.js"
|
|
8
8
|
},
|
|
9
9
|
"type": "module",
|
|
10
|
+
"files": [
|
|
11
|
+
"bin",
|
|
12
|
+
"dist",
|
|
13
|
+
"src"
|
|
14
|
+
],
|
|
10
15
|
"scripts": {
|
|
11
16
|
"dev": "tsx src/cli/index.ts",
|
|
12
17
|
"build": "tsup",
|
package/src/builder/vite-dev.ts
CHANGED
|
@@ -113,6 +113,9 @@ export async function createViteDevServer(options: DevServerOptions): Promise<Vi
|
|
|
113
113
|
// Start the server
|
|
114
114
|
await server.listen()
|
|
115
115
|
|
|
116
|
+
// Get actual port (in case requested port was taken)
|
|
117
|
+
const actualPort = server.config.server.port
|
|
118
|
+
|
|
116
119
|
// Extend server with cleanup method
|
|
117
120
|
const originalClose = server.close.bind(server)
|
|
118
121
|
server.close = async () => {
|
|
@@ -120,7 +123,7 @@ export async function createViteDevServer(options: DevServerOptions): Promise<Vi
|
|
|
120
123
|
return originalClose()
|
|
121
124
|
}
|
|
122
125
|
|
|
123
|
-
return server
|
|
126
|
+
return { server, actualPort }
|
|
124
127
|
}
|
|
125
128
|
|
|
126
129
|
export async function createProductionBuild() {
|
package/src/cli/commands/dev.ts
CHANGED
|
@@ -31,13 +31,13 @@ export async function devCommand(options: DevOptions) {
|
|
|
31
31
|
})
|
|
32
32
|
|
|
33
33
|
// Start Vite dev server
|
|
34
|
-
const server = await createViteDevServer({
|
|
34
|
+
const { server, actualPort } = await createViteDevServer({
|
|
35
35
|
port,
|
|
36
36
|
skillsDir: sourceDir,
|
|
37
37
|
config
|
|
38
38
|
})
|
|
39
|
-
|
|
40
|
-
console.log(`🎉 Dev server running at http://localhost:${
|
|
39
|
+
|
|
40
|
+
console.log(`🎉 Dev server running at http://localhost:${actualPort}`)
|
|
41
41
|
|
|
42
42
|
// Handle graceful shutdown
|
|
43
43
|
process.on('SIGINT', async () => {
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: PDF Processing
|
|
3
|
-
description: Comprehensive guide to working with PDF files including extraction, manipulation, and generation
|
|
4
|
-
tags: ["pdf", "documents", "data-extraction"]
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# PDF Processing
|
|
8
|
-
|
|
9
|
-
This skill covers various aspects of working with PDF documents programmatically.
|
|
10
|
-
|
|
11
|
-
## Overview
|
|
12
|
-
|
|
13
|
-
PDF (Portable Document Format) is a widely used file format for documents. This skill includes:
|
|
14
|
-
|
|
15
|
-
- Text extraction from PDFs
|
|
16
|
-
- PDF generation and manipulation
|
|
17
|
-
- Form handling
|
|
18
|
-
- Metadata processing
|
|
19
|
-
- Document conversion
|
|
20
|
-
|
|
21
|
-
## Key Libraries and Tools
|
|
22
|
-
|
|
23
|
-
### Python
|
|
24
|
-
- **PyPDF2/PyPDF4** - Basic PDF operations
|
|
25
|
-
- **pdfplumber** - Text extraction with layout preservation
|
|
26
|
-
- **ReportLab** - PDF generation
|
|
27
|
-
- **pdftk** - Command-line PDF toolkit
|
|
28
|
-
|
|
29
|
-
### JavaScript/Node.js
|
|
30
|
-
- **pdf-lib** - Create and modify PDF documents
|
|
31
|
-
- **pdf-parse** - Simple PDF parsing
|
|
32
|
-
- **puppeteer** - Generate PDFs from web content
|
|
33
|
-
|
|
34
|
-
## Common Use Cases
|
|
35
|
-
|
|
36
|
-
### Text Extraction
|
|
37
|
-
Extract text content from PDF files while preserving formatting and structure.
|
|
38
|
-
|
|
39
|
-
### Document Generation
|
|
40
|
-
Create PDFs programmatically from data, templates, or web content.
|
|
41
|
-
|
|
42
|
-
### Form Processing
|
|
43
|
-
Handle PDF forms, extract form data, and fill forms programmatically.
|
|
44
|
-
|
|
45
|
-
## Best Practices
|
|
46
|
-
|
|
47
|
-
1. **Memory Management** - Large PDFs can consume significant memory
|
|
48
|
-
2. **Error Handling** - PDFs can be corrupted or password-protected
|
|
49
|
-
3. **Performance** - Consider streaming for large documents
|
|
50
|
-
4. **Security** - Be cautious with user-uploaded PDFs
|
|
51
|
-
|
|
52
|
-
## Examples
|
|
53
|
-
|
|
54
|
-
### Basic Text Extraction (Python)
|
|
55
|
-
```python
|
|
56
|
-
import PyPDF2
|
|
57
|
-
|
|
58
|
-
with open('document.pdf', 'rb') as file:
|
|
59
|
-
reader = PyPDF2.PdfReader(file)
|
|
60
|
-
text = ""
|
|
61
|
-
for page in reader.pages:
|
|
62
|
-
text += page.extract_text()
|
|
63
|
-
print(text)
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
### PDF Generation (JavaScript)
|
|
67
|
-
```javascript
|
|
68
|
-
import { PDFDocument, rgb } from 'pdf-lib'
|
|
69
|
-
import fs from 'fs'
|
|
70
|
-
|
|
71
|
-
const pdfDoc = await PDFDocument.create()
|
|
72
|
-
const page = pdfDoc.addPage()
|
|
73
|
-
|
|
74
|
-
page.drawText('Hello, PDF!', {
|
|
75
|
-
x: 50,
|
|
76
|
-
y: 750,
|
|
77
|
-
size: 30,
|
|
78
|
-
color: rgb(0, 0, 0),
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
const pdfBytes = await pdfDoc.save()
|
|
82
|
-
fs.writeFileSync('output.pdf', pdfBytes)
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
## Troubleshooting
|
|
86
|
-
|
|
87
|
-
- **Encoding Issues** - Some PDFs may have encoding problems
|
|
88
|
-
- **Layout Preservation** - Complex layouts may not extract cleanly
|
|
89
|
-
- **Performance** - Large PDFs may require optimization
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Web Scraping
|
|
3
|
-
description: Techniques and tools for extracting data from websites
|
|
4
|
-
tags: ["scraping", "data-extraction", "automation"]
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Web Scraping
|
|
8
|
-
|
|
9
|
-
Web scraping is the process of extracting data from websites programmatically.
|
|
10
|
-
|
|
11
|
-
## Overview
|
|
12
|
-
|
|
13
|
-
Web scraping involves:
|
|
14
|
-
|
|
15
|
-
- Fetching web pages
|
|
16
|
-
- Parsing HTML content
|
|
17
|
-
- Extracting specific data
|
|
18
|
-
- Handling dynamic content
|
|
19
|
-
- Managing rate limits and ethics
|
|
20
|
-
|
|
21
|
-
## Tools and Libraries
|
|
22
|
-
|
|
23
|
-
### Python
|
|
24
|
-
- **BeautifulSoup** - HTML parsing
|
|
25
|
-
- **Scrapy** - Full-featured scraping framework
|
|
26
|
-
- **Selenium** - Browser automation
|
|
27
|
-
- **requests** - HTTP library
|
|
28
|
-
|
|
29
|
-
### JavaScript/Node.js
|
|
30
|
-
- **Puppeteer** - Chrome automation
|
|
31
|
-
- **Playwright** - Multi-browser automation
|
|
32
|
-
- **Cheerio** - Server-side jQuery
|
|
33
|
-
- **axios** - HTTP client
|
|
34
|
-
|
|
35
|
-
## Key Concepts
|
|
36
|
-
|
|
37
|
-
### Respect robots.txt
|
|
38
|
-
Always check the robots.txt file of websites before scraping.
|
|
39
|
-
|
|
40
|
-
### Rate Limiting
|
|
41
|
-
Implement delays between requests to avoid overwhelming servers.
|
|
42
|
-
|
|
43
|
-
### User Agents
|
|
44
|
-
Rotate user agents to appear more like regular browsers.
|
|
45
|
-
|
|
46
|
-
## Examples
|
|
47
|
-
|
|
48
|
-
### Basic Scraping (Python)
|
|
49
|
-
```python
|
|
50
|
-
import requests
|
|
51
|
-
from bs4 import BeautifulSoup
|
|
52
|
-
|
|
53
|
-
response = requests.get('https://example.com')
|
|
54
|
-
soup = BeautifulSoup(response.content, 'html.parser')
|
|
55
|
-
|
|
56
|
-
titles = soup.find_all('h2', class_='title')
|
|
57
|
-
for title in titles:
|
|
58
|
-
print(title.text)
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
### Dynamic Content (JavaScript)
|
|
62
|
-
```javascript
|
|
63
|
-
const puppeteer = require('puppeteer');
|
|
64
|
-
|
|
65
|
-
(async () => {
|
|
66
|
-
const browser = await puppeteer.launch();
|
|
67
|
-
const page = await browser.newPage();
|
|
68
|
-
|
|
69
|
-
await page.goto('https://example.com');
|
|
70
|
-
await page.waitForSelector('.dynamic-content');
|
|
71
|
-
|
|
72
|
-
const content = await page.$eval('.dynamic-content',
|
|
73
|
-
el => el.textContent);
|
|
74
|
-
|
|
75
|
-
console.log(content);
|
|
76
|
-
await browser.close();
|
|
77
|
-
})();
|
|
78
|
-
```
|
package/CLAUDE.md
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
# CLAUDE.md
|
|
2
|
-
|
|
3
|
-
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
-
|
|
5
|
-
## 项目概述
|
|
6
|
-
|
|
7
|
-
DocGen 是一个文档生成 CLI 工具,用于扫描任意目录并自动生成静态文档网站。支持 Markdown 渲染、全文搜索、代码高亮等功能。
|
|
8
|
-
|
|
9
|
-
## 常用命令
|
|
10
|
-
|
|
11
|
-
```bash
|
|
12
|
-
# 安装依赖
|
|
13
|
-
npm install
|
|
14
|
-
|
|
15
|
-
# 构建 CLI(输出到 dist/)
|
|
16
|
-
npm run build
|
|
17
|
-
|
|
18
|
-
# 类型检查
|
|
19
|
-
npm run typecheck
|
|
20
|
-
|
|
21
|
-
# 代码检查
|
|
22
|
-
npm run lint
|
|
23
|
-
|
|
24
|
-
# 开发模式运行 CLI(使用 tsx 直接执行)
|
|
25
|
-
npm run dev
|
|
26
|
-
|
|
27
|
-
# 使用 CLI 启动文档开发服务器
|
|
28
|
-
node dist/index.js dev --dir ./my-docs --port 3000
|
|
29
|
-
|
|
30
|
-
# 构建静态站点
|
|
31
|
-
node dist/index.js build --dir ./my-docs --output ./dist
|
|
32
|
-
|
|
33
|
-
# 预览构建结果
|
|
34
|
-
node dist/index.js preview --output ./dist --port 4173
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## 架构
|
|
38
|
-
|
|
39
|
-
```
|
|
40
|
-
src/
|
|
41
|
-
├── cli/ # CLI 入口和命令实现
|
|
42
|
-
│ ├── index.ts # 主入口,使用 Commander.js 定义命令
|
|
43
|
-
│ └── commands/ # dev/build/preview 命令
|
|
44
|
-
├── scanner/ # 目录扫描器,递归扫描 .md 文件
|
|
45
|
-
├── parser/ # Markdown 解析,使用 gray-matter + markdown-it + shiki
|
|
46
|
-
├── builder/ # Vite 构建逻辑
|
|
47
|
-
│ ├── index.ts # 生产构建,生成 sitemap/robots.txt
|
|
48
|
-
│ └── vite-dev.ts # 开发服务器,支持文件监听热更新
|
|
49
|
-
├── client/ # Vue 3 前端 SPA
|
|
50
|
-
│ ├── pages/ # Home(首页统计)和 SkillPage(文档渲染)
|
|
51
|
-
│ ├── components/ # Sidebar、SearchBox 等
|
|
52
|
-
│ └── router.ts # 路由配置,catch-all 匹配文档路径
|
|
53
|
-
└── types/ # TypeScript 类型定义
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
## 核心数据流
|
|
57
|
-
|
|
58
|
-
1. **Scanner** (`src/scanner/index.ts`): 扫描源目录,递归收集 `.md` 文件,构建 `SkillDirectory[]` 树结构
|
|
59
|
-
2. **Parser** (`src/parser/index.ts`): 解析 frontmatter、渲染 Markdown 为 HTML、提取 TOC、应用 shiki 代码高亮
|
|
60
|
-
3. **Builder**:
|
|
61
|
-
- 开发模式:通过 `/api/config` 端点注入配置,使用 chokidar 监听文件变化触发热更新
|
|
62
|
-
- 生产模式:将配置 base64 编码注入 HTML `<head>`,生成 sitemap.xml 和 robots.txt
|
|
63
|
-
4. **Client**: Vue 3 SPA 从 `globalThis.__DOCGEN_CONFIG__` 或 `/api/config` 获取配置,渲染文档
|
|
64
|
-
|
|
65
|
-
## 关键类型
|
|
66
|
-
|
|
67
|
-
- `SkillDirectory`: 目录节点,包含 `children` 和可选的 `skillFile`(SKILL.md)
|
|
68
|
-
- `SkillFile`: 文件节点,包含 `content`、`frontmatter`、`lastModified`
|
|
69
|
-
- `DocGenConfig`: 完整站点配置,包含 `navigation`、`files`、`directories`
|
|
70
|
-
|
|
71
|
-
## 技术栈
|
|
72
|
-
|
|
73
|
-
- **CLI**: Commander.js + tsup 打包
|
|
74
|
-
- **前端**: Vue 3 + Vue Router + Vite + Tailwind CSS
|
|
75
|
-
- **解析**: gray-matter(frontmatter)+ markdown-it(渲染)+ shiki(代码高亮)
|
|
76
|
-
- **监听**: chokidar
|
|
77
|
-
|
|
78
|
-
## 文档目录结构约定
|
|
79
|
-
|
|
80
|
-
```
|
|
81
|
-
docs/
|
|
82
|
-
├── category/
|
|
83
|
-
│ ├── SKILL.md # 主文档(可选,作为该目录的入口)
|
|
84
|
-
│ ├── advanced.md # 其他文档
|
|
85
|
-
│ └── sub-category/ # 子目录
|
|
86
|
-
└── another/
|
|
87
|
-
└── SKILL.md
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
路由规则:`SKILL.md` 文件映射到目录路径(如 `/api/SKILL.md` → `/api`),其他 `.md` 文件保留文件名(如 `/api/advanced.md` → `/api/advanced`)。
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
.sidebar-item[data-v-b5febdd6]{margin-bottom:.25rem}.item-wrapper[data-v-b5febdd6]{display:flex;align-items:center;padding:.5rem 1.5rem;position:relative}.expand-button[data-v-b5febdd6]{background:none;border:none;color:#666;cursor:pointer;padding:.25rem;margin-right:.5rem;display:flex;align-items:center;justify-content:center;transition:transform .2s ease}.expand-button.expanded[data-v-b5febdd6]{transform:rotate(90deg)}.expand-button[data-v-b5febdd6]:hover{color:#333}.nav-link[data-v-b5febdd6],.nav-text[data-v-b5febdd6]{flex:1;text-decoration:none;color:#555;font-weight:500;font-size:.9rem;line-height:1.4;transition:color .2s ease}.nav-link[data-v-b5febdd6]:hover{color:#3182ce}.nav-link.active[data-v-b5febdd6]{color:#3182ce;font-weight:600;position:relative}.nav-link.active[data-v-b5febdd6]:before{content:"";position:absolute;left:-1.5rem;top:50%;transform:translateY(-50%);width:3px;height:20px;background-color:#3182ce;border-radius:2px}.nav-text[data-v-b5febdd6]{color:#333;font-weight:600}.children[data-v-b5febdd6]{overflow:hidden;max-height:0;transition:max-height .3s ease;padding-left:1rem}.children.expanded[data-v-b5febdd6]{max-height:1000px}.children .sidebar-item[data-v-b5febdd6]{margin-left:1rem}.children .nav-link[data-v-b5febdd6],.children .nav-text[data-v-b5febdd6]{font-size:.85rem;font-weight:400;color:#666}.children .nav-link[data-v-b5febdd6]:hover{color:#3182ce}.children .nav-link.active[data-v-b5febdd6]{color:#3182ce;font-weight:500}.sidebar-nav[data-v-014c42bf]{height:100%;overflow-y:auto}.nav-content[data-v-014c42bf]{padding:1.5rem 0}.search-box[data-v-732fbf59]{position:relative;width:100%}.relative[data-v-732fbf59]{position:relative}.absolute[data-v-732fbf59]{position:absolute}.w-full[data-v-732fbf59]{width:100%}.h-4[data-v-732fbf59]{height:1rem}.w-4[data-v-732fbf59]{width:1rem}.h-5[data-v-732fbf59]{height:1.25rem}.h-9[data-v-732fbf59]{height:2.25rem}.left-3[data-v-732fbf59]{left:.75rem}.right-3[data-v-732fbf59]{right:.75rem}.top-1\/2[data-v-732fbf59]{top:50%}.top-full[data-v-732fbf59]{top:100%}.left-0[data-v-732fbf59]{left:0}.right-0[data-v-732fbf59]{right:0}.-translate-y-1\/2[data-v-732fbf59]{transform:translateY(-50%)}.pl-9[data-v-732fbf59]{padding-left:2.25rem}.pr-4[data-v-732fbf59]{padding-right:1rem}.px-1\.5[data-v-732fbf59]{padding-left:.375rem;padding-right:.375rem}.p-3[data-v-732fbf59]{padding:.75rem}.p-4[data-v-732fbf59]{padding:1rem}.mt-2[data-v-732fbf59]{margin-top:.5rem}.mb-1[data-v-732fbf59]{margin-bottom:.25rem}.gap-1[data-v-732fbf59]{gap:.25rem}.text-sm[data-v-732fbf59]{font-size:.875rem}.text-xs[data-v-732fbf59]{font-size:.75rem}.font-medium[data-v-732fbf59]{font-weight:500}.font-mono[data-v-732fbf59]{font-family:var(--font-mono)}.text-muted-foreground[data-v-732fbf59]{color:var(--color-muted-foreground)}.text-foreground[data-v-732fbf59]{color:var(--color-foreground)}.placeholder-muted-foreground[data-v-732fbf59]::placeholder{color:var(--color-muted-foreground)}.bg-secondary[data-v-732fbf59]{background-color:var(--color-secondary)}.bg-muted[data-v-732fbf59]{background-color:var(--color-muted)}.bg-card[data-v-732fbf59]{background-color:var(--color-card)}.bg-accent[data-v-732fbf59]{background-color:var(--color-accent)}.border[data-v-732fbf59]{border-width:1px}.border-b[data-v-732fbf59]{border-bottom-width:1px}.border-border[data-v-732fbf59]{border-color:var(--color-border)}.rounded-md[data-v-732fbf59]{border-radius:var(--radius-md)}.rounded-lg[data-v-732fbf59]{border-radius:var(--radius-lg)}.rounded[data-v-732fbf59]{border-radius:var(--radius-sm)}.shadow-lg[data-v-732fbf59]{box-shadow:var(--shadow-lg)}.z-50[data-v-732fbf59]{z-index:50}.max-h-96[data-v-732fbf59]{max-height:24rem}.overflow-y-auto[data-v-732fbf59]{overflow-y:auto}.items-center[data-v-732fbf59]{align-items:center}.text-center[data-v-732fbf59]{text-align:center}.block[data-v-732fbf59]{display:block}.hidden[data-v-732fbf59]{display:none}.inline-flex[data-v-732fbf59]{display:inline-flex}.transition-colors[data-v-732fbf59]{transition:color .2s ease,background-color .2s ease}.hover\:bg-accent[data-v-732fbf59]:hover{background-color:var(--color-accent)}.last\:border-b-0[data-v-732fbf59]:last-child{border-bottom-width:0}.line-clamp-2[data-v-732fbf59]{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.focus-outline[data-v-732fbf59]:focus{outline:none;border-color:var(--color-ring);box-shadow:0 0 0 2px #0000001a}@media (min-width: 640px){.sm\:inline-flex[data-v-732fbf59]{display:inline-flex}}.min-h-screen[data-v-073699c3]{min-height:100vh}.bg-background[data-v-073699c3]{background-color:var(--color-background)}.sticky[data-v-073699c3]{position:sticky}.top-0[data-v-073699c3]{top:0}.z-50[data-v-073699c3]{z-index:50}.z-40[data-v-073699c3]{z-index:40}.border-b[data-v-073699c3]{border-bottom:1px solid var(--color-border)}.border-r[data-v-073699c3]{border-right:1px solid var(--color-border)}.border-border[data-v-073699c3]{border-color:var(--color-border)}.backdrop-blur-sm[data-v-073699c3]{-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}.flex[data-v-073699c3]{display:flex}.items-center[data-v-073699c3]{align-items:center}.justify-between[data-v-073699c3]{justify-between:space-between}.gap-2[data-v-073699c3]{gap:.5rem}.gap-4[data-v-073699c3]{gap:1rem}.h-5[data-v-073699c3]{height:1.25rem}.w-5[data-v-073699c3]{width:1.25rem}.h-7[data-v-073699c3]{height:1.75rem}.w-7[data-v-073699c3]{width:1.75rem}.h-14[data-v-073699c3]{height:3.5rem}.w-64[data-v-073699c3]{width:16rem}.px-4[data-v-073699c3]{padding-left:1rem;padding-right:1rem}.px-6[data-v-073699c3]{padding-left:1.5rem;padding-right:1.5rem}.py-10[data-v-073699c3]{padding-top:2.5rem;padding-bottom:2.5rem}.p-2[data-v-073699c3]{padding:.5rem}.p-4[data-v-073699c3]{padding:1rem}.-ml-2[data-v-073699c3]{margin-left:-.5rem}.mx-8[data-v-073699c3]{margin-left:2rem;margin-right:2rem}.mx-auto[data-v-073699c3]{margin-left:auto;margin-right:auto}.max-w-md[data-v-073699c3]{max-width:28rem}.max-w-4xl[data-v-073699c3]{max-width:56rem}.flex-1[data-v-073699c3]{flex:1}.min-w-0[data-v-073699c3]{min-width:0}.rounded-md[data-v-073699c3]{border-radius:var(--radius-md)}.font-semibold[data-v-073699c3]{font-weight:600}.font-bold[data-v-073699c3]{font-weight:700}.text-sm[data-v-073699c3]{font-size:.875rem}.text-foreground[data-v-073699c3]{color:var(--color-foreground)}.text-primary-foreground[data-v-073699c3]{color:var(--color-primary-foreground)}.text-muted-foreground[data-v-073699c3]{color:var(--color-muted-foreground)}.bg-primary[data-v-073699c3]{background-color:var(--color-primary)}.bg-background\/80[data-v-073699c3]{background-color:#fffc}.hover\:bg-accent[data-v-073699c3]:hover{background-color:var(--color-accent)}.hover\:text-foreground[data-v-073699c3]:hover{color:var(--color-foreground)}.transition-colors[data-v-073699c3]{transition:color .2s ease,background-color .2s ease}.transition-transform[data-v-073699c3]{transition:transform .3s ease}.overflow-y-auto[data-v-073699c3]{overflow-y:auto}.fixed[data-v-073699c3]{position:fixed}.inset-0[data-v-073699c3]{top:0;right:0;bottom:0;left:0}.top-14[data-v-073699c3]{top:3.5rem}.h-\[calc\(100vh-3\.5rem\)\][data-v-073699c3]{height:calc(100vh - 3.5rem)}.-translate-x-full[data-v-073699c3]{transform:translate(-100%)}.translate-x-0[data-v-073699c3]{transform:translate(0)}.hidden[data-v-073699c3]{display:none}@media (min-width: 640px){.sm\:flex[data-v-073699c3]{display:flex}}@media (min-width: 1024px){.lg\:hidden[data-v-073699c3]{display:none}.lg\:px-6[data-v-073699c3]{padding-left:1.5rem;padding-right:1.5rem}.lg\:px-8[data-v-073699c3]{padding-left:2rem;padding-right:2rem}.lg\:sticky[data-v-073699c3]{position:sticky}.lg\:translate-x-0[data-v-073699c3]{transform:translate(0)}}#app[data-v-00943de1]{min-height:100vh}.home-page[data-v-12e10d98]{max-width:1000px;margin:0 auto}.hero-section[data-v-12e10d98]{text-align:center;padding:3rem 0;margin-bottom:3rem}.hero-title[data-v-12e10d98]{font-size:3rem;font-weight:700;color:#2c3e50;margin-bottom:1rem}.hero-description[data-v-12e10d98]{font-size:1.2rem;color:#666;max-width:600px;margin:0 auto;line-height:1.6}.content-overview[data-v-12e10d98]{space-y:3rem}.stats-grid[data-v-12e10d98]{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:1.5rem;margin-bottom:3rem}.stat-card[data-v-12e10d98]{background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;padding:2rem;border-radius:12px;text-align:center;box-shadow:0 4px 6px #0000001a}.stat-number[data-v-12e10d98]{font-size:2.5rem;font-weight:700;margin-bottom:.5rem}.stat-label[data-v-12e10d98]{font-size:1rem;opacity:.9}.recent-section[data-v-12e10d98],.navigation-section[data-v-12e10d98]{margin-bottom:3rem}.recent-section h2[data-v-12e10d98],.navigation-section h2[data-v-12e10d98]{font-size:1.8rem;font-weight:600;color:#2c3e50;margin-bottom:1.5rem}.recent-files[data-v-12e10d98]{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:1rem}.recent-file-card[data-v-12e10d98]{background:#fff;border:1px solid #e1e5e9;border-radius:8px;padding:1.5rem;text-decoration:none;color:inherit;transition:all .2s ease}.recent-file-card[data-v-12e10d98]:hover{transform:translateY(-2px);box-shadow:0 4px 12px #0000001a;border-color:#3182ce}.file-title[data-v-12e10d98]{font-weight:600;color:#2c3e50;margin-bottom:.5rem}.file-description[data-v-12e10d98]{color:#666;font-size:.9rem;margin-bottom:1rem;line-height:1.4}.file-meta[data-v-12e10d98]{color:#999;font-size:.8rem}.skill-grid[data-v-12e10d98]{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:1.5rem}.skill-card[data-v-12e10d98]{background:#fff;border:1px solid #e1e5e9;border-radius:8px;padding:1.5rem;text-decoration:none;color:inherit;transition:all .2s ease}.skill-card[data-v-12e10d98]:hover{transform:translateY(-2px);box-shadow:0 4px 12px #0000001a;border-color:#3182ce}.skill-name[data-v-12e10d98]{font-weight:600;color:#2c3e50;font-size:1.1rem;margin-bottom:.5rem}.skill-description[data-v-12e10d98]{color:#666;font-size:.9rem;line-height:1.4;margin-bottom:1rem}.skill-files-count[data-v-12e10d98]{color:#3182ce;font-size:.8rem;font-weight:500}@media (max-width: 768px){.hero-title[data-v-12e10d98]{font-size:2rem}.hero-description[data-v-12e10d98]{font-size:1rem}.stats-grid[data-v-12e10d98],.recent-files[data-v-12e10d98],.skill-grid[data-v-12e10d98]{grid-template-columns:1fr}}.skill-page[data-v-a95b5427]{max-width:800px;margin:0 auto}.skill-header[data-v-a95b5427]{margin-bottom:2rem;padding-bottom:1.5rem;border-bottom:1px solid #e1e5e9}.skill-title[data-v-a95b5427]{font-size:2.5rem;font-weight:700;color:#2c3e50;margin-bottom:1rem;line-height:1.2}.skill-description[data-v-a95b5427]{font-size:1.2rem;color:#666;margin-bottom:1rem;line-height:1.6}.skill-meta[data-v-a95b5427]{color:#999;font-size:.9rem}.toc[data-v-a95b5427]{background:#f8f9fa;border:1px solid #e1e5e9;border-radius:8px;padding:1.5rem;margin-bottom:2rem}.toc h3[data-v-a95b5427]{margin:0 0 1rem;color:#2c3e50;font-size:1.1rem}.toc-list[data-v-a95b5427]{list-style:none;padding:0;margin:0}.toc-list li[data-v-a95b5427]{margin-bottom:.5rem}.toc-level-1[data-v-a95b5427]{padding-left:0}.toc-level-2[data-v-a95b5427]{padding-left:1rem}.toc-level-3[data-v-a95b5427]{padding-left:2rem}.toc-level-4[data-v-a95b5427]{padding-left:3rem}.toc-level-5[data-v-a95b5427]{padding-left:4rem}.toc-level-6[data-v-a95b5427]{padding-left:5rem}.toc-link[data-v-a95b5427]{color:#555;text-decoration:none;font-size:.9rem;transition:color .2s ease}.toc-link[data-v-a95b5427]:hover{color:#3182ce}.markdown-content[data-v-a95b5427]{line-height:1.7;color:#333}.page-navigation[data-v-a95b5427]{display:flex;justify-content:space-between;margin-top:3rem;padding-top:2rem;border-top:1px solid #e1e5e9;gap:1rem}.nav-link[data-v-a95b5427]{flex:1;text-decoration:none;color:inherit;padding:1.5rem;border:1px solid #e1e5e9;border-radius:8px;transition:all .2s ease}.nav-link[data-v-a95b5427]:hover{border-color:#3182ce;box-shadow:0 2px 8px #0000001a}.nav-prev[data-v-a95b5427]{text-align:left}.nav-next[data-v-a95b5427]{text-align:right}.nav-direction[data-v-a95b5427]{color:#3182ce;font-size:.9rem;font-weight:500;margin-bottom:.25rem}.nav-title[data-v-a95b5427]{color:#2c3e50;font-weight:600}.not-found[data-v-a95b5427]{text-align:center;padding:3rem 0}.not-found h1[data-v-a95b5427]{font-size:2rem;color:#2c3e50;margin-bottom:1rem}.not-found p[data-v-a95b5427]{color:#666;margin-bottom:2rem}.back-home[data-v-a95b5427]{color:#3182ce;text-decoration:none;font-weight:500}.back-home[data-v-a95b5427]:hover{text-decoration:underline}@media (max-width: 768px){.skill-title[data-v-a95b5427]{font-size:2rem}.page-navigation[data-v-a95b5427]{flex-direction:column}.nav-next[data-v-a95b5427]{text-align:left}}.markdown-content h1,.markdown-content h2,.markdown-content h3,.markdown-content h4,.markdown-content h5,.markdown-content h6{color:#2c3e50;font-weight:600;margin-top:2rem;margin-bottom:1rem;line-height:1.3}.markdown-content h1{font-size:2rem}.markdown-content h2{font-size:1.5rem}.markdown-content h3{font-size:1.25rem}.markdown-content h4{font-size:1.1rem}.markdown-content p{margin-bottom:1rem}.markdown-content ul,.markdown-content ol{margin-bottom:1rem;padding-left:1.5rem}.markdown-content li{margin-bottom:.5rem}.markdown-content code{background:#f1f3f4;padding:.2rem .4rem;border-radius:3px;font-family:SF Mono,Consolas,monospace;font-size:.9em}.markdown-content pre{background:#f8f9fa;border:1px solid #e1e5e9;border-radius:6px;padding:1rem;overflow-x:auto;margin:1rem 0}.markdown-content pre code{background:none;padding:0;border-radius:0}.markdown-content blockquote{border-left:4px solid #3182ce;padding-left:1rem;margin:1rem 0;color:#666;font-style:italic}.markdown-content table{width:100%;border-collapse:collapse;margin:1rem 0}.markdown-content th,.markdown-content td{border:1px solid #e1e5e9;padding:.5rem;text-align:left}.markdown-content th{background:#f8f9fa;font-weight:600}:root{--color-background: #ffffff;--color-foreground: #0a0a0a;--color-muted: #f5f5f5;--color-muted-foreground: #737373;--color-border: #e5e5e5;--color-accent: #f5f5f5;--color-accent-foreground: #0a0a0a;--color-primary: #0a0a0a;--color-primary-foreground: #fafafa;--color-secondary: #f5f5f5;--color-secondary-foreground: #0a0a0a;--color-card: #ffffff;--color-card-foreground: #0a0a0a;--color-ring: #0a0a0a;--spacing-xs: .25rem;--spacing-sm: .5rem;--spacing-md: 1rem;--spacing-lg: 1.5rem;--spacing-xl: 2rem;--spacing-2xl: 3rem;--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Helvetica", "Arial", sans-serif;--font-mono: ui-monospace, "SF Mono", "Cascadia Code", "Source Code Pro", Menlo, Consolas, monospace;--radius-sm: .375rem;--radius-md: .5rem;--radius-lg: .75rem;--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / .05);--shadow-md: 0 4px 6px -1px rgb(0 0 0 / .1);--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / .1)}@media (prefers-color-scheme: dark){:root{--color-background: #0a0a0a;--color-foreground: #fafafa;--color-muted: #171717;--color-muted-foreground: #a3a3a3;--color-border: #262626;--color-accent: #171717;--color-accent-foreground: #fafafa;--color-primary: #fafafa;--color-primary-foreground: #0a0a0a;--color-secondary: #171717;--color-secondary-foreground: #fafafa;--color-card: #0a0a0a;--color-card-foreground: #fafafa;--color-ring: #fafafa}}*{margin:0;padding:0;box-sizing:border-box}html{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body{font-family:var(--font-sans);line-height:1.6;color:var(--color-foreground);background-color:var(--color-background)}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:var(--color-muted)}::-webkit-scrollbar-thumb{background:var(--color-border);border-radius:4px}::-webkit-scrollbar-thumb:hover{background:var(--color-muted-foreground)}h1,h2,h3,h4,h5,h6{font-weight:600;line-height:1.2;color:var(--color-foreground)}a{color:var(--color-primary);text-decoration:none;transition:opacity .2s ease}a:hover{opacity:.8}.container{width:100%;max-width:1400px;margin:0 auto;padding:0 var(--spacing-lg)}.text-muted{color:var(--color-muted-foreground)}.text-sm{font-size:.875rem}.text-xs{font-size:.75rem}.font-medium{font-weight:500}.font-semibold{font-weight:600}.font-bold{font-weight:700}.btn{display:inline-flex;align-items:center;justify-content:center;gap:.5rem;padding:.5rem 1rem;font-size:.875rem;font-weight:500;border-radius:var(--radius-md);border:1px solid transparent;cursor:pointer;transition:all .2s ease;white-space:nowrap}.btn:disabled{opacity:.5;cursor:not-allowed}.btn-primary{background:var(--color-primary);color:var(--color-primary-foreground)}.btn-primary:hover:not(:disabled){opacity:.9}.btn-ghost{background:transparent;color:var(--color-foreground)}.btn-ghost:hover:not(:disabled){background:var(--color-accent)}.card{background:var(--color-card);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:var(--spacing-lg);transition:all .2s ease}.card:hover{border-color:var(--color-primary);box-shadow:var(--shadow-md)}.input{width:100%;padding:.5rem .75rem;font-size:.875rem;background:var(--color-secondary);border:1px solid var(--color-border);border-radius:var(--radius-md);color:var(--color-foreground);transition:all .2s ease}.input:focus{outline:none;border-color:var(--color-ring);box-shadow:0 0 0 2px #0000001a}.input::placeholder{color:var(--color-muted-foreground)}.loading{display:flex;justify-content:center;align-items:center;min-height:200px;color:var(--color-muted-foreground)}@keyframes fadeIn{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.fade-in{animation:fadeIn .3s ease}@media (max-width: 1024px){.container{padding:0 var(--spacing-md)}}@media (max-width: 768px){.container{padding:0 var(--spacing-sm)}}
|