164 lines
5.2 KiB
TypeScript
164 lines
5.2 KiB
TypeScript
import type {
|
|
CanvasAgentContext,
|
|
CanvasAgentNodeRef,
|
|
CanvasViewOperationPlan,
|
|
} from "@/types/canvas/agent-protocol";
|
|
|
|
function normalizeNodeIds(values: Array<string | null | undefined>, maxItems = 20) {
|
|
return Array.from(
|
|
new Set(values.map((value) => String(value || "").trim()).filter(Boolean))
|
|
).slice(0, maxItems);
|
|
}
|
|
|
|
function getNodeSourceIds(node?: CanvasAgentNodeRef | null) {
|
|
const metadata = node?.metadata || {};
|
|
const sourceIds = Array.isArray(metadata.sourceNodeIds)
|
|
? metadata.sourceNodeIds
|
|
.map((value) => String(value || "").trim())
|
|
.filter(Boolean)
|
|
: [];
|
|
return normalizeNodeIds([
|
|
typeof metadata.revisionOf === "string" ? metadata.revisionOf : undefined,
|
|
typeof metadata.sourceNodeId === "string" ? metadata.sourceNodeId : undefined,
|
|
...sourceIds,
|
|
]);
|
|
}
|
|
|
|
function isAgentResultNode(node?: CanvasAgentNodeRef | null) {
|
|
const metadata = node?.metadata || {};
|
|
return (
|
|
metadata.generatedByAgent === true ||
|
|
typeof metadata.canvasAgentOperationId === "string" ||
|
|
typeof metadata.canvasAgentMediaItemId === "string" ||
|
|
getNodeSourceIds(node).length > 0
|
|
);
|
|
}
|
|
|
|
export function hasCanvasAgentComparisonViewIntent(userText: string) {
|
|
return /对比|比较|一起看|同时看|原图.*新图|新图.*原图/.test(
|
|
userText.toLowerCase()
|
|
);
|
|
}
|
|
|
|
export function hasCanvasAgentLatestResultViewIntent(userText: string) {
|
|
const text = userText.trim().toLowerCase();
|
|
if (!text) return false;
|
|
|
|
const hasLatestResultTarget =
|
|
/刚生成|刚才生成|最新(?:的)?(?:结果|图片|图|生成)|最近(?:的)?(?:结果|图片|图|生成)|上次生成|最后生成|刚出(?:的)?(?:图|结果)|新结果|新图/.test(
|
|
text
|
|
);
|
|
if (!hasLatestResultTarget) return false;
|
|
|
|
const hasViewVerb =
|
|
/聚焦|定位|居中|找到|跳到|看(?:看|一下|清楚)?|放大|拉近|focus|locate|zoom/.test(
|
|
text
|
|
);
|
|
if (!hasViewVerb) return false;
|
|
|
|
return !/生成(?:一|个|张|幅|段)|出图|做一版|再出|换|改|编辑|抠图|去水印|高清|清晰|扩图|裁剪|删除|替换|背景|视频|模型|洗图|重绘|尺寸|像素|分辨率/.test(
|
|
text
|
|
);
|
|
}
|
|
|
|
export function resolveCanvasAgentComparisonNodeIds(params: {
|
|
context: CanvasAgentContext;
|
|
maxItems?: number;
|
|
}) {
|
|
const maxItems = Math.max(2, Math.min(20, params.maxItems || 10));
|
|
const allNodes = [...params.context.selectedNodes, ...params.context.candidateNodes];
|
|
const nodeMap = new Map(allNodes.map((node) => [node.nodeId, node]));
|
|
const selectedNodeIds = normalizeNodeIds(
|
|
[
|
|
...params.context.selectedNodeIds,
|
|
...params.context.selectedNodes
|
|
.filter((node) => node.isSelected)
|
|
.map((node) => node.nodeId),
|
|
],
|
|
maxItems
|
|
).filter((nodeId) => nodeMap.has(nodeId));
|
|
|
|
if (selectedNodeIds.length >= 2) return selectedNodeIds.slice(0, maxItems);
|
|
|
|
const selectedNode = selectedNodeIds[0] ? nodeMap.get(selectedNodeIds[0]) : null;
|
|
const selectedSourceIds = getNodeSourceIds(selectedNode).filter((nodeId) =>
|
|
nodeMap.has(nodeId)
|
|
);
|
|
if (selectedNode?.nodeId && selectedSourceIds.length > 0) {
|
|
return normalizeNodeIds([...selectedSourceIds, selectedNode.nodeId], maxItems);
|
|
}
|
|
|
|
if (selectedNode?.nodeId) {
|
|
const relatedResult = allNodes.find(
|
|
(node) =>
|
|
node.nodeId !== selectedNode.nodeId &&
|
|
isAgentResultNode(node) &&
|
|
getNodeSourceIds(node).includes(selectedNode.nodeId)
|
|
);
|
|
if (relatedResult) {
|
|
return normalizeNodeIds([selectedNode.nodeId, relatedResult.nodeId], maxItems);
|
|
}
|
|
}
|
|
|
|
const latestResultWithSource = allNodes.find((node) => {
|
|
if (!isAgentResultNode(node)) return false;
|
|
return getNodeSourceIds(node).some((nodeId) => nodeMap.has(nodeId));
|
|
});
|
|
if (latestResultWithSource) {
|
|
return normalizeNodeIds(
|
|
[...getNodeSourceIds(latestResultWithSource), latestResultWithSource.nodeId],
|
|
maxItems
|
|
).filter((nodeId) => nodeMap.has(nodeId));
|
|
}
|
|
|
|
return [];
|
|
}
|
|
|
|
export function buildCanvasAgentComparisonViewPlan(params: {
|
|
context: CanvasAgentContext;
|
|
projectId: string;
|
|
operationId: string;
|
|
reason?: string;
|
|
}): CanvasViewOperationPlan | null {
|
|
const nodeIds = resolveCanvasAgentComparisonNodeIds({
|
|
context: params.context,
|
|
maxItems: 10,
|
|
});
|
|
if (nodeIds.length < 2) return null;
|
|
|
|
return {
|
|
version: 1,
|
|
viewOperationId: `${params.operationId}:view`,
|
|
operation: "focus_nodes",
|
|
projectId: params.projectId,
|
|
nodeIds,
|
|
anchorNodeId: nodeIds[0],
|
|
padding: 140,
|
|
persist: false,
|
|
reason: params.reason || "focus_canvas_comparison_targets",
|
|
};
|
|
}
|
|
|
|
export function buildCanvasAgentLatestResultViewPlan(params: {
|
|
userText: string;
|
|
projectId: string;
|
|
operationId: string;
|
|
reason?: string;
|
|
}): CanvasViewOperationPlan | null {
|
|
const projectId = String(params.projectId || "").trim();
|
|
const operationId = String(params.operationId || "").trim();
|
|
if (!projectId || !operationId) return null;
|
|
if (!hasCanvasAgentLatestResultViewIntent(params.userText)) return null;
|
|
|
|
return {
|
|
version: 1,
|
|
viewOperationId: `${operationId}:view`,
|
|
operation: "focus_latest_result",
|
|
projectId,
|
|
padding: 120,
|
|
durationMs: 360,
|
|
persist: false,
|
|
reason: params.reason || "focus_latest_canvas_agent_result",
|
|
};
|
|
}
|