diff --git a/frontend/src/features/layout/LayoutGrid.cellControlsLayering.test.tsx b/frontend/src/features/layout/LayoutGrid.cellControlsLayering.test.tsx index fbbdeec..887a09c 100644 --- a/frontend/src/features/layout/LayoutGrid.cellControlsLayering.test.tsx +++ b/frontend/src/features/layout/LayoutGrid.cellControlsLayering.test.tsx @@ -48,8 +48,10 @@ if (typeof globalThis.ResizeObserver === "undefined") { import type { DomainEvent } from "@/domain"; import type { Gateways } from "@/ports"; import { + MOCK_REFERENCE_PROFILES, MockAgentGateway, MockLayoutGateway, + MockProfileGateway, MockSystemGateway, MockTerminalGateway, } from "@/adapters/mock"; @@ -69,12 +71,14 @@ interface GridSetup { async function makeSplitAgentGrid(): Promise { const layout = new MockLayoutGateway(); const agentGateway = new MockAgentGateway(); + const profileGateway = new MockProfileGateway(); const terminal = new MockTerminalGateway(); const system = new MockSystemGateway(); + await profileGateway.saveProfile(MOCK_REFERENCE_PROFILES[0]); const agent = await agentGateway.createAgent("p1", { name: "Worker", - profileId: "claude", + profileId: "mock-claude", }); const initial = await layout.loadLayout("p1"); @@ -98,6 +102,7 @@ async function makeSplitAgentGrid(): Promise { gateways: { layout, agent: agentGateway, + profile: profileGateway, terminal, system, } as unknown as Gateways, @@ -174,6 +179,24 @@ function expectedOperation(action: CellAction, cellId: string) { } describe("LayoutGrid — ticket #48 cell control layering", () => { + it("keeps custom CLI mode controls compact inside the cell toolbar (#151)", async () => { + const setup = await makeSplitAgentGrid(); + + renderGrid(setup.gateways); + + const group = await screen.findByRole("group", { + name: `mode CLI agent ${setup.bId}`, + }); + const { controls } = controlsFor(setup.bId); + + expect(group.textContent).toBe("TUI nativeCLI custom"); + expect(group.style.maxWidth).toBe("128px"); + expect(controls.style.left).toBe("4px"); + expect(controls.style.right).toBe("16px"); + expect(controls.style.justifyContent).toBe("flex-end"); + expect(controls.style.overflow).toBe("hidden"); + }); + it.each(["selector", "split", "close"])( "keeps %s above/clickable over a terminal launch error", async (action) => { diff --git a/frontend/src/features/layout/LayoutGrid.tsx b/frontend/src/features/layout/LayoutGrid.tsx index a00a4cb..0a0f02b 100644 --- a/frontend/src/features/layout/LayoutGrid.tsx +++ b/frontend/src/features/layout/LayoutGrid.tsx @@ -922,6 +922,13 @@ function LeafView({ if (!conversationId) { return doLaunch(opts, onData, undefined); } + // If the cell was closed/remounted while the agent was running, resume + // silently. Layout/project changes must not interrupt an ongoing turn + // with a modal choice; the persisted conversation id is the continuity + // signal and `agentWasRunning` tells us this was not an intentional stop. + if (agentWasRunning) { + return doLaunch(opts, onData, conversationId); + } // Resume case: defer the launch behind the popup. Fetch the best-effort // enriched details (last topic + tokens) to enrich it; failure or empty // ⇒ degraded mode (status only). Inspection never blocks the resume. @@ -982,6 +989,7 @@ function LeafView({ style={{ position: "absolute", top: 2, + left: 4, // Clear the xterm viewport scrollbar (rendered flush against the right // edge, ~15px wide). Without this offset the right-most control — the // close button — sits *behind* the scrollbar and is hard to click. @@ -992,6 +1000,8 @@ function LeafView({ display: "flex", gap: 2, alignItems: "center", + justifyContent: "flex-end", + overflow: "hidden", }} > {/* Agent selector */} @@ -1053,7 +1063,8 @@ function LeafView({ border: "1px solid var(--color-border, #3a3a3a)", borderRadius: 3, padding: "1px 2px", - maxWidth: 100, + width: 96, + minWidth: 56, }} > @@ -1079,6 +1090,7 @@ function LeafView({ border: "1px solid var(--color-border, #3a3a3a)", borderRadius: 3, background: "var(--color-surface, #1e1e1e)", + maxWidth: 128, }} > {onClose && (