feat(agent): 创作能力 - 接通专业节点发现与插件执行基础

This commit is contained in:
王志伟
2026-09-08 20:56:57 +08:00
parent a594c8266f
commit 757d1dd2c7
29 changed files with 617 additions and 88 deletions

View File

@@ -1526,7 +1526,8 @@ function InfiniteCanvasPage() {
focusSelection: fitCanvasSelection,
});
const { selectCanvasStyle, styleApplying } = useCanvasStyleWorkflow({
const { selectCanvasStyle, applyCanvasStyleAsync, styleApplying } = useCanvasStyleWorkflow({
canvasId: projectId,
domainProjectId: currentProject?.projectId,
nodesRef,
selectedNodeIdsRef,
@@ -2484,6 +2485,7 @@ function InfiniteCanvasPage() {
onSelectNodeIds={setSelectedNodeIds}
onSessionsChange={handleAssistantSessionsChange}
onApplyOps={applyAgentOps}
onApplyStyle={applyCanvasStyleAsync}
canUndoOps={canUndoAgentOps}
undoOpsCount={agentUndoCount}
onUndoOps={undoAgentOps}

View File

@@ -1,4 +1,4 @@
import { useCallback, type Dispatch, type SetStateAction } from "react";
import { useCallback, useEffect, useRef, type Dispatch, type SetStateAction } from "react";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { App } from "antd";
@@ -7,8 +7,10 @@ import { createCanvasNode } from "@/lib/canvas/canvas-project-domain";
import { createStyleProfileSnapshot, serializeStyleProfile } from "@/lib/canvas/style-profile";
import { updateProject as updateDomainProject } from "@/services/api/projects";
import { CanvasNodeType, type CanvasNodeData, type CanvasNodeMetadata, type Position } from "@/types/canvas";
import { getActiveUserScope } from "@/lib/user-scope";
type UseCanvasStyleWorkflowOptions = {
canvasId: string;
domainProjectId?: string;
nodesRef: { current: CanvasNodeData[] };
selectedNodeIdsRef: { current: Set<string> };
@@ -20,9 +22,13 @@ type UseCanvasStyleWorkflowOptions = {
setStylePickerOpen: Dispatch<SetStateAction<boolean>>;
};
export function useCanvasStyleWorkflow({ domainProjectId, nodesRef, selectedNodeIdsRef, getCanvasCenter, setNodes, setSelectedNodeIds, setSelectedConnectionId, setDialogNodeId, setStylePickerOpen }: UseCanvasStyleWorkflowOptions) {
export function useCanvasStyleWorkflow({ canvasId, domainProjectId, nodesRef, selectedNodeIdsRef, getCanvasCenter, setNodes, setSelectedNodeIds, setSelectedConnectionId, setDialogNodeId, setStylePickerOpen }: UseCanvasStyleWorkflowOptions) {
const { message } = App.useApp();
const queryClient = useQueryClient();
const liveContext = useRef({ canvasId, domainProjectId, mounted: true });
liveContext.current = { canvasId, domainProjectId, mounted: liveContext.current.mounted };
const applyingRef = useRef(false);
useEffect(() => { liveContext.current.mounted = true; return () => { liveContext.current.mounted = false; }; }, []);
const applyCanvasStyle = useCallback(
(preset: CanvasStylePreset, profileJson: string) => {
@@ -66,25 +72,29 @@ export function useCanvasStyleWorkflow({ domainProjectId, nodesRef, selectedNode
if (!domainProjectId) throw new Error("画布尚未关联项目");
return updateDomainProject(domainProjectId, { stylePresetId: preset.id, styleProfileJson: profileJson });
},
onSuccess: (_project, { preset, profileJson }) => {
applyCanvasStyle(preset, profileJson);
void queryClient.invalidateQueries({ queryKey: ["project", domainProjectId] });
},
onError: (error) => message.error(error instanceof Error ? error.message : "项目画风保存失败"),
});
const applyCanvasStyleAsync = useCallback(async (preset: CanvasStylePreset) => {
if (applyingRef.current) throw new Error("画风正在保存,请稍后重试");
const scope = getActiveUserScope();
applyingRef.current = true;
try {
const profileJson = serializeStyleProfile(preset.profile || createStyleProfileSnapshot(preset));
if (domainProjectId) {
await persistStyleMutation.mutateAsync({ preset, profileJson });
void queryClient.invalidateQueries({ queryKey: ["project", domainProjectId] });
}
if (!liveContext.current.mounted || liveContext.current.canvasId !== canvasId || liveContext.current.domainProjectId !== domainProjectId || getActiveUserScope() !== scope) throw new Error("页面或账号已变化,未向当前画布应用画风;如项目保存已完成,请回原项目查看");
applyCanvasStyle(preset, profileJson);
} finally { applyingRef.current = false; }
}, [canvasId, domainProjectId, persistStyleMutation.mutateAsync, queryClient, applyCanvasStyle]);
const selectCanvasStyle = useCallback(
(preset: CanvasStylePreset) => {
if (persistStyleMutation.isPending) return;
const profileJson = serializeStyleProfile(preset.profile || createStyleProfileSnapshot(preset));
if (!domainProjectId) {
applyCanvasStyle(preset, profileJson);
return;
}
persistStyleMutation.mutate({ preset, profileJson });
void applyCanvasStyleAsync(preset).catch((error) => message.error(error instanceof Error ? error.message : "项目画风保存失败"));
},
[applyCanvasStyle, domainProjectId, persistStyleMutation],
[applyCanvasStyleAsync, message],
);
return { selectCanvasStyle, styleApplying: persistStyleMutation.isPending };
return { selectCanvasStyle, applyCanvasStyleAsync, styleApplying: persistStyleMutation.isPending };
}