@@ -86,6 +86,7 @@ import { formatWorktreePathForDisplay, getOrphanedWorktreePathForThread } from "
8686import { isNonEmpty as isNonEmptyString } from "effect/String" ;
8787import {
8888 resolveProjectStatusIndicator ,
89+ collectSidebarNonIdleProjectIds ,
8990 resolveSidebarNewThreadEnvMode ,
9091 resolveThreadRowClassName ,
9192 resolveThreadStatusPill ,
@@ -106,12 +107,6 @@ function formatRelativeTime(iso: string): string {
106107 return `${ Math . floor ( hours / 24 ) } d ago` ;
107108}
108109
109- interface TerminalStatusIndicator {
110- label : "Terminal process running" ;
111- colorClass : string ;
112- pulse : boolean ;
113- }
114-
115110interface PrStatusIndicator {
116111 label : "PR open" | "PR closed" | "PR merged" ;
117112 colorClass : string ;
@@ -121,19 +116,6 @@ interface PrStatusIndicator {
121116
122117type ThreadPr = GitStatusResult [ "pr" ] ;
123118
124- function terminalStatusFromRunningIds (
125- runningTerminalIds : string [ ] ,
126- ) : TerminalStatusIndicator | null {
127- if ( runningTerminalIds . length === 0 ) {
128- return null ;
129- }
130- return {
131- label : "Terminal process running" ,
132- colorClass : "text-teal-600 dark:text-teal-300/90" ,
133- pulse : true ,
134- } ;
135- }
136-
137119function prStatusIndicator ( pr : ThreadPr ) : PrStatusIndicator | null {
138120 if ( ! pr ) return null ;
139121
@@ -378,27 +360,35 @@ export default function Sidebar() {
378360 return map ;
379361 } , [ threads ] ) ;
380362
363+ const runningTerminalThreadIds = useMemo ( ( ) => {
364+ const ids = new Set < ThreadId > ( ) ;
365+ for ( const thread of threads ) {
366+ if ( selectThreadTerminalState ( terminalStateByThreadId , thread . id ) . runningTerminalIds . length ) {
367+ ids . add ( thread . id ) ;
368+ }
369+ }
370+ return ids ;
371+ } , [ terminalStateByThreadId , threads ] ) ;
372+
381373 const activeThread = routeThreadId
382374 ? threads . find ( ( thread ) => thread . id === routeThreadId )
383375 : undefined ;
384376 const activeDraftThread = useComposerDraftStore ( ( store ) =>
385377 routeThreadId ? store . draftThreadsByThreadId [ routeThreadId ] : undefined ,
386378 ) ;
387- const activeProjectId = activeThread ?. projectId ?? activeDraftThread ?. projectId ;
379+ const activeProjectId = activeThread ?. projectId ?? activeDraftThread ?. projectId ?? null ;
388380
389- // Currently active project and projects with a thread status
390- const activeProjectIds = useMemo ( ( ) => {
391- const ids = new Set < ProjectId > ( ) ;
392- if ( activeProjectId ) {
393- ids . add ( activeProjectId ) ;
394- }
395- for ( const thread of threads ) {
396- if ( threadStatusById . get ( thread . id ) !== null ) {
397- ids . add ( thread . projectId ) ;
398- }
399- }
400- return ids ;
401- } , [ activeProjectId , threadStatusById , threads ] ) ;
381+ // Currently active project and projects with a thread status or running terminal
382+ const nonIdleProjectIds = useMemo (
383+ ( ) =>
384+ collectSidebarNonIdleProjectIds ( {
385+ activeProjectId,
386+ threads,
387+ threadStatusById,
388+ runningTerminalThreadIds,
389+ } ) ,
390+ [ activeProjectId , runningTerminalThreadIds , threadStatusById , threads ] ,
391+ ) ;
402392
403393 const openPrLink = useCallback ( ( event : React . MouseEvent < HTMLElement > , prUrl : string ) => {
404394 event . preventDefault ( ) ;
@@ -1030,12 +1020,12 @@ export default function Sidebar() {
10301020
10311021 const handleCollapseIdleProjects = useCallback ( ( ) => {
10321022 for ( const project of projects ) {
1033- if ( ! project . expanded || activeProjectIds . has ( project . id ) ) {
1023+ if ( ! project . expanded || nonIdleProjectIds . has ( project . id ) ) {
10341024 continue ;
10351025 }
10361026 setProjectExpanded ( project . id , false ) ;
10371027 }
1038- } , [ projects , activeProjectIds , setProjectExpanded ] ) ;
1028+ } , [ projects , nonIdleProjectIds , setProjectExpanded ] ) ;
10391029
10401030 useEffect ( ( ) => {
10411031 const onMouseDown = ( event : globalThis . MouseEvent ) => {
@@ -1518,10 +1508,13 @@ export default function Sidebar() {
15181508 const prStatus = prStatusIndicator (
15191509 prByThreadId . get ( thread . id ) ?? null ,
15201510 ) ;
1521- const terminalStatus = terminalStatusFromRunningIds (
1522- selectThreadTerminalState ( terminalStateByThreadId , thread . id )
1523- . runningTerminalIds ,
1524- ) ;
1511+ const terminalStatus = runningTerminalThreadIds . has ( thread . id )
1512+ ? {
1513+ label : "Terminal process running" ,
1514+ colorClass : "text-teal-600 dark:text-teal-300/90" ,
1515+ pulse : true ,
1516+ }
1517+ : null ;
15251518
15261519 return (
15271520 < SidebarMenuSubItem
0 commit comments