@@ -18,6 +18,8 @@ const state = {
1818 notifPanelOpen : false ,
1919 sidebarMode : 'main' ,
2020 accountTab : 'info' ,
21+ sidebarServersOpen : false ,
22+ sidebarServersLoading : false ,
2123} ;
2224
2325function setRgpdConsent ( preferences ) {
@@ -1135,7 +1137,7 @@ function initSidebarTooltip() {
11351137 }
11361138
11371139 sidebarNav . addEventListener ( 'mouseover' , ( e ) => {
1138- const item = e . target . closest ( '.nav-item' ) ;
1140+ const item = e . target . closest ( '.nav-item, .nav-parent ' ) ;
11391141 if ( ! item ) return ;
11401142 if ( ! sidebar . classList . contains ( 'collapsed' ) ) return ;
11411143
@@ -1199,10 +1201,27 @@ function renderSidebarNav() {
11991201 < i data-lucide ="grid-3x3 "> </ i >
12001202 Overview
12011203 </ a >
1202- < a class ="nav-item ${ state . currentPage === 'servers' ? 'active ' : '' } " data-page =" servers " href =" /servers ">
1204+ < div class ="nav-parent ${ state . sidebarServersOpen ? 'open ' : '' } " id =" nav- servers-toggle ">
12031205 < i data-lucide ="server "> </ i >
1204- My Servers
1205- </ a >
1206+ < span class ="nav-parent-label "> My Servers</ span >
1207+ < svg class ="nav-parent-chevron " viewBox ="0 0 24 24 " fill ="none " stroke ="currentColor " stroke-width ="2 " stroke-linecap ="round " stroke-linejoin ="round "> < polyline points ="9 18 15 12 9 6 "> </ polyline > </ svg >
1208+ </ div >
1209+ < div class ="nav-sub-list ${ state . sidebarServersOpen ? 'open' : '' } " id ="nav-servers-list ">
1210+ ${ state . sidebarServersLoading ? html `< div class ="nav-sub-empty "> < span class ="spinner "> </ span > Loading...</ div > ` :
1211+ state . servers . length === 0 ? html `< div class ="nav-sub-empty "> No servers</ div > ` :
1212+ state . servers . map ( s => {
1213+ const isInstalling = s . status === 'installing' || s . installed === 0 || s . installed === '0' || s . installed === false ;
1214+ const isSuspended = s . status === 'suspended' ;
1215+ const dotClass = isSuspended ? 'dot-suspended' : ( isInstalling ? 'dot-installing' : 'dot-active' ) ;
1216+ const isActive = state . currentPage === 'server' && state . serverId === s . id ;
1217+ return html `
1218+ < a class ="nav-sub-item ${ isActive ? 'active' : '' } " data-server-nav ="${ s . id } " href ="/server/ ${ s . id } ">
1219+ < span class ="nav-sub-dot ${ dotClass } "> </ span >
1220+ ${ escapeHtml ( s . name ) }
1221+ </ a >
1222+ ` ;
1223+ } ) . join ( '' ) }
1224+ </ div >
12061225 < a class ="nav-item " id ="nav-notifications " href ="# ">
12071226 < span style ="position:relative;display:inline-flex ">
12081227 < i data-lucide ="bell "> </ i >
@@ -1255,6 +1274,37 @@ function renderSidebarNav() {
12551274 } ) ;
12561275 }
12571276
1277+ const serversToggle = document . querySelector ( '#nav-servers-toggle' ) ;
1278+ if ( serversToggle ) {
1279+ serversToggle . addEventListener ( 'click' , async ( e ) => {
1280+ e . preventDefault ( ) ;
1281+ state . sidebarServersOpen = ! state . sidebarServersOpen ;
1282+ serversToggle . classList . toggle ( 'open' , state . sidebarServersOpen ) ;
1283+ const subList = document . querySelector ( '#nav-servers-list' ) ;
1284+ if ( subList ) subList . classList . toggle ( 'open' , state . sidebarServersOpen ) ;
1285+ if ( state . sidebarServersOpen && state . servers . length === 0 && ! state . sidebarServersLoading ) {
1286+ state . sidebarServersLoading = true ;
1287+ const subListEl = document . querySelector ( '#nav-servers-list' ) ;
1288+ if ( subListEl ) subListEl . innerHTML = html `< div class ="nav-sub-empty "> < span class ="spinner "> </ span > Loading...</ div > ` ;
1289+ try {
1290+ const data = await api ( '/servers/list' ) ;
1291+ state . servers = data . servers || [ ] ;
1292+ } catch ( err ) {
1293+ state . servers = [ ] ;
1294+ }
1295+ state . sidebarServersLoading = false ;
1296+ renderSidebarNav ( ) ;
1297+ }
1298+ } ) ;
1299+ }
1300+
1301+ document . querySelectorAll ( '.nav-sub-item[data-server-nav]' ) . forEach ( item => {
1302+ item . addEventListener ( 'click' , ( e ) => {
1303+ e . preventDefault ( ) ;
1304+ navigateTo ( 'server/' + item . dataset . serverNav ) ;
1305+ } ) ;
1306+ } ) ;
1307+
12581308 updateNavIndicator ( ) ;
12591309 initIcons ( ) ;
12601310}
@@ -1343,6 +1393,8 @@ function navigateTo(page) {
13431393 if ( state . sidebarMode !== newSidebarMode ) {
13441394 state . sidebarMode = newSidebarMode ;
13451395 renderSidebarNav ( ) ;
1396+ } else if ( basePage === 'server' ) {
1397+ renderSidebarNav ( ) ;
13461398 }
13471399
13481400 const url = basePage === 'overview' && ! param ? '/' : `/${ page } ` ;
@@ -1457,6 +1509,8 @@ window.addEventListener('popstate', () => {
14571509 if ( state . sidebarMode !== newSidebarMode ) {
14581510 state . sidebarMode = newSidebarMode ;
14591511 renderSidebarNav ( ) ;
1512+ } else if ( basePage === 'server' ) {
1513+ renderSidebarNav ( ) ;
14601514 }
14611515
14621516 if ( basePage === 'server' && state . serverId ) {
0 commit comments