doccupine 0.0.78 → 0.0.80
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/lib/layout.js +31 -26
- package/dist/lib/structures.js +2 -0
- package/dist/templates/components/SearchDocs.d.ts +1 -0
- package/dist/templates/components/SearchDocs.js +373 -0
- package/dist/templates/components/layout/ActionBar.d.ts +1 -1
- package/dist/templates/components/layout/ActionBar.js +1 -1
- package/dist/templates/components/layout/Callout.d.ts +1 -1
- package/dist/templates/components/layout/Callout.js +7 -1
- package/dist/templates/components/layout/Header.d.ts +1 -1
- package/dist/templates/components/layout/Header.js +12 -1
- package/dist/templates/components/layout/SharedStyles.d.ts +1 -1
- package/dist/templates/components/layout/SharedStyles.js +1 -1
- package/dist/templates/components/layout/ThemeToggle.d.ts +1 -1
- package/dist/templates/components/layout/ThemeToggle.js +7 -2
- package/dist/templates/package.js +10 -10
- package/package.json +4 -4
package/dist/lib/layout.js
CHANGED
|
@@ -52,6 +52,7 @@ import { StyledComponentsRegistry } from "cherry-styled-components";
|
|
|
52
52
|
import { theme, themeDark } from "@/app/theme";
|
|
53
53
|
import { CherryThemeProvider } from "@/components/layout/CherryThemeProvider";
|
|
54
54
|
import { ChtProvider } from "@/components/Chat";
|
|
55
|
+
import { SearchProvider } from "@/components/SearchDocs";
|
|
55
56
|
${hasSections
|
|
56
57
|
? ""
|
|
57
58
|
: `import { Footer } from "@/components/layout/Footer";
|
|
@@ -133,19 +134,21 @@ ${hasSections
|
|
|
133
134
|
<StyledComponentsRegistry>
|
|
134
135
|
${analyticsEnabled ? " <PostHogProvider>\n" : ""}${a} <CherryThemeProvider theme={theme} themeDark={themeDark}>
|
|
135
136
|
${a} ${chtOpen}
|
|
136
|
-
${a} <
|
|
137
|
-
${a} <
|
|
138
|
-
${a}
|
|
139
|
-
${a}
|
|
140
|
-
${a}
|
|
141
|
-
${a} <
|
|
142
|
-
${a}
|
|
143
|
-
${a}
|
|
144
|
-
${a}
|
|
145
|
-
${a}
|
|
146
|
-
${a}
|
|
147
|
-
${a}
|
|
148
|
-
${a}
|
|
137
|
+
${a} <SearchProvider pages={pages}>
|
|
138
|
+
${a} <Header>
|
|
139
|
+
${a} <SectionBar sections={doccupineSections} />
|
|
140
|
+
${a} </Header>
|
|
141
|
+
${a} {process.env.LLM_PROVIDER && <Chat />}
|
|
142
|
+
${a} <DocsWrapper>
|
|
143
|
+
${a} <SectionNavProvider
|
|
144
|
+
${a} sections={doccupineSections}
|
|
145
|
+
${a} allPages={pages}
|
|
146
|
+
${a} hideBranding={hideBranding}
|
|
147
|
+
${a} >
|
|
148
|
+
${a} {children}
|
|
149
|
+
${a} </SectionNavProvider>
|
|
150
|
+
${a} </DocsWrapper>
|
|
151
|
+
${a} </SearchProvider>
|
|
149
152
|
${a} </ChtProvider>
|
|
150
153
|
${a} </CherryThemeProvider>
|
|
151
154
|
${analyticsEnabled ? " </PostHogProvider>\n" : ""} </StyledComponentsRegistry>
|
|
@@ -192,19 +195,21 @@ ${analyticsEnabled ? " </PostHogProvider>\n" : ""} </StyledCompo
|
|
|
192
195
|
<StyledComponentsRegistry>
|
|
193
196
|
${analyticsEnabled ? " <PostHogProvider>\n" : ""}${a} <CherryThemeProvider theme={theme} themeDark={themeDark}>
|
|
194
197
|
${a} ${chtOpen}
|
|
195
|
-
${a} <
|
|
196
|
-
${a}
|
|
197
|
-
${a}
|
|
198
|
-
${a} <
|
|
199
|
-
${a} <
|
|
200
|
-
${a}
|
|
201
|
-
${a}
|
|
202
|
-
${a}
|
|
203
|
-
${a}
|
|
204
|
-
${a}
|
|
205
|
-
${a}
|
|
206
|
-
${a}
|
|
207
|
-
${a}
|
|
198
|
+
${a} <SearchProvider pages={pages}>
|
|
199
|
+
${a} <Header />
|
|
200
|
+
${a} {process.env.LLM_PROVIDER && <Chat />}
|
|
201
|
+
${a} <SectionBarProvider hasSectionBar={false}>
|
|
202
|
+
${a} <DocsWrapper>
|
|
203
|
+
${a} <SideBar result={result.length ? result : defaultResults} />
|
|
204
|
+
${a} {children}
|
|
205
|
+
${a} <DocsNavigation
|
|
206
|
+
${a} result={result.length ? result : defaultResults}
|
|
207
|
+
${a} />
|
|
208
|
+
${a} <StaticLinks />
|
|
209
|
+
${a} <Footer hideBranding={hideBranding} />
|
|
210
|
+
${a} </DocsWrapper>
|
|
211
|
+
${a} </SectionBarProvider>
|
|
212
|
+
${a} </SearchProvider>
|
|
208
213
|
${a} </ChtProvider>
|
|
209
214
|
${a} </CherryThemeProvider>
|
|
210
215
|
${analyticsEnabled ? " </PostHogProvider>\n" : ""} </StyledComponentsRegistry>
|
package/dist/lib/structures.js
CHANGED
|
@@ -18,6 +18,7 @@ import { docsSideBarTemplate } from "../templates/components/DocsSideBar.js";
|
|
|
18
18
|
import { mdxComponentsTemplate } from "../templates/components/MDXComponents.js";
|
|
19
19
|
import { sectionNavProviderTemplate } from "../templates/components/SectionNavProvider.js";
|
|
20
20
|
import { postHogProviderTemplate } from "../templates/components/PostHogProvider.js";
|
|
21
|
+
import { searchDocsTemplate } from "../templates/components/SearchDocs.js";
|
|
21
22
|
import { sideBarTemplate } from "../templates/components/SideBar.js";
|
|
22
23
|
import { sectionBarTemplate } from "../templates/components/layout/SectionBar.js";
|
|
23
24
|
import { accordionTemplate } from "../templates/components/layout/Accordion.js";
|
|
@@ -140,6 +141,7 @@ export const appStructure = {
|
|
|
140
141
|
"components/MDXComponents.tsx": mdxComponentsTemplate,
|
|
141
142
|
"components/SectionNavProvider.tsx": sectionNavProviderTemplate,
|
|
142
143
|
"components/PostHogProvider.tsx": postHogProviderTemplate,
|
|
144
|
+
"components/SearchDocs.tsx": searchDocsTemplate,
|
|
143
145
|
"components/SideBar.tsx": sideBarTemplate,
|
|
144
146
|
"components/layout/Accordion.tsx": accordionTemplate,
|
|
145
147
|
"components/layout/ActionBar.tsx": actionBarTemplate,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const searchDocsTemplate = "\"use client\";\nimport React, {\n createContext,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { useRouter } from \"next/navigation\";\nimport styled, { css, keyframes } from \"styled-components\";\nimport { rgba } from \"polished\";\nimport { Search } from \"lucide-react\";\nimport { mq, Theme } from \"@/app/theme\";\nimport { interactiveStyles } from \"@/components/layout/SharedStyled\";\n\ninterface PageItem {\n slug: string;\n title: string;\n description?: string;\n category: string;\n section?: string;\n}\n\ninterface SearchContextValue {\n openSearch: () => void;\n}\n\nconst SearchContext = createContext<SearchContextValue>({\n openSearch: () => {},\n});\n\nconst ANIMATION_MS = 150;\n\nconst backdropIn = keyframes`\n from { opacity: 0; }\n to { opacity: 1; }\n`;\n\nconst backdropOut = keyframes`\n from { opacity: 1; }\n to { opacity: 0; }\n`;\n\nconst modalIn = keyframes`\n from { opacity: 0; transform: scale(0.96) translateY(-8px); }\n to { opacity: 1; transform: scale(1) translateY(0); }\n`;\n\nconst modalOut = keyframes`\n from { opacity: 1; transform: scale(1) translateY(0); }\n to { opacity: 0; transform: scale(0.96) translateY(-8px); }\n`;\n\nconst StyledBackdrop = styled.div<{ theme: Theme; $isClosing: boolean }>`\n position: fixed;\n inset: 0;\n z-index: 9999;\n background: ${({ theme }) =>\n rgba(theme.isDark ? theme.colors.light : theme.colors.dark, 0.5)};\n backdrop-filter: blur(4px);\n -webkit-backdrop-filter: blur(4px);\n display: flex;\n align-items: flex-start;\n justify-content: center;\n padding: 20px;\n animation: ${({ $isClosing }) => ($isClosing ? backdropOut : backdropIn)}\n ${ANIMATION_MS}ms ease forwards;\n\n ${mq(\"lg\")} {\n padding: 120px 20px 20px 20px;\n }\n`;\n\nconst StyledModal = styled.div<{ theme: Theme; $isClosing: boolean }>`\n background: ${({ theme }) => theme.colors.light};\n border-radius: ${({ theme }) => theme.spacing.radius.lg};\n box-shadow: ${({ theme }) => theme.shadows.xl};\n width: 100%;\n max-width: 560px;\n max-height: calc(100dvh - 40px);\n display: flex;\n flex-direction: column;\n border: solid 1px ${({ theme }) => theme.colors.grayLight};\n animation: ${({ $isClosing }) => ($isClosing ? modalOut : modalIn)}\n ${ANIMATION_MS}ms ease forwards;\n\n ${mq(\"lg\")} {\n max-height: calc(100dvh - 240px);\n }\n`;\n\nconst StyledInputWrapper = styled.div<{ theme: Theme }>`\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 16px;\n flex-shrink: 0;\n border-bottom: solid 1px ${({ theme }) => theme.colors.grayLight};\n\n & svg.lucide {\n color: ${({ theme }) => theme.colors.gray};\n flex-shrink: 0;\n }\n`;\n\nconst StyledInput = styled.input<{ theme: Theme }>`\n flex: 1;\n border: none;\n outline: none;\n background: transparent;\n font-size: ${({ theme }) => theme.fontSizes.text.lg};\n line-height: ${({ theme }) => theme.lineHeights.text.lg};\n color: ${({ theme }) => theme.colors.dark};\n font-family: inherit;\n\n &::placeholder {\n color: ${({ theme }) => theme.colors.gray};\n }\n`;\n\nconst StyledResults = styled.ul<{ theme: Theme }>`\n list-style: none;\n margin: 0;\n padding: 8px;\n overflow-y: auto;\n flex: 1;\n min-height: 0;\n -webkit-overflow-scrolling: touch;\n\n &::-webkit-scrollbar {\n display: none;\n }\n`;\n\nconst StyledResultItem = styled.li<{ theme: Theme; $isActive: boolean }>`\n padding: 10px 12px;\n border-radius: ${({ theme }) => theme.spacing.radius.xs};\n cursor: pointer;\n transition: background 0.15s ease;\n\n ${({ $isActive, theme }) =>\n $isActive &&\n css`\n background: ${rgba(theme.colors.primaryLight, 0.2)};\n `}\n\n &:hover {\n background: ${({ theme }) => rgba(theme.colors.primaryLight, 0.15)};\n }\n`;\n\nconst StyledResultTitle = styled.span<{ theme: Theme }>`\n font-size: ${({ theme }) => theme.fontSizes.text.lg};\n font-weight: 500;\n color: ${({ theme }) => theme.colors.dark};\n display: block;\n`;\n\nconst StyledResultMeta = styled.span<{ theme: Theme }>`\n font-size: ${({ theme }) => theme.fontSizes.small.lg};\n color: ${({ theme }) => theme.colors.gray};\n display: block;\n margin-top: 2px;\n`;\n\nconst StyledEmpty = styled.div<{ theme: Theme }>`\n padding: 20px;\n text-align: center;\n font-size: ${({ theme }) => theme.fontSizes.small.lg};\n color: ${({ theme }) => theme.colors.gray};\n`;\n\nconst StyledKbd = styled.kbd<{ theme: Theme }>`\n font-size: 11px;\n font-family: inherit;\n background: ${({ theme }) => theme.colors.grayLight};\n color: ${({ theme }) => theme.colors.grayDark};\n padding: 2px 6px;\n border-radius: 4px;\n margin-left: auto;\n font-weight: 600;\n display: none;\n\n ${mq(\"lg\")} {\n display: initial;\n }\n`;\n\nconst StyledSearchButton = styled.button<{ theme: Theme }>`\n ${interactiveStyles};\n border: solid 1px ${({ theme }) => theme.colors.grayLight};\n display: flex;\n align-items: center;\n gap: 6px;\n background: ${({ theme }) => theme.colors.light};\n color: ${({ theme }) => theme.colors.primary};\n border-radius: ${({ theme }) => theme.spacing.radius.xs};\n padding: 7px 8px;\n font-family: inherit;\n cursor: pointer;\n\n ${mq(\"lg\")} {\n padding: 5px 8px;\n }\n\n & svg.lucide {\n color: inherit;\n }\n`;\n\nfunction SearchProvider({\n pages,\n children,\n}: {\n pages: PageItem[];\n children: React.ReactNode;\n}) {\n const [isVisible, setIsVisible] = useState(false);\n const [isClosing, setIsClosing] = useState(false);\n const [query, setQuery] = useState(\"\");\n const [activeIndex, setActiveIndex] = useState(0);\n const inputRef = useRef<HTMLInputElement>(null);\n const resultsRef = useRef<HTMLUListElement>(null);\n const closingTimer = useRef<ReturnType<typeof setTimeout>>(null);\n const router = useRouter();\n\n const openSearch = useCallback(() => {\n if (closingTimer.current) clearTimeout(closingTimer.current);\n setIsClosing(false);\n setIsVisible(true);\n }, []);\n\n const closeSearch = useCallback(() => {\n setIsClosing(true);\n closingTimer.current = setTimeout(() => {\n setIsVisible(false);\n setIsClosing(false);\n setQuery(\"\");\n setActiveIndex(0);\n }, ANIMATION_MS);\n }, []);\n\n const filtered = useMemo(() => {\n if (!query.trim()) return pages;\n const q = query.toLowerCase();\n return pages.filter(\n (p) =>\n p.title.toLowerCase().includes(q) ||\n p.description?.toLowerCase().includes(q),\n );\n }, [pages, query]);\n\n const navigate = useCallback(\n (slug: string) => {\n closeSearch();\n router.push(`/${slug}`);\n },\n [closeSearch, router],\n );\n\n // Global Cmd+K / Ctrl+K listener\n const isVisibleRef = useRef(false);\n\n useEffect(() => {\n isVisibleRef.current = isVisible;\n }, [isVisible]);\n\n useEffect(() => {\n function handleKeyDown(e: KeyboardEvent) {\n if ((e.metaKey || e.ctrlKey) && e.key === \"k\") {\n e.preventDefault();\n if (isVisibleRef.current) {\n closeSearch();\n } else {\n openSearch();\n }\n }\n }\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => document.removeEventListener(\"keydown\", handleKeyDown);\n }, [closeSearch, openSearch]);\n\n // Focus input on open\n useEffect(() => {\n if (isVisible && !isClosing) {\n setTimeout(() => inputRef.current?.focus(), 10);\n }\n }, [isVisible, isClosing]);\n\n // Scroll active item into view\n useEffect(() => {\n if (!resultsRef.current) return;\n const active = resultsRef.current.children[activeIndex] as HTMLElement;\n active?.scrollIntoView({ block: \"nearest\" });\n }, [activeIndex]);\n\n function handleKeyDown(e: React.KeyboardEvent) {\n if (e.key === \"ArrowDown\") {\n e.preventDefault();\n setActiveIndex((i) => (i < filtered.length - 1 ? i + 1 : 0));\n } else if (e.key === \"ArrowUp\") {\n e.preventDefault();\n setActiveIndex((i) => (i > 0 ? i - 1 : filtered.length - 1));\n } else if (e.key === \"Enter\") {\n e.preventDefault();\n if (filtered[activeIndex]) {\n navigate(filtered[activeIndex].slug);\n }\n } else if (e.key === \"Escape\") {\n closeSearch();\n }\n }\n\n return (\n <SearchContext.Provider value={{ openSearch }}>\n {children}\n {isVisible && (\n <StyledBackdrop $isClosing={isClosing} onClick={closeSearch}>\n <StyledModal\n $isClosing={isClosing}\n onClick={(e) => e.stopPropagation()}\n >\n <StyledInputWrapper>\n <Search size={18} />\n <StyledInput\n ref={inputRef}\n value={query}\n onChange={(e) => {\n setQuery(e.target.value);\n setActiveIndex(0);\n }}\n onKeyDown={handleKeyDown}\n placeholder=\"Search docs...\"\n autoComplete=\"off\"\n spellCheck={false}\n />\n <StyledKbd>esc</StyledKbd>\n </StyledInputWrapper>\n {filtered.length > 0 ? (\n <StyledResults ref={resultsRef}>\n {filtered.map((page, index) => (\n <StyledResultItem\n key={page.slug + page.section}\n $isActive={index === activeIndex}\n onClick={() => navigate(page.slug)}\n onMouseEnter={() => setActiveIndex(index)}\n >\n <StyledResultTitle>{page.title}</StyledResultTitle>\n <StyledResultMeta>\n {page.section ? `${page.section} / ` : \"\"}\n {page.category}\n </StyledResultMeta>\n </StyledResultItem>\n ))}\n </StyledResults>\n ) : (\n <StyledEmpty>No results found</StyledEmpty>\n )}\n </StyledModal>\n </StyledBackdrop>\n )}\n </SearchContext.Provider>\n );\n}\n\nexport {\n SearchProvider,\n SearchContext,\n StyledKbd as SearchKbd,\n StyledSearchButton,\n};\n";
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
export const searchDocsTemplate = `"use client";
|
|
2
|
+
import React, {
|
|
3
|
+
createContext,
|
|
4
|
+
useCallback,
|
|
5
|
+
useEffect,
|
|
6
|
+
useMemo,
|
|
7
|
+
useRef,
|
|
8
|
+
useState,
|
|
9
|
+
} from "react";
|
|
10
|
+
import { useRouter } from "next/navigation";
|
|
11
|
+
import styled, { css, keyframes } from "styled-components";
|
|
12
|
+
import { rgba } from "polished";
|
|
13
|
+
import { Search } from "lucide-react";
|
|
14
|
+
import { mq, Theme } from "@/app/theme";
|
|
15
|
+
import { interactiveStyles } from "@/components/layout/SharedStyled";
|
|
16
|
+
|
|
17
|
+
interface PageItem {
|
|
18
|
+
slug: string;
|
|
19
|
+
title: string;
|
|
20
|
+
description?: string;
|
|
21
|
+
category: string;
|
|
22
|
+
section?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface SearchContextValue {
|
|
26
|
+
openSearch: () => void;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const SearchContext = createContext<SearchContextValue>({
|
|
30
|
+
openSearch: () => {},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const ANIMATION_MS = 150;
|
|
34
|
+
|
|
35
|
+
const backdropIn = keyframes\`
|
|
36
|
+
from { opacity: 0; }
|
|
37
|
+
to { opacity: 1; }
|
|
38
|
+
\`;
|
|
39
|
+
|
|
40
|
+
const backdropOut = keyframes\`
|
|
41
|
+
from { opacity: 1; }
|
|
42
|
+
to { opacity: 0; }
|
|
43
|
+
\`;
|
|
44
|
+
|
|
45
|
+
const modalIn = keyframes\`
|
|
46
|
+
from { opacity: 0; transform: scale(0.96) translateY(-8px); }
|
|
47
|
+
to { opacity: 1; transform: scale(1) translateY(0); }
|
|
48
|
+
\`;
|
|
49
|
+
|
|
50
|
+
const modalOut = keyframes\`
|
|
51
|
+
from { opacity: 1; transform: scale(1) translateY(0); }
|
|
52
|
+
to { opacity: 0; transform: scale(0.96) translateY(-8px); }
|
|
53
|
+
\`;
|
|
54
|
+
|
|
55
|
+
const StyledBackdrop = styled.div<{ theme: Theme; $isClosing: boolean }>\`
|
|
56
|
+
position: fixed;
|
|
57
|
+
inset: 0;
|
|
58
|
+
z-index: 9999;
|
|
59
|
+
background: \${({ theme }) =>
|
|
60
|
+
rgba(theme.isDark ? theme.colors.light : theme.colors.dark, 0.5)};
|
|
61
|
+
backdrop-filter: blur(4px);
|
|
62
|
+
-webkit-backdrop-filter: blur(4px);
|
|
63
|
+
display: flex;
|
|
64
|
+
align-items: flex-start;
|
|
65
|
+
justify-content: center;
|
|
66
|
+
padding: 20px;
|
|
67
|
+
animation: \${({ $isClosing }) => ($isClosing ? backdropOut : backdropIn)}
|
|
68
|
+
\${ANIMATION_MS}ms ease forwards;
|
|
69
|
+
|
|
70
|
+
\${mq("lg")} {
|
|
71
|
+
padding: 120px 20px 20px 20px;
|
|
72
|
+
}
|
|
73
|
+
\`;
|
|
74
|
+
|
|
75
|
+
const StyledModal = styled.div<{ theme: Theme; $isClosing: boolean }>\`
|
|
76
|
+
background: \${({ theme }) => theme.colors.light};
|
|
77
|
+
border-radius: \${({ theme }) => theme.spacing.radius.lg};
|
|
78
|
+
box-shadow: \${({ theme }) => theme.shadows.xl};
|
|
79
|
+
width: 100%;
|
|
80
|
+
max-width: 560px;
|
|
81
|
+
max-height: calc(100dvh - 40px);
|
|
82
|
+
display: flex;
|
|
83
|
+
flex-direction: column;
|
|
84
|
+
border: solid 1px \${({ theme }) => theme.colors.grayLight};
|
|
85
|
+
animation: \${({ $isClosing }) => ($isClosing ? modalOut : modalIn)}
|
|
86
|
+
\${ANIMATION_MS}ms ease forwards;
|
|
87
|
+
|
|
88
|
+
\${mq("lg")} {
|
|
89
|
+
max-height: calc(100dvh - 240px);
|
|
90
|
+
}
|
|
91
|
+
\`;
|
|
92
|
+
|
|
93
|
+
const StyledInputWrapper = styled.div<{ theme: Theme }>\`
|
|
94
|
+
display: flex;
|
|
95
|
+
align-items: center;
|
|
96
|
+
gap: 12px;
|
|
97
|
+
padding: 16px;
|
|
98
|
+
flex-shrink: 0;
|
|
99
|
+
border-bottom: solid 1px \${({ theme }) => theme.colors.grayLight};
|
|
100
|
+
|
|
101
|
+
& svg.lucide {
|
|
102
|
+
color: \${({ theme }) => theme.colors.gray};
|
|
103
|
+
flex-shrink: 0;
|
|
104
|
+
}
|
|
105
|
+
\`;
|
|
106
|
+
|
|
107
|
+
const StyledInput = styled.input<{ theme: Theme }>\`
|
|
108
|
+
flex: 1;
|
|
109
|
+
border: none;
|
|
110
|
+
outline: none;
|
|
111
|
+
background: transparent;
|
|
112
|
+
font-size: \${({ theme }) => theme.fontSizes.text.lg};
|
|
113
|
+
line-height: \${({ theme }) => theme.lineHeights.text.lg};
|
|
114
|
+
color: \${({ theme }) => theme.colors.dark};
|
|
115
|
+
font-family: inherit;
|
|
116
|
+
|
|
117
|
+
&::placeholder {
|
|
118
|
+
color: \${({ theme }) => theme.colors.gray};
|
|
119
|
+
}
|
|
120
|
+
\`;
|
|
121
|
+
|
|
122
|
+
const StyledResults = styled.ul<{ theme: Theme }>\`
|
|
123
|
+
list-style: none;
|
|
124
|
+
margin: 0;
|
|
125
|
+
padding: 8px;
|
|
126
|
+
overflow-y: auto;
|
|
127
|
+
flex: 1;
|
|
128
|
+
min-height: 0;
|
|
129
|
+
-webkit-overflow-scrolling: touch;
|
|
130
|
+
|
|
131
|
+
&::-webkit-scrollbar {
|
|
132
|
+
display: none;
|
|
133
|
+
}
|
|
134
|
+
\`;
|
|
135
|
+
|
|
136
|
+
const StyledResultItem = styled.li<{ theme: Theme; $isActive: boolean }>\`
|
|
137
|
+
padding: 10px 12px;
|
|
138
|
+
border-radius: \${({ theme }) => theme.spacing.radius.xs};
|
|
139
|
+
cursor: pointer;
|
|
140
|
+
transition: background 0.15s ease;
|
|
141
|
+
|
|
142
|
+
\${({ $isActive, theme }) =>
|
|
143
|
+
$isActive &&
|
|
144
|
+
css\`
|
|
145
|
+
background: \${rgba(theme.colors.primaryLight, 0.2)};
|
|
146
|
+
\`}
|
|
147
|
+
|
|
148
|
+
&:hover {
|
|
149
|
+
background: \${({ theme }) => rgba(theme.colors.primaryLight, 0.15)};
|
|
150
|
+
}
|
|
151
|
+
\`;
|
|
152
|
+
|
|
153
|
+
const StyledResultTitle = styled.span<{ theme: Theme }>\`
|
|
154
|
+
font-size: \${({ theme }) => theme.fontSizes.text.lg};
|
|
155
|
+
font-weight: 500;
|
|
156
|
+
color: \${({ theme }) => theme.colors.dark};
|
|
157
|
+
display: block;
|
|
158
|
+
\`;
|
|
159
|
+
|
|
160
|
+
const StyledResultMeta = styled.span<{ theme: Theme }>\`
|
|
161
|
+
font-size: \${({ theme }) => theme.fontSizes.small.lg};
|
|
162
|
+
color: \${({ theme }) => theme.colors.gray};
|
|
163
|
+
display: block;
|
|
164
|
+
margin-top: 2px;
|
|
165
|
+
\`;
|
|
166
|
+
|
|
167
|
+
const StyledEmpty = styled.div<{ theme: Theme }>\`
|
|
168
|
+
padding: 20px;
|
|
169
|
+
text-align: center;
|
|
170
|
+
font-size: \${({ theme }) => theme.fontSizes.small.lg};
|
|
171
|
+
color: \${({ theme }) => theme.colors.gray};
|
|
172
|
+
\`;
|
|
173
|
+
|
|
174
|
+
const StyledKbd = styled.kbd<{ theme: Theme }>\`
|
|
175
|
+
font-size: 11px;
|
|
176
|
+
font-family: inherit;
|
|
177
|
+
background: \${({ theme }) => theme.colors.grayLight};
|
|
178
|
+
color: \${({ theme }) => theme.colors.grayDark};
|
|
179
|
+
padding: 2px 6px;
|
|
180
|
+
border-radius: 4px;
|
|
181
|
+
margin-left: auto;
|
|
182
|
+
font-weight: 600;
|
|
183
|
+
display: none;
|
|
184
|
+
|
|
185
|
+
\${mq("lg")} {
|
|
186
|
+
display: initial;
|
|
187
|
+
}
|
|
188
|
+
\`;
|
|
189
|
+
|
|
190
|
+
const StyledSearchButton = styled.button<{ theme: Theme }>\`
|
|
191
|
+
\${interactiveStyles};
|
|
192
|
+
border: solid 1px \${({ theme }) => theme.colors.grayLight};
|
|
193
|
+
display: flex;
|
|
194
|
+
align-items: center;
|
|
195
|
+
gap: 6px;
|
|
196
|
+
background: \${({ theme }) => theme.colors.light};
|
|
197
|
+
color: \${({ theme }) => theme.colors.primary};
|
|
198
|
+
border-radius: \${({ theme }) => theme.spacing.radius.xs};
|
|
199
|
+
padding: 7px 8px;
|
|
200
|
+
font-family: inherit;
|
|
201
|
+
cursor: pointer;
|
|
202
|
+
|
|
203
|
+
\${mq("lg")} {
|
|
204
|
+
padding: 5px 8px;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
& svg.lucide {
|
|
208
|
+
color: inherit;
|
|
209
|
+
}
|
|
210
|
+
\`;
|
|
211
|
+
|
|
212
|
+
function SearchProvider({
|
|
213
|
+
pages,
|
|
214
|
+
children,
|
|
215
|
+
}: {
|
|
216
|
+
pages: PageItem[];
|
|
217
|
+
children: React.ReactNode;
|
|
218
|
+
}) {
|
|
219
|
+
const [isVisible, setIsVisible] = useState(false);
|
|
220
|
+
const [isClosing, setIsClosing] = useState(false);
|
|
221
|
+
const [query, setQuery] = useState("");
|
|
222
|
+
const [activeIndex, setActiveIndex] = useState(0);
|
|
223
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
224
|
+
const resultsRef = useRef<HTMLUListElement>(null);
|
|
225
|
+
const closingTimer = useRef<ReturnType<typeof setTimeout>>(null);
|
|
226
|
+
const router = useRouter();
|
|
227
|
+
|
|
228
|
+
const openSearch = useCallback(() => {
|
|
229
|
+
if (closingTimer.current) clearTimeout(closingTimer.current);
|
|
230
|
+
setIsClosing(false);
|
|
231
|
+
setIsVisible(true);
|
|
232
|
+
}, []);
|
|
233
|
+
|
|
234
|
+
const closeSearch = useCallback(() => {
|
|
235
|
+
setIsClosing(true);
|
|
236
|
+
closingTimer.current = setTimeout(() => {
|
|
237
|
+
setIsVisible(false);
|
|
238
|
+
setIsClosing(false);
|
|
239
|
+
setQuery("");
|
|
240
|
+
setActiveIndex(0);
|
|
241
|
+
}, ANIMATION_MS);
|
|
242
|
+
}, []);
|
|
243
|
+
|
|
244
|
+
const filtered = useMemo(() => {
|
|
245
|
+
if (!query.trim()) return pages;
|
|
246
|
+
const q = query.toLowerCase();
|
|
247
|
+
return pages.filter(
|
|
248
|
+
(p) =>
|
|
249
|
+
p.title.toLowerCase().includes(q) ||
|
|
250
|
+
p.description?.toLowerCase().includes(q),
|
|
251
|
+
);
|
|
252
|
+
}, [pages, query]);
|
|
253
|
+
|
|
254
|
+
const navigate = useCallback(
|
|
255
|
+
(slug: string) => {
|
|
256
|
+
closeSearch();
|
|
257
|
+
router.push(\`/\${slug}\`);
|
|
258
|
+
},
|
|
259
|
+
[closeSearch, router],
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
// Global Cmd+K / Ctrl+K listener
|
|
263
|
+
const isVisibleRef = useRef(false);
|
|
264
|
+
|
|
265
|
+
useEffect(() => {
|
|
266
|
+
isVisibleRef.current = isVisible;
|
|
267
|
+
}, [isVisible]);
|
|
268
|
+
|
|
269
|
+
useEffect(() => {
|
|
270
|
+
function handleKeyDown(e: KeyboardEvent) {
|
|
271
|
+
if ((e.metaKey || e.ctrlKey) && e.key === "k") {
|
|
272
|
+
e.preventDefault();
|
|
273
|
+
if (isVisibleRef.current) {
|
|
274
|
+
closeSearch();
|
|
275
|
+
} else {
|
|
276
|
+
openSearch();
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
281
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
282
|
+
}, [closeSearch, openSearch]);
|
|
283
|
+
|
|
284
|
+
// Focus input on open
|
|
285
|
+
useEffect(() => {
|
|
286
|
+
if (isVisible && !isClosing) {
|
|
287
|
+
setTimeout(() => inputRef.current?.focus(), 10);
|
|
288
|
+
}
|
|
289
|
+
}, [isVisible, isClosing]);
|
|
290
|
+
|
|
291
|
+
// Scroll active item into view
|
|
292
|
+
useEffect(() => {
|
|
293
|
+
if (!resultsRef.current) return;
|
|
294
|
+
const active = resultsRef.current.children[activeIndex] as HTMLElement;
|
|
295
|
+
active?.scrollIntoView({ block: "nearest" });
|
|
296
|
+
}, [activeIndex]);
|
|
297
|
+
|
|
298
|
+
function handleKeyDown(e: React.KeyboardEvent) {
|
|
299
|
+
if (e.key === "ArrowDown") {
|
|
300
|
+
e.preventDefault();
|
|
301
|
+
setActiveIndex((i) => (i < filtered.length - 1 ? i + 1 : 0));
|
|
302
|
+
} else if (e.key === "ArrowUp") {
|
|
303
|
+
e.preventDefault();
|
|
304
|
+
setActiveIndex((i) => (i > 0 ? i - 1 : filtered.length - 1));
|
|
305
|
+
} else if (e.key === "Enter") {
|
|
306
|
+
e.preventDefault();
|
|
307
|
+
if (filtered[activeIndex]) {
|
|
308
|
+
navigate(filtered[activeIndex].slug);
|
|
309
|
+
}
|
|
310
|
+
} else if (e.key === "Escape") {
|
|
311
|
+
closeSearch();
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return (
|
|
316
|
+
<SearchContext.Provider value={{ openSearch }}>
|
|
317
|
+
{children}
|
|
318
|
+
{isVisible && (
|
|
319
|
+
<StyledBackdrop $isClosing={isClosing} onClick={closeSearch}>
|
|
320
|
+
<StyledModal
|
|
321
|
+
$isClosing={isClosing}
|
|
322
|
+
onClick={(e) => e.stopPropagation()}
|
|
323
|
+
>
|
|
324
|
+
<StyledInputWrapper>
|
|
325
|
+
<Search size={18} />
|
|
326
|
+
<StyledInput
|
|
327
|
+
ref={inputRef}
|
|
328
|
+
value={query}
|
|
329
|
+
onChange={(e) => {
|
|
330
|
+
setQuery(e.target.value);
|
|
331
|
+
setActiveIndex(0);
|
|
332
|
+
}}
|
|
333
|
+
onKeyDown={handleKeyDown}
|
|
334
|
+
placeholder="Search docs..."
|
|
335
|
+
autoComplete="off"
|
|
336
|
+
spellCheck={false}
|
|
337
|
+
/>
|
|
338
|
+
<StyledKbd>esc</StyledKbd>
|
|
339
|
+
</StyledInputWrapper>
|
|
340
|
+
{filtered.length > 0 ? (
|
|
341
|
+
<StyledResults ref={resultsRef}>
|
|
342
|
+
{filtered.map((page, index) => (
|
|
343
|
+
<StyledResultItem
|
|
344
|
+
key={page.slug + page.section}
|
|
345
|
+
$isActive={index === activeIndex}
|
|
346
|
+
onClick={() => navigate(page.slug)}
|
|
347
|
+
onMouseEnter={() => setActiveIndex(index)}
|
|
348
|
+
>
|
|
349
|
+
<StyledResultTitle>{page.title}</StyledResultTitle>
|
|
350
|
+
<StyledResultMeta>
|
|
351
|
+
{page.section ? \`\${page.section} / \` : ""}
|
|
352
|
+
{page.category}
|
|
353
|
+
</StyledResultMeta>
|
|
354
|
+
</StyledResultItem>
|
|
355
|
+
))}
|
|
356
|
+
</StyledResults>
|
|
357
|
+
) : (
|
|
358
|
+
<StyledEmpty>No results found</StyledEmpty>
|
|
359
|
+
)}
|
|
360
|
+
</StyledModal>
|
|
361
|
+
</StyledBackdrop>
|
|
362
|
+
)}
|
|
363
|
+
</SearchContext.Provider>
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
export {
|
|
368
|
+
SearchProvider,
|
|
369
|
+
SearchContext,
|
|
370
|
+
StyledKbd as SearchKbd,
|
|
371
|
+
StyledSearchButton,
|
|
372
|
+
};
|
|
373
|
+
`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const actionBarTemplate = "\"use client\";\nimport { useContext, useState } from \"react\";\nimport styled, { css } from \"styled-components\";\nimport { Icon } from \"@/components/layout/Icon\";\nimport { mq, Theme } from \"@/app/theme\";\nimport { rgba } from \"polished\";\nimport { resetButton, Textarea } from \"cherry-styled-components\";\nimport { SectionBarContext } from \"@/components/layout/DocsComponents\";\nimport { StyledSmallButton } from \"@/components/layout/SharedStyled\";\n\ninterface ActionBarProps {\n children: React.ReactNode;\n content: string;\n}\n\nconst StyledActionBar = styled.div<{\n theme: Theme;\n $isChatOpen?: boolean;\n}>`\n border-bottom: solid 1px ${({ theme }) => theme.colors.grayLight};\n left: 0;\n padding: 12px 0;\n display: flex;\n justify-content: space-between;\n width: 100%;\n max-width: 640px;\n margin: auto;\n transition: all 0.3s ease;\n\n ${mq(\"lg\")} {\n padding: 12px 0;\n }\n`;\n\nconst StyledActionBarContent = styled.div`\n margin: auto 0;\n`;\n\nconst StyledCopyButton = styled(StyledSmallButton)<{\n theme: Theme;\n $copied: boolean;\n}>`\n border: solid 1px\n ${({ theme, $copied }) =>\n $copied ? theme.colors.success : theme.colors.grayLight};\n color: ${({ theme, $copied }) =>\n $copied ? theme.colors.success : theme.colors.primary};\n\n & svg.lucide {\n color: ${({ theme, $copied }) =>\n $copied ? theme.colors.success : theme.colors.primary};\n }\n`;\n\nconst StyledToggle = styled.button<{ theme: Theme; $isActive?: boolean }>`\n ${resetButton}\n width: 56px;\n height: 32px;\n border-radius: 30px;\n display: flex;\n position: relative;\n margin: auto 0;\n transform: scale(1);\n background: ${({ theme }) => theme.colors.light};\n border: solid 1px ${({ theme }) => theme.colors.grayLight};\n\n &::after {\n content: \"\";\n position: absolute;\n top: 3px;\n left: 3px;\n width: 24px;\n height: 24px;\n border-radius: 50%;\n background: ${({ theme }) => rgba(theme.colors.primaryLight, 0.2)};\n transition: all 0.3s ease;\n z-index: 1;\n ${({ $isActive }) =>\n !$isActive &&\n css`\n transform: translateX(24px);\n `}\n }\n\n & svg {\n width: 16px;\n height: 16px;\n object-fit: contain;\n margin: auto;\n transition: all 0.3s ease;\n position: relative;\n z-index: 2;\n }\n\n & .lucide-eye {\n transform: translateX(1px);\n }\n\n & .lucide-code-xml {\n transform: translateX(-1px);\n }\n\n & svg[stroke] {\n stroke: ${({ theme }) => theme.colors.primary};\n }\n\n &:hover {\n transform: scale(1.05);\n color: ${({ theme }) =>\n theme.isDark ? theme.colors.primaryLight : theme.colors.primaryDark};\n\n & svg[stroke] {\n stroke: ${({ theme }) =>\n theme.isDark ? theme.colors.primaryLight : theme.colors.primaryDark};\n }\n }\n\n &:active {\n transform: scale(0.97);\n }\n`;\n\nconst StyledContent = styled.div<{\n theme: Theme;\n $hasSectionBar?: boolean;\n}>`\n padding-top: 20px;\n transition: all 0.3s ease;\n\n & textarea {\n max-width: 640px;\n margin: auto;\n width: 100%;\n height: 100%;\n min-height: calc(\n 100vh - ${({ $hasSectionBar }) => ($hasSectionBar ? 202 : 160)}px\n );\n\n ${mq(\"lg\")} {\n min-height: calc(100vh - 159px);\n }\n }\n`;\n\nfunction ActionBar({ children, content }: ActionBarProps) {\n const [isView, setIsView] = useState(true);\n const [copied, setCopied] = useState(false);\n const hasSectionBar = useContext(SectionBarContext);\n\n const handleCopyContent = async () => {\n try {\n await navigator.clipboard.writeText(content);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n } catch (err) {\n console.error(\"Failed to copy:\", err);\n }\n };\n\n return (\n <>\n <StyledActionBar>\n <StyledCopyButton onClick={handleCopyContent} $copied={copied}>\n {copied ? (\n <>\n <Icon name=\"check\" size={16} />\n Copied!\n </>\n ) : (\n <>\n <Icon name=\"copy\" size={16} />\n Copy content\n </>\n )}\n </StyledCopyButton>\n <StyledActionBarContent>\n <StyledToggle\n onClick={() => setIsView(!isView)}\n aria-label=\"Toggle
|
|
1
|
+
export declare const actionBarTemplate = "\"use client\";\nimport { useContext, useState } from \"react\";\nimport styled, { css } from \"styled-components\";\nimport { Icon } from \"@/components/layout/Icon\";\nimport { mq, Theme } from \"@/app/theme\";\nimport { rgba } from \"polished\";\nimport { resetButton, Textarea } from \"cherry-styled-components\";\nimport { SectionBarContext } from \"@/components/layout/DocsComponents\";\nimport { StyledSmallButton } from \"@/components/layout/SharedStyled\";\n\ninterface ActionBarProps {\n children: React.ReactNode;\n content: string;\n}\n\nconst StyledActionBar = styled.div<{\n theme: Theme;\n $isChatOpen?: boolean;\n}>`\n border-bottom: solid 1px ${({ theme }) => theme.colors.grayLight};\n left: 0;\n padding: 12px 0;\n display: flex;\n justify-content: space-between;\n width: 100%;\n max-width: 640px;\n margin: auto;\n transition: all 0.3s ease;\n\n ${mq(\"lg\")} {\n padding: 12px 0;\n }\n`;\n\nconst StyledActionBarContent = styled.div`\n margin: auto 0;\n`;\n\nconst StyledCopyButton = styled(StyledSmallButton)<{\n theme: Theme;\n $copied: boolean;\n}>`\n border: solid 1px\n ${({ theme, $copied }) =>\n $copied ? theme.colors.success : theme.colors.grayLight};\n color: ${({ theme, $copied }) =>\n $copied ? theme.colors.success : theme.colors.primary};\n\n & svg.lucide {\n color: ${({ theme, $copied }) =>\n $copied ? theme.colors.success : theme.colors.primary};\n }\n`;\n\nconst StyledToggle = styled.button<{ theme: Theme; $isActive?: boolean }>`\n ${resetButton}\n width: 56px;\n height: 32px;\n border-radius: 30px;\n display: flex;\n position: relative;\n margin: auto 0;\n transform: scale(1);\n background: ${({ theme }) => theme.colors.light};\n border: solid 1px ${({ theme }) => theme.colors.grayLight};\n\n &::after {\n content: \"\";\n position: absolute;\n top: 3px;\n left: 3px;\n width: 24px;\n height: 24px;\n border-radius: 50%;\n background: ${({ theme }) => rgba(theme.colors.primaryLight, 0.2)};\n transition: all 0.3s ease;\n z-index: 1;\n ${({ $isActive }) =>\n !$isActive &&\n css`\n transform: translateX(24px);\n `}\n }\n\n & svg {\n width: 16px;\n height: 16px;\n object-fit: contain;\n margin: auto;\n transition: all 0.3s ease;\n position: relative;\n z-index: 2;\n }\n\n & .lucide-eye {\n transform: translateX(1px);\n }\n\n & .lucide-code-xml {\n transform: translateX(-1px);\n }\n\n & svg[stroke] {\n stroke: ${({ theme }) => theme.colors.primary};\n }\n\n &:hover {\n transform: scale(1.05);\n color: ${({ theme }) =>\n theme.isDark ? theme.colors.primaryLight : theme.colors.primaryDark};\n\n & svg[stroke] {\n stroke: ${({ theme }) =>\n theme.isDark ? theme.colors.primaryLight : theme.colors.primaryDark};\n }\n }\n\n &:active {\n transform: scale(0.97);\n }\n`;\n\nconst StyledContent = styled.div<{\n theme: Theme;\n $hasSectionBar?: boolean;\n}>`\n padding-top: 20px;\n transition: all 0.3s ease;\n\n & textarea {\n max-width: 640px;\n margin: auto;\n width: 100%;\n height: 100%;\n min-height: calc(\n 100vh - ${({ $hasSectionBar }) => ($hasSectionBar ? 202 : 160)}px\n );\n\n ${mq(\"lg\")} {\n min-height: calc(100vh - 159px);\n }\n }\n`;\n\nfunction ActionBar({ children, content }: ActionBarProps) {\n const [isView, setIsView] = useState(true);\n const [copied, setCopied] = useState(false);\n const hasSectionBar = useContext(SectionBarContext);\n\n const handleCopyContent = async () => {\n try {\n await navigator.clipboard.writeText(content);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n } catch (err) {\n console.error(\"Failed to copy:\", err);\n }\n };\n\n return (\n <>\n <StyledActionBar>\n <StyledCopyButton onClick={handleCopyContent} $copied={copied}>\n {copied ? (\n <>\n <Icon name=\"check\" size={16} />\n Copied!\n </>\n ) : (\n <>\n <Icon name=\"copy\" size={16} />\n Copy content\n </>\n )}\n </StyledCopyButton>\n <StyledActionBarContent>\n <StyledToggle\n onClick={() => setIsView(!isView)}\n aria-label=\"Toggle View\"\n $isActive={isView}\n >\n <Icon name=\"Eye\" />\n <Icon name=\"CodeXml\" />\n </StyledToggle>\n </StyledActionBarContent>\n </StyledActionBar>\n {isView && (\n <StyledContent $hasSectionBar={hasSectionBar}>{children}</StyledContent>\n )}\n {!isView && (\n <StyledContent $hasSectionBar={hasSectionBar}>\n <Textarea defaultValue={content} $fullWidth />\n </StyledContent>\n )}\n </>\n );\n}\n\nexport { ActionBar };\n";
|
|
@@ -176,7 +176,7 @@ function ActionBar({ children, content }: ActionBarProps) {
|
|
|
176
176
|
<StyledActionBarContent>
|
|
177
177
|
<StyledToggle
|
|
178
178
|
onClick={() => setIsView(!isView)}
|
|
179
|
-
aria-label="Toggle
|
|
179
|
+
aria-label="Toggle View"
|
|
180
180
|
$isActive={isView}
|
|
181
181
|
>
|
|
182
182
|
<Icon name="Eye" />
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const calloutTemplate = "\"use client\";\nimport { Theme } from \"@/app/theme\";\nimport { styledSmall } from \"cherry-styled-components\";\nimport styled, { css } from \"styled-components\";\nimport { Icon, IconProps } from \"@/components/layout/Icon\";\n\ntype CalloutType = \"note\" | \"info\" | \"warning\" | \"danger\" | \"success\";\n\nconst StyledCallout = styled.div<{ theme: Theme; $type?: CalloutType }>`\n background: ${({ theme }) => theme.colors.light};\n border: solid 1px ${({ theme }) => theme.colors.grayLight};\n border-radius: ${({ theme }) => theme.spacing.radius.lg};\n padding: 20px;\n margin: 0;\n ${({ theme }) => styledSmall(theme)}\n color: ${({ theme }) => theme.colors.grayDark};\n display: flex;\n\n & svg {\n vertical-align: middle;\n min-width: min-content;\n margin: 3px 10px 0 0;\n }\n\n ${({ theme, $type }) =>\n $type === \"note\" &&\n css`\n border-color: ${theme.isDark ? \"#0ea5e94d\" : \"#0ea5e933\"};\n background: ${theme.isDark ? \"#0ea5e91a\" : \"#f0f9ff80\"};\n\n & svg.lucide,\n & p {\n color: ${theme.isDark ? \"#bae6fd\" : \"#0c4a6e\"};\n }\n `}\n\n ${({ theme, $type }) =>\n $type === \"info\" &&\n css`\n border-color: ${theme.isDark ? \"#71717a4d\" : \"#71717a33\"};\n background: ${theme.isDark ? \"#71717a1a\" : \"#fafafa80\"};\n\n & svg.lucide,\n & .lucide,\n & p {\n color: ${theme.isDark ? \"#e4e4e7\" : \"#18181b\"};\n }\n `}\n\n ${({ theme, $type }) =>\n $type === \"warning\" &&\n css`\n border-color: ${theme.isDark ? \"#f59e0b4d\" : \"#f59e0b33\"};\n background: ${theme.isDark ? \"#f59e0b1a\" : \"#fffbeb80\"};\n\n & svg.lucide,\n & p {\n color: ${theme.isDark ? \"#fde68a\" : \"#78350f\"};\n }\n `}\n\n ${({ theme, $type }) =>\n $type === \"danger\" &&\n css`\n border-color: ${theme.isDark ? \"#ef44444d\" : \"#ef444433\"};\n background: ${theme.isDark ? \"#ef44441a\" : \"#fef2f280\"};\n\n & svg.lucide,\n & p {\n color: ${theme.isDark ? \"#fecaca\" : \"#7f1d1d\"};\n }\n `}\n\n ${({ theme, $type }) =>\n $type === \"success\" &&\n css`\n border-color: ${theme.isDark ? \"#10b9814d\" : \"#10b98133\"};\n background: ${theme.isDark ? \"#10b9811a\" : \"#ecfdf580\"};\n\n & svg.lucide,\n & p {\n color: ${theme.isDark ? \"#a7f3d0\" : \"#064e3b\"};\n }\n `}\n`;\n\ninterface CalloutProps extends React.HTMLAttributes<HTMLDivElement> {\n children: React.ReactNode;\n icon?: IconProps;\n type?: CalloutType;\n}\n\nfunction Callout({ children, type, icon }: CalloutProps) {\n const iconType =\n type === \"note\"\n ? \"CircleAlert\"\n : type === \"info\"\n ? \"Info\"\n : type === \"warning\"\n ? \"TriangleAlert\"\n : type === \"danger\"\n ? \"OctagonAlert\"\n : type === \"success\"\n ? \"Check\"\n : (icon as IconProps);\n return (\n <StyledCallout $type={type}>\n <Icon name={iconType} size={16} />\n {children}
|
|
1
|
+
export declare const calloutTemplate = "\"use client\";\nimport { Theme } from \"@/app/theme\";\nimport { styledSmall } from \"cherry-styled-components\";\nimport styled, { css } from \"styled-components\";\nimport { Icon, IconProps } from \"@/components/layout/Icon\";\n\ntype CalloutType = \"note\" | \"info\" | \"warning\" | \"danger\" | \"success\";\n\nconst StyledCallout = styled.div<{ theme: Theme; $type?: CalloutType }>`\n background: ${({ theme }) => theme.colors.light};\n border: solid 1px ${({ theme }) => theme.colors.grayLight};\n border-radius: ${({ theme }) => theme.spacing.radius.lg};\n padding: 20px;\n margin: 0;\n ${({ theme }) => styledSmall(theme)}\n color: ${({ theme }) => theme.colors.grayDark};\n display: flex;\n\n & svg {\n vertical-align: middle;\n min-width: min-content;\n margin: 3px 10px 0 0;\n }\n\n ${({ theme, $type }) =>\n $type === \"note\" &&\n css`\n border-color: ${theme.isDark ? \"#0ea5e94d\" : \"#0ea5e933\"};\n background: ${theme.isDark ? \"#0ea5e91a\" : \"#f0f9ff80\"};\n\n & svg.lucide,\n & p {\n color: ${theme.isDark ? \"#bae6fd\" : \"#0c4a6e\"};\n }\n `}\n\n ${({ theme, $type }) =>\n $type === \"info\" &&\n css`\n border-color: ${theme.isDark ? \"#71717a4d\" : \"#71717a33\"};\n background: ${theme.isDark ? \"#71717a1a\" : \"#fafafa80\"};\n\n & svg.lucide,\n & .lucide,\n & p {\n color: ${theme.isDark ? \"#e4e4e7\" : \"#18181b\"};\n }\n `}\n\n ${({ theme, $type }) =>\n $type === \"warning\" &&\n css`\n border-color: ${theme.isDark ? \"#f59e0b4d\" : \"#f59e0b33\"};\n background: ${theme.isDark ? \"#f59e0b1a\" : \"#fffbeb80\"};\n\n & svg.lucide,\n & p {\n color: ${theme.isDark ? \"#fde68a\" : \"#78350f\"};\n }\n `}\n\n ${({ theme, $type }) =>\n $type === \"danger\" &&\n css`\n border-color: ${theme.isDark ? \"#ef44444d\" : \"#ef444433\"};\n background: ${theme.isDark ? \"#ef44441a\" : \"#fef2f280\"};\n\n & svg.lucide,\n & p {\n color: ${theme.isDark ? \"#fecaca\" : \"#7f1d1d\"};\n }\n `}\n\n ${({ theme, $type }) =>\n $type === \"success\" &&\n css`\n border-color: ${theme.isDark ? \"#10b9814d\" : \"#10b98133\"};\n background: ${theme.isDark ? \"#10b9811a\" : \"#ecfdf580\"};\n\n & svg.lucide,\n & p {\n color: ${theme.isDark ? \"#a7f3d0\" : \"#064e3b\"};\n }\n `}\n`;\n\nconst StyledChildren = styled.span`\n display: flex;\n flex-direction: column;\n gap: 10px;\n`;\n\ninterface CalloutProps extends React.HTMLAttributes<HTMLDivElement> {\n children: React.ReactNode;\n icon?: IconProps;\n type?: CalloutType;\n}\n\nfunction Callout({ children, type, icon }: CalloutProps) {\n const iconType =\n type === \"note\"\n ? \"CircleAlert\"\n : type === \"info\"\n ? \"Info\"\n : type === \"warning\"\n ? \"TriangleAlert\"\n : type === \"danger\"\n ? \"OctagonAlert\"\n : type === \"success\"\n ? \"Check\"\n : (icon as IconProps);\n return (\n <StyledCallout $type={type}>\n <Icon name={iconType} size={16} />\n <StyledChildren>{children}</StyledChildren>\n </StyledCallout>\n );\n}\n\nexport { Callout };\n";
|
|
@@ -84,6 +84,12 @@ const StyledCallout = styled.div<{ theme: Theme; $type?: CalloutType }>\`
|
|
|
84
84
|
\`}
|
|
85
85
|
\`;
|
|
86
86
|
|
|
87
|
+
const StyledChildren = styled.span\`
|
|
88
|
+
display: flex;
|
|
89
|
+
flex-direction: column;
|
|
90
|
+
gap: 10px;
|
|
91
|
+
\`;
|
|
92
|
+
|
|
87
93
|
interface CalloutProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
88
94
|
children: React.ReactNode;
|
|
89
95
|
icon?: IconProps;
|
|
@@ -106,7 +112,7 @@ function Callout({ children, type, icon }: CalloutProps) {
|
|
|
106
112
|
return (
|
|
107
113
|
<StyledCallout $type={type}>
|
|
108
114
|
<Icon name={iconType} size={16} />
|
|
109
|
-
{children}
|
|
115
|
+
<StyledChildren>{children}</StyledChildren>
|
|
110
116
|
</StyledCallout>
|
|
111
117
|
);
|
|
112
118
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const headerTemplate = "\"use client\";\nimport React from \"react\";\nimport { useCallback, useContext, useRef, useState } from \"react\";\nimport styled, { css, useTheme } from \"styled-components\";\nimport Link from \"next/link\";\nimport { rgba } from \"polished\";\nimport { mq, Theme } from \"@/app/theme\";\nimport { useOnClickOutside } from \"@/components/ClickOutside\";\nimport { Logo } from \"@/components/layout/Pictograms\";\nimport { ChatContext, ChatButtonCTA } from \"@/components/Chat\";\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 }) => rgba(theme.colors.primaryLight, 0.05)};\n z-index: -1;\n }\n\n & .logo {\n display: flex;\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:
|
|
1
|
+
export declare const headerTemplate = "\"use client\";\nimport React from \"react\";\nimport { useCallback, useContext, useRef, useState } from \"react\";\nimport styled, { css, useTheme } from \"styled-components\";\nimport Link from \"next/link\";\nimport { rgba } from \"polished\";\nimport { mq, Theme } from \"@/app/theme\";\nimport { useOnClickOutside } from \"@/components/ClickOutside\";\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 }) => rgba(theme.colors.primaryLight, 0.05)};\n z-index: -1;\n }\n\n & .logo {\n display: flex;\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 theme = useTheme() as Theme;\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 theme.isDark ? (\n // eslint-disable-next-line @next/next/no-img-element\n <img\n src={customThemeJson.logo.dark}\n alt=\"Logo\"\n width=\"100\"\n height=\"100\"\n />\n ) : (\n // eslint-disable-next-line @next/next/no-img-element\n <img\n src={customThemeJson.logo.light}\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";
|
|
@@ -6,8 +6,14 @@ import Link from "next/link";
|
|
|
6
6
|
import { rgba } from "polished";
|
|
7
7
|
import { mq, Theme } from "@/app/theme";
|
|
8
8
|
import { useOnClickOutside } from "@/components/ClickOutside";
|
|
9
|
+
import { Search } from "lucide-react";
|
|
9
10
|
import { Logo } from "@/components/layout/Pictograms";
|
|
10
11
|
import { ChatContext, ChatButtonCTA } from "@/components/Chat";
|
|
12
|
+
import {
|
|
13
|
+
SearchContext,
|
|
14
|
+
SearchKbd,
|
|
15
|
+
StyledSearchButton,
|
|
16
|
+
} from "@/components/SearchDocs";
|
|
11
17
|
import themeJson from "@/theme.json";
|
|
12
18
|
|
|
13
19
|
const customThemeJson = themeJson as typeof themeJson & {
|
|
@@ -91,7 +97,7 @@ const StyledHeaderInner = styled.div<{ $hasChildren: boolean }>\`
|
|
|
91
97
|
const StyledLeftWrapper = styled.div\`
|
|
92
98
|
display: flex;
|
|
93
99
|
align-items: center;
|
|
94
|
-
gap:
|
|
100
|
+
gap: 10px;
|
|
95
101
|
min-width: fit-content;
|
|
96
102
|
padding-right: 20px;
|
|
97
103
|
|
|
@@ -123,6 +129,7 @@ function Header({ children }: HeaderProps) {
|
|
|
123
129
|
useOnClickOutside([langRef, wrapperRef], isLangActive ? closeMenu : () => {});
|
|
124
130
|
const theme = useTheme() as Theme;
|
|
125
131
|
const { isChatActive } = useContext(ChatContext);
|
|
132
|
+
const { openSearch } = useContext(SearchContext);
|
|
126
133
|
|
|
127
134
|
return (
|
|
128
135
|
<StyledHeader $hasChildren={children ? true : false} id="header">
|
|
@@ -152,6 +159,10 @@ function Header({ children }: HeaderProps) {
|
|
|
152
159
|
</Link>
|
|
153
160
|
{children}
|
|
154
161
|
<StyledLeftWrapper>
|
|
162
|
+
<StyledSearchButton onClick={openSearch} aria-label="Search docs">
|
|
163
|
+
<Search size={14} />
|
|
164
|
+
<SearchKbd>⌘K</SearchKbd>
|
|
165
|
+
</StyledSearchButton>
|
|
155
166
|
{isChatActive && <ChatButtonCTA />}
|
|
156
167
|
</StyledLeftWrapper>
|
|
157
168
|
</StyledHeaderInner>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const sharedStyledTemplate = "\"use client\";\nimport { mq, styledSmall, styledText, Theme } from \"cherry-styled-components\";\nimport styled, { css } from \"styled-components\";\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 }) =>\n theme.isDark ? theme.colors.primaryLight : theme.colors.primaryDark};\n box-shadow: 0 1px 0 0 ${({ theme }) => theme.colors.primary};\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 }\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:
|
|
1
|
+
export declare const sharedStyledTemplate = "\"use client\";\nimport { mq, styledSmall, styledText, Theme } from \"cherry-styled-components\";\nimport styled, { css } from \"styled-components\";\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 }) =>\n theme.isDark ? theme.colors.primaryLight : theme.colors.primaryDark};\n box-shadow: 0 1px 0 0 ${({ theme }) => theme.colors.primary};\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 }\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.primary};\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";
|
|
@@ -146,7 +146,7 @@ export const styledTable = css<{ theme: Theme }>\`
|
|
|
146
146
|
|
|
147
147
|
export const StyledSmallButton = styled.button<{ theme: Theme }>\`
|
|
148
148
|
\${interactiveStyles};
|
|
149
|
-
background:
|
|
149
|
+
background: \${({ theme }) => theme.colors.light};
|
|
150
150
|
border: solid 1px \${({ theme }) => theme.colors.grayLight};
|
|
151
151
|
color: \${({ theme }) => theme.colors.primary};
|
|
152
152
|
border-radius: \${({ theme }) => theme.spacing.radius.xs};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const themeToggleTemplate = "\"use client\";\nimport { Theme, resetButton } from \"cherry-styled-components\";\nimport styled, { css, useTheme } from \"styled-components\";\nimport { rgba } from \"polished\";\nimport { Icon } from \"@/components/layout/Icon\";\nimport { theme as themeLight, themeDark } from \"@/app/theme\";\nimport { useThemeOverride } from \"@/components/layout/ClientThemeProvider\";\n\nconst StyledThemeToggle = styled.button<{ theme: Theme; $hidden?: boolean }>`\n ${resetButton}\n width:
|
|
1
|
+
export declare const themeToggleTemplate = "\"use client\";\nimport { Theme, resetButton } from \"cherry-styled-components\";\nimport styled, { css, useTheme } from \"styled-components\";\nimport { rgba } from \"polished\";\nimport { Icon } from \"@/components/layout/Icon\";\nimport { theme as themeLight, themeDark } from \"@/app/theme\";\nimport { useThemeOverride } from \"@/components/layout/ClientThemeProvider\";\n\nconst StyledThemeToggle = styled.button<{ theme: Theme; $hidden?: boolean }>`\n ${resetButton}\n width: 59px;\n height: 32px;\n border-radius: 30px;\n display: flex;\n position: relative;\n margin: auto 0;\n transform: scale(1);\n background: ${({ theme }) => theme.colors.light};\n border: solid 1px ${({ theme }) => theme.colors.grayLight};\n\n &::after {\n content: \"\";\n position: absolute;\n top: 3px;\n left: 3px;\n width: 24px;\n height: 24px;\n border-radius: 50%;\n background: ${({ theme }) => rgba(theme.colors.primaryLight, 0.2)};\n transition: all 0.3s ease;\n z-index: 1;\n ${({ theme }) =>\n theme.isDark &&\n css`\n transform: translateX(27px);\n `}\n }\n\n ${({ $hidden }) =>\n $hidden &&\n css`\n display: none;\n `}\n\n & svg {\n width: 16px;\n height: 16px;\n object-fit: contain;\n margin: auto;\n transition: all 0.3s ease;\n position: relative;\n z-index: 2;\n }\n\n & .lucide-sun {\n transform: translateX(1px);\n }\n\n & .lucide-moon-star {\n transform: translateX(-1px);\n }\n\n & svg[stroke] {\n stroke: ${({ theme }) => theme.colors.primary};\n }\n\n &:hover {\n transform: scale(1.05);\n color: ${({ theme }) =>\n theme.isDark ? theme.colors.primaryLight : theme.colors.primaryDark};\n\n & svg[stroke] {\n stroke: ${({ theme }) =>\n theme.isDark ? theme.colors.primaryLight : theme.colors.primaryDark};\n }\n }\n\n &:active {\n transform: scale(0.97);\n }\n`;\n\nfunction ToggleTheme({ $hidden }: { $hidden?: boolean }) {\n const { setTheme } = useThemeOverride();\n const theme = useTheme() as Theme;\n\n return (\n <StyledThemeToggle\n onClick={async () => {\n const nextTheme = theme.isDark ? \"light\" : \"dark\";\n setTheme(nextTheme === \"light\" ? themeLight : themeDark);\n await fetch(\"/api/theme\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ theme: nextTheme }),\n }).catch((err) => console.error(\"Failed to persist theme:\", err));\n }}\n $hidden={$hidden}\n aria-label=\"Toggle Theme\"\n >\n <Icon name=\"Sun\" className=\"light\" />\n <Icon name=\"MoonStar\" className=\"dark\" />\n </StyledThemeToggle>\n );\n}\n\nfunction ToggleThemeLoading() {\n return (\n <StyledThemeToggle $hidden aria-label=\"Toggle Theme\">\n <Icon name=\"MoonStar\" className=\"dark\" />\n <Icon name=\"Sun\" className=\"light\" />\n </StyledThemeToggle>\n );\n}\n\nexport { ToggleTheme, ToggleThemeLoading };\n";
|
|
@@ -8,14 +8,15 @@ import { useThemeOverride } from "@/components/layout/ClientThemeProvider";
|
|
|
8
8
|
|
|
9
9
|
const StyledThemeToggle = styled.button<{ theme: Theme; $hidden?: boolean }>\`
|
|
10
10
|
\${resetButton}
|
|
11
|
-
width:
|
|
12
|
-
height:
|
|
11
|
+
width: 59px;
|
|
12
|
+
height: 32px;
|
|
13
13
|
border-radius: 30px;
|
|
14
14
|
display: flex;
|
|
15
15
|
position: relative;
|
|
16
16
|
margin: auto 0;
|
|
17
17
|
transform: scale(1);
|
|
18
18
|
background: \${({ theme }) => theme.colors.light};
|
|
19
|
+
border: solid 1px \${({ theme }) => theme.colors.grayLight};
|
|
19
20
|
|
|
20
21
|
&::after {
|
|
21
22
|
content: "";
|
|
@@ -55,6 +56,10 @@ const StyledThemeToggle = styled.button<{ theme: Theme; $hidden?: boolean }>\`
|
|
|
55
56
|
transform: translateX(1px);
|
|
56
57
|
}
|
|
57
58
|
|
|
59
|
+
& .lucide-moon-star {
|
|
60
|
+
transform: translateX(-1px);
|
|
61
|
+
}
|
|
62
|
+
|
|
58
63
|
& svg[stroke] {
|
|
59
64
|
stroke: \${({ theme }) => theme.colors.primary};
|
|
60
65
|
}
|
|
@@ -10,21 +10,21 @@ export const packageJsonTemplate = JSON.stringify({
|
|
|
10
10
|
format: "prettier --write .",
|
|
11
11
|
},
|
|
12
12
|
dependencies: {
|
|
13
|
-
"@langchain/anthropic": "^1.3.
|
|
14
|
-
"@langchain/core": "^1.1.
|
|
15
|
-
"@langchain/google-genai": "^2.1.
|
|
16
|
-
"@langchain/openai": "^1.
|
|
13
|
+
"@langchain/anthropic": "^1.3.24",
|
|
14
|
+
"@langchain/core": "^1.1.33",
|
|
15
|
+
"@langchain/google-genai": "^2.1.26",
|
|
16
|
+
"@langchain/openai": "^1.3.0",
|
|
17
17
|
"@mdx-js/react": "^3.1.1",
|
|
18
18
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
19
19
|
"@posthog/react": "^1.8.2",
|
|
20
20
|
"cherry-styled-components": "^0.1.13",
|
|
21
|
-
langchain: "^1.2.
|
|
21
|
+
langchain: "^1.2.34",
|
|
22
22
|
"lucide-react": "^0.577.0",
|
|
23
|
-
next: "16.
|
|
23
|
+
next: "16.2.0",
|
|
24
24
|
"next-mdx-remote": "^6.0.0",
|
|
25
25
|
polished: "^4.3.1",
|
|
26
|
-
"posthog-js": "^1.
|
|
27
|
-
"posthog-node": "^5.28.
|
|
26
|
+
"posthog-js": "^1.362.0",
|
|
27
|
+
"posthog-node": "^5.28.4",
|
|
28
28
|
react: "19.2.4",
|
|
29
29
|
"react-dom": "19.2.4",
|
|
30
30
|
"rehype-highlight": "^7.0.2",
|
|
@@ -39,9 +39,9 @@ export const packageJsonTemplate = JSON.stringify({
|
|
|
39
39
|
"@types/node": "^25",
|
|
40
40
|
"@types/react": "^19",
|
|
41
41
|
"@types/react-dom": "^19",
|
|
42
|
-
"baseline-browser-mapping": "^2.10.
|
|
42
|
+
"baseline-browser-mapping": "^2.10.8",
|
|
43
43
|
eslint: "^9",
|
|
44
|
-
"eslint-config-next": "16.
|
|
44
|
+
"eslint-config-next": "16.2.0",
|
|
45
45
|
prettier: "^3.8.1",
|
|
46
46
|
typescript: "^5",
|
|
47
47
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "doccupine",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.80",
|
|
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": {
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"commander": "^14.0.3",
|
|
40
40
|
"fs-extra": "^11.3.4",
|
|
41
41
|
"gray-matter": "^4.0.3",
|
|
42
|
-
"next": "^16.
|
|
42
|
+
"next": "^16.2.0",
|
|
43
43
|
"prompts": "^2.4.2",
|
|
44
44
|
"react": "^19.2.4",
|
|
45
45
|
"react-dom": "^19.2.4"
|
|
@@ -47,11 +47,11 @@
|
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/chokidar": "^2.1.7",
|
|
49
49
|
"@types/fs-extra": "^11.0.4",
|
|
50
|
-
"@types/node": "^25.
|
|
50
|
+
"@types/node": "^25.5.0",
|
|
51
51
|
"@types/prompts": "^2.4.9",
|
|
52
52
|
"prettier": "^3.8.1",
|
|
53
53
|
"typescript": "^5.9.3",
|
|
54
|
-
"vitest": "^4.0
|
|
54
|
+
"vitest": "^4.1.0"
|
|
55
55
|
},
|
|
56
56
|
"files": [
|
|
57
57
|
"dist/**/*"
|