doccupine 0.0.101 → 0.0.103
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +7 -0
- package/dist/lib/layout.js +1 -0
- package/dist/lib/structures.js +2 -0
- package/dist/lib/types.d.ts +2 -0
- package/dist/templates/components/Docs.d.ts +1 -1
- package/dist/templates/components/Docs.js +18 -13
- package/dist/templates/components/MDXComponents.d.ts +1 -1
- package/dist/templates/components/MDXComponents.js +16 -15
- package/dist/templates/components/SectionNavProvider.d.ts +1 -1
- package/dist/templates/components/SectionNavProvider.js +8 -1
- package/dist/templates/components/SideBar.d.ts +1 -1
- package/dist/templates/components/SideBar.js +136 -19
- package/dist/templates/components/layout/DocsComponents.d.ts +1 -1
- package/dist/templates/components/layout/DocsComponents.js +102 -6
- package/dist/templates/components/layout/DocsNavigation.d.ts +1 -1
- package/dist/templates/components/layout/DocsNavigation.js +27 -12
- package/dist/templates/components/layout/Slug.d.ts +1 -0
- package/dist/templates/components/layout/Slug.js +32 -0
- package/dist/templates/components/layout/Tabs.d.ts +1 -1
- package/dist/templates/components/layout/Tabs.js +11 -0
- package/dist/templates/components/layout/Update.d.ts +1 -1
- package/dist/templates/components/layout/Update.js +19 -4
- package/dist/templates/mdx/components.mdx.d.ts +1 -1
- package/dist/templates/mdx/components.mdx.js +1 -0
- package/dist/templates/mdx/globals.mdx.d.ts +1 -1
- package/dist/templates/mdx/globals.mdx.js +1 -0
- package/dist/templates/mdx/index.mdx.d.ts +1 -1
- package/dist/templates/mdx/index.mdx.js +1 -0
- package/dist/templates/mdx/navigation.mdx.d.ts +1 -1
- package/dist/templates/mdx/navigation.mdx.js +69 -1
- package/dist/templates/mdx/platform/build-and-deploy.mdx.d.ts +1 -1
- package/dist/templates/mdx/platform/build-and-deploy.mdx.js +1 -0
- package/dist/templates/mdx/platform/file-editor.mdx.d.ts +1 -1
- package/dist/templates/mdx/platform/file-editor.mdx.js +1 -0
- package/dist/templates/mdx/platform/index.mdx.d.ts +1 -1
- package/dist/templates/mdx/platform/index.mdx.js +1 -0
- package/dist/templates/mdx/platform/project-settings.mdx.d.ts +1 -1
- package/dist/templates/mdx/platform/project-settings.mdx.js +1 -0
- package/dist/templates/mdx/platform/site-settings.mdx.d.ts +1 -1
- package/dist/templates/mdx/platform/site-settings.mdx.js +1 -0
- package/dist/templates/mdx/tabs.mdx.d.ts +1 -1
- package/dist/templates/mdx/tabs.mdx.js +13 -6
- package/dist/templates/mdx/update.mdx.d.ts +1 -1
- package/dist/templates/mdx/update.mdx.js +6 -3
- package/dist/templates/utils/orderNavItems.d.ts +1 -1
- package/dist/templates/utils/orderNavItems.js +11 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -667,6 +667,13 @@ class MDXToNextJSGenerator {
|
|
|
667
667
|
categoryOrder: frontmatter.categoryOrder || 0,
|
|
668
668
|
order: frontmatter.order || 0,
|
|
669
669
|
section: sectionSlug,
|
|
670
|
+
// Sidebar icons (Lucide names). Kept separate from `icon`, which is
|
|
671
|
+
// reserved for the favicon/OG metadata. Only emitted when set so the
|
|
672
|
+
// generated page literal stays lean.
|
|
673
|
+
...(frontmatter.navIcon ? { navIcon: String(frontmatter.navIcon) } : {}),
|
|
674
|
+
...(frontmatter.categoryIcon
|
|
675
|
+
? { categoryIcon: String(frontmatter.categoryIcon) }
|
|
676
|
+
: {}),
|
|
670
677
|
lastModified,
|
|
671
678
|
};
|
|
672
679
|
}
|
package/dist/lib/layout.js
CHANGED
package/dist/lib/structures.js
CHANGED
|
@@ -45,6 +45,7 @@ import { iconTemplate } from "../templates/components/layout/Icon.js";
|
|
|
45
45
|
import { pictogramsTemplate } from "../templates/components/layout/Pictograms.js";
|
|
46
46
|
import { sharedStyledTemplate } from "../templates/components/layout/SharedStyles.js";
|
|
47
47
|
import { siteGateComponentTemplate } from "../templates/components/layout/SiteGate.js";
|
|
48
|
+
import { slugTemplate } from "../templates/components/layout/Slug.js";
|
|
48
49
|
import { staticLinksTemplate } from "../templates/components/layout/StaticLinks.js";
|
|
49
50
|
import { stepsTemplate } from "../templates/components/layout/Steps.js";
|
|
50
51
|
import { tabsTemplate } from "../templates/components/layout/Tabs.js";
|
|
@@ -176,6 +177,7 @@ export const appStructure = {
|
|
|
176
177
|
"components/layout/Pictograms.tsx": pictogramsTemplate,
|
|
177
178
|
"components/layout/SharedStyled.ts": sharedStyledTemplate,
|
|
178
179
|
"components/layout/SiteGate.tsx": siteGateComponentTemplate,
|
|
180
|
+
"components/layout/Slug.ts": slugTemplate,
|
|
179
181
|
"components/layout/StaticLinks.tsx": staticLinksTemplate,
|
|
180
182
|
"components/layout/Steps.tsx": stepsTemplate,
|
|
181
183
|
"components/layout/Tabs.tsx": tabsTemplate,
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const docsTemplate = "import React from \"react\";\nimport { Flex } from \"cherry-styled-components\";\nimport {\n DocsContainer,\n StyledMarkdownContainer,\n StyledMissingComponent,\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
|
|
1
|
+
export declare const docsTemplate = "import React from \"react\";\nimport { Flex } from \"cherry-styled-components\";\nimport {\n DocsContainer,\n StyledMarkdownContainer,\n StyledMissingComponent,\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\";\nimport { createSlugger } from \"@/components/layout/Slug\";\n\ninterface DocsProps {\n content: string;\n}\n\ninterface Heading {\n id: string;\n text: string;\n level: number;\n}\n\nfunction extractHeadings(content: string): Heading[] {\n const contentWithoutCodeBlocks = content.replace(/```[\\s\\S]*?```/g, \"\");\n const entries: { text: string; level: number; position: number }[] = [];\n let match;\n\n // Markdown headings (# .. ######)\n const headingRegex = /^(#{1,6})\\s+(.+)$/gm;\n while ((match = headingRegex.exec(contentWithoutCodeBlocks)) !== null) {\n const level = match[1].length;\n const text = match[2].trim();\n entries.push({ text, level, position: match.index });\n }\n\n // <Update label=\"...\"> blocks surface their label as a top-level entry\n const updateRegex = /<Update\\b[^>]*?\\blabel=[\"']([^\"']+)[\"'][^>]*>/g;\n while ((match = updateRegex.exec(contentWithoutCodeBlocks)) !== null) {\n entries.push({ text: match[1].trim(), level: 1, position: match.index });\n }\n\n // Assign ids in document order with a shared slugger so repeated heading\n // text produces unique anchors (\"setup\", \"setup-1\", ...) that stay in sync\n // with the ids rendered by MDXComponents/Update.\n const slug = createSlugger();\n return entries\n .sort((a, b) => a.position - b.position)\n .map(({ text, level }) => ({ id: slug(text), text, level }));\n}\n\nfunction extractComponentNames(source: string): string[] {\n const stripped = source\n .replace(/```[\\s\\S]*?```/g, \"\")\n .replace(/`[^`]*`/g, \"\");\n const tagRegex = /<([A-Z][a-zA-Z0-9]*)/g;\n const names = new Set<string>();\n let match;\n while ((match = tagRegex.exec(stripped)) !== null) {\n names.add(match[1]);\n }\n return Array.from(names);\n}\n\nfunction MissingComponent({\n componentName,\n children: _children,\n}: {\n componentName: string;\n children?: React.ReactNode;\n}) {\n return (\n <StyledMissingComponent>\n Missing component: <{componentName} />\n </StyledMissingComponent>\n );\n}\n\nfunction Docs({ content }: DocsProps) {\n const headings = extractHeadings(content);\n const components = useMDXComponents({});\n\n const knownNames = Object.keys(components);\n const usedNames = extractComponentNames(content);\n const missingNames = usedNames.filter((name) => !knownNames.includes(name));\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const stubs: Record<string, React.ComponentType<any>> = {};\n for (const name of missingNames) {\n stubs[name] = ({ children }: { children?: React.ReactNode }) => (\n <MissingComponent componentName={name}>{children}</MissingComponent>\n );\n }\n\n const allComponents = { ...components, ...stubs };\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={allComponents}\n />\n )}\n </StyledMarkdownContainer>\n </Flex>\n </ActionBar>\n </DocsContainer>\n <DocsSideBar headings={headings} />\n </>\n );\n}\n\nexport { Docs };\n";
|
|
@@ -10,6 +10,7 @@ import remarkGfm from "remark-gfm";
|
|
|
10
10
|
import { useMDXComponents } from "@/components/MDXComponents";
|
|
11
11
|
import { DocsSideBar } from "@/components/DocsSideBar";
|
|
12
12
|
import { ActionBar } from "@/components/layout/ActionBar";
|
|
13
|
+
import { createSlugger } from "@/components/layout/Slug";
|
|
13
14
|
|
|
14
15
|
interface DocsProps {
|
|
15
16
|
content: string;
|
|
@@ -21,28 +22,32 @@ interface Heading {
|
|
|
21
22
|
level: number;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
function generateId(text: string): string {
|
|
25
|
-
return text
|
|
26
|
-
.toLowerCase()
|
|
27
|
-
.replace(/[^\\w\\s-]/g, "")
|
|
28
|
-
.replace(/\\s+/g, "-")
|
|
29
|
-
.trim();
|
|
30
|
-
}
|
|
31
|
-
|
|
32
25
|
function extractHeadings(content: string): Heading[] {
|
|
33
26
|
const contentWithoutCodeBlocks = content.replace(/\`\`\`[\\s\\S]*?\`\`\`/g, "");
|
|
34
|
-
const
|
|
35
|
-
const headings: Heading[] = [];
|
|
27
|
+
const entries: { text: string; level: number; position: number }[] = [];
|
|
36
28
|
let match;
|
|
37
29
|
|
|
30
|
+
// Markdown headings (# .. ######)
|
|
31
|
+
const headingRegex = /^(#{1,6})\\s+(.+)$/gm;
|
|
38
32
|
while ((match = headingRegex.exec(contentWithoutCodeBlocks)) !== null) {
|
|
39
33
|
const level = match[1].length;
|
|
40
34
|
const text = match[2].trim();
|
|
41
|
-
|
|
42
|
-
|
|
35
|
+
entries.push({ text, level, position: match.index });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// <Update label="..."> blocks surface their label as a top-level entry
|
|
39
|
+
const updateRegex = /<Update\\b[^>]*?\\blabel=["']([^"']+)["'][^>]*>/g;
|
|
40
|
+
while ((match = updateRegex.exec(contentWithoutCodeBlocks)) !== null) {
|
|
41
|
+
entries.push({ text: match[1].trim(), level: 1, position: match.index });
|
|
43
42
|
}
|
|
44
43
|
|
|
45
|
-
|
|
44
|
+
// Assign ids in document order with a shared slugger so repeated heading
|
|
45
|
+
// text produces unique anchors ("setup", "setup-1", ...) that stay in sync
|
|
46
|
+
// with the ids rendered by MDXComponents/Update.
|
|
47
|
+
const slug = createSlugger();
|
|
48
|
+
return entries
|
|
49
|
+
.sort((a, b) => a.position - b.position)
|
|
50
|
+
.map(({ text, level }) => ({ id: slug(text), text, level }));
|
|
46
51
|
}
|
|
47
52
|
|
|
48
53
|
function extractComponentNames(source: string): string[] {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const mdxComponentsTemplate = "import React from \"react\";\nimport Link from \"next/link\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype MDXComponents = Record<string, React.ComponentType<any>>;\nimport { Space } from \"cherry-styled-components\";\nimport { Code as CodeBlock, CodeTabs } from \"@/components/layout/Code\";\nimport { Card } from \"@/components/layout/Card\";\nimport { Accordion } from \"@/components/layout/Accordion\";\nimport { Tabs, TabContent } from \"@/components/layout/Tabs\";\nimport { Callout } from \"@/components/layout/Callout\";\nimport { Icon } from \"@/components/layout/Icon\";\nimport { Columns } from \"@/components/layout/Columns\";\nimport { Field } from \"@/components/layout/Field\";\nimport { Update } from \"@/components/layout/Update\";\nimport { Steps, Step } from \"@/components/layout/Steps\";\nimport { Button } from \"@/components/layout/Button\";\nimport { ColorSwatch, ColorSwatchGroup } from \"@/components/layout/ColorSwatch\";\nimport { DemoTheme } from \"@/components/layout/DemoTheme\";\n\ninterface HeadingProps extends React.HTMLAttributes<HTMLHeadingElement> {\n children?: React.ReactNode;\n}\n\ninterface PreProps extends React.HTMLAttributes<HTMLPreElement> {\n children?: React.ReactNode;\n}\n\nfunction extractAllTextFromChildren(children: React.ReactNode): string {\n if (children == null) return \"\";\n if (typeof children === \"string\") return children;\n if (typeof children === \"number\") return String(children);\n if (typeof children === \"boolean\") return \"\";\n if (Array.isArray(children))\n return children.map(extractAllTextFromChildren).join(\"\");\n if (React.isValidElement(children)) {\n const element = children as React.ReactElement<{\n children?: React.ReactNode;\n }>;\n return extractAllTextFromChildren(element.props.children);\n }\n return \"\";\n}\n\
|
|
1
|
+
export declare const mdxComponentsTemplate = "import React from \"react\";\nimport Link from \"next/link\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype MDXComponents = Record<string, React.ComponentType<any>>;\nimport { Space } from \"cherry-styled-components\";\nimport { Code as CodeBlock, CodeTabs } from \"@/components/layout/Code\";\nimport { Card } from \"@/components/layout/Card\";\nimport { Accordion } from \"@/components/layout/Accordion\";\nimport { Tabs, TabContent } from \"@/components/layout/Tabs\";\nimport { Callout } from \"@/components/layout/Callout\";\nimport { Icon } from \"@/components/layout/Icon\";\nimport { Columns } from \"@/components/layout/Columns\";\nimport { Field } from \"@/components/layout/Field\";\nimport { Update } from \"@/components/layout/Update\";\nimport { Steps, Step } from \"@/components/layout/Steps\";\nimport { Button } from \"@/components/layout/Button\";\nimport { ColorSwatch, ColorSwatchGroup } from \"@/components/layout/ColorSwatch\";\nimport { DemoTheme } from \"@/components/layout/DemoTheme\";\nimport { createSlugger } from \"@/components/layout/Slug\";\n\ninterface HeadingProps extends React.HTMLAttributes<HTMLHeadingElement> {\n children?: React.ReactNode;\n}\n\ninterface PreProps extends React.HTMLAttributes<HTMLPreElement> {\n children?: React.ReactNode;\n}\n\nfunction extractAllTextFromChildren(children: React.ReactNode): string {\n if (children == null) return \"\";\n if (typeof children === \"string\") return children;\n if (typeof children === \"number\") return String(children);\n if (typeof children === \"boolean\") return \"\";\n if (Array.isArray(children))\n return children.map(extractAllTextFromChildren).join(\"\");\n if (React.isValidElement(children)) {\n const element = children as React.ReactElement<{\n children?: React.ReactNode;\n }>;\n return extractAllTextFromChildren(element.props.children);\n }\n return \"\";\n}\n\n// Map <pre><code class=\"language-xyz\"> to our <Code /> component\nfunction Pre(props: PreProps) {\n const child = React.Children.only(props.children) as React.ReactElement<{\n className?: string;\n children?: React.ReactNode;\n }> | null;\n if (child && child.type === \"code\") {\n const className = child.props.className || \"\";\n const match = /language-(\\w+)/.exec(className);\n const language = match ? match[1] : undefined;\n const code = extractAllTextFromChildren(child.props.children).replace(\n /\\n$/,\n \"\",\n );\n if (language) {\n return (\n <CodeBlock className={className} code={code} language={language} />\n );\n }\n }\n return <pre {...props} />;\n}\n\nexport function useMDXComponents(components: MDXComponents): MDXComponents {\n // One slugger per render so repeated heading text (and <Update> labels)\n // resolve to unique, document-order ids that match the index sidebar built\n // in components/Docs.tsx.\n const slug = createSlugger();\n return {\n // Headings with auto-generated ids for TOC and deep links\n h1: ({ children, ...props }: HeadingProps) => {\n const id = slug(extractAllTextFromChildren(children));\n return (\n <h1 id={id} {...props}>\n {children}\n </h1>\n );\n },\n h2: ({ children, ...props }: HeadingProps) => {\n const id = slug(extractAllTextFromChildren(children));\n return (\n <h2 id={id} {...props}>\n {children}\n </h2>\n );\n },\n h3: ({ children, ...props }: HeadingProps) => {\n const id = slug(extractAllTextFromChildren(children));\n return (\n <h3 id={id} {...props}>\n {children}\n </h3>\n );\n },\n h4: ({ children, ...props }: HeadingProps) => {\n const id = slug(extractAllTextFromChildren(children));\n return (\n <h4 id={id} {...props}>\n {children}\n </h4>\n );\n },\n h5: ({ children, ...props }: HeadingProps) => {\n const id = slug(extractAllTextFromChildren(children));\n return (\n <h5 id={id} {...props}>\n {children}\n </h5>\n );\n },\n h6: ({ children, ...props }: HeadingProps) => {\n const id = slug(extractAllTextFromChildren(children));\n return (\n <h6 id={id} {...props}>\n {children}\n </h6>\n );\n },\n\n // Links - use Next.js Link for internal paths\n a: ({\n href,\n children,\n ...props\n }: React.AnchorHTMLAttributes<HTMLAnchorElement>) => {\n if (href && href.startsWith(\"/\")) {\n return (\n <Link href={href} {...props}>\n {children}\n </Link>\n );\n }\n return (\n <a href={href} target=\"_blank\" rel=\"noopener noreferrer\" {...props}>\n {children}\n </a>\n );\n },\n\n // Tables - wrap in a div for responsive overflow\n table: (props: React.TableHTMLAttributes<HTMLTableElement>) => (\n <div className=\"table-wrapper\">\n <table {...props} />\n </div>\n ),\n\n // Code blocks\n pre: Pre,\n\n // Expose your custom components for MDX usage\n Code: CodeBlock,\n CodeTabs,\n Card,\n Accordion,\n Tabs,\n TabContent,\n Callout,\n Icon,\n Columns,\n Field,\n // Share the heading slugger so an <Update> label anchor stays unique and\n // in document order alongside the surrounding headings.\n Update: (props: React.ComponentProps<typeof Update>) => (\n <Update {...props} id={slug(props.label)} />\n ),\n Steps,\n Step,\n Button,\n ColorSwatch,\n ColorSwatchGroup,\n DemoTheme,\n Space,\n ...components,\n };\n}\n";
|
|
@@ -17,6 +17,7 @@ import { Steps, Step } from "@/components/layout/Steps";
|
|
|
17
17
|
import { Button } from "@/components/layout/Button";
|
|
18
18
|
import { ColorSwatch, ColorSwatchGroup } from "@/components/layout/ColorSwatch";
|
|
19
19
|
import { DemoTheme } from "@/components/layout/DemoTheme";
|
|
20
|
+
import { createSlugger } from "@/components/layout/Slug";
|
|
20
21
|
|
|
21
22
|
interface HeadingProps extends React.HTMLAttributes<HTMLHeadingElement> {
|
|
22
23
|
children?: React.ReactNode;
|
|
@@ -42,14 +43,6 @@ function extractAllTextFromChildren(children: React.ReactNode): string {
|
|
|
42
43
|
return "";
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
function generateId(text: string): string {
|
|
46
|
-
return text
|
|
47
|
-
.toLowerCase()
|
|
48
|
-
.replace(/[^\\w\\s-]/g, "")
|
|
49
|
-
.replace(/\\s+/g, "-")
|
|
50
|
-
.trim();
|
|
51
|
-
}
|
|
52
|
-
|
|
53
46
|
// Map <pre><code class="language-xyz"> to our <Code /> component
|
|
54
47
|
function Pre(props: PreProps) {
|
|
55
48
|
const child = React.Children.only(props.children) as React.ReactElement<{
|
|
@@ -74,10 +67,14 @@ function Pre(props: PreProps) {
|
|
|
74
67
|
}
|
|
75
68
|
|
|
76
69
|
export function useMDXComponents(components: MDXComponents): MDXComponents {
|
|
70
|
+
// One slugger per render so repeated heading text (and <Update> labels)
|
|
71
|
+
// resolve to unique, document-order ids that match the index sidebar built
|
|
72
|
+
// in components/Docs.tsx.
|
|
73
|
+
const slug = createSlugger();
|
|
77
74
|
return {
|
|
78
75
|
// Headings with auto-generated ids for TOC and deep links
|
|
79
76
|
h1: ({ children, ...props }: HeadingProps) => {
|
|
80
|
-
const id =
|
|
77
|
+
const id = slug(extractAllTextFromChildren(children));
|
|
81
78
|
return (
|
|
82
79
|
<h1 id={id} {...props}>
|
|
83
80
|
{children}
|
|
@@ -85,7 +82,7 @@ export function useMDXComponents(components: MDXComponents): MDXComponents {
|
|
|
85
82
|
);
|
|
86
83
|
},
|
|
87
84
|
h2: ({ children, ...props }: HeadingProps) => {
|
|
88
|
-
const id =
|
|
85
|
+
const id = slug(extractAllTextFromChildren(children));
|
|
89
86
|
return (
|
|
90
87
|
<h2 id={id} {...props}>
|
|
91
88
|
{children}
|
|
@@ -93,7 +90,7 @@ export function useMDXComponents(components: MDXComponents): MDXComponents {
|
|
|
93
90
|
);
|
|
94
91
|
},
|
|
95
92
|
h3: ({ children, ...props }: HeadingProps) => {
|
|
96
|
-
const id =
|
|
93
|
+
const id = slug(extractAllTextFromChildren(children));
|
|
97
94
|
return (
|
|
98
95
|
<h3 id={id} {...props}>
|
|
99
96
|
{children}
|
|
@@ -101,7 +98,7 @@ export function useMDXComponents(components: MDXComponents): MDXComponents {
|
|
|
101
98
|
);
|
|
102
99
|
},
|
|
103
100
|
h4: ({ children, ...props }: HeadingProps) => {
|
|
104
|
-
const id =
|
|
101
|
+
const id = slug(extractAllTextFromChildren(children));
|
|
105
102
|
return (
|
|
106
103
|
<h4 id={id} {...props}>
|
|
107
104
|
{children}
|
|
@@ -109,7 +106,7 @@ export function useMDXComponents(components: MDXComponents): MDXComponents {
|
|
|
109
106
|
);
|
|
110
107
|
},
|
|
111
108
|
h5: ({ children, ...props }: HeadingProps) => {
|
|
112
|
-
const id =
|
|
109
|
+
const id = slug(extractAllTextFromChildren(children));
|
|
113
110
|
return (
|
|
114
111
|
<h5 id={id} {...props}>
|
|
115
112
|
{children}
|
|
@@ -117,7 +114,7 @@ export function useMDXComponents(components: MDXComponents): MDXComponents {
|
|
|
117
114
|
);
|
|
118
115
|
},
|
|
119
116
|
h6: ({ children, ...props }: HeadingProps) => {
|
|
120
|
-
const id =
|
|
117
|
+
const id = slug(extractAllTextFromChildren(children));
|
|
121
118
|
return (
|
|
122
119
|
<h6 id={id} {...props}>
|
|
123
120
|
{children}
|
|
@@ -166,7 +163,11 @@ export function useMDXComponents(components: MDXComponents): MDXComponents {
|
|
|
166
163
|
Icon,
|
|
167
164
|
Columns,
|
|
168
165
|
Field,
|
|
169
|
-
Update
|
|
166
|
+
// Share the heading slugger so an <Update> label anchor stays unique and
|
|
167
|
+
// in document order alongside the surrounding headings.
|
|
168
|
+
Update: (props: React.ComponentProps<typeof Update>) => (
|
|
169
|
+
<Update {...props} id={slug(props.label)} />
|
|
170
|
+
),
|
|
170
171
|
Steps,
|
|
171
172
|
Step,
|
|
172
173
|
Button,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const sectionNavProviderTemplate = "\"use client\";\nimport { useMemo } from \"react\";\nimport { usePathname } from \"next/navigation\";\nimport { SideBar } from \"@/components/SideBar\";\nimport { DocsNavigation } from \"@/components/layout/DocsNavigation\";\nimport { SectionBarProvider } from \"@/components/layout/DocsComponents\";\nimport { Footer } from \"@/components/layout/Footer\";\nimport { StaticLinks } from \"@/components/layout/StaticLinks\";\nimport {\n transformPagesToGroupedStructure,\n type PagesProps,\n} from \"@/utils/orderNavItems\";\nimport rawNavigation from \"@/navigation.json\";\n\n// navigation.json can be an array (root section only) or an object keyed by section slug\ntype
|
|
1
|
+
export declare const sectionNavProviderTemplate = "\"use client\";\nimport { useMemo } from \"react\";\nimport { usePathname } from \"next/navigation\";\nimport { SideBar } from \"@/components/SideBar\";\nimport { DocsNavigation } from \"@/components/layout/DocsNavigation\";\nimport { SectionBarProvider } from \"@/components/layout/DocsComponents\";\nimport { Footer } from \"@/components/layout/Footer\";\nimport { StaticLinks } from \"@/components/layout/StaticLinks\";\nimport {\n transformPagesToGroupedStructure,\n type PagesProps,\n} from \"@/utils/orderNavItems\";\nimport rawNavigation from \"@/navigation.json\";\n\n// navigation.json can be an array (root section only) or an object keyed by section slug\ntype NavLink = {\n slug?: string;\n title: string;\n icon?: string;\n links?: NavLink[];\n};\ntype NavItem = { label: string; icon?: string; links: NavLink[] };\ntype NavigationConfig = NavItem[] | Record<string, NavItem[]>;\n\nconst navigation = rawNavigation as NavigationConfig;\n\nfunction getNavigationForSection(\n nav: NavigationConfig,\n sectionSlug: string,\n): NavItem[] | null {\n if (Array.isArray(nav)) {\n return sectionSlug === \"\" && nav.length ? nav : null;\n }\n const sectionNav = nav[sectionSlug];\n return sectionNav && sectionNav.length ? sectionNav : null;\n}\n\ninterface SectionConfig {\n label: string;\n slug: string;\n directory?: string;\n}\n\ninterface SectionNavProviderProps {\n sections: SectionConfig[];\n allPages: PagesProps[];\n hideBranding: boolean;\n children: React.ReactNode;\n}\n\nfunction SectionNavProvider({\n sections,\n allPages,\n hideBranding,\n children,\n}: SectionNavProviderProps) {\n const pathname = usePathname();\n const currentPath = pathname.replace(/^\\//, \"\").replace(/\\/$/, \"\");\n\n const activeSectionSlug = useMemo(() => {\n const match = sections.find((section) => {\n if (section.slug === \"\") return false;\n return (\n currentPath === section.slug ||\n currentPath.startsWith(section.slug + \"/\")\n );\n });\n return match ? match.slug : \"\";\n }, [sections, currentPath]);\n\n const result = useMemo(() => {\n const sectionNav = getNavigationForSection(navigation, activeSectionSlug);\n if (sectionNav) return sectionNav;\n const filtered = allPages.filter(\n (page) => (page.section || \"\") === activeSectionSlug,\n );\n return transformPagesToGroupedStructure(filtered);\n }, [allPages, activeSectionSlug]);\n\n // Fallback when no pages exist yet (also defined in layout.tsx for the non-sections path)\n const defaultPages = [\n {\n slug: \"\",\n title: \"Getting Started\",\n description:\n \"Doccupine is a 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.\",\n date: \"2025-01-15\",\n category: \"Introduction\",\n categoryOrder: 0,\n categoryIcon: \"rocket\",\n order: 0,\n },\n ];\n\n const defaultResults = transformPagesToGroupedStructure(defaultPages);\n\n return (\n <SectionBarProvider hasSectionBar={true}>\n <SideBar result={result.length ? result : defaultResults} />\n {children}\n <DocsNavigation result={result.length ? result : defaultResults} />\n <StaticLinks />\n <Footer hideBranding={hideBranding} />\n </SectionBarProvider>\n );\n}\n\nexport { SectionNavProvider };\n";
|
|
@@ -14,7 +14,13 @@ import {
|
|
|
14
14
|
import rawNavigation from "@/navigation.json";
|
|
15
15
|
|
|
16
16
|
// navigation.json can be an array (root section only) or an object keyed by section slug
|
|
17
|
-
type
|
|
17
|
+
type NavLink = {
|
|
18
|
+
slug?: string;
|
|
19
|
+
title: string;
|
|
20
|
+
icon?: string;
|
|
21
|
+
links?: NavLink[];
|
|
22
|
+
};
|
|
23
|
+
type NavItem = { label: string; icon?: string; links: NavLink[] };
|
|
18
24
|
type NavigationConfig = NavItem[] | Record<string, NavItem[]>;
|
|
19
25
|
|
|
20
26
|
const navigation = rawNavigation as NavigationConfig;
|
|
@@ -82,6 +88,7 @@ function SectionNavProvider({
|
|
|
82
88
|
date: "2025-01-15",
|
|
83
89
|
category: "Introduction",
|
|
84
90
|
categoryOrder: 0,
|
|
91
|
+
categoryIcon: "rocket",
|
|
85
92
|
order: 0,
|
|
86
93
|
},
|
|
87
94
|
];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const sideBarTemplate = "\"use client\";\nimport { useContext, useState } from \"react\";\nimport { usePathname } from \"next/navigation\";\nimport { Flex, Space, ThemeToggle } from \"cherry-styled-components\";\nimport {\n DocsSidebar,\n SectionBarContext,\n StyledSidebar,\n StyledSidebarList,\n StyledSidebarListItem,\n StyledStrong,\n StyledSidebarListItemLink,\n StyledSidebarFooter,\n StyleMobileBar,\n StyledMobileBurger,\n} from \"@/components/layout/DocsComponents\";\nimport { useLockBodyScroll } from \"@/components/LockBodyScroll\";\n\ntype
|
|
1
|
+
export declare const sideBarTemplate = "\"use client\";\nimport { useContext, useState } from \"react\";\nimport { usePathname } from \"next/navigation\";\nimport { Flex, Space, ThemeToggle } from \"cherry-styled-components\";\nimport {\n DocsSidebar,\n SectionBarContext,\n StyledSidebar,\n StyledSidebarList,\n StyledSidebarListItem,\n StyledStrong,\n StyledSidebarListItemLink,\n StyledSidebarGroupButton,\n StyledSidebarGroupRow,\n StyledSidebarGroupLink,\n StyledSidebarGroupChevron,\n StyledSidebarGroupContent,\n StyledSidebarFooter,\n StyleMobileBar,\n StyledMobileBurger,\n} from \"@/components/layout/DocsComponents\";\nimport { Icon } from \"@/components/layout/Icon\";\nimport { useLockBodyScroll } from \"@/components/LockBodyScroll\";\n\n// A link can be a leaf (slug + title) or a group with nested children. Both the\n// category icon and the per-link icon are optional Lucide names.\ntype NavItemLink = {\n slug?: string;\n title: string;\n icon?: string;\n links?: NavItemLink[];\n};\n\ntype NavItem = {\n label: string;\n icon?: string;\n links: NavItemLink[];\n};\n\ninterface SideBarProps {\n result: NavItem[];\n}\n\nfunction linkContainsActivePath(link: NavItemLink, pathname: string): boolean {\n if (link.slug !== undefined && pathname === `/${link.slug}`) {\n return true;\n }\n return (link.links ?? []).some((child) =>\n linkContainsActivePath(child, pathname),\n );\n}\n\nfunction SidebarNavLink({\n link,\n depth,\n pathname,\n onNavigate,\n}: {\n link: NavItemLink;\n depth: number;\n pathname: string;\n onNavigate: () => void;\n}) {\n const children = link.links ?? [];\n const hasChildren = children.length > 0;\n const href = link.slug !== undefined ? `/${link.slug}` : undefined;\n const isActive = href !== undefined && pathname === href;\n const indent = { paddingLeft: `${20 + depth * 14}px` };\n\n // Open collapsible groups that contain the active page so deep links land\n // with their ancestors already expanded.\n const [isOpen, setIsOpen] = useState(\n hasChildren\n ? children.some((child) => linkContainsActivePath(child, pathname))\n : false,\n );\n\n if (!hasChildren) {\n return (\n <StyledSidebarListItem>\n <StyledSidebarListItemLink\n href={href ?? \"#\"}\n $isActive={isActive}\n onClick={onNavigate}\n style={indent}\n >\n {link.icon && <Icon name={link.icon} size={16} />}\n {link.title}\n </StyledSidebarListItemLink>\n </StyledSidebarListItem>\n );\n }\n\n const toggle = () => setIsOpen((prev) => !prev);\n const toggleLabel = isOpen\n ? `Collapse ${link.title}`\n : `Expand ${link.title}`;\n const groupActive = linkContainsActivePath(link, pathname);\n\n return (\n <li>\n {href !== undefined ? (\n <StyledSidebarGroupRow\n $isActive={groupActive}\n $isOpen={isOpen}\n style={indent}\n >\n <StyledSidebarGroupLink href={href} onClick={onNavigate}>\n {link.icon && <Icon name={link.icon} size={16} />}\n {link.title}\n </StyledSidebarGroupLink>\n <StyledSidebarGroupChevron\n type=\"button\"\n onClick={toggle}\n aria-expanded={isOpen}\n aria-label={toggleLabel}\n >\n <Icon name=\"chevron-right\" size={16} />\n </StyledSidebarGroupChevron>\n </StyledSidebarGroupRow>\n ) : (\n <StyledSidebarGroupButton\n type=\"button\"\n onClick={toggle}\n $isActive={groupActive}\n $isOpen={isOpen}\n style={indent}\n aria-expanded={isOpen}\n aria-label={toggleLabel}\n >\n {link.icon && <Icon name={link.icon} size={16} />}\n {link.title}\n <Icon name=\"chevron-right\" size={16} />\n </StyledSidebarGroupButton>\n )}\n <StyledSidebarGroupContent $isOpen={isOpen}>\n {children.map((child: NavItemLink, index: number) => (\n <SidebarNavLink\n key={index}\n link={child}\n depth={depth + 1}\n pathname={pathname}\n onNavigate={onNavigate}\n />\n ))}\n </StyledSidebarGroupContent>\n </li>\n );\n}\n\nfunction SideBar({ result }: SideBarProps) {\n const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);\n const hasSectionBar = useContext(SectionBarContext);\n const pathname = usePathname();\n\n useLockBodyScroll(isMobileMenuOpen);\n\n return (\n <DocsSidebar>\n <StyleMobileBar\n onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}\n $isActive={isMobileMenuOpen}\n aria-label={\n isMobileMenuOpen ? \"Close navigation menu\" : \"Open navigation menu\"\n }\n aria-expanded={isMobileMenuOpen}\n >\n <StyledMobileBurger $isActive={isMobileMenuOpen} />\n </StyleMobileBar>\n\n <StyledSidebar\n $isActive={isMobileMenuOpen}\n $hasSectionBar={hasSectionBar}\n >\n {result &&\n result.map((item: NavItem, index: number) => {\n return (\n <StyledSidebarList key={index}>\n <StyledSidebarListItem>\n <StyledStrong>\n {item.icon && <Icon name={item.icon} size={16} />}\n {item.label}\n </StyledStrong>{\" \"}\n </StyledSidebarListItem>\n <li>\n <Space $size={20} />\n </li>\n {item.links &&\n item.links.map((link: NavItemLink, indexChild: number) => (\n <SidebarNavLink\n key={indexChild}\n link={link}\n depth={0}\n pathname={pathname}\n onNavigate={() => setIsMobileMenuOpen(false)}\n />\n ))}\n <li aria-hidden=\"true\">\n <Space $size={20} />\n </li>\n </StyledSidebarList>\n );\n })}\n <StyledSidebarFooter>\n <Flex $xsJustifyContent=\"flex-start\" $lgJustifyContent=\"flex-end\">\n <ThemeToggle />\n </Flex>\n </StyledSidebarFooter>\n </StyledSidebar>\n </DocsSidebar>\n );\n}\n\nexport { SideBar };\n";
|
|
@@ -10,26 +10,144 @@ import {
|
|
|
10
10
|
StyledSidebarListItem,
|
|
11
11
|
StyledStrong,
|
|
12
12
|
StyledSidebarListItemLink,
|
|
13
|
+
StyledSidebarGroupButton,
|
|
14
|
+
StyledSidebarGroupRow,
|
|
15
|
+
StyledSidebarGroupLink,
|
|
16
|
+
StyledSidebarGroupChevron,
|
|
17
|
+
StyledSidebarGroupContent,
|
|
13
18
|
StyledSidebarFooter,
|
|
14
19
|
StyleMobileBar,
|
|
15
20
|
StyledMobileBurger,
|
|
16
21
|
} from "@/components/layout/DocsComponents";
|
|
22
|
+
import { Icon } from "@/components/layout/Icon";
|
|
17
23
|
import { useLockBodyScroll } from "@/components/LockBodyScroll";
|
|
18
24
|
|
|
25
|
+
// A link can be a leaf (slug + title) or a group with nested children. Both the
|
|
26
|
+
// category icon and the per-link icon are optional Lucide names.
|
|
27
|
+
type NavItemLink = {
|
|
28
|
+
slug?: string;
|
|
29
|
+
title: string;
|
|
30
|
+
icon?: string;
|
|
31
|
+
links?: NavItemLink[];
|
|
32
|
+
};
|
|
33
|
+
|
|
19
34
|
type NavItem = {
|
|
20
35
|
label: string;
|
|
36
|
+
icon?: string;
|
|
21
37
|
links: NavItemLink[];
|
|
22
38
|
};
|
|
23
39
|
|
|
24
|
-
type NavItemLink = {
|
|
25
|
-
slug: string;
|
|
26
|
-
title: string;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
40
|
interface SideBarProps {
|
|
30
41
|
result: NavItem[];
|
|
31
42
|
}
|
|
32
43
|
|
|
44
|
+
function linkContainsActivePath(link: NavItemLink, pathname: string): boolean {
|
|
45
|
+
if (link.slug !== undefined && pathname === \`/\${link.slug}\`) {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
return (link.links ?? []).some((child) =>
|
|
49
|
+
linkContainsActivePath(child, pathname),
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function SidebarNavLink({
|
|
54
|
+
link,
|
|
55
|
+
depth,
|
|
56
|
+
pathname,
|
|
57
|
+
onNavigate,
|
|
58
|
+
}: {
|
|
59
|
+
link: NavItemLink;
|
|
60
|
+
depth: number;
|
|
61
|
+
pathname: string;
|
|
62
|
+
onNavigate: () => void;
|
|
63
|
+
}) {
|
|
64
|
+
const children = link.links ?? [];
|
|
65
|
+
const hasChildren = children.length > 0;
|
|
66
|
+
const href = link.slug !== undefined ? \`/\${link.slug}\` : undefined;
|
|
67
|
+
const isActive = href !== undefined && pathname === href;
|
|
68
|
+
const indent = { paddingLeft: \`\${20 + depth * 14}px\` };
|
|
69
|
+
|
|
70
|
+
// Open collapsible groups that contain the active page so deep links land
|
|
71
|
+
// with their ancestors already expanded.
|
|
72
|
+
const [isOpen, setIsOpen] = useState(
|
|
73
|
+
hasChildren
|
|
74
|
+
? children.some((child) => linkContainsActivePath(child, pathname))
|
|
75
|
+
: false,
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
if (!hasChildren) {
|
|
79
|
+
return (
|
|
80
|
+
<StyledSidebarListItem>
|
|
81
|
+
<StyledSidebarListItemLink
|
|
82
|
+
href={href ?? "#"}
|
|
83
|
+
$isActive={isActive}
|
|
84
|
+
onClick={onNavigate}
|
|
85
|
+
style={indent}
|
|
86
|
+
>
|
|
87
|
+
{link.icon && <Icon name={link.icon} size={16} />}
|
|
88
|
+
{link.title}
|
|
89
|
+
</StyledSidebarListItemLink>
|
|
90
|
+
</StyledSidebarListItem>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const toggle = () => setIsOpen((prev) => !prev);
|
|
95
|
+
const toggleLabel = isOpen
|
|
96
|
+
? \`Collapse \${link.title}\`
|
|
97
|
+
: \`Expand \${link.title}\`;
|
|
98
|
+
const groupActive = linkContainsActivePath(link, pathname);
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
<li>
|
|
102
|
+
{href !== undefined ? (
|
|
103
|
+
<StyledSidebarGroupRow
|
|
104
|
+
$isActive={groupActive}
|
|
105
|
+
$isOpen={isOpen}
|
|
106
|
+
style={indent}
|
|
107
|
+
>
|
|
108
|
+
<StyledSidebarGroupLink href={href} onClick={onNavigate}>
|
|
109
|
+
{link.icon && <Icon name={link.icon} size={16} />}
|
|
110
|
+
{link.title}
|
|
111
|
+
</StyledSidebarGroupLink>
|
|
112
|
+
<StyledSidebarGroupChevron
|
|
113
|
+
type="button"
|
|
114
|
+
onClick={toggle}
|
|
115
|
+
aria-expanded={isOpen}
|
|
116
|
+
aria-label={toggleLabel}
|
|
117
|
+
>
|
|
118
|
+
<Icon name="chevron-right" size={16} />
|
|
119
|
+
</StyledSidebarGroupChevron>
|
|
120
|
+
</StyledSidebarGroupRow>
|
|
121
|
+
) : (
|
|
122
|
+
<StyledSidebarGroupButton
|
|
123
|
+
type="button"
|
|
124
|
+
onClick={toggle}
|
|
125
|
+
$isActive={groupActive}
|
|
126
|
+
$isOpen={isOpen}
|
|
127
|
+
style={indent}
|
|
128
|
+
aria-expanded={isOpen}
|
|
129
|
+
aria-label={toggleLabel}
|
|
130
|
+
>
|
|
131
|
+
{link.icon && <Icon name={link.icon} size={16} />}
|
|
132
|
+
{link.title}
|
|
133
|
+
<Icon name="chevron-right" size={16} />
|
|
134
|
+
</StyledSidebarGroupButton>
|
|
135
|
+
)}
|
|
136
|
+
<StyledSidebarGroupContent $isOpen={isOpen}>
|
|
137
|
+
{children.map((child: NavItemLink, index: number) => (
|
|
138
|
+
<SidebarNavLink
|
|
139
|
+
key={index}
|
|
140
|
+
link={child}
|
|
141
|
+
depth={depth + 1}
|
|
142
|
+
pathname={pathname}
|
|
143
|
+
onNavigate={onNavigate}
|
|
144
|
+
/>
|
|
145
|
+
))}
|
|
146
|
+
</StyledSidebarGroupContent>
|
|
147
|
+
</li>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
33
151
|
function SideBar({ result }: SideBarProps) {
|
|
34
152
|
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
|
35
153
|
const hasSectionBar = useContext(SectionBarContext);
|
|
@@ -59,25 +177,24 @@ function SideBar({ result }: SideBarProps) {
|
|
|
59
177
|
return (
|
|
60
178
|
<StyledSidebarList key={index}>
|
|
61
179
|
<StyledSidebarListItem>
|
|
62
|
-
<StyledStrong>
|
|
180
|
+
<StyledStrong>
|
|
181
|
+
{item.icon && <Icon name={item.icon} size={16} />}
|
|
182
|
+
{item.label}
|
|
183
|
+
</StyledStrong>{" "}
|
|
63
184
|
</StyledSidebarListItem>
|
|
64
185
|
<li>
|
|
65
186
|
<Space $size={20} />
|
|
66
187
|
</li>
|
|
67
188
|
{item.links &&
|
|
68
|
-
item.links.map((link: NavItemLink, indexChild: number) =>
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
</StyledSidebarListItemLink>
|
|
78
|
-
</StyledSidebarListItem>
|
|
79
|
-
);
|
|
80
|
-
})}
|
|
189
|
+
item.links.map((link: NavItemLink, indexChild: number) => (
|
|
190
|
+
<SidebarNavLink
|
|
191
|
+
key={indexChild}
|
|
192
|
+
link={link}
|
|
193
|
+
depth={0}
|
|
194
|
+
pathname={pathname}
|
|
195
|
+
onNavigate={() => setIsMobileMenuOpen(false)}
|
|
196
|
+
/>
|
|
197
|
+
))}
|
|
81
198
|
<li aria-hidden="true">
|
|
82
199
|
<Space $size={20} />
|
|
83
200
|
</li>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const docsComponentsTemplate = "\"use client\";\nimport React, { createContext, useContext } from \"react\";\nimport styled, { css } from \"styled-components\";\nimport {\n resetButton,\n styledSmall,\n styledStrong,\n styledText,\n} from \"cherry-styled-components\";\nimport Link from \"next/link\";\nimport { mq, Theme } from \"@/app/theme\";\nimport {\n styledAnchor,\n styledTable,\n stylesLists,\n} from \"@/components/layout/SharedStyled\";\nimport { ChatContext } from \"@/components/Chat\";\n\nconst SectionBarContext = createContext(false);\n\nfunction SectionBarProvider({\n hasSectionBar,\n children,\n}: {\n hasSectionBar: boolean;\n children: React.ReactNode;\n}) {\n return (\n <SectionBarContext.Provider value={hasSectionBar}>\n {children}\n </SectionBarContext.Provider>\n );\n}\n\ninterface DocsProps {\n children: React.ReactNode;\n}\n\nconst StyledDocsWrapper = styled.main<{ theme: Theme }>`\n position: relative;\n`;\n\nconst StyledDocsSidebar = styled.div<{ theme: Theme }>`\n clear: both;\n`;\n\nconst StyledDocsContainer = styled.div<{ theme: Theme; $isChatOpen?: boolean }>`\n position: relative;\n padding: 0 20px 100px 20px;\n width: 100%;\n ${({ theme }) => styledText(theme)};\n transition: all 0.3s ease;\n\n ${mq(\"lg\")} {\n padding: 0 300px 80px 300px;\n\n ${({ $isChatOpen }) =>\n $isChatOpen &&\n css`\n padding: 0 440px 80px 300px;\n `}\n }\n\n & p {\n color: ${({ theme }) => theme.colors.grayDark};\n hyphens: auto;\n }\n\n & pre {\n max-width: 100%;\n }\n\n ${styledAnchor};\n ${stylesLists};\n ${styledTable};\n\n & img,\n & video,\n & iframe {\n max-width: 100%;\n border-radius: ${({ theme }) => theme.spacing.radius.lg};\n }\n\n & code:not([class]) {\n background: ${({ theme }) =>\n `color-mix(in srgb, ${theme.colors.primaryLight} 20%, transparent)`};\n color: ${({ theme }) => theme.colors.dark};\n padding: 2px 4px;\n border-radius: ${({ theme }) => theme.spacing.radius.xs};\n white-space: pre;\n }\n\n & .lucide {\n color: ${({ theme }) => theme.colors.primary};\n }\n\n & .aspect-video {\n aspect-ratio: 16 / 9;\n border-radius: ${({ theme }) => theme.spacing.radius.lg};\n }\n`;\n\nexport const StyledMarkdownContainer = styled.div`\n display: flex;\n flex-direction: column;\n gap: 20px;\n flex-wrap: wrap;\n flex: 1;\n max-width: 640px;\n margin: auto;\n`;\n\ninterface Props {\n theme?: Theme;\n $isActive?: boolean;\n $hasSectionBar?: boolean;\n}\n\nexport const StyledSidebar = styled.nav<Props>`\n position: fixed;\n overflow-y: auto;\n max-height: calc(\n 100dvh - ${({ $hasSectionBar }) => ($hasSectionBar ? 104 : 62)}px\n );\n width: 100%;\n z-index: 99;\n top: ${({ $hasSectionBar }) => ($hasSectionBar ? 104 : 62)}px;\n height: 100%;\n padding: 20px;\n opacity: 0;\n pointer-events: none;\n transition: all 0.3s ease;\n transform: translateY(30px);\n left: 0;\n background: ${({ theme }) => theme.colors.light};\n -webkit-overflow-scrolling: touch;\n display: flex;\n flex-direction: column;\n\n &::-webkit-scrollbar {\n display: none;\n }\n\n ${mq(\"lg\")} {\n border-right: solid 1px ${({ theme }) => theme.colors.grayLight};\n transition: none;\n max-height: 100dvh;\n width: 220px;\n background: transparent;\n padding: 82px 20px 20px 20px;\n opacity: 1;\n pointer-events: all;\n transform: translateY(0);\n background: ${({ theme }) =>\n `color-mix(in srgb, ${theme.colors.primaryLight} 5%, transparent)`};\n top: 0;\n width: 280px;\n }\n\n ${({ $isActive }) =>\n $isActive &&\n css`\n transform: translateY(0);\n opacity: 1;\n pointer-events: all;\n `}\n`;\n\nexport const StyledSidebarFooter = styled.div`\n padding: 22px 20px;\n position: sticky;\n border-top: 1px solid ${({ theme }) => theme.colors.grayLight};\n background: ${({ theme }) =>\n `color-mix(in srgb, ${theme.colors.primaryLight} 5%, transparent)`};\n margin: 0 -20px -20px;\n bottom: -20px;\n backdrop-filter: blur(10px);\n\n ${mq(\"lg\")} {\n padding: 16px 20px;\n }\n`;\n\nexport const StyledIndexSidebar = styled.ul<{ theme: Theme }>`\n display: none;\n list-style: none;\n margin: 0;\n padding: 0;\n position: fixed;\n top: 0;\n right: 0;\n width: 280px;\n height: 100dvh;\n overflow-y: auto;\n z-index: 1;\n padding: 82px 20px 20px 20px;\n background: ${({ theme }) => theme.colors.light};\n border-left: solid 1px ${({ theme }) => theme.colors.grayLight};\n -webkit-overflow-scrolling: touch;\n\n &::-webkit-scrollbar {\n display: none;\n }\n\n ${mq(\"lg\")} {\n display: block;\n }\n\n & li {\n padding: 5px 0;\n }\n`;\n\nexport const StyledIndexSidebarLabel = styled.span<{ theme: Theme }>`\n ${({ theme }) => styledSmall(theme)};\n color: ${({ theme }) => theme.colors.grayDark};\n`;\n\nexport const StyledIndexSidebarLi = styled.li<{\n theme: Theme;\n $isActive: boolean;\n}>`\n &::before {\n content: \"\";\n display: block;\n position: absolute;\n left: 0;\n height: 20px;\n width: 1px;\n background: transparent;\n transition: all 0.3s ease;\n }\n\n ${({ $isActive, theme }) =>\n $isActive &&\n css`\n &::before {\n background: ${theme.colors.primary};\n }\n `}\n`;\n\nexport const StyledIndexSidebarLink = styled.a<{\n theme: Theme;\n $isActive: boolean;\n}>`\n ${({ theme }) => styledSmall(theme)};\n color: ${({ theme, $isActive }) =>\n $isActive ? theme.colors.primary : theme.colors.dark};\n font-weight: ${({ $isActive }) => ($isActive ? \"600\" : \"400\")};\n text-decoration: none;\n transition: all 0.3s ease;\n\n &:hover {\n color: ${({ theme }) => theme.colors.primary};\n }\n`;\n\nexport const StyledSidebarList = styled.ul`\n list-style: none;\n margin: 0;\n padding: 0;\n\n &:last-of-type {\n margin-bottom: auto;\n }\n`;\n\nexport const StyledStrong = styled.strong<{ theme: Theme }>`\n font-weight: 600;\n ${({ theme }) => styledStrong(theme)};\n color: ${({ theme }) => theme.colors.accentStrong};\n`;\n\nexport const StyledSidebarListItem = styled.li`\n display: flex;\n gap: 10px;\n clear: both;\n`;\n\nexport const StyledSidebarListItemLink = styled(Link)<Props>`\n text-decoration: none;\n font-size: ${({ theme }) => theme.fontSizes.small.lg};\n line-height: 1.6;\n color: ${({ theme }) => theme.colors.accentMuted};\n padding: 5px 0 5px 20px;\n display: flex;\n transition: all 0.3s ease;\n border-left: solid 1px ${({ theme }) => theme.colors.grayLight};\n\n &:hover {\n color: ${({ theme }) => theme.colors.accent};\n border-color: ${({ theme }) => theme.colors.primary};\n }\n\n ${({ $isActive, theme }) =>\n $isActive &&\n `\n\t\t\tcolor: ${theme.colors.accentStrong};\n\t\t\tborder-color: ${theme.colors.primary};\n\t\t\tfont-weight: 600;\n\t`};\n`;\n\nexport const StyleMobileBar = styled.button<Props>`\n ${resetButton};\n position: fixed;\n z-index: 999;\n bottom: 0;\n right: 20px;\n font-size: ${({ theme }) => theme.fontSizes.strong.lg};\n line-height: ${({ theme }) => theme.fontSizes.strong.lg};\n box-shadow: ${({ theme }) => theme.shadows.sm};\n background: ${({ theme }) => theme.colors.primary};\n color: ${({ theme }) => theme.colors.surface};\n backdrop-filter: blur(10px);\n -webkit-backdrop-filter: blur(10px);\n padding: 10px;\n border-radius: 100px;\n margin: 0 0 20px 0;\n font-weight: 600;\n display: flex;\n justify-content: flex-start;\n width: auto;\n\n ${mq(\"lg\")} {\n display: none;\n }\n\n ${({ $isActive }) => $isActive && `position: fixed;`};\n`;\n\nexport const StyledMobileBurger = styled.span<Props>`\n display: block;\n margin: auto 0;\n width: 18px;\n height: 18px;\n position: relative;\n overflow: hidden;\n background: transparent;\n position: relative;\n transform: scale(0.8);\n\n &::before,\n &::after {\n content: \"\";\n display: block;\n position: absolute;\n width: 18px;\n height: 3px;\n border-radius: 3px;\n background: ${({ theme }) => theme.colors.surface};\n transition: all 0.3s ease;\n }\n\n &::before {\n top: 3px;\n }\n\n &::after {\n bottom: 3px;\n }\n\n ${({ $isActive }) =>\n $isActive &&\n css`\n &::before {\n transform: translateY(5px) rotate(45deg);\n }\n\n &::after {\n transform: translateY(-4px) rotate(-45deg);\n }\n `};\n`;\n\nexport const StyledMissingComponent = styled.div`\n background: ${({ theme }) => theme.colors.error};\n border-radius: ${({ theme }) => theme.spacing.radius.lg};\n padding: 20px;\n font-size: ${({ theme }) => theme.fontSizes.small.lg};\n color: ${({ theme }) => theme.colors.surface};\n font-weight: 600;\n display: flex;\n gap: 10px;\n align-items: center;\n`;\n\ninterface DocsWrapperProps {\n children: React.ReactNode;\n}\n\nfunction DocsWrapper({ children }: DocsWrapperProps) {\n return <StyledDocsWrapper>{children}</StyledDocsWrapper>;\n}\n\nfunction DocsSidebar({ children }: DocsProps) {\n return <StyledDocsSidebar>{children}</StyledDocsSidebar>;\n}\n\nfunction DocsContainer({ children }: DocsProps) {\n const { isOpen } = useContext(ChatContext);\n\n return (\n <StyledDocsContainer $isChatOpen={isOpen}>{children}</StyledDocsContainer>\n );\n}\n\nexport {\n DocsWrapper,\n DocsSidebar,\n DocsContainer,\n SectionBarContext,\n SectionBarProvider,\n};\n";
|
|
1
|
+
export declare const docsComponentsTemplate = "\"use client\";\nimport React, { createContext, useContext } from \"react\";\nimport styled, { css } from \"styled-components\";\nimport {\n resetButton,\n styledSmall,\n styledStrong,\n styledText,\n} from \"cherry-styled-components\";\nimport Link from \"next/link\";\nimport { mq, Theme } from \"@/app/theme\";\nimport {\n styledAnchor,\n styledTable,\n stylesLists,\n} from \"@/components/layout/SharedStyled\";\nimport { ChatContext } from \"@/components/Chat\";\n\nconst SectionBarContext = createContext(false);\n\nfunction SectionBarProvider({\n hasSectionBar,\n children,\n}: {\n hasSectionBar: boolean;\n children: React.ReactNode;\n}) {\n return (\n <SectionBarContext.Provider value={hasSectionBar}>\n {children}\n </SectionBarContext.Provider>\n );\n}\n\ninterface DocsProps {\n children: React.ReactNode;\n}\n\nconst StyledDocsWrapper = styled.main<{ theme: Theme }>`\n position: relative;\n`;\n\nconst StyledDocsSidebar = styled.div<{ theme: Theme }>`\n clear: both;\n`;\n\nconst StyledDocsContainer = styled.div<{ theme: Theme; $isChatOpen?: boolean }>`\n position: relative;\n padding: 0 20px 100px 20px;\n width: 100%;\n ${({ theme }) => styledText(theme)};\n transition: all 0.3s ease;\n\n ${mq(\"lg\")} {\n padding: 0 300px 80px 300px;\n\n ${({ $isChatOpen }) =>\n $isChatOpen &&\n css`\n padding: 0 440px 80px 300px;\n `}\n }\n\n & p {\n color: ${({ theme }) => theme.colors.grayDark};\n hyphens: auto;\n }\n\n & pre {\n max-width: 100%;\n }\n\n ${styledAnchor};\n ${stylesLists};\n ${styledTable};\n\n & img,\n & video,\n & iframe {\n max-width: 100%;\n border-radius: ${({ theme }) => theme.spacing.radius.lg};\n }\n\n & code:not([class]) {\n background: ${({ theme }) =>\n `color-mix(in srgb, ${theme.colors.primaryLight} 20%, transparent)`};\n color: ${({ theme }) => theme.colors.dark};\n padding: 2px 4px;\n border-radius: ${({ theme }) => theme.spacing.radius.xs};\n white-space: pre;\n }\n\n & .lucide {\n color: ${({ theme }) => theme.colors.primary};\n }\n\n & .aspect-video {\n aspect-ratio: 16 / 9;\n border-radius: ${({ theme }) => theme.spacing.radius.lg};\n }\n`;\n\nexport const StyledMarkdownContainer = styled.div`\n display: flex;\n flex-direction: column;\n gap: 20px;\n flex-wrap: wrap;\n flex: 1;\n max-width: 640px;\n margin: auto;\n`;\n\ninterface Props {\n theme?: Theme;\n $isActive?: boolean;\n $isOpen?: boolean;\n $hasSectionBar?: boolean;\n}\n\nexport const StyledSidebar = styled.nav<Props>`\n position: fixed;\n overflow-y: auto;\n max-height: calc(\n 100dvh - ${({ $hasSectionBar }) => ($hasSectionBar ? 104 : 62)}px\n );\n width: 100%;\n z-index: 99;\n top: ${({ $hasSectionBar }) => ($hasSectionBar ? 104 : 62)}px;\n height: 100%;\n padding: 20px;\n opacity: 0;\n pointer-events: none;\n transition: all 0.3s ease;\n transform: translateY(30px);\n left: 0;\n background: ${({ theme }) => theme.colors.light};\n -webkit-overflow-scrolling: touch;\n display: flex;\n flex-direction: column;\n\n &::-webkit-scrollbar {\n display: none;\n }\n\n ${mq(\"lg\")} {\n border-right: solid 1px ${({ theme }) => theme.colors.grayLight};\n transition: none;\n max-height: 100dvh;\n width: 220px;\n background: transparent;\n padding: 82px 20px 20px 20px;\n opacity: 1;\n pointer-events: all;\n transform: translateY(0);\n background: ${({ theme }) =>\n `color-mix(in srgb, ${theme.colors.primaryLight} 5%, transparent)`};\n top: 0;\n width: 280px;\n }\n\n ${({ $isActive }) =>\n $isActive &&\n css`\n transform: translateY(0);\n opacity: 1;\n pointer-events: all;\n `}\n`;\n\nexport const StyledSidebarFooter = styled.div`\n padding: 22px 20px;\n position: sticky;\n border-top: 1px solid ${({ theme }) => theme.colors.grayLight};\n background: ${({ theme }) =>\n `color-mix(in srgb, ${theme.colors.primaryLight} 5%, transparent)`};\n margin: 0 -20px -20px;\n bottom: -20px;\n backdrop-filter: blur(10px);\n\n ${mq(\"lg\")} {\n padding: 16px 20px;\n }\n`;\n\nexport const StyledIndexSidebar = styled.ul<{ theme: Theme }>`\n display: none;\n list-style: none;\n margin: 0;\n padding: 0;\n position: fixed;\n top: 0;\n right: 0;\n width: 280px;\n height: 100dvh;\n overflow-y: auto;\n z-index: 1;\n padding: 82px 20px 20px 20px;\n background: ${({ theme }) => theme.colors.light};\n border-left: solid 1px ${({ theme }) => theme.colors.grayLight};\n -webkit-overflow-scrolling: touch;\n\n &::-webkit-scrollbar {\n display: none;\n }\n\n ${mq(\"lg\")} {\n display: block;\n }\n\n & li {\n padding: 5px 0;\n }\n`;\n\nexport const StyledIndexSidebarLabel = styled.span<{ theme: Theme }>`\n ${({ theme }) => styledSmall(theme)};\n color: ${({ theme }) => theme.colors.grayDark};\n`;\n\nexport const StyledIndexSidebarLi = styled.li<{\n theme: Theme;\n $isActive: boolean;\n}>`\n &::before {\n content: \"\";\n display: block;\n position: absolute;\n left: 0;\n height: 20px;\n width: 1px;\n background: transparent;\n transition: all 0.3s ease;\n }\n\n ${({ $isActive, theme }) =>\n $isActive &&\n css`\n &::before {\n background: ${theme.colors.primary};\n }\n `}\n`;\n\nexport const StyledIndexSidebarLink = styled.a<{\n theme: Theme;\n $isActive: boolean;\n}>`\n ${({ theme }) => styledSmall(theme)};\n color: ${({ theme, $isActive }) =>\n $isActive ? theme.colors.primary : theme.colors.dark};\n font-weight: ${({ $isActive }) => ($isActive ? \"600\" : \"400\")};\n text-decoration: none;\n transition: all 0.3s ease;\n\n &:hover {\n color: ${({ theme }) => theme.colors.primary};\n }\n`;\n\nexport const StyledSidebarList = styled.ul`\n list-style: none;\n margin: 0;\n padding: 0;\n\n &:last-of-type {\n margin-bottom: auto;\n }\n`;\n\nexport const StyledStrong = styled.strong<{ theme: Theme }>`\n font-weight: 600;\n ${({ theme }) => styledStrong(theme)};\n color: ${({ theme }) => theme.colors.accentStrong};\n display: inline-flex;\n align-items: center;\n gap: 8px;\n\n & svg {\n flex-shrink: 0;\n }\n`;\n\nexport const StyledSidebarListItem = styled.li`\n display: flex;\n gap: 10px;\n clear: both;\n`;\n\n// Shared appearance for every sidebar row - leaf links AND nested group\n// headers - so hover, active state, and the left rail read identically.\nconst sidebarRowStyles = css<Props>`\n text-decoration: none;\n font-size: ${({ theme }) => theme.fontSizes.small.lg};\n line-height: 1.6;\n color: ${({ theme }) => theme.colors.accentMuted};\n padding: 5px 0 5px 20px;\n display: flex;\n align-items: center;\n gap: 8px;\n flex: 1;\n min-width: 0;\n transition: all 0.3s ease;\n border-left: solid 1px ${({ theme }) => theme.colors.grayLight};\n\n & svg {\n flex-shrink: 0;\n }\n\n &:hover {\n color: ${({ theme }) => theme.colors.accent};\n border-color: ${({ theme }) => theme.colors.primary};\n }\n\n ${({ $isActive, theme }) =>\n $isActive &&\n css`\n color: ${theme.colors.accentStrong};\n border-color: ${theme.colors.primary};\n font-weight: 600;\n `};\n`;\n\n// The collapse chevron points right when closed and rotates to point down\n// when the group is open.\nconst sidebarChevron = css<Props>`\n & .lucide-chevron-right {\n margin-left: auto;\n transition: transform 0.3s ease;\n\n ${({ $isOpen }) =>\n $isOpen &&\n css`\n transform: rotate(90deg);\n `}\n }\n`;\n\nexport const StyledSidebarListItemLink = styled(Link)<Props>`\n ${sidebarRowStyles};\n`;\n\n// Nested navigation group. The header shares the link appearance/hover; a\n// non-navigable group is a single toggle button, while a group that is also a\n// page pairs a link with a chevron toggle. Children live in\n// StyledSidebarGroupContent, which animates open/closed via height 0 <-> auto\n// (enabled by interpolate-size in GlobalStyles, same as the Accordion).\nexport const StyledSidebarGroupButton = styled.button<Props>`\n ${resetButton};\n ${sidebarRowStyles};\n ${sidebarChevron};\n width: 100%;\n text-align: left;\n`;\n\nexport const StyledSidebarGroupRow = styled.div<Props>`\n ${sidebarRowStyles};\n ${sidebarChevron};\n`;\n\nexport const StyledSidebarGroupLink = styled(Link)`\n display: flex;\n align-items: center;\n gap: 8px;\n flex: 1;\n min-width: 0;\n color: inherit;\n text-decoration: none;\n\n & svg {\n flex-shrink: 0;\n }\n`;\n\nexport const StyledSidebarGroupChevron = styled.button`\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n padding: 0;\n border: 0;\n background: none;\n color: inherit;\n cursor: pointer;\n`;\n\nexport const StyledSidebarGroupContent = styled.ul<Props>`\n list-style: none;\n margin: 0;\n padding: 0;\n height: 0;\n overflow: clip;\n transition: all 0.3s ease;\n\n ${({ $isOpen }) =>\n $isOpen &&\n css`\n height: auto;\n `}\n`;\n\nexport const StyleMobileBar = styled.button<Props>`\n ${resetButton};\n position: fixed;\n z-index: 999;\n bottom: 0;\n right: 20px;\n font-size: ${({ theme }) => theme.fontSizes.strong.lg};\n line-height: ${({ theme }) => theme.fontSizes.strong.lg};\n box-shadow: ${({ theme }) => theme.shadows.sm};\n background: ${({ theme }) => theme.colors.primary};\n color: ${({ theme }) => theme.colors.surface};\n backdrop-filter: blur(10px);\n -webkit-backdrop-filter: blur(10px);\n padding: 10px;\n border-radius: 100px;\n margin: 0 0 20px 0;\n font-weight: 600;\n display: flex;\n justify-content: flex-start;\n width: auto;\n\n ${mq(\"lg\")} {\n display: none;\n }\n\n ${({ $isActive }) => $isActive && `position: fixed;`};\n`;\n\nexport const StyledMobileBurger = styled.span<Props>`\n display: block;\n margin: auto 0;\n width: 18px;\n height: 18px;\n position: relative;\n overflow: hidden;\n background: transparent;\n position: relative;\n transform: scale(0.8);\n\n &::before,\n &::after {\n content: \"\";\n display: block;\n position: absolute;\n width: 18px;\n height: 3px;\n border-radius: 3px;\n background: ${({ theme }) => theme.colors.surface};\n transition: all 0.3s ease;\n }\n\n &::before {\n top: 3px;\n }\n\n &::after {\n bottom: 3px;\n }\n\n ${({ $isActive }) =>\n $isActive &&\n css`\n &::before {\n transform: translateY(5px) rotate(45deg);\n }\n\n &::after {\n transform: translateY(-4px) rotate(-45deg);\n }\n `};\n`;\n\nexport const StyledMissingComponent = styled.div`\n background: ${({ theme }) => theme.colors.error};\n border-radius: ${({ theme }) => theme.spacing.radius.lg};\n padding: 20px;\n font-size: ${({ theme }) => theme.fontSizes.small.lg};\n color: ${({ theme }) => theme.colors.surface};\n font-weight: 600;\n display: flex;\n gap: 10px;\n align-items: center;\n`;\n\ninterface DocsWrapperProps {\n children: React.ReactNode;\n}\n\nfunction DocsWrapper({ children }: DocsWrapperProps) {\n return <StyledDocsWrapper>{children}</StyledDocsWrapper>;\n}\n\nfunction DocsSidebar({ children }: DocsProps) {\n return <StyledDocsSidebar>{children}</StyledDocsSidebar>;\n}\n\nfunction DocsContainer({ children }: DocsProps) {\n const { isOpen } = useContext(ChatContext);\n\n return (\n <StyledDocsContainer $isChatOpen={isOpen}>{children}</StyledDocsContainer>\n );\n}\n\nexport {\n DocsWrapper,\n DocsSidebar,\n DocsContainer,\n SectionBarContext,\n SectionBarProvider,\n};\n";
|