History

feat(web): show how many subagents are running at a glance (#5745)

Theo Browne committed 3 months ago

Commit c2f8cb71 parent 89ee6923 files · +48 −3Browse files

Unified · Split

3 files
  1. apps/web/src/components/ChatView.tsx +7 −0
  2. apps/web/src/components/RightPanelTabs.tsx +20 −1
  3. apps/web/src/components/chat/PanelLayoutControls.tsx +21 −2
apps/web/src/components/ChatView.tsx+7−0
@@ -5913,6 +5913,11 @@ function ChatViewContent(props: ChatViewProps) { · 5912 unmodified lines
59135913 rightPanelAvailable={activeProject !== null}
59145914 rightPanelOpen={rightPanelOpen}
59155915 rightPanelShortcutLabel={shortcutLabelForCommand(keybindings, "rightPanel.toggle")}
5916+ // Suppressed while the Agents surface is visible: the roster itself is
5917+ // on screen, so the toggle badge would be pointing at nothing.
5918+ liveAgentCount={
5919+ rightPanelOpen && activeRightPanelSurface?.kind === "agents" ? 0 : agentPanelModel.liveCount
5920+ }
59165921 onToggleTerminal={toggleTerminalVisibility}
59175922 onToggleRightPanel={toggleRightPanel}
59185923 />
@@ -6419,6 +6424,7 @@ function ChatViewContent(props: ChatViewProps) { · 500 unmodified lines
64196424 browserAvailable={isPreviewSupportedInRuntime()}
64206425 diffAvailable={isServerThread && isGitRepo}
64216426 filesAvailable={activeProject !== null}
6427+ liveAgentCount={agentPanelModel.liveCount}
64226428 >
64236429 {rightPanelContent}
64246430 </RightPanelTabs>
@@ -6447,6 +6453,7 @@ function ChatViewContent(props: ChatViewProps) { · 22 unmodified lines
64476453 browserAvailable={isPreviewSupportedInRuntime()}
64486454 diffAvailable={isServerThread && isGitRepo}
64496455 filesAvailable={activeProject !== null}
6456+ liveAgentCount={agentPanelModel.liveCount}
64506457 >
64516458 {rightPanelContent}
64526459 </RightPanelTabs>
apps/web/src/components/RightPanelTabs.tsx+20−1
@@ -48,6 +48,8 @@ interface RightPanelTabsProps { · 47 unmodified lines
4848 browserAvailable: boolean;
4949 diffAvailable: boolean;
5050 filesAvailable: boolean;
51+ /** Running + waiting subagents; badges the Agents card in the empty state. */
52+ liveAgentCount: number;
5153 children: ReactNode;
5254 }
5355
@@ -96,6 +98,7 @@ function RightPanelEmptyState(props: { · 42 unmodified lines
9698 browserAvailable: boolean;
9799 diffAvailable: boolean;
98100 filesAvailable: boolean;
101+ liveAgentCount: number;
99102 }) {
100103 const actions = [
101104 {
@@ -105,6 +108,7 @@ function RightPanelEmptyState(props: { · 3 unmodified lines
105108 available: props.browserAvailable,
106109 disabledReason: SURFACE_DISABLED_REASONS.browser,
107110 onClick: props.onAddBrowser,
111+ badgeCount: 0,
108112 },
109113 {
110114 label: "Terminal",
@@ -113,6 +117,7 @@ function RightPanelEmptyState(props: { · 2 unmodified lines
113117 available: true,
114118 disabledReason: null,
115119 onClick: props.onAddTerminal,
120+ badgeCount: 0,
116121 },
117122 {
118123 label: "Files",
@@ -121,6 +126,7 @@ function RightPanelEmptyState(props: { · 2 unmodified lines
121126 available: props.filesAvailable,
122127 disabledReason: SURFACE_DISABLED_REASONS.files,
123128 onClick: props.onAddFiles,
129+ badgeCount: 0,
124130 },
125131 {
126132 label: "Diff",
@@ -129,6 +135,7 @@ function RightPanelEmptyState(props: { · 2 unmodified lines
129135 available: props.diffAvailable,
130136 disabledReason: SURFACE_DISABLED_REASONS.diff,
131137 onClick: props.onAddDiff,
138+ badgeCount: 0,
132139 },
133140 {
134141 label: "Agents",
@@ -137,6 +144,7 @@ function RightPanelEmptyState(props: { · 2 unmodified lines
137144 available: true,
138145 disabledReason: null,
139146 onClick: props.onAddAgents,
147+ badgeCount: props.liveAgentCount,
140148 },
141149 ] as const;
142150
@@ -154,7 +162,17 @@ function RightPanelEmptyState(props: { · 11 unmodified lines
154162 const Icon = action.icon;
155163 const content = (
156164 <>
157− <Icon className="mb-3 size-5" />
165+ <span className="relative mb-3 inline-flex">
166+ <Icon className="size-5" />
167+ {action.badgeCount > 0 ? (
168+ <span
169+ aria-hidden
170+ className="absolute -top-1.5 -right-2 flex h-3.5 min-w-3.5 items-center justify-center rounded-full bg-info px-1 text-[9px] font-semibold tabular-nums text-white"
171+ >
172+ {action.badgeCount}
173+ </span>
174+ ) : null}
175+ </span>
158176 <span className="text-sm font-medium">{action.label}</span>
159177 <span className="mt-1 text-xs leading-relaxed text-muted-foreground">
160178 {action.description}
@@ -498,6 +516,7 @@ export function RightPanelTabs(props: RightPanelTabsProps) { · 337 unmodified lines
498516 browserAvailable={props.browserAvailable}
499517 diffAvailable={props.diffAvailable}
500518 filesAvailable={props.filesAvailable}
519+ liveAgentCount={props.liveAgentCount}
501520 />
502521 ) : (
503522 props.children
apps/web/src/components/chat/PanelLayoutControls.tsx+21−2
@@ -11,6 +11,8 @@ interface PanelLayoutControlsProps { · 10 unmodified lines
1111 rightPanelAvailable: boolean;
1212 rightPanelOpen: boolean;
1313 rightPanelShortcutLabel: string | null;
14+ /** Running + waiting subagents in this thread; badges the right panel toggle. */
15+ liveAgentCount: number;
1416 onToggleTerminal: () => void;
1517 onToggleRightPanel: () => void;
1618 }
@@ -22,6 +24,7 @@ export const PanelLayoutControls = memo(function PanelLayoutControls({ · 5 unmodified lines
2224 rightPanelAvailable,
2325 rightPanelOpen,
2426 rightPanelShortcutLabel,
27+ liveAgentCount,
2528 onToggleTerminal,
2629 onToggleRightPanel,
2730 }: PanelLayoutControlsProps) {
@@ -59,18 +62,34 @@ export const PanelLayoutControls = memo(function PanelLayoutControls({ · 31 unmodified lines
5962 className="shrink-0 [-webkit-app-region:no-drag]"
6063 pressed={rightPanelOpen}
6164 onPressedChange={onToggleRightPanel}
62− aria-label="Toggle right panel"
65+ aria-label={
66+ liveAgentCount > 0
67+ ? `Toggle right panel, ${liveAgentCount} ${liveAgentCount === 1 ? "agent" : "agents"} working`
68+ : "Toggle right panel"
69+ }
6370 variant="ghost"
6471 size="sm"
6572 disabled={!rightPanelAvailable}
6673 >
6774 <PanelRightIcon className="size-3.5" />
75+ {liveAgentCount > 0 ? (
76+ <span
77+ aria-hidden
78+ className="absolute -top-1 -right-1 flex h-3.5 min-w-3.5 items-center justify-center rounded-full bg-info px-1 text-[9px] font-semibold tabular-nums text-white"
79+ >
80+ {liveAgentCount}
81+ </span>
82+ ) : null}
6883 </Toggle>
6984 }
7085 />
7186 <TooltipPopup side="bottom">
7287 {rightPanelAvailable
73− ? `Toggle right panel${rightPanelShortcutLabel ? ` (${rightPanelShortcutLabel})` : ""}`
88+ ? `Toggle right panel${rightPanelShortcutLabel ? ` (${rightPanelShortcutLabel})` : ""}${
89+ liveAgentCount > 0
90+ ? ` · ${liveAgentCount} ${liveAgentCount === 1 ? "agent" : "agents"} working`
91+ : ""
92+ }`
7493 : "Right panel is unavailable"}
7594 </TooltipPopup>
7695 </Tooltip>