@@ -9,7 +9,7 @@ import React, { useState, useMemo, useEffect, useRef } from 'react';
99import type { Agent } from '../types' ;
1010import { AgentCard } from './AgentCard' ;
1111import { groupAgents , getGroupStats , filterAgents , getAgentDisplayName , type AgentGroup } from '../lib/hierarchy' ;
12- import { STATUS_COLORS } from '../lib/colors' ;
12+ import { STATUS_COLORS , getAgentColor , getAgentInitials } from '../lib/colors' ;
1313
1414export interface AgentListProps {
1515 agents : Agent [ ] ;
@@ -47,6 +47,7 @@ export function AgentList({
4747} : AgentListProps ) {
4848 const [ expandedGroups , setExpandedGroups ] = useState < Set < string > > ( new Set ( ) ) ;
4949 const [ isPinnedExpanded , setIsPinnedExpanded ] = useState ( true ) ;
50+ const [ isAllCollapsed , setIsAllCollapsed ] = useState ( false ) ;
5051
5152 // Filter out setup agents (temporary agents for provider auth)
5253 // and system agents like Dashboard (used for dashboard message sending)
@@ -102,14 +103,15 @@ export function AgentList({
102103 } ) ;
103104 } ;
104105
105- // Derive from actual state so the label is always accurate
106- const allExpanded = groups . length > 0 && groups . every ( ( g ) => expandedGroups . has ( g . prefix ) ) ;
107-
108106 const toggleAll = ( ) => {
109- if ( allExpanded ) {
110- setExpandedGroups ( new Set ( ) ) ;
111- } else {
107+ if ( isAllCollapsed ) {
108+ // Expand: restore all groups and show everything
109+ setIsAllCollapsed ( false ) ;
112110 setExpandedGroups ( new Set ( groups . map ( ( g ) => g . prefix ) ) ) ;
111+ setIsPinnedExpanded ( true ) ;
112+ } else {
113+ // Collapse: hide entire panel contents
114+ setIsAllCollapsed ( true ) ;
113115 }
114116 } ;
115117
@@ -133,74 +135,76 @@ export function AgentList({
133135
134136 return (
135137 < div className = "flex flex-col gap-1" >
136- { groups . length > 1 && (
137- < div className = "flex justify-between items-center py-2 px-3 text-xs text-text-muted" >
138- < span > { filteredAgents . length } agents</ span >
139- < button
140- className = "bg-transparent border-none text-accent cursor-pointer text-xs hover:underline"
141- onClick = { toggleAll }
142- >
143- { allExpanded ? 'Collapse all' : 'Expand all' }
144- </ button >
145- </ div >
146- ) }
138+ < div className = "flex justify-between items-center py-2 px-3 text-xs text-text-muted" >
139+ < span > { filteredAgents . length } { filteredAgents . length === 1 ? 'agent' : 'agents' } </ span >
140+ < button
141+ className = "bg-transparent border-none text-accent cursor-pointer text-xs hover:underline"
142+ onClick = { toggleAll }
143+ >
144+ { isAllCollapsed ? 'Expand all' : 'Collapse all' }
145+ </ button >
146+ </ div >
147147
148- { /* Pinned Agents Section */ }
149- { pinnedAgentsList . length > 0 && (
150- < div className = "mb-2" >
151- < button
152- className = "flex items-center gap-2 w-full py-2 px-3 bg-transparent border-none cursor-pointer text-sm text-left rounded transition-colors duration-200 relative hover:bg-amber-400/5"
153- onClick = { ( ) => setIsPinnedExpanded ( ! isPinnedExpanded ) }
154- >
155- < div className = "absolute left-0 top-1 bottom-1 w-[3px] rounded-sm bg-amber-400" />
156- < PinnedChevronIcon expanded = { isPinnedExpanded } />
157- < PinHeaderIcon />
158- < span className = "font-semibold text-amber-400" > Pinned</ span >
159- < span className = "text-text-muted font-normal" > ({ pinnedAgentsList . length } )</ span >
160- </ button >
148+ { ! isAllCollapsed && (
149+ < >
150+ { /* Pinned Agents Section */ }
151+ { pinnedAgentsList . length > 0 && (
152+ < div className = "mb-2" >
153+ < button
154+ className = "flex items-center gap-2 w-full py-2 px-3 bg-transparent border-none cursor-pointer text-sm text-left rounded transition-colors duration-200 relative hover:bg-amber-400/5"
155+ onClick = { ( ) => setIsPinnedExpanded ( ! isPinnedExpanded ) }
156+ >
157+ < div className = "absolute left-0 top-1 bottom-1 w-[3px] rounded-sm bg-amber-400" />
158+ < PinnedChevronIcon expanded = { isPinnedExpanded } />
159+ < PinHeaderIcon />
160+ < span className = "font-semibold text-amber-400" > Pinned</ span >
161+ < span className = "text-text-muted font-normal" > ({ pinnedAgentsList . length } )</ span >
162+ </ button >
161163
162- { isPinnedExpanded && (
163- < div className = "py-1 pl-4 flex flex-col gap-1" >
164- { pinnedAgentsList . map ( ( agent ) => (
165- < AgentCard
166- key = { agent . name }
167- agent = { agent }
168- isSelected = { agent . name === selectedAgent }
169- compact = { compact }
170- isPinned = { true }
171- isMaxPinned = { isMaxPinned }
172- onClick = { onAgentSelect }
173- onMessageClick = { onAgentMessage }
174- onReleaseClick = { onReleaseClick }
175- onLogsClick = { onLogsClick }
176- onProfileClick = { onProfileClick }
177- onPinToggle = { onPinToggle }
178- />
179- ) ) }
164+ { isPinnedExpanded && (
165+ < div className = "py-1 pl-4 flex flex-col gap-1" >
166+ { pinnedAgentsList . map ( ( agent ) => (
167+ < AgentCard
168+ key = { agent . name }
169+ agent = { agent }
170+ isSelected = { agent . name === selectedAgent }
171+ compact = { compact }
172+ isPinned = { true }
173+ isMaxPinned = { isMaxPinned }
174+ onClick = { onAgentSelect }
175+ onMessageClick = { onAgentMessage }
176+ onReleaseClick = { onReleaseClick }
177+ onLogsClick = { onLogsClick }
178+ onProfileClick = { onProfileClick }
179+ onPinToggle = { onPinToggle }
180+ />
181+ ) ) }
182+ </ div >
183+ ) }
180184 </ div >
181185 ) }
182- </ div >
183- ) }
184186
185- { groups . map ( ( group ) => (
186- < AgentGroupComponent
187- key = { group . prefix }
188- group = { group }
189- isExpanded = { expandedGroups . has ( group . prefix ) }
190- selectedAgent = { selectedAgent }
191- compact = { compact }
192- showStats = { showGroupStats }
193- pinnedAgents = { pinnedAgents }
194- isMaxPinned = { isMaxPinned }
195- onToggle = { ( ) => toggleGroup ( group . prefix ) }
196- onAgentSelect = { onAgentSelect }
197- onAgentMessage = { onAgentMessage }
198- onReleaseClick = { onReleaseClick }
199- onLogsClick = { onLogsClick }
200- onProfileClick = { onProfileClick }
201- onPinToggle = { onPinToggle }
202- />
203- ) ) }
187+ { groups . map ( ( group ) => (
188+ < AgentGroupComponent
189+ key = { group . prefix }
190+ group = { group }
191+ isExpanded = { expandedGroups . has ( group . prefix ) }
192+ selectedAgent = { selectedAgent }
193+ compact = { compact }
194+ showStats = { showGroupStats }
195+ pinnedAgents = { pinnedAgents }
196+ isMaxPinned = { isMaxPinned }
197+ onToggle = { ( ) => toggleGroup ( group . prefix ) }
198+ onAgentSelect = { onAgentSelect }
199+ onAgentMessage = { onAgentMessage }
200+ onReleaseClick = { onReleaseClick }
201+ onLogsClick = { onLogsClick }
202+ onProfileClick = { onProfileClick }
203+ onPinToggle = { onPinToggle }
204+ />
205+ ) ) }
206+ </ >
207+ ) }
204208 </ div >
205209 ) ;
206210}
@@ -241,13 +245,11 @@ function AgentGroupComponent({
241245 const stats = showStats ? getGroupStats ( group . agents ) : null ;
242246
243247 // Check if this is a "solo" agent - single agent in group where name matches prefix
244- // (e.g., "Lead" agent with no team set creates a "lead" group)
245248 const isSoloAgent =
246249 group . agents . length === 1 &&
247250 group . agents [ 0 ] . name . toLowerCase ( ) === group . prefix . toLowerCase ( ) ;
248251
249252 // For solo agents, render just the card without a group header
250- // When collapsed, switch to compact mode since there's no group header to collapse to
251253 if ( isSoloAgent ) {
252254 const agent = group . agents [ 0 ] ;
253255 return (
@@ -256,7 +258,7 @@ function AgentGroupComponent({
256258 key = { agent . name }
257259 agent = { agent }
258260 isSelected = { agent . name === selectedAgent }
259- compact = { ! isExpanded || compact }
261+ compact = { compact }
260262 isPinned = { pinnedAgents . includes ( agent . name ) }
261263 isMaxPinned = { isMaxPinned }
262264 onClick = { onAgentSelect }
0 commit comments