doccupine 0.0.105 → 0.0.107
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/templates/components/Chat.d.ts +1 -1
- package/dist/templates/components/Chat.js +52 -40
- package/dist/templates/components/SearchModalContent.d.ts +1 -1
- package/dist/templates/components/SearchModalContent.js +27 -20
- package/dist/templates/components/layout/Accordion.d.ts +1 -1
- package/dist/templates/components/layout/Accordion.js +14 -4
- package/dist/templates/components/layout/Button.d.ts +1 -1
- package/dist/templates/components/layout/Button.js +5 -1
- package/dist/templates/components/layout/Code.d.ts +1 -1
- package/dist/templates/components/layout/Code.js +14 -4
- package/dist/templates/components/layout/DocsComponents.d.ts +1 -1
- package/dist/templates/components/layout/DocsComponents.js +12 -6
- package/dist/templates/components/layout/Footer.d.ts +1 -1
- package/dist/templates/components/layout/Footer.js +2 -3
- package/dist/templates/components/layout/GlobalStyles.d.ts +1 -1
- package/dist/templates/components/layout/GlobalStyles.js +14 -0
- package/dist/templates/components/layout/Header.d.ts +1 -1
- package/dist/templates/components/layout/Header.js +0 -2
- package/dist/templates/components/layout/SectionBar.d.ts +1 -1
- package/dist/templates/components/layout/SectionBar.js +16 -4
- package/dist/templates/components/layout/SharedStyles.d.ts +1 -1
- package/dist/templates/components/layout/SharedStyles.js +4 -17
- package/dist/templates/mdx/platform/navigation-settings.mdx.d.ts +1 -1
- package/dist/templates/mdx/platform/navigation-settings.mdx.js +47 -8
- package/dist/templates/mdx/update.mdx.d.ts +1 -1
- package/dist/templates/mdx/update.mdx.js +2 -2
- package/dist/templates/package.js +1 -1
- package/package.json +1 -1
|
@@ -167,11 +167,13 @@ const TabList = styled.div<{ theme: Theme }>\`
|
|
|
167
167
|
/* Individual tab button. Active tab reads as part of the window: a light body
|
|
168
168
|
fill (#ffffff) that echoes the code area, with a soft border. Inactive tabs
|
|
169
169
|
are muted and transparent. resetButton strips native styling; focus-visible
|
|
170
|
-
|
|
171
|
-
swap purely via :root.dark,
|
|
170
|
+
draws an inset brand primary ring on a pseudo-element so the scrolling
|
|
171
|
+
TabList can't clip it. All colors stay fixed and swap purely via :root.dark,
|
|
172
|
+
matching the no-re-render palette approach. */
|
|
172
173
|
const CodeTab = styled.button<{ theme: Theme; $active: boolean }>\`
|
|
173
174
|
\${resetButton}
|
|
174
175
|
flex: 0 0 auto;
|
|
176
|
+
position: relative;
|
|
175
177
|
font-family: \${({ theme }) => theme.fonts.mono};
|
|
176
178
|
font-size: 12px;
|
|
177
179
|
line-height: 1;
|
|
@@ -195,8 +197,16 @@ const CodeTab = styled.button<{ theme: Theme; $active: boolean }>\`
|
|
|
195
197
|
}
|
|
196
198
|
|
|
197
199
|
&:focus-visible {
|
|
198
|
-
outline:
|
|
199
|
-
|
|
200
|
+
outline: none;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
&:focus-visible::after {
|
|
204
|
+
content: "";
|
|
205
|
+
position: absolute;
|
|
206
|
+
inset: 2px;
|
|
207
|
+
border: solid 2px \${({ theme }) => theme.colors.primary};
|
|
208
|
+
border-radius: \${({ theme }) => theme.spacing.radius.xs};
|
|
209
|
+
pointer-events: none;
|
|
200
210
|
}
|
|
201
211
|
|
|
202
212
|
:root.dark & {
|
|
@@ -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 focusRing,\n styledAnchor,\n styledTable,\n stylesLists,\n} from \"@/components/layout/SharedStyled\";\nimport { ChatContext } from \"@/components/Chat\";\n\nconst SectionBarContext = createContext(false);\n\nfunction SectionBarProvider({\n hasSectionBar,\n children,\n}: {\n hasSectionBar: boolean;\n children: React.ReactNode;\n}) {\n return (\n <SectionBarContext.Provider value={hasSectionBar}>\n {children}\n </SectionBarContext.Provider>\n );\n}\n\ninterface DocsProps {\n children: React.ReactNode;\n}\n\nconst StyledDocsWrapper = styled.main<{ theme: Theme }>`\n position: relative;\n`;\n\nconst StyledDocsSidebar = styled.div<{ theme: Theme }>`\n clear: both;\n`;\n\nconst StyledDocsContainer = styled.div<{ theme: Theme; $isChatOpen?: boolean }>`\n position: relative;\n padding: 0 20px 100px 20px;\n width: 100%;\n ${({ theme }) => styledText(theme)};\n transition: all 0.3s ease;\n\n ${mq(\"lg\")} {\n padding: 0 300px 80px 300px;\n\n ${({ $isChatOpen }) =>\n $isChatOpen &&\n css`\n padding: 0 440px 80px 300px;\n `}\n }\n\n & p {\n color: ${({ theme }) => theme.colors.grayDark};\n hyphens: auto;\n }\n\n & pre {\n max-width: 100%;\n }\n\n ${styledAnchor};\n ${stylesLists};\n ${styledTable};\n\n & img,\n & video,\n & iframe {\n max-width: 100%;\n border-radius: ${({ theme }) => theme.spacing.radius.lg};\n }\n\n & code:not([class]) {\n background: ${({ theme }) =>\n `color-mix(in srgb, ${theme.colors.primaryLight} 20%, transparent)`};\n color: ${({ theme }) => theme.colors.dark};\n padding: 2px 4px;\n border-radius: ${({ theme }) => theme.spacing.radius.xs};\n white-space: pre;\n }\n\n & .lucide {\n color: ${({ theme }) => theme.colors.primary};\n }\n\n & .aspect-video {\n aspect-ratio: 16 / 9;\n border-radius: ${({ theme }) => theme.spacing.radius.lg};\n }\n`;\n\nexport const StyledMarkdownContainer = styled.div`\n display: flex;\n flex-direction: column;\n gap: 20px;\n flex-wrap: wrap;\n flex: 1;\n max-width: 640px;\n margin: auto;\n`;\n\ninterface Props {\n theme?: Theme;\n $isActive?: boolean;\n $isOpen?: boolean;\n $hasSectionBar?: boolean;\n}\n\nexport const StyledSidebar = styled.nav<Props>`\n position: fixed;\n overflow-y: auto;\n max-height: calc(\n 100dvh - ${({ $hasSectionBar }) => ($hasSectionBar ? 104 : 62)}px\n );\n width: 100%;\n z-index: 99;\n top: ${({ $hasSectionBar }) => ($hasSectionBar ? 104 : 62)}px;\n height: 100%;\n padding: 20px;\n opacity: 0;\n pointer-events: none;\n transition: all 0.3s ease;\n transform: translateY(30px);\n left: 0;\n background: ${({ theme }) => theme.colors.light};\n -webkit-overflow-scrolling: touch;\n display: flex;\n flex-direction: column;\n\n &::-webkit-scrollbar {\n display: none;\n }\n\n ${mq(\"lg\")} {\n border-right: solid 1px ${({ theme }) => theme.colors.grayLight};\n transition: none;\n max-height: 100dvh;\n width: 220px;\n background: transparent;\n padding: 82px 20px 20px 20px;\n opacity: 1;\n pointer-events: all;\n transform: translateY(0);\n background: ${({ theme }) =>\n `color-mix(in srgb, ${theme.colors.primaryLight} 5%, transparent)`};\n top: 0;\n width: 280px;\n }\n\n ${({ $isActive }) =>\n $isActive &&\n css`\n transform: translateY(0);\n opacity: 1;\n pointer-events: all;\n `}\n`;\n\nexport const StyledSidebarFooter = styled.div`\n padding: 22px 20px;\n position: sticky;\n border-top: 1px solid ${({ theme }) => theme.colors.grayLight};\n background: ${({ theme }) =>\n `color-mix(in srgb, ${theme.colors.primaryLight} 5%, transparent)`};\n margin: 0 -20px -20px;\n bottom: -20px;\n backdrop-filter: blur(10px);\n\n ${mq(\"lg\")} {\n padding: 16px 20px;\n }\n`;\n\nexport const StyledIndexSidebar = styled.ul<{ theme: Theme }>`\n display: none;\n list-style: none;\n margin: 0;\n padding: 0;\n position: fixed;\n top: 0;\n right: 0;\n width: 280px;\n height: 100dvh;\n overflow-y: auto;\n z-index: 1;\n padding: 82px 20px 20px 20px;\n background: ${({ theme }) => theme.colors.light};\n border-left: solid 1px ${({ theme }) => theme.colors.grayLight};\n -webkit-overflow-scrolling: touch;\n\n &::-webkit-scrollbar {\n display: none;\n }\n\n ${mq(\"lg\")} {\n display: block;\n }\n\n & li {\n padding: 5px 0;\n }\n`;\n\nexport const StyledIndexSidebarLabel = styled.span<{ theme: Theme }>`\n ${({ theme }) => styledSmall(theme)};\n color: ${({ theme }) => theme.colors.grayDark};\n`;\n\nexport const StyledIndexSidebarLi = styled.li<{\n theme: Theme;\n $isActive: boolean;\n}>`\n &::before {\n content: \"\";\n display: block;\n position: absolute;\n left: 0;\n height: 20px;\n width: 1px;\n background: transparent;\n transition: all 0.3s ease;\n }\n\n ${({ $isActive, theme }) =>\n $isActive &&\n css`\n &::before {\n background: ${theme.colors.primary};\n }\n `}\n`;\n\nexport const StyledIndexSidebarLink = styled.a<{\n theme: Theme;\n $isActive: boolean;\n}>`\n ${({ theme }) => styledSmall(theme)};\n color: ${({ theme, $isActive }) =>\n $isActive ? theme.colors.primary : theme.colors.dark};\n font-weight: ${({ $isActive }) => ($isActive ? \"600\" : \"400\")};\n text-decoration: none;\n transition: all 0.3s ease;\n ${focusRing};\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 ${focusRing};\n\n ${({ $isActive, theme }) =>\n $isActive &&\n css`\n color: ${theme.colors.accentStrong};\n border-color: ${theme.colors.primary};\n font-weight: 600;\n `};\n`;\n\n// The collapse chevron points right when closed and rotates to point down\n// when the group is open.\nconst sidebarChevron = css<Props>`\n & .lucide-chevron-right {\n margin-left: auto;\n transition: transform 0.3s ease;\n\n ${({ $isOpen }) =>\n $isOpen &&\n css`\n transform: rotate(90deg);\n `}\n }\n`;\n\nexport const StyledSidebarListItemLink = styled(Link)<Props>`\n ${sidebarRowStyles};\n`;\n\n// Nested navigation group. The header shares the link appearance/hover; a\n// non-navigable group is a single toggle button, while a group that is also a\n// page pairs a link with a chevron toggle. Children live in\n// StyledSidebarGroupContent, which animates open/closed via height 0 <-> auto\n// (enabled by interpolate-size in GlobalStyles, same as the Accordion).\nexport const StyledSidebarGroupButton = styled.button<Props>`\n ${resetButton};\n ${sidebarRowStyles};\n ${sidebarChevron};\n width: 100%;\n text-align: left;\n`;\n\nexport const StyledSidebarGroupRow = styled.div<Props>`\n ${sidebarRowStyles};\n ${sidebarChevron};\n\n /* 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 4px ${({ 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). */\n &:focus-visible {\n outline: 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 background: ${({ theme }) =>\n `color-mix(in srgb, ${theme.colors.primaryLight} 20%, transparent)`};\n color: ${({ theme }) => theme.colors.dark};\n padding: 2px 4px;\n border-radius: ${({ theme }) => theme.spacing.radius.xs};\n white-space: pre;\n }\n\n & .lucide {\n color: ${({ theme }) => theme.colors.primary};\n }\n\n & .aspect-video {\n aspect-ratio: 16 / 9;\n border-radius: ${({ theme }) => theme.spacing.radius.lg};\n }\n`;\n\nexport const StyledMarkdownContainer = styled.div`\n display: flex;\n flex-direction: column;\n gap: 20px;\n flex-wrap: wrap;\n flex: 1;\n max-width: 640px;\n margin: auto;\n`;\n\ninterface Props {\n theme?: Theme;\n $isActive?: boolean;\n $isOpen?: boolean;\n $hasSectionBar?: boolean;\n}\n\nexport const StyledSidebar = styled.nav<Props>`\n position: fixed;\n overflow-y: auto;\n max-height: calc(\n 100dvh - ${({ $hasSectionBar }) => ($hasSectionBar ? 104 : 62)}px\n );\n width: 100%;\n z-index: 99;\n top: ${({ $hasSectionBar }) => ($hasSectionBar ? 104 : 62)}px;\n height: 100%;\n padding: 20px;\n opacity: 0;\n pointer-events: none;\n transition: all 0.3s ease;\n transform: translateY(30px);\n left: 0;\n background: ${({ theme }) => theme.colors.light};\n -webkit-overflow-scrolling: touch;\n display: flex;\n flex-direction: column;\n\n &::-webkit-scrollbar {\n display: none;\n }\n\n ${mq(\"lg\")} {\n border-right: solid 1px ${({ theme }) => theme.colors.grayLight};\n transition: none;\n max-height: 100dvh;\n width: 220px;\n background: transparent;\n padding: 82px 20px 20px 20px;\n opacity: 1;\n pointer-events: all;\n transform: translateY(0);\n background: ${({ theme }) =>\n `color-mix(in srgb, ${theme.colors.primaryLight} 5%, transparent)`};\n top: 0;\n width: 280px;\n }\n\n ${({ $isActive }) =>\n $isActive &&\n css`\n transform: translateY(0);\n opacity: 1;\n pointer-events: all;\n `}\n`;\n\nexport const StyledSidebarFooter = styled.div`\n padding: 22px 20px;\n position: sticky;\n border-top: 1px solid ${({ theme }) => theme.colors.grayLight};\n background: ${({ theme }) =>\n `color-mix(in srgb, ${theme.colors.primaryLight} 5%, transparent)`};\n margin: 0 -20px -20px;\n bottom: -20px;\n backdrop-filter: blur(10px);\n\n ${mq(\"lg\")} {\n padding: 16px 20px;\n }\n`;\n\nexport const StyledIndexSidebar = styled.ul<{ theme: Theme }>`\n display: none;\n list-style: none;\n margin: 0;\n padding: 0;\n position: fixed;\n top: 0;\n right: 0;\n width: 280px;\n height: 100dvh;\n overflow-y: auto;\n z-index: 1;\n padding: 82px 20px 20px 20px;\n background: ${({ theme }) => theme.colors.light};\n border-left: solid 1px ${({ theme }) => theme.colors.grayLight};\n -webkit-overflow-scrolling: touch;\n\n &::-webkit-scrollbar {\n display: none;\n }\n\n ${mq(\"lg\")} {\n display: block;\n }\n\n & li {\n padding: 5px 0;\n }\n`;\n\nexport const StyledIndexSidebarLabel = styled.span<{ theme: Theme }>`\n ${({ theme }) => styledSmall(theme)};\n color: ${({ theme }) => theme.colors.grayDark};\n`;\n\nexport const StyledIndexSidebarLi = styled.li<{\n theme: Theme;\n $isActive: boolean;\n}>`\n &::before {\n content: \"\";\n display: block;\n position: absolute;\n left: 0;\n height: 20px;\n width: 1px;\n background: transparent;\n transition: all 0.3s ease;\n }\n\n ${({ $isActive, theme }) =>\n $isActive &&\n css`\n &::before {\n background: ${theme.colors.primary};\n }\n `}\n`;\n\nexport const StyledIndexSidebarLink = styled.a<{\n theme: Theme;\n $isActive: boolean;\n}>`\n ${({ theme }) => styledSmall(theme)};\n color: ${({ theme, $isActive }) =>\n $isActive ? theme.colors.primary : theme.colors.dark};\n font-weight: ${({ $isActive }) => ($isActive ? \"600\" : \"400\")};\n text-decoration: none;\n transition: all 0.3s ease;\n\n &:hover {\n color: ${({ theme }) => theme.colors.primary};\n }\n`;\n\nexport const StyledSidebarList = styled.ul`\n list-style: none;\n margin: 0;\n padding: 0;\n\n &:last-of-type {\n margin-bottom: auto;\n }\n`;\n\nexport const StyledStrong = styled.strong<{ theme: Theme }>`\n font-weight: 600;\n ${({ theme }) => styledStrong(theme)};\n color: ${({ theme }) => theme.colors.accentStrong};\n display: inline-flex;\n align-items: center;\n gap: 8px;\n\n & svg {\n flex-shrink: 0;\n }\n`;\n\nexport const StyledSidebarListItem = styled.li`\n display: flex;\n gap: 10px;\n clear: both;\n`;\n\n// Shared appearance for every sidebar row - leaf links AND nested group\n// headers - so hover, active state, and the left rail read identically.\nconst sidebarRowStyles = css<Props>`\n text-decoration: none;\n font-size: ${({ theme }) => theme.fontSizes.small.lg};\n line-height: 1.6;\n color: ${({ theme }) => theme.colors.accentMuted};\n padding: 5px 0 5px 20px;\n display: flex;\n align-items: center;\n gap: 8px;\n flex: 1;\n min-width: 0;\n transition: all 0.3s ease;\n border-left: solid 1px ${({ theme }) => theme.colors.grayLight};\n\n & svg {\n flex-shrink: 0;\n }\n\n &:hover {\n color: ${({ theme }) => theme.colors.accent};\n border-color: ${({ theme }) => theme.colors.primary};\n }\n\n ${({ $isActive, theme }) =>\n $isActive &&\n css`\n color: ${theme.colors.accentStrong};\n border-color: ${theme.colors.primary};\n font-weight: 600;\n `};\n`;\n\n// The collapse chevron points right when closed and rotates to point down\n// when the group is open.\nconst sidebarChevron = css<Props>`\n & .lucide-chevron-right {\n margin-left: auto;\n transition: transform 0.3s ease;\n\n ${({ $isOpen }) =>\n $isOpen &&\n css`\n transform: rotate(90deg);\n `}\n }\n`;\n\nexport const StyledSidebarListItemLink = styled(Link)<Props>`\n ${sidebarRowStyles};\n`;\n\n// Nested navigation group. The header shares the link appearance/hover; a\n// non-navigable group is a single toggle button, while a group that is also a\n// page pairs a link with a chevron toggle. Children live in\n// StyledSidebarGroupContent, which animates open/closed via height 0 <-> auto\n// (enabled by interpolate-size in GlobalStyles, same as the Accordion).\nexport const StyledSidebarGroupButton = styled.button<Props>`\n ${resetButton};\n ${sidebarRowStyles};\n ${sidebarChevron};\n width: 100%;\n text-align: left;\n\n /* 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";
|
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
import Link from "next/link";
|
|
12
12
|
import { mq, Theme } from "@/app/theme";
|
|
13
13
|
import {
|
|
14
|
-
focusRing,
|
|
15
14
|
styledAnchor,
|
|
16
15
|
styledTable,
|
|
17
16
|
stylesLists,
|
|
@@ -253,7 +252,6 @@ export const StyledIndexSidebarLink = styled.a<{
|
|
|
253
252
|
font-weight: \${({ $isActive }) => ($isActive ? "600" : "400")};
|
|
254
253
|
text-decoration: none;
|
|
255
254
|
transition: all 0.3s ease;
|
|
256
|
-
\${focusRing};
|
|
257
255
|
|
|
258
256
|
&:hover {
|
|
259
257
|
color: \${({ theme }) => theme.colors.primary};
|
|
@@ -314,8 +312,6 @@ const sidebarRowStyles = css<Props>\`
|
|
|
314
312
|
border-color: \${({ theme }) => theme.colors.primary};
|
|
315
313
|
}
|
|
316
314
|
|
|
317
|
-
\${focusRing};
|
|
318
|
-
|
|
319
315
|
\${({ $isActive, theme }) =>
|
|
320
316
|
$isActive &&
|
|
321
317
|
css\`
|
|
@@ -355,6 +351,14 @@ export const StyledSidebarGroupButton = styled.button<Props>\`
|
|
|
355
351
|
\${sidebarChevron};
|
|
356
352
|
width: 100%;
|
|
357
353
|
text-align: left;
|
|
354
|
+
|
|
355
|
+
/* Buttons aren't covered by the global a:focus-visible ring, so this
|
|
356
|
+
nav-row toggle carries its own copy of it. */
|
|
357
|
+
&:focus-visible {
|
|
358
|
+
outline: none;
|
|
359
|
+
border-radius: \${({ theme }) => theme.spacing.radius.xs};
|
|
360
|
+
box-shadow: 0 0 0 2px \${({ theme }) => theme.colors.primaryLight};
|
|
361
|
+
}
|
|
358
362
|
\`;
|
|
359
363
|
|
|
360
364
|
export const StyledSidebarGroupRow = styled.div<Props>\`
|
|
@@ -366,7 +370,7 @@ export const StyledSidebarGroupRow = styled.div<Props>\`
|
|
|
366
370
|
match the leaf-row focus treatment. */
|
|
367
371
|
&:has(:focus-visible) {
|
|
368
372
|
border-radius: \${({ theme }) => theme.spacing.radius.xs};
|
|
369
|
-
box-shadow: 0 0 0
|
|
373
|
+
box-shadow: 0 0 0 2px \${({ theme }) => theme.colors.primaryLight};
|
|
370
374
|
}
|
|
371
375
|
\`;
|
|
372
376
|
|
|
@@ -383,9 +387,11 @@ export const StyledSidebarGroupLink = styled(Link)\`
|
|
|
383
387
|
flex-shrink: 0;
|
|
384
388
|
}
|
|
385
389
|
|
|
386
|
-
/* Ring is drawn on the parent row via :has(:focus-visible)
|
|
390
|
+
/* Ring is drawn on the parent row via :has(:focus-visible); suppress the
|
|
391
|
+
global a:focus-visible glow so it isn't drawn twice. */
|
|
387
392
|
&:focus-visible {
|
|
388
393
|
outline: none;
|
|
394
|
+
box-shadow: none;
|
|
389
395
|
}
|
|
390
396
|
\`;
|
|
391
397
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const footerTemplate = "\"use client\";\nimport { useContext } from \"react\";\nimport styled, { css } from \"styled-components\";\nimport { Space, styledSmall } from \"cherry-styled-components\";\nimport { ChatContext } from \"@/components/Chat\";\nimport { mq, Theme } from \"@/app/theme\";\nimport { GitHubLogo } from \"@/components/layout/Pictograms\";\nimport
|
|
1
|
+
export declare const footerTemplate = "\"use client\";\nimport { useContext } from \"react\";\nimport styled, { css } from \"styled-components\";\nimport { Space, styledSmall } from \"cherry-styled-components\";\nimport { ChatContext } from \"@/components/Chat\";\nimport { mq, Theme } from \"@/app/theme\";\nimport { GitHubLogo } from \"@/components/layout/Pictograms\";\nimport linksData from \"@/links.json\";\n\ninterface LinkProps {\n title: string;\n url: string;\n icon?: string;\n}\n\nconst links = linksData as LinkProps[];\n\nconst StyledFooter = styled.footer<{\n theme: Theme;\n $isChatOpen?: boolean;\n $hasLinks?: boolean;\n}>`\n padding: 0 20px;\n transition: all 0.3s ease;\n\n ${({ $hasLinks }) =>\n $hasLinks &&\n css`\n margin-top: 20px;\n `}\n\n ${mq(\"lg\")} {\n margin: 0;\n padding: 0 300px 0 300px;\n\n ${({ $isChatOpen }) =>\n $isChatOpen &&\n css`\n padding: 0 440px 0 300px;\n `}\n }\n`;\n\nconst StyledFooterInner = styled.div<{ theme: Theme }>`\n border-top: solid 1px ${({ theme }) => theme.colors.grayLight};\n max-width: 640px;\n margin: 0 auto;\n padding: 28px 0;\n color: ${({ theme }) => theme.colors.grayDark};\n ${({ theme }) => styledSmall(theme)};\n\n ${mq(\"lg\")} {\n padding: 20px 0;\n }\n\n & a {\n font-weight: 700;\n color: ${({ theme }) => theme.colors.accent};\n text-decoration: none;\n transition: all 0.3s ease;\n display: inline-flex;\n\n &:hover {\n color: ${({ theme }) => theme.colors.primary};\n }\n\n /* The GitHub logo is an icon-only link - round the global focus ring so it\n hugs the icon instead of drawing a rectangle. */\n &:has(svg):focus-visible {\n border-radius: 100%;\n }\n\n & svg {\n width: 18px;\n height: 18px;\n }\n }\n`;\n\nconst StyledFooterFlex = styled.div`\n display: flex;\n justify-content: flex-start;\n align-items: center;\n gap: 20px;\n\n ${mq(\"lg\")} {\n justify-content: space-between;\n }\n`;\n\nfunction Footer({ hideBranding }: { hideBranding?: boolean }) {\n const { isOpen } = useContext(ChatContext);\n\n if (hideBranding) return <Space $xs={80} $lg=\"none\" />;\n\n return (\n <StyledFooter $isChatOpen={isOpen} $hasLinks={links.length > 0}>\n <StyledFooterInner>\n <StyledFooterFlex>\n <span>\n Powered by{\" \"}\n <a\n href=\"https://doccupine.com\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n Doccupine\n </a>\n </span>\n <a\n href=\"https://github.com/doccupine/cli\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n aria-label=\"Doccupine on GitHub\"\n >\n <GitHubLogo />\n </a>\n </StyledFooterFlex>\n </StyledFooterInner>\n </StyledFooter>\n );\n}\n\nexport { Footer };\n";
|
|
@@ -6,7 +6,6 @@ import { Space, styledSmall } from "cherry-styled-components";
|
|
|
6
6
|
import { ChatContext } from "@/components/Chat";
|
|
7
7
|
import { mq, Theme } from "@/app/theme";
|
|
8
8
|
import { GitHubLogo } from "@/components/layout/Pictograms";
|
|
9
|
-
import { focusRing } from "@/components/layout/SharedStyled";
|
|
10
9
|
import linksData from "@/links.json";
|
|
11
10
|
|
|
12
11
|
interface LinkProps {
|
|
@@ -61,13 +60,13 @@ const StyledFooterInner = styled.div<{ theme: Theme }>\`
|
|
|
61
60
|
text-decoration: none;
|
|
62
61
|
transition: all 0.3s ease;
|
|
63
62
|
display: inline-flex;
|
|
64
|
-
\${focusRing};
|
|
65
63
|
|
|
66
64
|
&:hover {
|
|
67
65
|
color: \${({ theme }) => theme.colors.primary};
|
|
68
66
|
}
|
|
69
67
|
|
|
70
|
-
/* The GitHub logo is an icon-only link - round
|
|
68
|
+
/* The GitHub logo is an icon-only link - round the global focus ring so it
|
|
69
|
+
hugs the icon instead of drawing a rectangle. */
|
|
71
70
|
&:has(svg):focus-visible {
|
|
72
71
|
border-radius: 100%;
|
|
73
72
|
}
|
|
@@ -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\nol,\nul {\n list-style: none;\n margin: 0;\n padding: 0;\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 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}\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";
|
|
@@ -118,6 +118,20 @@ a {
|
|
|
118
118
|
color: var(--color-primary);
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
+
/* Keyboard-focus ring, defined once globally so links don't each re-declare it.
|
|
122
|
+
:focus-visible keeps it keyboard-only (no ring on mouse click); box-shadow
|
|
123
|
+
gives a soft primaryLight glow that follows the element's border-radius. The
|
|
124
|
+
6px radius mirrors theme.spacing.radius.xs so the ring rounds on links that
|
|
125
|
+
have no radius of their own. Cherry link-buttons (.button-link) are excluded
|
|
126
|
+
via :where() so the exclusion adds no specificity - this keeps the selector
|
|
127
|
+
at (0,1,1) so bespoke per-component :focus-visible rules (inline content
|
|
128
|
+
links, the section bar, the sidebar group rows) still override it. */
|
|
129
|
+
a:focus-visible:where(:not(.button-link)) {
|
|
130
|
+
outline: none;
|
|
131
|
+
border-radius: 6px;
|
|
132
|
+
box-shadow: 0 0 0 2px var(--color-primaryLight);
|
|
133
|
+
}
|
|
134
|
+
|
|
121
135
|
ol,
|
|
122
136
|
ul {
|
|
123
137
|
list-style: none;
|
|
@@ -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 {
|
|
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 & path[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";
|
|
@@ -4,7 +4,6 @@ import { useCallback, useContext, useRef, useState } from "react";
|
|
|
4
4
|
import styled, { css } from "styled-components";
|
|
5
5
|
import Link from "next/link";
|
|
6
6
|
import { mq, Theme } from "@/app/theme";
|
|
7
|
-
import { focusRing } from "@/components/layout/SharedStyled";
|
|
8
7
|
import { useOnClickOutside } from "cherry-styled-components";
|
|
9
8
|
import { Search } from "lucide-react";
|
|
10
9
|
import { Logo } from "@/components/layout/Pictograms";
|
|
@@ -60,7 +59,6 @@ const StyledHeader = styled.header<{ theme: Theme; $hasChildren: boolean }>\`
|
|
|
60
59
|
& .logo {
|
|
61
60
|
display: flex;
|
|
62
61
|
min-width: max-content;
|
|
63
|
-
\${focusRing};
|
|
64
62
|
|
|
65
63
|
& svg,
|
|
66
64
|
& img {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const sectionBarTemplate = "\"use client\";\nimport { usePathname } from \"next/navigation\";\nimport Link from \"next/link\";\nimport styled from \"styled-components\";\nimport { styledText } from \"cherry-styled-components\";\nimport { mq, Theme } from \"@/app/theme\";\n\ninterface SectionConfig {\n label: string;\n slug: string;\n directory?: string;\n}\n\ninterface SectionBarProps {\n sections: SectionConfig[];\n}\n\nconst StyledSectionBar = styled.nav<{ theme: Theme }>`\n display: flex;\n order: 3;\n width: calc(100% + 20px);\n margin: 0 0 0 -10px;\n padding: 0;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n position: relative;\n\n &::-webkit-scrollbar {\n display: none;\n }\n\n ${mq(\"lg\")} {\n padding: 0 10px;\n order: unset;\n width: 100%;\n margin: 0;\n justify-content: flex-end;\n }\n`;\n\nconst StyledSectionLink = styled(Link)<{\n theme: Theme;\n $isActive: boolean;\n}>`\n ${({ theme }) => styledText(theme)};\n text-decoration: none;\n padding: 16px 10px;\n white-space: nowrap;\n font-weight: ${({ $isActive }) => ($isActive ? \"600\" : \"500\")};\n color: ${({ theme, $isActive }) =>\n $isActive ? theme.colors.primary : theme.colors.gray};\n border-bottom: solid 2px\n ${({ theme, $isActive }) =>\n $isActive ? theme.colors.primary : \"transparent\"};\n transition: all 0.3s ease;\n min-width: fit-content;\n\n /*
|
|
1
|
+
export declare const sectionBarTemplate = "\"use client\";\nimport { usePathname } from \"next/navigation\";\nimport Link from \"next/link\";\nimport styled from \"styled-components\";\nimport { styledText } from \"cherry-styled-components\";\nimport { mq, Theme } from \"@/app/theme\";\n\ninterface SectionConfig {\n label: string;\n slug: string;\n directory?: string;\n}\n\ninterface SectionBarProps {\n sections: SectionConfig[];\n}\n\nconst StyledSectionBar = styled.nav<{ theme: Theme }>`\n display: flex;\n order: 3;\n width: calc(100% + 20px);\n margin: 0 0 0 -10px;\n padding: 0;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n position: relative;\n\n &::-webkit-scrollbar {\n display: none;\n }\n\n ${mq(\"lg\")} {\n padding: 0 10px;\n order: unset;\n width: 100%;\n margin: 0;\n justify-content: flex-end;\n }\n`;\n\nconst StyledSectionLink = styled(Link)<{\n theme: Theme;\n $isActive: boolean;\n}>`\n ${({ theme }) => styledText(theme)};\n text-decoration: none;\n padding: 16px 10px;\n white-space: nowrap;\n font-weight: ${({ $isActive }) => ($isActive ? \"600\" : \"500\")};\n color: ${({ theme, $isActive }) =>\n $isActive ? theme.colors.primary : theme.colors.gray};\n border-bottom: solid 2px\n ${({ theme, $isActive }) =>\n $isActive ? theme.colors.primary : \"transparent\"};\n transition: all 0.3s ease;\n min-width: fit-content;\n position: relative;\n\n /* The section bar scrolls horizontally, and overflow-x:auto makes it clip\n vertically too, so an edge-drawn ring gets shaved top and bottom. Draw the\n ring on an inset pseudo-element so it sits fully inside the link and can't\n be clipped on any side. */\n &:focus-visible {\n outline: none;\n /* Opt out of the global a:focus-visible ring; the ::after below draws it\n inset so the scroll container can't clip it. */\n box-shadow: none;\n }\n\n &:focus-visible::after {\n content: \"\";\n position: absolute;\n inset: 3px;\n border: solid 2px ${({ theme }) => theme.colors.primaryLight};\n border-radius: ${({ theme }) => theme.spacing.radius.xs};\n pointer-events: none;\n }\n\n &:hover {\n color: ${({ theme }) => theme.colors.primary};\n }\n`;\n\nfunction SectionBar({ sections }: SectionBarProps) {\n const pathname = usePathname();\n const currentPath = pathname.replace(/^\\//, \"\").replace(/\\/$/, \"\");\n\n const activeSection = sections.find((section) => {\n if (section.slug === \"\") return false;\n return (\n currentPath === section.slug || currentPath.startsWith(section.slug + \"/\")\n );\n });\n\n const activeSectionSlug = activeSection ? activeSection.slug : \"\";\n\n return (\n <StyledSectionBar>\n {sections.map((section) => (\n <StyledSectionLink\n key={section.slug}\n href={section.slug === \"\" ? \"/\" : `/${section.slug}`}\n $isActive={activeSectionSlug === section.slug}\n >\n {section.label}\n </StyledSectionLink>\n ))}\n </StyledSectionBar>\n );\n}\n\nexport { SectionBar };\n";
|
|
@@ -54,14 +54,26 @@ const StyledSectionLink = styled(Link)<{
|
|
|
54
54
|
$isActive ? theme.colors.primary : "transparent"};
|
|
55
55
|
transition: all 0.3s ease;
|
|
56
56
|
min-width: fit-content;
|
|
57
|
+
position: relative;
|
|
57
58
|
|
|
58
|
-
/*
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
/* The section bar scrolls horizontally, and overflow-x:auto makes it clip
|
|
60
|
+
vertically too, so an edge-drawn ring gets shaved top and bottom. Draw the
|
|
61
|
+
ring on an inset pseudo-element so it sits fully inside the link and can't
|
|
62
|
+
be clipped on any side. */
|
|
61
63
|
&:focus-visible {
|
|
62
64
|
outline: none;
|
|
65
|
+
/* Opt out of the global a:focus-visible ring; the ::after below draws it
|
|
66
|
+
inset so the scroll container can't clip it. */
|
|
67
|
+
box-shadow: none;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
&:focus-visible::after {
|
|
71
|
+
content: "";
|
|
72
|
+
position: absolute;
|
|
73
|
+
inset: 3px;
|
|
74
|
+
border: solid 2px \${({ theme }) => theme.colors.primaryLight};
|
|
63
75
|
border-radius: \${({ theme }) => theme.spacing.radius.xs};
|
|
64
|
-
|
|
76
|
+
pointer-events: none;
|
|
65
77
|
}
|
|
66
78
|
|
|
67
79
|
&:hover {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const sharedStyledTemplate = "\"use client\";\nimport { styledSmall, styledText } from \"cherry-styled-components\";\nimport { mq, Theme } from \"@/app/theme\";\nimport styled, { css } from \"styled-components\";\n\n/** Slim, theme-aware scrollbar for internal scroll areas (code blocks,\n modals, tables) so the chunky native bar doesn't stand out, especially\n in dark mode. */\nexport const thinScrollbar = css<{ theme: Theme }>`\n scrollbar-width: thin;\n scrollbar-color: ${({ theme }) => theme.colors.grayLight} transparent;\n`;\n\nexport const interactiveStyles = css<{ theme: Theme }>`\n transition: all 0.3s ease;\n border: solid 1px transparent;\n box-shadow: 0 0 0 0px ${({ theme }) => theme.colors.primary};\n\n &:hover {\n border-color: ${({ theme }) => theme.colors.primary};\n }\n\n &:focus {\n border-color: ${({ theme }) => theme.colors.primary};\n box-shadow: 0 0 0 4px ${({ theme }) => theme.colors.primaryLight};\n }\n\n &:active {\n box-shadow: 0 0 0 2px ${({ theme }) => theme.colors.primaryLight};\n }\n`;\n\
|
|
1
|
+
export declare const sharedStyledTemplate = "\"use client\";\nimport { styledSmall, styledText } from \"cherry-styled-components\";\nimport { mq, Theme } from \"@/app/theme\";\nimport styled, { css } from \"styled-components\";\n\n/** Slim, theme-aware scrollbar for internal scroll areas (code blocks,\n modals, tables) so the chunky native bar doesn't stand out, especially\n in dark mode. */\nexport const thinScrollbar = css<{ theme: Theme }>`\n scrollbar-width: thin;\n scrollbar-color: ${({ theme }) => theme.colors.grayLight} transparent;\n`;\n\nexport const interactiveStyles = css<{ theme: Theme }>`\n transition: all 0.3s ease;\n border: solid 1px transparent;\n box-shadow: 0 0 0 0px ${({ theme }) => theme.colors.primary};\n\n &:hover {\n border-color: ${({ theme }) => theme.colors.primary};\n }\n\n &:focus {\n border-color: ${({ theme }) => theme.colors.primary};\n box-shadow: 0 0 0 4px ${({ theme }) => theme.colors.primaryLight};\n }\n\n &:active {\n box-shadow: 0 0 0 2px ${({ theme }) => theme.colors.primaryLight};\n }\n`;\n\nexport const styledAnchor = css<{ theme: Theme }>`\n & a:not([class]):not(:has(img)) {\n color: inherit;\n transition: all 0.3s ease;\n text-decoration: none;\n box-shadow: 0 2px 0 0 ${({ theme }) => theme.colors.primary};\n\n &:hover {\n color: ${({ theme }) => theme.colors.accent};\n box-shadow: 0 1px 0 0 ${({ theme }) => theme.colors.primary};\n }\n\n /* Same primaryLight focus glow as the global a:focus-visible ring, but\n layered over the link's own box-shadow underline (same property) so the\n underline is preserved. */\n &:focus-visible {\n outline: none;\n border-radius: ${({ theme }) => theme.spacing.radius.xs};\n box-shadow:\n 0 2px 0 0 ${({ theme }) => theme.colors.primary},\n 0 0 0 2px ${({ theme }) => theme.colors.primaryLight};\n }\n }\n`;\n\nexport const stylesLists = css<{ theme: Theme }>`\n & ul,\n & ol {\n & li {\n & > .code-wrapper {\n margin: 10px 0;\n }\n }\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 & ul {\n padding-left: 15px;\n }\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 styledTable = css<{ theme: Theme }>`\n & .table-wrapper {\n overflow-x: auto;\n width: 100%;\n ${thinScrollbar};\n }\n\n & table {\n margin: 0;\n padding: 0;\n border-collapse: collapse;\n width: 100%;\n text-align: left;\n\n & tr {\n margin: 0;\n padding: 0;\n }\n\n & th {\n border-bottom: solid 1px ${({ theme }) => theme.colors.grayLight};\n padding: 10px 10px 10px 0;\n ${({ theme }) => styledSmall(theme)};\n font-weight: 600;\n color: ${({ theme }) => theme.colors.dark};\n }\n\n & td {\n border-bottom: solid 1px ${({ theme }) => theme.colors.grayLight};\n padding: 10px 10px 10px 0;\n color: ${({ theme }) => theme.colors.grayDark};\n ${({ theme }) => styledSmall(theme)};\n }\n }\n`;\n\nexport const StyledSmallButton = styled.button<{ theme: Theme }>`\n ${interactiveStyles};\n background: ${({ theme }) => theme.colors.light};\n border: solid 1px ${({ theme }) => theme.colors.grayLight};\n color: ${({ theme }) => theme.colors.accent};\n border-radius: ${({ theme }) => theme.spacing.radius.xs};\n padding: 6px 8px;\n font-size: 12px;\n font-family: inherit;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s ease;\n display: flex;\n align-items: center;\n gap: 6px;\n margin-right: -6px;\n\n & svg.lucide {\n color: inherit;\n }\n`;\n";
|
|
@@ -30,20 +30,6 @@ export const interactiveStyles = css<{ theme: Theme }>\`
|
|
|
30
30
|
}
|
|
31
31
|
\`;
|
|
32
32
|
|
|
33
|
-
/** Keyboard-focus ring for bare navigation links (header links, sidebar rows,
|
|
34
|
-
footer links) that don't carry the full interactiveStyles treatment. Mirrors
|
|
35
|
-
the primaryLight glow from interactiveStyles, but uses :focus-visible so the
|
|
36
|
-
ring only shows for keyboard users - not on mouse click. Pair with an element
|
|
37
|
-
that already has a transition for a smooth fade. The small border-radius
|
|
38
|
-
rounds the ring, so don't apply this to elements that set their own radius. */
|
|
39
|
-
export const focusRing = css<{ theme?: Theme }>\`
|
|
40
|
-
&:focus-visible {
|
|
41
|
-
outline: none;
|
|
42
|
-
border-radius: \${({ theme }) => theme.spacing.radius.xs};
|
|
43
|
-
box-shadow: 0 0 0 4px \${({ theme }) => theme.colors.primaryLight};
|
|
44
|
-
}
|
|
45
|
-
\`;
|
|
46
|
-
|
|
47
33
|
export const styledAnchor = css<{ theme: Theme }>\`
|
|
48
34
|
& a:not([class]):not(:has(img)) {
|
|
49
35
|
color: inherit;
|
|
@@ -56,14 +42,15 @@ export const styledAnchor = css<{ theme: Theme }>\`
|
|
|
56
42
|
box-shadow: 0 1px 0 0 \${({ theme }) => theme.colors.primary};
|
|
57
43
|
}
|
|
58
44
|
|
|
59
|
-
/* Same primaryLight focus glow as
|
|
60
|
-
own box-shadow underline (same property) so the
|
|
45
|
+
/* Same primaryLight focus glow as the global a:focus-visible ring, but
|
|
46
|
+
layered over the link's own box-shadow underline (same property) so the
|
|
47
|
+
underline is preserved. */
|
|
61
48
|
&:focus-visible {
|
|
62
49
|
outline: none;
|
|
63
50
|
border-radius: \${({ theme }) => theme.spacing.radius.xs};
|
|
64
51
|
box-shadow:
|
|
65
52
|
0 2px 0 0 \${({ theme }) => theme.colors.primary},
|
|
66
|
-
0 0 0
|
|
53
|
+
0 0 0 2px \${({ theme }) => theme.colors.primaryLight};
|
|
67
54
|
}
|
|
68
55
|
}
|
|
69
56
|
\`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const platformNavigationSettingsMdxTemplate = "---\ntitle: \"Navigation Settings\"\ndescription: \"Define the sidebar structure for your documentation site using the drag-and-drop Navigation Builder.\"\ndate: \"2026-
|
|
1
|
+
export declare const platformNavigationSettingsMdxTemplate = "---\ntitle: \"Navigation Settings\"\ndescription: \"Define the sidebar structure for your documentation site using the drag-and-drop Navigation Builder.\"\ndate: \"2026-07-06\"\ncategory: \"Configuration\"\ncategoryOrder: 2\norder: 2\nsection: \"Platform\"\n---\n\n# Navigation Settings\n\nThe Navigation Builder lets you define your sidebar structure through a visual, drag-and-drop interface. It lives inside the **File Explorer** as a dedicated **Navigation** tab, alongside the Files and Media tabs.\n\n## Structure\n\nNavigation is organized into these levels:\n\n- **Section** - a top-level area of your site (e.g., \"Docs\", \"API Reference\"). Each section appears as a tab below the site header and has its own sidebar. Sections are defined by a label, URL slug, and docs directory.\n- **Category** - a group label within a section's sidebar (e.g., \"Getting Started\"). Categories can have an optional icon.\n- **Link** - an individual page entry within a category, defined by a slug and title, with an optional icon.\n- **Group** - a link that holds its own nested links, rendered as a collapsible item in the sidebar. Groups can nest as deep as you need. See [Nested groups](#nested-groups).\n\n## Drag-and-drop reordering\n\nYou can reorder items at every level by dragging their handles:\n\n- **Drag categories** between sections or within the same section to change their position\n- **Drag links** between categories or within the same category to reorder them\n\n## Managing sections\n\n- Click **Add Section** in the toolbar to create a new section. Fill in the label, slug, and directory fields.\n- Click the **edit** button on a section header to open its edit modal, where you can update the label, slug, and directory.\n- Delete a section from its edit modal using the delete button. The root section cannot be deleted.\n\n### The default section\n\nOne section should have an empty slug (`\"\"`). This is the default/root section that serves pages at the root URL. Pages not assigned to any other section belong here.\n\n### Frontmatter-based sections\n\nYou can also define sections purely through page frontmatter without using the Navigation Builder. Add a `section` field to your MDX frontmatter and Doccupine will create sections automatically. See the [Sections documentation](/sections) for details.\n\n## Managing categories\n\n- Click **Add Category** within a section to create a new group\n- Click the **edit** button on a category to open its **Edit Category** modal, where you can rename it, pick an icon, or delete it\n\n## Adding links\n\nWithin each category, click **Add files** to open a popover that lists available MDX files not yet included in the navigation. You can:\n\n- **Search** by file name to filter the list\n- **Select** one or more files using checkboxes\n- Click **Add** to insert them as links in the category\n\n### Editing a link\n\n- **Double-click** a link's title to rename it inline.\n- Click the **edit** button on a link to open the **Edit Link** modal, where you can change its title, slug, and icon. Leaving the slug empty makes a leaf link point at the index page; for a group it makes the group a non-clickable label.\n- Click the **delete** button next to a link to remove it.\n\n## Icons\n\nYou can show a [Lucide](https://lucide.dev/icons) icon next to any category or link to make the sidebar easier to scan.\n\n- Set a **category icon** from the Edit Category modal.\n- Set a **link icon** from the Edit Link modal.\n\nBoth use a searchable icon picker, and icon names are stored in kebab-case (e.g. `rocket`, `book-open`, `settings`). You can also set icons from page frontmatter using the `navIcon` (link) and `categoryIcon` (category) fields, which the **Regenerate** button picks up.\n\n## Nested groups\n\nA link can hold its own child links, rendering as a collapsible group in the sidebar. Nesting can go as deep as you need.\n\nThere are two ways to create a group:\n\n- Click **Add group** (in a category or inside another group) to create a collapsible group. Give it a title in the Edit Link modal, then leave its slug empty to make it a plain, non-clickable label, or set a slug to make the group header link to its own page.\n- Click **Add pages inside** on an existing link to nest pages under it. The link keeps its own slug and becomes the group header.\n\nInside a group, use **Add files** to add pages and **Add group** to nest another group. Toggle the chevron on a group to collapse or expand it in the builder.\n\n## Regenerate from files\n\nThe toolbar includes a **Regenerate** button that rebuilds the entire navigation tree from your MDX files' frontmatter (`category`, `categoryOrder`, `order`, `navIcon`, and `categoryIcon` fields). A confirmation modal appears before any existing manual navigation is replaced.\n\n<Callout type=\"warning\">\n Regenerating from files replaces your current navigation structure. This cannot be undone.\n</Callout>\n\n## Auto-generated vs. manual\n\nIf you don't configure navigation at all, Doccupine automatically builds your sidebar from page frontmatter. The Navigation Builder is only needed when you want explicit control over the order and grouping.\n\n## How it works\n\nWhen you save, the Navigation Builder writes two files to your repository as pending changes:\n\n- `navigation.json` - the category and link structure for each section\n- `sections.json` - the list of sections with their labels, slugs, and directories\n\nIcons are written as an `icon` field on a category or link, and nested groups as a `links` array on a link.\n\n**Array format** for single-section sites (`navigation.json`):\n\n```json\n[\n {\n \"label\": \"Getting Started\",\n \"icon\": \"rocket\",\n \"links\": [\n {\n \"slug\": \"getting-started\",\n \"title\": \"Quick Start\",\n \"icon\": \"book-open\"\n },\n {\n \"title\": \"Guides\",\n \"icon\": \"compass\",\n \"links\": [{ \"slug\": \"guides/deploy\", \"title\": \"Deploying\" }]\n }\n ]\n }\n]\n```\n\n**Object format** for multi-section sites (`navigation.json`):\n\n```json\n{\n \"\": [\n { \"label\": \"General\", \"links\": [{ \"slug\": \"\", \"title\": \"Introduction\" }] }\n ],\n \"api\": [\n {\n \"label\": \"Auth\",\n \"links\": [{ \"slug\": \"api/auth\", \"title\": \"Authentication\" }]\n }\n ]\n}\n```\n\n**Sections** (`sections.json`):\n\n```json\n[\n { \"label\": \"Docs\", \"slug\": \"\" },\n { \"label\": \"API Reference\", \"slug\": \"api\" }\n]\n```";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const platformNavigationSettingsMdxTemplate = `---
|
|
2
2
|
title: "Navigation Settings"
|
|
3
3
|
description: "Define the sidebar structure for your documentation site using the drag-and-drop Navigation Builder."
|
|
4
|
-
date: "2026-
|
|
4
|
+
date: "2026-07-06"
|
|
5
5
|
category: "Configuration"
|
|
6
6
|
categoryOrder: 2
|
|
7
7
|
order: 2
|
|
@@ -14,11 +14,12 @@ The Navigation Builder lets you define your sidebar structure through a visual,
|
|
|
14
14
|
|
|
15
15
|
## Structure
|
|
16
16
|
|
|
17
|
-
Navigation is organized into
|
|
17
|
+
Navigation is organized into these levels:
|
|
18
18
|
|
|
19
19
|
- **Section** - a top-level area of your site (e.g., "Docs", "API Reference"). Each section appears as a tab below the site header and has its own sidebar. Sections are defined by a label, URL slug, and docs directory.
|
|
20
|
-
- **Category** - a group label within a section's sidebar (e.g., "Getting Started")
|
|
21
|
-
- **Link** - an individual page entry within a category, defined by a slug and title
|
|
20
|
+
- **Category** - a group label within a section's sidebar (e.g., "Getting Started"). Categories can have an optional icon.
|
|
21
|
+
- **Link** - an individual page entry within a category, defined by a slug and title, with an optional icon.
|
|
22
|
+
- **Group** - a link that holds its own nested links, rendered as a collapsible item in the sidebar. Groups can nest as deep as you need. See [Nested groups](#nested-groups).
|
|
22
23
|
|
|
23
24
|
## Drag-and-drop reordering
|
|
24
25
|
|
|
@@ -44,7 +45,7 @@ You can also define sections purely through page frontmatter without using the N
|
|
|
44
45
|
## Managing categories
|
|
45
46
|
|
|
46
47
|
- Click **Add Category** within a section to create a new group
|
|
47
|
-
- Click the **edit** button on a category to open its
|
|
48
|
+
- Click the **edit** button on a category to open its **Edit Category** modal, where you can rename it, pick an icon, or delete it
|
|
48
49
|
|
|
49
50
|
## Adding links
|
|
50
51
|
|
|
@@ -54,11 +55,35 @@ Within each category, click **Add files** to open a popover that lists available
|
|
|
54
55
|
- **Select** one or more files using checkboxes
|
|
55
56
|
- Click **Add** to insert them as links in the category
|
|
56
57
|
|
|
57
|
-
|
|
58
|
+
### Editing a link
|
|
59
|
+
|
|
60
|
+
- **Double-click** a link's title to rename it inline.
|
|
61
|
+
- Click the **edit** button on a link to open the **Edit Link** modal, where you can change its title, slug, and icon. Leaving the slug empty makes a leaf link point at the index page; for a group it makes the group a non-clickable label.
|
|
62
|
+
- Click the **delete** button next to a link to remove it.
|
|
63
|
+
|
|
64
|
+
## Icons
|
|
65
|
+
|
|
66
|
+
You can show a [Lucide](https://lucide.dev/icons) icon next to any category or link to make the sidebar easier to scan.
|
|
67
|
+
|
|
68
|
+
- Set a **category icon** from the Edit Category modal.
|
|
69
|
+
- Set a **link icon** from the Edit Link modal.
|
|
70
|
+
|
|
71
|
+
Both use a searchable icon picker, and icon names are stored in kebab-case (e.g. \`rocket\`, \`book-open\`, \`settings\`). You can also set icons from page frontmatter using the \`navIcon\` (link) and \`categoryIcon\` (category) fields, which the **Regenerate** button picks up.
|
|
72
|
+
|
|
73
|
+
## Nested groups
|
|
74
|
+
|
|
75
|
+
A link can hold its own child links, rendering as a collapsible group in the sidebar. Nesting can go as deep as you need.
|
|
76
|
+
|
|
77
|
+
There are two ways to create a group:
|
|
78
|
+
|
|
79
|
+
- Click **Add group** (in a category or inside another group) to create a collapsible group. Give it a title in the Edit Link modal, then leave its slug empty to make it a plain, non-clickable label, or set a slug to make the group header link to its own page.
|
|
80
|
+
- Click **Add pages inside** on an existing link to nest pages under it. The link keeps its own slug and becomes the group header.
|
|
81
|
+
|
|
82
|
+
Inside a group, use **Add files** to add pages and **Add group** to nest another group. Toggle the chevron on a group to collapse or expand it in the builder.
|
|
58
83
|
|
|
59
84
|
## Regenerate from files
|
|
60
85
|
|
|
61
|
-
The toolbar includes a **Regenerate** button that rebuilds the entire navigation tree from your MDX files' frontmatter (\`category\`, \`categoryOrder\`, and \`
|
|
86
|
+
The toolbar includes a **Regenerate** button that rebuilds the entire navigation tree from your MDX files' frontmatter (\`category\`, \`categoryOrder\`, \`order\`, \`navIcon\`, and \`categoryIcon\` fields). A confirmation modal appears before any existing manual navigation is replaced.
|
|
62
87
|
|
|
63
88
|
<Callout type="warning">
|
|
64
89
|
Regenerating from files replaces your current navigation structure. This cannot be undone.
|
|
@@ -75,13 +100,27 @@ When you save, the Navigation Builder writes two files to your repository as pen
|
|
|
75
100
|
- \`navigation.json\` - the category and link structure for each section
|
|
76
101
|
- \`sections.json\` - the list of sections with their labels, slugs, and directories
|
|
77
102
|
|
|
103
|
+
Icons are written as an \`icon\` field on a category or link, and nested groups as a \`links\` array on a link.
|
|
104
|
+
|
|
78
105
|
**Array format** for single-section sites (\`navigation.json\`):
|
|
79
106
|
|
|
80
107
|
\`\`\`json
|
|
81
108
|
[
|
|
82
109
|
{
|
|
83
110
|
"label": "Getting Started",
|
|
84
|
-
"
|
|
111
|
+
"icon": "rocket",
|
|
112
|
+
"links": [
|
|
113
|
+
{
|
|
114
|
+
"slug": "getting-started",
|
|
115
|
+
"title": "Quick Start",
|
|
116
|
+
"icon": "book-open"
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"title": "Guides",
|
|
120
|
+
"icon": "compass",
|
|
121
|
+
"links": [{ "slug": "guides/deploy", "title": "Deploying" }]
|
|
122
|
+
}
|
|
123
|
+
]
|
|
85
124
|
}
|
|
86
125
|
]
|
|
87
126
|
\`\`\`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const updateMdxTemplate = "---\ntitle: \"Update\"\ndescription: \"Easily manage and present change history.\"\ndate: \"2026-02-19\"\ncategory: \"Components\"\ncategoryOrder: 1\norder: 12\n---\n\n# Update\n\nEasily manage and present change history.\n\nThe `Update` component helps you display release notes, version details, and changelogs in a standardized format.\n\nEach `Update` label is added to the \"On this page\" sidebar and gets its own anchor, so you can link directly to a specific entry.\n\n<Update label=\"
|
|
1
|
+
export declare const updateMdxTemplate = "---\ntitle: \"Update\"\ndescription: \"Easily manage and present change history.\"\ndate: \"2026-02-19\"\ncategory: \"Components\"\ncategoryOrder: 1\norder: 12\n---\n\n# Update\n\nEasily manage and present change history.\n\nThe `Update` component helps you display release notes, version details, and changelogs in a standardized format.\n\nEach `Update` label is added to the \"On this page\" sidebar and gets its own anchor, so you can link directly to a specific entry.\n\n<Update label=\"v0.0.1\" description=\"Example\">\n ## Example entry\n\nYou can include anything here - images, code snippets, or a bullet list of modifications.\n\n\n\n### Key additions\n\n- Fully responsive layout\n- Individual anchor for each update\n- Automatic RSS feed entry generation\n\n</Update>\n\n## Update Usage\n\nYou can combine multiple `Update` components to build complete changelogs.\n\n```mdx\n<Update label=\"v0.0.1\" description=\"Example\">\n ## Example entry\n\nYou can include anything here - images, code snippets, or a bullet list of modifications.\n\n\n\n### Key additions\n\n- Fully responsive layout\n- Individual anchor for each update\n- Automatic RSS feed entry generation\n\n</Update>\n```\n\n## Properties\n\n<Field value=\"label\" type=\"string\" required>\n The label of the update. It also appears in the page's \"On this page\"\n navigation and acts as a deep-link anchor to this entry.\n</Field>\n\n<Field value=\"description\" type=\"string\" required>\n The description of the update.\n</Field>\n\n<Field value=\"children\" type=\"node\" required>\n The content of the update.\n</Field>";
|
|
@@ -15,7 +15,7 @@ The \`Update\` component helps you display release notes, version details, and c
|
|
|
15
15
|
|
|
16
16
|
Each \`Update\` label is added to the "On this page" sidebar and gets its own anchor, so you can link directly to a specific entry.
|
|
17
17
|
|
|
18
|
-
<Update label="
|
|
18
|
+
<Update label="v0.0.1" description="Example">
|
|
19
19
|
## Example entry
|
|
20
20
|
|
|
21
21
|
You can include anything here - images, code snippets, or a bullet list of modifications.
|
|
@@ -35,7 +35,7 @@ You can include anything here - images, code snippets, or a bullet list of modif
|
|
|
35
35
|
You can combine multiple \`Update\` components to build complete changelogs.
|
|
36
36
|
|
|
37
37
|
\`\`\`mdx
|
|
38
|
-
<Update label="
|
|
38
|
+
<Update label="v0.0.1" description="Example">
|
|
39
39
|
## Example entry
|
|
40
40
|
|
|
41
41
|
You can include anything here - images, code snippets, or a bullet list of modifications.
|
|
@@ -17,7 +17,7 @@ export const packageJsonTemplate = JSON.stringify({
|
|
|
17
17
|
"@mdx-js/react": "^3.1.1",
|
|
18
18
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
19
19
|
"@posthog/react": "^1.10.3",
|
|
20
|
-
"cherry-styled-components": "^0.2.
|
|
20
|
+
"cherry-styled-components": "^0.2.9",
|
|
21
21
|
langchain: "^1.5.2",
|
|
22
22
|
"lucide-react": "^1.23.0",
|
|
23
23
|
minisearch: "^7.2.0",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "doccupine",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.107",
|
|
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": {
|