doccupine 0.0.125 → 0.0.126
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 +5 -5
- package/dist/lib/structures.js +14 -0
- package/dist/templates/components/Docs.d.ts +1 -1
- package/dist/templates/components/Docs.js +6 -2
- package/dist/templates/components/MDXComponents.d.ts +1 -1
- package/dist/templates/components/MDXComponents.js +2 -2
- package/dist/templates/components/MermaidPre.d.ts +1 -0
- package/dist/templates/components/MermaidPre.js +68 -0
- package/dist/templates/components/layout/DocsComponents.d.ts +1 -1
- package/dist/templates/components/layout/DocsComponents.js +0 -1
- package/dist/templates/components/layout/GlobalStyles.d.ts +1 -1
- package/dist/templates/components/layout/GlobalStyles.js +11 -0
- package/dist/templates/components/layout/Header.d.ts +1 -1
- package/dist/templates/components/layout/Header.js +1 -1
- package/dist/templates/components/layout/Mermaid.d.ts +1 -0
- package/dist/templates/components/layout/Mermaid.js +342 -0
- package/dist/templates/components/layout/Pictograms.d.ts +1 -1
- package/dist/templates/components/layout/Pictograms.js +15 -13
- package/dist/templates/mdx/components.mdx.d.ts +1 -1
- package/dist/templates/mdx/components.mdx.js +4 -1
- package/dist/templates/mdx/mermaid.mdx.d.ts +1 -0
- package/dist/templates/mdx/mermaid.mdx.js +142 -0
- package/dist/templates/mdx/navigation.mdx.d.ts +1 -1
- package/dist/templates/mdx/navigation.mdx.js +4 -2
- package/dist/templates/mdx/what-is-doccupine.mdx.d.ts +1 -0
- package/dist/templates/mdx/what-is-doccupine.mdx.js +68 -0
- package/dist/templates/package.js +7 -6
- package/dist/templates/utils/mermaid.d.ts +1 -0
- package/dist/templates/utils/mermaid.js +48 -0
- package/dist/templates/utils/parseCodeMeta.d.ts +1 -0
- package/dist/templates/utils/parseCodeMeta.js +32 -0
- package/dist/templates/utils/rehypeCodeMeta.d.ts +1 -0
- package/dist/templates/utils/rehypeCodeMeta.js +23 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -716,7 +716,7 @@ class MDXToNextJSGenerator {
|
|
|
716
716
|
await this.generatePageFromMDX(mdxFile);
|
|
717
717
|
}
|
|
718
718
|
if (isSectionIndex) {
|
|
719
|
-
await this.updateSectionIndex(sectionSlug, frontmatter, mdxContent);
|
|
719
|
+
await this.updateSectionIndex(sectionSlug, frontmatter, mdxContent, filePath);
|
|
720
720
|
}
|
|
721
721
|
}
|
|
722
722
|
/**
|
|
@@ -893,7 +893,7 @@ export default function Page() {
|
|
|
893
893
|
return (
|
|
894
894
|
<>
|
|
895
895
|
${jsonLd.element}
|
|
896
|
-
<Docs content={content} />
|
|
896
|
+
<Docs content={content} sourcePath={${JSON.stringify(mdxFile.path)}} />
|
|
897
897
|
</>
|
|
898
898
|
);
|
|
899
899
|
}
|
|
@@ -963,7 +963,7 @@ export default function Home() {
|
|
|
963
963
|
return (
|
|
964
964
|
<>
|
|
965
965
|
${homeJsonLd.element}
|
|
966
|
-
<Docs content={content} />
|
|
966
|
+
<Docs content={content} sourcePath="index.mdx" />
|
|
967
967
|
</>
|
|
968
968
|
);
|
|
969
969
|
}
|
|
@@ -972,7 +972,7 @@ export default function Home() {
|
|
|
972
972
|
await fs.ensureDir(path.dirname(homePath));
|
|
973
973
|
await fs.writeFile(homePath, indexContent, "utf8");
|
|
974
974
|
}
|
|
975
|
-
async updateSectionIndex(sectionSlug, frontmatter, mdxContent) {
|
|
975
|
+
async updateSectionIndex(sectionSlug, frontmatter, mdxContent, sourcePath) {
|
|
976
976
|
const metadataBlock = generateMetadataBlock({
|
|
977
977
|
title: frontmatter.title,
|
|
978
978
|
titleFallback: "Section",
|
|
@@ -1013,7 +1013,7 @@ export default function Page() {
|
|
|
1013
1013
|
return (
|
|
1014
1014
|
<>
|
|
1015
1015
|
${sectionJsonLd.element}
|
|
1016
|
-
<Docs content={content} />
|
|
1016
|
+
<Docs content={content} sourcePath={${JSON.stringify(sourcePath ?? `${sectionSlug}/index.mdx`)}} />
|
|
1017
1017
|
</>
|
|
1018
1018
|
);
|
|
1019
1019
|
}
|
package/dist/lib/structures.js
CHANGED
|
@@ -18,6 +18,11 @@ import { lockBodyScrollTemplate } from "../templates/components/LockBodyScroll.j
|
|
|
18
18
|
import { docsTemplate } from "../templates/components/Docs.js";
|
|
19
19
|
import { docsSideBarTemplate } from "../templates/components/DocsSideBar.js";
|
|
20
20
|
import { mdxComponentsTemplate } from "../templates/components/MDXComponents.js";
|
|
21
|
+
import { mermaidPreTemplate } from "../templates/components/MermaidPre.js";
|
|
22
|
+
import { mermaidViewTemplate } from "../templates/components/layout/Mermaid.js";
|
|
23
|
+
import { mermaidTemplate } from "../templates/utils/mermaid.js";
|
|
24
|
+
import { parseCodeMetaTemplate } from "../templates/utils/parseCodeMeta.js";
|
|
25
|
+
import { rehypeCodeMetaTemplate } from "../templates/utils/rehypeCodeMeta.js";
|
|
21
26
|
import { sectionNavProviderTemplate } from "../templates/components/SectionNavProvider.js";
|
|
22
27
|
import { postHogProviderTemplate } from "../templates/components/PostHogProvider.js";
|
|
23
28
|
import { postHogProviderLazyTemplate } from "../templates/components/PostHogProviderLazy.js";
|
|
@@ -95,6 +100,7 @@ import { indexMdxTemplate } from "../templates/mdx/index.mdx.js";
|
|
|
95
100
|
import { footerLinksMdxTemplate } from "../templates/mdx/footer-links.mdx.js";
|
|
96
101
|
import { listsAndTablesMdxTemplate } from "../templates/mdx/lists-and-tables.mdx.js";
|
|
97
102
|
import { mediaAndAssetsMdxTemplate } from "../templates/mdx/media-and-assets.mdx.js";
|
|
103
|
+
import { mermaidMdxTemplate } from "../templates/mdx/mermaid.mdx.js";
|
|
98
104
|
import { mcpMdxTemplate } from "../templates/mdx/model-context-protocol.mdx.js";
|
|
99
105
|
import { navigationMdxTemplate } from "../templates/mdx/navigation.mdx.js";
|
|
100
106
|
import { sectionsMdxTemplate } from "../templates/mdx/sections.mdx.js";
|
|
@@ -102,6 +108,7 @@ import { stepsMdxTemplate } from "../templates/mdx/steps.mdx.js";
|
|
|
102
108
|
import { tabsMdxTemplate } from "../templates/mdx/tabs.mdx.js";
|
|
103
109
|
import { themeMdxTemplate } from "../templates/mdx/theme.mdx.js";
|
|
104
110
|
import { updateMdxTemplate } from "../templates/mdx/update.mdx.js";
|
|
111
|
+
import { whatIsDoccupineMdxTemplate } from "../templates/mdx/what-is-doccupine.mdx.js";
|
|
105
112
|
import { platformIndexMdxTemplate } from "../templates/mdx/platform/index.mdx.js";
|
|
106
113
|
import { platformFileEditorMdxTemplate } from "../templates/mdx/platform/file-editor.mdx.js";
|
|
107
114
|
import { platformPublishingMdxTemplate } from "../templates/mdx/platform/publishing.mdx.js";
|
|
@@ -151,14 +158,18 @@ export const appStructure = {
|
|
|
151
158
|
"lib/posthog.ts": posthogServerTemplate,
|
|
152
159
|
"lib/siteGate.ts": siteGateTemplate,
|
|
153
160
|
"utils/branding.ts": brandingTemplate,
|
|
161
|
+
"utils/mermaid.ts": mermaidTemplate,
|
|
154
162
|
"utils/orderNavItems.ts": orderNavItemsTemplate,
|
|
163
|
+
"utils/parseCodeMeta.ts": parseCodeMetaTemplate,
|
|
155
164
|
"utils/rateLimit.ts": rateLimitTemplate,
|
|
165
|
+
"utils/rehypeCodeMeta.ts": rehypeCodeMetaTemplate,
|
|
156
166
|
"utils/config.ts": configTemplate,
|
|
157
167
|
"components/Chat.tsx": chatTemplate,
|
|
158
168
|
"components/LockBodyScroll.ts": lockBodyScrollTemplate,
|
|
159
169
|
"components/Docs.tsx": docsTemplate,
|
|
160
170
|
"components/DocsSideBar.tsx": docsSideBarTemplate,
|
|
161
171
|
"components/MDXComponents.tsx": mdxComponentsTemplate,
|
|
172
|
+
"components/MermaidPre.tsx": mermaidPreTemplate,
|
|
162
173
|
"components/SectionNavProvider.tsx": sectionNavProviderTemplate,
|
|
163
174
|
"components/PostHogProvider.tsx": postHogProviderTemplate,
|
|
164
175
|
"components/PostHogProviderLazy.tsx": postHogProviderLazyTemplate,
|
|
@@ -184,6 +195,7 @@ export const appStructure = {
|
|
|
184
195
|
"components/layout/GlobalStyles.ts": globalStylesTemplate,
|
|
185
196
|
"components/layout/Header.tsx": headerTemplate,
|
|
186
197
|
"components/layout/Icon.tsx": iconTemplate,
|
|
198
|
+
"components/layout/Mermaid.tsx": mermaidViewTemplate,
|
|
187
199
|
"components/layout/Pictograms.tsx": pictogramsTemplate,
|
|
188
200
|
"components/layout/SharedStyled.ts": sharedStyledTemplate,
|
|
189
201
|
"components/layout/NotFound.tsx": notFoundComponentTemplate,
|
|
@@ -228,6 +240,7 @@ export const startingDocsStructure = {
|
|
|
228
240
|
"footer-links.mdx": footerLinksMdxTemplate,
|
|
229
241
|
"lists-and-tables.mdx": listsAndTablesMdxTemplate,
|
|
230
242
|
"media-and-assets.mdx": mediaAndAssetsMdxTemplate,
|
|
243
|
+
"mermaid.mdx": mermaidMdxTemplate,
|
|
231
244
|
"model-context-protocol.mdx": mcpMdxTemplate,
|
|
232
245
|
"navigation.mdx": navigationMdxTemplate,
|
|
233
246
|
"sections.mdx": sectionsMdxTemplate,
|
|
@@ -235,6 +248,7 @@ export const startingDocsStructure = {
|
|
|
235
248
|
"tabs.mdx": tabsMdxTemplate,
|
|
236
249
|
"theme.mdx": themeMdxTemplate,
|
|
237
250
|
"update.mdx": updateMdxTemplate,
|
|
251
|
+
"what-is-doccupine.mdx": whatIsDoccupineMdxTemplate,
|
|
238
252
|
"platform/index.mdx": platformIndexMdxTemplate,
|
|
239
253
|
"platform/file-editor.mdx": platformFileEditorMdxTemplate,
|
|
240
254
|
"platform/publishing.mdx": platformPublishingMdxTemplate,
|
|
@@ -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\";\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";
|
|
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 { createMermaidPre } from \"@/components/MermaidPre\";\nimport { DocsSideBar } from \"@/components/DocsSideBar\";\nimport { ActionBar } from \"@/components/layout/ActionBar\";\nimport { createSlugger } from \"@/components/layout/Slug\";\nimport { rehypeCodeMeta } from \"@/utils/rehypeCodeMeta\";\n\ninterface DocsProps {\n content: string;\n sourcePath?: 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, sourcePath }: DocsProps) {\n const headings = extractHeadings(content);\n const components = useMDXComponents({ pre: createMermaidPre(sourcePath) });\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 rehypePlugins: [rehypeCodeMeta],\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";
|
|
@@ -8,12 +8,15 @@ import {
|
|
|
8
8
|
import { MDXRemote } from "next-mdx-remote/rsc";
|
|
9
9
|
import remarkGfm from "remark-gfm";
|
|
10
10
|
import { useMDXComponents } from "@/components/MDXComponents";
|
|
11
|
+
import { createMermaidPre } from "@/components/MermaidPre";
|
|
11
12
|
import { DocsSideBar } from "@/components/DocsSideBar";
|
|
12
13
|
import { ActionBar } from "@/components/layout/ActionBar";
|
|
13
14
|
import { createSlugger } from "@/components/layout/Slug";
|
|
15
|
+
import { rehypeCodeMeta } from "@/utils/rehypeCodeMeta";
|
|
14
16
|
|
|
15
17
|
interface DocsProps {
|
|
16
18
|
content: string;
|
|
19
|
+
sourcePath?: string;
|
|
17
20
|
}
|
|
18
21
|
|
|
19
22
|
interface Heading {
|
|
@@ -77,9 +80,9 @@ function MissingComponent({
|
|
|
77
80
|
);
|
|
78
81
|
}
|
|
79
82
|
|
|
80
|
-
function Docs({ content }: DocsProps) {
|
|
83
|
+
function Docs({ content, sourcePath }: DocsProps) {
|
|
81
84
|
const headings = extractHeadings(content);
|
|
82
|
-
const components = useMDXComponents({});
|
|
85
|
+
const components = useMDXComponents({ pre: createMermaidPre(sourcePath) });
|
|
83
86
|
|
|
84
87
|
const knownNames = Object.keys(components);
|
|
85
88
|
const usedNames = extractComponentNames(content);
|
|
@@ -108,6 +111,7 @@ function Docs({ content }: DocsProps) {
|
|
|
108
111
|
blockJS: false,
|
|
109
112
|
mdxOptions: {
|
|
110
113
|
remarkPlugins: [remarkGfm],
|
|
114
|
+
rehypePlugins: [rehypeCodeMeta],
|
|
111
115
|
},
|
|
112
116
|
}}
|
|
113
117
|
components={allComponents}
|
|
@@ -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\";\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\
|
|
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\nexport function 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\nexport function 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. Only consume a\n // slug when a label is present so a label-less <Update> does not crash the\n // slugger (Update itself renders gracefully without a label anchor).\n Update: (props: React.ComponentProps<typeof Update>) => (\n <Update {...props} id={props.label ? slug(props.label) : undefined} />\n ),\n Steps,\n Step,\n Button,\n ColorSwatch,\n ColorSwatchGroup,\n DemoTheme,\n Space,\n ...components,\n };\n}\n";
|
|
@@ -27,7 +27,7 @@ interface PreProps extends React.HTMLAttributes<HTMLPreElement> {
|
|
|
27
27
|
children?: React.ReactNode;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
function extractAllTextFromChildren(children: React.ReactNode): string {
|
|
30
|
+
export function extractAllTextFromChildren(children: React.ReactNode): string {
|
|
31
31
|
if (children == null) return "";
|
|
32
32
|
if (typeof children === "string") return children;
|
|
33
33
|
if (typeof children === "number") return String(children);
|
|
@@ -44,7 +44,7 @@ function extractAllTextFromChildren(children: React.ReactNode): string {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
// Map <pre><code class="language-xyz"> to our <Code /> component
|
|
47
|
-
function Pre(props: PreProps) {
|
|
47
|
+
export function Pre(props: PreProps) {
|
|
48
48
|
const child = React.Children.only(props.children) as React.ReactElement<{
|
|
49
49
|
className?: string;
|
|
50
50
|
children?: React.ReactNode;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const mermaidPreTemplate = "import React from \"react\";\nimport { Pre, extractAllTextFromChildren } from \"@/components/MDXComponents\";\nimport { Code as CodeBlock } from \"@/components/layout/Code\";\nimport { MermaidView, MermaidPlacement } from \"@/components/layout/Mermaid\";\nimport { parseCodeMeta } from \"@/utils/parseCodeMeta\";\nimport { renderDiagram } from \"@/utils/mermaid\";\n\nconst PLACEMENTS: MermaidPlacement[] = [\n \"top-left\",\n \"top-right\",\n \"bottom-left\",\n \"bottom-right\",\n];\n\ninterface PreProps extends React.HTMLAttributes<HTMLPreElement> {\n children?: React.ReactNode;\n}\n\nfunction toPlacement(value: unknown): MermaidPlacement | undefined {\n return typeof value === \"string\" && (PLACEMENTS as string[]).includes(value)\n ? (value as MermaidPlacement)\n : undefined;\n}\n\nfunction createMermaidPre(sourcePath?: string) {\n return function MermaidPre(props: PreProps) {\n const child = React.Children.only(props.children) as React.ReactElement<{\n className?: string;\n meta?: string;\n children?: React.ReactNode;\n }> | null;\n\n const isMermaid =\n child &&\n child.type === \"code\" &&\n /language-mermaid\\b/.test(child.props.className || \"\");\n\n if (child && isMermaid) {\n const code = extractAllTextFromChildren(child.props.children).replace(\n /\\n$/,\n \"\",\n );\n const meta = parseCodeMeta(child.props.meta);\n const result = renderDiagram(code);\n\n if (!result.ok) {\n console.warn(\n `[doccupine] Mermaid diagram in ${sourcePath ?? \"an MDX file\"} could not be rendered, falling back to a code block: ${result.error}`,\n );\n return <CodeBlock code={code} language=\"mermaid\" />;\n }\n\n return (\n <MermaidView\n svg={result.svg}\n width={result.width}\n placement={toPlacement(meta.placement)}\n actions={typeof meta.actions === \"boolean\" ? meta.actions : undefined}\n />\n );\n }\n\n return <Pre {...props} />;\n };\n}\n\nexport { createMermaidPre };\n";
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export const mermaidPreTemplate = `import React from "react";
|
|
2
|
+
import { Pre, extractAllTextFromChildren } from "@/components/MDXComponents";
|
|
3
|
+
import { Code as CodeBlock } from "@/components/layout/Code";
|
|
4
|
+
import { MermaidView, MermaidPlacement } from "@/components/layout/Mermaid";
|
|
5
|
+
import { parseCodeMeta } from "@/utils/parseCodeMeta";
|
|
6
|
+
import { renderDiagram } from "@/utils/mermaid";
|
|
7
|
+
|
|
8
|
+
const PLACEMENTS: MermaidPlacement[] = [
|
|
9
|
+
"top-left",
|
|
10
|
+
"top-right",
|
|
11
|
+
"bottom-left",
|
|
12
|
+
"bottom-right",
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
interface PreProps extends React.HTMLAttributes<HTMLPreElement> {
|
|
16
|
+
children?: React.ReactNode;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function toPlacement(value: unknown): MermaidPlacement | undefined {
|
|
20
|
+
return typeof value === "string" && (PLACEMENTS as string[]).includes(value)
|
|
21
|
+
? (value as MermaidPlacement)
|
|
22
|
+
: undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function createMermaidPre(sourcePath?: string) {
|
|
26
|
+
return function MermaidPre(props: PreProps) {
|
|
27
|
+
const child = React.Children.only(props.children) as React.ReactElement<{
|
|
28
|
+
className?: string;
|
|
29
|
+
meta?: string;
|
|
30
|
+
children?: React.ReactNode;
|
|
31
|
+
}> | null;
|
|
32
|
+
|
|
33
|
+
const isMermaid =
|
|
34
|
+
child &&
|
|
35
|
+
child.type === "code" &&
|
|
36
|
+
/language-mermaid\\b/.test(child.props.className || "");
|
|
37
|
+
|
|
38
|
+
if (child && isMermaid) {
|
|
39
|
+
const code = extractAllTextFromChildren(child.props.children).replace(
|
|
40
|
+
/\\n$/,
|
|
41
|
+
"",
|
|
42
|
+
);
|
|
43
|
+
const meta = parseCodeMeta(child.props.meta);
|
|
44
|
+
const result = renderDiagram(code);
|
|
45
|
+
|
|
46
|
+
if (!result.ok) {
|
|
47
|
+
console.warn(
|
|
48
|
+
\`[doccupine] Mermaid diagram in \${sourcePath ?? "an MDX file"} could not be rendered, falling back to a code block: \${result.error}\`,
|
|
49
|
+
);
|
|
50
|
+
return <CodeBlock code={code} language="mermaid" />;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<MermaidView
|
|
55
|
+
svg={result.svg}
|
|
56
|
+
width={result.width}
|
|
57
|
+
placement={toPlacement(meta.placement)}
|
|
58
|
+
actions={typeof meta.actions === "boolean" ? meta.actions : undefined}
|
|
59
|
+
/>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return <Pre {...props} />;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export { createMermaidPre };
|
|
68
|
+
`;
|
|
@@ -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 & kbd {\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 overflow-wrap: anywhere;\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 /* Buttons aren't covered by the global a:focus-visible ring, so this\n nav-row toggle carries its own copy of it. */\n &:focus-visible {\n outline: none;\n border-radius: ${({ theme }) => theme.spacing.radius.xs};\n box-shadow: 0 0 0 2px ${({ theme }) => theme.colors.primaryLight};\n }\n`;\n\nexport const StyledSidebarGroupRow = styled.div<Props>`\n ${sidebarRowStyles};\n ${sidebarChevron};\n\n /* The row is a plain div, so it never matches :focus-visible itself. Ring the\n whole row when its inner link or chevron takes keyboard focus instead, to\n match the leaf-row focus treatment. */\n &:has(:focus-visible) {\n border-radius: ${({ theme }) => theme.spacing.radius.xs};\n box-shadow: 0 0 0 2px ${({ theme }) => theme.colors.primaryLight};\n }\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 /* Ring is drawn on the parent row via :has(:focus-visible); suppress the\n global a:focus-visible glow so it isn't drawn twice. */\n &:focus-visible {\n outline: none;\n box-shadow: none;\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 /* Ring is drawn on the parent row via :has(:focus-visible). */\n &:focus-visible {\n outline: none;\n }\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";
|
|
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 & kbd {\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 overflow-wrap: anywhere;\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: 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 /* Buttons aren't covered by the global a:focus-visible ring, so this\n nav-row toggle carries its own copy of it. */\n &:focus-visible {\n outline: none;\n border-radius: ${({ theme }) => theme.spacing.radius.xs};\n box-shadow: 0 0 0 2px ${({ theme }) => theme.colors.primaryLight};\n }\n`;\n\nexport const StyledSidebarGroupRow = styled.div<Props>`\n ${sidebarRowStyles};\n ${sidebarChevron};\n\n /* The row is a plain div, so it never matches :focus-visible itself. Ring the\n whole row when its inner link or chevron takes keyboard focus instead, to\n match the leaf-row focus treatment. */\n &:has(:focus-visible) {\n border-radius: ${({ theme }) => theme.spacing.radius.xs};\n box-shadow: 0 0 0 2px ${({ theme }) => theme.colors.primaryLight};\n }\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 /* Ring is drawn on the parent row via :has(:focus-visible); suppress the\n global a:focus-visible glow so it isn't drawn twice. */\n &:focus-visible {\n outline: none;\n box-shadow: none;\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 /* Ring is drawn on the parent row via :has(:focus-visible). */\n &:focus-visible {\n outline: none;\n }\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";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const globalStylesTemplate = "\"use client\";\nimport { createGlobalStyle } from \"styled-components\";\nimport {\n colorsLight,\n colorsDark,\n shadowsLight,\n shadowsDark,\n} from \"@/app/theme\";\n\n// Build \"name: value;\" lines for every entry in a record. Used to emit\n// :root and :root.dark blocks from the resolved hex objects in theme.ts.\n// Custom theme.json overrides flow through automatically because they are\n// already merged into colorsLight / colorsDark.\nfunction toCssVars<T extends object>(prefix: string, record: T): string {\n return Object.entries(record)\n .map(([k, v]) => ` --${prefix}-${k}: ${v};`)\n .join(\"\\n\");\n}\n\nconst lightVars = [\n toCssVars(\"color\", colorsLight),\n toCssVars(\"shadow\", shadowsLight),\n // Semantic tokens \u2014 derived from the brand palette per mode. See the\n // SemanticColors interface in theme.ts for the intent of each.\n // These reference the palette via var() (not baked hex) so that any\n // runtime override of --color-primary* (e.g. the DemoTheme presets)\n // cascades into the semantic tokens automatically.\n ` --color-accent: var(--color-primaryDark);`,\n // accentStrong = accent shifted ~10% toward black (light mode) or white (dark\n // mode). color-mix in sRGB is not identical to polished's HSL darken/lighten\n // but the visual difference at 10% on a UI accent is imperceptible.\n ` --color-accentStrong: color-mix(in srgb, var(--color-primaryDark) 90%, black);`,\n ` --color-accentMuted: var(--color-primary);`,\n ` --color-surface: var(--color-light);`,\n].join(\"\\n\");\n\nconst darkVars = [\n toCssVars(\"color\", colorsDark),\n toCssVars(\"shadow\", shadowsDark),\n ` --color-accent: var(--color-primaryLight);`,\n ` --color-accentStrong: color-mix(in srgb, var(--color-primaryLight) 90%, white);`,\n ` --color-accentMuted: var(--color-grayDark);`,\n ` --color-surface: var(--color-dark);`,\n].join(\"\\n\");\n\nconst GlobalStyles = createGlobalStyle`\n:root {\n${lightVars}\n}\n\n:root.dark {\n${darkVars}\n}\n\nhtml,\nbody {\n margin: 0;\n padding: 0;\n min-height: 100%;\n background-color: var(--color-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 var(--color-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: var(--color-dark);\n}\n\na {\n color: var(--color-primary);\n}\n\n/* Keyboard-focus ring, defined once globally so links don't each re-declare it.\n :focus-visible keeps it keyboard-only (no ring on mouse click); box-shadow\n gives a soft primaryLight glow that follows the element's border-radius. The\n 6px radius mirrors theme.spacing.radius.xs so the ring rounds on links that\n have no radius of their own. Cherry link-buttons (.button-link) are excluded\n via :where() so the exclusion adds no specificity - this keeps the selector\n at (0,1,1) so bespoke per-component :focus-visible rules (inline content\n links, the section bar, the sidebar group rows) still override it. */\na:focus-visible:where(:not(.button-link)) {\n outline: none;\n border-radius: 6px;\n box-shadow: 0 0 0 2px var(--color-primaryLight);\n}\n\nol,\nul {\n list-style: none;\n margin: 0;\n padding: 0;\n max-width: 100%;\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/* Mode-conditional visibility helpers. ThemeToggle uses these to swap Sun\n and Moon icons; Header uses them to swap light/dark logos. Pure CSS so\n the swap happens via the active <html> class without JS or re-render. */\n:root.dark .light-only {\n display: none !important;\n}\n:root:not(.dark) .dark-only {\n display: none !important;\n}\n`;\n\nexport { GlobalStyles };\n";
|
|
1
|
+
export declare const globalStylesTemplate = "\"use client\";\nimport { createGlobalStyle } from \"styled-components\";\nimport {\n colorsLight,\n colorsDark,\n shadowsLight,\n shadowsDark,\n} from \"@/app/theme\";\n\n// Build \"name: value;\" lines for every entry in a record. Used to emit\n// :root and :root.dark blocks from the resolved hex objects in theme.ts.\n// Custom theme.json overrides flow through automatically because they are\n// already merged into colorsLight / colorsDark.\nfunction toCssVars<T extends object>(prefix: string, record: T): string {\n return Object.entries(record)\n .map(([k, v]) => ` --${prefix}-${k}: ${v};`)\n .join(\"\\n\");\n}\n\nconst lightVars = [\n toCssVars(\"color\", colorsLight),\n toCssVars(\"shadow\", shadowsLight),\n // Semantic tokens \u2014 derived from the brand palette per mode. See the\n // SemanticColors interface in theme.ts for the intent of each.\n // These reference the palette via var() (not baked hex) so that any\n // runtime override of --color-primary* (e.g. the DemoTheme presets)\n // cascades into the semantic tokens automatically.\n ` --color-accent: var(--color-primaryDark);`,\n // accentStrong = accent shifted ~10% toward black (light mode) or white (dark\n // mode). color-mix in sRGB is not identical to polished's HSL darken/lighten\n // but the visual difference at 10% on a UI accent is imperceptible.\n ` --color-accentStrong: color-mix(in srgb, var(--color-primaryDark) 90%, black);`,\n ` --color-accentMuted: var(--color-primary);`,\n ` --color-surface: var(--color-light);`,\n].join(\"\\n\");\n\nconst darkVars = [\n toCssVars(\"color\", colorsDark),\n toCssVars(\"shadow\", shadowsDark),\n ` --color-accent: var(--color-primaryLight);`,\n ` --color-accentStrong: color-mix(in srgb, var(--color-primaryLight) 90%, white);`,\n ` --color-accentMuted: var(--color-grayDark);`,\n ` --color-surface: var(--color-dark);`,\n].join(\"\\n\");\n\nconst diagramVars = [\n ` --mermaid-bg: var(--color-light);`,\n ` --mermaid-fg: var(--color-dark);`,\n ` --mermaid-line: var(--color-grayDark);`,\n ` --mermaid-accent: var(--color-accent);`,\n ` --mermaid-muted: var(--color-grayDark);`,\n ` --mermaid-surface: var(--color-grayLight);`,\n ` --mermaid-border: var(--color-gray);`,\n].join(\"\\n\");\n\nconst GlobalStyles = createGlobalStyle`\n:root {\n${lightVars}\n${diagramVars}\n}\n\n:root.dark {\n${darkVars}\n}\n\nhtml,\nbody {\n margin: 0;\n padding: 0;\n min-height: 100%;\n background-color: var(--color-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 var(--color-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: var(--color-dark);\n}\n\na {\n color: var(--color-primary);\n}\n\n/* Keyboard-focus ring, defined once globally so links don't each re-declare it.\n :focus-visible keeps it keyboard-only (no ring on mouse click); box-shadow\n gives a soft primaryLight glow that follows the element's border-radius. The\n 6px radius mirrors theme.spacing.radius.xs so the ring rounds on links that\n have no radius of their own. Cherry link-buttons (.button-link) are excluded\n via :where() so the exclusion adds no specificity - this keeps the selector\n at (0,1,1) so bespoke per-component :focus-visible rules (inline content\n links, the section bar, the sidebar group rows) still override it. */\na:focus-visible:where(:not(.button-link)) {\n outline: none;\n border-radius: 6px;\n box-shadow: 0 0 0 2px var(--color-primaryLight);\n}\n\nol,\nul {\n list-style: none;\n margin: 0;\n padding: 0;\n max-width: 100%;\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/* Mode-conditional visibility helpers. ThemeToggle uses these to swap Sun\n and Moon icons; Header uses them to swap light/dark logos. Pure CSS so\n the swap happens via the active <html> class without JS or re-render. */\n:root.dark .light-only {\n display: none !important;\n}\n:root:not(.dark) .dark-only {\n display: none !important;\n}\n`;\n\nexport { GlobalStyles };\n";
|
|
@@ -43,9 +43,20 @@ const darkVars = [
|
|
|
43
43
|
\` --color-surface: var(--color-dark);\`,
|
|
44
44
|
].join("\\n");
|
|
45
45
|
|
|
46
|
+
const diagramVars = [
|
|
47
|
+
\` --mermaid-bg: var(--color-light);\`,
|
|
48
|
+
\` --mermaid-fg: var(--color-dark);\`,
|
|
49
|
+
\` --mermaid-line: var(--color-grayDark);\`,
|
|
50
|
+
\` --mermaid-accent: var(--color-accent);\`,
|
|
51
|
+
\` --mermaid-muted: var(--color-grayDark);\`,
|
|
52
|
+
\` --mermaid-surface: var(--color-grayLight);\`,
|
|
53
|
+
\` --mermaid-border: var(--color-gray);\`,
|
|
54
|
+
].join("\\n");
|
|
55
|
+
|
|
46
56
|
const GlobalStyles = createGlobalStyle\`
|
|
47
57
|
:root {
|
|
48
58
|
\${lightVars}
|
|
59
|
+
\${diagramVars}
|
|
49
60
|
}
|
|
50
61
|
|
|
51
62
|
:root.dark {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const headerTemplate = "\"use client\";\nimport React from \"react\";\nimport { useCallback, useContext, useRef, useState } from \"react\";\nimport styled, { css } from \"styled-components\";\nimport Link from \"next/link\";\nimport { mq, Theme } from \"@/app/theme\";\nimport { useOnClickOutside } from \"cherry-styled-components\";\nimport { Search } from \"lucide-react\";\nimport { Logo } from \"@/components/layout/Pictograms\";\nimport { ChatContext, ChatButtonCTA } from \"@/components/Chat\";\nimport {\n SearchContext,\n SearchKbd,\n StyledSearchButton,\n} from \"@/components/SearchDocs\";\nimport themeJson from \"@/theme.json\";\n\nconst customThemeJson = themeJson as typeof themeJson & {\n logo?: { dark: string; light: string };\n};\n\nconst StyledHeader = styled.header<{ theme: Theme; $hasChildren: boolean }>`\n position: sticky;\n top: 0;\n margin: 0;\n z-index: 1000;\n width: 100%;\n border-bottom: solid 1px ${({ theme }) => theme.colors.grayLight};\n\n ${({ $hasChildren }) =>\n !$hasChildren &&\n css`\n ${mq(\"lg\")} {\n padding-bottom: 16px;\n padding-top: 16px;\n }\n `}\n\n &::before,\n &::after {\n display: block;\n content: \"\";\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n pointer-events: none;\n background: ${({ theme }) => theme.colors.light};\n z-index: -2;\n }\n\n &::after {\n background: ${({ theme }) =>\n `color-mix(in srgb, ${theme.colors.primaryLight} 5%, transparent)`};\n z-index: -1;\n }\n\n & .logo {\n display: flex;\n min-width: max-content;\n\n & svg,\n & img {\n margin: auto;\n height: auto;\n width: fit-content;\n min-width: fit-content;\n max-width: 182px;\n max-height: 30px;\n\n &
|
|
1
|
+
export declare const headerTemplate = "\"use client\";\nimport React from \"react\";\nimport { useCallback, useContext, useRef, useState } from \"react\";\nimport styled, { css } from \"styled-components\";\nimport Link from \"next/link\";\nimport { mq, Theme } from \"@/app/theme\";\nimport { useOnClickOutside } from \"cherry-styled-components\";\nimport { Search } from \"lucide-react\";\nimport { Logo } from \"@/components/layout/Pictograms\";\nimport { ChatContext, ChatButtonCTA } from \"@/components/Chat\";\nimport {\n SearchContext,\n SearchKbd,\n StyledSearchButton,\n} from \"@/components/SearchDocs\";\nimport themeJson from \"@/theme.json\";\n\nconst customThemeJson = themeJson as typeof themeJson & {\n logo?: { dark: string; light: string };\n};\n\nconst StyledHeader = styled.header<{ theme: Theme; $hasChildren: boolean }>`\n position: sticky;\n top: 0;\n margin: 0;\n z-index: 1000;\n width: 100%;\n border-bottom: solid 1px ${({ theme }) => theme.colors.grayLight};\n\n ${({ $hasChildren }) =>\n !$hasChildren &&\n css`\n ${mq(\"lg\")} {\n padding-bottom: 16px;\n padding-top: 16px;\n }\n `}\n\n &::before,\n &::after {\n display: block;\n content: \"\";\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n pointer-events: none;\n background: ${({ theme }) => theme.colors.light};\n z-index: -2;\n }\n\n &::after {\n background: ${({ theme }) =>\n `color-mix(in srgb, ${theme.colors.primaryLight} 5%, transparent)`};\n z-index: -1;\n }\n\n & .logo {\n display: flex;\n min-width: max-content;\n\n & svg,\n & img {\n margin: auto;\n height: auto;\n width: fit-content;\n min-width: fit-content;\n max-width: 182px;\n max-height: 30px;\n\n & [fill]:not(.ignore-fill) {\n fill: ${({ theme }) => theme.colors.primary};\n }\n }\n }\n`;\n\nconst StyledHeaderInner = styled.div<{ $hasChildren: boolean }>`\n display: flex;\n align-items: center;\n justify-content: space-between;\n flex-wrap: wrap;\n padding: 16px 0 0 20px;\n\n ${({ $hasChildren }) =>\n !$hasChildren &&\n css`\n padding-bottom: 16px;\n `}\n\n ${mq(\"lg\")} {\n flex-wrap: nowrap;\n padding: 0 20px;\n }\n`;\n\nconst StyledLeftWrapper = styled.div`\n display: flex;\n align-items: center;\n gap: 10px;\n min-width: fit-content;\n padding-right: 20px;\n\n ${mq(\"lg\")} {\n padding-right: 0;\n }\n`;\n\ninterface HeaderProps {\n children?: React.ReactNode;\n}\n\nfunction Header({ children }: HeaderProps) {\n const [isOptionActive, setIsOptionActive] = useState(false);\n const [isLangActive, setIsLangActive] = useState(false);\n\n const wrapperRef = useRef<HTMLSpanElement>(null);\n const elmRef = useRef<HTMLDivElement>(null);\n const langRef = useRef<HTMLSpanElement>(null);\n const closeMenu = useCallback(() => {\n setIsOptionActive(false);\n setIsLangActive(false);\n }, []);\n\n useOnClickOutside(\n [elmRef, wrapperRef],\n isOptionActive ? closeMenu : () => {},\n );\n useOnClickOutside([langRef, wrapperRef], isLangActive ? closeMenu : () => {});\n const { isChatActive } = useContext(ChatContext);\n const { openSearch } = useContext(SearchContext);\n\n return (\n <StyledHeader $hasChildren={children ? true : false} id=\"header\">\n <StyledHeaderInner $hasChildren={children ? true : false}>\n <Link href=\"/\" className=\"logo\" aria-label=\"Logo\">\n {customThemeJson.logo ? (\n <>\n {/* Both logos render; .light-only and .dark-only classes in\n GlobalStyles hide the inactive one based on the \"dark\" class\n on <html>. Avoids a JS-driven swap so no flash on first load. */}\n {/* eslint-disable-next-line @next/next/no-img-element */}\n <img\n className=\"light-only\"\n src={customThemeJson.logo.light}\n alt=\"Logo\"\n width=\"100\"\n height=\"100\"\n />\n {/* eslint-disable-next-line @next/next/no-img-element */}\n <img\n className=\"dark-only\"\n src={customThemeJson.logo.dark}\n alt=\"Logo\"\n width=\"100\"\n height=\"100\"\n />\n </>\n ) : (\n <Logo />\n )}\n </Link>\n {children}\n <StyledLeftWrapper>\n <StyledSearchButton onClick={openSearch} aria-label=\"Search docs\">\n <Search size={14} />\n <SearchKbd>⌘K</SearchKbd>\n </StyledSearchButton>\n {isChatActive && <ChatButtonCTA />}\n </StyledLeftWrapper>\n </StyledHeaderInner>\n </StyledHeader>\n );\n}\n\nexport { Header };\n";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const mermaidViewTemplate = "\"use client\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport styled, { css } from \"styled-components\";\nimport { interactiveStyles, resetButton } from \"cherry-styled-components\";\nimport { Theme } from \"@/app/theme\";\nimport { Icon } from \"@/components/layout/Icon\";\n\nconst MIN_SCALE = 0.5;\nconst MAX_SCALE = 4;\nconst SCALE_STEP = 0.25;\nconst PAN_STEP = 48;\nconst AUTO_ACTIONS_MIN_HEIGHT = 120;\n\nexport type MermaidPlacement =\n \"top-left\" | \"top-right\" | \"bottom-left\" | \"bottom-right\";\n\ninterface MermaidViewProps {\n svg: string;\n width?: number | null;\n placement?: MermaidPlacement;\n actions?: boolean;\n theme?: Theme;\n}\n\nconst Viewport = styled.div<{ $draggable: boolean; $dragging: boolean }>`\n overflow: hidden;\n padding: 16px;\n ${({ $draggable, $dragging }) =>\n $draggable &&\n css`\n cursor: ${$dragging ? \"grabbing\" : \"grab\"};\n user-select: none;\n `}\n`;\n\nconst Stage = styled.div<{\n $scale: number;\n $x: number;\n $y: number;\n $dragging: boolean;\n}>`\n transform: translate(${({ $x }) => $x}px, ${({ $y }) => $y}px)\n scale(${({ $scale }) => $scale});\n transform-origin: center center;\n transition: ${({ $dragging }) =>\n $dragging ? \"none\" : \"transform 0.15s ease-out\"};\n margin: 0 auto;\n\n @media (prefers-reduced-motion: reduce) {\n transition: none;\n }\n\n & > svg {\n display: block;\n width: 100%;\n height: auto;\n margin: 0 auto;\n }\n`;\n\nconst Figure = styled.figure<{ theme: Theme }>`\n position: relative;\n flex-shrink: 0;\n margin: 0;\n padding: 0;\n border: solid 1px ${({ theme }) => theme.colors.grayLight};\n border-radius: ${({ theme }) => theme.spacing.radius.lg};\n background: ${({ theme }) => theme.colors.light};\n overflow: hidden;\n\n &:fullscreen {\n border: none;\n border-radius: 0;\n\n ${Viewport} {\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n ${Stage} {\n width: 100%;\n height: 100%;\n }\n\n ${Stage} > svg {\n height: 100%;\n }\n }\n`;\n\nconst Controls = styled.div<{ theme: Theme; $placement: MermaidPlacement }>`\n position: absolute;\n ${({ $placement }) => {\n const [vertical, horizontal] = $placement.split(\"-\");\n return css`\n ${vertical}: 8px;\n ${horizontal}: 8px;\n `;\n }}\n display: grid;\n grid-template-columns: repeat(3, 24px);\n grid-template-rows: repeat(3, 24px);\n gap: 2px;\n padding: 4px;\n border: solid 1px ${({ theme }) => theme.colors.grayLight};\n border-radius: ${({ theme }) => theme.spacing.radius.xs};\n background: ${({ theme }) => theme.colors.light};\n box-shadow: ${({ theme }) => theme.shadows.sm};\n`;\n\nconst ControlButton = styled.button<{ theme: Theme }>`\n ${resetButton};\n ${interactiveStyles};\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 4px;\n color: ${({ theme }) => theme.colors.grayDark};\n cursor: pointer;\n\n &:hover:not(:disabled) {\n background: ${({ theme }) => theme.colors.grayLight};\n color: ${({ theme }) => theme.colors.dark};\n }\n\n &:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n }\n`;\n\nfunction MermaidView({\n svg,\n width,\n placement = \"bottom-right\",\n actions,\n ...props\n}: MermaidViewProps) {\n const [scale, setScale] = useState(1);\n const [offset, setOffset] = useState({ x: 0, y: 0 });\n const [autoShow, setAutoShow] = useState(false);\n const [dragging, setDragging] = useState(false);\n const [isFullscreen, setIsFullscreen] = useState(false);\n const [canFullscreen, setCanFullscreen] = useState(false);\n const viewportRef = useRef<HTMLDivElement>(null);\n const figureRef = useRef<HTMLElement>(null);\n const dragStart = useRef<{\n x: number;\n y: number;\n ox: number;\n oy: number;\n } | null>(null);\n\n useEffect(() => {\n if (actions !== undefined) return;\n const element = viewportRef.current;\n if (!element || typeof ResizeObserver === \"undefined\") return;\n\n const measure = () => {\n setAutoShow(\n element.getBoundingClientRect().height > AUTO_ACTIONS_MIN_HEIGHT,\n );\n };\n measure();\n const observer = new ResizeObserver(measure);\n observer.observe(element);\n return () => observer.disconnect();\n }, [actions]);\n\n useEffect(() => {\n setCanFullscreen(Boolean(document.fullscreenEnabled));\n const onChange = () =>\n setIsFullscreen(document.fullscreenElement === figureRef.current);\n document.addEventListener(\"fullscreenchange\", onChange);\n return () => document.removeEventListener(\"fullscreenchange\", onChange);\n }, []);\n\n const showControls = actions ?? autoShow;\n\n const zoom = useCallback((delta: number) => {\n setScale((current) =>\n Math.min(\n MAX_SCALE,\n Math.max(MIN_SCALE, Math.round((current + delta) * 100) / 100),\n ),\n );\n }, []);\n\n const pan = useCallback((x: number, y: number) => {\n setOffset((current) => ({ x: current.x + x, y: current.y + y }));\n }, []);\n\n const reset = useCallback(() => {\n setScale(1);\n setOffset({ x: 0, y: 0 });\n }, []);\n\n const toggleFullscreen = useCallback(() => {\n const element = figureRef.current;\n if (!element) return;\n if (document.fullscreenElement === element) {\n void document.exitFullscreen().catch(() => {});\n } else {\n void element.requestFullscreen().catch(() => {});\n }\n }, []);\n\n const handlePointerDown = (event: React.PointerEvent<HTMLDivElement>) => {\n if (!showControls || event.pointerType !== \"mouse\" || event.button !== 0) {\n return;\n }\n event.currentTarget.setPointerCapture(event.pointerId);\n dragStart.current = {\n x: event.clientX,\n y: event.clientY,\n ox: offset.x,\n oy: offset.y,\n };\n setDragging(true);\n };\n\n const handlePointerMove = (event: React.PointerEvent<HTMLDivElement>) => {\n const start = dragStart.current;\n if (!start) return;\n setOffset({\n x: start.ox + (event.clientX - start.x),\n y: start.oy + (event.clientY - start.y),\n });\n };\n\n const endDrag = (event: React.PointerEvent<HTMLDivElement>) => {\n if (!dragStart.current) return;\n dragStart.current = null;\n setDragging(false);\n if (event.currentTarget.hasPointerCapture(event.pointerId)) {\n event.currentTarget.releasePointerCapture(event.pointerId);\n }\n };\n\n const isReset = scale === 1 && offset.x === 0 && offset.y === 0;\n\n return (\n <Figure ref={figureRef} {...props}>\n <Viewport\n ref={viewportRef}\n $draggable={showControls}\n $dragging={dragging}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={endDrag}\n onPointerCancel={endDrag}\n >\n <Stage\n $scale={scale}\n $x={offset.x}\n $y={offset.y}\n $dragging={dragging}\n style={\n width && !isFullscreen ? { maxWidth: `${width}px` } : undefined\n }\n dangerouslySetInnerHTML={{ __html: svg }}\n />\n </Viewport>\n\n {showControls && (\n <Controls $placement={placement}>\n <ControlButton\n type=\"button\"\n aria-label=\"Zoom out\"\n onClick={() => zoom(-SCALE_STEP)}\n disabled={scale <= MIN_SCALE}\n >\n <Icon name=\"zoom-out\" size={14} />\n </ControlButton>\n <ControlButton\n type=\"button\"\n aria-label=\"Pan up\"\n onClick={() => pan(0, -PAN_STEP)}\n >\n <Icon name=\"chevron-up\" size={14} />\n </ControlButton>\n <ControlButton\n type=\"button\"\n aria-label=\"Zoom in\"\n onClick={() => zoom(SCALE_STEP)}\n disabled={scale >= MAX_SCALE}\n >\n <Icon name=\"zoom-in\" size={14} />\n </ControlButton>\n\n <ControlButton\n type=\"button\"\n aria-label=\"Pan left\"\n onClick={() => pan(-PAN_STEP, 0)}\n >\n <Icon name=\"chevron-left\" size={14} />\n </ControlButton>\n <ControlButton\n type=\"button\"\n aria-label=\"Reset view\"\n onClick={reset}\n disabled={isReset}\n >\n <Icon name=\"rotate-ccw\" size={14} />\n </ControlButton>\n <ControlButton\n type=\"button\"\n aria-label=\"Pan right\"\n onClick={() => pan(PAN_STEP, 0)}\n >\n <Icon name=\"chevron-right\" size={14} />\n </ControlButton>\n\n {canFullscreen ? (\n <ControlButton\n type=\"button\"\n aria-label={isFullscreen ? \"Exit full screen\" : \"Full screen\"}\n onClick={toggleFullscreen}\n >\n <Icon name={isFullscreen ? \"minimize\" : \"maximize\"} size={14} />\n </ControlButton>\n ) : (\n <span />\n )}\n <ControlButton\n type=\"button\"\n aria-label=\"Pan down\"\n onClick={() => pan(0, PAN_STEP)}\n >\n <Icon name=\"chevron-down\" size={14} />\n </ControlButton>\n <span />\n </Controls>\n )}\n </Figure>\n );\n}\n\nexport { MermaidView };\n";
|