doccupine 0.0.63 → 0.0.64

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.
Files changed (86) hide show
  1. package/README.md +4 -3
  2. package/dist/lib/structures.js +2 -0
  3. package/dist/templates/app/theme.d.ts +3 -1
  4. package/dist/templates/app/theme.js +17 -15
  5. package/dist/templates/components/Chat.d.ts +1 -1
  6. package/dist/templates/components/Chat.js +251 -158
  7. package/dist/templates/components/DocsSideBar.d.ts +1 -1
  8. package/dist/templates/components/DocsSideBar.js +34 -11
  9. package/dist/templates/components/SideBar.d.ts +1 -1
  10. package/dist/templates/components/SideBar.js +12 -2
  11. package/dist/templates/components/layout/Accordion.d.ts +1 -1
  12. package/dist/templates/components/layout/Accordion.js +1 -1
  13. package/dist/templates/components/layout/ActionBar.d.ts +1 -1
  14. package/dist/templates/components/layout/ActionBar.js +15 -60
  15. package/dist/templates/components/layout/Callout.d.ts +1 -1
  16. package/dist/templates/components/layout/Callout.js +1 -1
  17. package/dist/templates/components/layout/Card.d.ts +1 -1
  18. package/dist/templates/components/layout/Card.js +26 -7
  19. package/dist/templates/components/layout/Columns.d.ts +1 -1
  20. package/dist/templates/components/layout/Columns.js +1 -1
  21. package/dist/templates/components/layout/DocsComponents.d.ts +1 -1
  22. package/dist/templates/components/layout/DocsComponents.js +37 -11
  23. package/dist/templates/components/layout/DocsNavigation.d.ts +1 -1
  24. package/dist/templates/components/layout/DocsNavigation.js +3 -2
  25. package/dist/templates/components/layout/Field.d.ts +1 -1
  26. package/dist/templates/components/layout/Field.js +1 -1
  27. package/dist/templates/components/layout/Footer.d.ts +1 -1
  28. package/dist/templates/components/layout/Footer.js +28 -6
  29. package/dist/templates/components/layout/Header.d.ts +1 -1
  30. package/dist/templates/components/layout/Header.js +10 -12
  31. package/dist/templates/components/layout/SharedStyles.d.ts +1 -1
  32. package/dist/templates/components/layout/SharedStyles.js +26 -2
  33. package/dist/templates/components/layout/StaticLinks.d.ts +1 -1
  34. package/dist/templates/components/layout/StaticLinks.js +7 -3
  35. package/dist/templates/components/layout/Steps.d.ts +1 -1
  36. package/dist/templates/components/layout/Steps.js +7 -2
  37. package/dist/templates/components/layout/Tabs.d.ts +1 -1
  38. package/dist/templates/components/layout/Tabs.js +2 -2
  39. package/dist/templates/components/layout/Update.d.ts +1 -1
  40. package/dist/templates/components/layout/Update.js +1 -1
  41. package/dist/templates/mdx/ai-assistant.mdx.d.ts +1 -1
  42. package/dist/templates/mdx/ai-assistant.mdx.js +8 -0
  43. package/dist/templates/mdx/callouts.mdx.d.ts +1 -1
  44. package/dist/templates/mdx/callouts.mdx.js +6 -2
  45. package/dist/templates/mdx/cards.mdx.d.ts +1 -1
  46. package/dist/templates/mdx/cards.mdx.js +19 -3
  47. package/dist/templates/mdx/columns.mdx.d.ts +1 -1
  48. package/dist/templates/mdx/columns.mdx.js +2 -2
  49. package/dist/templates/mdx/commands.mdx.d.ts +1 -1
  50. package/dist/templates/mdx/commands.mdx.js +10 -2
  51. package/dist/templates/mdx/components.mdx.d.ts +1 -0
  52. package/dist/templates/mdx/components.mdx.js +56 -0
  53. package/dist/templates/mdx/deployment.mdx.d.ts +1 -1
  54. package/dist/templates/mdx/deployment.mdx.js +1 -1
  55. package/dist/templates/mdx/globals.mdx.d.ts +1 -1
  56. package/dist/templates/mdx/globals.mdx.js +5 -0
  57. package/dist/templates/mdx/index.mdx.d.ts +1 -1
  58. package/dist/templates/mdx/index.mdx.js +5 -5
  59. package/dist/templates/mdx/model-context-protocol.mdx.d.ts +1 -1
  60. package/dist/templates/mdx/model-context-protocol.mdx.js +2 -2
  61. package/dist/templates/mdx/navigation.mdx.d.ts +1 -1
  62. package/dist/templates/mdx/navigation.mdx.js +1 -1
  63. package/dist/templates/mdx/platform/external-links.mdx.d.ts +1 -1
  64. package/dist/templates/mdx/platform/external-links.mdx.js +2 -0
  65. package/dist/templates/mdx/platform/fonts-settings.mdx.d.ts +1 -1
  66. package/dist/templates/mdx/platform/fonts-settings.mdx.js +8 -5
  67. package/dist/templates/mdx/platform/index.mdx.d.ts +1 -1
  68. package/dist/templates/mdx/platform/index.mdx.js +10 -1
  69. package/dist/templates/mdx/platform/site-settings.mdx.d.ts +1 -1
  70. package/dist/templates/mdx/platform/site-settings.mdx.js +4 -4
  71. package/dist/templates/mdx/sections.mdx.d.ts +1 -1
  72. package/dist/templates/mdx/sections.mdx.js +2 -2
  73. package/dist/templates/mdx/steps.mdx.d.ts +1 -1
  74. package/dist/templates/mdx/steps.mdx.js +4 -0
  75. package/dist/templates/mdx/tabs.mdx.d.ts +1 -1
  76. package/dist/templates/mdx/tabs.mdx.js +1 -1
  77. package/dist/templates/package.js +4 -4
  78. package/dist/templates/services/llm/types.d.ts +1 -1
  79. package/dist/templates/services/llm/types.js +1 -1
  80. package/dist/templates/services/mcp/server.d.ts +1 -1
  81. package/dist/templates/services/mcp/server.js +1 -1
  82. package/dist/templates/services/mcp/tools.d.ts +1 -1
  83. package/dist/templates/services/mcp/tools.js +1 -5
  84. package/dist/templates/utils/config.d.ts +1 -1
  85. package/dist/templates/utils/config.js +1 -1
  86. package/package.json +1 -1
