merge(layout): retrait des affichages Busy et Cancel de la cellule agent — #277 (QA verte: 25/25 tests LayoutGrid ciblés ré-exécutés avant merge; branche fix/277-agent-cell-busy-cancel)

This commit is contained in:
2026-09-08 12:14:36 +02:00
3 changed files with 13 additions and 86 deletions

View File

@ -243,10 +243,10 @@ describe("LayoutGrid — ticket #48 cell control layering", () => {
expect(screen.getByLabelText(`split ${setup.bId} columns`)).toBeTruthy(); expect(screen.getByLabelText(`split ${setup.bId} columns`)).toBeTruthy();
expect(screen.getByLabelText(`split ${setup.bId} rows`)).toBeTruthy(); expect(screen.getByLabelText(`split ${setup.bId} rows`)).toBeTruthy();
expect(screen.getByLabelText(`close ${setup.bId}`)).toBeTruthy(); expect(screen.getByLabelText(`close ${setup.bId}`)).toBeTruthy();
expect(screen.getByRole("button", { expect(screen.queryByRole("button", {
name: `cancel current turn ${setup.bId}`, name: `cancel current turn ${setup.bId}`,
})).toBeTruthy(); })).toBeNull();
expect(screen.getByText("Busy")).toBeTruthy(); expect(screen.queryByText("Busy")).toBeNull();
fireEvent.click(screen.getByLabelText(`split ${setup.bId} columns`)); fireEvent.click(screen.getByLabelText(`split ${setup.bId} columns`));
await waitFor(() => await waitFor(() =>
@ -276,7 +276,7 @@ describe("LayoutGrid — ticket #48 cell control layering", () => {
); );
}); });
it("keeps busy Cancel above persistent cell controls and removes History (#151, #160)", async () => { it("hides busy controls while keeping persistent cell controls and History removed (#277)", async () => {
const setup = await makeSplitAgentGrid(); const setup = await makeSplitAgentGrid();
await setup.layout.mutateLayout("p1", { await setup.layout.mutateLayout("p1", {
type: "setCellConversation", type: "setCellConversation",
@ -297,24 +297,13 @@ describe("LayoutGrid — ticket #48 cell control layering", () => {
renderGrid(setup.gateways, { onOpenConversation: vi.fn() }); renderGrid(setup.gateways, { onOpenConversation: vi.fn() });
await flush(); await flush();
act(() => setup.system.emit(busy(setup.agentId, true))); expect(screen.queryByRole("button", {
const cancel = await screen.findByRole("button", {
name: `cancel current turn ${setup.bId}`, name: `cancel current turn ${setup.bId}`,
}); })).toBeNull();
const status = screen expect(screen.queryByText("Busy")).toBeNull();
.getAllByRole("status")
.find((element) => element.textContent === "Busy") as HTMLElement;
const { controls } = controlsFor(setup.bId); const { controls } = controlsFor(setup.bId);
expect(status).toBeTruthy(); expect(controls.style.zIndex).toBe("5");
expect(cancel.style.position).toBe("absolute");
expect(status.style.top).toBe("24px");
expect(status.style.zIndex).toBe("3");
expect(cancel.style.top).toBe("24px");
expect(cancel.style.right).toBe("20px");
expect(cancel.style.zIndex).toBe("6");
expect(Number(cancel.style.zIndex)).toBeGreaterThan(Number(controls.style.zIndex));
expect(screen.queryByRole("button", { expect(screen.queryByRole("button", {
name: `open cell conversation ${setup.bId}`, name: `open cell conversation ${setup.bId}`,
})).toBeNull(); })).toBeNull();

View File

@ -219,11 +219,11 @@ describe("LayoutGrid custom agent CLI (#147)", () => {
expect(await screen.findByTestId("custom-agent-chat-view")).toBeTruthy(); expect(await screen.findByTestId("custom-agent-chat-view")).toBeTruthy();
expect(screen.queryByTestId("target-announcements-overlay")).toBeNull(); expect(screen.queryByTestId("target-announcements-overlay")).toBeNull();
expect(screen.getByText("Busy · Main")).toBeTruthy(); expect(screen.queryByText("Busy · Main")).toBeNull();
expect(screen.getByLabelText(`agent selector ${leaf.id}`)).toBeTruthy(); expect(screen.getByLabelText(`agent selector ${leaf.id}`)).toBeTruthy();
expect(screen.getByRole("button", { expect(screen.queryByRole("button", {
name: `cancel current turn ${leaf.id}`, name: `cancel current turn ${leaf.id}`,
})).toBeTruthy(); })).toBeNull();
await waitFor(() => await waitFor(() =>
expect(screen.getByLabelText(/message CLI custom/)).toHaveProperty( expect(screen.getByLabelText(/message CLI custom/)).toHaveProperty(
"disabled", "disabled",

View File

@ -395,7 +395,6 @@ function StoppedAgentCellPanel({
* status/error 3 (in-cell status strip, launch-error banner, notices) * status/error 3 (in-cell status strip, launch-error banner, notices)
* full-cell veils 4 (write-portal + F3 overlays; mutually exclusive) * full-cell veils 4 (write-portal + F3 overlays; mutually exclusive)
* cell controls 5 (always on top and clickable) * cell controls 5 (always on top and clickable)
* turn actions 6 (busy-state actions; Cancel must stay visible)
* *
* The terminal/chat body must create its own z-index 0 stacking context. Custom * The terminal/chat body must create its own z-index 0 stacking context. Custom
* CLI views can legitimately contain local high-z children (for example their * CLI views can legitimately contain local high-z children (for example their
@ -405,7 +404,6 @@ const CELL_Z = {
banner: 3, banner: 3,
veil: 4, veil: 4,
controls: 5, controls: 5,
turnActions: 6,
} as const; } as const;
const CUSTOM_CHAT_COMPOSER_DEFAULT_HEIGHT = 64; const CUSTOM_CHAT_COMPOSER_DEFAULT_HEIGHT = 64;
@ -446,7 +444,6 @@ function LeafView({
const siblingIndex = parentSplit ? (parentSplit.index === 0 ? 1 : 0) : 0; const siblingIndex = parentSplit ? (parentSplit.index === 0 ? 1 : 0) : 0;
const { const {
agent: agentGateway, agent: agentGateway,
input,
profile: profileGateway, profile: profileGateway,
system, system,
terminal, terminal,
@ -812,16 +809,6 @@ function LeafView({
} }
}, [agentId, freshRestartedSession, liveAgents]); }, [agentId, freshRestartedSession, liveAgents]);
async function interruptCurrentTurn(): Promise<void> {
if (!agentId || !input) return;
try {
await input.interrupt(projectId, agentId);
await refreshWorkState();
} catch (err) {
setBusyNotice({ message: describeNotice(err) });
}
}
function requestMode(target: AgentCellMode): void { function requestMode(target: AgentCellMode): void {
if (!effectiveCustomCliAvailable || target === cellMode) return; if (!effectiveCustomCliAvailable || target === cellMode) return;
if (session) setPendingModeSwitch({ target }); if (session) setPendingModeSwitch({ target });
@ -1323,33 +1310,7 @@ function LeafView({
</button> </button>
)} )}
</div> </div>
{agentId && busyByWorkState && ( {agentId && (inboxDepth > 0 || completedBackgroundTasks.length > 0) && (
<button
type="button"
aria-label={`cancel current turn ${id}`}
title="Cancel current turn"
onClick={() => void interruptCurrentTurn()}
disabled={!input}
style={{
position: "absolute",
top: 24,
right: 20,
zIndex: CELL_Z.turnActions,
flexShrink: 0,
border: "1px solid var(--color-danger, #d45a5a)",
borderRadius: 3,
background: "var(--color-surface, #1e1e1e)",
color: "var(--color-danger, #d45a5a)",
cursor: input ? "pointer" : "default",
fontSize: 11,
padding: "1px 6px",
pointerEvents: "auto",
}}
>
Cancel
</button>
)}
{agentId && (busyByWorkState || inboxDepth > 0 || completedBackgroundTasks.length > 0) && (
<div <div
role="status" role="status"
aria-live="polite" aria-live="polite"
@ -1357,38 +1318,15 @@ function LeafView({
position: "absolute", position: "absolute",
top: 24, top: 24,
left: 4, left: 4,
right: busyByWorkState ? 76 : undefined,
zIndex: CELL_Z.banner, zIndex: CELL_Z.banner,
display: "flex", display: "flex",
maxWidth: busyByWorkState ? undefined : "calc(100% - 8px)", maxWidth: "calc(100% - 8px)",
alignItems: "center", alignItems: "center",
gap: 4, gap: 4,
overflow: "hidden", overflow: "hidden",
pointerEvents: "auto", pointerEvents: "auto",
}} }}
> >
{busyByWorkState && (
<span
title={delegatedTicket?.requesterLabel ?? activeTicket?.taskPreview}
style={{
maxWidth: 180,
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
border: "1px solid var(--color-warning, #d49b3a)",
borderRadius: 3,
background: "rgba(212, 155, 58, 0.16)",
color: "var(--color-warning, #d49b3a)",
padding: "1px 6px",
fontSize: 11,
fontWeight: 600,
}}
>
{delegatedTicket
? `Busy · ${delegatedTicket.requesterLabel}`
: "Busy"}
</span>
)}
{!busyByWorkState && inboxDepth > 0 && ( {!busyByWorkState && inboxDepth > 0 && (
<span <span
title={`${inboxDepth} pending message${inboxDepth === 1 ? "" : "s"}`} title={`${inboxDepth} pending message${inboxDepth === 1 ? "" : "s"}`}