diff --git a/apps/web/src/components/ChatView.tsx b/apps/web/src/components/ChatView.tsx index 8b510d457fd..26a5c41ded5 100644 --- a/apps/web/src/components/ChatView.tsx +++ b/apps/web/src/components/ChatView.tsx @@ -5913,6 +5913,11 @@ function ChatViewContent(props: ChatViewProps) { rightPanelAvailable={activeProject !== null} rightPanelOpen={rightPanelOpen} rightPanelShortcutLabel={shortcutLabelForCommand(keybindings, "rightPanel.toggle")} + // Suppressed while the Agents surface is visible: the roster itself is + // on screen, so the toggle badge would be pointing at nothing. + liveAgentCount={ + rightPanelOpen && activeRightPanelSurface?.kind === "agents" ? 0 : agentPanelModel.liveCount + } onToggleTerminal={toggleTerminalVisibility} onToggleRightPanel={toggleRightPanel} /> @@ -6419,6 +6424,7 @@ function ChatViewContent(props: ChatViewProps) { browserAvailable={isPreviewSupportedInRuntime()} diffAvailable={isServerThread && isGitRepo} filesAvailable={activeProject !== null} + liveAgentCount={agentPanelModel.liveCount} > {rightPanelContent} @@ -6447,6 +6453,7 @@ function ChatViewContent(props: ChatViewProps) { browserAvailable={isPreviewSupportedInRuntime()} diffAvailable={isServerThread && isGitRepo} filesAvailable={activeProject !== null} + liveAgentCount={agentPanelModel.liveCount} > {rightPanelContent} diff --git a/apps/web/src/components/RightPanelTabs.tsx b/apps/web/src/components/RightPanelTabs.tsx index b9345ab8c3c..0b1700e7349 100644 --- a/apps/web/src/components/RightPanelTabs.tsx +++ b/apps/web/src/components/RightPanelTabs.tsx @@ -48,6 +48,8 @@ interface RightPanelTabsProps { browserAvailable: boolean; diffAvailable: boolean; filesAvailable: boolean; + /** Running + waiting subagents; badges the Agents card in the empty state. */ + liveAgentCount: number; children: ReactNode; } @@ -96,6 +98,7 @@ function RightPanelEmptyState(props: { browserAvailable: boolean; diffAvailable: boolean; filesAvailable: boolean; + liveAgentCount: number; }) { const actions = [ { @@ -105,6 +108,7 @@ function RightPanelEmptyState(props: { available: props.browserAvailable, disabledReason: SURFACE_DISABLED_REASONS.browser, onClick: props.onAddBrowser, + badgeCount: 0, }, { label: "Terminal", @@ -113,6 +117,7 @@ function RightPanelEmptyState(props: { available: true, disabledReason: null, onClick: props.onAddTerminal, + badgeCount: 0, }, { label: "Files", @@ -121,6 +126,7 @@ function RightPanelEmptyState(props: { available: props.filesAvailable, disabledReason: SURFACE_DISABLED_REASONS.files, onClick: props.onAddFiles, + badgeCount: 0, }, { label: "Diff", @@ -129,6 +135,7 @@ function RightPanelEmptyState(props: { available: props.diffAvailable, disabledReason: SURFACE_DISABLED_REASONS.diff, onClick: props.onAddDiff, + badgeCount: 0, }, { label: "Agents", @@ -137,6 +144,7 @@ function RightPanelEmptyState(props: { available: true, disabledReason: null, onClick: props.onAddAgents, + badgeCount: props.liveAgentCount, }, ] as const; @@ -154,7 +162,17 @@ function RightPanelEmptyState(props: { const Icon = action.icon; const content = ( <> - + + + {action.badgeCount > 0 ? ( + + {action.badgeCount} + + ) : null} + {action.label} {action.description} @@ -498,6 +516,7 @@ export function RightPanelTabs(props: RightPanelTabsProps) { browserAvailable={props.browserAvailable} diffAvailable={props.diffAvailable} filesAvailable={props.filesAvailable} + liveAgentCount={props.liveAgentCount} /> ) : ( props.children diff --git a/apps/web/src/components/chat/PanelLayoutControls.tsx b/apps/web/src/components/chat/PanelLayoutControls.tsx index deb7b3ee9af..c61826f677d 100644 --- a/apps/web/src/components/chat/PanelLayoutControls.tsx +++ b/apps/web/src/components/chat/PanelLayoutControls.tsx @@ -11,6 +11,8 @@ interface PanelLayoutControlsProps { rightPanelAvailable: boolean; rightPanelOpen: boolean; rightPanelShortcutLabel: string | null; + /** Running + waiting subagents in this thread; badges the right panel toggle. */ + liveAgentCount: number; onToggleTerminal: () => void; onToggleRightPanel: () => void; } @@ -22,6 +24,7 @@ export const PanelLayoutControls = memo(function PanelLayoutControls({ rightPanelAvailable, rightPanelOpen, rightPanelShortcutLabel, + liveAgentCount, onToggleTerminal, onToggleRightPanel, }: PanelLayoutControlsProps) { @@ -59,18 +62,34 @@ export const PanelLayoutControls = memo(function PanelLayoutControls({ className="shrink-0 [-webkit-app-region:no-drag]" pressed={rightPanelOpen} onPressedChange={onToggleRightPanel} - aria-label="Toggle right panel" + aria-label={ + liveAgentCount > 0 + ? `Toggle right panel, ${liveAgentCount} ${liveAgentCount === 1 ? "agent" : "agents"} working` + : "Toggle right panel" + } variant="ghost" size="sm" disabled={!rightPanelAvailable} > + {liveAgentCount > 0 ? ( + + {liveAgentCount} + + ) : null} } /> {rightPanelAvailable - ? `Toggle right panel${rightPanelShortcutLabel ? ` (${rightPanelShortcutLabel})` : ""}` + ? `Toggle right panel${rightPanelShortcutLabel ? ` (${rightPanelShortcutLabel})` : ""}${ + liveAgentCount > 0 + ? ` ยท ${liveAgentCount} ${liveAgentCount === 1 ? "agent" : "agents"} working` + : "" + }` : "Right panel is unavailable"}