- #151: chevauchement des boutons dans la barre supérieure pendant une conversation agent - #158: popup de reprise de conversation intempestive (ajout cellule / changement layout / projet) - #159: pastille d'activité de projet
This commit is contained in:
@ -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<GridSetup> {
|
||||
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<GridSetup> {
|
||||
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<CellAction>(["selector", "split", "close"])(
|
||||
"keeps %s above/clickable over a terminal launch error",
|
||||
async (action) => {
|
||||
|
||||
@ -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,
|
||||
}}
|
||||
>
|
||||
<option value="">Plain</option>
|
||||
@ -1079,6 +1090,7 @@ function LeafView({
|
||||
border: "1px solid var(--color-border, #3a3a3a)",
|
||||
borderRadius: 3,
|
||||
background: "var(--color-surface, #1e1e1e)",
|
||||
maxWidth: 128,
|
||||
}}
|
||||
>
|
||||
<button
|
||||
@ -1100,6 +1112,10 @@ function LeafView({
|
||||
fontSize: 11,
|
||||
padding: "1px 6px",
|
||||
cursor: "pointer",
|
||||
maxWidth: 66,
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
>
|
||||
TUI native
|
||||
@ -1122,6 +1138,10 @@ function LeafView({
|
||||
fontSize: 11,
|
||||
padding: "1px 6px",
|
||||
cursor: "pointer",
|
||||
maxWidth: 70,
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
>
|
||||
CLI custom
|
||||
|
||||
@ -313,4 +313,40 @@ describe("LayoutGrid — resume popup wiring (T7)", () => {
|
||||
expect(screen.queryByTestId("resume-conversation-popup")).toBeNull();
|
||||
expect(inspectConversation).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not show the popup for a remounted cell whose agent was still running (#158)", async () => {
|
||||
const layout = new MockLayoutGateway();
|
||||
const agent = new MockAgentGateway();
|
||||
const a = await agent.createAgent("p1", { name: "MyAgent", profileId: "p" });
|
||||
const leafId = await seedResumeLeaf(layout, a.id);
|
||||
await layout.mutateLayout("p1", {
|
||||
type: "setAgentRunning",
|
||||
target: leafId,
|
||||
running: true,
|
||||
});
|
||||
|
||||
const launchAgent = vi.fn(
|
||||
async (
|
||||
_p: string,
|
||||
_ag: string,
|
||||
_opts: OpenTerminalOptions,
|
||||
_onData: (b: Uint8Array) => void,
|
||||
) => makeHandle("sess-running-resume"),
|
||||
);
|
||||
const inspectConversation = vi.fn();
|
||||
const stubAgent = {
|
||||
listAgents: () => Promise.resolve([a]),
|
||||
launchAgent,
|
||||
reattach: vi.fn(),
|
||||
inspectConversation,
|
||||
};
|
||||
|
||||
renderGrid(layout, stubAgent);
|
||||
|
||||
await waitFor(() => expect(launchAgent).toHaveBeenCalledTimes(1));
|
||||
expect(screen.queryByTestId("resume-conversation-popup")).toBeNull();
|
||||
expect(inspectConversation).not.toHaveBeenCalled();
|
||||
const opts = launchAgent.mock.calls[0][2] as OpenTerminalOptions;
|
||||
expect(opts.conversationId).toBe("prior-conv");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user