doccupine 0.0.70 → 0.0.72
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/README.md +1 -0
- package/dist/index.js +50 -20
- package/dist/lib/structures.js +2 -0
- package/dist/templates/components/PostHogProvider.d.ts +1 -1
- package/dist/templates/components/PostHogProvider.js +1 -1
- package/dist/templates/components/layout/Button.d.ts +1 -1
- package/dist/templates/components/layout/Button.js +8 -0
- package/dist/templates/components/layout/GlobalStyles.d.ts +1 -1
- package/dist/templates/components/layout/GlobalStyles.js +8 -0
- package/dist/templates/mdx/image-and-embeds.mdx.d.ts +1 -1
- package/dist/templates/mdx/image-and-embeds.mdx.js +15 -0
- package/dist/templates/mdx/platform/analytics.mdx.d.ts +1 -0
- package/dist/templates/mdx/platform/analytics.mdx.js +51 -0
- package/dist/templates/package.js +10 -9
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -135,6 +135,7 @@ Place these JSON files in your project root (where you run `doccupine`). They ar
|
|
|
135
135
|
| `links.json` | Static header/footer links |
|
|
136
136
|
| `fonts.json` | Font configuration (Google Fonts or local) |
|
|
137
137
|
| `sections.json` | Section definitions for tabbed doc groups (see [Sections](#sections)) |
|
|
138
|
+
| `analytics.json` | Analytics provider configuration (PostHog supported) |
|
|
138
139
|
|
|
139
140
|
## Public Directory
|
|
140
141
|
|
package/dist/index.js
CHANGED
|
@@ -26,6 +26,7 @@ class MDXToNextJSGenerator {
|
|
|
26
26
|
configWatcher = null;
|
|
27
27
|
fontWatcher = null;
|
|
28
28
|
publicWatcher = null;
|
|
29
|
+
rootDirWatcher = null;
|
|
29
30
|
analyticsWatcher = null;
|
|
30
31
|
configFiles = [
|
|
31
32
|
"theme.json",
|
|
@@ -552,27 +553,53 @@ class MDXToNextJSGenerator {
|
|
|
552
553
|
});
|
|
553
554
|
const publicDir = path.join(this.rootDir, "public");
|
|
554
555
|
if (await fs.pathExists(publicDir)) {
|
|
555
|
-
this.
|
|
556
|
-
persistent: true,
|
|
557
|
-
ignoreInitial: true,
|
|
558
|
-
});
|
|
559
|
-
this.publicWatcher
|
|
560
|
-
.on("add", (filePath) => {
|
|
561
|
-
console.log(chalk.cyan(`📁 Public file added: ${path.relative(publicDir, filePath)}`));
|
|
562
|
-
this.handlePublicFileChange(filePath);
|
|
563
|
-
})
|
|
564
|
-
.on("change", (filePath) => {
|
|
565
|
-
console.log(chalk.cyan(`📁 Public file changed: ${path.relative(publicDir, filePath)}`));
|
|
566
|
-
this.handlePublicFileChange(filePath);
|
|
567
|
-
})
|
|
568
|
-
.on("unlink", (filePath) => {
|
|
569
|
-
console.log(chalk.red(`🗑️ Public file deleted: ${path.relative(publicDir, filePath)}`));
|
|
570
|
-
this.handlePublicFileDelete(filePath);
|
|
571
|
-
})
|
|
572
|
-
.on("error", (error) => {
|
|
573
|
-
console.error(chalk.red("❌ Public watcher error:"), error);
|
|
574
|
-
});
|
|
556
|
+
this.setupPublicWatcher();
|
|
575
557
|
}
|
|
558
|
+
// Watch rootDir for public directory creation
|
|
559
|
+
this.rootDirWatcher = chokidar.watch(this.rootDir, {
|
|
560
|
+
persistent: true,
|
|
561
|
+
ignoreInitial: true,
|
|
562
|
+
depth: 0,
|
|
563
|
+
});
|
|
564
|
+
this.rootDirWatcher
|
|
565
|
+
.on("addDir", async (dirPath) => {
|
|
566
|
+
if (path.basename(dirPath) === "public" &&
|
|
567
|
+
path.dirname(dirPath) === this.rootDir &&
|
|
568
|
+
!this.publicWatcher) {
|
|
569
|
+
console.log(chalk.cyan("📁 Public directory created"));
|
|
570
|
+
await this.copyPublicFiles();
|
|
571
|
+
this.setupPublicWatcher();
|
|
572
|
+
}
|
|
573
|
+
})
|
|
574
|
+
.on("error", (error) => {
|
|
575
|
+
console.error(chalk.red("❌ Root dir watcher error:"), error);
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
setupPublicWatcher() {
|
|
579
|
+
if (this.publicWatcher) {
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
const publicDir = path.join(this.rootDir, "public");
|
|
583
|
+
this.publicWatcher = chokidar.watch(publicDir, {
|
|
584
|
+
persistent: true,
|
|
585
|
+
ignoreInitial: true,
|
|
586
|
+
});
|
|
587
|
+
this.publicWatcher
|
|
588
|
+
.on("add", (filePath) => {
|
|
589
|
+
console.log(chalk.cyan(`📁 Public file added: ${path.relative(publicDir, filePath)}`));
|
|
590
|
+
this.handlePublicFileChange(filePath);
|
|
591
|
+
})
|
|
592
|
+
.on("change", (filePath) => {
|
|
593
|
+
console.log(chalk.cyan(`📁 Public file changed: ${path.relative(publicDir, filePath)}`));
|
|
594
|
+
this.handlePublicFileChange(filePath);
|
|
595
|
+
})
|
|
596
|
+
.on("unlink", (filePath) => {
|
|
597
|
+
console.log(chalk.red(`🗑️ Public file deleted: ${path.relative(publicDir, filePath)}`));
|
|
598
|
+
this.handlePublicFileDelete(filePath);
|
|
599
|
+
})
|
|
600
|
+
.on("error", (error) => {
|
|
601
|
+
console.error(chalk.red("❌ Public watcher error:"), error);
|
|
602
|
+
});
|
|
576
603
|
}
|
|
577
604
|
async parseMDXFile(file) {
|
|
578
605
|
const fullPath = path.join(this.watchDir, file);
|
|
@@ -839,6 +866,9 @@ export default function Page() {
|
|
|
839
866
|
await this.publicWatcher.close();
|
|
840
867
|
console.log(chalk.yellow("👋 Stopped watching for public directory changes"));
|
|
841
868
|
}
|
|
869
|
+
if (this.rootDirWatcher) {
|
|
870
|
+
await this.rootDirWatcher.close();
|
|
871
|
+
}
|
|
842
872
|
}
|
|
843
873
|
}
|
|
844
874
|
program
|
package/dist/lib/structures.js
CHANGED
|
@@ -96,6 +96,7 @@ import { platformThemeSettingsMdxTemplate } from "../templates/mdx/platform/them
|
|
|
96
96
|
import { platformNavigationSettingsMdxTemplate } from "../templates/mdx/platform/navigation-settings.mdx.js";
|
|
97
97
|
import { platformFontsSettingsMdxTemplate } from "../templates/mdx/platform/fonts-settings.mdx.js";
|
|
98
98
|
import { platformExternalLinksMdxTemplate } from "../templates/mdx/platform/external-links.mdx.js";
|
|
99
|
+
import { platformAnalyticsMdxTemplate } from "../templates/mdx/platform/analytics.mdx.js";
|
|
99
100
|
import { platformAiAssistantMdxTemplate } from "../templates/mdx/platform/ai-assistant.mdx.js";
|
|
100
101
|
import { platformCustomDomainsMdxTemplate } from "../templates/mdx/platform/custom-domains.mdx.js";
|
|
101
102
|
import { platformBuildAndDeployMdxTemplate } from "../templates/mdx/platform/build-and-deploy.mdx.js";
|
|
@@ -203,6 +204,7 @@ export const startingDocsStructure = {
|
|
|
203
204
|
"platform/navigation-settings.mdx": platformNavigationSettingsMdxTemplate,
|
|
204
205
|
"platform/fonts-settings.mdx": platformFontsSettingsMdxTemplate,
|
|
205
206
|
"platform/external-links.mdx": platformExternalLinksMdxTemplate,
|
|
207
|
+
"platform/analytics.mdx": platformAnalyticsMdxTemplate,
|
|
206
208
|
"platform/ai-assistant.mdx": platformAiAssistantMdxTemplate,
|
|
207
209
|
"platform/custom-domains.mdx": platformCustomDomainsMdxTemplate,
|
|
208
210
|
"platform/build-and-deploy.mdx": platformBuildAndDeployMdxTemplate,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const postHogProviderTemplate = "\"use client\";\n\nimport posthog from \"posthog-js\";\nimport { PostHogProvider as PHProvider } from \"posthog
|
|
1
|
+
export declare const postHogProviderTemplate = "\"use client\";\n\nimport posthog from \"posthog-js\";\nimport { PostHogProvider as PHProvider } from \"@posthog/react\";\nimport { Suspense, useEffect, useRef, useState } from \"react\";\nimport { usePathname, useSearchParams } from \"next/navigation\";\nimport rawAnalyticsConfig from \"@/analytics.json\";\n\ninterface AnalyticsConfig {\n provider?: string;\n posthog?: {\n key?: string;\n host?: string;\n };\n}\n\nconst analyticsConfig = rawAnalyticsConfig as AnalyticsConfig;\n\nconst posthogKey =\n analyticsConfig?.provider === \"posthog\" ? analyticsConfig.posthog?.key : null;\n\nfunction PostHogInit({ onReady }: { onReady: () => void }) {\n const initRef = useRef(false);\n\n useEffect(() => {\n if (initRef.current || !posthogKey) return;\n initRef.current = true;\n\n posthog.init(posthogKey, {\n api_host: \"/ingest\",\n ui_host: analyticsConfig.posthog?.host || \"https://us.posthog.com\",\n capture_pageview: false,\n capture_pageleave: true,\n loaded: onReady,\n });\n }, [onReady]);\n\n return null;\n}\n\nfunction PostHogPageviewTracker() {\n const pathname = usePathname();\n const searchParams = useSearchParams();\n\n useEffect(() => {\n if (pathname) {\n const url = searchParams?.size\n ? `${pathname}?${searchParams.toString()}`\n : pathname;\n posthog.capture(\"$pageview\", { $current_url: url });\n }\n }, [pathname, searchParams]);\n\n return null;\n}\n\nexport function PostHogProvider({ children }: { children: React.ReactNode }) {\n const [ready, setReady] = useState(false);\n\n if (!posthogKey) {\n return <>{children}</>;\n }\n\n return (\n <PHProvider client={posthog}>\n <PostHogInit onReady={() => setReady(true)} />\n <Suspense fallback={null}>{ready && <PostHogPageviewTracker />}</Suspense>\n {children}\n </PHProvider>\n );\n}\n";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const postHogProviderTemplate = `"use client";
|
|
2
2
|
|
|
3
3
|
import posthog from "posthog-js";
|
|
4
|
-
import { PostHogProvider as PHProvider } from "posthog
|
|
4
|
+
import { PostHogProvider as PHProvider } from "@posthog/react";
|
|
5
5
|
import { Suspense, useEffect, useRef, useState } from "react";
|
|
6
6
|
import { usePathname, useSearchParams } from "next/navigation";
|
|
7
7
|
import rawAnalyticsConfig from "@/analytics.json";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const buttonTemplate = "\"use client\";\nimport Link from \"next/link\";\nimport styled from \"styled-components\";\nimport {\n theme as localTheme,\n ButtonProps,\n buttonStyles,\n} from \"cherry-styled-components\";\nimport { Icon } from \"@/components/layout/Icon\";\n\ninterface LinkButtonProps extends ButtonProps {\n href?: string;\n target?: \"_blank\" | \"_self\" | \"_parent\" | \"_top\";\n variant?: \"primary\" | \"secondary\" | \"tertiary\";\n size?: \"default\" | \"big\";\n outline?: boolean;\n fullWidth?: boolean;\n icon?: string;\n iconPosition?: \"left\" | \"right\";\n theme?: typeof localTheme;\n}\n\nconst StyledLinkButton = styled(Link)<LinkButtonProps>`\n ${({ theme, $variant, $size, $outline, $fullWidth, disabled }) =>\n buttonStyles(theme, $variant, $size, $outline, $fullWidth, disabled)}\n\n & svg.lucide {\n margin: auto 0;\n min-width: min-content;\n color: inherit;\n }\n`;\n\nconst ButtonBase = styled.button<ButtonProps>`\n ${({ theme, $variant, $size, $outline, $fullWidth, disabled }) =>\n buttonStyles(theme, $variant, $size, $outline, $fullWidth, disabled)}\n\n & svg.lucide {\n margin: auto 0;\n min-width: min-content;\n color: inherit;\n }\n`;\n\nfunction Button({\n variant = \"primary\",\n size,\n outline,\n fullWidth,\n icon,\n iconPosition = \"left\",\n theme: _theme = localTheme,\n href,\n ...props\n}: LinkButtonProps) {\n return href ? (\n <div>\n <StyledLinkButton\n {...props}\n href={href}\n $variant={variant}\n $size={size}\n $outline={outline}\n $fullWidth={fullWidth}\n >\n {iconPosition === \"left\" && icon && <Icon name={icon} size={16} />}\n {props.children}\n {iconPosition === \"right\" && icon && <Icon name={icon} size={16} />}\n </StyledLinkButton>\n </div>\n ) : (\n <div>\n <ButtonBase\n {...props}\n $variant={variant}\n $size={size}\n $outline={outline}\n $fullWidth={fullWidth}\n >\n {iconPosition === \"left\" && icon && <Icon name={icon} size={16} />}\n {props.children}\n {iconPosition === \"right\" && icon && <Icon name={icon} size={16} />}\n </ButtonBase>\n </div>\n );\n}\n\nexport { Button };\n";
|
|
1
|
+
export declare const buttonTemplate = "\"use client\";\nimport Link from \"next/link\";\nimport styled from \"styled-components\";\nimport {\n theme as localTheme,\n ButtonProps,\n buttonStyles,\n} from \"cherry-styled-components\";\nimport { Icon } from \"@/components/layout/Icon\";\n\ninterface LinkButtonProps extends ButtonProps {\n href?: string;\n target?: \"_blank\" | \"_self\" | \"_parent\" | \"_top\";\n variant?: \"primary\" | \"secondary\" | \"tertiary\";\n size?: \"default\" | \"big\";\n outline?: boolean;\n fullWidth?: boolean;\n icon?: string;\n iconPosition?: \"left\" | \"right\";\n theme?: typeof localTheme;\n}\n\nconst StyledLinkButton = styled(Link)<LinkButtonProps>`\n ${({ theme, $variant, $size, $outline, $fullWidth, disabled }) =>\n buttonStyles(theme, $variant, $size, $outline, $fullWidth, disabled)}\n\n & p {\n color: inherit;\n }\n\n & svg.lucide {\n margin: auto 0;\n min-width: min-content;\n color: inherit;\n }\n`;\n\nconst ButtonBase = styled.button<ButtonProps>`\n ${({ theme, $variant, $size, $outline, $fullWidth, disabled }) =>\n buttonStyles(theme, $variant, $size, $outline, $fullWidth, disabled)}\n\n & p {\n color: inherit;\n }\n\n & svg.lucide {\n margin: auto 0;\n min-width: min-content;\n color: inherit;\n }\n`;\n\nfunction Button({\n variant = \"primary\",\n size,\n outline,\n fullWidth,\n icon,\n iconPosition = \"left\",\n theme: _theme = localTheme,\n href,\n ...props\n}: LinkButtonProps) {\n return href ? (\n <div>\n <StyledLinkButton\n {...props}\n href={href}\n $variant={variant}\n $size={size}\n $outline={outline}\n $fullWidth={fullWidth}\n >\n {iconPosition === \"left\" && icon && <Icon name={icon} size={16} />}\n {props.children}\n {iconPosition === \"right\" && icon && <Icon name={icon} size={16} />}\n </StyledLinkButton>\n </div>\n ) : (\n <div>\n <ButtonBase\n {...props}\n $variant={variant}\n $size={size}\n $outline={outline}\n $fullWidth={fullWidth}\n >\n {iconPosition === \"left\" && icon && <Icon name={icon} size={16} />}\n {props.children}\n {iconPosition === \"right\" && icon && <Icon name={icon} size={16} />}\n </ButtonBase>\n </div>\n );\n}\n\nexport { Button };\n";
|
|
@@ -24,6 +24,10 @@ const StyledLinkButton = styled(Link)<LinkButtonProps>\`
|
|
|
24
24
|
\${({ theme, $variant, $size, $outline, $fullWidth, disabled }) =>
|
|
25
25
|
buttonStyles(theme, $variant, $size, $outline, $fullWidth, disabled)}
|
|
26
26
|
|
|
27
|
+
& p {
|
|
28
|
+
color: inherit;
|
|
29
|
+
}
|
|
30
|
+
|
|
27
31
|
& svg.lucide {
|
|
28
32
|
margin: auto 0;
|
|
29
33
|
min-width: min-content;
|
|
@@ -35,6 +39,10 @@ const ButtonBase = styled.button<ButtonProps>\`
|
|
|
35
39
|
\${({ theme, $variant, $size, $outline, $fullWidth, disabled }) =>
|
|
36
40
|
buttonStyles(theme, $variant, $size, $outline, $fullWidth, disabled)}
|
|
37
41
|
|
|
42
|
+
& p {
|
|
43
|
+
color: inherit;
|
|
44
|
+
}
|
|
45
|
+
|
|
38
46
|
& svg.lucide {
|
|
39
47
|
margin: auto 0;
|
|
40
48
|
min-width: min-content;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const globalStylesTemplate = "\"use client\";\nimport { createGlobalStyle } from \"styled-components\";\n\nconst GlobalStyles = createGlobalStyle`\nhtml,\nbody {\n margin: 0;\n padding: 0;\n min-height: 100%;\n background-color: ${({ theme }) => theme.colors.light};\n scroll-padding-top: 80px;\n}\n\nhtml:has(:target) {\n scroll-behavior: smooth;\n}\n\nbody {\n font-family: \"Inter\", sans-serif;\n -moz-osx-font-smoothing: grayscale;\n -webkit-text-size-adjust: 100%;\n -webkit-font-smoothing: antialiased;\n}\n\n:root {\n interpolate-size: allow-keywords;\n}\n\n* {\n box-sizing: border-box;\n min-width: 0;\n}\n\nhr {\n border: none;\n border-bottom: solid 1px ${({ theme }) => theme.colors.grayLight};\n margin: 10px 0;\n}\n\npre,\ncode,\nkbd,\nsamp {\n font-family: monospace, monospace;\n}\n\npre,\ncode,\nkbd,\nsamp,\nblockquote,\np,\na,\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\nul li,\nol li {\n margin: 0;\n padding: 0;\n color: ${({ theme }) => theme.colors.dark};\n}\n\na {\n color: ${({ theme }) => (theme.isDark ? theme.colors.dark : theme.colors.primary)};\n}\n\nol,\nul {\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\nfigure {\n margin: 0;\n}\n\nfieldset {\n appearance: none;\n border: none;\n}\n\nbutton,\ninput,\na,\nimg,\nsvg,\nsvg * {\n transition: all 0.3s ease;\n}\n\nstrong,\nb {\n font-weight: 700;\n}\n\n.full-width {\n width: 100%;\n}`;\n\nexport { GlobalStyles };\n";
|
|
1
|
+
export declare const globalStylesTemplate = "\"use client\";\nimport { createGlobalStyle } from \"styled-components\";\n\nconst GlobalStyles = createGlobalStyle`\nhtml,\nbody {\n margin: 0;\n padding: 0;\n min-height: 100%;\n background-color: ${({ theme }) => theme.colors.light};\n scroll-padding-top: 80px;\n}\n\nhtml:has(:target) {\n scroll-behavior: smooth;\n}\n\nbody {\n font-family: \"Inter\", sans-serif;\n -moz-osx-font-smoothing: grayscale;\n -webkit-text-size-adjust: 100%;\n -webkit-font-smoothing: antialiased;\n}\n\n:root {\n interpolate-size: allow-keywords;\n}\n\n* {\n box-sizing: border-box;\n min-width: 0;\n}\n\nhr {\n border: none;\n border-bottom: solid 1px ${({ theme }) => theme.colors.grayLight};\n margin: 10px 0;\n}\n\npre,\ncode,\nkbd,\nsamp {\n font-family: monospace, monospace;\n}\n\npre,\ncode,\nkbd,\nsamp,\nblockquote,\np,\na,\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\nul li,\nol li {\n margin: 0;\n padding: 0;\n color: ${({ theme }) => theme.colors.dark};\n}\n\na {\n color: ${({ theme }) => (theme.isDark ? theme.colors.dark : theme.colors.primary)};\n}\n\nol,\nul {\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\nfigure {\n margin: 0;\n}\n\nfieldset {\n appearance: none;\n border: none;\n}\n\nbutton,\ninput,\na,\nimg,\nsvg,\nsvg * {\n transition: all 0.3s ease;\n}\n\nstrong,\nb {\n font-weight: 700;\n}\n\n.full-width {\n width: 100%;\n}\n\n.light-only {\n ${({ theme }) => theme.isDark && \"display: none !important;\"}\n}\n\n.dark-only {\n ${({ theme }) => !theme.isDark && \"display: none !important;\"}\n}`;\n\nexport { GlobalStyles };\n";
|
|
@@ -100,6 +100,14 @@ b {
|
|
|
100
100
|
|
|
101
101
|
.full-width {
|
|
102
102
|
width: 100%;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.light-only {
|
|
106
|
+
\${({ theme }) => theme.isDark && "display: none !important;"}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.dark-only {
|
|
110
|
+
\${({ theme }) => !theme.isDark && "display: none !important;"}
|
|
103
111
|
}\`;
|
|
104
112
|
|
|
105
113
|
export { GlobalStyles };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const imageAndEmbedsMdxTemplate = "---\ntitle: \"Images and embeds\"\ndescription: \"Enrich your documentation with visuals, videos, and interactive embeds.\"\ndate: \"2026-02-19\"\ncategory: \"Components\"\ncategoryOrder: 1\norder: 8\n---\n# Images and embeds\nEnrich your documentation with visuals, videos, and interactive embeds.\n\nDisplay images, embed video content, or add interactive frames via iframes to supplement your docs.\n\n\n\n## Images\nImages enhance documentation with context, illustration, or decorative visual cues.\n\n### Basic Image Syntax\nInclude an image in Markdown using the syntax below:\n\n```md\n\n```\n\n<Callout type=\"note\">\n Use clear, descriptive alt text for accessibility and better SEO. Alt text should describe the image\u2019s appearance or content.\n</Callout>\n\n### HTML image embeds\nEmbed images in your Markdown content using HTML syntax.\n\n```md\n<img src=\"https://docs.doccupine.com/demo.png\" alt=\"Alt text\">\n```\n\n## Videos\nVideos add a dynamic element to your documentation, engaging your audience and providing a more immersive experience.\n\n### YouTube Embed\nTo embed a YouTube video, use the following syntax:\n\n```html\n<iframe\n className=\"aspect-video\"\n src=\"https://www.youtube.com/embed/ResP_eVPYQo\"\n title=\"YouTube video player\"\n frameBorder=\"0\"\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n allowFullScreen\n></iframe>\n```\n\n<iframe\n className=\"aspect-video\"\n src=\"https://www.youtube.com/embed/ResP_eVPYQo\"\n title=\"YouTube video player\"\n frameBorder=\"0\"\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n allowFullScreen\n></iframe>\n\n### Self-hosted videos\nServe up your own video content using the `<video>` tag:\n\n```html\n<video\n controls\n className=\"aspect-video\"\n src=\"https://samplelib.com/lib/preview/mp4/sample-20s.mp4\"\n></video>\n```\n\n<video\n controls\n className=\"aspect-video\"\n src=\"https://samplelib.com/lib/preview/mp4/sample-20s.mp4\"\n></video>\n\n\n#### Autoplay and Looping\nFor demonstration videos that loop or start automatically, add attributes as shown:\n\n```html\n<video\n controls\n className=\"aspect-video\"\n src=\"https://samplelib.com/lib/preview/mp4/sample-20s.mp4\"\n autoPlay\n muted\n loop\n playsInline\n></video>\n```\n";
|
|
1
|
+
export declare const imageAndEmbedsMdxTemplate = "---\ntitle: \"Images and embeds\"\ndescription: \"Enrich your documentation with visuals, videos, and interactive embeds.\"\ndate: \"2026-02-19\"\ncategory: \"Components\"\ncategoryOrder: 1\norder: 8\n---\n# Images and embeds\nEnrich your documentation with visuals, videos, and interactive embeds.\n\nDisplay images, embed video content, or add interactive frames via iframes to supplement your docs.\n\n\n\n## Images\nImages enhance documentation with context, illustration, or decorative visual cues.\n\n### Basic Image Syntax\nInclude an image in Markdown using the syntax below:\n\n```md\n\n```\n\n<Callout type=\"note\">\n Use clear, descriptive alt text for accessibility and better SEO. Alt text should describe the image\u2019s appearance or content.\n</Callout>\n\n### HTML image embeds\nEmbed images in your Markdown content using HTML syntax.\n\n```md\n<img src=\"https://docs.doccupine.com/demo.png\" alt=\"Alt text\">\n```\n\n### Theme-aware images\nShow different images depending on whether the user is in light or dark mode. Add the `light-only` or `dark-only` className to display an image exclusively in that theme.\n\n```md\n<img className=\"light-only\" src=\"/images/diagram-light.png\" alt=\"Diagram\">\n<img className=\"dark-only\" src=\"/images/diagram-dark.png\" alt=\"Diagram\">\n```\n\n<img className=\"light-only\" src=\"https://docs.doccupine.com/demo.png\" alt=\"This image is only visible in light mode\">\n<img className=\"dark-only\" src=\"https://docs.doccupine.com/demo.png\" alt=\"This image is only visible in dark mode\" style={{ filter: \"invert(1)\" }}>\n\n<Callout type=\"note\">\n The `light-only` and `dark-only` classes work on any element, not just images. You can use them on videos, iframes, or wrapper divs too.\n</Callout>\n\n## Videos\nVideos add a dynamic element to your documentation, engaging your audience and providing a more immersive experience.\n\n### YouTube Embed\nTo embed a YouTube video, use the following syntax:\n\n```html\n<iframe\n className=\"aspect-video\"\n src=\"https://www.youtube.com/embed/ResP_eVPYQo\"\n title=\"YouTube video player\"\n frameBorder=\"0\"\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n allowFullScreen\n></iframe>\n```\n\n<iframe\n className=\"aspect-video\"\n src=\"https://www.youtube.com/embed/ResP_eVPYQo\"\n title=\"YouTube video player\"\n frameBorder=\"0\"\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n allowFullScreen\n></iframe>\n\n### Self-hosted videos\nServe up your own video content using the `<video>` tag:\n\n```html\n<video\n controls\n className=\"aspect-video\"\n src=\"https://samplelib.com/lib/preview/mp4/sample-20s.mp4\"\n></video>\n```\n\n<video\n controls\n className=\"aspect-video\"\n src=\"https://samplelib.com/lib/preview/mp4/sample-20s.mp4\"\n></video>\n\n\n#### Autoplay and Looping\nFor demonstration videos that loop or start automatically, add attributes as shown:\n\n```html\n<video\n controls\n className=\"aspect-video\"\n src=\"https://samplelib.com/lib/preview/mp4/sample-20s.mp4\"\n autoPlay\n muted\n loop\n playsInline\n></video>\n```\n";
|
|
@@ -34,6 +34,21 @@ Embed images in your Markdown content using HTML syntax.
|
|
|
34
34
|
<img src="https://docs.doccupine.com/demo.png" alt="Alt text">
|
|
35
35
|
\`\`\`
|
|
36
36
|
|
|
37
|
+
### Theme-aware images
|
|
38
|
+
Show different images depending on whether the user is in light or dark mode. Add the \`light-only\` or \`dark-only\` className to display an image exclusively in that theme.
|
|
39
|
+
|
|
40
|
+
\`\`\`md
|
|
41
|
+
<img className="light-only" src="/images/diagram-light.png" alt="Diagram">
|
|
42
|
+
<img className="dark-only" src="/images/diagram-dark.png" alt="Diagram">
|
|
43
|
+
\`\`\`
|
|
44
|
+
|
|
45
|
+
<img className="light-only" src="https://docs.doccupine.com/demo.png" alt="This image is only visible in light mode">
|
|
46
|
+
<img className="dark-only" src="https://docs.doccupine.com/demo.png" alt="This image is only visible in dark mode" style={{ filter: "invert(1)" }}>
|
|
47
|
+
|
|
48
|
+
<Callout type="note">
|
|
49
|
+
The \`light-only\` and \`dark-only\` classes work on any element, not just images. You can use them on videos, iframes, or wrapper divs too.
|
|
50
|
+
</Callout>
|
|
51
|
+
|
|
37
52
|
## Videos
|
|
38
53
|
Videos add a dynamic element to your documentation, engaging your audience and providing a more immersive experience.
|
|
39
54
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const platformAnalyticsMdxTemplate = "---\ntitle: \"Analytics\"\ndescription: \"Enable PostHog analytics to track page views on your documentation site.\"\ndate: \"2026-02-24\"\ncategory: \"Configuration\"\ncategoryOrder: 2\norder: 3\nsection: \"Platform\"\n---\n# Analytics\nThe Analytics settings page lets you add PostHog analytics to your documentation site. Page views are tracked client-side and proxied through your own domain for privacy - no data is sent directly to PostHog.\n\n## Enabling analytics\nUse the **Enable Analytics** toggle to turn tracking on or off. When disabled, no tracking code is added to your site.\n\n## Configuration\n\n### PostHog Project API Key\nYour project API key from PostHog (starts with `phc_`). This is a public identifier and is safe to commit to your repository.\n\nTo find your key:\n1. Log in to [PostHog](https://posthog.com).\n2. Open your project settings.\n3. Copy the **Project API Key**.\n\n### Region\nSelect the PostHog cloud region that matches your project:\n\n- **US Cloud** - `us.i.posthog.com`\n- **EU Cloud** - `eu.i.posthog.com`\n\n## How it works\nAnalytics settings are stored in `analytics.json` at the root of your repository. Here's an example:\n\n```json\n{\n \"provider\": \"posthog\",\n \"posthog\": {\n \"key\": \"phc_your_project_api_key\",\n \"host\": \"https://us.i.posthog.com\"\n }\n}\n```\n\nWhen enabled, Doccupine routes all analytics traffic through your documentation domain using Next.js rewrites. Instead of sending data directly to PostHog (which ad blockers may intercept), requests go through `/ingest` on your own domain and are proxied to PostHog.\n\n<Callout type=\"note\">\n Changes to analytics settings are staged as pending changes. Click **Publish** to commit them to your repository and trigger a deploy.\n</Callout>\n\nSee the [Analytics](/analytics) page for the full configuration reference and additional details on the privacy proxy.";
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export const platformAnalyticsMdxTemplate = `---
|
|
2
|
+
title: "Analytics"
|
|
3
|
+
description: "Enable PostHog analytics to track page views on your documentation site."
|
|
4
|
+
date: "2026-02-24"
|
|
5
|
+
category: "Configuration"
|
|
6
|
+
categoryOrder: 2
|
|
7
|
+
order: 3
|
|
8
|
+
section: "Platform"
|
|
9
|
+
---
|
|
10
|
+
# Analytics
|
|
11
|
+
The Analytics settings page lets you add PostHog analytics to your documentation site. Page views are tracked client-side and proxied through your own domain for privacy - no data is sent directly to PostHog.
|
|
12
|
+
|
|
13
|
+
## Enabling analytics
|
|
14
|
+
Use the **Enable Analytics** toggle to turn tracking on or off. When disabled, no tracking code is added to your site.
|
|
15
|
+
|
|
16
|
+
## Configuration
|
|
17
|
+
|
|
18
|
+
### PostHog Project API Key
|
|
19
|
+
Your project API key from PostHog (starts with \`phc_\`). This is a public identifier and is safe to commit to your repository.
|
|
20
|
+
|
|
21
|
+
To find your key:
|
|
22
|
+
1. Log in to [PostHog](https://posthog.com).
|
|
23
|
+
2. Open your project settings.
|
|
24
|
+
3. Copy the **Project API Key**.
|
|
25
|
+
|
|
26
|
+
### Region
|
|
27
|
+
Select the PostHog cloud region that matches your project:
|
|
28
|
+
|
|
29
|
+
- **US Cloud** - \`us.i.posthog.com\`
|
|
30
|
+
- **EU Cloud** - \`eu.i.posthog.com\`
|
|
31
|
+
|
|
32
|
+
## How it works
|
|
33
|
+
Analytics settings are stored in \`analytics.json\` at the root of your repository. Here's an example:
|
|
34
|
+
|
|
35
|
+
\`\`\`json
|
|
36
|
+
{
|
|
37
|
+
"provider": "posthog",
|
|
38
|
+
"posthog": {
|
|
39
|
+
"key": "phc_your_project_api_key",
|
|
40
|
+
"host": "https://us.i.posthog.com"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
\`\`\`
|
|
44
|
+
|
|
45
|
+
When enabled, Doccupine routes all analytics traffic through your documentation domain using Next.js rewrites. Instead of sending data directly to PostHog (which ad blockers may intercept), requests go through \`/ingest\` on your own domain and are proxied to PostHog.
|
|
46
|
+
|
|
47
|
+
<Callout type="note">
|
|
48
|
+
Changes to analytics settings are staged as pending changes. Click **Publish** to commit them to your repository and trigger a deploy.
|
|
49
|
+
</Callout>
|
|
50
|
+
|
|
51
|
+
See the [Analytics](/analytics) page for the full configuration reference and additional details on the privacy proxy.`;
|
|
@@ -10,20 +10,21 @@ export const packageJsonTemplate = JSON.stringify({
|
|
|
10
10
|
format: "prettier --write .",
|
|
11
11
|
},
|
|
12
12
|
dependencies: {
|
|
13
|
-
"@langchain/anthropic": "^1.3.
|
|
14
|
-
"@langchain/core": "^1.1.
|
|
15
|
-
"@langchain/google-genai": "^2.1.
|
|
16
|
-
"@langchain/openai": "^1.2.
|
|
13
|
+
"@langchain/anthropic": "^1.3.23",
|
|
14
|
+
"@langchain/core": "^1.1.32",
|
|
15
|
+
"@langchain/google-genai": "^2.1.25",
|
|
16
|
+
"@langchain/openai": "^1.2.13",
|
|
17
17
|
"@mdx-js/react": "^3.1.1",
|
|
18
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
18
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
19
|
+
"@posthog/react": "^1.8.2",
|
|
19
20
|
"cherry-styled-components": "^0.1.13",
|
|
20
|
-
langchain: "^1.2.
|
|
21
|
-
"lucide-react": "^0.
|
|
21
|
+
langchain: "^1.2.31",
|
|
22
|
+
"lucide-react": "^0.577.0",
|
|
22
23
|
next: "16.1.6",
|
|
23
24
|
"next-mdx-remote": "^6.0.0",
|
|
24
25
|
polished: "^4.3.1",
|
|
25
|
-
"posthog-js": "^1.
|
|
26
|
-
"posthog-node": "^5.
|
|
26
|
+
"posthog-js": "^1.360.1",
|
|
27
|
+
"posthog-node": "^5.28.1",
|
|
27
28
|
react: "19.2.4",
|
|
28
29
|
"react-dom": "19.2.4",
|
|
29
30
|
"rehype-highlight": "^7.0.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "doccupine",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.72",
|
|
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": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"chalk": "^5.6.2",
|
|
38
38
|
"chokidar": "^5.0.0",
|
|
39
39
|
"commander": "^14.0.3",
|
|
40
|
-
"fs-extra": "^11.3.
|
|
40
|
+
"fs-extra": "^11.3.4",
|
|
41
41
|
"gray-matter": "^4.0.3",
|
|
42
42
|
"next": "^16.1.6",
|
|
43
43
|
"prompts": "^2.4.2",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/chokidar": "^2.1.7",
|
|
49
49
|
"@types/fs-extra": "^11.0.4",
|
|
50
|
-
"@types/node": "^25.
|
|
50
|
+
"@types/node": "^25.4.0",
|
|
51
51
|
"@types/prompts": "^2.4.9",
|
|
52
52
|
"prettier": "^3.8.1",
|
|
53
53
|
"typescript": "^5.9.3",
|