fix(site): scope chevron to icon hover when any child is running (#22456)

Follow-up to #22452. The previous fix only checked the chat's own
status, so a root chat in `waiting` status with actively running
sub-agents still showed the expand/collapse chevron on full-row hover.

## Problem

A root chat that's idle (`waiting`/`completed`) but has running
sub-agents would still swap its status icon for the `>` chevron on row
hover. The fix in #22452 only gated on `chat.status` being
`pending`/`running`, which doesn't cover the parent when sub-agents are
the ones executing.

## Fix

`isExecuting` now also checks whether **any direct child** is
`pending`/`running`:

```ts
const isExecuting =
  chat.status === "pending" ||
  chat.status === "running" ||
  (hasChildren &&
    childIDs.some((id) => {
      const c = chatById.get(id);
      return c?.status === "pending" || c?.status === "running";
    }));
```

When `isExecuting` is true, the chevron only appears on hover of the
icon area itself (`group-hover/icon`), not the entire row.

## New story

Added `IdleParentWithRunningChild` — verifies a `waiting` parent with a
`running` child uses icon-only hover scope for the toggle.

Co-authored-by: Coder <coder@users.noreply.github.com>
This commit is contained in:
Kyle Carberry
2026-03-02 09:20:46 -05:00
committed by GitHub
co-authored by Coder
parent 80688ec221
commit ebe8c8a5b4
2 changed files with 48 additions and 1 deletions
@@ -240,6 +240,46 @@ export const RunningChatPreservesSpinner: Story = {
},
};
// When a root chat is idle but has a running child, the chevron
// should still be scoped to the icon area hover, not the full row.
export const IdleParentWithRunningChild: Story = {
args: {
chats: [
buildChat({
id: "idle-parent",
title: "Idle parent agent",
status: "waiting",
}),
buildChat({
id: "running-child",
title: "Running sub-agent",
status: "running",
parent_chat_id: "idle-parent",
root_chat_id: "idle-parent",
}),
],
},
parameters: {
reactRouter: reactRouterParameters({
location: {
path: "/agents/running-child",
pathParams: { agentId: "running-child" },
},
routing: agentsRouting,
}),
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
// The parent's toggle should use icon-only hover scope because
// its child is actively running.
const toggle = canvas.getByTestId("agents-tree-toggle-idle-parent");
await expect(toggle).toBeInTheDocument();
await expect(toggle.className).toMatch(/\binvisible\b/);
await expect(toggle.className).toContain("group-hover/icon:visible");
},
};
export const ActiveChatAncestryExpanded: Story = {
args: {
chats: [
+8 -1
View File
@@ -327,7 +327,14 @@ const ChatTreeNode = memo<ChatTreeNodeProps>(({ chat, isChildNode }) => {
}`;
const isArchivingThisChat = isArchiving && archivingChatId === chat.id;
const isExpanded = normalizedSearch ? true : (expandedById[chatID] ?? false);
const isExecuting = chat.status === "pending" || chat.status === "running";
const isExecuting =
chat.status === "pending" ||
chat.status === "running" ||
(hasChildren &&
childIDs.some((id) => {
const c = chatById.get(id);
return c?.status === "pending" || c?.status === "running";
}));
return (
<div className="flex min-w-0 flex-col">