fix: focus canvas after agent writeback
This commit is contained in:
parent
5e82adb7eb
commit
ad6b09d1b2
@ -73,6 +73,7 @@ import {
|
||||
resolveCanvasAgentSlotCreationIntent,
|
||||
type CanvasAgentModelPlannerOutcome,
|
||||
} from "@/lib/canvas/agent-model-planner";
|
||||
import { buildCanvasAgentResultViewOperation } from "@/lib/canvas/agent-result-view";
|
||||
import {
|
||||
getCanvasAgentOperationErrorMessage,
|
||||
normalizeCanvasAgentOperationErrorCode,
|
||||
@ -3056,6 +3057,23 @@ export async function POST(request: Request) {
|
||||
},
|
||||
`${params.operationLabel}:canvas-operation`,
|
||||
);
|
||||
if (canvasAgentFeatures.viewOperationEnabled) {
|
||||
const viewOperation = buildCanvasAgentResultViewOperation({
|
||||
projectId,
|
||||
operationId,
|
||||
mutationOperation: operation,
|
||||
createdNodeIds: result.createdNodeIds,
|
||||
updatedNodeIds: result.updatedNodeIds,
|
||||
anchorNodeId: result.createdNodeIds[0] || result.updatedNodeIds[0] || null,
|
||||
});
|
||||
if (viewOperation) {
|
||||
writeCanvasViewOperationChunk(
|
||||
params.writer,
|
||||
viewOperation,
|
||||
`${params.operationLabel}:canvas-view-operation`,
|
||||
);
|
||||
}
|
||||
}
|
||||
return {
|
||||
mediaItemIds: handledCanvasMediaItemIds,
|
||||
handledCanvasMediaItemIds,
|
||||
|
||||
68
lib/canvas/agent-result-view.ts
Normal file
68
lib/canvas/agent-result-view.ts
Normal file
@ -0,0 +1,68 @@
|
||||
import type {
|
||||
CanvasOperationKind,
|
||||
CanvasViewOperationPlan,
|
||||
} from "@/types/canvas/agent-protocol";
|
||||
|
||||
function normalizeNodeIds(nodeIds?: string[]) {
|
||||
return Array.from(
|
||||
new Set(
|
||||
(nodeIds || [])
|
||||
.map((nodeId) => String(nodeId || "").trim())
|
||||
.filter(Boolean)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function buildCanvasAgentResultViewOperation(params: {
|
||||
projectId?: string | null;
|
||||
operationId: string;
|
||||
mutationOperation?: CanvasOperationKind;
|
||||
createdNodeIds?: string[];
|
||||
updatedNodeIds?: string[];
|
||||
anchorNodeId?: string | null;
|
||||
padding?: number;
|
||||
durationMs?: number;
|
||||
reason?: string;
|
||||
}): CanvasViewOperationPlan | null {
|
||||
const projectId = String(params.projectId || "").trim();
|
||||
const operationId = String(params.operationId || "").trim();
|
||||
if (!projectId || !operationId) return null;
|
||||
|
||||
const createdNodeIds = normalizeNodeIds(params.createdNodeIds);
|
||||
const updatedNodeIds = normalizeNodeIds(params.updatedNodeIds).filter(
|
||||
(nodeId) => !createdNodeIds.includes(nodeId)
|
||||
);
|
||||
const nodeIds = createdNodeIds.length > 0 ? createdNodeIds : updatedNodeIds;
|
||||
if (nodeIds.length === 0) return null;
|
||||
|
||||
const requestedAnchorNodeId = String(params.anchorNodeId || "").trim();
|
||||
const anchorNodeId = nodeIds.includes(requestedAnchorNodeId)
|
||||
? requestedAnchorNodeId
|
||||
: nodeIds[0];
|
||||
const hasCreatedResult = createdNodeIds.length > 0;
|
||||
const operation =
|
||||
nodeIds.length > 1
|
||||
? "focus_nodes"
|
||||
: hasCreatedResult
|
||||
? "focus_latest_result"
|
||||
: "focus_node";
|
||||
|
||||
return {
|
||||
version: 1,
|
||||
viewOperationId: `${operationId}:view`,
|
||||
operation,
|
||||
projectId,
|
||||
nodeIds,
|
||||
anchorNodeId,
|
||||
padding: params.padding ?? 120,
|
||||
durationMs: params.durationMs ?? 360,
|
||||
persist: false,
|
||||
reason:
|
||||
params.reason ||
|
||||
(params.mutationOperation === "replace_node_media"
|
||||
? "focus_canvas_agent_replaced_nodes"
|
||||
: hasCreatedResult
|
||||
? "focus_canvas_agent_writeback_result"
|
||||
: "focus_canvas_agent_updated_nodes"),
|
||||
};
|
||||
}
|
||||
@ -48,6 +48,7 @@ import {
|
||||
shouldConsiderCanvasAgentModelPlanner,
|
||||
} from "@/lib/canvas/agent-model-planner";
|
||||
import { resolveCanvasAgentMediaPlacements } from "@/lib/canvas/agent-placement";
|
||||
import { buildCanvasAgentResultViewOperation } from "@/lib/canvas/agent-result-view";
|
||||
import {
|
||||
computeCanvasAgentPanViewport,
|
||||
resolveCanvasViewOperation,
|
||||
@ -1205,6 +1206,59 @@ test.describe("canvas chat pure helpers", () => {
|
||||
expect(useCanvasStore.getState().isAgentOperationRefreshActive()).toBe(false);
|
||||
});
|
||||
|
||||
test("builds transient view operations for agent writeback results", () => {
|
||||
expect(
|
||||
buildCanvasAgentResultViewOperation({
|
||||
projectId: plannerContext.projectId,
|
||||
operationId: "operation-1",
|
||||
mutationOperation: "insert_beside",
|
||||
createdNodeIds: ["new-image-1"],
|
||||
})
|
||||
).toMatchObject({
|
||||
version: 1,
|
||||
viewOperationId: "operation-1:view",
|
||||
operation: "focus_latest_result",
|
||||
nodeIds: ["new-image-1"],
|
||||
anchorNodeId: "new-image-1",
|
||||
persist: false,
|
||||
reason: "focus_canvas_agent_writeback_result",
|
||||
});
|
||||
|
||||
expect(
|
||||
buildCanvasAgentResultViewOperation({
|
||||
projectId: plannerContext.projectId,
|
||||
operationId: "operation-2",
|
||||
mutationOperation: "append_revision",
|
||||
createdNodeIds: ["new-image-1", "new-image-2", "new-image-1"],
|
||||
updatedNodeIds: ["slot-1"],
|
||||
})
|
||||
).toMatchObject({
|
||||
operation: "focus_nodes",
|
||||
nodeIds: ["new-image-1", "new-image-2"],
|
||||
anchorNodeId: "new-image-1",
|
||||
});
|
||||
|
||||
expect(
|
||||
buildCanvasAgentResultViewOperation({
|
||||
projectId: plannerContext.projectId,
|
||||
operationId: "operation-3",
|
||||
mutationOperation: "replace_node_media",
|
||||
updatedNodeIds: ["image-1"],
|
||||
})
|
||||
).toMatchObject({
|
||||
operation: "focus_node",
|
||||
nodeIds: ["image-1"],
|
||||
reason: "focus_canvas_agent_replaced_nodes",
|
||||
});
|
||||
|
||||
expect(
|
||||
buildCanvasAgentResultViewOperation({
|
||||
projectId: plannerContext.projectId,
|
||||
operationId: "operation-4",
|
||||
})
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
test("resolves latest-result and pan canvas view operations", () => {
|
||||
expect(
|
||||
resolveCanvasViewOperationTargetNodeIds({
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user