doccupine 0.0.6 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Welcome to Doccupine
2
2
 
3
- Doccupine is a free and open-source document management system that allows you to store, organize, and share your documentation with ease. Using Doccupine, you simply create your documentation in MDX files with traditional Markdown syntax, Doccupine monitors your changes automatically generating a beautiful, modern documentation website.
3
+ [Doccupine](https://doccupine.com) is a free and open-source document management system that allows you to store, organize, and share your documentation with ease. Using Doccupine, you simply create your documentation in MDX files with traditional Markdown syntax, Doccupine monitors your changes automatically generating a beautiful, modern documentation website.
4
4
 
5
5
  ## Open Source and Extensible
6
6
 
package/dist/index.js CHANGED
@@ -469,7 +469,7 @@ export default function Home() {
469
469
  program
470
470
  .name("doccupine")
471
471
  .description("Watch MDX files and generate Next.js documentation pages automatically")
472
- .version("0.0.6");
472
+ .version("0.0.8");
473
473
  program
474
474
  .command("watch", { isDefault: true })
475
475
  .description("Watch a directory for MDX changes and generate Next.js app")
@@ -1 +1 @@
1
- export declare const docsTemplate = "\"use client\";\nimport { Flex } from \"cherry-styled-components/src/lib\";\nimport Markdown from \"react-markdown\";\nimport remarkGfm from \"remark-gfm\";\nimport { Code } from \"@/components/layout/Code\";\nimport { \n DocsContainer,\n StyledMarkdownContainer,\n} from \"@/components/layout/DocsComponents\";\n\ninterface DocsProps {\n content: string;\n}\n \nfunction Docs({ content }: DocsProps) {\n return (\n <DocsContainer>\n <Flex $gap={20}>\n <StyledMarkdownContainer>\n {content && (\n <Markdown\n remarkPlugins={[remarkGfm]}\n components={{\n code(props) {\n const { children, className, node, ...rest } = props;\n const match = /language-(\\w+)/.exec(className || \"\");\n return match ? (\n <Code\n {...rest}\n className={className}\n code={String(children).replace(/\\n$/, \"\")}\n language={match[1]}\n />\n ) : (\n <code {...rest} className={className}>\n {children}\n </code>\n );\n },\n }}\n >\n {content}\n </Markdown>\n )}\n </StyledMarkdownContainer>\n </Flex>\n </DocsContainer>\n );\n}\n\nexport { Docs };\n";
1
+ export declare const docsTemplate = "\"use client\";\nimport { useEffect, useState } from \"react\";\nimport { Flex, Space } from \"cherry-styled-components/src/lib\";\nimport Markdown from \"react-markdown\";\nimport remarkGfm from \"remark-gfm\";\nimport { Code } from \"@/components/layout/Code\";\nimport { \n DocsContainer,\n StyledMarkdownContainer,\n StyledIndexSidebar,\n} from \"@/components/layout/DocsComponents\";\nimport { StyledText } from \"@/components/layout/Typography\";\n\ninterface DocsProps {\n content: string;\n}\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 headingRegex = /^(#{1,6})\\s+(.+)$/gm;\n const headings: Heading[] = [];\n let match;\n\n while ((match = headingRegex.exec(content)) !== 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, setHeadings] = useState<Heading[]>([]);\n\n useEffect(() => {\n if (content) {\n const extractedHeadings = extractHeadings(content);\n setHeadings(extractedHeadings);\n }\n }, [content]);\n\n return (\n <>\n <DocsContainer>\n <Flex $gap={20}>\n <StyledMarkdownContainer>\n {content && (\n <Markdown\n remarkPlugins={[remarkGfm]}\n components={{\n code(props) {\n const { children, className, node, ...rest } = props;\n const match = /language-(\\w+)/.exec(className || \"\");\n return match ? (\n <Code\n {...rest}\n className={className}\n code={String(children).replace(/\\n$/, \"\")}\n language={match[1]}\n />\n ) : (\n <code {...rest} className={className}>\n {children}\n </code>\n );\n },\n h1: ({ children, ...props }) => {\n const id = generateId(String(children));\n return (\n <h1 id={id} {...props}>\n {children}\n </h1>\n );\n },\n h2: ({ children, ...props }) => {\n const id = generateId(String(children));\n return (\n <h2 id={id} {...props}>\n {children}\n </h2>\n );\n },\n h3: ({ children, ...props }) => {\n const id = generateId(String(children));\n return (\n <h3 id={id} {...props}>\n {children}\n </h3>\n );\n },\n h4: ({ children, ...props }) => {\n const id = generateId(String(children));\n return (\n <h4 id={id} {...props}>\n {children}\n </h4>\n );\n },\n h5: ({ children, ...props }) => {\n const id = generateId(String(children));\n return (\n <h5 id={id} {...props}>\n {children}\n </h5>\n );\n },\n h6: ({ children, ...props }) => {\n const id = generateId(String(children));\n return (\n <h6 id={id} {...props}>\n {children}\n </h6>\n );\n },\n }}\n >\n {content}\n </Markdown>\n )}\n </StyledMarkdownContainer>\n </Flex>\n </DocsContainer>\n <StyledIndexSidebar>\n {headings?.length > 0 && (\n <>\n <StyledText>On this page</StyledText>\n <Space $size={20} />\n </>\n )}\n {headings.map((heading, index) => (\n <li\n key={index}\n style={\n {\n // paddingLeft: `${(heading.level - 1) * 16}px`,\n }\n }\n >\n <a href={`#${heading.id}`}>{heading.text}</a>\n </li>\n ))}\n </StyledIndexSidebar>\n </>\n );\n}\n\nexport { Docs };\n";
@@ -1,50 +1,162 @@
1
1
  export const docsTemplate = `"use client";
2
- import { Flex } from "cherry-styled-components/src/lib";
2
+ import { useEffect, useState } from "react";
3
+ import { Flex, Space } from "cherry-styled-components/src/lib";
3
4
  import Markdown from "react-markdown";
4
5
  import remarkGfm from "remark-gfm";
5
6
  import { Code } from "@/components/layout/Code";
6
7
  import {
7
8
  DocsContainer,
8
9
  StyledMarkdownContainer,
10
+ StyledIndexSidebar,
9
11
  } from "@/components/layout/DocsComponents";
12
+ import { StyledText } from "@/components/layout/Typography";
10
13
 
11
14
  interface DocsProps {
12
15
  content: string;
13
16
  }
17
+
18
+
19
+ interface Heading {
20
+ id: string;
21
+ text: string;
22
+ level: number;
23
+ }
24
+
25
+ function generateId(text: string): string {
26
+ return text
27
+ .toLowerCase()
28
+ .replace(/[^\\w\\s-]/g, "")
29
+ .replace(/\\s+/g, "-")
30
+ .trim();
31
+ }
32
+
33
+ function extractHeadings(content: string): Heading[] {
34
+ const headingRegex = /^(#{1,6})\\s+(.+)$/gm;
35
+ const headings: Heading[] = [];
36
+ let match;
37
+
38
+ while ((match = headingRegex.exec(content)) !== null) {
39
+ const level = match[1].length;
40
+ const text = match[2].trim();
41
+ const id = generateId(text);
42
+ headings.push({ id, text, level });
43
+ }
44
+
45
+ return headings;
46
+ }
14
47
 
15
48
  function Docs({ content }: DocsProps) {
49
+ const [headings, setHeadings] = useState<Heading[]>([]);
50
+
51
+ useEffect(() => {
52
+ if (content) {
53
+ const extractedHeadings = extractHeadings(content);
54
+ setHeadings(extractedHeadings);
55
+ }
56
+ }, [content]);
57
+
16
58
  return (
17
- <DocsContainer>
18
- <Flex $gap={20}>
19
- <StyledMarkdownContainer>
20
- {content && (
21
- <Markdown
22
- remarkPlugins={[remarkGfm]}
23
- components={{
24
- code(props) {
25
- const { children, className, node, ...rest } = props;
26
- const match = /language-(\\w+)/.exec(className || "");
27
- return match ? (
28
- <Code
29
- {...rest}
30
- className={className}
31
- code={String(children).replace(/\\n\$/, "")}
32
- language={match[1]}
33
- />
34
- ) : (
35
- <code {...rest} className={className}>
36
- {children}
37
- </code>
38
- );
39
- },
40
- }}
59
+ <>
60
+ <DocsContainer>
61
+ <Flex $gap={20}>
62
+ <StyledMarkdownContainer>
63
+ {content && (
64
+ <Markdown
65
+ remarkPlugins={[remarkGfm]}
66
+ components={{
67
+ code(props) {
68
+ const { children, className, node, ...rest } = props;
69
+ const match = /language-(\\w+)/.exec(className || "");
70
+ return match ? (
71
+ <Code
72
+ {...rest}
73
+ className={className}
74
+ code={String(children).replace(/\\n\$/, "")}
75
+ language={match[1]}
76
+ />
77
+ ) : (
78
+ <code {...rest} className={className}>
79
+ {children}
80
+ </code>
81
+ );
82
+ },
83
+ h1: ({ children, ...props }) => {
84
+ const id = generateId(String(children));
85
+ return (
86
+ <h1 id={id} {...props}>
87
+ {children}
88
+ </h1>
89
+ );
90
+ },
91
+ h2: ({ children, ...props }) => {
92
+ const id = generateId(String(children));
93
+ return (
94
+ <h2 id={id} {...props}>
95
+ {children}
96
+ </h2>
97
+ );
98
+ },
99
+ h3: ({ children, ...props }) => {
100
+ const id = generateId(String(children));
101
+ return (
102
+ <h3 id={id} {...props}>
103
+ {children}
104
+ </h3>
105
+ );
106
+ },
107
+ h4: ({ children, ...props }) => {
108
+ const id = generateId(String(children));
109
+ return (
110
+ <h4 id={id} {...props}>
111
+ {children}
112
+ </h4>
113
+ );
114
+ },
115
+ h5: ({ children, ...props }) => {
116
+ const id = generateId(String(children));
117
+ return (
118
+ <h5 id={id} {...props}>
119
+ {children}
120
+ </h5>
121
+ );
122
+ },
123
+ h6: ({ children, ...props }) => {
124
+ const id = generateId(String(children));
125
+ return (
126
+ <h6 id={id} {...props}>
127
+ {children}
128
+ </h6>
129
+ );
130
+ },
131
+ }}
132
+ >
133
+ {content}
134
+ </Markdown>
135
+ )}
136
+ </StyledMarkdownContainer>
137
+ </Flex>
138
+ </DocsContainer>
139
+ <StyledIndexSidebar>
140
+ {headings?.length > 0 && (
141
+ <>
142
+ <StyledText>On this page</StyledText>
143
+ <Space $size={20} />
144
+ </>
145
+ )}
146
+ {headings.map((heading, index) => (
147
+ <li
148
+ key={index}
149
+ style={
150
+ {
151
+ // paddingLeft: \`\${(heading.level - 1) * 16}px\`,
152
+ }
153
+ }
41
154
  >
42
- {content}
43
- </Markdown>
44
- )}
45
- </StyledMarkdownContainer>
46
- </Flex>
47
- </DocsContainer>
155
+ <a href={\`#\${heading.id}\`}>{heading.text}</a>
156
+ </li>
157
+ ))}
158
+ </StyledIndexSidebar>
159
+ </>
48
160
  );
49
161
  }
50
162
 
@@ -1 +1 @@
1
- export declare const docsComponentsTemplate = "\"use client\";\nimport { mq, Theme } from \"@/app/theme\";\nimport {\n resetButton,\n styledStrong,\n styledText,\n} from \"cherry-styled-components/src/lib\";\nimport Link from \"next/link\";\nimport { rgba } from \"polished\";\nimport React from \"react\";\nimport styled, { css } from \"styled-components\";\nimport { interactiveStyles } from \"./SharedStyled\";\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 position: sticky;\n top: 90px;\n min-width: 200px;\n float: left;\n z-index: 999;\n\n ${mq(\"lg\")} {\n z-index: 2;\n }\n`;\n\nconst StyledDocsContainer = styled.div<{ theme: Theme }>`\n position: relative;\n padding: 0;\n width: 100%;\n\n & pre {\n max-width: 100%;\n }\n\n ${mq(\"lg\")} {\n padding-left: 230px;\n }\n\n & ul {\n list-style: none;\n padding: 0;\n margin: 0;\n\n & li {\n text-indent: 0;\n display: block;\n position: relative;\n padding: 0 0 0 15px;\n margin: 0;\n ${({ theme }) => styledText(theme)};\n min-height: 23px;\n\n $mq: \"lg\" {\n min-height: 27px;\n }\n\n &::before {\n content: \"\";\n display: block;\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: ${({ theme }) => theme.colors.primary};\n position: absolute;\n top: 8px;\n left: 2px;\n\n ${mq(\"lg\")} {\n top: 10px;\n }\n }\n }\n }\n\n & ol {\n padding: 0;\n margin: 0;\n\n & > li {\n position: relative;\n padding: 0;\n counter-increment: item;\n padding: 0;\n margin: 0;\n ${({ theme }) => styledText(theme)};\n\n &::before {\n content: counter(item) \".\";\n display: inline-block;\n margin: 0 4px 0 0;\n font-weight: 700;\n color: ${({ theme }) => theme.colors.primary};\n min-width: max-content;\n }\n }\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`;\n\ninterface Props {\n theme?: Theme;\n $isActive?: boolean;\n}\n\nexport const StyledSidebar = styled.nav<Props>`\n position: fixed;\n overflow-y: auto;\n max-height: calc(100svh - 70px);\n width: 100%;\n background: ${({ theme }) => theme.colors.light};\n z-index: 99;\n top: 70px;\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\n ${mq(\"lg\")} {\n width: 220px;\n left: 20px;\n background: transparent;\n padding: 20px 20px 20px 0;\n opacity: 1;\n pointer-events: all;\n transform: translateY(0);\n }\n\n ${mq(\"xl\")} {\n width: 220px;\n left: calc(50% - 610px);\n }\n\n ${mq(\"xxl\")} {\n width: 220px;\n left: calc(50% - 690px);\n }\n\n ${({ $isActive }) =>\n $isActive &&\n css`\n transform: translateY(0);\n opacity: 1;\n pointer-events: all;\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`;\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.strong.xs};\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 ${mq(\"lg\")} {\n font-size: ${({ theme }) => theme.fontSizes.strong.lg};\n }\n\n @media (hover: hover) {\n &:hover {\n color: ${({ theme }) =>\n theme.isDark ? theme.colors.primaryLight : theme.colors.primaryDark};\n border-color: ${({ theme }) => theme.colors.info};\n }\n }\n\n ${({ $isActive, theme }) =>\n $isActive &&\n `\n\t\t\tcolor: ${theme.colors.dark};\n\t\t\tborder-color: ${theme.colors.info};\n\t\t\tfont-weight: 600;\n\t`};\n`;\n\nexport const StyleMobileBar = styled.button<Props>`\n ${resetButton};\n position: fixed;\n z-index: 1000;\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: 30px;\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 StyledInlineButton = styled.button<{ theme: Theme }>`\n ${resetButton};\n ${interactiveStyles};\n color: ${({ theme }) => theme.colors.primary};\n vertical-align: middle;\n border: solid 1px ${({ theme }) => theme.colors.grayLight};\n border-radius: ${({ theme }) => theme.spacing.radius.xs};\n padding: 0;\n width: 18px;\n height: 18px;\n display: flex;\n margin: auto 0;\n transition: all 0.3s ease;\n\n @media (hover: hover) {\n &:hover {\n color: ${({ theme }) => theme.colors.light};\n background: ${({ theme }) => theme.colors.primary};\n border-color: ${({ theme }) => theme.colors.primary};\n }\n }\n\n & svg {\n vertical-align: middle;\n width: 12px;\n height: 12px;\n margin: auto;\n }\n`;\n\nexport const StyledPlusButton = styled.button<{ theme: Theme }>`\n ${resetButton};\n color: ${({ theme }) => theme.colors.primary};\n vertical-align: middle;\n padding: 0;\n width: 100%;\n height: 18px;\n display: flex;\n margin: auto 0;\n transition: all 0.3s ease;\n position: relative;\n margin: 20px 0 0 0;\n\n &::before {\n content: \"\";\n display: block;\n position: absolute;\n top: 50%;\n transform: translateY(-50%);\n width: 100%;\n height: 1px;\n border-radius: 3px;\n background: ${({ theme }) => theme.colors.grayLight};\n transition: all 0.3s ease;\n z-index: -1;\n }\n\n &::after {\n content: \"\";\n display: block;\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n width: 18px;\n height: 18px;\n border: solid 1px ${({ theme }) => theme.colors.grayLight};\n border-radius: ${({ theme }) => theme.spacing.radius.xs};\n background: ${({ theme }) => theme.colors.light};\n transition: all 0.3s ease;\n z-index: -1;\n }\n\n box-shadow: 0 0 0 0px ${({ theme }) => theme.colors.primary};\n\n @media (hover: hover) {\n &:hover {\n &::after {\n border-color: ${({ theme }) => theme.colors.primary};\n }\n }\n }\n\n &:focus {\n &::after {\n border-color: ${({ theme }) => theme.colors.primary};\n box-shadow: 0 0 0 4px ${({ theme }) => theme.colors.primaryLight};\n }\n }\n\n &:active {\n &::after {\n box-shadow: 0 0 0 2px ${({ theme }) => theme.colors.primaryLight};\n }\n }\n\n @media (hover: hover) {\n &:hover {\n color: ${({ theme }) => theme.colors.light};\n\n &::after {\n background: ${({ theme }) => theme.colors.primary};\n border-color: ${({ theme }) => theme.colors.primary};\n }\n }\n }\n\n & svg {\n vertical-align: middle;\n width: 14px;\n height: 14px;\n margin: auto;\n }\n`;\n\nexport const StyledFullHeightInput = styled.div`\n height: 100%;\n width: 100%;\n display: flex;\n flex-direction: column;\n gap: 20px;\n flex: 1;\n\n & > span {\n height: 100%;\n width: 100%;\n }\n\n & textarea {\n height: 100%;\n }\n`;\n\nexport const StyledEditorWrapper = styled.div`\n flex: 1;\n\n &:empty {\n display: none;\n }\n`;\n\nfunction DocsWrapper({ children }: DocsProps) {\n return <StyledDocsWrapper>{children}</StyledDocsWrapper>;\n}\n\nfunction DocsSidebar({ children }: DocsProps) {\n return <StyledDocsSidebar>{children}</StyledDocsSidebar>;\n}\n\nfunction DocsContainer({ children }: DocsProps) {\n return <StyledDocsContainer>{children}</StyledDocsContainer>;\n}\n\nexport { DocsWrapper, DocsSidebar, DocsContainer };\n";
1
+ export declare const docsComponentsTemplate = "\"use client\";\nimport { mq, Theme } from \"@/app/theme\";\nimport {\n resetButton,\n styledStrong,\n styledText,\n} from \"cherry-styled-components/src/lib\";\nimport Link from \"next/link\";\nimport { rgba } from \"polished\";\nimport React from \"react\";\nimport styled, { css } from \"styled-components\";\nimport { interactiveStyles } from \"./SharedStyled\";\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 }>`\n position: relative;\n padding: 0;\n width: 100%;\n ${({ theme }) => styledText(theme)};\n\n ${mq(\"lg\")} {\n padding-left: 320px;\n padding-right: 320px;\n }\n\n & p {\n color: ${({ theme }) => theme.colors.grayDark};\n }\n\n & pre {\n max-width: 100%;\n }\n\n & ul {\n list-style: none;\n padding: 0;\n margin: 0;\n\n & li {\n text-indent: 0;\n display: block;\n position: relative;\n padding: 0 0 0 15px;\n margin: 0;\n ${({ theme }) => styledText(theme)};\n min-height: 23px;\n\n $mq: \"lg\" {\n min-height: 27px;\n }\n\n &::before {\n content: \"\";\n display: block;\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: ${({ theme }) => theme.colors.primary};\n position: absolute;\n top: 8px;\n left: 2px;\n\n ${mq(\"lg\")} {\n top: 10px;\n }\n }\n }\n }\n\n & ol {\n padding: 0;\n margin: 0;\n\n & > li {\n position: relative;\n padding: 0;\n counter-increment: item;\n margin: 0;\n ${({ theme }) => styledText(theme)};\n\n &::before {\n content: counter(item) \".\";\n display: inline-block;\n margin: 0 4px 0 0;\n font-weight: 700;\n color: ${({ theme }) => theme.colors.primary};\n min-width: max-content;\n }\n }\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 ${mq(\"lg\")} {\n padding-bottom: 110px;\n }\n`;\n\ninterface Props {\n theme?: Theme;\n $isActive?: boolean;\n}\n\nexport const StyledSidebar = styled.nav<Props>`\n position: fixed;\n overflow-y: auto;\n max-height: calc(100svh - 70px);\n width: 100%;\n z-index: 99;\n top: 70px;\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\n ${mq(\"lg\")} {\n max-height: 100svh;\n width: 220px;\n background: transparent;\n padding: 90px 40px 40px;\n opacity: 1;\n pointer-events: all;\n transform: translateY(0);\n background: ${({ theme }) => rgba(theme.colors.primaryLight, 0.1)};\n top: 0;\n width: 320px;\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: 320px;\n height: 100vh;\n overflow-y: auto;\n z-index: 1;\n padding: 40px;\n background: ${({ theme }) => theme.colors.light};\n border-left: solid 1px ${({ theme }) => theme.colors.grayLight};\n\n ${mq(\"lg\")} {\n display: block;\n }\n\n & li {\n padding: 5px 0;\n\n & a {\n ${({ theme }) => styledText(theme)};\n color: ${({ theme }) => theme.colors.primary};\n font-weight: 600;\n text-decoration: none;\n transition: all 0.3s ease;\n\n &:hover {\n color: ${({ theme }) => theme.colors.primaryDark};\n }\n }\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`;\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.strong.xs};\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 ${mq(\"lg\")} {\n font-size: ${({ theme }) => theme.fontSizes.strong.lg};\n }\n\n @media (hover: hover) {\n &:hover {\n color: ${({ theme }) =>\n theme.isDark ? theme.colors.primaryLight : theme.colors.primaryDark};\n border-color: ${({ theme }) => theme.colors.info};\n }\n }\n\n ${({ $isActive, theme }) =>\n $isActive &&\n `\n\t\t\tcolor: ${theme.colors.dark};\n\t\t\tborder-color: ${theme.colors.info};\n\t\t\tfont-weight: 600;\n\t`};\n`;\n\nexport const StyleMobileBar = styled.button<Props>`\n ${resetButton};\n position: fixed;\n z-index: 1000;\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: 30px;\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 StyledInlineButton = styled.button<{ theme: Theme }>`\n ${resetButton};\n ${interactiveStyles};\n color: ${({ theme }) => theme.colors.primary};\n vertical-align: middle;\n border: solid 1px ${({ theme }) => theme.colors.grayLight};\n border-radius: ${({ theme }) => theme.spacing.radius.xs};\n padding: 0;\n width: 18px;\n height: 18px;\n display: flex;\n margin: auto 0;\n transition: all 0.3s ease;\n\n @media (hover: hover) {\n &:hover {\n color: ${({ theme }) => theme.colors.light};\n background: ${({ theme }) => theme.colors.primary};\n border-color: ${({ theme }) => theme.colors.primary};\n }\n }\n\n & svg {\n vertical-align: middle;\n width: 12px;\n height: 12px;\n margin: auto;\n }\n`;\n\nexport const StyledPlusButton = styled.button<{ theme: Theme }>`\n ${resetButton};\n color: ${({ theme }) => theme.colors.primary};\n vertical-align: middle;\n padding: 0;\n width: 100%;\n height: 18px;\n display: flex;\n margin: auto 0;\n transition: all 0.3s ease;\n position: relative;\n margin: 20px 0 0 0;\n\n &::before {\n content: \"\";\n display: block;\n position: absolute;\n top: 50%;\n transform: translateY(-50%);\n width: 100%;\n height: 1px;\n border-radius: 3px;\n background: ${({ theme }) => theme.colors.grayLight};\n transition: all 0.3s ease;\n z-index: -1;\n }\n\n &::after {\n content: \"\";\n display: block;\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n width: 18px;\n height: 18px;\n border: solid 1px ${({ theme }) => theme.colors.grayLight};\n border-radius: ${({ theme }) => theme.spacing.radius.xs};\n background: ${({ theme }) => theme.colors.light};\n transition: all 0.3s ease;\n z-index: -1;\n }\n\n box-shadow: 0 0 0 0px ${({ theme }) => theme.colors.primary};\n\n @media (hover: hover) {\n &:hover {\n &::after {\n border-color: ${({ theme }) => theme.colors.primary};\n }\n }\n }\n\n &:focus {\n &::after {\n border-color: ${({ theme }) => theme.colors.primary};\n box-shadow: 0 0 0 4px ${({ theme }) => theme.colors.primaryLight};\n }\n }\n\n &:active {\n &::after {\n box-shadow: 0 0 0 2px ${({ theme }) => theme.colors.primaryLight};\n }\n }\n\n @media (hover: hover) {\n &:hover {\n color: ${({ theme }) => theme.colors.light};\n\n &::after {\n background: ${({ theme }) => theme.colors.primary};\n border-color: ${({ theme }) => theme.colors.primary};\n }\n }\n }\n\n & svg {\n vertical-align: middle;\n width: 14px;\n height: 14px;\n margin: auto;\n }\n`;\n\nexport const StyledFullHeightInput = styled.div`\n height: 100%;\n width: 100%;\n display: flex;\n flex-direction: column;\n gap: 20px;\n flex: 1;\n\n & > span {\n height: 100%;\n width: 100%;\n }\n\n & textarea {\n height: 100%;\n }\n`;\n\nexport const StyledEditorWrapper = styled.div`\n flex: 1;\n\n &:empty {\n display: none;\n }\n`;\n\nfunction DocsWrapper({ children }: DocsProps) {\n return <StyledDocsWrapper>{children}</StyledDocsWrapper>;\n}\n\nfunction DocsSidebar({ children }: DocsProps) {\n return <StyledDocsSidebar>{children}</StyledDocsSidebar>;\n}\n\nfunction DocsContainer({ children }: DocsProps) {\n return <StyledDocsContainer>{children}</StyledDocsContainer>;\n}\n\nexport { DocsWrapper, DocsSidebar, DocsContainer };\n";
@@ -20,28 +20,26 @@ const StyledDocsWrapper = styled.div<{ theme: Theme }>\`
20
20
  \`;
21
21
 
22
22
  const StyledDocsSidebar = styled.div<{ theme: Theme }>\`
23
- position: sticky;
24
- top: 90px;
25
- min-width: 200px;
26
- float: left;
27
- z-index: 999;
28
-
29
- \${mq("lg")} {
30
- z-index: 2;
31
- }
23
+ clear: both;
32
24
  \`;
33
25
 
34
26
  const StyledDocsContainer = styled.div<{ theme: Theme }>\`
35
27
  position: relative;
36
28
  padding: 0;
37
29
  width: 100%;
30
+ \${({ theme }) => styledText(theme)};
38
31
 
39
- & pre {
40
- max-width: 100%;
32
+ \${mq("lg")} {
33
+ padding-left: 320px;
34
+ padding-right: 320px;
41
35
  }
42
36
 
43
- \${mq("lg")} {
44
- padding-left: 230px;
37
+ & p {
38
+ color: \${({ theme }) => theme.colors.grayDark};
39
+ }
40
+
41
+ & pre {
42
+ max-width: 100%;
45
43
  }
46
44
 
47
45
  & ul {
@@ -88,7 +86,6 @@ const StyledDocsContainer = styled.div<{ theme: Theme }>\`
88
86
  position: relative;
89
87
  padding: 0;
90
88
  counter-increment: item;
91
- padding: 0;
92
89
  margin: 0;
93
90
  \${({ theme }) => styledText(theme)};
94
91
 
@@ -110,6 +107,12 @@ export const StyledMarkdownContainer = styled.div\`
110
107
  gap: 20px;
111
108
  flex-wrap: wrap;
112
109
  flex: 1;
110
+ max-width: 640px;
111
+ margin: auto;
112
+
113
+ \${mq("lg")} {
114
+ padding-bottom: 110px;
115
+ }
113
116
  \`;
114
117
 
115
118
  interface Props {
@@ -122,7 +125,6 @@ export const StyledSidebar = styled.nav<Props>\`
122
125
  overflow-y: auto;
123
126
  max-height: calc(100svh - 70px);
124
127
  width: 100%;
125
- background: \${({ theme }) => theme.colors.light};
126
128
  z-index: 99;
127
129
  top: 70px;
128
130
  height: 100%;
@@ -132,25 +134,19 @@ export const StyledSidebar = styled.nav<Props>\`
132
134
  transition: all 0.3s ease;
133
135
  transform: translateY(30px);
134
136
  left: 0;
137
+ background: \${({ theme }) => theme.colors.light};
135
138
 
136
139
  \${mq("lg")} {
140
+ max-height: 100svh;
137
141
  width: 220px;
138
- left: 20px;
139
142
  background: transparent;
140
- padding: 20px 20px 20px 0;
143
+ padding: 90px 40px 40px;
141
144
  opacity: 1;
142
145
  pointer-events: all;
143
146
  transform: translateY(0);
144
- }
145
-
146
- \${mq("xl")} {
147
- width: 220px;
148
- left: calc(50% - 610px);
149
- }
150
-
151
- \${mq("xxl")} {
152
- width: 220px;
153
- left: calc(50% - 690px);
147
+ background: \${({ theme }) => rgba(theme.colors.primaryLight, 0.1)};
148
+ top: 0;
149
+ width: 320px;
154
150
  }
155
151
 
156
152
  \${({ $isActive }) =>
@@ -162,6 +158,43 @@ export const StyledSidebar = styled.nav<Props>\`
162
158
  \`}
163
159
  \`;
164
160
 
161
+ export const StyledIndexSidebar = styled.ul<{ theme: Theme }>\`
162
+ display: none;
163
+ list-style: none;
164
+ margin: 0;
165
+ padding: 0;
166
+ position: fixed;
167
+ top: 0;
168
+ right: 0;
169
+ width: 320px;
170
+ height: 100vh;
171
+ overflow-y: auto;
172
+ z-index: 1;
173
+ padding: 40px;
174
+ background: \${({ theme }) => theme.colors.light};
175
+ border-left: solid 1px \${({ theme }) => theme.colors.grayLight};
176
+
177
+ \${mq("lg")} {
178
+ display: block;
179
+ }
180
+
181
+ & li {
182
+ padding: 5px 0;
183
+
184
+ & a {
185
+ \${({ theme }) => styledText(theme)};
186
+ color: \${({ theme }) => theme.colors.primary};
187
+ font-weight: 600;
188
+ text-decoration: none;
189
+ transition: all 0.3s ease;
190
+
191
+ &:hover {
192
+ color: \${({ theme }) => theme.colors.primaryDark};
193
+ }
194
+ }
195
+ }
196
+ \`;
197
+
165
198
  export const StyledSidebarList = styled.ul\`
166
199
  list-style: none;
167
200
  margin: 0;
@@ -1 +1 @@
1
- export declare const headerTemplate = "\"use client\";\nimport {\n Flex,\n MaxWidth,\n resetButton,\n styledText,\n} from \"cherry-styled-components/src/lib\";\nimport React, { \n useCallback,\n useContext,\n useRef,\n useState,\n Suspense\n} from \"react\";\nimport styled, { css } from \"styled-components\";\nimport Link from \"next/link\";\nimport { rgba } from \"polished\";\nimport { mq, Theme } from \"@/app/theme\";\nimport { ToggleTheme, ToggleThemeLoading } from \"@/components/layout/ThemeToggle\";\nimport {\n StyledTinyDesktopOnly,\n StyledTinyMobileOnly,\n} from \"@/components/layout/SharedStyled\";\nimport { useOnClickOutside } from \"@/components/ClickOutside\";\nimport { Logo } from \"@/components/layout/Pictograms\";\n\nconst StyledHeader = styled.header<{ theme: Theme }>`\n position: sticky;\n top: 0;\n padding: 20px;\n margin: 0;\n z-index: 1000;\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 }) => rgba(theme.colors.primaryLight, 0.1)};\n z-index: -1;\n }\n\n & .logo {\n display: flex;\n\n & svg {\n margin: auto;\n }\n }\n`;\n\nconst StyledLink = styled(Link)<{ theme: Theme }>`\n text-decoration: none;\n margin: auto 0;\n color: ${({ theme }) => theme.colors.primary};\n font-weight: 500;\n\n @media (hover: hover) {\n &:hover {\n color: ${({ theme }) => theme.colors.primaryDark};\n }\n }\n`;\n\nexport const StyledMobileBurger = styled.button<{\n theme: Theme;\n $isActive: boolean;\n}>`\n ${resetButton};\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 ${mq(\"lg\")} {\n display: none;\n }\n\n &::before,\n &::after {\n content: \"\";\n display: block;\n position: absolute;\n width: 18px;\n height: 2px;\n border-radius: 3px;\n background: ${({ theme }) => theme.colors.primary};\n transition: all 0.3s ease;\n }\n\n &::before {\n top: 4px;\n }\n\n &::after {\n bottom: 4px;\n }\n\n ${({ $isActive }) =>\n $isActive &&\n css`\n &::before {\n transform: translateY(4px) rotate(45deg);\n }\n\n &::after {\n transform: translateY(-4px) rotate(-45deg);\n }\n `};\n`;\n\nconst StyledMobileMenu = styled.ul<{ theme: Theme; $isActive: boolean }>`\n list-style: none;\n padding: 70px 0 0 0;\n margin: 0;\n position: fixed;\n top: 0;\n left: 0;\n background: ${({ theme }) => theme.colors.light};\n pointer-events: none;\n opacity: 0;\n width: 100%;\n height: 100dvh;\n transition: all 0.3s ease;\n z-index: 800;\n transform: translateY(40px) scale(1.2);\n display: block;\n\n ${({ $isActive }) =>\n $isActive &&\n css`\n pointer-events: all;\n opacity: 1;\n transform: translateY(0) scale(1);\n `};\n\n & li {\n display: block;\n width: 100%;\n padding: 0;\n margin: 0;\n border-bottom: solid 1px ${({ theme }) => theme.colors.grayLight};\n\n &.languages {\n display: flex;\n justify-content: center;\n gap: 10px;\n }\n\n & a,\n & button {\n ${({ theme }) => styledText(theme)};\n font-family: ${({ theme }) => theme.fonts.text};\n font-weight: 500;\n appearance: none;\n background: transparent;\n border: none;\n color: ${({ theme }) => theme.colors.dark};\n display: flex;\n margin: 0;\n padding: 20px;\n font-weight: 500;\n text-decoration: none;\n cursor: pointer;\n }\n\n & button {\n flex: 1;\n border-right: solid 1px ${({ theme }) => theme.colors.grayLight};\n text-align: center;\n justify-content: center;\n\n &:last-of-type {\n border-right: none;\n }\n }\n }\n`;\n\nconst StyledMenu = styled.div<{ theme: Theme }>`\n display: none;\n\n ${mq(\"lg\")} {\n display: flex;\n gap: 20px;\n }\n`;\n\nconst StyledDivider = styled.div<{ theme: Theme }>`\n width: 1px;\n height: calc(100% - 10px);\n margin: auto 0;\n background: ${({ theme }) => theme.colors.grayLight};\n`;\n\nconst StyledWrapper = styled.div<{ theme: Theme }>`\n margin: auto 0;\n`;\n\nfunction Header() {\n const [isOptionActive, setIsOptionActive] = useState(false);\n const [isLangActive, setIsLangActive] = useState(false);\n const [isMobileMenuActive, setIsMobileMenuActive] = 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\n return (\n <>\n <StyledHeader>\n <MaxWidth $size={1000}>\n <Flex $justifyContent=\"space-between\" $wrap=\"nowrap\">\n <Link href=\"/\" className=\"logo\" aria-label=\"Logo\">\n <StyledTinyDesktopOnly>\n <Logo />\n </StyledTinyDesktopOnly>\n <StyledTinyMobileOnly>\n <Logo />\n </StyledTinyMobileOnly>\n </Link>\n <StyledWrapper>\n <span ref={wrapperRef}>\n <Flex $gap={20} $wrap=\"wrap\">\n <StyledMenu>\n <StyledLink href=\"https://doccupine.com\" target=\"_blank\">\n Doccupine\n </StyledLink>\n </StyledMenu>\n <Suspense fallback={<ToggleThemeLoading />}>\n <ToggleTheme />\n </Suspense>\n <StyledMobileBurger\n $isActive={isMobileMenuActive}\n onClick={() => setIsMobileMenuActive(!isMobileMenuActive)}\n aria-label=\"Burger Menu\"\n />\n </Flex>\n </span>\n </StyledWrapper>\n </Flex>\n </MaxWidth>\n </StyledHeader>\n <StyledMobileMenu $isActive={isMobileMenuActive}>\n <li onClick={() => setIsMobileMenuActive(false)}>\n <a href=\"https://doccupine.com\" target=\"_blank\">\n Doccupine\n </a>\n </li>\n </StyledMobileMenu>\n </>\n );\n}\n\nexport { Header };\n";
1
+ export declare const headerTemplate = "\"use client\";\nimport { Flex, MaxWidth, resetButton } from \"cherry-styled-components/src/lib\";\nimport React, { useCallback, useRef, useState, Suspense } from \"react\";\nimport styled, { css } from \"styled-components\";\nimport Link from \"next/link\";\nimport { rgba } from \"polished\";\nimport { mq, Theme } from \"@/app/theme\";\nimport { \n ToggleTheme,\n ToggleThemeLoading\n} from \"@/components/layout/ThemeToggle\";\nimport {\n StyledTinyDesktopOnly,\n StyledTinyMobileOnly,\n} from \"@/components/layout/SharedStyled\";\nimport { useOnClickOutside } from \"@/components/ClickOutside\";\nimport { Logo } from \"@/components/layout/Pictograms\";\n\nconst StyledHeader = styled.header<{ theme: Theme }>`\n position: sticky;\n top: 0;\n padding: 20px;\n margin: 0;\n z-index: 1000;\n\n ${mq(\"lg\")} {\n width: 320px;\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 }) => rgba(theme.colors.primaryLight, 0.1)};\n z-index: -1;\n }\n\n & .logo {\n display: flex;\n\n & svg {\n margin: auto;\n }\n }\n`;\n\nfunction Header() {\n const [isOptionActive, setIsOptionActive] = useState(false);\n const [isLangActive, setIsLangActive] = useState(false);\n const [isMobileMenuActive, setIsMobileMenuActive] = 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\n return (\n <>\n <StyledHeader>\n <MaxWidth $size={1000}>\n <Flex $justifyContent=\"space-between\" $wrap=\"nowrap\">\n <Link href=\"/\" className=\"logo\" aria-label=\"Logo\">\n <StyledTinyDesktopOnly>\n <Logo />\n </StyledTinyDesktopOnly>\n <StyledTinyMobileOnly>\n <Logo />\n </StyledTinyMobileOnly>\n </Link>\n <Suspense fallback={<ToggleThemeLoading />}>\n <ToggleTheme />\n </Suspense>\n </Flex>\n </MaxWidth>\n </StyledHeader>\n </>\n );\n}\n\nexport { Header };\n";
@@ -1,22 +1,14 @@
1
1
  export const headerTemplate = `"use client";
2
- import {
3
- Flex,
4
- MaxWidth,
5
- resetButton,
6
- styledText,
7
- } from "cherry-styled-components/src/lib";
8
- import React, {
9
- useCallback,
10
- useContext,
11
- useRef,
12
- useState,
13
- Suspense
14
- } from "react";
2
+ import { Flex, MaxWidth, resetButton } from "cherry-styled-components/src/lib";
3
+ import React, { useCallback, useRef, useState, Suspense } from "react";
15
4
  import styled, { css } from "styled-components";
16
5
  import Link from "next/link";
17
6
  import { rgba } from "polished";
18
7
  import { mq, Theme } from "@/app/theme";
19
- import { ToggleTheme, ToggleThemeLoading } from "@/components/layout/ThemeToggle";
8
+ import {
9
+ ToggleTheme,
10
+ ToggleThemeLoading
11
+ } from "@/components/layout/ThemeToggle";
20
12
  import {
21
13
  StyledTinyDesktopOnly,
22
14
  StyledTinyMobileOnly,
@@ -31,6 +23,10 @@ const StyledHeader = styled.header<{ theme: Theme }>\`
31
23
  margin: 0;
32
24
  z-index: 1000;
33
25
 
26
+ \${mq("lg")} {
27
+ width: 320px;
28
+ }
29
+
34
30
  &::before,
35
31
  &::after {
36
32
  display: block;
@@ -59,158 +55,6 @@ const StyledHeader = styled.header<{ theme: Theme }>\`
59
55
  }
60
56
  \`;
61
57
 
62
- const StyledLink = styled(Link)<{ theme: Theme }>\`
63
- text-decoration: none;
64
- margin: auto 0;
65
- color: \${({ theme }) => theme.colors.primary};
66
- font-weight: 500;
67
-
68
- @media (hover: hover) {
69
- &:hover {
70
- color: \${({ theme }) => theme.colors.primaryDark};
71
- }
72
- }
73
- \`;
74
-
75
- export const StyledMobileBurger = styled.button<{
76
- theme: Theme;
77
- $isActive: boolean;
78
- }>\`
79
- \${resetButton};
80
- display: block;
81
- margin: auto 0;
82
- width: 18px;
83
- height: 18px;
84
- position: relative;
85
- overflow: hidden;
86
- background: transparent;
87
- position: relative;
88
-
89
- \${mq("lg")} {
90
- display: none;
91
- }
92
-
93
- &::before,
94
- &::after {
95
- content: "";
96
- display: block;
97
- position: absolute;
98
- width: 18px;
99
- height: 2px;
100
- border-radius: 3px;
101
- background: \${({ theme }) => theme.colors.primary};
102
- transition: all 0.3s ease;
103
- }
104
-
105
- &::before {
106
- top: 4px;
107
- }
108
-
109
- &::after {
110
- bottom: 4px;
111
- }
112
-
113
- \${({ $isActive }) =>
114
- $isActive &&
115
- css\`
116
- &::before {
117
- transform: translateY(4px) rotate(45deg);
118
- }
119
-
120
- &::after {
121
- transform: translateY(-4px) rotate(-45deg);
122
- }
123
- \`};
124
- \`;
125
-
126
- const StyledMobileMenu = styled.ul<{ theme: Theme; $isActive: boolean }>\`
127
- list-style: none;
128
- padding: 70px 0 0 0;
129
- margin: 0;
130
- position: fixed;
131
- top: 0;
132
- left: 0;
133
- background: \${({ theme }) => theme.colors.light};
134
- pointer-events: none;
135
- opacity: 0;
136
- width: 100%;
137
- height: 100dvh;
138
- transition: all 0.3s ease;
139
- z-index: 800;
140
- transform: translateY(40px) scale(1.2);
141
- display: block;
142
-
143
- \${({ $isActive }) =>
144
- $isActive &&
145
- css\`
146
- pointer-events: all;
147
- opacity: 1;
148
- transform: translateY(0) scale(1);
149
- \`};
150
-
151
- & li {
152
- display: block;
153
- width: 100%;
154
- padding: 0;
155
- margin: 0;
156
- border-bottom: solid 1px \${({ theme }) => theme.colors.grayLight};
157
-
158
- &.languages {
159
- display: flex;
160
- justify-content: center;
161
- gap: 10px;
162
- }
163
-
164
- & a,
165
- & button {
166
- \${({ theme }) => styledText(theme)};
167
- font-family: \${({ theme }) => theme.fonts.text};
168
- font-weight: 500;
169
- appearance: none;
170
- background: transparent;
171
- border: none;
172
- color: \${({ theme }) => theme.colors.dark};
173
- display: flex;
174
- margin: 0;
175
- padding: 20px;
176
- font-weight: 500;
177
- text-decoration: none;
178
- cursor: pointer;
179
- }
180
-
181
- & button {
182
- flex: 1;
183
- border-right: solid 1px \${({ theme }) => theme.colors.grayLight};
184
- text-align: center;
185
- justify-content: center;
186
-
187
- &:last-of-type {
188
- border-right: none;
189
- }
190
- }
191
- }
192
- \`;
193
-
194
- const StyledMenu = styled.div<{ theme: Theme }>\`
195
- display: none;
196
-
197
- \${mq("lg")} {
198
- display: flex;
199
- gap: 20px;
200
- }
201
- \`;
202
-
203
- const StyledDivider = styled.div<{ theme: Theme }>\`
204
- width: 1px;
205
- height: calc(100% - 10px);
206
- margin: auto 0;
207
- background: \${({ theme }) => theme.colors.grayLight};
208
- \`;
209
-
210
- const StyledWrapper = styled.div<{ theme: Theme }>\`
211
- margin: auto 0;
212
- \`;
213
-
214
58
  function Header() {
215
59
  const [isOptionActive, setIsOptionActive] = useState(false);
216
60
  const [isLangActive, setIsLangActive] = useState(false);
@@ -243,35 +87,12 @@ function Header() {
243
87
  <Logo />
244
88
  </StyledTinyMobileOnly>
245
89
  </Link>
246
- <StyledWrapper>
247
- <span ref={wrapperRef}>
248
- <Flex $gap={20} $wrap="wrap">
249
- <StyledMenu>
250
- <StyledLink href="https://doccupine.com" target="_blank">
251
- Doccupine
252
- </StyledLink>
253
- </StyledMenu>
254
- <Suspense fallback={<ToggleThemeLoading />}>
255
- <ToggleTheme />
256
- </Suspense>
257
- <StyledMobileBurger
258
- $isActive={isMobileMenuActive}
259
- onClick={() => setIsMobileMenuActive(!isMobileMenuActive)}
260
- aria-label="Burger Menu"
261
- />
262
- </Flex>
263
- </span>
264
- </StyledWrapper>
90
+ <Suspense fallback={<ToggleThemeLoading />}>
91
+ <ToggleTheme />
92
+ </Suspense>
265
93
  </Flex>
266
94
  </MaxWidth>
267
95
  </StyledHeader>
268
- <StyledMobileMenu $isActive={isMobileMenuActive}>
269
- <li onClick={() => setIsMobileMenuActive(false)}>
270
- <a href="https://doccupine.com" target="_blank">
271
- Doccupine
272
- </a>
273
- </li>
274
- </StyledMobileMenu>
275
96
  </>
276
97
  );
277
98
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "doccupine",
3
- "version": "0.0.6",
4
- "description": "CLI tool to watch MDX files and generate Next.js beautiful documentation pages automatically",
3
+ "version": "0.0.8",
4
+ "description": "Generate beautiful, ready-to-use documentation with just one CLI command fast, simple, and open source.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
7
7
  "doccupine": "dist/index.js"
@@ -16,18 +16,22 @@
16
16
  "keywords": [
17
17
  "doccupine",
18
18
  "documentation",
19
+ "docs",
19
20
  "mdx",
20
21
  "nextjs",
21
22
  "cli",
23
+ "generator",
22
24
  "file-watcher",
23
25
  "static-site-generator"
24
26
  ],
27
+ "homepage": "https://doccupine.com",
28
+ "repository": "https://github.com/thethemefoundry/doccupine-cli",
25
29
  "author": "Luan Gjokaj",
26
30
  "license": "MIT",
27
31
  "dependencies": {
28
- "chalk": "^5.6.0",
32
+ "chalk": "^5.6.2",
29
33
  "chokidar": "^4.0.3",
30
- "commander": "^14.0.0",
34
+ "commander": "^14.0.1",
31
35
  "fs-extra": "^11.3.1",
32
36
  "gray-matter": "^4.0.3",
33
37
  "prompts": "^2.4.2"
@@ -35,7 +39,7 @@
35
39
  "devDependencies": {
36
40
  "@types/chokidar": "^2.1.7",
37
41
  "@types/fs-extra": "^11.0.4",
38
- "@types/node": "^24.3.1",
42
+ "@types/node": "^24.4.0",
39
43
  "@types/prompts": "^2.4.9",
40
44
  "typescript": "^5.9.2"
41
45
  },