doccupine 0.0.107 → 0.0.108

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.
@@ -1 +1 @@
1
- export declare const componentsMdxTemplate = "---\ntitle: \"Components\"\ndescription: \"Explore the full library of built-in components available in your documentation pages.\"\ndate: \"2026-02-19\"\ncategory: \"Components\"\ncategoryOrder: 1\ncategoryIcon: \"blocks\"\norder: 0\n---\n\n# Components\n\nDoccupine includes a rich set of built-in components you can use directly in your MDX files - no imports needed. Browse the full library below.\n\n<Columns cols={3}>\n <Card title=\"Headers and Text\" icon=\"heading\" href=\"/headers-and-text\">\n Headings, paragraphs, bold, italic, links, and other text formatting.\n </Card>\n <Card title=\"Lists and Tables\" icon=\"list\" href=\"/lists-and-tables\">\n Ordered lists, unordered lists, and data tables.\n </Card>\n <Card title=\"Code\" icon=\"code\" href=\"/code\">\n Inline code, fenced code blocks, and syntax highlighting.\n </Card>\n <Card title=\"Accordion\" icon=\"chevrons-down\" href=\"/accordion\">\n Collapsible sections for organizing lengthy content.\n </Card>\n <Card title=\"Tabs\" icon=\"panel-top\" href=\"/tabs\">\n Tabbed interfaces for grouping related content.\n </Card>\n <Card title=\"Cards\" icon=\"square\" href=\"/cards\">\n Visual containers for content, icons, and links.\n </Card>\n <Card title=\"Buttons\" icon=\"mouse-pointer-click\" href=\"/buttons\">\n Action components with variants, sizes, and icons.\n </Card>\n <Card title=\"Callouts\" icon=\"megaphone\" href=\"/callouts\">\n Highlighted blocks for tips, warnings, and important notes.\n </Card>\n <Card title=\"Images and Embeds\" icon=\"image\" href=\"/images-and-embeds\">\n Images, videos, and embedded content.\n </Card>\n <Card title=\"Icons\" icon=\"smile\" href=\"/icons\">\n Lucide icons you can use anywhere in your docs.\n </Card>\n <Card title=\"Fields\" icon=\"text-cursor-input\" href=\"/fields\">\n Property and parameter documentation blocks.\n </Card>\n <Card title=\"Update\" icon=\"bell\" href=\"/update\">\n Changelog and update announcement blocks.\n </Card>\n <Card title=\"Columns\" icon=\"columns-3\" href=\"/columns\">\n Grid layouts for arranging cards and content side by side.\n </Card>\n <Card title=\"Steps\" icon=\"footprints\" href=\"/steps\">\n Numbered step-by-step guides and walkthroughs.\n </Card>\n <Card title=\"Color Swatches\" icon=\"palette\" href=\"/color-swatches\">\n Visual color palette swatches to document your theme colors.\n </Card>\n</Columns>";
1
+ export declare const componentsMdxTemplate = "---\ntitle: \"Components\"\ndescription: \"Explore the full library of built-in components available in your documentation pages.\"\ndate: \"2026-02-19\"\ncategory: \"Components\"\ncategoryOrder: 1\ncategoryIcon: \"blocks\"\norder: 0\n---\n\n# Components\n\nDoccupine includes a rich set of built-in components you can use directly in your MDX files - no imports needed. Browse the full library below.\n\n<Columns cols={3}>\n <Card title=\"Headers and Text\" icon=\"heading\" href=\"/headers-and-text\">\n Headings, paragraphs, bold, italic, links, and other text formatting.\n </Card>\n <Card title=\"Lists and Tables\" icon=\"list\" href=\"/lists-and-tables\">\n Ordered lists, unordered lists, and data tables.\n </Card>\n <Card title=\"Code\" icon=\"code\" href=\"/code\">\n Inline code, fenced code blocks, and syntax highlighting.\n </Card>\n <Card title=\"Accordion\" icon=\"chevrons-down\" href=\"/accordion\">\n Collapsible sections for organizing lengthy content.\n </Card>\n <Card title=\"Tabs\" icon=\"panel-top\" href=\"/tabs\">\n Tabbed interfaces for grouping related content.\n </Card>\n <Card title=\"Cards\" icon=\"square\" href=\"/cards\">\n Visual containers for content, icons, and links.\n </Card>\n <Card title=\"Buttons\" icon=\"mouse-pointer-click\" href=\"/buttons\">\n Action components with variants, sizes, and icons.\n </Card>\n <Card title=\"Callouts\" icon=\"megaphone\" href=\"/callouts\">\n Highlighted blocks for tips, warnings, and important notes.\n </Card>\n <Card title=\"Images and Embeds\" icon=\"image\" href=\"/image-and-embeds\">\n Images, videos, and embedded content.\n </Card>\n <Card title=\"Icons\" icon=\"smile\" href=\"/icons\">\n Lucide icons you can use anywhere in your docs.\n </Card>\n <Card title=\"Fields\" icon=\"text-cursor-input\" href=\"/fields\">\n Property and parameter documentation blocks.\n </Card>\n <Card title=\"Update\" icon=\"bell\" href=\"/update\">\n Changelog and update announcement blocks.\n </Card>\n <Card title=\"Columns\" icon=\"columns-3\" href=\"/columns\">\n Grid layouts for arranging cards and content side by side.\n </Card>\n <Card title=\"Steps\" icon=\"footprints\" href=\"/steps\">\n Numbered step-by-step guides and walkthroughs.\n </Card>\n <Card title=\"Color Swatches\" icon=\"palette\" href=\"/color-swatches\">\n Visual color palette swatches to document your theme colors.\n </Card>\n</Columns>";
@@ -37,7 +37,7 @@ Doccupine includes a rich set of built-in components you can use directly in you
37
37
  <Card title="Callouts" icon="megaphone" href="/callouts">
