@clidey/dory 0.30.0 → 0.33.0
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/bin/dist/dory.js +44 -20
- package/bin/dist/dory.js.map +1 -1
- package/index.html +54 -0
- package/package.json +9 -4
- package/src/app.css +5 -0
- package/src/app.tsx +13 -0
- package/src/components/dropdown.tsx +53 -0
- package/src/components/hooks.tsx +36 -0
- package/src/components/loading.tsx +21 -0
- package/src/components/mdx-provider.tsx +13 -0
- package/src/components/navigation.tsx +58 -0
- package/src/components/notification.tsx +13 -0
- package/src/components/routes.tsx +92 -0
- package/src/components/search-index.ts +43 -0
- package/src/components/search.tsx +448 -0
- package/src/components/store.ts +216 -0
- package/src/config/mdx.js +12 -0
- package/src/config/mdx.ts +12 -0
- package/src/embed/loader.ts +200 -0
- package/src/embed/types.ts +54 -0
- package/src/embed/widget-iframe.ts +302 -0
- package/src/main.tsx +22 -0
- package/src/mdx/accordion.tsx +41 -0
- package/src/mdx/api-playground.tsx +659 -0
- package/src/mdx/api.tsx +354 -0
- package/src/mdx/async-api.tsx +353 -0
- package/src/mdx/callout.tsx +64 -0
- package/src/mdx/card.tsx +54 -0
- package/src/mdx/code.tsx +289 -0
- package/src/mdx/expandable.tsx +36 -0
- package/src/mdx/fence.tsx +74 -0
- package/src/mdx/icon.tsx +73 -0
- package/src/mdx/latex.tsx +26 -0
- package/src/mdx/list.tsx +7 -0
- package/src/mdx/mdx.tsx +47 -0
- package/src/mdx/mermaid.tsx +147 -0
- package/src/mdx/open-api.tsx +311 -0
- package/src/mdx/processor.ts +174 -0
- package/src/mdx/source.tsx +58 -0
- package/src/mdx/step.tsx +51 -0
- package/src/mdx/table.tsx +20 -0
- package/src/mdx/tag.tsx +53 -0
- package/src/mdx/text.tsx +99 -0
- package/src/mdx/websocket-playground.tsx +824 -0
- package/src/plugins/docs-assets.ts +79 -0
- package/src/plugins/frontmatter-dev-server.ts +158 -0
- package/src/plugins/frontmatter-generator.ts +157 -0
- package/src/plugins/html-files-middleware.ts +108 -0
- package/src/plugins/html-metadata-injector.ts +76 -0
- package/src/plugins/llm-txt-dev-server.ts +183 -0
- package/src/plugins/llm-txt-generator.ts +186 -0
- package/src/plugins/sanitize.ts +163 -0
- package/src/plugins/unified-mdx-plugin.ts +35 -0
- package/src/sdk/index.ts +89 -0
- package/src/sdk/react.tsx +139 -0
- package/src/sdk/vue.ts +107 -0
- package/src/theme.scss +72 -0
- package/src/types/config.ts +89 -0
- package/src/ui/header.tsx +96 -0
- package/src/ui/layout.tsx +237 -0
- package/src/ui/mobile-navigation.tsx +72 -0
- package/src/ui/prev-next-link.tsx +74 -0
- package/src/ui/table-of-content.tsx +167 -0
- package/src/utils/colors.ts +54 -0
- package/src/utils/fonts.ts +23 -0
- package/src/utils/functions.ts +12 -0
- package/src/vite-env.d.ts +1 -0
- package/vite.config.embed-loader.ts +28 -0
- package/vite.config.embed-widget.ts +31 -0
- package/vite.config.ts +45 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React SDK for Dory Embedded Documentation
|
|
3
|
+
* Usage: npm install @clidey/dory-embedded
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useEffect, useRef, useCallback } from 'react';
|
|
7
|
+
import type { DoryDocsAPI } from '../embed/types';
|
|
8
|
+
|
|
9
|
+
interface DoryDocsConfig {
|
|
10
|
+
docsUrl: string;
|
|
11
|
+
position?: 'left' | 'right' | 'bottom';
|
|
12
|
+
width?: number | string;
|
|
13
|
+
height?: string;
|
|
14
|
+
theme?: 'light' | 'dark' | 'inherit';
|
|
15
|
+
initialPath?: string;
|
|
16
|
+
showOverlay?: boolean;
|
|
17
|
+
closeOnEscape?: boolean;
|
|
18
|
+
closeOnOverlayClick?: boolean;
|
|
19
|
+
onOpen?: () => void;
|
|
20
|
+
onClose?: () => void;
|
|
21
|
+
onNavigate?: (path: string) => void;
|
|
22
|
+
onReady?: () => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Hook to use Dory Documentation in React components
|
|
27
|
+
*/
|
|
28
|
+
export function useDoryDocs(config: DoryDocsConfig) {
|
|
29
|
+
const docsRef = useRef<any>(null);
|
|
30
|
+
const configRef = useRef(config);
|
|
31
|
+
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
configRef.current = config;
|
|
34
|
+
}, [config]);
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
// Wait for DoryDocs to be available
|
|
38
|
+
const checkDoryDocs = () => {
|
|
39
|
+
if (window.DoryDocs) {
|
|
40
|
+
docsRef.current = window.DoryDocs.init(configRef.current);
|
|
41
|
+
} else {
|
|
42
|
+
console.warn('[useDoryDocs] DoryDocs not loaded. Make sure to include the embed script.');
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
if (document.readyState === 'loading') {
|
|
47
|
+
window.addEventListener('dory:ready', checkDoryDocs);
|
|
48
|
+
} else {
|
|
49
|
+
checkDoryDocs();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return () => {
|
|
53
|
+
window.removeEventListener('dory:ready', checkDoryDocs);
|
|
54
|
+
docsRef.current?.destroy();
|
|
55
|
+
};
|
|
56
|
+
}, []);
|
|
57
|
+
|
|
58
|
+
const open = useCallback((path?: string) => {
|
|
59
|
+
return docsRef.current?.open(path);
|
|
60
|
+
}, []);
|
|
61
|
+
|
|
62
|
+
const close = useCallback(() => {
|
|
63
|
+
return docsRef.current?.close();
|
|
64
|
+
}, []);
|
|
65
|
+
|
|
66
|
+
const toggle = useCallback(() => {
|
|
67
|
+
return docsRef.current?.toggle();
|
|
68
|
+
}, []);
|
|
69
|
+
|
|
70
|
+
const navigate = useCallback((path: string) => {
|
|
71
|
+
return docsRef.current?.navigate(path);
|
|
72
|
+
}, []);
|
|
73
|
+
|
|
74
|
+
const setTheme = useCallback((theme: 'light' | 'dark') => {
|
|
75
|
+
return docsRef.current?.setTheme(theme);
|
|
76
|
+
}, []);
|
|
77
|
+
|
|
78
|
+
const isOpen = useCallback(() => {
|
|
79
|
+
return docsRef.current?.isOpen() || false;
|
|
80
|
+
}, []);
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
open,
|
|
84
|
+
close,
|
|
85
|
+
toggle,
|
|
86
|
+
navigate,
|
|
87
|
+
setTheme,
|
|
88
|
+
isOpen,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Component that renders a trigger button for Dory Documentation
|
|
94
|
+
*/
|
|
95
|
+
export function DoryDocsTrigger({
|
|
96
|
+
children,
|
|
97
|
+
config,
|
|
98
|
+
className,
|
|
99
|
+
path,
|
|
100
|
+
}: {
|
|
101
|
+
children: React.ReactNode;
|
|
102
|
+
config: DoryDocsConfig;
|
|
103
|
+
className?: string;
|
|
104
|
+
path?: string;
|
|
105
|
+
}) {
|
|
106
|
+
const docs = useDoryDocs(config);
|
|
107
|
+
|
|
108
|
+
const handleClick = () => {
|
|
109
|
+
docs.open(path);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<button onClick={handleClick} className={className}>
|
|
114
|
+
{children}
|
|
115
|
+
</button>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Component that initializes Dory Documentation without rendering anything
|
|
121
|
+
*/
|
|
122
|
+
export function DoryDocsProvider({
|
|
123
|
+
config,
|
|
124
|
+
children,
|
|
125
|
+
}: {
|
|
126
|
+
config: DoryDocsConfig;
|
|
127
|
+
children: React.ReactNode;
|
|
128
|
+
}) {
|
|
129
|
+
useDoryDocs(config);
|
|
130
|
+
|
|
131
|
+
return <>{children}</>;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Type declarations
|
|
135
|
+
declare global {
|
|
136
|
+
interface Window {
|
|
137
|
+
DoryDocs: DoryDocsAPI;
|
|
138
|
+
}
|
|
139
|
+
}
|
package/src/sdk/vue.ts
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vue SDK for Dory Embedded Documentation
|
|
3
|
+
* Usage: npm install @clidey/dory-embedded
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// @ts-ignore - Vue is an optional peer dependency
|
|
7
|
+
import { ref, onMounted, onUnmounted, computed } from 'vue';
|
|
8
|
+
import type { DoryDocsAPI } from '../embed/types';
|
|
9
|
+
|
|
10
|
+
interface DoryDocsConfig {
|
|
11
|
+
docsUrl: string;
|
|
12
|
+
position?: 'left' | 'right' | 'bottom';
|
|
13
|
+
width?: number | string;
|
|
14
|
+
height?: string;
|
|
15
|
+
theme?: 'light' | 'dark' | 'inherit';
|
|
16
|
+
initialPath?: string;
|
|
17
|
+
showOverlay?: boolean;
|
|
18
|
+
closeOnEscape?: boolean;
|
|
19
|
+
closeOnOverlayClick?: boolean;
|
|
20
|
+
onOpen?: () => void;
|
|
21
|
+
onClose?: () => void;
|
|
22
|
+
onNavigate?: (path: string) => void;
|
|
23
|
+
onReady?: () => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Composable to use Dory Documentation in Vue components
|
|
28
|
+
*/
|
|
29
|
+
export function useDoryDocs(config: DoryDocsConfig) {
|
|
30
|
+
const docsInstance = ref<any>(null);
|
|
31
|
+
const isReady = ref(false);
|
|
32
|
+
|
|
33
|
+
const initDocs = () => {
|
|
34
|
+
if (window.DoryDocs) {
|
|
35
|
+
docsInstance.value = window.DoryDocs.init(config);
|
|
36
|
+
isReady.value = true;
|
|
37
|
+
} else {
|
|
38
|
+
console.warn('[useDoryDocs] DoryDocs not loaded. Make sure to include the embed script.');
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
onMounted(() => {
|
|
43
|
+
if (document.readyState === 'loading') {
|
|
44
|
+
window.addEventListener('dory:ready', initDocs);
|
|
45
|
+
} else {
|
|
46
|
+
initDocs();
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
onUnmounted(() => {
|
|
51
|
+
window.removeEventListener('dory:ready', initDocs);
|
|
52
|
+
if (docsInstance.value) {
|
|
53
|
+
docsInstance.value.destroy();
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const open = async (path?: string) => {
|
|
58
|
+
if (docsInstance.value) {
|
|
59
|
+
await docsInstance.value.open(path);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const close = async () => {
|
|
64
|
+
if (docsInstance.value) {
|
|
65
|
+
await docsInstance.value.close();
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const toggle = async () => {
|
|
70
|
+
if (docsInstance.value) {
|
|
71
|
+
await docsInstance.value.toggle();
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const navigate = async (path: string) => {
|
|
76
|
+
if (docsInstance.value) {
|
|
77
|
+
await docsInstance.value.navigate(path);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const setTheme = async (theme: 'light' | 'dark') => {
|
|
82
|
+
if (docsInstance.value) {
|
|
83
|
+
await docsInstance.value.setTheme(theme);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const isOpen = computed(() => {
|
|
88
|
+
return docsInstance.value?.isOpen() || false;
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
open,
|
|
93
|
+
close,
|
|
94
|
+
toggle,
|
|
95
|
+
navigate,
|
|
96
|
+
setTheme,
|
|
97
|
+
isOpen: isOpen.value,
|
|
98
|
+
isReady,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Type declarations
|
|
103
|
+
declare global {
|
|
104
|
+
interface Window {
|
|
105
|
+
DoryDocs: DoryDocsAPI;
|
|
106
|
+
}
|
|
107
|
+
}
|
package/src/theme.scss
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
font-synthesis: none;
|
|
3
|
+
text-rendering: optimizeLegibility;
|
|
4
|
+
-webkit-font-smoothing: antialiased;
|
|
5
|
+
-moz-osx-font-smoothing: grayscale;
|
|
6
|
+
|
|
7
|
+
// Brand colors (dynamically set from dory.json via loadColors())
|
|
8
|
+
// These variables are set at runtime:
|
|
9
|
+
// --brand-primary: primary color
|
|
10
|
+
// --brand-light: light mode accent
|
|
11
|
+
// --brand-dark: dark mode accent
|
|
12
|
+
// --brand-foreground: legacy/fallback (set to primary)
|
|
13
|
+
|
|
14
|
+
// Default fallback colors (will be overridden by loadColors())
|
|
15
|
+
--brand-foreground: #0ea5e9;
|
|
16
|
+
--brand-primary: #0ea5e9;
|
|
17
|
+
--brand-light: #38bdf8;
|
|
18
|
+
--brand-dark: #0284c7;
|
|
19
|
+
|
|
20
|
+
// ShadCN/clidey/ux compatible primary color
|
|
21
|
+
// Using light color for light mode
|
|
22
|
+
--primary: var(--brand-light);
|
|
23
|
+
--primary-rgb: var(--brand-light-rgb);
|
|
24
|
+
|
|
25
|
+
--ring: rgba(0, 0, 0, 0.1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Dark mode overrides
|
|
29
|
+
:root.dark {
|
|
30
|
+
// Use dark color for dark mode
|
|
31
|
+
--primary: var(--brand-dark);
|
|
32
|
+
--primary-rgb: var(--brand-dark-rgb);
|
|
33
|
+
|
|
34
|
+
--ring: rgba(255, 255, 255, 0.1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
article h2:first-of-type {
|
|
38
|
+
display: none;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
article hr:first-of-type {
|
|
42
|
+
display: none;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
article img {
|
|
46
|
+
margin-top: 1rem;
|
|
47
|
+
margin-bottom: 1rem;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
[data-testid="header-actions"] button,
|
|
51
|
+
[data-slot="dropdown-menu-content"] {
|
|
52
|
+
border-color: var(--ring);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
[data-slot="search-input-wrapper"] {
|
|
56
|
+
font-size: 12px;
|
|
57
|
+
outline: none;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
[data-slot="search-input"] {
|
|
61
|
+
font-size: 12px;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
[data-slot="search-input-wrapper"]:focus-within {
|
|
65
|
+
outline: none !important;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
[role="dialog"] [data-slot="search-input-wrapper"]:focus-within {
|
|
69
|
+
outline: none !important;
|
|
70
|
+
border: 0px;
|
|
71
|
+
box-shadow: none !important;
|
|
72
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript interfaces for dory.json configuration
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface DoryColors {
|
|
6
|
+
primary: string;
|
|
7
|
+
light: string;
|
|
8
|
+
dark: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface DoryFonts {
|
|
12
|
+
family: string;
|
|
13
|
+
source: string;
|
|
14
|
+
format: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface DoryLogo {
|
|
18
|
+
light: string;
|
|
19
|
+
dark: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface DoryNavigationPage {
|
|
23
|
+
[key: string]: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface DoryNavigationGroup {
|
|
27
|
+
group: string;
|
|
28
|
+
pages: string[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface DoryNavigationTab {
|
|
32
|
+
tab: string;
|
|
33
|
+
groups: DoryNavigationGroup[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface DoryNavigation {
|
|
37
|
+
tabs: DoryNavigationTab[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface DoryEmbedConfig {
|
|
41
|
+
enabled?: boolean;
|
|
42
|
+
version?: string;
|
|
43
|
+
defaults?: {
|
|
44
|
+
position?: 'left' | 'right' | 'bottom';
|
|
45
|
+
width?: number;
|
|
46
|
+
height?: string;
|
|
47
|
+
theme?: 'light' | 'dark' | 'inherit';
|
|
48
|
+
showOverlay?: boolean;
|
|
49
|
+
closeOnEscape?: boolean;
|
|
50
|
+
closeOnOverlayClick?: boolean;
|
|
51
|
+
};
|
|
52
|
+
branding?: {
|
|
53
|
+
showLogo?: boolean;
|
|
54
|
+
showName?: boolean;
|
|
55
|
+
customCSS?: string;
|
|
56
|
+
};
|
|
57
|
+
features?: {
|
|
58
|
+
search?: boolean;
|
|
59
|
+
toc?: boolean;
|
|
60
|
+
codeBlocks?: boolean;
|
|
61
|
+
mermaid?: boolean;
|
|
62
|
+
katex?: boolean;
|
|
63
|
+
apiPlayground?: boolean;
|
|
64
|
+
darkMode?: boolean;
|
|
65
|
+
};
|
|
66
|
+
analytics?: {
|
|
67
|
+
enabled?: boolean;
|
|
68
|
+
trackEvents?: string[];
|
|
69
|
+
};
|
|
70
|
+
security?: {
|
|
71
|
+
allowedOrigins?: string[];
|
|
72
|
+
corsEnabled?: boolean;
|
|
73
|
+
};
|
|
74
|
+
performance?: {
|
|
75
|
+
lazyLoadImages?: boolean;
|
|
76
|
+
prefetchPages?: boolean;
|
|
77
|
+
cacheStrategy?: 'cache-first' | 'network-first';
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface DoryConfig {
|
|
82
|
+
name: string;
|
|
83
|
+
favicon: string;
|
|
84
|
+
colors?: DoryColors;
|
|
85
|
+
fonts?: DoryFonts;
|
|
86
|
+
navigation: DoryNavigation;
|
|
87
|
+
logo: DoryLogo;
|
|
88
|
+
embed?: DoryEmbedConfig;
|
|
89
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import classNames from 'classnames';
|
|
2
|
+
import { useEffect, useMemo, useState } from 'preact/hooks';
|
|
3
|
+
import { Link } from 'wouter-preact';
|
|
4
|
+
import { usePathname } from 'wouter-preact/use-browser-location';
|
|
5
|
+
import docsConfig from '../../docs/dory.json' with { type: 'json' };
|
|
6
|
+
import type { DoryConfig } from '../types/config';
|
|
7
|
+
import { MobileSearch, Search } from '../components/search';
|
|
8
|
+
import { ALL_NAVIGATION } from '../components/store';
|
|
9
|
+
import { MobileNavigation } from './mobile-navigation';
|
|
10
|
+
import { ModeToggle } from '@clidey/ux';
|
|
11
|
+
import { useIsEmbedded } from '../components/hooks';
|
|
12
|
+
|
|
13
|
+
const config = docsConfig as DoryConfig;
|
|
14
|
+
|
|
15
|
+
function TopLevelNavItem({ href, children, isActive }: { href: string; children: string; isActive?: boolean }) {
|
|
16
|
+
return (
|
|
17
|
+
<li>
|
|
18
|
+
<Link
|
|
19
|
+
to={href}
|
|
20
|
+
className={classNames(
|
|
21
|
+
"text-xs transition hover:opacity-80",
|
|
22
|
+
{
|
|
23
|
+
"text-brand-foreground": isActive,
|
|
24
|
+
},
|
|
25
|
+
)}
|
|
26
|
+
>
|
|
27
|
+
{children}
|
|
28
|
+
</Link>
|
|
29
|
+
</li>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function Header({ className }: { className?: string }) {
|
|
34
|
+
const [isScrolled, setIsScrolled] = useState(false);
|
|
35
|
+
const pathname = usePathname();
|
|
36
|
+
const isEmbedded = useIsEmbedded();
|
|
37
|
+
|
|
38
|
+
const { light, dark } = useMemo(() => {
|
|
39
|
+
return { light: config.logo.light, dark: config.logo.dark };
|
|
40
|
+
}, []);
|
|
41
|
+
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
function onScroll() {
|
|
44
|
+
setIsScrolled(window.scrollY > 0);
|
|
45
|
+
}
|
|
46
|
+
onScroll();
|
|
47
|
+
window.addEventListener('scroll', onScroll, { passive: true });
|
|
48
|
+
return () => {
|
|
49
|
+
window.removeEventListener('scroll', onScroll);
|
|
50
|
+
}
|
|
51
|
+
}, []);
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<header
|
|
55
|
+
className={classNames(
|
|
56
|
+
'sticky top-0 z-50 flex flex-none flex-wrap items-center justify-between bg-white px-4 py-5 shadow-md shadow-gray-900/5 sm:px-6 lg:px-8 dark:shadow-none',
|
|
57
|
+
className,
|
|
58
|
+
{
|
|
59
|
+
'dark:bg-gray-900/95 dark:backdrop-blur-sm dark:[@supports(backdrop-filter:blur(0))]:bg-neutral-900/75': isScrolled,
|
|
60
|
+
'dark:bg-transparent': !isScrolled,
|
|
61
|
+
},
|
|
62
|
+
)}
|
|
63
|
+
>
|
|
64
|
+
<div className="relative flex justify-between gap-6 sm:gap-8 md:grow items-center w-full px-4">
|
|
65
|
+
<div className="flex items-center grow gap-2">
|
|
66
|
+
<img src={`/${light}`} alt="logo" className="w-8 hidden dark:block" />
|
|
67
|
+
<img src={`/${dark}`} alt="logo" className="w-8 block dark:hidden" />
|
|
68
|
+
<h1 className="text-base lg:text-2xl font-bold">{config.name}</h1>
|
|
69
|
+
</div>
|
|
70
|
+
<nav className="hidden md:block">
|
|
71
|
+
<ul role="list" className="flex items-center gap-8">
|
|
72
|
+
{ALL_NAVIGATION.map((tab) => (
|
|
73
|
+
<TopLevelNavItem
|
|
74
|
+
key={tab.title}
|
|
75
|
+
href={tab.groups[0].pages[0].href}
|
|
76
|
+
isActive={pathname.startsWith(`/${tab.groups[0].pages[0].href.split('/')[1]}`)}
|
|
77
|
+
>
|
|
78
|
+
{tab.title}
|
|
79
|
+
</TopLevelNavItem>
|
|
80
|
+
))}
|
|
81
|
+
</ul>
|
|
82
|
+
</nav>
|
|
83
|
+
<div className="hidden md:block md:h-5 md:w-px md:bg-zinc-900/10 md:dark:bg-white/15" />
|
|
84
|
+
<div className="flex items-center gap-3" data-testid="header-actions">
|
|
85
|
+
<MobileSearch />
|
|
86
|
+
<Search />
|
|
87
|
+
{
|
|
88
|
+
!isEmbedded &&
|
|
89
|
+
<ModeToggle data-testid="mode-toggle" />
|
|
90
|
+
}
|
|
91
|
+
<MobileNavigation />
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
</header>
|
|
95
|
+
);
|
|
96
|
+
}
|