@@ -13,6 +13,7 @@ import { useServerManagementController } from "@/components/dialog-select-server
1313import { DialogServerV2 } from "@/components/settings-v2/dialog-server-v2"
1414import { useSettingsCommand } from "@/components/settings-dialog"
1515import { ChannelIndicator } from "@/components/titlebar"
16+ import { useAppSidebar } from "@/context/app-sidebar"
1617import { useGlobal } from "@/context/global"
1718import { useLanguage } from "@/context/language"
1819import { usePlatform } from "@/context/platform"
@@ -54,6 +55,7 @@ export function AppSidebar(props: { class?: string; debugTools?: { visible: bool
5455 const global = useGlobal ( )
5556 const dialog = useDialog ( )
5657 const model = createHomeModel ( )
58+ const sidebar = useAppSidebar ( )
5759 const openSettings = useSettingsCommand ( )
5860 const controller = useServerManagementController ( { navigateOnAdd : false } )
5961 const [ _state , setState , _ , ready ] = persisted (
@@ -66,138 +68,142 @@ export function AppSidebar(props: { class?: string; debugTools?: { visible: bool
6668 { initialValue : _state } ,
6769 )
6870
71+ // Collapsed hides the column outright rather than narrowing it to a rail, so
72+ // the content pane takes the full width. The titlebar toggle is the way back.
6973 return (
70- < aside
71- data-component = "app-sidebar"
72- class = { `${ props . class ?? "" } w-[280px] min-h-0 min-w-0 shrink-0 flex-col gap-4 overflow-hidden px-3 pb-4 pt-6` }
73- aria-label = { language . t ( "home.projects" ) }
74- >
75- < div class = "flex h-7 min-w-0 shrink-0 items-center justify-between pl-1.5" >
76- < div class = "text-v2-text-text-muted [font-weight:530]" > { language . t ( "home.projects" ) } </ div >
77- < Show
78- when = {
79- global . servers . list ( ) . length === 1 &&
80- ! ( model . projects ( ) . length === 0 && model . recentlyClosed ( ) . length > 0 )
81- }
82- >
83- < TooltipV2 placement = "bottom" value = { language . t ( "home.project.add" ) } >
84- < IconButtonV2
85- data-action = "home-add-project"
86- variant = "ghost-muted"
87- size = "large"
88- class = "titlebar-icon [&_[data-slot=icon-svg]]:text-v2-icon-icon-muted"
89- icon = { < IconV2 name = "folder-add-left" /> }
90- disabled = { global . servers . health [ ServerConnection . key ( global . servers . list ( ) [ 0 ] ! ) ] ?. healthy === false }
91- onClick = { ( ) => model . chooseProject ( global . servers . list ( ) [ 0 ] ! ) }
92- aria-label = { language . t ( "home.project.add" ) }
93- />
94- </ TooltipV2 >
95- </ Show >
96- </ div >
97- < ScrollView data-slot = "home-projects-scroll" class = "min-h-0 min-w-0 shrink" >
98- < Show
99- when = { global . servers . list ( ) . length > 1 }
100- fallback = {
101- < Show
102- when = { model . projects ( ) . length > 0 }
103- fallback = {
104- < HomeProjectEmpty
74+ < Show when = { ! sidebar ?. collapsed ( ) } >
75+ < aside
76+ data-component = "app-sidebar"
77+ class = { `${ props . class ?? "" } w-[280px] min-h-0 min-w-0 shrink-0 flex-col gap-4 overflow-hidden px-3 pb-4 pt-6` }
78+ aria-label = { language . t ( "home.projects" ) }
79+ >
80+ < div class = "flex h-7 min-w-0 shrink-0 items-center justify-between pl-1.5" >
81+ < div class = "text-v2-text-text-muted [font-weight:530]" > { language . t ( "home.projects" ) } </ div >
82+ < Show
83+ when = {
84+ global . servers . list ( ) . length === 1 &&
85+ ! ( model . projects ( ) . length === 0 && model . recentlyClosed ( ) . length > 0 )
86+ }
87+ >
88+ < TooltipV2 placement = "bottom" value = { language . t ( "home.project.add" ) } >
89+ < IconButtonV2
90+ data-action = "home-add-project"
91+ variant = "ghost-muted"
92+ size = "large"
93+ class = "titlebar-icon [&_[data-slot=icon-svg]]:text-v2-icon-icon-muted"
94+ icon = { < IconV2 name = "folder-add-left" /> }
95+ disabled = { global . servers . health [ ServerConnection . key ( global . servers . list ( ) [ 0 ] ! ) ] ?. healthy === false }
96+ onClick = { ( ) => model . chooseProject ( global . servers . list ( ) [ 0 ] ! ) }
97+ aria-label = { language . t ( "home.project.add" ) }
98+ />
99+ </ TooltipV2 >
100+ </ Show >
101+ </ div >
102+ < ScrollView data-slot = "home-projects-scroll" class = "min-h-0 min-w-0 shrink" >
103+ < Show
104+ when = { global . servers . list ( ) . length > 1 }
105+ fallback = {
106+ < Show
107+ when = { model . projects ( ) . length > 0 }
108+ fallback = {
109+ < HomeProjectEmpty
110+ server = { global . servers . list ( ) [ 0 ] ! }
111+ recentlyClosed = { model . recentlyClosed ( ) }
112+ homedir = { model . homedir ( ) }
113+ chooseProject = { model . chooseProject }
114+ openRecentProject = { ( conn , directory ) => model . addProjects ( conn , [ directory ] ) }
115+ language = { language }
116+ />
117+ }
118+ >
119+ < HomeProjectList
105120 server = { global . servers . list ( ) [ 0 ] ! }
106- recentlyClosed = { model . recentlyClosed ( ) }
107- homedir = { model . homedir ( ) }
108- chooseProject = { model . chooseProject }
109- openRecentProject = { ( conn , directory ) => model . addProjects ( conn , [ directory ] ) }
121+ projects = { model . projects ( ) }
122+ selected = { model . selection ( ) }
123+ selectProject = { model . selectProject }
124+ openNewSession = { model . openProjectNewSession }
125+ editProject = { model . editProject }
126+ closeProject = { model . closeProject }
127+ clearNotifications = { model . clearNotifications }
128+ unseenCount = { model . unseenCount }
110129 language = { language }
111130 />
112- }
113- >
114- < HomeProjectList
115- server = { global . servers . list ( ) [ 0 ] ! }
116- projects = { model . projects ( ) }
117- selected = { model . selection ( ) }
118- selectProject = { model . selectProject }
119- openNewSession = { model . openProjectNewSession }
120- editProject = { model . editProject }
121- closeProject = { model . closeProject }
122- clearNotifications = { model . clearNotifications }
123- unseenCount = { model . unseenCount }
124- language = { language }
125- />
126- </ Show >
127- }
128- >
129- < div class = "flex min-w-0 flex-col gap-4" >
130- < For each = { global . servers . list ( ) } >
131- { ( item ) => {
132- const key = ServerConnection . key ( item )
133- const healthy = ( ) => ! ! global . servers . health [ key ] ?. healthy
134- const serverCtx = global . ensureServerCtx ( item )
135- const projects = ( ) => serverCtx . projects . list ( )
136- const hasProjects = ( ) => projects ( ) . length > 0
137- const collapsed = ( ) => ! ! state ( ) . collapsed [ key ]
138- return (
139- < div class = "flex min-w-0 flex-col gap-1" >
140- < HomeServerRow
141- server = { item }
142- selected = { model . selection ( ) . server === key && ! model . selection ( ) . directory }
143- collapsed = { collapsed ( ) }
144- health = { global . servers . health [ key ] }
145- controller = { controller }
146- focusServer = { model . focusServer }
147- chooseProject = { model . chooseProject }
148- openEdit = { ( server ) => dialog . show ( ( ) => < DialogServerV2 mode = "edit" server = { server } /> ) }
149- toggleCollapsed = { ( ) => setState ( "collapsed" , key , ! state ( ) . collapsed [ key ] ) }
150- language = { language }
151- />
152- < Show when = { healthy ( ) && hasProjects ( ) && ! collapsed ( ) } >
153- < div class = "mx-3 h-px bg-v2-border-border-base" />
154- < HomeProjectList
131+ </ Show >
132+ }
133+ >
134+ < div class = "flex min-w-0 flex-col gap-4" >
135+ < For each = { global . servers . list ( ) } >
136+ { ( item ) => {
137+ const key = ServerConnection . key ( item )
138+ const healthy = ( ) => ! ! global . servers . health [ key ] ?. healthy
139+ const serverCtx = global . ensureServerCtx ( item )
140+ const projects = ( ) => serverCtx . projects . list ( )
141+ const hasProjects = ( ) => projects ( ) . length > 0
142+ const collapsed = ( ) => ! ! state ( ) . collapsed [ key ]
143+ return (
144+ < div class = "flex min-w-0 flex-col gap-1" >
145+ < HomeServerRow
155146 server = { item }
156- projects = { projects ( ) }
157- selected = { model . selection ( ) }
158- selectProject = { model . selectProject }
159- openNewSession = { model . openProjectNewSession }
160- editProject = { model . editProject }
161- closeProject = { model . closeProject }
162- clearNotifications = { model . clearNotifications }
163- unseenCount = { model . unseenCount }
147+ selected = { model . selection ( ) . server === key && ! model . selection ( ) . directory }
148+ collapsed = { collapsed ( ) }
149+ health = { global . servers . health [ key ] }
150+ controller = { controller }
151+ focusServer = { model . focusServer }
152+ chooseProject = { model . chooseProject }
153+ openEdit = { ( server ) => dialog . show ( ( ) => < DialogServerV2 mode = "edit" server = { server } /> ) }
154+ toggleCollapsed = { ( ) => setState ( "collapsed" , key , ! state ( ) . collapsed [ key ] ) }
164155 language = { language }
165156 />
166- </ Show >
167- </ div >
168- )
169- } }
170- </ For >
171- </ div >
172- </ Show >
173- </ ScrollView >
157+ < Show when = { healthy ( ) && hasProjects ( ) && ! collapsed ( ) } >
158+ < div class = "mx-3 h-px bg-v2-border-border-base" />
159+ < HomeProjectList
160+ server = { item }
161+ projects = { projects ( ) }
162+ selected = { model . selection ( ) }
163+ selectProject = { model . selectProject }
164+ openNewSession = { model . openProjectNewSession }
165+ editProject = { model . editProject }
166+ closeProject = { model . closeProject }
167+ clearNotifications = { model . clearNotifications }
168+ unseenCount = { model . unseenCount }
169+ language = { language }
170+ />
171+ </ Show >
172+ </ div >
173+ )
174+ } }
175+ </ For >
176+ </ div >
177+ </ Show >
178+ </ ScrollView >
174179
175- < SidebarRecents
176- records = { model . records ( ) }
177- server = { model . selection ( ) . server }
178- loading = { model . sessionLoad . isLoading }
179- canCreate = { ! ! model . newSessionProject ( ) }
180- openNewSession = { model . openNewSession }
181- openSession = { model . openSession }
182- />
180+ < SidebarRecents
181+ records = { model . records ( ) }
182+ server = { model . selection ( ) . server }
183+ loading = { model . sessionLoad . isLoading }
184+ canCreate = { ! ! model . newSessionProject ( ) }
185+ openNewSession = { model . openNewSession }
186+ openSession = { model . openSession }
187+ />
183188
184- < HomeUtilityNav
185- class = "flex shrink-0"
186- openSettings = { openSettings }
187- openHelp = { ( ) => platform . openLink ( DOCS_URL ) }
188- language = { language }
189- />
189+ < HomeUtilityNav
190+ class = "flex shrink-0"
191+ openSettings = { openSettings }
192+ openHelp = { ( ) => platform . openLink ( DOCS_URL ) }
193+ language = { language }
194+ />
190195
191- { /* The DEV / BETA badge, moved out of the titlebar so Home can lead it.
196+ { /* The DEV / BETA badge, moved out of the titlebar so Home can lead it.
192197 The condition mirrors ChannelIndicator's own — kept here rather than
193198 wrapping unconditionally so prod, where it renders nothing, does not
194199 leave the column's gap-4 behind as dead space. */ }
195- < Show when = { CHANNEL === "dev" || CHANNEL === "beta" } >
196- < div class = "flex shrink-0 pl-1.5" >
197- < ChannelIndicator debugTools = { props . debugTools } />
198- </ div >
199- </ Show >
200- </ aside >
200+ < Show when = { CHANNEL === "dev" || CHANNEL === "beta" } >
201+ < div class = "flex shrink-0 pl-1.5" >
202+ < ChannelIndicator debugTools = { props . debugTools } />
203+ </ div >
204+ </ Show >
205+ </ aside >
206+ </ Show >
201207 )
202208}
203209
0 commit comments