feat(FN-3902): compact sidebar agent action labels to prevent overflow

Adds compact agent action labels to the sidebar in AgentsView, with CSS improvements and tests asserting the wrapper behavior, plus a changeset to release the change as a patch.

Fusion-Task-Id: FN-3902
This commit is contained in:
Fusion
2026-05-09 17:57:47 -07:00
committed by gsxdsm
parent 00b35b8ca5
commit eea4defc79
4 changed files with 50 additions and 9 deletions

View File

@@ -0,0 +1,5 @@
---
"@runfusion/fusion": patch
---
Fix agent sidebar action buttons (Run Now, Pause, Details) overflowing on narrow agent cards by collapsing them to icon-only controls in the sidebar context.

View File

@@ -709,6 +709,19 @@
white-space: nowrap;
}
.agents-split-sidebar .agent-card-actions {
gap: var(--space-xs);
}
.agents-split-sidebar .agent-card-actions .btn {
gap: var(--space-xs);
padding: var(--space-xs) var(--space-sm);
}
.agents-split-sidebar .agent-card-actions .agent-card-action-label {
display: none;
}
.agent-card-details-btn {
margin-left: auto;
}

View File

@@ -1375,7 +1375,7 @@ export function AgentsView({ addToast, projectId, onOpenTaskLogs, agentOnboardin
disabled={transitioningAgentIds.has(agent.id)}
title="Activate"
>
<Play size={14} /> Start
<Play size={14} /> <span className="agent-card-action-label">Start</span>
</button>
)}
{agent.state === "active" && (
@@ -1387,7 +1387,7 @@ export function AgentsView({ addToast, projectId, onOpenTaskLogs, agentOnboardin
title="Run Now"
aria-label={`Run now for ${agent.name}`}
>
<Activity size={14} /> Run Now
<Activity size={14} /> <span className="agent-card-action-label">Run Now</span>
</button>
<button
className="btn btn-sm"
@@ -1395,7 +1395,7 @@ export function AgentsView({ addToast, projectId, onOpenTaskLogs, agentOnboardin
disabled={transitioningAgentIds.has(agent.id)}
title="Pause"
>
<Pause size={14} /> Pause
<Pause size={14} /> <span className="agent-card-action-label">Pause</span>
</button>
</>
)}
@@ -1406,7 +1406,7 @@ export function AgentsView({ addToast, projectId, onOpenTaskLogs, agentOnboardin
disabled={transitioningAgentIds.has(agent.id)}
title="Resume"
>
<Play size={14} /> Resume
<Play size={14} /> <span className="agent-card-action-label">Resume</span>
</button>
)}
{agent.state === "running" && (
@@ -1417,7 +1417,7 @@ export function AgentsView({ addToast, projectId, onOpenTaskLogs, agentOnboardin
title="View live run details"
aria-label={`View live run details for ${agent.name}`}
>
<Activity size={14} /> Running
<Activity size={14} /> <span className="agent-card-action-label">Running</span>
</button>
<button
className="btn btn-sm"
@@ -1425,7 +1425,7 @@ export function AgentsView({ addToast, projectId, onOpenTaskLogs, agentOnboardin
disabled={transitioningAgentIds.has(agent.id)}
title="Pause"
>
<Pause size={14} /> Pause
<Pause size={14} /> <span className="agent-card-action-label">Pause</span>
</button>
</>
)}
@@ -1436,7 +1436,7 @@ export function AgentsView({ addToast, projectId, onOpenTaskLogs, agentOnboardin
disabled={transitioningAgentIds.has(agent.id)}
title="Retry"
>
<Play size={14} /> Retry
<Play size={14} /> <span className="agent-card-action-label">Retry</span>
</button>
)}
<button
@@ -1445,7 +1445,7 @@ export function AgentsView({ addToast, projectId, onOpenTaskLogs, agentOnboardin
title={`View details for ${agent.name}`}
aria-label={`View details for ${agent.name}`}
>
Details
<span className="agent-card-action-label">Details</span>
</button>
{(agent.state === "idle" || agent.state === "paused") && (
<button
@@ -1453,7 +1453,7 @@ export function AgentsView({ addToast, projectId, onOpenTaskLogs, agentOnboardin
onClick={() => void handleDelete(agent.id, agent.name)}
title="Delete"
>
<Trash2 size={14} /> Delete
<Trash2 size={14} /> <span className="agent-card-action-label">Delete</span>
</button>
)}
</div>

View File

@@ -1976,6 +1976,29 @@ describe("AgentsView", () => {
});
describe("Run Now button", () => {
it("renders sidebar action labels with wrapper spans for icon-only compaction (FN-3902)", async () => {
const activeWithoutTaskId = { ...mockAgents[1] };
delete activeWithoutTaskId.taskId;
mockFetchAgents.mockResolvedValue([
mockAgents[0],
activeWithoutTaskId,
mockAgents[2],
mockAgents[3],
]);
render(<AgentsView addToast={mockAddToast} />);
const runNowButton = await screen.findByTitle("Run Now");
const pauseButton = await screen.findByTitle("Pause");
const activeCard = runNowButton.closest(".agent-card");
const detailsButton = activeCard?.querySelector('[aria-label^="View details for "]') as HTMLButtonElement | null;
expect(detailsButton).toBeTruthy();
expect(runNowButton.querySelector(".agent-card-action-label")).toBeTruthy();
expect(pauseButton.querySelector(".agent-card-action-label")).toBeTruthy();
expect(detailsButton?.querySelector(".agent-card-action-label")).toBeTruthy();
});
it("shows Run Now button for active agent without taskId", async () => {
const activeWithoutTaskId = { ...mockAgents[1] };
delete activeWithoutTaskId.taskId;