@@ -22,6 +22,13 @@ const state = {
2222 selectedWiki : "" ,
2323} ;
2424
25+ const FOLDER_TITLE_WORDS = new Map ( [
26+ [ "ai" , "AI" ] ,
27+ [ "rls" , "RLS" ] ,
28+ ] ) ;
29+
30+ const FOLDER_SORT_PRIORITY = new Map ( [ [ "start-here" , - 1 ] ] ) ;
31+
2532export function startViewer ( ) {
2633 const elements = readElements ( ) ;
2734 elements . searchForm . addEventListener ( "submit" , ( event ) => {
@@ -126,16 +133,117 @@ function renderNav(elements, overview) {
126133 ) ,
127134 ) ;
128135 elements . pageList . replaceChildren (
129- ...overview . pages . map ( ( page ) =>
130- navLink ( pageHref ( page . slug ) , page . title || page . slug , {
131- kind : RouteKind . PAGE ,
132- value : page . slug ,
133- title : page . summary || `${ page . slug } .md` ,
134- } ) ,
135- ) ,
136+ ...pageTree ( overview . navigation_pages || overview . pages ) ,
136137 ) ;
137138}
138139
140+ function pageTree ( pages ) {
141+ const root = createFolderNode ( "" ) ;
142+ for ( const page of pages ) {
143+ insertPage ( root , page ) ;
144+ }
145+ return renderFolderChildren ( root , 0 ) ;
146+ }
147+
148+ function createFolderNode ( name ) {
149+ return {
150+ name,
151+ folders : new Map ( ) ,
152+ pages : [ ] ,
153+ } ;
154+ }
155+
156+ function insertPage ( root , page ) {
157+ const path = page . path || `${ page . slug } .md` ;
158+ const parts = path . split ( "/" ) . filter ( Boolean ) ;
159+ const filename = parts . pop ( ) || `${ page . slug } .md` ;
160+ let cursor = root ;
161+ for ( const part of parts ) {
162+ if ( ! cursor . folders . has ( part ) ) {
163+ cursor . folders . set ( part , createFolderNode ( part ) ) ;
164+ }
165+ cursor = cursor . folders . get ( part ) ;
166+ }
167+ cursor . pages . push ( { ...page , filename } ) ;
168+ }
169+
170+ function renderFolderChildren ( folder , depth ) {
171+ const children = [ ] ;
172+ for ( const child of sortedFolders ( folder ) ) {
173+ children . push ( renderFolder ( child , depth ) ) ;
174+ }
175+ for ( const page of sortedPages ( folder . pages ) ) {
176+ children . push ( renderPageLink ( page , depth ) ) ;
177+ }
178+ return children ;
179+ }
180+
181+ function renderFolder ( folder , depth ) {
182+ const details = document . createElement ( "details" ) ;
183+ details . className = "wiki-rail-folder" ;
184+
185+ const summary = document . createElement ( "summary" ) ;
186+ summary . className = "wiki-rail-folder-summary" ;
187+ setRailIndent ( summary , depth ) ;
188+ summary . textContent = folderTitle ( folder . name ) ;
189+
190+ const children = document . createElement ( "div" ) ;
191+ children . className = "wiki-rail-folder-children" ;
192+ children . append ( ...renderFolderChildren ( folder , depth + 1 ) ) ;
193+
194+ details . append ( summary , children ) ;
195+ return details ;
196+ }
197+
198+ function renderPageLink ( page , depth ) {
199+ const link = navLink ( pageHref ( page . slug ) , page . title || page . slug , {
200+ kind : RouteKind . PAGE ,
201+ value : page . slug ,
202+ title : page . summary || page . path || `${ page . slug } .md` ,
203+ } ) ;
204+ link . classList . add ( "wiki-rail-page" ) ;
205+ setRailIndent ( link , depth ) ;
206+ return link ;
207+ }
208+
209+ function setRailIndent ( element , depth ) {
210+ element . style . setProperty ( "--rail-indent" , `${ depth * 14 } px` ) ;
211+ }
212+
213+ function sortedFolders ( folder ) {
214+ return Array . from ( folder . folders . values ( ) ) . sort ( compareFolders ) ;
215+ }
216+
217+ function sortedPages ( pages ) {
218+ return [ ...pages ] . sort ( ( a , b ) => {
219+ const aTitle = a . title || a . filename || a . slug ;
220+ const bTitle = b . title || b . filename || b . slug ;
221+ return aTitle . localeCompare ( bTitle ) ;
222+ } ) ;
223+ }
224+
225+ function folderTitle ( name ) {
226+ return name
227+ . split ( / [ - _ ] / )
228+ . filter ( Boolean )
229+ . map ( ( part ) => FOLDER_TITLE_WORDS . get ( part ) || titleWord ( part ) )
230+ . join ( " " ) ;
231+ }
232+
233+ function compareFolders ( a , b ) {
234+ const priority = folderPriority ( a . name ) - folderPriority ( b . name ) ;
235+ if ( priority !== 0 ) return priority ;
236+ return folderTitle ( a . name ) . localeCompare ( folderTitle ( b . name ) ) ;
237+ }
238+
239+ function folderPriority ( name ) {
240+ return FOLDER_SORT_PRIORITY . get ( name ) || 0 ;
241+ }
242+
243+ function titleWord ( value ) {
244+ return value . charAt ( 0 ) . toUpperCase ( ) + value . slice ( 1 ) ;
245+ }
246+
139247function setActiveNav ( elements , routeState ) {
140248 const active = activeNavKind ( routeState ) ;
141249 for ( const item of elements . navItems ) {
0 commit comments