merge feature/frontend-mcp-panel-terminal-refit-fixes dans develop (panneau permissions idea_ask_agents + fit terminal au remount projet)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-28 22:51:53 +02:00
5 changed files with 73 additions and 3 deletions

View File

@ -2462,6 +2462,7 @@ export class MockPermissionGateway implements PermissionGateway {
],
writeActionTools: [
"idea_ask_agent",
"idea_ask_agents",
"idea_run_in_background",
"idea_launch_agent",
"idea_stop_agent",

View File

@ -108,6 +108,19 @@ describe("PermissionsPanel — Tools MCP IdeA tab", () => {
expect(screen.getByRole("button", { name: "Enregistrer" })).toHaveProperty("disabled", true);
});
it("shows multi-agent delegation in the Délégation agents MCP group", async () => {
await renderPanel();
await openMcpToolsTab();
const delegationGroup = screen.getByRole("button", {
name: /Délégation agents/,
});
expect(delegationGroup).toBeTruthy();
expect(screen.getByRole("checkbox", {
name: /Solliciter plusieurs agents, idea_ask_agents$/,
})).toBeTruthy();
});
it("creating an override prefills the draft with the current effective allowlist", async () => {
await renderPanel();
await openMcpToolsTab();

View File

@ -39,7 +39,12 @@ export const MCP_TOOL_GROUPS: McpToolGroupDef[] = [
{
id: "delegation",
label: "Délégation agents",
tools: ["idea_ask_agent", "idea_launch_agent", "idea_stop_agent"],
tools: [
"idea_ask_agent",
"idea_ask_agents",
"idea_launch_agent",
"idea_stop_agent",
],
},
{
id: "contextMemory",
@ -83,6 +88,7 @@ export const MCP_TOOL_LABELS: Record<string, string> = {
idea_ticket_read_carnet: "Lire le carnet d'un ticket",
idea_sprint_list: "Lister les sprints",
idea_ask_agent: "Déléguer à un agent",
idea_ask_agents: "Solliciter plusieurs agents",
idea_launch_agent: "Lancer un agent",
idea_stop_agent: "Arrêter un agent",
idea_update_context: "Modifier le contexte d'un agent",

View File

@ -455,6 +455,52 @@ describe("TerminalView — visible launch-failure surface (ticket #14 F3)", () =
fitSpy.mockRestore();
});
it("keeps fitting across a keyed project remount until the new cell settles", async () => {
// ProjectsView keys LayoutGrid by project. A project switch therefore
// unmounts/remounts TerminalView, so the mount path itself must keep a
// short tail of fits alive while the new cell geometry settles.
const fitSpy = vi.spyOn(FitAddon.prototype, "fit");
const firstHandle = makeHandle({ sessionId: "project-1" });
const secondHandle = makeHandle({ sessionId: "project-2" });
const open = vi
.fn()
.mockResolvedValueOnce(firstHandle)
.mockResolvedValueOnce(secondHandle);
function ProjectTerminal({ projectId }: { projectId: string }) {
return (
<DIProvider gateways={{ terminal: new MockTerminalGateway() } as unknown as Gateways}>
<TerminalView
key={projectId}
cwd={`/cwd/${projectId}`}
open={open}
/>
</DIProvider>
);
}
const { rerender } = render(<ProjectTerminal projectId="p1" />);
await waitFor(() => expect(open).toHaveBeenCalledTimes(1));
setTerminalBoxSize(400, 200);
await waitFor(() => expect(firstHandle.resize).toHaveBeenCalled());
fitSpy.mockClear();
rerender(<ProjectTerminal projectId="p2" />);
await waitFor(() => expect(open).toHaveBeenCalledTimes(2));
setTerminalBoxSize(520, 260);
for (let i = 0; i < 8; i += 1) {
await new Promise((resolve) => requestAnimationFrame(resolve));
}
expect(fitSpy.mock.calls.length).toBeGreaterThan(5);
await waitFor(() => expect(secondHandle.resize).toHaveBeenCalled());
expect(firstHandle.close).not.toHaveBeenCalled();
expect(secondHandle.close).not.toHaveBeenCalled();
fitSpy.mockRestore();
});
it("does not refit when refitSignal is left undefined (no-op for callers that don't pass it)", async () => {
const fitSpy = vi.spyOn(FitAddon.prototype, "fit");
const open = vi.fn(async () => makeHandle({ sessionId: "no-signal-1" }));

View File

@ -368,7 +368,11 @@ export function TerminalView({
// manual resize. This stays bounded and preserves the rows/cols-changed
// guard before touching the PTY.
const SETTLE_REFIT_FRAMES = 4;
const MAX_ZERO_SIZE_RETRIES = 8;
// Project switches remount LayoutGrid/TerminalView by key. The new cell can
// report a non-zero but still intermediate box for more than the ordinary
// transition tail, so the mount path keeps fitting a little longer.
const MOUNT_SETTLE_REFIT_FRAMES = 12;
const MAX_ZERO_SIZE_RETRIES = MOUNT_SETTLE_REFIT_FRAMES;
let zeroSizeRetries = 0;
const refit = () => {
rafId = 0;
@ -418,7 +422,7 @@ export function TerminalView({
};
const ro = new ResizeObserver(() => scheduleRefit());
ro.observe(container);
scheduleSettledRefit();
scheduleRefit(MOUNT_SETTLE_REFIT_FRAMES);
window.addEventListener("resize", scheduleSettledRefit);
window.addEventListener("focus", scheduleSettledRefit);
window.addEventListener("pageshow", scheduleSettledRefit);