fix: retry canvas agent view after refresh
This commit is contained in:
parent
41acb006cf
commit
dfb8971bcb
@ -129,6 +129,7 @@ import {
|
||||
getCanvasMediaPayloadHandledItemIds,
|
||||
getCanvasOperationHandledMediaItemIds,
|
||||
type ImageGenerationStatusStreamPayload,
|
||||
shouldRetryCanvasViewOperationAfterRefresh,
|
||||
shouldSkipCanvasMediaPayloadForHandledItems,
|
||||
} from "@/components/canvas/chat-panel/canvas-agent-stream";
|
||||
import {
|
||||
@ -1249,6 +1250,8 @@ export function CanvasChatPanel({
|
||||
const autoContextRef = useRef<string | null>(null);
|
||||
const contextPayloadRef = useRef<any>(null);
|
||||
const handledCanvasMediaItemIdsRef = useRef<Set<string>>(new Set());
|
||||
const pendingCanvasViewOperationRef =
|
||||
useRef<CanvasViewOperationStreamPayload | null>(null);
|
||||
const lastWorkflowAutofillSignatureRef = useRef<string | null>(null);
|
||||
const lastWorkflowAutofillUrlsRef = useRef<string[]>([]);
|
||||
const pendingLaunchRef = useRef<CanvasLaunchPayload | null>(null);
|
||||
@ -1826,10 +1829,10 @@ export function CanvasChatPanel({
|
||||
|
||||
const applyCanvasViewOperation = useCallback(
|
||||
(payload: CanvasViewOperationStreamPayload) => {
|
||||
if (!payload || typeof payload !== "object") return;
|
||||
if (!payload || typeof payload !== "object") return false;
|
||||
const targetProjectId = String(payload.projectId || "").trim();
|
||||
if (targetProjectId && targetProjectId !== canvasId) return;
|
||||
if (typeof window === "undefined") return;
|
||||
if (targetProjectId && targetProjectId !== canvasId) return false;
|
||||
if (typeof window === "undefined") return false;
|
||||
|
||||
const store = useCanvasStore.getState();
|
||||
const resolved = resolveCanvasViewOperation({
|
||||
@ -1842,7 +1845,7 @@ export function CanvasChatPanel({
|
||||
height: window.innerHeight,
|
||||
},
|
||||
});
|
||||
if (!resolved) return;
|
||||
if (!resolved) return false;
|
||||
|
||||
window.requestAnimationFrame(() => {
|
||||
setTransientViewport(resolved.viewport);
|
||||
@ -1850,6 +1853,7 @@ export function CanvasChatPanel({
|
||||
if (resolved.targetNodeIds.length > 0) {
|
||||
store.selectNodes(resolved.targetNodeIds);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
[canvasId, setTransientViewport]
|
||||
);
|
||||
@ -4062,6 +4066,16 @@ export function CanvasChatPanel({
|
||||
focusCanvasOnNode(focusNode);
|
||||
}
|
||||
}
|
||||
const pendingViewOperation = pendingCanvasViewOperationRef.current;
|
||||
if (pendingViewOperation) {
|
||||
const pendingProjectId = String(pendingViewOperation.projectId || "").trim();
|
||||
if (!pendingProjectId || pendingProjectId === targetProjectId) {
|
||||
const applied = applyCanvasViewOperation(pendingViewOperation);
|
||||
if (applied) {
|
||||
pendingCanvasViewOperationRef.current = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
@ -4071,7 +4085,7 @@ export function CanvasChatPanel({
|
||||
return false;
|
||||
}
|
||||
},
|
||||
[canvasId, focusCanvasOnNode, mutate]
|
||||
[applyCanvasViewOperation, canvasId, focusCanvasOnNode, mutate]
|
||||
);
|
||||
|
||||
const {
|
||||
@ -4242,7 +4256,10 @@ export function CanvasChatPanel({
|
||||
try {
|
||||
const payload: CanvasViewOperationStreamPayload =
|
||||
typeof dataPart.data === "string" ? JSON.parse(dataPart.data) : dataPart.data;
|
||||
applyCanvasViewOperation(payload);
|
||||
const applied = applyCanvasViewOperation(payload);
|
||||
if (!applied && shouldRetryCanvasViewOperationAfterRefresh(payload)) {
|
||||
pendingCanvasViewOperationRef.current = payload;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("[CanvasChat] Failed to parse data-canvasViewOperation:", e);
|
||||
}
|
||||
|
||||
@ -64,6 +64,22 @@ export function shouldSkipCanvasMediaPayloadForHandledItems(params: {
|
||||
);
|
||||
}
|
||||
|
||||
export function shouldRetryCanvasViewOperationAfterRefresh(
|
||||
payload: CanvasViewOperationStreamPayload
|
||||
) {
|
||||
const operation = String(payload?.operation || "").trim();
|
||||
if (!operation) return false;
|
||||
if (normalizeCanvasStreamIdList(payload.nodeIds).length > 0) return true;
|
||||
if (String(payload.anchorNodeId || "").trim()) return true;
|
||||
return (
|
||||
operation === "focus_node" ||
|
||||
operation === "focus_nodes" ||
|
||||
operation === "focus_selection" ||
|
||||
operation === "focus_latest_result" ||
|
||||
operation === "fit_all"
|
||||
);
|
||||
}
|
||||
|
||||
export type ImageGenerationStatusStreamPayload = {
|
||||
phase?: "queued" | "processing" | "finalizing" | "failed" | "completed";
|
||||
providerType?: string;
|
||||
|
||||
@ -9,6 +9,7 @@ import {
|
||||
import {
|
||||
getCanvasMediaPayloadHandledItemIds,
|
||||
getCanvasOperationHandledMediaItemIds,
|
||||
shouldRetryCanvasViewOperationAfterRefresh,
|
||||
shouldSkipCanvasMediaPayloadForHandledItems,
|
||||
} from "@/components/canvas/chat-panel/canvas-agent-stream";
|
||||
import {
|
||||
@ -1217,6 +1218,31 @@ test.describe("canvas chat pure helpers", () => {
|
||||
expect(useCanvasStore.getState().isAgentOperationRefreshActive()).toBe(false);
|
||||
});
|
||||
|
||||
test("queues target-based canvas view operations for post-refresh retry", () => {
|
||||
expect(
|
||||
shouldRetryCanvasViewOperationAfterRefresh({
|
||||
version: 1,
|
||||
viewOperationId: "view-result",
|
||||
operation: "focus_latest_result",
|
||||
nodeIds: ["new-image-1"],
|
||||
})
|
||||
).toBe(true);
|
||||
expect(
|
||||
shouldRetryCanvasViewOperationAfterRefresh({
|
||||
version: 1,
|
||||
viewOperationId: "view-fit-all",
|
||||
operation: "fit_all",
|
||||
})
|
||||
).toBe(true);
|
||||
expect(
|
||||
shouldRetryCanvasViewOperationAfterRefresh({
|
||||
version: 1,
|
||||
viewOperationId: "view-zoom",
|
||||
operation: "zoom_in",
|
||||
})
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
test("builds transient view operations for agent writeback results", () => {
|
||||
expect(
|
||||
buildCanvasAgentResultViewOperation({
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user