doccupine 0.0.67 → 0.0.68

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- export declare const docsTemplate = "import { Flex } from \"cherry-styled-components\";\nimport {\n DocsContainer,\n StyledMarkdownContainer,\n} from \"@/components/layout/DocsComponents\";\nimport { MDXRemote } from \"next-mdx-remote/rsc\";\nimport remarkGfm from \"remark-gfm\";\nimport { useMDXComponents } from \"@/components/MDXComponents\";\nimport { DocsSideBar } from \"@/components/DocsSideBar\";\nimport { ActionBar } from \"@/components/layout/ActionBar\";\n\ninterface DocsProps {\n content: string;\n}\n\ninterface Heading {\n id: string;\n text: string;\n level: number;\n}\n\nfunction generateId(text: string): string {\n return text\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, \"\")\n .replace(/\\s+/g, \"-\")\n .trim();\n}\n\nfunction extractHeadings(content: string): Heading[] {\n const contentWithoutCodeBlocks = content.replace(/```[\\s\\S]*?```/g, \"\");\n const headingRegex = /^(#{1,6})\\s+(.+)$/gm;\n const headings: Heading[] = [];\n let match;\n\n while ((match = headingRegex.exec(contentWithoutCodeBlocks)) !== null) {\n const level = match[1].length;\n const text = match[2].trim();\n const id = generateId(text);\n headings.push({ id, text, level });\n }\n\n return headings;\n}\n\nfunction Docs({ content }: DocsProps) {\n const headings = extractHeadings(content);\n const components = useMDXComponents({});\n\n return (\n <>\n <DocsContainer>\n <ActionBar content={content}>\n <Flex $gap={20}>\n <StyledMarkdownContainer>\n {content && (\n <MDXRemote\n source={content}\n options={{\n blockJS: false,\n mdxOptions: {\n remarkPlugins: [remarkGfm],\n },\n }}\n components={components}\n />\n )}\n </StyledMarkdownContainer>\n </Flex>\n </ActionBar>\n </DocsContainer>\n <DocsSideBar headings={headings} />\n </>\n );\n}\n\nexport { Docs };\n";
1
+ 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 generateId(text: string): string {\n return text\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, \"\")\n .replace(/\\s+/g, \"-\")\n .trim();\n}\n\nfunction extractHeadings(content: string): Heading[] {\n const contentWithoutCodeBlocks = content.replace(/```[\\s\\S]*?```/g, \"\");\n const headingRegex = /^(#{1,6})\\s+(.+)$/gm;\n const headings: Heading[] = [];\n let match;\n\n while ((match = headingRegex.exec(contentWithoutCodeBlocks)) !== null) {\n const level = match[1].length;\n const text = match[2].trim();\n const id = generateId(text);\n headings.push({ id, text, level });\n }\n\n return headings;\n}\n\nfunction 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,\n}: {\n componentName: string;\n children?: React.ReactNode;\n}) {\n return (\n <StyledMissingComponent>\n Missing component: &lt;{componentName} /&gt;\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,7 +1,9 @@
1
- export const docsTemplate = `import { Flex } from "cherry-styled-components";
1
+ export const docsTemplate = `import React from "react";
2
+ import { Flex } from "cherry-styled-components";
2
3
  import {
3
4
  DocsContainer,
4
5
  StyledMarkdownContainer,
6
+ StyledMissingComponent,
5
7
  } from "@/components/layout/DocsComponents";
6
8
  import { MDXRemote } from "next-mdx-remote/rsc";
7
9
  import remarkGfm from "remark-gfm";
@@ -43,10 +45,51 @@ function extractHeadings(content: string): Heading[] {
43
45
  return headings;
44
46
  }
45
47
 
48
+ function extractComponentNames(source: string): string[] {
49
+ const stripped = source
50
+ .replace(/\`\`\`[\\s\\S]*?\`\`\`/g, "")
51
+ .replace(/\`[^\`]*\`/g, "");
52
+ const tagRegex = /<([A-Z][a-zA-Z0-9]*)/g;
53
+ const names = new Set<string>();
54
+ let match;
55
+ while ((match = tagRegex.exec(stripped)) !== null) {
56
+ names.add(match[1]);
57
+ }
58
+ return Array.from(names);
59
+ }
60
+
61
+ function MissingComponent({
62
+ componentName,
63
+ children,
64
+ }: {
65
+ componentName: string;
66
+ children?: React.ReactNode;
67
+ }) {
68
+ return (
69
+ <StyledMissingComponent>
70
+ Missing component: &lt;{componentName} /&gt;
71
+ </StyledMissingComponent>
72
+ );
73
+ }
74
+
46
75
  function Docs({ content }: DocsProps) {
47
76
  const headings = extractHeadings(content);
48
77
  const components = useMDXComponents({});
49
78
 
79
+ const knownNames = Object.keys(components);
80
+ const usedNames = extractComponentNames(content);
81
+ const missingNames = usedNames.filter((name) => !knownNames.includes(name));
82
+
83
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
84
+ const stubs: Record<string, React.ComponentType<any>> = {};
85
+ for (const name of missingNames) {
86
+ stubs[name] = ({ children }: { children?: React.ReactNode }) => (
87
+ <MissingComponent componentName={name}>{children}</MissingComponent>
88
+ );
89
+ }
90
+
91
+ const allComponents = { ...components, ...stubs };
92
+
50
93
  return (
51
94
  <>
52
95
  <DocsContainer>
@@ -62,7 +105,7 @@ function Docs({ content }: DocsProps) {
62
105
  remarkPlugins: [remarkGfm],
63
106
  },
64
107
  }}
65
- components={components}
108
+ components={allComponents}
66
109
  />
67
110
  )}
68
111
  </StyledMarkdownContainer>
@@ -1 +1 @@
1
- export declare const columnsTemplate = "\"use client\";\nimport styled from \"styled-components\";\nimport { mq, Theme } from \"cherry-styled-components\";\n\nconst StyledColumns = styled.div<{ theme: Theme; $columns?: number }>`\n display: flex;\n flex-direction: column;\n gap: 20px;\n\n ${mq(\"lg\")} {\n display: grid;\n grid-template-columns: repeat(${({ $columns }) => $columns ?? 1}, 1fr);\n }\n`;\n\ninterface ColumnsProps extends React.HTMLAttributes<HTMLDivElement> {\n children: React.ReactNode;\n cols?: number;\n}\n\nfunction Columns({ children, cols }: ColumnsProps) {\n return <StyledColumns $columns={cols}>{children}</StyledColumns>;\n}\n\nexport { Columns };\n";
1
+ export declare const columnsTemplate = "\"use client\";\nimport styled from \"styled-components\";\nimport { mq, Theme } from \"cherry-styled-components\";\n\nconst StyledColumns = styled.div<{ theme: Theme; $columns?: number }>`\n display: flex;\n flex-direction: column;\n gap: 20px;\n\n ${mq(\"lg\")} {\n ${({ $columns }) =>\n $columns && $columns >= 3\n ? `display: grid; grid-template-columns: repeat(2, 1fr);`\n : \"\"}\n }\n\n ${mq(\"xl\")} {\n display: grid;\n grid-template-columns: repeat(${({ $columns }) => $columns ?? 1}, 1fr);\n }\n`;\n\ninterface ColumnsProps extends React.HTMLAttributes<HTMLDivElement> {\n children: React.ReactNode;\n cols?: number;\n}\n\nfunction Columns({ children, cols }: ColumnsProps) {\n return <StyledColumns $columns={cols}>{children}</StyledColumns>;\n}\n\nexport { Columns };\n";
@@ -8,6 +8,13 @@ const StyledColumns = styled.div<{ theme: Theme; $columns?: number }>\`
8
8
  gap: 20px;
9
9
 
10
10
  \${mq("lg")} {
11
+ \${({ $columns }) =>
12
+ $columns && $columns >= 3
13
+ ? \`display: grid; grid-template-columns: repeat(2, 1fr);\`
14
+ : ""}
15
+ }
16
+
17
+ \${mq("xl")} {
11
18
  display: grid;
12
19
  grid-template-columns: repeat(\${({ $columns }) => $columns ?? 1}, 1fr);
13
20
  }
@@ -1 +1 @@
1
- export declare const docsComponentsTemplate = "\"use client\";\nimport { darken, lighten, rgba } from \"polished\";\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.div<{ 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 }) => rgba(theme.colors.primaryLight, 0.2)};\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 20px 80px 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\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 }) => rgba(theme.colors.primaryLight, 0.05)};\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 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\nexport const StyledStrong = styled.strong<{ theme: Theme }>`\n font-weight: 600;\n ${({ theme }) => styledStrong(theme)};\n color: ${({ theme }) =>\n theme.isDark\n ? lighten(0.1, theme.colors.primaryLight)\n : darken(0.1, theme.colors.primaryDark)};\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 }) =>\n theme.isDark ? theme.colors.grayDark : theme.colors.primary};\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 }) =>\n theme.isDark ? theme.colors.primaryLight : theme.colors.primaryDark};\n border-color: ${({ theme }) => theme.colors.primary};\n }\n\n ${({ $isActive, theme }) =>\n $isActive &&\n `\n\t\t\tcolor: ${theme.isDark ? lighten(0.1, theme.colors.primaryLight) : darken(0.1, theme.colors.primaryDark)};\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 }) =>\n theme.isDark\n ? rgba(theme.colors.grayLight, 0.7)\n : rgba(theme.colors.light, 0.7)};\n color: ${({ theme }) =>\n theme.isDark ? theme.colors.dark : theme.colors.primary};\n backdrop-filter: blur(10px);\n -webkit-backdrop-filter: blur(10px);\n padding: 20px;\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\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 }) =>\n theme.isDark ? theme.colors.dark : theme.colors.primary};\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\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 { darken, lighten, rgba } from \"polished\";\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.div<{ 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 }) => rgba(theme.colors.primaryLight, 0.2)};\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 20px 80px 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\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 }) => rgba(theme.colors.primaryLight, 0.05)};\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 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\nexport const StyledStrong = styled.strong<{ theme: Theme }>`\n font-weight: 600;\n ${({ theme }) => styledStrong(theme)};\n color: ${({ theme }) =>\n theme.isDark\n ? lighten(0.1, theme.colors.primaryLight)\n : darken(0.1, theme.colors.primaryDark)};\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 }) =>\n theme.isDark ? theme.colors.grayDark : theme.colors.primary};\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 }) =>\n theme.isDark ? theme.colors.primaryLight : theme.colors.primaryDark};\n border-color: ${({ theme }) => theme.colors.primary};\n }\n\n ${({ $isActive, theme }) =>\n $isActive &&\n `\n\t\t\tcolor: ${theme.isDark ? lighten(0.1, theme.colors.primaryLight) : darken(0.1, theme.colors.primaryDark)};\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 }) =>\n theme.isDark\n ? rgba(theme.colors.grayLight, 0.7)\n : rgba(theme.colors.light, 0.7)};\n color: ${({ theme }) =>\n theme.isDark ? theme.colors.dark : theme.colors.primary};\n backdrop-filter: blur(10px);\n -webkit-backdrop-filter: blur(10px);\n padding: 20px;\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\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 }) =>\n theme.isDark ? theme.colors.dark : theme.colors.primary};\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 }) =>\n theme.isDark ? theme.colors.dark : theme.colors.light};\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";
@@ -362,6 +362,19 @@ export const StyledMobileBurger = styled.span<Props>\`
362
362
  \`};
363
363
  \`;
364
364
 
365
+ export const StyledMissingComponent = styled.div\`
366
+ background: \${({ theme }) => theme.colors.error};
367
+ border-radius: \${({ theme }) => theme.spacing.radius.lg};
368
+ padding: 20px;
369
+ font-size: \${({ theme }) => theme.fontSizes.small.lg};
370
+ color: \${({ theme }) =>
371
+ theme.isDark ? theme.colors.dark : theme.colors.light};
372
+ font-weight: 600;
373
+ display: flex;
374
+ gap: 10px;
375
+ align-items: center;
376
+ \`;
377
+
365
378
  interface DocsWrapperProps {
366
379
  children: React.ReactNode;
367
380
  }
@@ -1 +1 @@
1
- export declare const orderNavItemsTemplate = "export interface PagesProps {\n slug: string;\n title: string;\n date: string;\n category: string;\n description?: string;\n path?: string;\n categoryOrder?: number;\n order?: number;\n section?: string;\n}\n\ninterface AccProps {\n [key: string]: {\n categoryOrder: number;\n pages: { date: string; slug: string; title: string; order: number }[];\n };\n}\n\nfunction transformPagesToGroupedStructure(pages: PagesProps[]) {\n const grouped = pages.reduce((acc: AccProps, page: PagesProps) => {\n const category = page.category || \"Uncategorized\";\n\n if (!acc[category]) {\n acc[category] = {\n categoryOrder: page.categoryOrder || 0,\n pages: [],\n };\n }\n\n acc[category].pages.push({\n date: page.date,\n slug: page.slug,\n title: page.title,\n order: page.order || 0,\n });\n\n return acc;\n }, {});\n\n return Object.entries(grouped)\n .sort(([, a], [, b]) => a.categoryOrder - b.categoryOrder)\n .map(([categoryName, categoryData], index) => ({\n slug: index === 0 ? \"\" : categoryName.toLowerCase().replace(/s+/g, \"-\"),\n label: categoryName,\n links: categoryData.pages.sort((a, b) => a.order - b.order),\n }));\n}\n\nexport { transformPagesToGroupedStructure };\n";
1
+ export declare const orderNavItemsTemplate = "export interface PagesProps {\n slug: string;\n title: string;\n date: string | null;\n category: string;\n description?: string;\n path?: string;\n categoryOrder?: number;\n order?: number;\n section?: string;\n}\n\ninterface AccProps {\n [key: string]: {\n categoryOrder: number;\n pages: {\n date: string | null;\n slug: string;\n title: string;\n order: number;\n }[];\n };\n}\n\nfunction transformPagesToGroupedStructure(pages: PagesProps[]) {\n const grouped = pages.reduce((acc: AccProps, page: PagesProps) => {\n const category = page.category || \"Uncategorized\";\n\n if (!acc[category]) {\n acc[category] = {\n categoryOrder: page.categoryOrder || 0,\n pages: [],\n };\n }\n\n acc[category].pages.push({\n date: page.date,\n slug: page.slug,\n title: page.title,\n order: page.order || 0,\n });\n\n return acc;\n }, {});\n\n return Object.entries(grouped)\n .sort(([, a], [, b]) => a.categoryOrder - b.categoryOrder)\n .map(([categoryName, categoryData], index) => ({\n slug: index === 0 ? \"\" : categoryName.toLowerCase().replace(/s+/g, \"-\"),\n label: categoryName,\n links: categoryData.pages.sort((a, b) => a.order - b.order),\n }));\n}\n\nexport { transformPagesToGroupedStructure };\n";
@@ -1,7 +1,7 @@
1
1
  export const orderNavItemsTemplate = `export interface PagesProps {
2
2
  slug: string;
3
3
  title: string;
4
- date: string;
4
+ date: string | null;
5
5
  category: string;
6
6
  description?: string;
7
7
  path?: string;
@@ -13,7 +13,12 @@ export const orderNavItemsTemplate = `export interface PagesProps {
13
13
  interface AccProps {
14
14
  [key: string]: {
15
15
  categoryOrder: number;
16
- pages: { date: string; slug: string; title: string; order: number }[];
16
+ pages: {
17
+ date: string | null;
18
+ slug: string;
19
+ title: string;
20
+ order: number;
21
+ }[];
17
22
  };
18
23
  }
19
24
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "doccupine",
3
- "version": "0.0.67",
3
+ "version": "0.0.68",
4
4
  "description": "Free and open-source documentation platform. Write MDX, get a production-ready site with AI chat, built-in components, and an MCP server - in one command.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {