polen 0.10.0-next.5 → 0.10.0-next.7
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/build/api/content/$$.d.ts +1 -0
- package/build/api/content/$$.d.ts.map +1 -1
- package/build/api/content/$$.js +1 -0
- package/build/api/content/$$.js.map +1 -1
- package/build/api/content/navbar.d.ts +10 -0
- package/build/api/content/navbar.d.ts.map +1 -0
- package/build/api/content/navbar.js +45 -0
- package/build/api/content/navbar.js.map +1 -0
- package/build/api/content/sidebar.d.ts +85 -5
- package/build/api/content/sidebar.d.ts.map +1 -1
- package/build/api/content/sidebar.js +151 -75
- package/build/api/content/sidebar.js.map +1 -1
- package/build/api/vite/plugins/pages.d.ts +1 -4
- package/build/api/vite/plugins/pages.d.ts.map +1 -1
- package/build/api/vite/plugins/pages.js +4 -42
- package/build/api/vite/plugins/pages.js.map +1 -1
- package/build/lib/file-router/scan.d.ts.map +1 -1
- package/build/lib/file-router/scan.js +6 -1
- package/build/lib/file-router/scan.js.map +1 -1
- package/build/sandbox.js +17 -2
- package/build/sandbox.js.map +1 -1
- package/build/template/components/HamburgerMenu.d.ts +9 -0
- package/build/template/components/HamburgerMenu.d.ts.map +1 -0
- package/build/template/components/HamburgerMenu.jsx +53 -0
- package/build/template/components/HamburgerMenu.jsx.map +1 -0
- package/build/template/components/NotFound.d.ts +2 -0
- package/build/template/components/NotFound.d.ts.map +1 -0
- package/build/template/components/NotFound.jsx +26 -0
- package/build/template/components/NotFound.jsx.map +1 -0
- package/build/template/components/ThemeToggle.d.ts +3 -0
- package/build/template/components/ThemeToggle.d.ts.map +1 -0
- package/build/template/components/ThemeToggle.jsx +10 -0
- package/build/template/components/ThemeToggle.jsx.map +1 -0
- package/build/template/contexts/ThemeContext.d.ts +12 -0
- package/build/template/contexts/ThemeContext.d.ts.map +1 -0
- package/build/template/contexts/ThemeContext.jsx +41 -0
- package/build/template/contexts/ThemeContext.jsx.map +1 -0
- package/build/template/routes/root.d.ts.map +1 -1
- package/build/template/routes/root.jsx +55 -39
- package/build/template/routes/root.jsx.map +1 -1
- package/package.json +1 -1
- package/src/api/content/$$.ts +1 -0
- package/src/api/content/navbar.test.ts +55 -0
- package/src/api/content/navbar.ts +61 -0
- package/src/api/content/sidebar.test.ts +297 -0
- package/src/api/content/sidebar.ts +235 -88
- package/src/api/vite/plugins/pages.ts +5 -51
- package/src/lib/file-router/scan.ts +7 -1
- package/src/sandbox.ts +20 -1
- package/src/template/components/HamburgerMenu.tsx +96 -0
- package/src/template/components/NotFound.tsx +28 -0
- package/src/template/components/ThemeToggle.tsx +21 -0
- package/src/template/contexts/ThemeContext.tsx +60 -0
- package/src/template/routes/root.tsx +74 -51
@@ -1,5 +1,6 @@
|
|
1
1
|
import type { Config } from '#api/config/index'
|
2
2
|
import { Content } from '#api/content/$'
|
3
|
+
import { createNavbar } from '#api/content/navbar'
|
3
4
|
import type { NavbarDataRegistry } from '#api/vite/data/navbar'
|
4
5
|
import { polenVirtual } from '#api/vite/vi'
|
5
6
|
import type { Vite } from '#dep/vite/index'
|
@@ -9,7 +10,6 @@ import { debugPolen } from '#singletons/debug'
|
|
9
10
|
import { superjson } from '#singletons/superjson'
|
10
11
|
import mdx from '@mdx-js/rollup'
|
11
12
|
import rehypeShiki from '@shikijs/rehype'
|
12
|
-
import { Tree } from '@wollybeard/kit'
|
13
13
|
import { Arr, Cache, Path, Str } from '@wollybeard/kit'
|
14
14
|
import remarkFrontmatter from 'remark-frontmatter'
|
15
15
|
import remarkGfm from 'remark-gfm'
|
@@ -26,14 +26,10 @@ export interface Options {
|
|
26
26
|
}
|
27
27
|
|
28
28
|
export interface ProjectDataPages {
|
29
|
-
sidebarIndex: SidebarIndex
|
29
|
+
sidebarIndex: Content.SidebarIndex
|
30
30
|
pages: Content.Page[]
|
31
31
|
}
|
32
32
|
|
33
|
-
export interface SidebarIndex {
|
34
|
-
[pathExpression: string]: Content.Sidebar
|
35
|
-
}
|
36
|
-
|
37
33
|
/**
|
38
34
|
* Pages plugin with tree support
|
39
35
|
*/
|
@@ -204,57 +200,15 @@ export const Pages = ({
|
|
204
200
|
const navbarPages = navbarData.get('pages')
|
205
201
|
navbarPages.length = 0 // Clear existing
|
206
202
|
|
207
|
-
|
208
|
-
|
209
|
-
for (const child of scanResult.tree.root.children) {
|
210
|
-
// Now we have Page objects in the tree
|
211
|
-
const page = child.value
|
212
|
-
const pathExp = FileRouter.routeToPathExpression(page.route)
|
213
|
-
|
214
|
-
// Skip hidden pages and index files at root level
|
215
|
-
if (page.metadata.hidden || page.route.logical.path.slice(-1)[0] === 'index') {
|
216
|
-
continue
|
217
|
-
}
|
218
|
-
|
219
|
-
// Only include top-level pages (files directly in pages directory)
|
220
|
-
if (page.route.logical.path.length === 1) {
|
221
|
-
const title = Str.titlizeSlug(page.route.logical.path[0]!)
|
222
|
-
navbarPages.push({
|
223
|
-
// IMPORTANT: Always ensure paths start with '/' for React Router compatibility.
|
224
|
-
pathExp: pathExp.startsWith('/') ? pathExp : '/' + pathExp,
|
225
|
-
title,
|
226
|
-
})
|
227
|
-
}
|
228
|
-
}
|
229
|
-
}
|
203
|
+
const navbarItems = createNavbar(scanResult.list)
|
204
|
+
navbarPages.push(...navbarItems)
|
230
205
|
}
|
231
206
|
|
232
207
|
//
|
233
208
|
// ━━ Build Sidebar
|
234
209
|
//
|
235
210
|
|
236
|
-
const sidebarIndex
|
237
|
-
|
238
|
-
// Build sidebar for each top-level directory using the page tree
|
239
|
-
if (scanResult.tree.root) {
|
240
|
-
Tree.visit(scanResult.tree, (node) => {
|
241
|
-
if (!node.value) return
|
242
|
-
const page = node.value as any
|
243
|
-
// Only process top-level directories (pages with logical path length > 1 indicate nested structure)
|
244
|
-
if (page.route.logical.path.length === 1 && node.children.length > 0) {
|
245
|
-
const topLevelDir = page.route.logical.path[0]!
|
246
|
-
const pathExp = `/${topLevelDir}`
|
247
|
-
|
248
|
-
// Create a subtree for this directory
|
249
|
-
const subtree = Tree.Tree(Tree.Node(page, node.children)) as Tree.Tree<any>
|
250
|
-
|
251
|
-
// Build sidebar using the new page tree builder
|
252
|
-
const sidebar = Content.buildFromPageTree(subtree, [topLevelDir])
|
253
|
-
debug(`Built sidebar for ${pathExp}:`, sidebar)
|
254
|
-
sidebarIndex[pathExp] = sidebar
|
255
|
-
}
|
256
|
-
})
|
257
|
-
}
|
211
|
+
const sidebarIndex = Content.buildSidebarIndex(scanResult)
|
258
212
|
|
259
213
|
//
|
260
214
|
// ━━ Put It All together
|
@@ -87,7 +87,13 @@ export const filePathToRoute = (filePathExpression: string, rootDir: string): Ro
|
|
87
87
|
}
|
88
88
|
|
89
89
|
export const filePathToRouteLogical = (filePath: Path.Parsed): RouteLogical => {
|
90
|
-
const
|
90
|
+
const dirSegments = Str.split(Str.removeSurrounding(filePath.dir, Path.sep), Path.sep)
|
91
|
+
|
92
|
+
// Parse numbered prefixes from directory segments
|
93
|
+
const dirPath = dirSegments.map(segment => {
|
94
|
+
const prefixMatch = Str.match(segment, conventions.numberedPrefix.pattern)
|
95
|
+
return prefixMatch?.groups.name ?? segment
|
96
|
+
})
|
91
97
|
|
92
98
|
// Parse numbered prefix from filename
|
93
99
|
const prefixMatch = Str.match(filePath.name, conventions.numberedPrefix.pattern)
|
package/src/sandbox.ts
CHANGED
@@ -1 +1,20 @@
|
|
1
|
-
|
1
|
+
import { filePathToRouteLogical } from '#lib/file-router/scan'
|
2
|
+
import { Path } from '@wollybeard/kit'
|
3
|
+
|
4
|
+
// Test parsing of numbered directory
|
5
|
+
const testPath1 = Path.parse('a/10_b/index.md')
|
6
|
+
const logical1 = filePathToRouteLogical(testPath1)
|
7
|
+
console.log('Path 1:', testPath1)
|
8
|
+
console.log('Logical 1:', logical1)
|
9
|
+
|
10
|
+
// Test parsing of numbered file
|
11
|
+
const testPath2 = Path.parse('a/10_b/g.md')
|
12
|
+
const logical2 = filePathToRouteLogical(testPath2)
|
13
|
+
console.log('\nPath 2:', testPath2)
|
14
|
+
console.log('Logical 2:', logical2)
|
15
|
+
|
16
|
+
// Test directory structure
|
17
|
+
const testPath3 = Path.parse('a/30_d/index.md')
|
18
|
+
const logical3 = filePathToRouteLogical(testPath3)
|
19
|
+
console.log('\nPath 3:', testPath3)
|
20
|
+
console.log('Logical 3:', logical3)
|
@@ -0,0 +1,96 @@
|
|
1
|
+
import type { Content } from '#api/content/$'
|
2
|
+
import { Cross2Icon, HamburgerMenuIcon } from '@radix-ui/react-icons'
|
3
|
+
import { Box, Flex, IconButton, Text } from '@radix-ui/themes'
|
4
|
+
import { useEffect } from 'react'
|
5
|
+
import { Sidebar } from '../components/sidebar/Sidebar.tsx'
|
6
|
+
|
7
|
+
export interface HamburgerMenuProps {
|
8
|
+
isOpen: boolean
|
9
|
+
onToggle: () => void
|
10
|
+
onClose: () => void
|
11
|
+
sidebarData: Content.Item[]
|
12
|
+
}
|
13
|
+
|
14
|
+
export const HamburgerMenu: React.FC<HamburgerMenuProps> = ({
|
15
|
+
isOpen,
|
16
|
+
onToggle,
|
17
|
+
onClose,
|
18
|
+
sidebarData,
|
19
|
+
}) => {
|
20
|
+
// Prevent body scroll when mobile menu is open
|
21
|
+
useEffect(() => {
|
22
|
+
if (isOpen) {
|
23
|
+
document.body.style.overflow = 'hidden'
|
24
|
+
} else {
|
25
|
+
document.body.style.overflow = ''
|
26
|
+
}
|
27
|
+
|
28
|
+
// Cleanup
|
29
|
+
return () => {
|
30
|
+
document.body.style.overflow = ''
|
31
|
+
}
|
32
|
+
}, [isOpen])
|
33
|
+
|
34
|
+
return (
|
35
|
+
<>
|
36
|
+
{/* Mobile menu button - show on mobile/tablet, hide on desktop */}
|
37
|
+
<Box display={{ initial: 'block', xs: 'block', sm: 'block', md: 'none', lg: 'none', xl: 'none' }}>
|
38
|
+
<IconButton
|
39
|
+
size='2'
|
40
|
+
variant='ghost'
|
41
|
+
onClick={onToggle}
|
42
|
+
aria-label='Toggle navigation menu'
|
43
|
+
>
|
44
|
+
{isOpen ? <Cross2Icon width='18' height='18' /> : <HamburgerMenuIcon width='18' height='18' />}
|
45
|
+
</IconButton>
|
46
|
+
</Box>
|
47
|
+
|
48
|
+
{/* Mobile Sidebar Drawer */}
|
49
|
+
{isOpen && (
|
50
|
+
<>
|
51
|
+
{/* Backdrop */}
|
52
|
+
<Box
|
53
|
+
position='fixed'
|
54
|
+
inset='0'
|
55
|
+
style={{
|
56
|
+
backgroundColor: 'var(--black-a9)',
|
57
|
+
zIndex: 50,
|
58
|
+
}}
|
59
|
+
onClick={onClose}
|
60
|
+
display={{ initial: 'block', xs: 'block', sm: 'block', md: 'none', lg: 'none', xl: 'none' }}
|
61
|
+
/>
|
62
|
+
|
63
|
+
{/* Drawer */}
|
64
|
+
<Box
|
65
|
+
position='fixed'
|
66
|
+
top='0'
|
67
|
+
left='0'
|
68
|
+
bottom='0'
|
69
|
+
width='280px'
|
70
|
+
style={{
|
71
|
+
backgroundColor: 'var(--color-background)',
|
72
|
+
boxShadow: 'var(--shadow-6)',
|
73
|
+
zIndex: 100,
|
74
|
+
overflowY: 'auto',
|
75
|
+
}}
|
76
|
+
p='4'
|
77
|
+
display={{ initial: 'block', xs: 'block', sm: 'block', md: 'none', lg: 'none', xl: 'none' }}
|
78
|
+
>
|
79
|
+
<Flex justify='between' align='center' mb='4'>
|
80
|
+
<Text size='5' weight='bold'>Navigation</Text>
|
81
|
+
<IconButton
|
82
|
+
size='2'
|
83
|
+
variant='ghost'
|
84
|
+
onClick={onClose}
|
85
|
+
aria-label='Close navigation menu'
|
86
|
+
>
|
87
|
+
<Cross2Icon width='18' height='18' />
|
88
|
+
</IconButton>
|
89
|
+
</Flex>
|
90
|
+
<Sidebar data={sidebarData} />
|
91
|
+
</Box>
|
92
|
+
</>
|
93
|
+
)}
|
94
|
+
</>
|
95
|
+
)
|
96
|
+
}
|
@@ -0,0 +1,28 @@
|
|
1
|
+
import { Box, Button, Flex, Heading, Text } from '@radix-ui/themes'
|
2
|
+
import { Link as LinkReactRouter } from 'react-router'
|
3
|
+
|
4
|
+
export const NotFound: React.FC = () => {
|
5
|
+
return (
|
6
|
+
<Flex direction='column' align='center' gap='6' style={{ textAlign: `center`, paddingTop: `4rem` }}>
|
7
|
+
<Heading size='9' style={{ color: `var(--gray-12)` }}>404</Heading>
|
8
|
+
<Box>
|
9
|
+
<Heading size='5' mb='2'>Page Not Found</Heading>
|
10
|
+
<Text size='3' color='gray'>
|
11
|
+
The page you're looking for doesn't exist or has been moved.
|
12
|
+
</Text>
|
13
|
+
</Box>
|
14
|
+
<Flex gap='3'>
|
15
|
+
<LinkReactRouter to='/'>
|
16
|
+
<Button variant='soft' size='3'>
|
17
|
+
Go Home
|
18
|
+
</Button>
|
19
|
+
</LinkReactRouter>
|
20
|
+
<LinkReactRouter to='/reference'>
|
21
|
+
<Button variant='outline' size='3'>
|
22
|
+
View API Reference
|
23
|
+
</Button>
|
24
|
+
</LinkReactRouter>
|
25
|
+
</Flex>
|
26
|
+
</Flex>
|
27
|
+
)
|
28
|
+
}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import type { React } from '#dep/react/index'
|
2
|
+
import { MoonIcon, SunIcon } from '@radix-ui/react-icons'
|
3
|
+
import { IconButton } from '@radix-ui/themes'
|
4
|
+
import { useTheme } from '../contexts/ThemeContext.tsx'
|
5
|
+
|
6
|
+
export const ThemeToggle: React.FC = () => {
|
7
|
+
const { appearance, toggleTheme } = useTheme()
|
8
|
+
|
9
|
+
return (
|
10
|
+
<IconButton
|
11
|
+
size='2'
|
12
|
+
variant='ghost'
|
13
|
+
color='gray'
|
14
|
+
onClick={toggleTheme}
|
15
|
+
aria-label={`Switch to ${appearance === 'light' ? 'dark' : 'light'} theme`}
|
16
|
+
style={{ cursor: 'pointer' }}
|
17
|
+
>
|
18
|
+
{appearance === 'light' ? <MoonIcon width='18' height='18' /> : <SunIcon width='18' height='18' />}
|
19
|
+
</IconButton>
|
20
|
+
)
|
21
|
+
}
|
@@ -0,0 +1,60 @@
|
|
1
|
+
import type { React } from '#dep/react/index'
|
2
|
+
import { createContext, useContext, useEffect, useState } from 'react'
|
3
|
+
|
4
|
+
type ThemeAppearance = 'light' | 'dark'
|
5
|
+
|
6
|
+
interface ThemeContextValue {
|
7
|
+
appearance: ThemeAppearance
|
8
|
+
toggleTheme: () => void
|
9
|
+
}
|
10
|
+
|
11
|
+
const ThemeContext = createContext<ThemeContextValue | undefined>(undefined)
|
12
|
+
|
13
|
+
const THEME_STORAGE_KEY = 'polen-theme-preference'
|
14
|
+
|
15
|
+
export const ThemeProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
16
|
+
const [appearance, setAppearance] = useState<ThemeAppearance>(() => {
|
17
|
+
// Check if we're in the browser
|
18
|
+
if (typeof window === 'undefined') {
|
19
|
+
return 'light'
|
20
|
+
}
|
21
|
+
|
22
|
+
// Check localStorage first
|
23
|
+
const stored = localStorage.getItem(THEME_STORAGE_KEY)
|
24
|
+
if (stored === 'light' || stored === 'dark') {
|
25
|
+
return stored
|
26
|
+
}
|
27
|
+
|
28
|
+
// Check system preference
|
29
|
+
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
30
|
+
return 'dark'
|
31
|
+
}
|
32
|
+
|
33
|
+
return 'light'
|
34
|
+
})
|
35
|
+
|
36
|
+
useEffect(() => {
|
37
|
+
// Persist to localStorage
|
38
|
+
if (typeof window !== 'undefined') {
|
39
|
+
localStorage.setItem(THEME_STORAGE_KEY, appearance)
|
40
|
+
}
|
41
|
+
}, [appearance])
|
42
|
+
|
43
|
+
const toggleTheme = () => {
|
44
|
+
setAppearance(prev => prev === 'light' ? 'dark' : 'light')
|
45
|
+
}
|
46
|
+
|
47
|
+
return (
|
48
|
+
<ThemeContext.Provider value={{ appearance, toggleTheme }}>
|
49
|
+
{children}
|
50
|
+
</ThemeContext.Provider>
|
51
|
+
)
|
52
|
+
}
|
53
|
+
|
54
|
+
export const useTheme = () => {
|
55
|
+
const context = useContext(ThemeContext)
|
56
|
+
if (!context) {
|
57
|
+
throw new Error('useTheme must be used within a ThemeProvider')
|
58
|
+
}
|
59
|
+
return context
|
60
|
+
}
|
@@ -1,10 +1,10 @@
|
|
1
|
-
import { assetUrl } from '#api/utils/asset-url/index'
|
2
1
|
import type { ReactRouter } from '#dep/react-router/index'
|
3
2
|
import { createRoute } from '#lib/react-router-aid/react-router-aid'
|
4
|
-
import { Box,
|
3
|
+
import { Box, Grid } from '@radix-ui/themes'
|
5
4
|
import { Flex, Theme } from '@radix-ui/themes'
|
6
5
|
import radixStylesUrl from '@radix-ui/themes/styles.css?url'
|
7
6
|
import { Arr } from '@wollybeard/kit'
|
7
|
+
import { useEffect, useState } from 'react'
|
8
8
|
import { Link as LinkReactRouter } from 'react-router'
|
9
9
|
import { Outlet, ScrollRestoration, useLocation } from 'react-router'
|
10
10
|
import logoSrc from 'virtual:polen/project/assets/logo.svg'
|
@@ -13,13 +13,17 @@ import projectDataNavbar from 'virtual:polen/project/data/navbar.jsonsuper'
|
|
13
13
|
import projectDataPages from 'virtual:polen/project/data/pages.jsonsuper'
|
14
14
|
import { pages } from 'virtual:polen/project/pages.jsx'
|
15
15
|
import { templateVariables } from 'virtual:polen/template/variables'
|
16
|
-
import {
|
17
|
-
import {
|
18
|
-
import {
|
16
|
+
import { HamburgerMenu } from '../components/HamburgerMenu.tsx'
|
17
|
+
import { Link } from '../components/Link.tsx'
|
18
|
+
import { Logo } from '../components/Logo.tsx'
|
19
|
+
import { NotFound } from '../components/NotFound.tsx'
|
20
|
+
import { Sidebar } from '../components/sidebar/Sidebar.tsx'
|
21
|
+
import { ThemeToggle } from '../components/ThemeToggle.tsx'
|
22
|
+
import { ThemeProvider, useTheme } from '../contexts/ThemeContext.tsx'
|
19
23
|
import entryClientUrl from '../entry.client.jsx?url'
|
20
|
-
import { changelog } from './changelog.
|
21
|
-
import { index } from './index.
|
22
|
-
import { reference } from './reference.
|
24
|
+
import { changelog } from './changelog.tsx'
|
25
|
+
import { index } from './index.tsx'
|
26
|
+
import { reference } from './reference.tsx'
|
23
27
|
|
24
28
|
// todo: not needed anymore because not using hono dev vite plugin right?
|
25
29
|
const reactRefreshPreamble = `
|
@@ -45,7 +49,9 @@ export const Component = () => {
|
|
45
49
|
{/* <meta name='theme-color' content='#000000' /> */}
|
46
50
|
</head>
|
47
51
|
<body style={{ margin: 0 }}>
|
48
|
-
<
|
52
|
+
<ThemeProvider>
|
53
|
+
<Layout />
|
54
|
+
</ThemeProvider>
|
49
55
|
<ScrollRestoration />
|
50
56
|
{import.meta.env.DEV && <script type='module' src={entryClientUrl}></script>}
|
51
57
|
</body>
|
@@ -55,6 +61,13 @@ export const Component = () => {
|
|
55
61
|
|
56
62
|
const Layout = () => {
|
57
63
|
const location = useLocation()
|
64
|
+
const { appearance } = useTheme()
|
65
|
+
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
|
66
|
+
|
67
|
+
// Close mobile menu on route change
|
68
|
+
useEffect(() => {
|
69
|
+
setMobileMenuOpen(false)
|
70
|
+
}, [location.pathname])
|
58
71
|
|
59
72
|
// Determine if we should show sidebar based on current path
|
60
73
|
const getCurrentNavPathExp = (): string | null => {
|
@@ -75,42 +88,75 @@ const Layout = () => {
|
|
75
88
|
<Flex
|
76
89
|
gridArea={'header'}
|
77
90
|
align='center'
|
78
|
-
gap='8'
|
91
|
+
gap={{ initial: '4', md: '8' }}
|
79
92
|
pb='4'
|
80
|
-
mb='8'
|
93
|
+
mb={{ initial: '4', md: '8' }}
|
81
94
|
style={{
|
82
95
|
borderBottom: `1px solid var(--gray-3)`,
|
83
96
|
}}
|
84
97
|
>
|
98
|
+
{/* Mobile menu - only show when sidebar exists */}
|
99
|
+
{isShowSidebar && (
|
100
|
+
<HamburgerMenu
|
101
|
+
isOpen={mobileMenuOpen}
|
102
|
+
onToggle={() => setMobileMenuOpen(!mobileMenuOpen)}
|
103
|
+
onClose={() => setMobileMenuOpen(false)}
|
104
|
+
sidebarData={sidebar.items}
|
105
|
+
/>
|
106
|
+
)}
|
107
|
+
|
85
108
|
<LinkReactRouter
|
86
109
|
to='/'
|
87
110
|
style={{ color: `inherit`, textDecoration: `none` }}
|
88
111
|
>
|
89
|
-
<
|
112
|
+
<Box display={{ initial: 'block', md: 'block' }}>
|
113
|
+
<Logo src={logoSrc} title={templateVariables.title} height={30} showTitle={true} />
|
114
|
+
</Box>
|
90
115
|
</LinkReactRouter>
|
91
|
-
<Flex direction='row' gap='4'>
|
116
|
+
<Flex direction='row' gap='4' style={{ flex: 1 }}>
|
92
117
|
{projectDataNavbar.map((item, key) => (
|
93
118
|
<Link key={key} color='gray' to={item.pathExp}>
|
94
119
|
{item.title}
|
95
120
|
</Link>
|
96
121
|
))}
|
97
122
|
</Flex>
|
123
|
+
<ThemeToggle />
|
98
124
|
</Flex>
|
99
125
|
)
|
100
126
|
|
101
127
|
return (
|
102
|
-
<Theme asChild>
|
128
|
+
<Theme asChild appearance={appearance}>
|
103
129
|
<Grid
|
104
|
-
width={{ initial: 'var(--container-4)' }}
|
105
|
-
|
130
|
+
width={{ initial: '100%', sm: '100%', md: 'var(--container-4)' }}
|
131
|
+
maxWidth='100vw'
|
132
|
+
areas={{
|
133
|
+
initial: "'header' 'content'",
|
134
|
+
sm: "'header' 'content'",
|
135
|
+
md:
|
136
|
+
"'header header header header header header header header' 'sidebar sidebar . content content content content content'",
|
137
|
+
}}
|
106
138
|
rows='min-content auto'
|
107
|
-
columns='repeat(8, 1fr)'
|
108
|
-
gapX='2'
|
109
|
-
my='8'
|
139
|
+
columns={{ initial: '1fr', sm: '1fr', md: 'repeat(8, 1fr)' }}
|
140
|
+
gapX={{ initial: '0', sm: '0', md: '2' }}
|
141
|
+
my={{ initial: '0', sm: '0', md: '8' }}
|
110
142
|
mx='auto'
|
143
|
+
px={{ initial: '4', sm: '4', md: '0' }}
|
144
|
+
py={{ initial: '4', sm: '4', md: '0' }}
|
111
145
|
>
|
112
146
|
<style>
|
113
147
|
{`
|
148
|
+
/* Responsive container fixes */
|
149
|
+
@media (max-width: 768px) {
|
150
|
+
body {
|
151
|
+
overflow-x: hidden;
|
152
|
+
}
|
153
|
+
}
|
154
|
+
|
155
|
+
/* Ensure proper centering on all screen sizes */
|
156
|
+
.rt-Grid {
|
157
|
+
box-sizing: border-box;
|
158
|
+
}
|
159
|
+
|
114
160
|
/* Shiki code blocks */
|
115
161
|
pre.shiki {
|
116
162
|
margin: 1rem 0;
|
@@ -144,15 +190,18 @@ const Layout = () => {
|
|
144
190
|
`}
|
145
191
|
</style>
|
146
192
|
{header}
|
193
|
+
|
194
|
+
{/* Desktop Sidebar */}
|
147
195
|
{isShowSidebar && (
|
148
|
-
<
|
196
|
+
<Box
|
197
|
+
display={{ initial: 'none', xs: 'none', sm: 'none', md: 'block' }}
|
149
198
|
gridColumn='1 / 3'
|
150
199
|
gridRow='2 / auto'
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
/>
|
200
|
+
>
|
201
|
+
<Sidebar data={sidebar.items} />
|
202
|
+
</Box>
|
155
203
|
)}
|
204
|
+
|
156
205
|
<Box gridArea='content / content / auto / 8'>
|
157
206
|
<Outlet />
|
158
207
|
</Box>
|
@@ -189,36 +238,10 @@ if (PROJECT_DATA.schema) {
|
|
189
238
|
//
|
190
239
|
//
|
191
240
|
|
192
|
-
const NotFoundComponent = () => {
|
193
|
-
return (
|
194
|
-
<Flex direction='column' align='center' gap='6' style={{ textAlign: `center`, paddingTop: `4rem` }}>
|
195
|
-
<Heading size='9' style={{ color: `var(--gray-12)` }}>404</Heading>
|
196
|
-
<Box>
|
197
|
-
<Heading size='5' mb='2'>Page Not Found</Heading>
|
198
|
-
<Text size='3' color='gray'>
|
199
|
-
The page you're looking for doesn't exist or has been moved.
|
200
|
-
</Text>
|
201
|
-
</Box>
|
202
|
-
<Flex gap='3'>
|
203
|
-
<LinkReactRouter to='/'>
|
204
|
-
<Button variant='soft' size='3'>
|
205
|
-
Go Home
|
206
|
-
</Button>
|
207
|
-
</LinkReactRouter>
|
208
|
-
<LinkReactRouter to='/reference'>
|
209
|
-
<Button variant='outline' size='3'>
|
210
|
-
View API Reference
|
211
|
-
</Button>
|
212
|
-
</LinkReactRouter>
|
213
|
-
</Flex>
|
214
|
-
</Flex>
|
215
|
-
)
|
216
|
-
}
|
217
|
-
|
218
241
|
const notFoundRoute = createRoute({
|
219
242
|
id: `*_not_found`,
|
220
243
|
path: `*`,
|
221
|
-
Component:
|
244
|
+
Component: NotFound,
|
222
245
|
handle: {
|
223
246
|
statusCode: 404,
|
224
247
|
},
|