@@ -745,7 +745,14 @@ function generateEndpointSection(
745745 ? generateWebSocketMessagesDocs ( endpoint . messages , websocketMessages , types )
746746 : '' ;
747747
748+ // Generate anchor ID from endpoint name
749+ const anchorId = endpoint . name
750+ . toLowerCase ( )
751+ . replace ( / [ ^ a - z 0 - 9 ] + / g, '-' )
752+ . replace ( / ^ - | - $ / g, '' ) ;
753+
748754 let content = `
755+ <div id="${ anchorId } ">
749756<MethodPage client:load>
750757 <MethodPageLeft client:load>
751758 <MethodHeader
@@ -817,6 +824,7 @@ ${streamingEventsDocs}
817824 />
818825 </MethodPageRight>
819826</MethodPage>
827+ </div>
820828` ;
821829
822830 return content ;
@@ -842,11 +850,30 @@ function getCategoryTitle(category: string): string {
842850}
843851
844852// Manual pages that are not generated from schema but should be included
845- const MANUAL_PAGES = [
853+ interface ManualEndpoint {
854+ method : string ;
855+ title : string ;
856+ }
857+
858+ interface ManualPage {
859+ category : string ;
860+ title : string ;
861+ description : string ;
862+ endpoints ?: ManualEndpoint [ ] ;
863+ }
864+
865+ const MANUAL_PAGES : ManualPage [ ] = [
846866 {
847867 category : 'sprites' ,
848868 title : 'Sprites' ,
849869 description : 'Create, list, update, and delete Sprites' ,
870+ endpoints : [
871+ { method : 'POST' , title : 'Create Sprite' } ,
872+ { method : 'GET' , title : 'List Sprites' } ,
873+ { method : 'GET' , title : 'Get Sprite' } ,
874+ { method : 'PUT' , title : 'Update Sprite' } ,
875+ { method : 'DELETE' , title : 'Delete Sprite' } ,
876+ ] ,
850877 } ,
851878] ;
852879
@@ -1147,42 +1174,149 @@ ${JSON.stringify(msg.example, null, 2)}
11471174// Sidebar config generation
11481175// ============================================================================
11491176
1150- interface SidebarItem {
1177+ interface SidebarBadge {
1178+ text : string ;
1179+ variant : 'note' | 'tip' | 'caution' | 'danger' | 'success' | 'default' ;
1180+ class ?: string ;
1181+ }
1182+
1183+ interface SidebarLink {
11511184 label : string ;
1152- slug : string ;
1185+ slug ?: string ;
1186+ link ?: string ;
1187+ badge ?: SidebarBadge ;
11531188 attrs ?: Record < string , string > ;
11541189}
11551190
1191+ interface SidebarGroup {
1192+ label : string ;
1193+ collapsed ?: boolean ;
1194+ items : ( SidebarLink | SidebarGroup ) [ ] ;
1195+ }
1196+
1197+ type SidebarItem = SidebarLink | SidebarGroup ;
1198+
1199+ function getMethodBadge ( method : string ) : SidebarBadge {
1200+ const variants : Record < string , SidebarBadge [ 'variant' ] > = {
1201+ GET : 'note' ,
1202+ POST : 'success' ,
1203+ PUT : 'caution' ,
1204+ PATCH : 'caution' ,
1205+ DELETE : 'danger' ,
1206+ WSS : 'tip' ,
1207+ } ;
1208+ return {
1209+ text : method ,
1210+ variant : variants [ method . toUpperCase ( ) ] || 'default' ,
1211+ class : `sidebar-method-${ method . toLowerCase ( ) } ` ,
1212+ } ;
1213+ }
1214+
1215+ function slugifyEndpoint ( title : string ) : string {
1216+ return title
1217+ . toLowerCase ( )
1218+ . replace ( / [ ^ a - z 0 - 9 ] + / g, '-' )
1219+ . replace ( / ^ - | - $ / g, '' ) ;
1220+ }
1221+
11561222function generateSidebarItems (
11571223 categories : string [ ] ,
1158- _endpointsByCategory : Record < string , APIEndpoint [ ] > ,
1224+ endpointsByCategory : Record < string , APIEndpoint [ ] > ,
11591225 versionId : string ,
11601226) : SidebarItem [ ] {
11611227 const items : SidebarItem [ ] = [
11621228 { label : 'Overview' , slug : `api/${ versionId } ` } ,
11631229 ] ;
11641230
1165- // Add manual pages first
1231+ // Add manual pages (Sprites) with nested endpoint items
11661232 for ( const page of MANUAL_PAGES ) {
1167- items . push ( {
1168- label : page . title ,
1169- slug : `api/${ versionId } /${ page . category } ` ,
1170- } ) ;
1233+ if ( page . endpoints && page . endpoints . length > 0 ) {
1234+ // Create a group with nested endpoint items
1235+ const endpointItems : SidebarLink [ ] = page . endpoints . map ( ( ep ) => ( {
1236+ label : ep . title ,
1237+ link : `/api/${ versionId } /${ page . category } #${ slugifyEndpoint ( ep . title ) } ` ,
1238+ badge : getMethodBadge ( ep . method ) ,
1239+ } ) ) ;
1240+ items . push ( {
1241+ label : page . title ,
1242+ collapsed : true ,
1243+ items : endpointItems ,
1244+ } ) ;
1245+ } else {
1246+ items . push ( {
1247+ label : page . title ,
1248+ slug : `api/${ versionId } /${ page . category } ` ,
1249+ } ) ;
1250+ }
11711251 }
11721252
1173- // Add generated categories
1253+ // Add generated categories with nested endpoint items
11741254 for ( const category of categories ) {
1175- items . push ( {
1176- label : getCategoryTitle ( category ) ,
1177- slug : `api/${ versionId } /${ category } ` ,
1178- } ) ;
1255+ const endpoints = endpointsByCategory [ category ] || [ ] ;
1256+ if ( endpoints . length > 0 ) {
1257+ const endpointItems : SidebarLink [ ] = endpoints . map ( ( ep ) => ( {
1258+ label : ep . name ,
1259+ link : `/api/${ versionId } /${ category } #${ slugifyEndpoint ( ep . name ) } ` ,
1260+ badge : getMethodBadge ( ep . method ) ,
1261+ } ) ) ;
1262+ items . push ( {
1263+ label : getCategoryTitle ( category ) ,
1264+ collapsed : true ,
1265+ items : endpointItems ,
1266+ } ) ;
1267+ } else {
1268+ items . push ( {
1269+ label : getCategoryTitle ( category ) ,
1270+ slug : `api/${ versionId } /${ category } ` ,
1271+ } ) ;
1272+ }
11791273 }
11801274
11811275 items . push ( { label : 'Type Definitions' , slug : `api/${ versionId } /types` } ) ;
11821276
11831277 return items ;
11841278}
11851279
1280+ function serializeSidebarItem ( item : SidebarItem , indent : number ) : string {
1281+ const pad = ' ' . repeat ( indent ) ;
1282+
1283+ // Check if it's a group (has items array)
1284+ if ( 'items' in item ) {
1285+ const group = item as SidebarGroup ;
1286+ const nestedItems = group . items
1287+ . map ( ( i ) => serializeSidebarItem ( i , indent + 1 ) )
1288+ . join ( ',\n' ) ;
1289+ return `${ pad } {
1290+ ${ pad } label: '${ group . label } ',
1291+ ${ pad } collapsed: ${ group . collapsed ?? false } ,
1292+ ${ pad } items: [
1293+ ${ nestedItems }
1294+ ${ pad } ]
1295+ ${ pad } }`;
1296+ }
1297+
1298+ // It's a link
1299+ const sidebarLink = item as SidebarLink ;
1300+ const parts = [ `label: '${ sidebarLink . label } '` ] ;
1301+ if ( sidebarLink . link ) {
1302+ parts . push ( `link: '${ sidebarLink . link } '` ) ;
1303+ } else if ( sidebarLink . slug ) {
1304+ parts . push ( `slug: '${ sidebarLink . slug } '` ) ;
1305+ }
1306+ if ( sidebarLink . badge ) {
1307+ parts . push (
1308+ `badge: { text: '${ sidebarLink . badge . text } ', variant: '${ sidebarLink . badge . variant } '${ sidebarLink . badge . class ? `, class: '${ sidebarLink . badge . class } '` : '' } }` ,
1309+ ) ;
1310+ }
1311+ if ( sidebarLink . attrs ) {
1312+ const attrsStr = Object . entries ( sidebarLink . attrs )
1313+ . map ( ( [ k , v ] ) => `'${ k } ': '${ v } '` )
1314+ . join ( ', ' ) ;
1315+ parts . push ( `attrs: { ${ attrsStr } }` ) ;
1316+ }
1317+ return `${ pad } { ${ parts . join ( ', ' ) } }` ;
1318+ }
1319+
11861320function generateSidebarConfig (
11871321 categories : string [ ] ,
11881322 endpointsByCategory : Record < string , APIEndpoint [ ] > ,
@@ -1195,16 +1329,7 @@ function generateSidebarConfig(
11951329 ) ;
11961330
11971331 const itemsStr = items
1198- . map ( ( item ) => {
1199- const parts = [ `label: '${ item . label } '` , `slug: '${ item . slug } '` ] ;
1200- if ( item . attrs ) {
1201- const attrsStr = Object . entries ( item . attrs )
1202- . map ( ( [ k , v ] ) => `'${ k } ': '${ v } '` )
1203- . join ( ', ' ) ;
1204- parts . push ( `attrs: { ${ attrsStr } }` ) ;
1205- }
1206- return ` { ${ parts . join ( ', ' ) } }` ;
1207- } )
1332+ . map ( ( item ) => serializeSidebarItem ( item , 3 ) )
12081333 . join ( ',\n' ) ;
12091334
12101335 return `
0 commit comments