@@ -46,6 +46,7 @@ import { useCommand } from "@/context/command"
4646import { ServerRowMenu } from "@/components/server/server-row-menu"
4747import { ServerHealthIndicator } from "@/components/server/server-row"
4848import { type ServerHealth } from "@/utils/server-health"
49+ import { Persist , persisted } from "@/utils/persist"
4950
5051const HOME_SESSION_LIMIT = 64
5152const HOME_ROW_LAYOUT =
@@ -329,7 +330,7 @@ export function NewHome() {
329330
330331 return (
331332 < div class = "rounded-[10px] shadow-[var(--v2-elevation-raised)] m-2 min-h-0 lg:overflow-hidden bg-v2-background-bg-base self-stretch flex-1" >
332- < div class = "mx-auto grid w -full h -full max-w-[1080px] gap-8 px-6 pb-16 lg:grid-cols-[280px_minmax(0,720px)]" >
333+ < div class = "mx-auto grid h -full w -full max-w-[1080px] grid-rows-[auto_minmax(0,1fr)_auto] gap-4 px-3 pb-3 lg:grid-cols-[280px_minmax(0,720px)] lg:grid-rows-1 lg:gap-8 lg:px-6 lg:pb-16 " >
333334 < HomeProjectColumn
334335 projects = { projects ( ) }
335336 selected = { state . selection }
@@ -355,7 +356,7 @@ export function NewHome() {
355356 />
356357
357358 < section
358- class = "min-h-0 min-w-0 flex-1 flex flex-col pt-12"
359+ class = "min-h-0 min-w-0 flex-1 flex flex-col pt-6 lg:pt- 12"
359360 aria-label = { language . t ( "sidebar.project.recentSessions" ) }
360361 >
361362 < HomeSessionSearch
@@ -419,6 +420,12 @@ export function NewHome() {
419420 </ div >
420421 </ ScrollView >
421422 </ section >
423+ < HomeUtilityNav
424+ class = "flex lg:hidden"
425+ openSettings = { openSettings }
426+ openHelp = { ( ) => platform . openLink ( "https://opencode.ai/desktop-feedback" ) }
427+ language = { language }
428+ />
422429 </ div >
423430 </ div >
424431 )
@@ -442,8 +449,15 @@ function HomeProjectColumn(props: {
442449 const global = useGlobal ( )
443450 const dialog = useDialog ( )
444451 const controller = useServerManagementController ( { navigateOnAdd : false } )
452+ const [ state , setState ] = persisted (
453+ Persist . global ( "home.servers" , [ "home.servers.v1" ] ) ,
454+ createStore ( { collapsed : { } as Record < string , boolean > } ) ,
455+ )
445456 return (
446- < aside class = "flex min-w-0 flex-col lg:pt-[52px] mt-14 gap-4" aria-label = { props . language . t ( "home.projects" ) } >
457+ < aside
458+ class = "mt-6 flex min-w-0 flex-col gap-4 lg:mt-14 lg:pt-[52px]"
459+ aria-label = { props . language . t ( "home.projects" ) }
460+ >
447461 < div class = "flex h-7 min-w-0 items-center justify-between pl-1.5" >
448462 < div class = { HOME_SECTION_LABEL } > { props . language . t ( "home.projects" ) } </ div >
449463 < Show when = { global . servers . list ( ) . length === 1 } >
@@ -467,20 +481,23 @@ function HomeProjectColumn(props: {
467481 const key = ServerConnection . key ( item )
468482 const healthy = ( ) => ! ! global . servers . health [ key ] ?. healthy
469483 const serverCtx = global . createServerCtx ( item )
484+ const collapsed = ( ) => ! ! state . collapsed [ key ]
470485 return (
471486 < div class = "flex max-h-[min(572px,calc(100vh_-_300px))] min-w-0 flex-col gap-1 overflow-y-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden" >
472487 < HomeServerRow
473488 server = { item }
474489 selected = { props . selected . server === key && ! props . selected . directory }
475490 healthy = { healthy ( ) }
491+ collapsed = { collapsed ( ) }
476492 health = { global . servers . health [ key ] }
477493 controller = { controller }
478494 focusServer = { props . focusServer }
479495 chooseProject = { props . chooseProject }
480496 openEdit = { ( server ) => dialog . show ( ( ) => < DialogServerV2 mode = "edit" server = { server } /> ) }
497+ toggleCollapsed = { ( ) => setState ( "collapsed" , key , ! state . collapsed [ key ] ) }
481498 language = { props . language }
482499 />
483- < Show when = { healthy ( ) } >
500+ < Show when = { healthy ( ) && ! collapsed ( ) } >
484501 < div class = "mx-3 h-px bg-v2-border-border-base" />
485502 < HomeProjectList { ...props } server = { item } projects = { serverCtx . projects . list ( ) } />
486503 </ Show >
@@ -489,37 +506,55 @@ function HomeProjectColumn(props: {
489506 } }
490507 </ For >
491508 </ Show >
492- < div class = "mt-4 flex min-w-0 flex-col gap-1" >
493- < button
494- type = "button"
495- class = { `${ HOME_PROJECT_NAV_ROW } text-v2-text-text-faint [&>[data-slot=icon-svg]]:text-v2-icon-icon-muted` }
496- onClick = { props . openSettings }
497- >
498- < IconV2 name = "settings-gear" size = "small" />
499- < span class = { HOME_PROJECT_NAV_LABEL } > { props . language . t ( "sidebar.settings" ) } </ span >
500- </ button >
501- < button
502- type = "button"
503- class = { `${ HOME_PROJECT_NAV_ROW } text-v2-text-text-faint [&>[data-slot=icon-svg]]:text-v2-icon-icon-muted` }
504- onClick = { props . openHelp }
505- >
506- < IconV2 name = "help" size = "small" />
507- < span class = { HOME_PROJECT_NAV_LABEL } > { props . language . t ( "sidebar.help" ) } </ span >
508- </ button >
509- </ div >
509+ < HomeUtilityNav
510+ class = "mt-4 hidden lg:flex"
511+ openSettings = { props . openSettings }
512+ openHelp = { props . openHelp }
513+ language = { props . language }
514+ />
510515 </ aside >
511516 )
512517}
513518
519+ function HomeUtilityNav ( props : {
520+ class ?: string
521+ openSettings : ( ) => void
522+ openHelp : ( ) => void
523+ language : ReturnType < typeof useLanguage >
524+ } ) {
525+ return (
526+ < div class = { `${ props . class ?? "" } min-w-0 flex-col gap-1` } >
527+ < button
528+ type = "button"
529+ class = { `${ HOME_PROJECT_NAV_ROW } text-v2-text-text-faint [&>[data-slot=icon-svg]]:text-v2-icon-icon-muted` }
530+ onClick = { props . openSettings }
531+ >
532+ < IconV2 name = "settings-gear" size = "small" />
533+ < span class = { HOME_PROJECT_NAV_LABEL } > { props . language . t ( "sidebar.settings" ) } </ span >
534+ </ button >
535+ < button
536+ type = "button"
537+ class = { `${ HOME_PROJECT_NAV_ROW } text-v2-text-text-faint [&>[data-slot=icon-svg]]:text-v2-icon-icon-muted` }
538+ onClick = { props . openHelp }
539+ >
540+ < IconV2 name = "help" size = "small" />
541+ < span class = { HOME_PROJECT_NAV_LABEL } > { props . language . t ( "sidebar.help" ) } </ span >
542+ </ button >
543+ </ div >
544+ )
545+ }
546+
514547function HomeServerRow ( props : {
515548 server : ServerConnection . Any
516549 selected : boolean
517550 healthy : boolean
551+ collapsed : boolean
518552 health : ServerHealth | undefined
519553 controller : ReturnType < typeof useServerManagementController >
520554 focusServer : ( server : ServerConnection . Any ) => void
521555 chooseProject : ( server : ServerConnection . Any ) => void
522556 openEdit : ( server : ServerConnection . Http ) => void
557+ toggleCollapsed : ( ) => void
523558 language : ReturnType < typeof useLanguage >
524559} ) {
525560 const [ state , setState ] = createStore ( { menuOpen : false } )
@@ -532,7 +567,30 @@ function HomeServerRow(props: {
532567 disabled = { ! props . healthy }
533568 onClick = { ( ) => props . focusServer ( props . server ) }
534569 >
535- < div class = "flex size-4 shrink-0 items-center justify-center" >
570+ < Show when = { props . healthy } >
571+ < span
572+ data-action = "home-server-collapse"
573+ class = "inline-flex -ml-0.5 -mr-1.5 size-5 shrink-0 items-center justify-center rounded-[4px] text-v2-icon-icon-muted hover:bg-v2-overlay-simple-overlay-hover"
574+ aria-label = {
575+ props . collapsed ? props . language . t ( "home.server.expand" ) : props . language . t ( "home.server.collapse" )
576+ }
577+ aria-expanded = { ! props . collapsed }
578+ onClick = { ( event ) => {
579+ event . preventDefault ( )
580+ event . stopPropagation ( )
581+ props . toggleCollapsed ( )
582+ } }
583+ onPointerDown = { ( event ) => event . preventDefault ( ) }
584+ >
585+ < IconV2
586+ name = "chevron-down"
587+ size = "small"
588+ class = "transition-transform duration-150 ease-in-out"
589+ style = { { transform : `rotate(${ props . collapsed ? - 90 : 0 } deg)` } }
590+ />
591+ </ span >
592+ </ Show >
593+ < div class = "flex size-4 shrink-0 items-center justify-center -mr-0.5" >
536594 < ServerHealthIndicator health = { props . health } />
537595 </ div >
538596 < span class = "flex min-w-0 items-center gap-1" >
0 commit comments