polen 0.10.0-next.6 → 0.10.0-next.8
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.jsx +2 -2
- package/build/template/routes/root.d.ts.map +1 -1
- package/build/template/routes/root.jsx +40 -30
- 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/lib/version-history/index.test.ts +12 -4
- 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 +5 -5
- package/src/template/contexts/ThemeContext.tsx +4 -4
- package/src/template/routes/root.tsx +59 -42
@@ -59,7 +59,12 @@ export const filePathToRoute = (filePathExpression, rootDir) => {
|
|
59
59
|
};
|
60
60
|
};
|
61
61
|
export const filePathToRouteLogical = (filePath) => {
|
62
|
-
const
|
62
|
+
const dirSegments = Str.split(Str.removeSurrounding(filePath.dir, Path.sep), Path.sep);
|
63
|
+
// Parse numbered prefixes from directory segments
|
64
|
+
const dirPath = dirSegments.map(segment => {
|
65
|
+
const prefixMatch = Str.match(segment, conventions.numberedPrefix.pattern);
|
66
|
+
return prefixMatch?.groups.name ?? segment;
|
67
|
+
});
|
63
68
|
// Parse numbered prefix from filename
|
64
69
|
const prefixMatch = Str.match(filePath.name, conventions.numberedPrefix.pattern);
|
65
70
|
const order = prefixMatch ? parseInt(prefixMatch.groups.order, 10) : undefined;
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"scan.js","sourceRoot":"","sources":["../../../src/lib/file-router/scan.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAmB,IAAI,EAAE,MAAM,aAAa,CAAA;AACnD,OAAO,EAAiD,MAAM,YAAY,CAAA;AAE1E,EAAE;AACF,EAAE;AACF,EAAE;AACF,EAAE;AACF,yDAAyD;AACzD,EAAE;AACF,EAAE;AAEF,MAAM,WAAW,GAAG;IAClB,KAAK,EAAE;QACL,IAAI,EAAE,OAAO;KACd;IACD,cAAc,EAAE;QACd,OAAO,EAAE,GAAG,CAAC,OAAO,CAAgC,gCAAgC,CAAC;KACtF;CACF,CAAA;AAeD,EAAE;AACF,EAAE;AACF,EAAE;AACF,EAAE;AACF,oDAAoD;AACpD,EAAE;AACF,EAAE;AAEF,MAAM,CAAC,MAAM,IAAI,GAAG,KAAK,EAAE,UAG1B,EAAuB,EAAE;IACxB,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,eAAe,EAAE,GAAG,UAAU,CAAA;IAElD,yBAAyB;IACzB,MAAM,SAAS,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE;QAC5C,QAAQ,EAAE,IAAI;QACd,GAAG,EAAE,GAAG;QACR,SAAS,EAAE,IAAI;KAChB,CAAC,CAAA;IAEF,oBAAoB;IACpB,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,eAAe,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAA;IAExE,gBAAgB;IAChB,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,CAAA;IAE/B,OAAO,UAAU,CAAA;AACnB,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,kBAA0B,EAAE,OAAe,EAAS,EAAE;IACpF,MAAM,IAAI,GAAc;QACtB,IAAI,EAAE;YACJ,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC;YACxC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;SACjE;KACF,CAAA;IACD,MAAM,OAAO,GAAG,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;IAE1D,6CAA6C;IAC7C,MAAM,EAAE,GAAG,kBAAkB,CAAA,CAAC,iCAAiC;IAC/D,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAA;IAC/D,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAA;IAC5C,MAAM,QAAQ,GAAG,SAAS,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAA;IAEzE,OAAO;QACL,OAAO;QACP,IAAI;QACJ,EAAE;QACF,QAAQ;KACT,CAAA;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,QAAqB,EAAgB,EAAE;IAC5E,MAAM,
|
1
|
+
{"version":3,"file":"scan.js","sourceRoot":"","sources":["../../../src/lib/file-router/scan.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAmB,IAAI,EAAE,MAAM,aAAa,CAAA;AACnD,OAAO,EAAiD,MAAM,YAAY,CAAA;AAE1E,EAAE;AACF,EAAE;AACF,EAAE;AACF,EAAE;AACF,yDAAyD;AACzD,EAAE;AACF,EAAE;AAEF,MAAM,WAAW,GAAG;IAClB,KAAK,EAAE;QACL,IAAI,EAAE,OAAO;KACd;IACD,cAAc,EAAE;QACd,OAAO,EAAE,GAAG,CAAC,OAAO,CAAgC,gCAAgC,CAAC;KACtF;CACF,CAAA;AAeD,EAAE;AACF,EAAE;AACF,EAAE;AACF,EAAE;AACF,oDAAoD;AACpD,EAAE;AACF,EAAE;AAEF,MAAM,CAAC,MAAM,IAAI,GAAG,KAAK,EAAE,UAG1B,EAAuB,EAAE;IACxB,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,eAAe,EAAE,GAAG,UAAU,CAAA;IAElD,yBAAyB;IACzB,MAAM,SAAS,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE;QAC5C,QAAQ,EAAE,IAAI;QACd,GAAG,EAAE,GAAG;QACR,SAAS,EAAE,IAAI;KAChB,CAAC,CAAA;IAEF,oBAAoB;IACpB,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,eAAe,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAA;IAExE,gBAAgB;IAChB,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,CAAA;IAE/B,OAAO,UAAU,CAAA;AACnB,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,kBAA0B,EAAE,OAAe,EAAS,EAAE;IACpF,MAAM,IAAI,GAAc;QACtB,IAAI,EAAE;YACJ,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC;YACxC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;SACjE;KACF,CAAA;IACD,MAAM,OAAO,GAAG,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;IAE1D,6CAA6C;IAC7C,MAAM,EAAE,GAAG,kBAAkB,CAAA,CAAC,iCAAiC;IAC/D,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAA;IAC/D,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAA;IAC5C,MAAM,QAAQ,GAAG,SAAS,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAA;IAEzE,OAAO;QACL,OAAO;QACP,IAAI;QACJ,EAAE;QACF,QAAQ;KACT,CAAA;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,QAAqB,EAAgB,EAAE;IAC5E,MAAM,WAAW,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;IAEtF,kDAAkD;IAClD,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE;QACxC,MAAM,WAAW,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,WAAW,CAAC,cAAc,CAAC,OAAO,CAAC,CAAA;QAC1E,OAAO,WAAW,EAAE,MAAM,CAAC,IAAI,IAAI,OAAO,CAAA;IAC5C,CAAC,CAAC,CAAA;IAEF,sCAAsC;IACtC,MAAM,WAAW,GAAG,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,cAAc,CAAC,OAAO,CAAC,CAAA;IAChF,MAAM,KAAK,GAAG,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IAC9E,MAAM,iBAAiB,GAAG,WAAW,EAAE,MAAM,CAAC,IAAI,IAAI,QAAQ,CAAC,IAAI,CAAA;IAEnE,IAAI,iBAAiB,KAAK,WAAW,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACjD,MAAM,IAAI,GAAG,OAAO,CAAA;QACpB,OAAO;YACL,IAAI;YACJ,KAAK;SACN,CAAA;IACH,CAAC;IAED,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAA;IAC9C,OAAO;QACL,IAAI;QACJ,KAAK;KACN,CAAA;AACH,CAAC,CAAA"}
|
package/build/sandbox.js
CHANGED
@@ -1,3 +1,18 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
import { filePathToRouteLogical } from '#lib/file-router/scan';
|
2
|
+
import { Path } from '@wollybeard/kit';
|
3
|
+
// Test parsing of numbered directory
|
4
|
+
const testPath1 = Path.parse('a/10_b/index.md');
|
5
|
+
const logical1 = filePathToRouteLogical(testPath1);
|
6
|
+
console.log('Path 1:', testPath1);
|
7
|
+
console.log('Logical 1:', logical1);
|
8
|
+
// Test parsing of numbered file
|
9
|
+
const testPath2 = Path.parse('a/10_b/g.md');
|
10
|
+
const logical2 = filePathToRouteLogical(testPath2);
|
11
|
+
console.log('\nPath 2:', testPath2);
|
12
|
+
console.log('Logical 2:', logical2);
|
13
|
+
// Test directory structure
|
14
|
+
const testPath3 = Path.parse('a/30_d/index.md');
|
15
|
+
const logical3 = filePathToRouteLogical(testPath3);
|
16
|
+
console.log('\nPath 3:', testPath3);
|
17
|
+
console.log('Logical 3:', logical3);
|
3
18
|
//# sourceMappingURL=sandbox.js.map
|
package/build/sandbox.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"
|
1
|
+
{"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAC9D,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAEtC,qCAAqC;AACrC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAA;AAC/C,MAAM,QAAQ,GAAG,sBAAsB,CAAC,SAAS,CAAC,CAAA;AAClD,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,CAAA;AACjC,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAA;AAEnC,gCAAgC;AAChC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAA;AAC3C,MAAM,QAAQ,GAAG,sBAAsB,CAAC,SAAS,CAAC,CAAA;AAClD,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,SAAS,CAAC,CAAA;AACnC,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAA;AAEnC,2BAA2B;AAC3B,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAA;AAC/C,MAAM,QAAQ,GAAG,sBAAsB,CAAC,SAAS,CAAC,CAAA;AAClD,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,SAAS,CAAC,CAAA;AACnC,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAA"}
|
@@ -0,0 +1,9 @@
|
|
1
|
+
import type { Content } from '#api/content/$';
|
2
|
+
export interface HamburgerMenuProps {
|
3
|
+
isOpen: boolean;
|
4
|
+
onToggle: () => void;
|
5
|
+
onClose: () => void;
|
6
|
+
sidebarData: Content.Item[];
|
7
|
+
}
|
8
|
+
export declare const HamburgerMenu: React.FC<HamburgerMenuProps>;
|
9
|
+
//# sourceMappingURL=HamburgerMenu.d.ts.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"HamburgerMenu.d.ts","sourceRoot":"","sources":["../../../src/template/components/HamburgerMenu.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AAM7C,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,OAAO,CAAA;IACf,QAAQ,EAAE,MAAM,IAAI,CAAA;IACpB,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,WAAW,EAAE,OAAO,CAAC,IAAI,EAAE,CAAA;CAC5B;AAED,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAkFtD,CAAA"}
|
@@ -0,0 +1,53 @@
|
|
1
|
+
import { Cross2Icon, HamburgerMenuIcon } from '@radix-ui/react-icons';
|
2
|
+
import { Box, Flex, IconButton, Text } from '@radix-ui/themes';
|
3
|
+
import { useEffect } from 'react';
|
4
|
+
import { Sidebar } from "../components/sidebar/Sidebar.jsx";
|
5
|
+
export const HamburgerMenu = ({ isOpen, onToggle, onClose, sidebarData, }) => {
|
6
|
+
// Prevent body scroll when mobile menu is open
|
7
|
+
useEffect(() => {
|
8
|
+
if (isOpen) {
|
9
|
+
document.body.style.overflow = 'hidden';
|
10
|
+
}
|
11
|
+
else {
|
12
|
+
document.body.style.overflow = '';
|
13
|
+
}
|
14
|
+
// Cleanup
|
15
|
+
return () => {
|
16
|
+
document.body.style.overflow = '';
|
17
|
+
};
|
18
|
+
}, [isOpen]);
|
19
|
+
return (<>
|
20
|
+
{/* Mobile menu button - show on mobile/tablet, hide on desktop */}
|
21
|
+
<Box display={{ initial: 'block', xs: 'block', sm: 'block', md: 'none', lg: 'none', xl: 'none' }}>
|
22
|
+
<IconButton size='2' variant='ghost' onClick={onToggle} aria-label='Toggle navigation menu'>
|
23
|
+
{isOpen ? <Cross2Icon width='18' height='18'/> : <HamburgerMenuIcon width='18' height='18'/>}
|
24
|
+
</IconButton>
|
25
|
+
</Box>
|
26
|
+
|
27
|
+
{/* Mobile Sidebar Drawer */}
|
28
|
+
{isOpen && (<>
|
29
|
+
{/* Backdrop */}
|
30
|
+
<Box position='fixed' inset='0' style={{
|
31
|
+
backgroundColor: 'var(--black-a9)',
|
32
|
+
zIndex: 50,
|
33
|
+
}} onClick={onClose} display={{ initial: 'block', xs: 'block', sm: 'block', md: 'none', lg: 'none', xl: 'none' }}/>
|
34
|
+
|
35
|
+
{/* Drawer */}
|
36
|
+
<Box position='fixed' top='0' left='0' bottom='0' width='280px' style={{
|
37
|
+
backgroundColor: 'var(--color-background)',
|
38
|
+
boxShadow: 'var(--shadow-6)',
|
39
|
+
zIndex: 100,
|
40
|
+
overflowY: 'auto',
|
41
|
+
}} p='4' display={{ initial: 'block', xs: 'block', sm: 'block', md: 'none', lg: 'none', xl: 'none' }}>
|
42
|
+
<Flex justify='between' align='center' mb='4'>
|
43
|
+
<Text size='5' weight='bold'>Navigation</Text>
|
44
|
+
<IconButton size='2' variant='ghost' onClick={onClose} aria-label='Close navigation menu'>
|
45
|
+
<Cross2Icon width='18' height='18'/>
|
46
|
+
</IconButton>
|
47
|
+
</Flex>
|
48
|
+
<Sidebar data={sidebarData}/>
|
49
|
+
</Box>
|
50
|
+
</>)}
|
51
|
+
</>);
|
52
|
+
};
|
53
|
+
//# sourceMappingURL=HamburgerMenu.jsx.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"HamburgerMenu.jsx","sourceRoot":"","sources":["../../../src/template/components/HamburgerMenu.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AACrE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,mCAAmC,CAAA;AAS3D,MAAM,CAAC,MAAM,aAAa,GAAiC,CAAC,EAC1D,MAAM,EACN,QAAQ,EACR,OAAO,EACP,WAAW,GACZ,EAAE,EAAE;IACH,+CAA+C;IAC/C,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,MAAM,EAAE,CAAC;YACX,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,QAAQ,CAAA;QACzC,CAAC;aAAM,CAAC;YACN,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,EAAE,CAAA;QACnC,CAAC;QAED,UAAU;QACV,OAAO,GAAG,EAAE;YACV,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,EAAE,CAAA;QACnC,CAAC,CAAA;IACH,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAA;IAEZ,OAAO,CACL,EACE;MAAA,CAAC,iEAAiE,CAClE;MAAA,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAC/F;QAAA,CAAC,UAAU,CACT,IAAI,CAAC,GAAG,CACR,OAAO,CAAC,OAAO,CACf,OAAO,CAAC,CAAC,QAAQ,CAAC,CAClB,UAAU,CAAC,wBAAwB,CAEnC;UAAA,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAG,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAG,CAChG;QAAA,EAAE,UAAU,CACd;MAAA,EAAE,GAAG,CAEL;;MAAA,CAAC,2BAA2B,CAC5B;MAAA,CAAC,MAAM,IAAI,CACT,EACE;UAAA,CAAC,cAAc,CACf;UAAA,CAAC,GAAG,CACF,QAAQ,CAAC,OAAO,CAChB,KAAK,CAAC,GAAG,CACT,KAAK,CAAC,CAAC;gBACL,eAAe,EAAE,iBAAiB;gBAClC,MAAM,EAAE,EAAE;aACX,CAAC,CACF,OAAO,CAAC,CAAC,OAAO,CAAC,CACjB,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,EAG9F;;UAAA,CAAC,YAAY,CACb;UAAA,CAAC,GAAG,CACF,QAAQ,CAAC,OAAO,CAChB,GAAG,CAAC,GAAG,CACP,IAAI,CAAC,GAAG,CACR,MAAM,CAAC,GAAG,CACV,KAAK,CAAC,OAAO,CACb,KAAK,CAAC,CAAC;gBACL,eAAe,EAAE,yBAAyB;gBAC1C,SAAS,EAAE,iBAAiB;gBAC5B,MAAM,EAAE,GAAG;gBACX,SAAS,EAAE,MAAM;aAClB,CAAC,CACF,CAAC,CAAC,GAAG,CACL,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAE5F;YAAA,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,CAC3C;cAAA,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,IAAI,CAC7C;cAAA,CAAC,UAAU,CACT,IAAI,CAAC,GAAG,CACR,OAAO,CAAC,OAAO,CACf,OAAO,CAAC,CAAC,OAAO,CAAC,CACjB,UAAU,CAAC,uBAAuB,CAElC;gBAAA,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EACpC;cAAA,EAAE,UAAU,CACd;YAAA,EAAE,IAAI,CACN;YAAA,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC,EAC7B;UAAA,EAAE,GAAG,CACP;QAAA,GAAG,CACJ,CACH;IAAA,GAAG,CACJ,CAAA;AACH,CAAC,CAAA"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"NotFound.d.ts","sourceRoot":"","sources":["../../../src/template/components/NotFound.tsx"],"names":[],"mappings":"AAGA,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAwB5B,CAAA"}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import { Box, Button, Flex, Heading, Text } from '@radix-ui/themes';
|
2
|
+
import { Link as LinkReactRouter } from 'react-router';
|
3
|
+
export const NotFound = () => {
|
4
|
+
return (<Flex direction='column' align='center' gap='6' style={{ textAlign: `center`, paddingTop: `4rem` }}>
|
5
|
+
<Heading size='9' style={{ color: `var(--gray-12)` }}>404</Heading>
|
6
|
+
<Box>
|
7
|
+
<Heading size='5' mb='2'>Page Not Found</Heading>
|
8
|
+
<Text size='3' color='gray'>
|
9
|
+
The page you're looking for doesn't exist or has been moved.
|
10
|
+
</Text>
|
11
|
+
</Box>
|
12
|
+
<Flex gap='3'>
|
13
|
+
<LinkReactRouter to='/'>
|
14
|
+
<Button variant='soft' size='3'>
|
15
|
+
Go Home
|
16
|
+
</Button>
|
17
|
+
</LinkReactRouter>
|
18
|
+
<LinkReactRouter to='/reference'>
|
19
|
+
<Button variant='outline' size='3'>
|
20
|
+
View API Reference
|
21
|
+
</Button>
|
22
|
+
</LinkReactRouter>
|
23
|
+
</Flex>
|
24
|
+
</Flex>);
|
25
|
+
};
|
26
|
+
//# sourceMappingURL=NotFound.jsx.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"NotFound.jsx","sourceRoot":"","sources":["../../../src/template/components/NotFound.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AACnE,OAAO,EAAE,IAAI,IAAI,eAAe,EAAE,MAAM,cAAc,CAAA;AAEtD,MAAM,CAAC,MAAM,QAAQ,GAAa,GAAG,EAAE;IACrC,OAAO,CACL,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,CACjG;MAAA,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,CAAC,GAAG,EAAE,OAAO,CAClE;MAAA,CAAC,GAAG,CACF;QAAA,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,cAAc,EAAE,OAAO,CAChD;QAAA,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CACzB;;QACF,EAAE,IAAI,CACR;MAAA,EAAE,GAAG,CACL;MAAA,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CACX;QAAA,CAAC,eAAe,CAAC,EAAE,CAAC,GAAG,CACrB;UAAA,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAC7B;;UACF,EAAE,MAAM,CACV;QAAA,EAAE,eAAe,CACjB;QAAA,CAAC,eAAe,CAAC,EAAE,CAAC,YAAY,CAC9B;UAAA,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAChC;;UACF,EAAE,MAAM,CACV;QAAA,EAAE,eAAe,CACnB;MAAA,EAAE,IAAI,CACR;IAAA,EAAE,IAAI,CAAC,CACR,CAAA;AACH,CAAC,CAAA"}
|
@@ -3,8 +3,8 @@ import { IconButton } from '@radix-ui/themes';
|
|
3
3
|
import { useTheme } from "../contexts/ThemeContext.jsx";
|
4
4
|
export const ThemeToggle = () => {
|
5
5
|
const { appearance, toggleTheme } = useTheme();
|
6
|
-
return (<IconButton size=
|
7
|
-
{appearance === 'light' ? <MoonIcon width=
|
6
|
+
return (<IconButton size='2' variant='ghost' color='gray' onClick={toggleTheme} aria-label={`Switch to ${appearance === 'light' ? 'dark' : 'light'} theme`} style={{ cursor: 'pointer' }}>
|
7
|
+
{appearance === 'light' ? <MoonIcon width='18' height='18'/> : <SunIcon width='18' height='18'/>}
|
8
8
|
</IconButton>);
|
9
9
|
};
|
10
10
|
//# sourceMappingURL=ThemeToggle.jsx.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"root.d.ts","sourceRoot":"","sources":["../../../src/template/routes/root.tsx"],"names":[],"mappings":"
|
1
|
+
{"version":3,"file":"root.d.ts","sourceRoot":"","sources":["../../../src/template/routes/root.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAoC1D,eAAO,MAAM,SAAS,mCAuBrB,CAAA;AAsMD,eAAO,MAAM,IAAI;;;;CAIf,CAAA"}
|
@@ -1,9 +1,9 @@
|
|
1
|
-
import { assetUrl } from '#api/utils/asset-url/index';
|
2
1
|
import { createRoute } from '#lib/react-router-aid/react-router-aid';
|
3
|
-
import { Box,
|
2
|
+
import { Box, Grid } from '@radix-ui/themes';
|
4
3
|
import { Flex, Theme } from '@radix-ui/themes';
|
5
4
|
import radixStylesUrl from '@radix-ui/themes/styles.css?url';
|
6
5
|
import { Arr } from '@wollybeard/kit';
|
6
|
+
import { useEffect, useState } from 'react';
|
7
7
|
import { Link as LinkReactRouter } from 'react-router';
|
8
8
|
import { Outlet, ScrollRestoration, useLocation } from 'react-router';
|
9
9
|
import logoSrc from 'virtual:polen/project/assets/logo.svg';
|
@@ -12,8 +12,10 @@ import projectDataNavbar from 'virtual:polen/project/data/navbar.jsonsuper';
|
|
12
12
|
import projectDataPages from 'virtual:polen/project/data/pages.jsonsuper';
|
13
13
|
import { pages } from 'virtual:polen/project/pages.jsx';
|
14
14
|
import { templateVariables } from 'virtual:polen/template/variables';
|
15
|
+
import { HamburgerMenu } from "../components/HamburgerMenu.jsx";
|
15
16
|
import { Link } from "../components/Link.jsx";
|
16
17
|
import { Logo } from "../components/Logo.jsx";
|
18
|
+
import { NotFound } from "../components/NotFound.jsx";
|
17
19
|
import { Sidebar } from "../components/sidebar/Sidebar.jsx";
|
18
20
|
import { ThemeToggle } from "../components/ThemeToggle.jsx";
|
19
21
|
import { ThemeProvider, useTheme } from "../contexts/ThemeContext.jsx";
|
@@ -54,6 +56,11 @@ export const Component = () => {
|
|
54
56
|
const Layout = () => {
|
55
57
|
const location = useLocation();
|
56
58
|
const { appearance } = useTheme();
|
59
|
+
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
60
|
+
// Close mobile menu on route change
|
61
|
+
useEffect(() => {
|
62
|
+
setMobileMenuOpen(false);
|
63
|
+
}, [location.pathname]);
|
57
64
|
// Determine if we should show sidebar based on current path
|
58
65
|
const getCurrentNavPathExp = () => {
|
59
66
|
// todo: general path manipulation lib because we are duplicating logic here found in FileRouter
|
@@ -67,11 +74,16 @@ const Layout = () => {
|
|
67
74
|
const currentNavPathExp = getCurrentNavPathExp();
|
68
75
|
const sidebar = currentNavPathExp && projectDataPages.sidebarIndex[currentNavPathExp];
|
69
76
|
const isShowSidebar = sidebar && sidebar.items.length > 0;
|
70
|
-
const header = (<Flex gridArea={'header'} align='center' gap='8' pb='4' mb='8' style={{
|
77
|
+
const header = (<Flex gridArea={'header'} align='center' gap={{ initial: '4', md: '8' }} pb='4' mb={{ initial: '4', md: '8' }} style={{
|
71
78
|
borderBottom: `1px solid var(--gray-3)`,
|
72
79
|
}}>
|
80
|
+
{/* Mobile menu - only show when sidebar exists */}
|
81
|
+
{isShowSidebar && (<HamburgerMenu isOpen={mobileMenuOpen} onToggle={() => setMobileMenuOpen(!mobileMenuOpen)} onClose={() => setMobileMenuOpen(false)} sidebarData={sidebar.items}/>)}
|
82
|
+
|
73
83
|
<LinkReactRouter to='/' style={{ color: `inherit`, textDecoration: `none` }}>
|
74
|
-
<
|
84
|
+
<Box display={{ initial: 'block', md: 'block' }}>
|
85
|
+
<Logo src={logoSrc} title={templateVariables.title} height={30} showTitle={true}/>
|
86
|
+
</Box>
|
75
87
|
</LinkReactRouter>
|
76
88
|
<Flex direction='row' gap='4' style={{ flex: 1 }}>
|
77
89
|
{projectDataNavbar.map((item, key) => (<Link key={key} color='gray' to={item.pathExp}>
|
@@ -81,9 +93,25 @@ const Layout = () => {
|
|
81
93
|
<ThemeToggle />
|
82
94
|
</Flex>);
|
83
95
|
return (<Theme asChild appearance={appearance}>
|
84
|
-
<Grid width={{ initial: '
|
96
|
+
<Grid width={{ initial: '100%', sm: '100%', md: 'var(--container-4)' }} maxWidth='100vw' areas={{
|
97
|
+
initial: "'header' 'content'",
|
98
|
+
sm: "'header' 'content'",
|
99
|
+
md: "'header header header header header header header header' 'sidebar sidebar . content content content content content'",
|
100
|
+
}} rows='min-content auto' columns={{ initial: '1fr', sm: '1fr', md: 'repeat(8, 1fr)' }} gapX={{ initial: '0', sm: '0', md: '2' }} my={{ initial: '0', sm: '0', md: '8' }} mx='auto' px={{ initial: '4', sm: '4', md: '0' }} py={{ initial: '4', sm: '4', md: '0' }}>
|
85
101
|
<style>
|
86
102
|
{`
|
103
|
+
/* Responsive container fixes */
|
104
|
+
@media (max-width: 768px) {
|
105
|
+
body {
|
106
|
+
overflow-x: hidden;
|
107
|
+
}
|
108
|
+
}
|
109
|
+
|
110
|
+
/* Ensure proper centering on all screen sizes */
|
111
|
+
.rt-Grid {
|
112
|
+
box-sizing: border-box;
|
113
|
+
}
|
114
|
+
|
87
115
|
/* Shiki code blocks */
|
88
116
|
pre.shiki {
|
89
117
|
margin: 1rem 0;
|
@@ -117,7 +145,12 @@ const Layout = () => {
|
|
117
145
|
`}
|
118
146
|
</style>
|
119
147
|
{header}
|
120
|
-
|
148
|
+
|
149
|
+
{/* Desktop Sidebar */}
|
150
|
+
{isShowSidebar && (<Box display={{ initial: 'none', xs: 'none', sm: 'none', md: 'block' }} gridColumn='1 / 3' gridRow='2 / auto'>
|
151
|
+
<Sidebar data={sidebar.items}/>
|
152
|
+
</Box>)}
|
153
|
+
|
121
154
|
<Box gridArea='content / content / auto / 8'>
|
122
155
|
<Outlet />
|
123
156
|
</Box>
|
@@ -148,33 +181,10 @@ if (PROJECT_DATA.schema) {
|
|
148
181
|
//
|
149
182
|
//
|
150
183
|
//
|
151
|
-
const NotFoundComponent = () => {
|
152
|
-
return (<Flex direction='column' align='center' gap='6' style={{ textAlign: `center`, paddingTop: `4rem` }}>
|
153
|
-
<Heading size='9' style={{ color: `var(--gray-12)` }}>404</Heading>
|
154
|
-
<Box>
|
155
|
-
<Heading size='5' mb='2'>Page Not Found</Heading>
|
156
|
-
<Text size='3' color='gray'>
|
157
|
-
The page you're looking for doesn't exist or has been moved.
|
158
|
-
</Text>
|
159
|
-
</Box>
|
160
|
-
<Flex gap='3'>
|
161
|
-
<LinkReactRouter to='/'>
|
162
|
-
<Button variant='soft' size='3'>
|
163
|
-
Go Home
|
164
|
-
</Button>
|
165
|
-
</LinkReactRouter>
|
166
|
-
<LinkReactRouter to='/reference'>
|
167
|
-
<Button variant='outline' size='3'>
|
168
|
-
View API Reference
|
169
|
-
</Button>
|
170
|
-
</LinkReactRouter>
|
171
|
-
</Flex>
|
172
|
-
</Flex>);
|
173
|
-
};
|
174
184
|
const notFoundRoute = createRoute({
|
175
185
|
id: `*_not_found`,
|
176
186
|
path: `*`,
|
177
|
-
Component:
|
187
|
+
Component: NotFound,
|
178
188
|
handle: {
|
179
189
|
statusCode: 404,
|
180
190
|
},
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"root.jsx","sourceRoot":"","sources":["../../../src/template/routes/root.tsx"],"names":[],"mappings":"
|
1
|
+
{"version":3,"file":"root.jsx","sourceRoot":"","sources":["../../../src/template/routes/root.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/api/content/$$.ts
CHANGED
@@ -0,0 +1,55 @@
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
2
|
+
import { createNavbar } from './navbar.ts'
|
3
|
+
import type { Page } from './page.ts'
|
4
|
+
|
5
|
+
const createPage = (path: string[], fileName = 'index', hidden = false): Page => ({
|
6
|
+
route: {
|
7
|
+
id: path.join('/'),
|
8
|
+
parentId: path.length > 1 ? path.slice(0, -1).join('/') : null,
|
9
|
+
logical: { path },
|
10
|
+
file: {
|
11
|
+
path: {
|
12
|
+
absolute: { root: '/', dir: `/pages/${path.join('/')}`, base: `${fileName}.md`, ext: '.md', name: fileName },
|
13
|
+
relative: { root: '', dir: path.join('/'), base: `${fileName}.md`, ext: '.md', name: fileName },
|
14
|
+
},
|
15
|
+
},
|
16
|
+
},
|
17
|
+
metadata: { description: undefined, hidden },
|
18
|
+
})
|
19
|
+
|
20
|
+
describe('createNavbar', () => {
|
21
|
+
it('returns empty for empty input', () => {
|
22
|
+
expect(createNavbar([])).toEqual([])
|
23
|
+
})
|
24
|
+
|
25
|
+
it('excludes hidden pages', () => {
|
26
|
+
const pages = [createPage(['visible'], 'index', false), createPage(['hidden'], 'index', true)]
|
27
|
+
const result = createNavbar(pages)
|
28
|
+
expect(result).toEqual([{ pathExp: '/visible', title: 'Visible' }])
|
29
|
+
})
|
30
|
+
|
31
|
+
it('includes directories with index pages', () => {
|
32
|
+
const pages = [createPage(['guide'], 'index'), createPage(['guide', 'intro'], 'intro')]
|
33
|
+
const result = createNavbar(pages)
|
34
|
+
expect(result).toEqual([{ pathExp: '/guide', title: 'Guide' }])
|
35
|
+
})
|
36
|
+
|
37
|
+
it('excludes directories without index pages', () => {
|
38
|
+
const pages = [createPage(['guide', 'intro'], 'intro')]
|
39
|
+
const result = createNavbar(pages)
|
40
|
+
expect(result).toEqual([])
|
41
|
+
})
|
42
|
+
|
43
|
+
it('includes single top-level pages', () => {
|
44
|
+
const pages = [createPage(['about'], 'about')]
|
45
|
+
const result = createNavbar(pages)
|
46
|
+
expect(result).toEqual([{ pathExp: '/about', title: 'About' }])
|
47
|
+
})
|
48
|
+
|
49
|
+
it('sorts results alphabetically', () => {
|
50
|
+
const pages = [createPage(['zebra'], 'zebra'), createPage(['alpha'], 'alpha')]
|
51
|
+
const result = createNavbar(pages)
|
52
|
+
expect(result[0]!.title).toBe('Alpha')
|
53
|
+
expect(result[1]!.title).toBe('Zebra')
|
54
|
+
})
|
55
|
+
})
|
@@ -0,0 +1,61 @@
|
|
1
|
+
import { Str } from '@wollybeard/kit'
|
2
|
+
import type { Page } from './page.ts'
|
3
|
+
|
4
|
+
export interface NavbarItem {
|
5
|
+
pathExp: string
|
6
|
+
title: string
|
7
|
+
}
|
8
|
+
|
9
|
+
/**
|
10
|
+
* Generate navbar items from a list of pages
|
11
|
+
*/
|
12
|
+
export const createNavbar = (pages: Page[]): NavbarItem[] => {
|
13
|
+
const navbarItems: NavbarItem[] = []
|
14
|
+
|
15
|
+
// Group pages by their top-level directory/segment
|
16
|
+
const topLevelGroups = new Map<string, Page[]>()
|
17
|
+
|
18
|
+
pages.forEach(page => {
|
19
|
+
// Skip hidden pages
|
20
|
+
if (page.metadata.hidden) return
|
21
|
+
|
22
|
+
const firstSegment = page.route.logical.path[0]
|
23
|
+
if (firstSegment) {
|
24
|
+
if (!topLevelGroups.has(firstSegment)) {
|
25
|
+
topLevelGroups.set(firstSegment, [])
|
26
|
+
}
|
27
|
+
topLevelGroups.get(firstSegment)!.push(page)
|
28
|
+
}
|
29
|
+
})
|
30
|
+
|
31
|
+
// Add each top-level group to navbar
|
32
|
+
topLevelGroups.forEach((pages, segment) => {
|
33
|
+
// For directories, check if there's an index page
|
34
|
+
const indexPage = pages.find(p =>
|
35
|
+
p.route.logical.path.length === 1
|
36
|
+
&& p.route.file.path.relative.name === 'index'
|
37
|
+
)
|
38
|
+
|
39
|
+
// For single non-index files at top level
|
40
|
+
const singlePage = pages.length === 1 && pages[0]!.route.logical.path.length === 1
|
41
|
+
&& pages[0]!.route.file.path.relative.name !== 'index'
|
42
|
+
|
43
|
+
// Include in navbar if:
|
44
|
+
// 1. It's a directory with an index page, OR
|
45
|
+
// 2. It's a single top-level page (not index)
|
46
|
+
if (indexPage || singlePage) {
|
47
|
+
const title = Str.titlizeSlug(segment)
|
48
|
+
const pathExp = `/${segment}`
|
49
|
+
|
50
|
+
navbarItems.push({
|
51
|
+
pathExp,
|
52
|
+
title,
|
53
|
+
})
|
54
|
+
}
|
55
|
+
})
|
56
|
+
|
57
|
+
// Sort navbar items alphabetically for consistency
|
58
|
+
navbarItems.sort((a, b) => a.title.localeCompare(b.title))
|
59
|
+
|
60
|
+
return navbarItems
|
61
|
+
}
|