doccupine 0.0.49 → 0.0.51
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/LICENSE +2 -2
- package/README.md +88 -22
- package/dist/index.d.ts +2 -1
- package/dist/index.js +173 -404
- package/dist/index.test.d.ts +1 -0
- package/dist/index.test.js +48 -0
- package/dist/templates/components/Docs.d.ts +1 -1
- package/dist/templates/components/Docs.js +1 -0
- package/dist/templates/components/layout/Button.d.ts +1 -1
- package/dist/templates/components/layout/Button.js +6 -20
- package/dist/templates/components/layout/Icon.d.ts +1 -1
- package/dist/templates/components/layout/Icon.js +4 -2
- package/dist/templates/components/layout/Steps.d.ts +1 -0
- package/dist/templates/components/layout/Steps.js +114 -0
- package/dist/templates/eslint.config.d.ts +1 -1
- package/dist/templates/eslint.config.js +1 -1
- package/dist/templates/mdx/deployment.mdx.d.ts +1 -1
- package/dist/templates/mdx/deployment.mdx.js +16 -21
- package/dist/templates/package.js +2 -2
- package/package.json +8 -5
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { generateSlug, escapeTemplateContent } from "./index.js";
|
|
3
|
+
describe("generateSlug", () => {
|
|
4
|
+
it("returns empty string for index.mdx", () => {
|
|
5
|
+
expect(generateSlug("index.mdx")).toBe("");
|
|
6
|
+
expect(generateSlug("./index.mdx")).toBe("");
|
|
7
|
+
});
|
|
8
|
+
it("strips .mdx extension", () => {
|
|
9
|
+
expect(generateSlug("getting-started.mdx")).toBe("getting-started");
|
|
10
|
+
});
|
|
11
|
+
it("lowercases the slug", () => {
|
|
12
|
+
expect(generateSlug("MyPage.mdx")).toBe("mypage");
|
|
13
|
+
});
|
|
14
|
+
it("replaces special characters with hyphens", () => {
|
|
15
|
+
expect(generateSlug("hello world.mdx")).toBe("hello-world");
|
|
16
|
+
});
|
|
17
|
+
it("preserves forward slashes for nested paths", () => {
|
|
18
|
+
expect(generateSlug("guides/setup.mdx")).toBe("guides/setup");
|
|
19
|
+
});
|
|
20
|
+
it("converts backslashes to forward slashes", () => {
|
|
21
|
+
expect(generateSlug("guides\\setup.mdx")).toBe("guides/setup");
|
|
22
|
+
});
|
|
23
|
+
it("preserves underscores and hyphens", () => {
|
|
24
|
+
expect(generateSlug("my_page-name.mdx")).toBe("my_page-name");
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
describe("escapeTemplateContent", () => {
|
|
28
|
+
it("escapes backticks", () => {
|
|
29
|
+
expect(escapeTemplateContent("hello `world`")).toBe("hello \\`world\\`");
|
|
30
|
+
});
|
|
31
|
+
it("escapes template literal expressions", () => {
|
|
32
|
+
expect(escapeTemplateContent("${variable}")).toBe("\\${variable}");
|
|
33
|
+
});
|
|
34
|
+
it("escapes backslashes", () => {
|
|
35
|
+
expect(escapeTemplateContent("path\\to\\file")).toBe("path\\\\to\\\\file");
|
|
36
|
+
});
|
|
37
|
+
it("escapes all dangerous characters together", () => {
|
|
38
|
+
const input = "Use `${process.env.SECRET}` here";
|
|
39
|
+
const result = escapeTemplateContent(input);
|
|
40
|
+
expect(result).toBe("Use \\`\\${process.env.SECRET}\\` here");
|
|
41
|
+
});
|
|
42
|
+
it("handles content with no special characters", () => {
|
|
43
|
+
expect(escapeTemplateContent("plain text")).toBe("plain text");
|
|
44
|
+
});
|
|
45
|
+
it("handles empty string", () => {
|
|
46
|
+
expect(escapeTemplateContent("")).toBe("");
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const docsTemplate = "import { Flex } from \"cherry-styled-components/src/lib\";\nimport {\n DocsContainer,\n StyledMarkdownContainer,\n} from \"@/components/layout/DocsComponents\";\nimport { MDXRemote } from \"next-mdx-remote/rsc\";\nimport remarkGfm from \"remark-gfm\";\nimport { useMDXComponents } from \"@/components/MDXComponents\";\nimport { DocsSideBar } from \"@/components/DocsSideBar\";\nimport { ActionBar } from \"@/components/layout/ActionBar\";\n\ninterface DocsProps {\n content: string;\n}\n\ninterface Heading {\n id: string;\n text: string;\n level: number;\n}\n\nfunction generateId(text: string): string {\n return text\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, \"\")\n .replace(/\\s+/g, \"-\")\n .trim();\n}\n\nfunction extractHeadings(content: string): Heading[] {\n const contentWithoutCodeBlocks = content.replace(/```[\\s\\S]*?```/g, \"\");\n const headingRegex = /^(#{1,6})\\s+(.+)$/gm;\n const headings: Heading[] = [];\n let match;\n\n while ((match = headingRegex.exec(contentWithoutCodeBlocks)) !== null) {\n const level = match[1].length;\n const text = match[2].trim();\n const id = generateId(text);\n headings.push({ id, text, level });\n }\n\n return headings;\n}\n\nfunction Docs({ content }: DocsProps) {\n const headings = extractHeadings(content);\n const components = useMDXComponents({});\n\n return (\n <>\n <DocsContainer>\n <ActionBar content={content}>\n <Flex $gap={20}>\n <StyledMarkdownContainer>\n {content && (\n <MDXRemote\n source={content}\n options={{\n mdxOptions: {\n remarkPlugins: [remarkGfm],\n },\n }}\n components={components}\n />\n )}\n </StyledMarkdownContainer>\n </Flex>\n </ActionBar>\n </DocsContainer>\n <DocsSideBar headings={headings} />\n </>\n );\n}\n\nexport { Docs };\n";
|
|
1
|
+
export declare const docsTemplate = "import { Flex } from \"cherry-styled-components/src/lib\";\nimport {\n DocsContainer,\n StyledMarkdownContainer,\n} from \"@/components/layout/DocsComponents\";\nimport { MDXRemote } from \"next-mdx-remote/rsc\";\nimport remarkGfm from \"remark-gfm\";\nimport { useMDXComponents } from \"@/components/MDXComponents\";\nimport { DocsSideBar } from \"@/components/DocsSideBar\";\nimport { ActionBar } from \"@/components/layout/ActionBar\";\n\ninterface DocsProps {\n content: string;\n}\n\ninterface Heading {\n id: string;\n text: string;\n level: number;\n}\n\nfunction generateId(text: string): string {\n return text\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, \"\")\n .replace(/\\s+/g, \"-\")\n .trim();\n}\n\nfunction extractHeadings(content: string): Heading[] {\n const contentWithoutCodeBlocks = content.replace(/```[\\s\\S]*?```/g, \"\");\n const headingRegex = /^(#{1,6})\\s+(.+)$/gm;\n const headings: Heading[] = [];\n let match;\n\n while ((match = headingRegex.exec(contentWithoutCodeBlocks)) !== null) {\n const level = match[1].length;\n const text = match[2].trim();\n const id = generateId(text);\n headings.push({ id, text, level });\n }\n\n return headings;\n}\n\nfunction Docs({ content }: DocsProps) {\n const headings = extractHeadings(content);\n const components = useMDXComponents({});\n\n return (\n <>\n <DocsContainer>\n <ActionBar content={content}>\n <Flex $gap={20}>\n <StyledMarkdownContainer>\n {content && (\n <MDXRemote\n source={content}\n options={{\n blockJS: false,\n mdxOptions: {\n remarkPlugins: [remarkGfm],\n },\n }}\n components={components}\n />\n )}\n </StyledMarkdownContainer>\n </Flex>\n </ActionBar>\n </DocsContainer>\n <DocsSideBar headings={headings} />\n </>\n );\n}\n\nexport { Docs };\n";
|
|
@@ -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/src/lib\";\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}\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:
|
|
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/src/lib\";\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}\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 = 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";
|
|
@@ -26,10 +26,7 @@ const StyledLinkButton = styled(Link)<LinkButtonProps>\`
|
|
|
26
26
|
& svg.lucide {
|
|
27
27
|
margin: auto 0;
|
|
28
28
|
min-width: min-content;
|
|
29
|
-
color:
|
|
30
|
-
$outline ? "inherit" : theme.colors.light};
|
|
31
|
-
stroke: \${({ theme, $outline }) =>
|
|
32
|
-
$outline ? "currentColor" : theme.colors.light};
|
|
29
|
+
color: inherit;
|
|
33
30
|
}
|
|
34
31
|
\`;
|
|
35
32
|
|
|
@@ -40,10 +37,7 @@ const ButtonBase = styled.button<ButtonProps>\`
|
|
|
40
37
|
& svg.lucide {
|
|
41
38
|
margin: auto 0;
|
|
42
39
|
min-width: min-content;
|
|
43
|
-
color:
|
|
44
|
-
$outline ? "inherit" : theme.colors.light};
|
|
45
|
-
stroke: \${({ theme, $outline }) =>
|
|
46
|
-
$outline ? "currentColor" : theme.colors.light};
|
|
40
|
+
color: inherit;
|
|
47
41
|
}
|
|
48
42
|
\`;
|
|
49
43
|
|
|
@@ -68,13 +62,9 @@ function Button({
|
|
|
68
62
|
$outline={outline}
|
|
69
63
|
$fullWidth={fullWidth}
|
|
70
64
|
>
|
|
71
|
-
{iconPosition === "left" && icon &&
|
|
72
|
-
<Icon name={icon} color={theme.colors.light} size={16} />
|
|
73
|
-
)}
|
|
65
|
+
{iconPosition === "left" && icon && <Icon name={icon} size={16} />}
|
|
74
66
|
{props.children}
|
|
75
|
-
{iconPosition === "right" && icon &&
|
|
76
|
-
<Icon name={icon} color={theme.colors.light} size={16} />
|
|
77
|
-
)}
|
|
67
|
+
{iconPosition === "right" && icon && <Icon name={icon} size={16} />}
|
|
78
68
|
</StyledLinkButton>
|
|
79
69
|
</div>
|
|
80
70
|
) : (
|
|
@@ -86,13 +76,9 @@ function Button({
|
|
|
86
76
|
$outline={outline}
|
|
87
77
|
$fullWidth={fullWidth}
|
|
88
78
|
>
|
|
89
|
-
{iconPosition === "left" && icon &&
|
|
90
|
-
<Icon name={icon} color={theme.colors.light} size={16} />
|
|
91
|
-
)}
|
|
79
|
+
{iconPosition === "left" && icon && <Icon name={icon} size={16} />}
|
|
92
80
|
{props.children}
|
|
93
|
-
{iconPosition === "right" && icon &&
|
|
94
|
-
<Icon name={icon} color={theme.colors.light} size={16} />
|
|
95
|
-
)}
|
|
81
|
+
{iconPosition === "right" && icon && <Icon name={icon} size={16} />}
|
|
96
82
|
</ButtonBase>
|
|
97
83
|
</div>
|
|
98
84
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const iconTemplate = "import { icons } from \"lucide-react\";\n\nexport type IconProps = keyof typeof icons;\n\ninterface Props {\n name: string | IconProps;\n color?: string;\n size?: number;\n className?: string;\n}\n\nfunction transformIconName(name: string): string {\n return name\n .split(\"-\")\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(\"\");\n}\n\nconst Icon = ({ name, color, size, className }: Props) => {\n const IconName = transformIconName(name as string);\n const LucideIcon = icons[IconName as keyof typeof icons];\n if (!LucideIcon) return null;\n\n return <LucideIcon color={color} size={
|
|
1
|
+
export declare const iconTemplate = "import { icons } from \"lucide-react\";\n\nexport type IconProps = keyof typeof icons;\n\ninterface Props {\n name: string | IconProps;\n color?: string;\n size?: string | number;\n className?: string;\n}\n\nfunction transformIconName(name: string): string {\n return name\n .split(\"-\")\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(\"\");\n}\n\nconst Icon = ({ name, color, size, className }: Props) => {\n const IconName = transformIconName(name as string);\n const LucideIcon = icons[IconName as keyof typeof icons];\n if (!LucideIcon) return null;\n\n const numericSize = size != null ? Number(size) : undefined;\n\n return <LucideIcon color={color} size={numericSize} className={className} />;\n};\n\nexport { Icon };\n";
|
|
@@ -5,7 +5,7 @@ export type IconProps = keyof typeof icons;
|
|
|
5
5
|
interface Props {
|
|
6
6
|
name: string | IconProps;
|
|
7
7
|
color?: string;
|
|
8
|
-
size?: number;
|
|
8
|
+
size?: string | number;
|
|
9
9
|
className?: string;
|
|
10
10
|
}
|
|
11
11
|
|
|
@@ -21,7 +21,9 @@ const Icon = ({ name, color, size, className }: Props) => {
|
|
|
21
21
|
const LucideIcon = icons[IconName as keyof typeof icons];
|
|
22
22
|
if (!LucideIcon) return null;
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
const numericSize = size != null ? Number(size) : undefined;
|
|
25
|
+
|
|
26
|
+
return <LucideIcon color={color} size={numericSize} className={className} />;
|
|
25
27
|
};
|
|
26
28
|
|
|
27
29
|
export { Icon };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const stepsTemplate = "\"use client\";\nimport React from \"react\";\nimport styled, { useTheme } from \"styled-components\";\nimport { styledText, Theme } from \"cherry-styled-components/src/lib\";\nimport { rgba } from \"polished\";\nimport { Icon, IconProps } from \"@/components/layout/Icon\";\n\nconst StyledStepsContainer = styled.div<{ theme: Theme }>`\n position: relative;\n width: 100%;\n`;\n\nconst StyledStep = styled.div<{ theme: Theme }>`\n background: ${({ theme }) => theme.colors.light};\n border-radius: ${({ theme }) => theme.spacing.radius.lg};\n padding: 0 0 20px 52px;\n margin: 0;\n position: relative;\n ${({ theme }) => styledText(theme)}\n color: ${({ theme }) => theme.colors.grayDark};\n\n &::after {\n content: \"\";\n position: absolute;\n left: 16px;\n top: 0;\n width: 1px;\n height: 100%;\n background: ${({ theme }) => theme.colors.primary};\n background: linear-gradient(\n 180deg,\n ${({ theme }) => theme.colors.primary},\n ${({ theme }) => rgba(theme.colors.primary, 0)}\n );\n border-radius: 4px;\n }\n`;\n\nconst StepNumber = styled.div<{ theme: Theme }>`\n width: 32px;\n height: 32px;\n border-radius: 50%;\n background: ${({ theme }) => theme.colors.primary};\n color: ${({ theme }) => theme.colors.light};\n display: flex;\n align-items: center;\n justify-content: center;\n font-weight: 700;\n margin-bottom: 12px;\n ${({ theme }) => styledText(theme)};\n position: absolute;\n left: 0;\n top: 0;\n z-index: 1;\n`;\n\nconst StyledStepTitle = styled.h3<{ theme: Theme }>`\n margin: 0 0 10px 0;\n padding: 2px 0 0 0;\n color: ${({ theme }) => theme.colors.dark};\n ${({ theme }) => styledText(theme)};\n`;\n\nconst StepContent = styled.div<{ theme: Theme }>`\n color: ${({ theme }) => theme.colors.grayDark};\n ${({ theme }) => styledText(theme)};\n\n & > .code-wrapper {\n margin: 10px 0;\n }\n`;\n\ninterface StepProps extends React.HTMLAttributes<HTMLDivElement> {\n title: string;\n children: React.ReactNode;\n icon?: IconProps;\n}\n\nfunction Step({ title, children, icon, ...props }: StepProps) {\n return null;\n}\n\ninterface StepsProps extends React.HTMLAttributes<HTMLDivElement> {\n children: React.ReactNode;\n}\n\nfunction Steps({ children, ...props }: StepsProps) {\n const theme = useTheme() as Theme;\n\n const steps = React.Children.toArray(children).filter(\n (child): child is React.ReactElement<StepProps> =>\n React.isValidElement(child),\n );\n\n return (\n <StyledStepsContainer theme={theme}>\n {steps.map((step, index) => {\n const { title, children: stepContent, icon } = step.props;\n\n return (\n <StyledStep key={index} theme={theme}>\n <StepNumber theme={theme}>{index + 1}</StepNumber>\n {icon && <Icon name={icon} color={theme.colors.primary} />}\n <StyledStepTitle theme={theme}>{title}</StyledStepTitle>\n <StepContent theme={theme}>{stepContent}</StepContent>\n </StyledStep>\n );\n })}\n </StyledStepsContainer>\n );\n}\n\nexport { Steps, Step };\n";
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
export const stepsTemplate = `"use client";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import styled, { useTheme } from "styled-components";
|
|
4
|
+
import { styledText, Theme } from "cherry-styled-components/src/lib";
|
|
5
|
+
import { rgba } from "polished";
|
|
6
|
+
import { Icon, IconProps } from "@/components/layout/Icon";
|
|
7
|
+
|
|
8
|
+
const StyledStepsContainer = styled.div<{ theme: Theme }>\`
|
|
9
|
+
position: relative;
|
|
10
|
+
width: 100%;
|
|
11
|
+
\`;
|
|
12
|
+
|
|
13
|
+
const StyledStep = styled.div<{ theme: Theme }>\`
|
|
14
|
+
background: \${({ theme }) => theme.colors.light};
|
|
15
|
+
border-radius: \${({ theme }) => theme.spacing.radius.lg};
|
|
16
|
+
padding: 0 0 20px 52px;
|
|
17
|
+
margin: 0;
|
|
18
|
+
position: relative;
|
|
19
|
+
\${({ theme }) => styledText(theme)}
|
|
20
|
+
color: \${({ theme }) => theme.colors.grayDark};
|
|
21
|
+
|
|
22
|
+
&::after {
|
|
23
|
+
content: "";
|
|
24
|
+
position: absolute;
|
|
25
|
+
left: 16px;
|
|
26
|
+
top: 0;
|
|
27
|
+
width: 1px;
|
|
28
|
+
height: 100%;
|
|
29
|
+
background: \${({ theme }) => theme.colors.primary};
|
|
30
|
+
background: linear-gradient(
|
|
31
|
+
180deg,
|
|
32
|
+
\${({ theme }) => theme.colors.primary},
|
|
33
|
+
\${({ theme }) => rgba(theme.colors.primary, 0)}
|
|
34
|
+
);
|
|
35
|
+
border-radius: 4px;
|
|
36
|
+
}
|
|
37
|
+
\`;
|
|
38
|
+
|
|
39
|
+
const StepNumber = styled.div<{ theme: Theme }>\`
|
|
40
|
+
width: 32px;
|
|
41
|
+
height: 32px;
|
|
42
|
+
border-radius: 50%;
|
|
43
|
+
background: \${({ theme }) => theme.colors.primary};
|
|
44
|
+
color: \${({ theme }) => theme.colors.light};
|
|
45
|
+
display: flex;
|
|
46
|
+
align-items: center;
|
|
47
|
+
justify-content: center;
|
|
48
|
+
font-weight: 700;
|
|
49
|
+
margin-bottom: 12px;
|
|
50
|
+
\${({ theme }) => styledText(theme)};
|
|
51
|
+
position: absolute;
|
|
52
|
+
left: 0;
|
|
53
|
+
top: 0;
|
|
54
|
+
z-index: 1;
|
|
55
|
+
\`;
|
|
56
|
+
|
|
57
|
+
const StyledStepTitle = styled.h3<{ theme: Theme }>\`
|
|
58
|
+
margin: 0 0 10px 0;
|
|
59
|
+
padding: 2px 0 0 0;
|
|
60
|
+
color: \${({ theme }) => theme.colors.dark};
|
|
61
|
+
\${({ theme }) => styledText(theme)};
|
|
62
|
+
\`;
|
|
63
|
+
|
|
64
|
+
const StepContent = styled.div<{ theme: Theme }>\`
|
|
65
|
+
color: \${({ theme }) => theme.colors.grayDark};
|
|
66
|
+
\${({ theme }) => styledText(theme)};
|
|
67
|
+
|
|
68
|
+
& > .code-wrapper {
|
|
69
|
+
margin: 10px 0;
|
|
70
|
+
}
|
|
71
|
+
\`;
|
|
72
|
+
|
|
73
|
+
interface StepProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
74
|
+
title: string;
|
|
75
|
+
children: React.ReactNode;
|
|
76
|
+
icon?: IconProps;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function Step({ title, children, icon, ...props }: StepProps) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
interface StepsProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
84
|
+
children: React.ReactNode;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function Steps({ children, ...props }: StepsProps) {
|
|
88
|
+
const theme = useTheme() as Theme;
|
|
89
|
+
|
|
90
|
+
const steps = React.Children.toArray(children).filter(
|
|
91
|
+
(child): child is React.ReactElement<StepProps> =>
|
|
92
|
+
React.isValidElement(child),
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<StyledStepsContainer theme={theme}>
|
|
97
|
+
{steps.map((step, index) => {
|
|
98
|
+
const { title, children: stepContent, icon } = step.props;
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
<StyledStep key={index} theme={theme}>
|
|
102
|
+
<StepNumber theme={theme}>{index + 1}</StepNumber>
|
|
103
|
+
{icon && <Icon name={icon} color={theme.colors.primary} />}
|
|
104
|
+
<StyledStepTitle theme={theme}>{title}</StyledStepTitle>
|
|
105
|
+
<StepContent theme={theme}>{stepContent}</StepContent>
|
|
106
|
+
</StyledStep>
|
|
107
|
+
);
|
|
108
|
+
})}
|
|
109
|
+
</StyledStepsContainer>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export { Steps, Step };
|
|
114
|
+
`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const
|
|
1
|
+
export declare const eslintConfigTemplate = "import nextConfig from \"eslint-config-next\";\n\nconst config = [...nextConfig];\n\nexport default config;\n";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const deploymentMdxTemplate = "---\ntitle: \"Deployment\"\ndescription: \"Deploy your documentation site with Doccupine or self-host on
|
|
1
|
+
export declare const deploymentMdxTemplate = "---\ntitle: \"Deployment\"\ndescription: \"Deploy your documentation site with Doccupine or self-host on any platform that supports Next.js.\"\ndate: \"2025-01-15\"\ncategory: \"Configuration\"\ncategoryOrder: 3\norder: 9\n---\n# Deployment\n\n## Deploy with Doccupine\n\nSign up for an account at [Doccupine](https://www.doccupine.com) and create your docs instantly - no build configuration, no infrastructure to manage.\n\nDoccupine gives you:\n- **Automatic deployments** on every push to your repository\n- **Site customization** through a visual dashboard - no code changes needed\n- **Team collaboration** so your whole team can manage docs together\n- **Custom domains** with automatic SSL\n- **AI Assistant and MCP server** included out of the box, no API key required\n\nGet started at [doccupine.com](https://www.doccupine.com).\n\n---\n\n## Self-hosting\n\nDoccupine generates a standard Next.js app, so you can deploy it anywhere that supports Node.js or Next.js.\n\n<Callout type=\"warning\">\n Deploy the generated website directory (the Next.js app), not your MDX source folder. In a monorepo, set the root directory to the generated site folder.\n</Callout>\n\n### Popular hosting options\n\n- **Vercel** - native Next.js support, zero-config deploys. Connect your repo and set the root directory to the generated app folder.\n- **Netlify** - supports Next.js via the `@netlify/plugin-nextjs` adapter. Works with the standard `next build` output.\n- **AWS Amplify** - fully managed hosting with CI/CD. Supports Next.js SSR out of the box.\n- **Cloudflare Pages** - deploy using the `@cloudflare/next-on-pages` adapter for edge-based hosting.\n- **Docker** - build a container from the generated app using the standard [Next.js Docker example](https://github.com/vercel/next.js/tree/canary/examples/with-docker) and deploy to any container platform.\n- **Node.js server** - run `next build && next start` on any server or VPS with Node.js installed.\n\n### Troubleshooting\n- **Build failed** - check build logs. Ensure your lockfile and correct Node.js version are present.\n- **Missing content** - verify your MDX files and assets are in the repository.\n- **SSR issues on edge platforms** - some features (like the AI chat API routes) require a Node.js runtime. Check your platform's documentation for SSR/API route support.";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export const deploymentMdxTemplate = `---
|
|
2
2
|
title: "Deployment"
|
|
3
|
-
description: "Deploy your documentation site with Doccupine or self-host on
|
|
3
|
+
description: "Deploy your documentation site with Doccupine or self-host on any platform that supports Next.js."
|
|
4
4
|
date: "2025-01-15"
|
|
5
5
|
category: "Configuration"
|
|
6
6
|
categoryOrder: 3
|
|
@@ -10,11 +10,11 @@ order: 9
|
|
|
10
10
|
|
|
11
11
|
## Deploy with Doccupine
|
|
12
12
|
|
|
13
|
-
Sign up for an account at [Doccupine](https://www.doccupine.com) and create your docs instantly
|
|
13
|
+
Sign up for an account at [Doccupine](https://www.doccupine.com) and create your docs instantly - no build configuration, no infrastructure to manage.
|
|
14
14
|
|
|
15
15
|
Doccupine gives you:
|
|
16
16
|
- **Automatic deployments** on every push to your repository
|
|
17
|
-
- **Site customization** through a visual dashboard
|
|
17
|
+
- **Site customization** through a visual dashboard - no code changes needed
|
|
18
18
|
- **Team collaboration** so your whole team can manage docs together
|
|
19
19
|
- **Custom domains** with automatic SSL
|
|
20
20
|
- **AI Assistant and MCP server** included out of the box, no API key required
|
|
@@ -23,29 +23,24 @@ Get started at [doccupine.com](https://www.doccupine.com).
|
|
|
23
23
|
|
|
24
24
|
---
|
|
25
25
|
|
|
26
|
-
## Self-hosting
|
|
26
|
+
## Self-hosting
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
Doccupine generates a standard Next.js app, so you can deploy it anywhere that supports Node.js or Next.js.
|
|
29
29
|
|
|
30
30
|
<Callout type="warning">
|
|
31
|
-
Deploy the generated website directory (the Next.js app), not your MDX source folder. In a monorepo, set the
|
|
31
|
+
Deploy the generated website directory (the Next.js app), not your MDX source folder. In a monorepo, set the root directory to the generated site folder.
|
|
32
32
|
</Callout>
|
|
33
33
|
|
|
34
|
-
###
|
|
35
|
-
1. Push the generated site folder to GitHub, GitLab, or Bitbucket.
|
|
36
|
-
2. Import the repository at [vercel.com/new](https://vercel.com/new). Vercel auto-detects Next.js and applies the correct build settings.
|
|
37
|
-
3. Add any required environment variables under Project → Settings → Environment Variables.
|
|
38
|
-
4. Deploy. Vercel creates Preview deployments per branch and promotes to Production on merge to main.
|
|
34
|
+
### Popular hosting options
|
|
39
35
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
### Custom domains
|
|
47
|
-
Add a domain under Project → Settings → Domains and point your DNS to Vercel.
|
|
36
|
+
- **Vercel** - native Next.js support, zero-config deploys. Connect your repo and set the root directory to the generated app folder.
|
|
37
|
+
- **Netlify** - supports Next.js via the \`@netlify/plugin-nextjs\` adapter. Works with the standard \`next build\` output.
|
|
38
|
+
- **AWS Amplify** - fully managed hosting with CI/CD. Supports Next.js SSR out of the box.
|
|
39
|
+
- **Cloudflare Pages** - deploy using the \`@cloudflare/next-on-pages\` adapter for edge-based hosting.
|
|
40
|
+
- **Docker** - build a container from the generated app using the standard [Next.js Docker example](https://github.com/vercel/next.js/tree/canary/examples/with-docker) and deploy to any container platform.
|
|
41
|
+
- **Node.js server** - run \`next build && next start\` on any server or VPS with Node.js installed.
|
|
48
42
|
|
|
49
43
|
### Troubleshooting
|
|
50
|
-
- **Build failed**
|
|
51
|
-
- **Missing content**
|
|
44
|
+
- **Build failed** - check build logs. Ensure your lockfile and correct Node.js version are present.
|
|
45
|
+
- **Missing content** - verify your MDX files and assets are in the repository.
|
|
46
|
+
- **SSR issues on edge platforms** - some features (like the AI chat API routes) require a Node.js runtime. Check your platform's documentation for SSR/API route support.`;
|
|
@@ -24,10 +24,10 @@ export const packageJsonTemplate = JSON.stringify({
|
|
|
24
24
|
"@types/react": "^19",
|
|
25
25
|
"@types/react-dom": "^19",
|
|
26
26
|
"baseline-browser-mapping": "^2.9.19",
|
|
27
|
-
"cherry-styled-components": "^0.1.
|
|
27
|
+
"cherry-styled-components": "^0.1.11",
|
|
28
28
|
eslint: "^9",
|
|
29
29
|
"eslint-config-next": "16.1.6",
|
|
30
|
-
"lucide-react": "^0.
|
|
30
|
+
"lucide-react": "^0.564.0",
|
|
31
31
|
"next-mdx-remote": "^6.0.0",
|
|
32
32
|
polished: "^4.3.1",
|
|
33
33
|
prettier: "^3.8.1",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "doccupine",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.51",
|
|
4
4
|
"description": "Document management system that allows you to store, organize, and share your documentation with ease. AI-ready.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
"build": "tsc",
|
|
12
12
|
"dev": "tsc --watch",
|
|
13
13
|
"start": "node dist/index.js",
|
|
14
|
-
"prepare": "
|
|
14
|
+
"prepare": "tsc",
|
|
15
|
+
"test": "vitest run"
|
|
15
16
|
},
|
|
16
17
|
"keywords": [
|
|
17
18
|
"doccupine",
|
|
@@ -20,6 +21,7 @@
|
|
|
20
21
|
"mdx",
|
|
21
22
|
"ai",
|
|
22
23
|
"llm",
|
|
24
|
+
"mcp",
|
|
23
25
|
"nextjs",
|
|
24
26
|
"cli",
|
|
25
27
|
"generator",
|
|
@@ -27,9 +29,9 @@
|
|
|
27
29
|
"static-site-generator"
|
|
28
30
|
],
|
|
29
31
|
"homepage": "https://doccupine.com",
|
|
30
|
-
"repository": "https://github.com/
|
|
32
|
+
"repository": "https://github.com/doccupine/cli",
|
|
31
33
|
"author": "Luan Gjokaj",
|
|
32
|
-
"license": "
|
|
34
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
33
35
|
"dependencies": {
|
|
34
36
|
"chalk": "^5.6.2",
|
|
35
37
|
"chokidar": "^5.0.0",
|
|
@@ -46,7 +48,8 @@
|
|
|
46
48
|
"@types/fs-extra": "^11.0.4",
|
|
47
49
|
"@types/node": "^25.2.3",
|
|
48
50
|
"@types/prompts": "^2.4.9",
|
|
49
|
-
"typescript": "^5.9.3"
|
|
51
|
+
"typescript": "^5.9.3",
|
|
52
|
+
"vitest": "^4.0.18"
|
|
50
53
|
},
|
|
51
54
|
"files": [
|
|
52
55
|
"dist/**/*"
|