fix: centralize canvas agent pending requests
This commit is contained in:
parent
914e09af73
commit
b7ff4e68eb
@ -136,6 +136,17 @@ import {
|
||||
mergePendingCanvasAgentTargetNodeIds,
|
||||
resolvePendingCanvasAgentTargetNode,
|
||||
} from "@/components/canvas/chat-panel/canvas-agent-targets";
|
||||
import {
|
||||
CANVAS_AGENT_ANNOTATION_EDIT_EVENT,
|
||||
CANVAS_AGENT_SLOT_FILL_EVENT,
|
||||
consumeCanvasAgentAnnotationEditRequest,
|
||||
consumeCanvasAgentSlotFillRequest,
|
||||
getPendingCanvasAgentAnnotationEditRequest,
|
||||
getPendingCanvasAgentSlotFillRequest,
|
||||
type CanvasAgentPendingRequestWindow,
|
||||
type PendingCanvasAgentAnnotationEditRequest,
|
||||
type PendingCanvasAgentSlotFillRequest,
|
||||
} from "@/lib/canvas/agent-pending-requests";
|
||||
import {
|
||||
type DetailArchive,
|
||||
type DetailBriefForm,
|
||||
@ -3594,19 +3605,7 @@ export function CanvasChatPanel({
|
||||
|
||||
useEffect(() => {
|
||||
if (!effectiveAgentEnabled || typeof window === "undefined") return;
|
||||
type PendingCanvasAgentSlotFillRequest = {
|
||||
nodeId?: string;
|
||||
label?: string;
|
||||
};
|
||||
type PendingCanvasAgentAnnotationEditRequest = {
|
||||
nodeId?: string;
|
||||
title?: string;
|
||||
annotationCount?: number;
|
||||
};
|
||||
const targetWindow = window as Window & {
|
||||
__canvasAgentPendingSlotFillRequest?: PendingCanvasAgentSlotFillRequest;
|
||||
__canvasAgentPendingAnnotationEditRequest?: PendingCanvasAgentAnnotationEditRequest;
|
||||
};
|
||||
const targetWindow = window as CanvasAgentPendingRequestWindow;
|
||||
|
||||
const focusComposerWithPrompt = (prompt: string) => {
|
||||
handleSelectGenerationMode("agent");
|
||||
@ -3630,11 +3629,8 @@ export function CanvasChatPanel({
|
||||
};
|
||||
|
||||
const handleAgentSlotFillRequest = (event: Event) => {
|
||||
const detail = (event as CustomEvent<{ nodeId?: string; label?: string }>).detail;
|
||||
const pending = targetWindow.__canvasAgentPendingSlotFillRequest;
|
||||
if (!pending?.nodeId || pending.nodeId === detail?.nodeId) {
|
||||
delete targetWindow.__canvasAgentPendingSlotFillRequest;
|
||||
}
|
||||
const detail = (event as CustomEvent<PendingCanvasAgentSlotFillRequest>).detail;
|
||||
consumeCanvasAgentSlotFillRequest(targetWindow, detail);
|
||||
const label = String(detail?.label || "这个槽位").trim();
|
||||
const prompt = `在${label === "这个槽位" ? label : `「${label}」`}生成一张图`;
|
||||
|
||||
@ -3644,17 +3640,9 @@ export function CanvasChatPanel({
|
||||
};
|
||||
|
||||
const handleAgentAnnotationEditRequest = (event: Event) => {
|
||||
const detail = (
|
||||
event as CustomEvent<{
|
||||
nodeId?: string;
|
||||
title?: string;
|
||||
annotationCount?: number;
|
||||
}>
|
||||
).detail;
|
||||
const pending = targetWindow.__canvasAgentPendingAnnotationEditRequest;
|
||||
if (!pending?.nodeId || pending.nodeId === detail?.nodeId) {
|
||||
delete targetWindow.__canvasAgentPendingAnnotationEditRequest;
|
||||
}
|
||||
const detail = (event as CustomEvent<PendingCanvasAgentAnnotationEditRequest>)
|
||||
.detail;
|
||||
consumeCanvasAgentAnnotationEditRequest(targetWindow, detail);
|
||||
const nodeId = String(detail?.nodeId || "").trim();
|
||||
if (nodeId) {
|
||||
pendingCanvasAgentAnnotationEditTargetRef.current = {
|
||||
@ -3674,37 +3662,37 @@ export function CanvasChatPanel({
|
||||
};
|
||||
|
||||
window.addEventListener(
|
||||
"canvas-agent-slot-fill-request",
|
||||
CANVAS_AGENT_SLOT_FILL_EVENT,
|
||||
handleAgentSlotFillRequest
|
||||
);
|
||||
window.addEventListener(
|
||||
"canvas-agent-annotation-edit-request",
|
||||
CANVAS_AGENT_ANNOTATION_EDIT_EVENT,
|
||||
handleAgentAnnotationEditRequest
|
||||
);
|
||||
const pendingSlotFillRequest = targetWindow.__canvasAgentPendingSlotFillRequest;
|
||||
const pendingSlotFillRequest = getPendingCanvasAgentSlotFillRequest(targetWindow);
|
||||
if (pendingSlotFillRequest) {
|
||||
handleAgentSlotFillRequest(
|
||||
new CustomEvent("canvas-agent-slot-fill-request", {
|
||||
new CustomEvent(CANVAS_AGENT_SLOT_FILL_EVENT, {
|
||||
detail: pendingSlotFillRequest,
|
||||
})
|
||||
);
|
||||
}
|
||||
const pendingAnnotationEditRequest =
|
||||
targetWindow.__canvasAgentPendingAnnotationEditRequest;
|
||||
getPendingCanvasAgentAnnotationEditRequest(targetWindow);
|
||||
if (pendingAnnotationEditRequest) {
|
||||
handleAgentAnnotationEditRequest(
|
||||
new CustomEvent("canvas-agent-annotation-edit-request", {
|
||||
new CustomEvent(CANVAS_AGENT_ANNOTATION_EDIT_EVENT, {
|
||||
detail: pendingAnnotationEditRequest,
|
||||
})
|
||||
);
|
||||
}
|
||||
return () => {
|
||||
window.removeEventListener(
|
||||
"canvas-agent-slot-fill-request",
|
||||
CANVAS_AGENT_SLOT_FILL_EVENT,
|
||||
handleAgentSlotFillRequest
|
||||
);
|
||||
window.removeEventListener(
|
||||
"canvas-agent-annotation-edit-request",
|
||||
CANVAS_AGENT_ANNOTATION_EDIT_EVENT,
|
||||
handleAgentAnnotationEditRequest
|
||||
);
|
||||
};
|
||||
|
||||
@ -60,6 +60,12 @@ import {
|
||||
LOCAL_UPLOAD_PENDING_METADATA_KEY,
|
||||
LOCAL_UPLOAD_PREVIEW_METADATA_KEY,
|
||||
} from "@/lib/canvas/local-upload-state";
|
||||
import {
|
||||
dispatchCanvasAgentOpenChatPanelRequest,
|
||||
dispatchCanvasAgentSlotFillRequest,
|
||||
storeCanvasAgentSlotFillRequest,
|
||||
type CanvasAgentPendingRequestWindow,
|
||||
} from "@/lib/canvas/agent-pending-requests";
|
||||
import { inferOriginalImageUrl } from "@/lib/image-url";
|
||||
|
||||
const CANVAS_NO_DRAG_SELECTOR =
|
||||
@ -691,24 +697,14 @@ export function CanvasWorkspace({
|
||||
nodeId: agentSlotContextMenu.nodeId,
|
||||
label: agentSlotContextMenu.label,
|
||||
};
|
||||
const targetWindow = window as Window & {
|
||||
__canvasAgentPendingSlotFillRequest?: typeof detail;
|
||||
};
|
||||
const targetWindow = window as CanvasAgentPendingRequestWindow;
|
||||
selectNode(agentSlotContextMenu.nodeId, false);
|
||||
targetWindow.__canvasAgentPendingSlotFillRequest = detail;
|
||||
targetWindow.dispatchEvent(
|
||||
new CustomEvent("canvas-open-chat-panel-request", {
|
||||
detail: {
|
||||
reason: "agent-slot-fill",
|
||||
nodeId: detail.nodeId,
|
||||
},
|
||||
})
|
||||
);
|
||||
targetWindow.dispatchEvent(
|
||||
new CustomEvent("canvas-agent-slot-fill-request", {
|
||||
detail,
|
||||
})
|
||||
);
|
||||
storeCanvasAgentSlotFillRequest(targetWindow, detail);
|
||||
dispatchCanvasAgentOpenChatPanelRequest(targetWindow, {
|
||||
reason: "agent-slot-fill",
|
||||
nodeId: detail.nodeId,
|
||||
});
|
||||
dispatchCanvasAgentSlotFillRequest(targetWindow, detail);
|
||||
setAgentSlotContextMenu(null);
|
||||
}, [agentSlotContextMenu, selectNode]);
|
||||
|
||||
|
||||
@ -125,6 +125,12 @@ import {
|
||||
syncCanvasLocalEditRegionAnnotationsMetadata,
|
||||
syncCanvasNodeNoteAnnotationMetadata,
|
||||
} from "@/lib/canvas/agent-annotation-metadata";
|
||||
import {
|
||||
dispatchCanvasAgentAnnotationEditRequest,
|
||||
dispatchCanvasAgentOpenChatPanelRequest,
|
||||
storeCanvasAgentAnnotationEditRequest,
|
||||
type CanvasAgentPendingRequestWindow,
|
||||
} from "@/lib/canvas/agent-pending-requests";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
@ -6685,25 +6691,15 @@ export function ImageNode({
|
||||
title: displayTitle,
|
||||
annotationCount: canvasAgentAnnotations.length,
|
||||
};
|
||||
const targetWindow = window as Window & {
|
||||
__canvasAgentPendingAnnotationEditRequest?: typeof detail;
|
||||
};
|
||||
const targetWindow = window as CanvasAgentPendingRequestWindow;
|
||||
|
||||
selectNode(node.id, false);
|
||||
targetWindow.__canvasAgentPendingAnnotationEditRequest = detail;
|
||||
targetWindow.dispatchEvent(
|
||||
new CustomEvent("canvas-open-chat-panel-request", {
|
||||
detail: {
|
||||
reason: "agent-annotation-edit",
|
||||
nodeId: detail.nodeId,
|
||||
},
|
||||
})
|
||||
);
|
||||
targetWindow.dispatchEvent(
|
||||
new CustomEvent("canvas-agent-annotation-edit-request", {
|
||||
detail,
|
||||
})
|
||||
);
|
||||
storeCanvasAgentAnnotationEditRequest(targetWindow, detail);
|
||||
dispatchCanvasAgentOpenChatPanelRequest(targetWindow, {
|
||||
reason: "agent-annotation-edit",
|
||||
nodeId: detail.nodeId,
|
||||
});
|
||||
dispatchCanvasAgentAnnotationEditRequest(targetWindow, detail);
|
||||
}, [
|
||||
canvasAgentAnnotations.length,
|
||||
displayTitle,
|
||||
|
||||
103
lib/canvas/agent-pending-requests.ts
Normal file
103
lib/canvas/agent-pending-requests.ts
Normal file
@ -0,0 +1,103 @@
|
||||
export type PendingCanvasAgentSlotFillRequest = {
|
||||
nodeId?: string;
|
||||
label?: string;
|
||||
};
|
||||
|
||||
export type PendingCanvasAgentAnnotationEditRequest = {
|
||||
nodeId?: string;
|
||||
title?: string;
|
||||
annotationCount?: number;
|
||||
};
|
||||
|
||||
export type CanvasAgentPendingRequestWindow = Window & {
|
||||
__canvasAgentPendingSlotFillRequest?: PendingCanvasAgentSlotFillRequest;
|
||||
__canvasAgentPendingAnnotationEditRequest?: PendingCanvasAgentAnnotationEditRequest;
|
||||
};
|
||||
|
||||
export const CANVAS_AGENT_OPEN_CHAT_PANEL_EVENT = "canvas-open-chat-panel-request";
|
||||
export const CANVAS_AGENT_SLOT_FILL_EVENT = "canvas-agent-slot-fill-request";
|
||||
export const CANVAS_AGENT_ANNOTATION_EDIT_EVENT =
|
||||
"canvas-agent-annotation-edit-request";
|
||||
|
||||
function normalizeNodeId(value: unknown) {
|
||||
return String(value || "").trim();
|
||||
}
|
||||
|
||||
export function storeCanvasAgentSlotFillRequest(
|
||||
targetWindow: CanvasAgentPendingRequestWindow,
|
||||
request: PendingCanvasAgentSlotFillRequest
|
||||
) {
|
||||
targetWindow.__canvasAgentPendingSlotFillRequest = { ...request };
|
||||
}
|
||||
|
||||
export function getPendingCanvasAgentSlotFillRequest(
|
||||
targetWindow: CanvasAgentPendingRequestWindow
|
||||
) {
|
||||
return targetWindow.__canvasAgentPendingSlotFillRequest || null;
|
||||
}
|
||||
|
||||
export function consumeCanvasAgentSlotFillRequest(
|
||||
targetWindow: CanvasAgentPendingRequestWindow,
|
||||
detail?: PendingCanvasAgentSlotFillRequest | null
|
||||
) {
|
||||
const pending = getPendingCanvasAgentSlotFillRequest(targetWindow);
|
||||
if (!pending) return null;
|
||||
const pendingNodeId = normalizeNodeId(pending.nodeId);
|
||||
const detailNodeId = normalizeNodeId(detail?.nodeId);
|
||||
if (!pendingNodeId || pendingNodeId === detailNodeId) {
|
||||
delete targetWindow.__canvasAgentPendingSlotFillRequest;
|
||||
}
|
||||
return pending;
|
||||
}
|
||||
|
||||
export function storeCanvasAgentAnnotationEditRequest(
|
||||
targetWindow: CanvasAgentPendingRequestWindow,
|
||||
request: PendingCanvasAgentAnnotationEditRequest
|
||||
) {
|
||||
targetWindow.__canvasAgentPendingAnnotationEditRequest = { ...request };
|
||||
}
|
||||
|
||||
export function getPendingCanvasAgentAnnotationEditRequest(
|
||||
targetWindow: CanvasAgentPendingRequestWindow
|
||||
) {
|
||||
return targetWindow.__canvasAgentPendingAnnotationEditRequest || null;
|
||||
}
|
||||
|
||||
export function consumeCanvasAgentAnnotationEditRequest(
|
||||
targetWindow: CanvasAgentPendingRequestWindow,
|
||||
detail?: PendingCanvasAgentAnnotationEditRequest | null
|
||||
) {
|
||||
const pending = getPendingCanvasAgentAnnotationEditRequest(targetWindow);
|
||||
if (!pending) return null;
|
||||
const pendingNodeId = normalizeNodeId(pending.nodeId);
|
||||
const detailNodeId = normalizeNodeId(detail?.nodeId);
|
||||
if (!pendingNodeId || pendingNodeId === detailNodeId) {
|
||||
delete targetWindow.__canvasAgentPendingAnnotationEditRequest;
|
||||
}
|
||||
return pending;
|
||||
}
|
||||
|
||||
export function dispatchCanvasAgentOpenChatPanelRequest(
|
||||
targetWindow: Window,
|
||||
detail: { reason: "agent-slot-fill" | "agent-annotation-edit"; nodeId?: string }
|
||||
) {
|
||||
targetWindow.dispatchEvent(
|
||||
new CustomEvent(CANVAS_AGENT_OPEN_CHAT_PANEL_EVENT, { detail })
|
||||
);
|
||||
}
|
||||
|
||||
export function dispatchCanvasAgentSlotFillRequest(
|
||||
targetWindow: Window,
|
||||
detail: PendingCanvasAgentSlotFillRequest
|
||||
) {
|
||||
targetWindow.dispatchEvent(new CustomEvent(CANVAS_AGENT_SLOT_FILL_EVENT, { detail }));
|
||||
}
|
||||
|
||||
export function dispatchCanvasAgentAnnotationEditRequest(
|
||||
targetWindow: Window,
|
||||
detail: PendingCanvasAgentAnnotationEditRequest
|
||||
) {
|
||||
targetWindow.dispatchEvent(
|
||||
new CustomEvent(CANVAS_AGENT_ANNOTATION_EDIT_EVENT, { detail })
|
||||
);
|
||||
}
|
||||
@ -16,6 +16,15 @@ import {
|
||||
mergePendingCanvasAgentTargetNodeIds,
|
||||
resolvePendingCanvasAgentTargetNode,
|
||||
} from "@/components/canvas/chat-panel/canvas-agent-targets";
|
||||
import {
|
||||
consumeCanvasAgentAnnotationEditRequest,
|
||||
consumeCanvasAgentSlotFillRequest,
|
||||
getPendingCanvasAgentAnnotationEditRequest,
|
||||
getPendingCanvasAgentSlotFillRequest,
|
||||
storeCanvasAgentAnnotationEditRequest,
|
||||
storeCanvasAgentSlotFillRequest,
|
||||
type CanvasAgentPendingRequestWindow,
|
||||
} from "@/lib/canvas/agent-pending-requests";
|
||||
import {
|
||||
writeCanvasMediaChunk,
|
||||
writeCanvasOperationChunk,
|
||||
@ -2675,6 +2684,51 @@ test.describe("canvas chat pure helpers", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
test("keeps pending canvas agent UI requests until the matching panel event consumes them", () => {
|
||||
const targetWindow = {} as CanvasAgentPendingRequestWindow;
|
||||
const slotRequest = {
|
||||
nodeId: "slot-1",
|
||||
label: "主图槽",
|
||||
};
|
||||
const annotationRequest = {
|
||||
nodeId: "image-annotated",
|
||||
title: "白底主图",
|
||||
annotationCount: 2,
|
||||
};
|
||||
|
||||
storeCanvasAgentSlotFillRequest(targetWindow, slotRequest);
|
||||
storeCanvasAgentAnnotationEditRequest(targetWindow, annotationRequest);
|
||||
|
||||
expect(getPendingCanvasAgentSlotFillRequest(targetWindow)).toEqual(slotRequest);
|
||||
expect(getPendingCanvasAgentAnnotationEditRequest(targetWindow)).toEqual(
|
||||
annotationRequest
|
||||
);
|
||||
|
||||
expect(
|
||||
consumeCanvasAgentSlotFillRequest(targetWindow, { nodeId: "other-slot" })
|
||||
).toEqual(slotRequest);
|
||||
expect(
|
||||
consumeCanvasAgentAnnotationEditRequest(targetWindow, {
|
||||
nodeId: "other-image",
|
||||
})
|
||||
).toEqual(annotationRequest);
|
||||
expect(getPendingCanvasAgentSlotFillRequest(targetWindow)).toEqual(slotRequest);
|
||||
expect(getPendingCanvasAgentAnnotationEditRequest(targetWindow)).toEqual(
|
||||
annotationRequest
|
||||
);
|
||||
|
||||
expect(
|
||||
consumeCanvasAgentSlotFillRequest(targetWindow, { nodeId: "slot-1" })
|
||||
).toEqual(slotRequest);
|
||||
expect(
|
||||
consumeCanvasAgentAnnotationEditRequest(targetWindow, {
|
||||
nodeId: "image-annotated",
|
||||
})
|
||||
).toEqual(annotationRequest);
|
||||
expect(getPendingCanvasAgentSlotFillRequest(targetWindow)).toBeNull();
|
||||
expect(getPendingCanvasAgentAnnotationEditRequest(targetWindow)).toBeNull();
|
||||
});
|
||||
|
||||
test("keeps pending annotation edit targets eligible for the next canvas context", () => {
|
||||
const nodes = [
|
||||
{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user