@@ -30,6 +30,31 @@ import usePersistentState from '../hooks/usePersistentState';
3030import { KEY_SIDEBAR_STATE } from '../utils/LocalStorageManager' ;
3131import { useAchievements } from '../context/AchievementContext' ;
3232import { useSiteConfig } from '../context/SiteConfigContext' ;
33+ import piml from 'piml' ;
34+
35+ const ICON_MAP = {
36+ HouseIcon,
37+ UserIcon,
38+ BookOpenIcon,
39+ WrenchIcon,
40+ ArticleIcon,
41+ SquaresFourIcon,
42+ GearSixIcon,
43+ MagnifyingGlassIcon,
44+ TimerIcon,
45+ PushPinIcon,
46+ TrophyIcon,
47+ ShuffleIcon,
48+ EnvelopeSimpleIcon,
49+ BugBeetleIcon,
50+ ArrowRightIcon,
51+ SwordIcon,
52+ RssIcon,
53+ GraphIcon,
54+ CaretDoubleDownIcon,
55+ CaretDoubleUpIcon,
56+ FlaskIcon,
57+ } ;
3358
3459const BrutalistSidebar = ( {
3560 isOpen,
@@ -38,6 +63,24 @@ const BrutalistSidebar = ({
3863 setIsPaletteOpen,
3964} ) => {
4065 const { config } = useSiteConfig ( ) ;
66+ const [ sidebarConfig , setSidebarConfig ] = useState ( null ) ;
67+
68+ useEffect ( ( ) => {
69+ const fetchSidebarConfig = async ( ) => {
70+ try {
71+ const response = await fetch ( '/sidebar.piml' ) ;
72+ if ( response . ok ) {
73+ const text = await response . text ( ) ;
74+ const parsed = piml . parse ( text ) ;
75+ setSidebarConfig ( parsed . sidebar ) ;
76+ }
77+ } catch ( error ) {
78+ console . error ( 'Failed to load sidebar config:' , error ) ;
79+ }
80+ } ;
81+ fetchSidebarConfig ( ) ;
82+ } , [ ] ) ;
83+
4184 const [ sidebarState , setSidebarState ] = usePersistentState (
4285 KEY_SIDEBAR_STATE ,
4386 {
@@ -179,182 +222,66 @@ const BrutalistSidebar = ({
179222 ref = { scrollRef }
180223 className = "h-full overflow-y-auto scrollbar-hide no-scrollbar"
181224 >
182- { /* Section: Main */ }
183- < SectionHeader
184- id = "isMainOpen"
185- label = "Main"
186- isOpen = { sidebarState . isMainOpen }
187- active = {
188- location . pathname === '/' ||
189- location . pathname === '/about' ||
190- location . pathname === '/achievements'
191- }
192- />
193- { sidebarState . isMainOpen && (
194- < nav className = "flex flex-col" >
195- < SidebarLink
196- to = "/"
197- icon = { HouseIcon }
198- label = "Home"
199- getLinkClass = { getLinkClass }
200- />
201- < SidebarLink
202- to = "/about"
203- icon = { UserIcon }
204- label = "About"
205- getLinkClass = { getLinkClass }
206- />
207- < SidebarLink
208- to = "/achievements"
209- icon = { TrophyIcon }
210- label = "Achievements"
211- getLinkClass = { getLinkClass }
212- />
213- </ nav >
214- ) }
225+ { Array . isArray ( sidebarConfig ) &&
226+ sidebarConfig . map ( ( section , sectionIdx ) => {
227+ const items = Array . isArray ( section . content )
228+ ? section . content
229+ : [ ] ;
230+ const isActive = items . some ( ( item ) =>
231+ item . to === '/'
232+ ? location . pathname === '/'
233+ : item . to && location . pathname . startsWith ( item . to ) ,
234+ ) ;
215235
216- { /* Section: Content */ }
217- < SectionHeader
218- id = "isContentOpen"
219- label = "Feed"
220- isOpen = { sidebarState . isContentOpen }
221- active = {
222- location . pathname . startsWith ( '/blog' ) ||
223- location . pathname . startsWith ( '/projects' ) ||
224- location . pathname . startsWith ( '/logs' )
225- }
226- />
227- { sidebarState . isContentOpen && (
228- < nav className = "flex flex-col" >
229- < SidebarLink
230- to = "/blog"
231- icon = { BookOpenIcon }
232- label = "Blogposts"
233- getLinkClass = { getLinkClass }
234- />
235- < SidebarLink
236- to = "/projects"
237- icon = { WrenchIcon }
238- label = "Projects"
239- getLinkClass = { getLinkClass }
240- />
241- < SidebarLink
242- to = "/logs"
243- icon = { ArticleIcon }
244- label = "Discovery Logs"
245- getLinkClass = { getLinkClass }
246- />
247- </ nav >
248- ) }
249-
250- { /* Section: Tools */ }
251- < SectionHeader
252- id = "isAppsOpen"
253- label = "Utilities"
254- isOpen = { sidebarState . isAppsOpen }
255- active = {
256- location . pathname . startsWith ( '/apps' ) ||
257- location . pathname . startsWith ( '/pinned-apps' ) ||
258- location . pathname . startsWith ( '/commands' )
259- }
260- />
261- { sidebarState . isAppsOpen && (
262- < nav className = "flex flex-col" >
263- < SidebarLink
264- to = "/pinned-apps"
265- icon = { PushPinIcon }
266- label = "Favorites"
267- getLinkClass = { getLinkClass }
268- />
269- < SidebarLink
270- to = "/apps"
271- icon = { SquaresFourIcon }
272- label = "App Center"
273- getLinkClass = { getLinkClass }
274- />
275- < SidebarLink
276- to = "/commands"
277- icon = { MagnifyingGlassIcon }
278- label = "Manuals"
279- getLinkClass = { getLinkClass }
280- />
281- </ nav >
282- ) }
283-
284- { /* Section: Status */ }
285- < SectionHeader
286- id = "isStatusOpen"
287- label = "System"
288- isOpen = { sidebarState . isStatusOpen }
289- active = {
290- location . pathname . startsWith ( '/roadmap' ) ||
291- location . pathname . startsWith ( '/timeline' ) ||
292- location . pathname . startsWith ( '/brufez' )
293- }
294- />
295- { sidebarState . isStatusOpen && (
296- < nav className = "flex flex-col" >
297- < SidebarLink
298- to = "/timeline"
299- icon = { TimerIcon }
300- label = "History"
301- getLinkClass = { getLinkClass }
302- />
303- < SidebarLink
304- to = "/roadmap"
305- icon = { BugBeetleIcon }
306- label = "Fezzilla"
307- getLinkClass = { getLinkClass }
308- />
309- < SidebarLink
310- to = "/brufez"
311- icon = { FlaskIcon }
312- label = "Brufez Spec"
313- getLinkClass = { getLinkClass }
314- />
315- </ nav >
316- ) }
317-
318- { /* Section: Extras */ }
319- < SectionHeader
320- id = "isExtrasOpen"
321- label = "External Nodes"
322- isOpen = { sidebarState . isExtrasOpen }
323- active = { location . pathname . startsWith ( '/stories' ) }
324- />
325- { sidebarState . isExtrasOpen && (
326- < nav className = "flex flex-col" >
327- < SidebarLink
328- to = "/graph"
329- icon = { GraphIcon }
330- label = "Neural Net"
331- getLinkClass = { getLinkClass }
332- />
333- < SidebarLink
334- to = "/stories"
335- icon = { SwordIcon }
336- label = "Serfs & Frauds"
337- getLinkClass = { getLinkClass }
338- />
339- < a
340- href = "/rss.xml"
341- target = "_blank"
342- rel = "noopener noreferrer"
343- className = "group flex items-center justify-between px-6 py-3 transition-all duration-300 border-b border-white/5 text-gray-300 hover:text-white hover:bg-white/5"
344- >
345- < div className = "flex items-center gap-4" >
346- < RssIcon size = { 18 } weight = "bold" />
347- < span className = "font-arvo text-sm font-medium uppercase tracking-widest" >
348- RSS_Feed
349- </ span >
350- </ div >
351- < ArrowRightIcon
352- size = { 14 }
353- className = "opacity-0 group-hover:opacity-100 -translate-x-2 group-hover:translate-x-0 transition-all"
354- />
355- </ a >
356- </ nav >
357- ) }
236+ return (
237+ < React . Fragment key = { section . id || sectionIdx } >
238+ < SectionHeader
239+ id = { section . id }
240+ label = { section . label }
241+ isOpen = { sidebarState [ section . id ] }
242+ active = { isActive }
243+ />
244+ { sidebarState [ section . id ] && (
245+ < nav className = "flex flex-col" >
246+ { items . map ( ( item , idx ) => {
247+ const Icon = ICON_MAP [ item . icon ] || ArrowRightIcon ;
248+ if ( item . external === 'true' || item . url ) {
249+ return (
250+ < a
251+ key = { idx }
252+ href = { item . url || item . to }
253+ target = "_blank"
254+ rel = "noopener noreferrer"
255+ className = "group flex items-center justify-between px-6 py-3 transition-all duration-300 border-b border-white/5 text-gray-300 hover:text-white hover:bg-white/5"
256+ >
257+ < div className = "flex items-center gap-4" >
258+ < Icon size = { 18 } weight = "bold" />
259+ < span className = "font-arvo text-sm font-medium uppercase tracking-widest" >
260+ { item . label }
261+ </ span >
262+ </ div >
263+ < ArrowRightIcon
264+ size = { 14 }
265+ className = "opacity-0 group-hover:opacity-100 -translate-x-2 group-hover:translate-x-0 transition-all"
266+ />
267+ </ a >
268+ ) ;
269+ }
270+ return (
271+ < SidebarLink
272+ key = { idx }
273+ to = { item . to }
274+ icon = { Icon }
275+ label = { item . label }
276+ getLinkClass = { getLinkClass }
277+ />
278+ ) ;
279+ } ) }
280+ </ nav >
281+ ) }
282+ </ React . Fragment >
283+ ) ;
284+ } ) }
358285 </ div >
359286
360287 { showScrollGradient . bottom && (
0 commit comments