38
38
  Highlighted blocks for tips, warnings, and important notes.
39
39
  </Card>
40
- <Card title="Images and Embeds" icon="image" href="/images-and-embeds">
40
+ <Card title="Images and Embeds" icon="image" href="/image-and-embeds">
41
41
  Images, videos, and embedded content.
42
42
  </Card>
43
43
  <Card title="Icons" icon="smile" href="/icons">
@@ -23,7 +23,7 @@ export const packageJsonTemplate = JSON.stringify({
23
23
  minisearch: "^7.2.0",
24
24
  next: "16.2.10",
25
25
  "next-mdx-remote": "^6.0.0",
26
- "posthog-js": "^1.396.6",
26
+ "posthog-js": "^1.398.0",
27
27
  "posthog-node": "^5.39.4",
28
28
  react: "19.2.7",
29
29
  "react-dom": "19.2.7",
@@ -1 +1 @@
1
- export declare const searchServiceTemplate = "import MiniSearch from \"minisearch\";\nimport { listDocs } from \"@/services/mcp/tools\";\n\ninterface IndexedDoc {\n id: string;\n slug: string;\n title: string;\n content: string;\n}\n\nexport interface SearchHit {\n slug: string;\n snippet: string;\n}\n\nlet index: MiniSearch<IndexedDoc> | null = null;\nlet docs: IndexedDoc[] = [];\nlet buildPromise: Promise<void> | null = null;\n\nconst CONTEXT_BEFORE = 60;\nconst CONTEXT_AFTER = 90;\n\nasync function ensureIndex(): Promise<MiniSearch<IndexedDoc>> {\n if (index) return index;\n if (buildPromise) {\n await buildPromise;\n return index!;\n }\n\n buildPromise = (async () => {\n const resources = await listDocs();\n\n docs = resources.map((doc) => {\n const slug =\n doc.path.replace(/^app\\//, \"\").replace(/\\/page\\.\\w+$/, \"\") || \"\";\n const cleanContent = doc.content\n .replace(/\\r\\n/g, \"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .slice(0, 200_000);\n return {\n id: slug || \"__index__\",\n slug,\n title: doc.name,\n content: cleanContent,\n };\n });\n\n index = new MiniSearch<IndexedDoc>({\n fields: [\"title\", \"content\"],\n storeFields: [\"slug\", \"title\", \"content\"],\n searchOptions: {\n boost: { title: 3 },\n fuzzy: 0.2,\n prefix: true,\n },\n });\n\n index.addAll(docs);\n })();\n\n await buildPromise;\n return index!;\n}\n\nfunction extractSnippet(content: string, query: string): string {\n const lower = content.toLowerCase();\n const qLower = query.toLowerCase();\n const matchIdx = lower.indexOf(qLower);\n\n if (matchIdx === -1) {\n // Fuzzy match - no exact substring. Return start of content.\n const end = Math.min(content.length, CONTEXT_BEFORE + CONTEXT_AFTER);\n const raw = content.slice(0, end).trim();\n return raw.length < content.length ? raw + \"...\" : raw;\n }\n\n let start = Math.max(0, matchIdx - CONTEXT_BEFORE);\n let end = Math.min(content.length, matchIdx + query.length + CONTEXT_AFTER);\n\n // Align to word boundaries\n if (start > 0) {\n const space = content.indexOf(\" \", start);\n if (space !== -1 && space < matchIdx) start = space + 1;\n }\n if (end < content.length) {\n const space = content.lastIndexOf(\" \", end);\n if (space > matchIdx + query.length) end = space;\n }\n\n const prefix = start > 0 ? \"...\" : \"\";\n const suffix = end < content.length ? \"...\" : \"\";\n const snippet = content.slice(start, end).replace(/\\n+/g, \" \").trim();\n\n return prefix + snippet + suffix;\n}\n\nexport async function searchContent(\n query: string,\n limit = 10,\n): Promise<SearchHit[]> {\n const q = query.trim();\n if (!q) return [];\n\n const idx = await ensureIndex();\n const results = idx.search(q);\n\n return results.slice(0, limit).map((result) => {\n const doc = docs.find((d) => d.id === result.id);\n const content = doc?.content || \"\";\n return {\n slug: doc?.slug || \"\",\n snippet: extractSnippet(content, q),\n };\n });\n}\n";
1
+ export declare const searchServiceTemplate = "import MiniSearch from \"minisearch\";\nimport { listDocs } from \"@/services/mcp/tools\";\n\ninterface IndexedDoc {\n id: string;\n slug: string;\n title: string;\n content: string;\n}\n\nexport interface SearchHit {\n slug: string;\n snippet: string;\n}\n\nlet index: MiniSearch<IndexedDoc> | null = null;\nlet docs: IndexedDoc[] = [];\nlet buildPromise: Promise<void> | null = null;\n\nconst CONTEXT_BEFORE = 60;\nconst CONTEXT_AFTER = 90;\n\nasync function ensureIndex(): Promise<MiniSearch<IndexedDoc>> {\n if (index) return index;\n if (buildPromise) {\n await buildPromise;\n return index!;\n }\n\n buildPromise = (async () => {\n const resources = await listDocs();\n\n docs = resources.map((doc) => {\n // Strip the leading \"app/\", the \"/page.ext\" suffix, and any Next.js\n // route-group segments like \"(site)\" which never appear in URLs. This\n // must match the URL slug produced by toDocPath() in mcp/tools.ts and\n // the nav slugs in the layout, otherwise content hits fail to map back\n // to a page on the client and get silently dropped.\n const slug = doc.path\n .replace(/^app\\//, \"\")\n .replace(/\\/page\\.\\w+$/, \"\")\n .split(\"/\")\n .filter((seg) => !(seg.startsWith(\"(\") && seg.endsWith(\")\")))\n .join(\"/\");\n const cleanContent = doc.content\n .replace(/\\r\\n/g, \"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .slice(0, 200_000);\n return {\n id: slug || \"__index__\",\n slug,\n title: doc.name,\n content: cleanContent,\n };\n });\n\n index = new MiniSearch<IndexedDoc>({\n fields: [\"title\", \"content\"],\n storeFields: [\"slug\", \"title\", \"content\"],\n searchOptions: {\n boost: { title: 3 },\n fuzzy: 0.2,\n prefix: true,\n },\n });\n\n index.addAll(docs);\n })();\n\n await buildPromise;\n return index!;\n}\n\nfunction extractSnippet(content: string, query: string): string {\n const lower = content.toLowerCase();\n const qLower = query.toLowerCase();\n const matchIdx = lower.indexOf(qLower);\n\n if (matchIdx === -1) {\n // Fuzzy match - no exact substring. Return start of content.\n const end = Math.min(content.length, CONTEXT_BEFORE + CONTEXT_AFTER);\n const raw = content.slice(0, end).trim();\n return raw.length < content.length ? raw + \"...\" : raw;\n }\n\n let start = Math.max(0, matchIdx - CONTEXT_BEFORE);\n let end = Math.min(content.length, matchIdx + query.length + CONTEXT_AFTER);\n\n // Align to word boundaries\n if (start > 0) {\n const space = content.indexOf(\" \", start);\n if (space !== -1 && space < matchIdx) start = space + 1;\n }\n if (end < content.length) {\n const space = content.lastIndexOf(\" \", end);\n if (space > matchIdx + query.length) end = space;\n }\n\n const prefix = start > 0 ? \"...\" : \"\";\n const suffix = end < content.length ? \"...\" : \"\";\n const snippet = content.slice(start, end).replace(/\\n+/g, \" \").trim();\n\n return prefix + snippet + suffix;\n}\n\nexport async function searchContent(\n query: string,\n limit = 10,\n): Promise<SearchHit[]> {\n const q = query.trim();\n if (!q) return [];\n\n const idx = await ensureIndex();\n const results = idx.search(q);\n\n return results.slice(0, limit).map((result) => {\n const doc = docs.find((d) => d.id === result.id);\n const content = doc?.content || \"\";\n return {\n slug: doc?.slug || \"\",\n snippet: extractSnippet(content, q),\n };\n });\n}\n";
@@ -31,8 +31,17 @@ async function ensureIndex(): Promise<MiniSearch<IndexedDoc>> {
31
31
  const resources = await listDocs();
32
32
 
33
33
  docs = resources.map((doc) => {
34
- const slug =
35
- doc.path.replace(/^app\\//, "").replace(/\\/page\\.\\w+$/, "") || "";
34
+ // Strip the leading "app/", the "/page.ext" suffix, and any Next.js
35
+ // route-group segments like "(site)" which never appear in URLs. This
36
+ // must match the URL slug produced by toDocPath() in mcp/tools.ts and
37
+ // the nav slugs in the layout, otherwise content hits fail to map back
38
+ // to a page on the client and get silently dropped.
39
+ const slug = doc.path
40
+ .replace(/^app\\//, "")
41
+ .replace(/\\/page\\.\\w+$/, "")
42
+ .split("/")
43
+ .filter((seg) => !(seg.startsWith("(") && seg.endsWith(")")))
44
+ .join("/");
36
45
  const cleanContent = doc.content
37
46
  .replace(/\\r\\n/g, "\\n")
38
47
  .replace(/\\n{3,}/g, "\\n\\n")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "doccupine",
3
- "version": "0.0.107",
3
+ "version": "0.0.108",
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": {