feat(FN-2932): improve active agent visibility and grouped tool-call UX
- Preserve and emphasize active agent card highlights across hover and modal states, with dedicated CSS updates and regression tests - Expand dashboard and TUI activity surfaces for grouped tool-call and live agent status/log visibility, including Quick Chat and chat view refinements - Add route and runtime updates for agent/settings flows plus self-healing coverage for merge recovery behavior - Include accompanying changesets and test stabilizations for mission activity assertions and active highlight hooks Fusion-Task-Id: FN-2932
This commit is contained in:
@@ -81,6 +81,11 @@
|
||||
background: var(--card-hover);
|
||||
}
|
||||
|
||||
.agent-list-modal .agent-card[data-state="active"]:hover,
|
||||
.agent-list-modal .agent-card[data-state="running"]:hover {
|
||||
background: color-mix(in srgb, var(--state-active-border) 20%, var(--card-hover));
|
||||
}
|
||||
|
||||
.agent-list-modal .agent-card:focus-within {
|
||||
box-shadow: var(--focus-ring);
|
||||
}
|
||||
@@ -150,7 +155,10 @@
|
||||
|
||||
.agent-list-modal .agent-board-card[data-state="idle"] { border-top-color: var(--state-idle-border); }
|
||||
.agent-list-modal .agent-board-card[data-state="active"],
|
||||
.agent-list-modal .agent-board-card[data-state="running"] { border-top-color: var(--state-active-border); }
|
||||
.agent-list-modal .agent-board-card[data-state="running"] {
|
||||
background: var(--state-active-bg);
|
||||
border-top-color: var(--state-active-border);
|
||||
}
|
||||
.agent-list-modal .agent-board-card[data-state="paused"] { border-top-color: var(--state-paused-border); }
|
||||
.agent-list-modal .agent-board-card[data-state="error"],
|
||||
.agent-list-modal .agent-board-card[data-state="terminated"] { border-top-color: var(--state-error-border); }
|
||||
@@ -209,7 +217,10 @@
|
||||
|
||||
.agent-list-modal .agent-card[data-state="idle"] { border-left-color: var(--state-idle-border); }
|
||||
.agent-list-modal .agent-card[data-state="active"],
|
||||
.agent-list-modal .agent-card[data-state="running"] { border-left-color: var(--state-active-border); }
|
||||
.agent-list-modal .agent-card[data-state="running"] {
|
||||
background: var(--state-active-bg);
|
||||
border-left-color: var(--state-active-border);
|
||||
}
|
||||
.agent-list-modal .agent-card[data-state="paused"] { border-left-color: var(--state-paused-border); }
|
||||
.agent-list-modal .agent-card[data-state="error"],
|
||||
.agent-list-modal .agent-card[data-state="terminated"] { border-left-color: var(--state-error-border); }
|
||||
|
||||
@@ -391,6 +391,11 @@
|
||||
border-color: var(--text-muted);
|
||||
}
|
||||
|
||||
.agent-board-card--active:hover,
|
||||
.agent-board-card--running:hover {
|
||||
background: color-mix(in srgb, var(--state-active-border) 20%, var(--card-hover));
|
||||
}
|
||||
|
||||
.agent-board-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -676,6 +681,11 @@
|
||||
border-left-color: var(--state-active-border);
|
||||
background: var(--state-active-bg);
|
||||
}
|
||||
|
||||
.agent-card--active:hover,
|
||||
.agent-card--running:hover {
|
||||
background: color-mix(in srgb, var(--state-active-border) 20%, var(--card-hover));
|
||||
}
|
||||
.agent-card--paused { border-left-color: var(--state-paused-border); }
|
||||
.agent-card--error,
|
||||
.agent-card--terminated { border-left-color: var(--state-error-border); }
|
||||
|
||||
@@ -201,6 +201,33 @@ describe("AgentListModal", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("marks active cards with data-state in list and board views", async () => {
|
||||
render(
|
||||
<AgentListModal
|
||||
isOpen={true}
|
||||
onClose={mockOnClose}
|
||||
addToast={mockAddToast}
|
||||
projectId={TEST_PROJECT_ID}
|
||||
/>
|
||||
);
|
||||
|
||||
const activeName = await screen.findByText("Test Agent 2");
|
||||
const activeListCard = activeName.closest(".agent-card");
|
||||
expect(activeListCard?.getAttribute("data-state")).toBe("active");
|
||||
|
||||
fireEvent.click(screen.getByTitle("Board view"));
|
||||
await waitFor(() => {
|
||||
expect(document.querySelector(".agent-board")).toBeTruthy();
|
||||
});
|
||||
|
||||
const activeBoardCard = Array.from(document.querySelectorAll(".agent-board-card")).find((card) =>
|
||||
card.textContent?.includes("Test Agent 2"),
|
||||
);
|
||||
expect(activeBoardCard?.getAttribute("data-state")).toBe("active");
|
||||
|
||||
localStorage.removeItem(AGENT_VIEW_KEY);
|
||||
});
|
||||
|
||||
it("shows terminated agents when explicitly filtered", async () => {
|
||||
render(
|
||||
<AgentListModal
|
||||
|
||||
@@ -342,40 +342,53 @@ describe("AgentsView", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it.each(["active", "running"] as const)("applies active highlight state classes across views for %s agents", async (state) => {
|
||||
const highlightAgent: Agent = {
|
||||
id: `agent-highlight-${state}`,
|
||||
name: `Highlight ${state}`,
|
||||
role: "executor",
|
||||
state,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
metadata: {},
|
||||
};
|
||||
describe("active agent card highlight", () => {
|
||||
it.each(["active", "running"] as const)("applies active highlight state classes across views for %s agents", async (state) => {
|
||||
const highlightAgent: Agent = {
|
||||
id: `agent-highlight-${state}`,
|
||||
name: `Highlight ${state}`,
|
||||
role: "executor",
|
||||
state,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
metadata: {},
|
||||
};
|
||||
|
||||
mockFetchAgents.mockResolvedValue([highlightAgent]);
|
||||
mockFetchAgentStats.mockResolvedValue({ total: 1, byState: { [state]: 1 }, byRole: { executor: 1 } });
|
||||
mockFetchOrgTree.mockResolvedValue([{ agent: highlightAgent, children: [] }]);
|
||||
mockFetchAgents.mockResolvedValue([highlightAgent]);
|
||||
mockFetchAgentStats.mockResolvedValue({ total: 1, byState: { [state]: 1 }, byRole: { executor: 1 } });
|
||||
mockFetchOrgTree.mockResolvedValue([{ agent: highlightAgent, children: [] }]);
|
||||
|
||||
render(<AgentsView addToast={mockAddToast} />);
|
||||
render(<AgentsView addToast={mockAddToast} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(document.querySelector(`.agent-card--${state}`)).toBeTruthy();
|
||||
await waitFor(() => {
|
||||
expect(document.querySelector(`.agent-card--${state}`)).toBeTruthy();
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "Board view" }));
|
||||
await waitFor(() => {
|
||||
expect(document.querySelector(`.agent-board-card--${state}`)).toBeTruthy();
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "Tree view" }));
|
||||
await waitFor(() => {
|
||||
expect(document.querySelector(`.agent-tree__node--${state}`)).toBeTruthy();
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "Org Chart view" }));
|
||||
await waitFor(() => {
|
||||
expect(document.querySelector(`.org-chart-node-card--${state}`)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "Board view" }));
|
||||
await waitFor(() => {
|
||||
expect(document.querySelector(`.agent-board-card--${state}`)).toBeTruthy();
|
||||
});
|
||||
it("keeps paused agents out of active highlight classes", async () => {
|
||||
render(<AgentsView addToast={mockAddToast} />);
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "Tree view" }));
|
||||
await waitFor(() => {
|
||||
expect(document.querySelector(`.agent-tree__node--${state}`)).toBeTruthy();
|
||||
});
|
||||
const pausedAgentCard = await screen.findByText("Test Agent 3");
|
||||
const pausedCard = pausedAgentCard.closest(".agent-card");
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "Org Chart view" }));
|
||||
await waitFor(() => {
|
||||
expect(document.querySelector(`.org-chart-node-card--${state}`)).toBeTruthy();
|
||||
expect(pausedCard).toBeTruthy();
|
||||
expect(pausedCard?.classList.contains("agent-card--paused")).toBe(true);
|
||||
expect(pausedCard?.classList.contains("agent-card--active")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -950,7 +950,7 @@ describe("MissionManager", () => {
|
||||
|
||||
fireEvent.click(screen.getByTestId("mission-activity-load-more"));
|
||||
|
||||
await screen.findByText("Mission event 65", undefined, { timeout: 10_000 });
|
||||
await screen.findByText("Mission event 51", undefined, { timeout: 10_000 });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId("mission-activity-load-more")).toBeNull();
|
||||
|
||||
Reference in New Issue
Block a user