@@ -1 +1 @@
1
- export declare const mcpToolsTemplate = "import path from \"node:path\";\nimport fs from \"node:fs/promises\";\nimport type {\n MCPToolDefinition,\n DocsResource,\n DocsChunk,\n GetDocParams,\n ListDocsParams,\n} from \"@/services/mcp/types\";\n\nconst PROJECT_ROOT = process.cwd();\nconst APP_DIR = path.join(PROJECT_ROOT, \"app\");\nconst VALID_EXT = new Set([\".ts\", \".tsx\", \".js\", \".jsx\"]);\n\n/**\n * Tool definitions for MCP - these describe the available tools\n */\nexport const DOCS_TOOLS: MCPToolDefinition[] = [\n {\n name: \"search_docs\",\n description:\n \"Search through the documentation content using semantic search. Returns relevant chunks of documentation based on the query.\",\n inputSchema: {\n type: \"object\",\n properties: {\n query: {\n type: \"string\",\n description: \"The search query to find relevant documentation\",\n },\n limit: {\n type: \"number\",\n description: \"Maximum number of results to return (default: 6)\",\n },\n },\n required: [\"query\"],\n },\n },\n {\n name: \"get_doc\",\n description:\n \"Get the full content of a specific documentation page by its path.\",\n inputSchema: {\n type: \"object\",\n properties: {\n path: {\n type: \"string\",\n description:\n \"The file path to the documentation page (e.g., 'app/getting-started/page.tsx')\",\n },\n },\n required: [\"path\"],\n },\n },\n {\n name: \"list_docs\",\n description:\n \"List all available documentation pages, optionally filtered by directory.\",\n inputSchema: {\n type: \"object\",\n properties: {\n directory: {\n type: \"string\",\n description:\n \"Optional directory to filter results (e.g., 'components')\",\n },\n },\n },\n },\n];\n\n/**\n * Recursively walk directory to find documentation files\n */\nasync function* walkDocs(dir: string): AsyncGenerator<string> {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n if ([\"node_modules\", \".next\", \".git\", \"api\"].includes(entry.name)) {\n continue;\n }\n yield* walkDocs(fullPath);\n } else {\n const ext = path.extname(entry.name).toLowerCase();\n if (VALID_EXT.has(ext) && entry.name.startsWith(\"page.\")) {\n yield fullPath;\n }\n }\n }\n}\n\n/**\n * Extract content blocks from a file\n */\nfunction extractContentBlocks(fileText: string): string[] {\n const results: string[] = [];\n\n const tplRegex = /(?:export\\s+)?const\\s+content\\s*=\\s*`((?:\\\\`|[^`])*)`\\s*;/g;\n let m: RegExpExecArray | null;\n while ((m = tplRegex.exec(fileText)) !== null) {\n results.push(m[1]);\n }\n\n const sglRegex = /(?:export\\s+)?const\\s+content\\s*=\\s*'([^']*)'\\s*;/g;\n while ((m = sglRegex.exec(fileText)) !== null) {\n results.push(m[1]);\n }\n\n const dblRegex = /(?:export\\s+)?const\\s+content\\s*=\\s*\"([^\"]*)\"\\s*;/g;\n while ((m = dblRegex.exec(fileText)) !== null) {\n results.push(m[1]);\n }\n\n return results;\n}\n\n/**\n * Get the title from markdown content\n */\nfunction extractTitle(content: string): string {\n const match = content.match(/^#\\s+(.+)$/m);\n return match ? match[1].trim() : \"Untitled\";\n}\n\n/**\n * List all documentation resources\n */\nexport async function listDocs(\n params?: ListDocsParams,\n): Promise<DocsResource[]> {\n const resources: DocsResource[] = [];\n const filterDir = params?.directory;\n\n for await (const filePath of walkDocs(APP_DIR)) {\n const relativePath = path.relative(PROJECT_ROOT, filePath);\n\n if (filterDir && !relativePath.includes(filterDir)) {\n continue;\n }\n\n try {\n const fileContent = await fs.readFile(filePath, \"utf8\");\n const blocks = extractContentBlocks(fileContent);\n const content = blocks.join(\"\\n\\n\");\n const title = extractTitle(content);\n const docPath = path.dirname(relativePath).replace(/^app\\/?/, \"\") || \"/\";\n\n resources.push({\n uri: `docs://${docPath}`,\n name: title,\n path: relativePath,\n content,\n });\n } catch (error) {\n console.warn(`Failed to read doc file: ${filePath}`, error);\n }\n }\n\n return resources;\n}\n\n/**\n * Get a specific documentation page\n */\nexport async function getDoc(\n params: GetDocParams,\n): Promise<DocsResource | null> {\n let targetPath = params.path;\n\n // Normalize path\n if (!targetPath.startsWith(\"app/\")) {\n targetPath = `app/${targetPath}`;\n }\n if (!targetPath.includes(\"page.\")) {\n targetPath = path.join(targetPath, \"page.tsx\");\n }\n\n const fullPath = path.join(PROJECT_ROOT, targetPath);\n\n // Prevent path traversal\n const resolvedPath = path.resolve(fullPath);\n if (!resolvedPath.startsWith(path.resolve(APP_DIR))) {\n return null;\n }\n\n try {\n const fileContent = await fs.readFile(fullPath, \"utf8\");\n const blocks = extractContentBlocks(fileContent);\n const content = blocks.join(\"\\n\\n\");\n const title = extractTitle(content);\n const docPath = path.dirname(targetPath).replace(/^app\\/?/, \"\") || \"/\";\n\n return {\n uri: `docs://${docPath}`,\n name: title,\n path: targetPath,\n content,\n };\n } catch (error) {\n console.warn(`Failed to read doc: ${targetPath}`, error);\n return null;\n }\n}\n\n/**\n * Chunk text for embeddings.\n * - chunkSize=800 chars balances granularity with embedding context window limits\n * - overlap=100 chars ensures continuity so searches don't miss content at chunk boundaries\n */\nexport function chunkText(\n text: string,\n chunkSize = 800,\n overlap = 100,\n): string[] {\n const chunks: string[] = [];\n let i = 0;\n while (i < text.length) {\n const end = Math.min(i + chunkSize, text.length);\n chunks.push(text.slice(i, end));\n if (end === text.length) break;\n i = end - overlap;\n if (i < 0) i = 0;\n }\n return chunks;\n}\n\n/**\n * Get all documentation chunks for indexing\n */\nexport async function getAllDocsChunks(): Promise<DocsChunk[]> {\n const allChunks: DocsChunk[] = [];\n const docs = await listDocs();\n\n for (const doc of docs) {\n const cleanContent = doc.content\n .replace(/\\r\\n/g, \"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .slice(0, 200_000);\n\n const textChunks = chunkText(cleanContent);\n for (let i = 0; i < textChunks.length; i++) {\n allChunks.push({\n id: `${doc.path}:${i}`,\n text: textChunks[i],\n path: doc.path,\n uri: doc.uri,\n });\n }\n }\n\n return allChunks;\n}\n";
1
+ export declare const mcpToolsTemplate = "import path from \"node:path\";\nimport fs from \"node:fs/promises\";\nimport type {\n MCPToolDefinition,\n DocsResource,\n DocsChunk,\n GetDocParams,\n ListDocsParams,\n} from \"@/services/mcp/types\";\n\nconst PROJECT_ROOT = process.cwd();\nconst APP_DIR = path.join(PROJECT_ROOT, \"app\");\nconst VALID_EXT = new Set([\".ts\", \".tsx\", \".js\", \".jsx\"]);\n\n/**\n * Tool definitions for MCP - these describe the available tools\n */\nexport const DOCS_TOOLS: MCPToolDefinition[] = [\n {\n name: \"search_docs\",\n description:\n \"Search through the documentation content using semantic search. Returns relevant chunks of documentation based on the query.\",\n inputSchema: {\n type: \"object\",\n properties: {\n query: {\n type: \"string\",\n description: \"The search query to find relevant documentation\",\n },\n limit: {\n type: \"number\",\n description: \"Maximum number of results to return (default: 6)\",\n },\n },\n required: [\"query\"],\n },\n },\n {\n name: \"get_doc\",\n description:\n \"Get the full content of a specific documentation page by its path.\",\n inputSchema: {\n type: \"object\",\n properties: {\n path: {\n type: \"string\",\n description:\n \"The file path to the documentation page (e.g., 'app/getting-started/page.tsx')\",\n },\n },\n required: [\"path\"],\n },\n },\n {\n name: \"list_docs\",\n description:\n \"List all available documentation pages, optionally filtered by directory.\",\n inputSchema: {\n type: \"object\",\n properties: {\n directory: {\n type: \"string\",\n description:\n \"Optional directory to filter results (e.g., 'components')\",\n },\n },\n },\n },\n];\n\n/**\n * Recursively walk directory to find documentation files\n */\nasync function* walkDocs(dir: string): AsyncGenerator<string> {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n if ([\"node_modules\", \".next\", \".git\", \"api\"].includes(entry.name)) {\n continue;\n }\n yield* walkDocs(fullPath);\n } else {\n const ext = path.extname(entry.name).toLowerCase();\n if (VALID_EXT.has(ext) && entry.name.startsWith(\"page.\")) {\n yield fullPath;\n }\n }\n }\n}\n\n/**\n * Extract content blocks from a file\n */\nfunction extractContentBlocks(fileText: string): string[] {\n const results: string[] = [];\n\n const tplRegex = /(?:export\\s+)?const\\s+content\\s*=\\s*`((?:\\\\`|[^`])*)`\\s*;/g;\n let m: RegExpExecArray | null;\n while ((m = tplRegex.exec(fileText)) !== null) {\n results.push(m[1]);\n }\n\n const sglRegex = /(?:export\\s+)?const\\s+content\\s*=\\s*'([^']*)'\\s*;/g;\n while ((m = sglRegex.exec(fileText)) !== null) {\n results.push(m[1]);\n }\n\n const dblRegex = /(?:export\\s+)?const\\s+content\\s*=\\s*\"([^\"]*)\"\\s*;/g;\n while ((m = dblRegex.exec(fileText)) !== null) {\n results.push(m[1]);\n }\n\n return results;\n}\n\n/**\n * Get the title from markdown content\n */\nfunction extractTitle(content: string): string {\n const match = content.match(/^#\\s+(.+)$/m);\n return match ? match[1].trim() : \"Untitled\";\n}\n\n/**\n * List all documentation resources\n */\nexport async function listDocs(\n params?: ListDocsParams,\n): Promise<DocsResource[]> {\n const resources: DocsResource[] = [];\n const filterDir = params?.directory;\n\n for await (const filePath of walkDocs(APP_DIR)) {\n const relativePath = path.relative(PROJECT_ROOT, filePath);\n\n if (filterDir && !relativePath.includes(filterDir)) {\n continue;\n }\n\n try {\n const fileContent = await fs.readFile(filePath, \"utf8\");\n const blocks = extractContentBlocks(fileContent);\n const content = blocks.join(\"\\n\\n\");\n const title = extractTitle(content);\n const docPath = path.dirname(relativePath).replace(/^app\\/?/, \"\") || \"/\";\n\n resources.push({\n uri: `docs://${docPath}`,\n name: title,\n path: relativePath,\n content,\n });\n } catch (error) {\n console.warn(`Failed to read doc file: ${filePath}`, error);\n }\n }\n\n return resources;\n}\n\n/**\n * Get a specific documentation page\n */\nexport async function getDoc(\n params: GetDocParams,\n): Promise<DocsResource | null> {\n let targetPath = params.path;\n\n // Normalize path\n if (!targetPath.startsWith(\"app/\")) {\n targetPath = `app/${targetPath}`;\n }\n if (!targetPath.includes(\"page.\")) {\n targetPath = path.join(targetPath, \"page.tsx\");\n }\n\n const fullPath = path.join(PROJECT_ROOT, targetPath);\n\n // Prevent path traversal\n const resolvedPath = path.resolve(fullPath);\n if (!resolvedPath.startsWith(path.resolve(APP_DIR))) {\n return null;\n }\n\n try {\n const fileContent = await fs.readFile(fullPath, \"utf8\");\n const blocks = extractContentBlocks(fileContent);\n const content = blocks.join(\"\\n\\n\");\n const title = extractTitle(content);\n const docPath = path.dirname(targetPath).replace(/^app\\/?/, \"\") || \"/\";\n\n return {\n uri: `docs://${docPath}`,\n name: title,\n path: targetPath,\n content,\n };\n } catch (error) {\n console.warn(`Failed to read doc: ${targetPath}`, error);\n return null;\n }\n}\n\n/**\n * Chunk text for embeddings.\n * - chunkSize=800 chars balances granularity with embedding context window limits\n * - overlap=100 chars ensures continuity so searches don't miss content at chunk boundaries\n */\nfunction chunkText(text: string, chunkSize = 800, overlap = 100): string[] {\n const chunks: string[] = [];\n let i = 0;\n while (i < text.length) {\n const end = Math.min(i + chunkSize, text.length);\n chunks.push(text.slice(i, end));\n if (end === text.length) break;\n i = end - overlap;\n if (i < 0) i = 0;\n }\n return chunks;\n}\n\n/**\n * Get all documentation chunks for indexing\n */\nexport async function getAllDocsChunks(): Promise<DocsChunk[]> {\n const allChunks: DocsChunk[] = [];\n const docs = await listDocs();\n\n for (const doc of docs) {\n const cleanContent = doc.content\n .replace(/\\r\\n/g, \"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .slice(0, 200_000);\n\n const textChunks = chunkText(cleanContent);\n for (let i = 0; i < textChunks.length; i++) {\n allChunks.push({\n id: `${doc.path}:${i}`,\n text: textChunks[i],\n path: doc.path,\n uri: doc.uri,\n });\n }\n }\n\n return allChunks;\n}\n";
@@ -207,11 +207,7 @@ export async function getDoc(
207
207
  * - chunkSize=800 chars balances granularity with embedding context window limits
208
208
  * - overlap=100 chars ensures continuity so searches don't miss content at chunk boundaries
209
209
  */
210
- export function chunkText(
211
- text: string,
212
- chunkSize = 800,
213
- overlap = 100,
214
- ): string[] {
210
+ function chunkText(text: string, chunkSize = 800, overlap = 100): string[] {
215
211
  const chunks: string[] = [];
216
212
  let i = 0;
217
213
  while (i < text.length) {
@@ -1 +1 @@
1
- export declare const configTemplate = "import { z } from \"zod\";\nimport configData from \"@/config.json\";\n\nconst configSchema = z.object({\n name: z.string().optional(),\n description: z.string().optional(),\n icon: z.string().optional(),\n image: z.string().optional(),\n});\n\nexport type Config = z.infer<typeof configSchema>;\n\nexport const config: Config = configSchema.parse(configData);\n";
1
+ export declare const configTemplate = "import { z } from \"zod\";\nimport configData from \"@/config.json\";\n\nconst configSchema = z.object({\n name: z.string().optional(),\n description: z.string().optional(),\n icon: z.string().optional(),\n image: z.string().optional(),\n});\n\ntype Config = z.infer<typeof configSchema>;\n\nexport const config: Config = configSchema.parse(configData);\n";
@@ -8,7 +8,7 @@ const configSchema = z.object({
8
8
  image: z.string().optional(),
9
9
  });
10
10
 
11
- export type Config = z.infer<typeof configSchema>;
11
+ type Config = z.infer<typeof configSchema>;
12
12
 
13
13
  export const config: Config = configSchema.parse(configData);
14
14
  `;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "doccupine",
3
- "version": "0.0.63",
3
+ "version": "0.0.64",
4
4
  "description": "Free and open-source documentation platform. Write MDX, get a production-ready site with AI chat, built-in components, and an MCP server - in one command.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {