Fix workspace composer focus routing
This commit is contained in:
@@ -7,7 +7,7 @@ import { createRoot, type Root } from "react-dom/client";
|
|||||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||||
import { DaemonClient } from "@server/client/daemon-client";
|
import { DaemonClient } from "@server/client/daemon-client";
|
||||||
import { PaneFocusProvider, PaneProvider } from "@/panels/pane-context";
|
import { PaneFocusProvider, PaneProvider, type PaneFocusContextValue } from "@/panels/pane-context";
|
||||||
import { agentPanelRegistration } from "@/panels/agent-panel";
|
import { agentPanelRegistration } from "@/panels/agent-panel";
|
||||||
import { useDraftStore } from "@/stores/draft-store";
|
import { useDraftStore } from "@/stores/draft-store";
|
||||||
import { useSessionStore, type Agent } from "@/stores/session-store";
|
import { useSessionStore, type Agent } from "@/stores/session-store";
|
||||||
@@ -39,6 +39,7 @@ const {
|
|||||||
composerRenderCount,
|
composerRenderCount,
|
||||||
composerUnmountCount,
|
composerUnmountCount,
|
||||||
latestComposerCwd,
|
latestComposerCwd,
|
||||||
|
latestComposerIsPaneFocused,
|
||||||
streamRenderCount,
|
streamRenderCount,
|
||||||
latestStreamPermissionKeys,
|
latestStreamPermissionKeys,
|
||||||
latestStreamText,
|
latestStreamText,
|
||||||
@@ -66,6 +67,7 @@ const {
|
|||||||
composerRenderCount: vi.fn(),
|
composerRenderCount: vi.fn(),
|
||||||
composerUnmountCount: vi.fn(),
|
composerUnmountCount: vi.fn(),
|
||||||
latestComposerCwd: { current: null as string | null },
|
latestComposerCwd: { current: null as string | null },
|
||||||
|
latestComposerIsPaneFocused: { current: null as boolean | null },
|
||||||
streamRenderCount: vi.fn(),
|
streamRenderCount: vi.fn(),
|
||||||
latestStreamPermissionKeys: { current: [] as string[] },
|
latestStreamPermissionKeys: { current: [] as string[] },
|
||||||
latestStreamText: { current: null as string | null },
|
latestStreamText: { current: null as string | null },
|
||||||
@@ -166,7 +168,7 @@ vi.mock("@/hooks/use-archive-agent", () => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock("@/components/composer", () => ({
|
vi.mock("@/components/composer", () => ({
|
||||||
Composer: ({ cwd }: { cwd: string }) => {
|
Composer: ({ cwd, isPaneFocused }: { cwd: string; isPaneFocused: boolean }) => {
|
||||||
React.useEffect(
|
React.useEffect(
|
||||||
() => () => {
|
() => () => {
|
||||||
composerUnmountCount();
|
composerUnmountCount();
|
||||||
@@ -175,6 +177,7 @@ vi.mock("@/components/composer", () => ({
|
|||||||
);
|
);
|
||||||
composerRenderCount();
|
composerRenderCount();
|
||||||
latestComposerCwd.current = cwd;
|
latestComposerCwd.current = cwd;
|
||||||
|
latestComposerIsPaneFocused.current = isPaneFocused;
|
||||||
return <div data-testid="composer">{cwd}</div>;
|
return <div data-testid="composer">{cwd}</div>;
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
@@ -244,7 +247,14 @@ function seedReadyAgent(agent: Agent = makeAgent()) {
|
|||||||
store.setAgentAuthoritativeHistoryApplied("server", "agent", true);
|
store.setAgentAuthoritativeHistoryApplied("server", "agent", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function renderAgentPanel(root: Root) {
|
async function renderAgentPanel(
|
||||||
|
root: Root,
|
||||||
|
focus: PaneFocusContextValue = {
|
||||||
|
isWorkspaceFocused: true,
|
||||||
|
isPaneFocused: false,
|
||||||
|
isInteractive: false,
|
||||||
|
},
|
||||||
|
) {
|
||||||
const AgentPanel = agentPanelRegistration.component;
|
const AgentPanel = agentPanelRegistration.component;
|
||||||
const queryClient = new QueryClient({
|
const queryClient = new QueryClient({
|
||||||
defaultOptions: {
|
defaultOptions: {
|
||||||
@@ -267,9 +277,7 @@ async function renderAgentPanel(root: Root) {
|
|||||||
openFileInWorkspace: vi.fn(),
|
openFileInWorkspace: vi.fn(),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<PaneFocusProvider
|
<PaneFocusProvider value={focus}>
|
||||||
value={{ isWorkspaceFocused: true, isPaneFocused: false, isInteractive: false }}
|
|
||||||
>
|
|
||||||
<AgentPanel />
|
<AgentPanel />
|
||||||
</PaneFocusProvider>
|
</PaneFocusProvider>
|
||||||
</PaneProvider>
|
</PaneProvider>
|
||||||
@@ -355,6 +363,7 @@ describe("AgentPanel render isolation", () => {
|
|||||||
composerRenderCount.mockClear();
|
composerRenderCount.mockClear();
|
||||||
composerUnmountCount.mockClear();
|
composerUnmountCount.mockClear();
|
||||||
latestComposerCwd.current = null;
|
latestComposerCwd.current = null;
|
||||||
|
latestComposerIsPaneFocused.current = null;
|
||||||
streamRenderCount.mockClear();
|
streamRenderCount.mockClear();
|
||||||
latestStreamPermissionKeys.current = [];
|
latestStreamPermissionKeys.current = [];
|
||||||
latestStreamText.current = null;
|
latestStreamText.current = null;
|
||||||
@@ -379,6 +388,21 @@ describe("AgentPanel render isolation", () => {
|
|||||||
expect(composerRenderCount).toHaveBeenCalledTimes(composerBaseline);
|
expect(composerRenderCount).toHaveBeenCalledTimes(composerBaseline);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("does not advertise the composer as focused when its workspace is hidden", async () => {
|
||||||
|
seedReadyAgent();
|
||||||
|
container = document.createElement("div");
|
||||||
|
document.body.appendChild(container);
|
||||||
|
root = createRoot(container);
|
||||||
|
|
||||||
|
await renderAgentPanel(root, {
|
||||||
|
isWorkspaceFocused: false,
|
||||||
|
isPaneFocused: true,
|
||||||
|
isInteractive: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(latestComposerIsPaneFocused.current).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
it("still invokes Composer for current-agent cwd changes and unmounts it for archives", async () => {
|
it("still invokes Composer for current-agent cwd changes and unmounts it for archives", async () => {
|
||||||
seedReadyAgent();
|
seedReadyAgent();
|
||||||
container = document.createElement("div");
|
container = document.createElement("div");
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ function useAgentPanelDescriptor(
|
|||||||
|
|
||||||
function AgentPanel() {
|
function AgentPanel() {
|
||||||
const { serverId, target, openFileInWorkspace } = usePaneContext();
|
const { serverId, target, openFileInWorkspace } = usePaneContext();
|
||||||
const { isPaneFocused } = usePaneFocus();
|
const { isInteractive } = usePaneFocus();
|
||||||
invariant(target.kind === "agent", "AgentPanel requires agent target");
|
invariant(target.kind === "agent", "AgentPanel requires agent target");
|
||||||
|
|
||||||
function openWorkspaceFile(input: { filePath: string }) {
|
function openWorkspaceFile(input: { filePath: string }) {
|
||||||
@@ -126,7 +126,7 @@ function AgentPanel() {
|
|||||||
<AgentPanelContent
|
<AgentPanelContent
|
||||||
serverId={serverId}
|
serverId={serverId}
|
||||||
agentId={target.agentId}
|
agentId={target.agentId}
|
||||||
isPaneFocused={isPaneFocused}
|
isPaneFocused={isInteractive}
|
||||||
onOpenWorkspaceFile={handleOpenWorkspaceFile}
|
onOpenWorkspaceFile={handleOpenWorkspaceFile}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user