1- import React from 'react' ;
2- import { NavLink } from 'react-router-dom' ;
1+ import React , { useState } from 'react' ;
2+ import { NavLink , useLocation } from 'react-router-dom' ;
33
4- const sections = [
4+ interface SidebarSection {
5+ title : string ;
6+ icon : React . ReactNode ;
7+ items : { label : string ; to : string } [ ] ;
8+ }
9+
10+ const sections : SidebarSection [ ] = [
511 {
612 title : 'Getting Started' ,
13+ icon : (
14+ < svg className = "w-4 h-4" fill = "none" stroke = "currentColor" viewBox = "0 0 24 24" >
15+ < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 1.5 } d = "M15.59 14.37a6 6 0 01-5.84 7.38v-4.8m5.84-2.58a14.98 14.98 0 006.16-12.12A14.98 14.98 0 009.63 8.41m5.96 5.96a14.926 14.926 0 01-5.84 2.58m0 0a14.926 14.926 0 01-5.84-2.58" />
16+ </ svg >
17+ ) ,
718 items : [
819 { label : 'Linux' , to : '/docs/installation/linux' } ,
920 { label : 'macOS' , to : '/docs/installation/macos' } ,
@@ -12,53 +23,150 @@ const sections = [
1223 } ,
1324 {
1425 title : 'Usage' ,
26+ icon : (
27+ < svg className = "w-4 h-4" fill = "none" stroke = "currentColor" viewBox = "0 0 24 24" >
28+ < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 1.5 } d = "M6.75 7.5l3 2.25-3 2.25m4.5 0h3m-9 8.25h13.5A2.25 2.25 0 0021 18V6a2.25 2.25 0 00-2.25-2.25H5.25A2.25 2.25 0 003 6v12a2.25 2.25 0 002.25 2.25z" />
29+ </ svg >
30+ ) ,
1531 items : [
1632 { label : 'CLI Commands' , to : '/docs/usage' } ,
33+ { label : 'Configuration' , to : '/docs/configuration' } ,
34+ ] ,
35+ } ,
36+ {
37+ title : 'Images' ,
38+ icon : (
39+ < svg className = "w-4 h-4" fill = "none" stroke = "currentColor" viewBox = "0 0 24 24" >
40+ < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 1.5 } d = "M21 7.5l-2.25-1.313M21 7.5v2.25m0-2.25l-2.25 1.313M3 7.5l2.25-1.313M3 7.5l2.25 1.313M3 7.5v2.25m9 3l2.25-1.313M12 12.75l-2.25-1.313M12 12.75V15m0 6.75l2.25-1.313M12 21.75V19.5m0 2.25l-2.25-1.313m0-16.875L12 2.25l2.25 1.313M21 14.25v2.25l-2.25 1.313m-13.5 0L3 16.5v-2.25" />
41+ </ svg >
42+ ) ,
43+ items : [
44+ { label : 'Available Images' , to : '/docs/images' } ,
45+ ] ,
46+ } ,
47+ {
48+ title : 'nihil-history' ,
49+ icon : (
50+ < svg className = "w-4 h-4" fill = "none" stroke = "currentColor" viewBox = "0 0 24 24" >
51+ < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 1.5 } d = "M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z" />
52+ </ svg >
53+ ) ,
54+ items : [
55+ { label : 'Overview & CLI' , to : '/docs/nihil-history' } ,
1756 ] ,
1857 } ,
1958 {
2059 title : 'Features' ,
60+ icon : (
61+ < svg className = "w-4 h-4" fill = "none" stroke = "currentColor" viewBox = "0 0 24 24" >
62+ < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 1.5 } d = "M9.813 15.904L9 18.75l-.813-2.846a4.5 4.5 0 00-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 003.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 003.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 00-3.09 3.09z" />
63+ </ svg >
64+ ) ,
2165 items : [
2266 { label : 'Shell Completion' , to : '/docs/completion' } ,
2367 { label : 'Command History' , to : '/docs/history' } ,
2468 ] ,
2569 } ,
70+ {
71+ title : 'Project' ,
72+ icon : (
73+ < svg className = "w-4 h-4" fill = "none" stroke = "currentColor" viewBox = "0 0 24 24" >
74+ < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 1.5 } d = "M2.25 12.75V12A2.25 2.25 0 014.5 9.75h15A2.25 2.25 0 0121.75 12v.75m-8.69-6.44l-2.12-2.12a1.5 1.5 0 00-1.061-.44H4.5A2.25 2.25 0 002.25 6v12a2.25 2.25 0 002.25 2.25h15A2.25 2.25 0 0021.75 18V9a2.25 2.25 0 00-2.25-2.25h-5.379a1.5 1.5 0 01-1.06-.44z" />
75+ </ svg >
76+ ) ,
77+ items : [
78+ { label : 'Architecture' , to : '/docs/architecture' } ,
79+ { label : 'Contributing' , to : '/docs/contributing' } ,
80+ ] ,
81+ } ,
2682 {
2783 title : 'More' ,
84+ icon : (
85+ < svg className = "w-4 h-4" fill = "none" stroke = "currentColor" viewBox = "0 0 24 24" >
86+ < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 1.5 } d = "M8.625 12a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0H8.25m4.125 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0H12m4.125 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0h-.375M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
87+ </ svg >
88+ ) ,
2889 items : [
2990 { label : 'FAQ' , to : '/docs/faq' } ,
3091 { label : 'About' , to : '/docs/about' } ,
3192 ] ,
3293 } ,
3394] ;
3495
96+ const SidebarSection : React . FC < { section : SidebarSection ; defaultOpen : boolean } > = ( { section, defaultOpen } ) => {
97+ const [ open , setOpen ] = useState ( defaultOpen ) ;
98+
99+ return (
100+ < div >
101+ < button
102+ onClick = { ( ) => setOpen ( ! open ) }
103+ className = "flex items-center gap-2 w-full px-3 py-2 rounded-lg text-left group transition-colors hover:bg-slate-800/40"
104+ >
105+ < span className = "text-slate-500 group-hover:text-amber-400 transition-colors" >
106+ { section . icon }
107+ </ span >
108+ < span className = "text-[12px] font-semibold uppercase tracking-wider text-slate-400 group-hover:text-slate-200 transition-colors flex-1" >
109+ { section . title }
110+ </ span >
111+ < svg
112+ className = {
113+ 'w-3.5 h-3.5 text-slate-600 transition-transform duration-200 ' +
114+ ( open ? 'rotate-90' : '' )
115+ }
116+ fill = "none"
117+ stroke = "currentColor"
118+ viewBox = "0 0 24 24"
119+ >
120+ < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 2 } d = "M9 5l7 7-7 7" />
121+ </ svg >
122+ </ button >
123+ < div
124+ className = {
125+ 'overflow-hidden transition-all duration-200 ' +
126+ ( open ? 'max-h-96 opacity-100 mt-1' : 'max-h-0 opacity-0' )
127+ }
128+ >
129+ < nav className = "space-y-0.5 ml-3 pl-3 border-l border-slate-800/80" >
130+ { section . items . map ( ( item ) => (
131+ < NavLink
132+ key = { item . to }
133+ to = { item . to }
134+ className = { ( { isActive } ) =>
135+ 'block px-3 py-1.5 rounded-md text-[13px] transition-all duration-150 ' +
136+ ( isActive
137+ ? 'bg-gradient-to-r from-amber-400/10 to-transparent text-amber-300 font-medium border-l-2 border-amber-400 -ml-[13px] pl-[23px]'
138+ : 'text-slate-500 hover:text-slate-200 hover:bg-slate-800/40' )
139+ }
140+ >
141+ { item . label }
142+ </ NavLink >
143+ ) ) }
144+ </ nav >
145+ </ div >
146+ </ div >
147+ ) ;
148+ } ;
149+
35150export const DocsLayout : React . FC < { children : React . ReactNode } > = ( { children } ) => {
151+ const location = useLocation ( ) ;
152+
153+ // Auto-open the section that contains the current page
154+ const isInSection = ( section : SidebarSection ) =>
155+ section . items . some ( ( item ) => location . pathname === item . to ) ;
156+
36157 return (
37- < div className = "flex gap-10 items-start" >
38- < aside className = "w-48 shrink-0 sticky top-24 hidden md:block" >
39- < div className = "space-y-6" >
158+ < div className = "flex gap-12 items-start" >
159+ < aside className = "w-60 shrink-0 sticky top-24 hidden md:block" >
160+ < div className = "space-y-1 p-2 rounded-xl bg-slate-900/30 border border-slate-800/50 backdrop-blur-sm" >
161+ < div className = "px-3 pt-1 pb-2 mb-1 border-b border-slate-800/50" >
162+ < p className = "text-[10px] font-bold uppercase tracking-[0.25em] text-slate-600" > Documentation</ p >
163+ </ div >
40164 { sections . map ( ( section ) => (
41- < div key = { section . title } >
42- < p className = "text-[11px] font-semibold uppercase tracking-widest text-slate-500 mb-2 px-3" >
43- { section . title }
44- </ p >
45- < nav className = "space-y-0.5" >
46- { section . items . map ( ( item ) => (
47- < NavLink
48- key = { item . to }
49- to = { item . to }
50- className = { ( { isActive } ) =>
51- 'block px-3 py-1.5 rounded-md text-sm transition-colors ' +
52- ( isActive
53- ? 'bg-amber-400/10 text-amber-300 font-medium'
54- : 'text-slate-400 hover:text-white hover:bg-slate-800/60' )
55- }
56- >
57- { item . label }
58- </ NavLink >
59- ) ) }
60- </ nav >
61- </ div >
165+ < SidebarSection
166+ key = { section . title }
167+ section = { section }
168+ defaultOpen = { isInSection ( section ) }
169+ />
62170 ) ) }
63171 </ div >
64172 </ aside >
0 commit comments