create-unmint 1.0.1 → 1.1.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/package.json
CHANGED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState, useCallback } from 'react'
|
|
4
|
+
import Link from 'next/link'
|
|
5
|
+
import Image from 'next/image'
|
|
6
|
+
import { SearchTrigger } from './search-dialog'
|
|
7
|
+
import { ThemeToggle } from './theme-toggle'
|
|
8
|
+
import { MobileSidebar } from './mobile-sidebar'
|
|
9
|
+
import { siteConfig } from '@/lib/theme-config'
|
|
10
|
+
import type { Root } from 'fumadocs-core/page-tree'
|
|
11
|
+
|
|
12
|
+
interface DocsHeaderProps {
|
|
13
|
+
tree: Root
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function DocsHeader({ tree }: DocsHeaderProps) {
|
|
17
|
+
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false)
|
|
18
|
+
|
|
19
|
+
const openMobileMenu = useCallback(() => setIsMobileMenuOpen(true), [])
|
|
20
|
+
const closeMobileMenu = useCallback(() => setIsMobileMenuOpen(false), [])
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<>
|
|
24
|
+
<header className="sticky top-0 z-50 w-full border-b border-border bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
|
25
|
+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
26
|
+
<div className="flex h-16 items-center justify-between gap-4">
|
|
27
|
+
{/* Left: Hamburger + Logo */}
|
|
28
|
+
<div className="flex items-center gap-2">
|
|
29
|
+
{/* Mobile menu button */}
|
|
30
|
+
<button
|
|
31
|
+
onClick={openMobileMenu}
|
|
32
|
+
className="lg:hidden p-2 -ml-2 text-muted-foreground hover:text-foreground hover:bg-muted rounded-md transition-colors min-w-[44px] min-h-[44px] flex items-center justify-center"
|
|
33
|
+
aria-label="Open menu"
|
|
34
|
+
aria-expanded={isMobileMenuOpen}
|
|
35
|
+
>
|
|
36
|
+
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
37
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
|
|
38
|
+
</svg>
|
|
39
|
+
</button>
|
|
40
|
+
|
|
41
|
+
{/* Logo */}
|
|
42
|
+
<Link href="/" className="flex items-center gap-2">
|
|
43
|
+
{siteConfig.logo.src && (
|
|
44
|
+
<Image
|
|
45
|
+
src={siteConfig.logo.src}
|
|
46
|
+
alt={siteConfig.logo.alt}
|
|
47
|
+
width={siteConfig.logo.width}
|
|
48
|
+
height={siteConfig.logo.height}
|
|
49
|
+
className="dark:invert"
|
|
50
|
+
/>
|
|
51
|
+
)}
|
|
52
|
+
<span className="font-semibold text-lg hidden sm:inline">{siteConfig.name}</span>
|
|
53
|
+
</Link>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
{/* Center: Search */}
|
|
57
|
+
<div className="flex-1 flex justify-center px-2 sm:px-4">
|
|
58
|
+
<SearchTrigger />
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
{/* Right: Links */}
|
|
62
|
+
<div className="flex items-center gap-1 sm:gap-2">
|
|
63
|
+
{siteConfig.links.github && (
|
|
64
|
+
<a
|
|
65
|
+
href={siteConfig.links.github}
|
|
66
|
+
target="_blank"
|
|
67
|
+
rel="noopener noreferrer"
|
|
68
|
+
className="p-2 text-muted-foreground hover:text-foreground transition-colors min-w-[44px] min-h-[44px] flex items-center justify-center"
|
|
69
|
+
aria-label="GitHub"
|
|
70
|
+
>
|
|
71
|
+
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
|
|
72
|
+
<path fillRule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clipRule="evenodd" />
|
|
73
|
+
</svg>
|
|
74
|
+
</a>
|
|
75
|
+
)}
|
|
76
|
+
<ThemeToggle />
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</header>
|
|
81
|
+
|
|
82
|
+
{/* Mobile sidebar */}
|
|
83
|
+
<MobileSidebar
|
|
84
|
+
tree={tree}
|
|
85
|
+
isOpen={isMobileMenuOpen}
|
|
86
|
+
onClose={closeMobileMenu}
|
|
87
|
+
/>
|
|
88
|
+
</>
|
|
89
|
+
)
|
|
90
|
+
}
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useEffect, useRef, useCallback, TouchEvent, useState } from 'react'
|
|
4
|
+
import Link from 'next/link'
|
|
5
|
+
import { usePathname } from 'next/navigation'
|
|
6
|
+
import { cn } from '@/lib/utils'
|
|
7
|
+
import { siteConfig } from '@/lib/theme-config'
|
|
8
|
+
import type { Root, Node } from 'fumadocs-core/page-tree'
|
|
9
|
+
|
|
10
|
+
interface MobileSidebarProps {
|
|
11
|
+
tree: Root
|
|
12
|
+
isOpen: boolean
|
|
13
|
+
onClose: () => void
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Mobile sidebar with slide-in drawer animation
|
|
18
|
+
* - Swipe right to close
|
|
19
|
+
* - Tap backdrop to close
|
|
20
|
+
* - Escape key to close
|
|
21
|
+
* - Auto-close on navigation
|
|
22
|
+
*/
|
|
23
|
+
export function MobileSidebar({ tree, isOpen, onClose }: MobileSidebarProps) {
|
|
24
|
+
const pathname = usePathname()
|
|
25
|
+
const panelRef = useRef<HTMLDivElement>(null)
|
|
26
|
+
const touchStart = useRef<{ x: number; y: number; time: number } | null>(null)
|
|
27
|
+
const touchMove = useRef<{ x: number; y: number } | null>(null)
|
|
28
|
+
|
|
29
|
+
// Close on route change
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
onClose()
|
|
32
|
+
}, [pathname, onClose])
|
|
33
|
+
|
|
34
|
+
// Close on escape key
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
const handleKeyDown = (e: KeyboardEvent) => {
|
|
37
|
+
if (e.key === 'Escape' && isOpen) {
|
|
38
|
+
onClose()
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
document.addEventListener('keydown', handleKeyDown)
|
|
43
|
+
return () => document.removeEventListener('keydown', handleKeyDown)
|
|
44
|
+
}, [isOpen, onClose])
|
|
45
|
+
|
|
46
|
+
// Prevent body scroll when open
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
if (isOpen) {
|
|
49
|
+
document.body.style.overflow = 'hidden'
|
|
50
|
+
} else {
|
|
51
|
+
document.body.style.overflow = ''
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return () => {
|
|
55
|
+
document.body.style.overflow = ''
|
|
56
|
+
}
|
|
57
|
+
}, [isOpen])
|
|
58
|
+
|
|
59
|
+
// Swipe handlers
|
|
60
|
+
const handleTouchStart = useCallback((e: TouchEvent) => {
|
|
61
|
+
const touch = e.touches[0]
|
|
62
|
+
touchStart.current = {
|
|
63
|
+
x: touch.clientX,
|
|
64
|
+
y: touch.clientY,
|
|
65
|
+
time: Date.now(),
|
|
66
|
+
}
|
|
67
|
+
touchMove.current = null
|
|
68
|
+
}, [])
|
|
69
|
+
|
|
70
|
+
const handleTouchMove = useCallback((e: TouchEvent) => {
|
|
71
|
+
const touch = e.touches[0]
|
|
72
|
+
touchMove.current = {
|
|
73
|
+
x: touch.clientX,
|
|
74
|
+
y: touch.clientY,
|
|
75
|
+
}
|
|
76
|
+
}, [])
|
|
77
|
+
|
|
78
|
+
const handleTouchEnd = useCallback(() => {
|
|
79
|
+
if (!touchStart.current || !touchMove.current) return
|
|
80
|
+
|
|
81
|
+
const deltaX = touchMove.current.x - touchStart.current.x
|
|
82
|
+
const deltaY = touchMove.current.y - touchStart.current.y
|
|
83
|
+
const timeElapsed = Date.now() - touchStart.current.time
|
|
84
|
+
|
|
85
|
+
// Calculate velocity
|
|
86
|
+
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY)
|
|
87
|
+
const velocity = distance / timeElapsed
|
|
88
|
+
|
|
89
|
+
// Swipe left to close (when panel is on left side)
|
|
90
|
+
const threshold = 50
|
|
91
|
+
const velocityThreshold = 0.3
|
|
92
|
+
|
|
93
|
+
if (
|
|
94
|
+
Math.abs(deltaX) > Math.abs(deltaY) && // Horizontal swipe
|
|
95
|
+
deltaX < -threshold && // Swiping left
|
|
96
|
+
velocity > velocityThreshold
|
|
97
|
+
) {
|
|
98
|
+
onClose()
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
touchStart.current = null
|
|
102
|
+
touchMove.current = null
|
|
103
|
+
}, [onClose])
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<>
|
|
107
|
+
{/* Backdrop */}
|
|
108
|
+
<div
|
|
109
|
+
className={cn(
|
|
110
|
+
'fixed inset-0 bg-black/60 z-40 transition-opacity duration-300 lg:hidden',
|
|
111
|
+
isOpen ? 'opacity-100' : 'opacity-0 pointer-events-none'
|
|
112
|
+
)}
|
|
113
|
+
onClick={onClose}
|
|
114
|
+
aria-hidden="true"
|
|
115
|
+
/>
|
|
116
|
+
|
|
117
|
+
{/* Sliding panel */}
|
|
118
|
+
<div
|
|
119
|
+
ref={panelRef}
|
|
120
|
+
className={cn(
|
|
121
|
+
'fixed top-0 left-0 bottom-0 w-80 max-w-[85vw] bg-background border-r border-border z-50 transform transition-transform duration-300 ease-out lg:hidden',
|
|
122
|
+
isOpen ? 'translate-x-0' : '-translate-x-full'
|
|
123
|
+
)}
|
|
124
|
+
onTouchStart={handleTouchStart}
|
|
125
|
+
onTouchMove={handleTouchMove}
|
|
126
|
+
onTouchEnd={handleTouchEnd}
|
|
127
|
+
>
|
|
128
|
+
{/* Header */}
|
|
129
|
+
<div className="flex items-center justify-between h-16 px-4 border-b border-border">
|
|
130
|
+
<Link href="/" className="font-semibold text-lg" onClick={onClose}>
|
|
131
|
+
{siteConfig.name}
|
|
132
|
+
</Link>
|
|
133
|
+
<button
|
|
134
|
+
onClick={onClose}
|
|
135
|
+
className="p-2 -mr-2 text-muted-foreground hover:text-foreground hover:bg-muted rounded-md transition-colors min-w-[44px] min-h-[44px] flex items-center justify-center"
|
|
136
|
+
aria-label="Close menu"
|
|
137
|
+
>
|
|
138
|
+
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
139
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
|
140
|
+
</svg>
|
|
141
|
+
</button>
|
|
142
|
+
</div>
|
|
143
|
+
|
|
144
|
+
{/* Navigation */}
|
|
145
|
+
<nav className="h-[calc(100%-4rem)] overflow-y-auto p-4">
|
|
146
|
+
{/* Quick links */}
|
|
147
|
+
<div className="mb-6 pb-5 border-b border-border">
|
|
148
|
+
<ul className="space-y-1">
|
|
149
|
+
<li>
|
|
150
|
+
<Link
|
|
151
|
+
href="/docs"
|
|
152
|
+
onClick={onClose}
|
|
153
|
+
className="flex items-center gap-3 py-2 px-2 text-sm text-[var(--accent)] font-medium hover:bg-[var(--accent-muted)] rounded-md transition-colors min-h-[44px]"
|
|
154
|
+
>
|
|
155
|
+
<span className="flex items-center justify-center w-7 h-7 rounded-md bg-[var(--accent-muted)]">
|
|
156
|
+
<svg className="w-4 h-4 text-[var(--accent)]" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
157
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" />
|
|
158
|
+
</svg>
|
|
159
|
+
</span>
|
|
160
|
+
Documentation
|
|
161
|
+
</Link>
|
|
162
|
+
</li>
|
|
163
|
+
{siteConfig.links.github && (
|
|
164
|
+
<li>
|
|
165
|
+
<a
|
|
166
|
+
href={siteConfig.links.github}
|
|
167
|
+
target="_blank"
|
|
168
|
+
rel="noopener noreferrer"
|
|
169
|
+
className="flex items-center gap-3 py-2 px-2 text-sm text-muted-foreground hover:text-foreground hover:bg-muted rounded-md transition-colors min-h-[44px]"
|
|
170
|
+
>
|
|
171
|
+
<span className="flex items-center justify-center w-7 h-7 rounded-md bg-muted">
|
|
172
|
+
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
|
|
173
|
+
<path fillRule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clipRule="evenodd" />
|
|
174
|
+
</svg>
|
|
175
|
+
</span>
|
|
176
|
+
GitHub
|
|
177
|
+
</a>
|
|
178
|
+
</li>
|
|
179
|
+
)}
|
|
180
|
+
{siteConfig.links.support && (
|
|
181
|
+
<li>
|
|
182
|
+
<a
|
|
183
|
+
href={siteConfig.links.support}
|
|
184
|
+
className="flex items-center gap-3 py-2 px-2 text-sm text-muted-foreground hover:text-foreground hover:bg-muted rounded-md transition-colors min-h-[44px]"
|
|
185
|
+
>
|
|
186
|
+
<span className="flex items-center justify-center w-7 h-7 rounded-md bg-muted">
|
|
187
|
+
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
188
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
|
189
|
+
</svg>
|
|
190
|
+
</span>
|
|
191
|
+
Support
|
|
192
|
+
</a>
|
|
193
|
+
</li>
|
|
194
|
+
)}
|
|
195
|
+
</ul>
|
|
196
|
+
</div>
|
|
197
|
+
|
|
198
|
+
{/* Page tree navigation */}
|
|
199
|
+
<MobileSidebarNodes nodes={tree.children} pathname={pathname} onNavigate={onClose} />
|
|
200
|
+
</nav>
|
|
201
|
+
</div>
|
|
202
|
+
</>
|
|
203
|
+
)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
interface MobileSidebarNodesProps {
|
|
207
|
+
nodes: Node[]
|
|
208
|
+
pathname: string
|
|
209
|
+
onNavigate: () => void
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function MobileSidebarNodes({ nodes, pathname, onNavigate }: MobileSidebarNodesProps) {
|
|
213
|
+
return (
|
|
214
|
+
<div className="space-y-1">
|
|
215
|
+
{nodes.map((node, index) => (
|
|
216
|
+
<MobileSidebarNode key={index} node={node} pathname={pathname} onNavigate={onNavigate} />
|
|
217
|
+
))}
|
|
218
|
+
</div>
|
|
219
|
+
)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
interface MobileSidebarNodeProps {
|
|
223
|
+
node: Node
|
|
224
|
+
pathname: string
|
|
225
|
+
onNavigate: () => void
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function MobileSidebarNode({ node, pathname, onNavigate }: MobileSidebarNodeProps) {
|
|
229
|
+
const [isExpanded, setIsExpanded] = useState(true)
|
|
230
|
+
|
|
231
|
+
if (node.type === 'separator') {
|
|
232
|
+
return (
|
|
233
|
+
<div className="pt-4 first:pt-0">
|
|
234
|
+
<h5 className="text-sm font-semibold text-foreground mb-1.5 px-2">
|
|
235
|
+
{node.name}
|
|
236
|
+
</h5>
|
|
237
|
+
</div>
|
|
238
|
+
)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (node.type === 'folder') {
|
|
242
|
+
return (
|
|
243
|
+
<div>
|
|
244
|
+
<button
|
|
245
|
+
onClick={() => setIsExpanded(!isExpanded)}
|
|
246
|
+
className="flex items-center justify-between w-full py-2 px-2 text-sm font-medium text-muted-foreground hover:text-foreground hover:bg-muted rounded-md transition-colors min-h-[44px]"
|
|
247
|
+
>
|
|
248
|
+
<span>{node.name}</span>
|
|
249
|
+
<svg
|
|
250
|
+
className={cn('w-4 h-4 transition-transform', isExpanded && 'rotate-90')}
|
|
251
|
+
fill="none"
|
|
252
|
+
stroke="currentColor"
|
|
253
|
+
viewBox="0 0 24 24"
|
|
254
|
+
>
|
|
255
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
|
256
|
+
</svg>
|
|
257
|
+
</button>
|
|
258
|
+
{isExpanded && node.children && (
|
|
259
|
+
<ul className="ml-3 mt-1 space-y-0.5 border-l border-border pl-3">
|
|
260
|
+
{node.children.map((child, index) => (
|
|
261
|
+
<MobileSidebarNode key={index} node={child} pathname={pathname} onNavigate={onNavigate} />
|
|
262
|
+
))}
|
|
263
|
+
</ul>
|
|
264
|
+
)}
|
|
265
|
+
</div>
|
|
266
|
+
)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const isActive = pathname === node.url
|
|
270
|
+
|
|
271
|
+
return (
|
|
272
|
+
<li className="list-none">
|
|
273
|
+
<Link
|
|
274
|
+
href={node.url}
|
|
275
|
+
onClick={onNavigate}
|
|
276
|
+
className={cn(
|
|
277
|
+
'flex items-center gap-2 py-2 px-2 text-sm transition-colors rounded-md min-h-[44px]',
|
|
278
|
+
isActive
|
|
279
|
+
? 'text-[var(--accent)] font-medium bg-[var(--accent-muted)]'
|
|
280
|
+
: 'text-muted-foreground hover:text-foreground hover:bg-muted'
|
|
281
|
+
)}
|
|
282
|
+
>
|
|
283
|
+
<span>{node.name}</span>
|
|
284
|
+
</Link>
|
|
285
|
+
</li>
|
|
286
|
+
)
|
|
287
|
+
}
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
import Link from 'next/link'
|
|
2
|
-
import Image from 'next/image'
|
|
3
1
|
import { source } from '@/lib/docs-source'
|
|
4
2
|
import { DocsSidebar } from '../components/docs/docs-sidebar'
|
|
5
|
-
import {
|
|
6
|
-
import { ThemeToggle } from '../components/docs/theme-toggle'
|
|
3
|
+
import { DocsHeader } from '../components/docs/docs-header'
|
|
7
4
|
import { siteConfig } from '@/lib/theme-config'
|
|
8
5
|
|
|
9
6
|
export default function DocsLayout({
|
|
@@ -15,48 +12,8 @@ export default function DocsLayout({
|
|
|
15
12
|
|
|
16
13
|
return (
|
|
17
14
|
<div className="min-h-screen flex flex-col">
|
|
18
|
-
{/* Header */}
|
|
19
|
-
<
|
|
20
|
-
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
21
|
-
<div className="flex h-16 items-center justify-between">
|
|
22
|
-
{/* Logo */}
|
|
23
|
-
<Link href="/" className="flex items-center gap-2">
|
|
24
|
-
{siteConfig.logo.src && (
|
|
25
|
-
<Image
|
|
26
|
-
src={siteConfig.logo.src}
|
|
27
|
-
alt={siteConfig.logo.alt}
|
|
28
|
-
width={siteConfig.logo.width}
|
|
29
|
-
height={siteConfig.logo.height}
|
|
30
|
-
className="dark:invert"
|
|
31
|
-
/>
|
|
32
|
-
)}
|
|
33
|
-
<span className="font-semibold text-lg">{siteConfig.name}</span>
|
|
34
|
-
</Link>
|
|
35
|
-
|
|
36
|
-
{/* Center: Search */}
|
|
37
|
-
<div className="flex-1 flex justify-center px-4">
|
|
38
|
-
<SearchTrigger />
|
|
39
|
-
</div>
|
|
40
|
-
|
|
41
|
-
{/* Right: Links */}
|
|
42
|
-
<div className="flex items-center gap-2">
|
|
43
|
-
{siteConfig.links.github && (
|
|
44
|
-
<a
|
|
45
|
-
href={siteConfig.links.github}
|
|
46
|
-
target="_blank"
|
|
47
|
-
rel="noopener noreferrer"
|
|
48
|
-
className="p-2 text-muted-foreground hover:text-foreground transition-colors"
|
|
49
|
-
>
|
|
50
|
-
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
|
|
51
|
-
<path fillRule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clipRule="evenodd" />
|
|
52
|
-
</svg>
|
|
53
|
-
</a>
|
|
54
|
-
)}
|
|
55
|
-
<ThemeToggle />
|
|
56
|
-
</div>
|
|
57
|
-
</div>
|
|
58
|
-
</div>
|
|
59
|
-
</header>
|
|
15
|
+
{/* Header with mobile navigation */}
|
|
16
|
+
<DocsHeader tree={tree} />
|
|
60
17
|
|
|
61
18
|
{/* Main content */}
|
|
62
19
|
<div className="flex-1">
|