feat(canvas): 图片转换插件 - 增加本地媒体转换节点与运行时

This commit is contained in:
QAyong committed 2026-09-08 22:55:44 +08:00
1 parent 8360b2c933
commit f755d6db97
51 files changed
+3284 -44

No files matched your search

+20 -3
View File
@@ -72,7 +72,7 @@ import { CanvasLocalAgentPanel } from "@/components/canvas/canvas-local-agent-pa
import { useFocusMode } from "@/hooks/use-focus-mode";
import { useCanvasAgentStore } from "@/stores/canvas/use-canvas-agent-store";
import { applyCanvasConnectionPromptSync, getContextResourceNodes, normalizeCanvasNodeMentionTokens, type CanvasResourceReference } from "@/lib/canvas/canvas-resource-references";
import { CanvasConnectionCreateMenu, CanvasNodePanelOverlay } from "@/components/canvas/canvas-workspace-overlays";
import { CanvasConnectionCreateMenu, CanvasNodePanelOverlay, type PendingConnectionCreate } from "@/components/canvas/canvas-workspace-overlays";
import { CanvasOverlayLayerContainer, CanvasOverlayLayerProvider } from "@/components/canvas/canvas-overlay-layer";
import { CanvasLeaferGraphicsLayer } from "@/components/canvas/canvas-leafer-graphics-layer";
import { CanvasFreeformEmptyState, CanvasLinkedProjectEmptyState, CanvasShortDramaEmptyState, CanvasShortDramaGuide, CanvasStoryInputNodeContent, CanvasStylePlaceholderNodeContent } from "@/components/canvas/canvas-short-drama-entry";
@@ -1018,6 +1018,20 @@ function InfiniteCanvasPage() {
.filter((node) => selectedNodeIds.has(node.id) && !batchSourceRestriction(node))
.map((node) => node.id), [nodes, selectedNodeIds]);
const createConversionFromSource = useCallback((source: CanvasNodeData) => {
if (source.type !== CanvasNodeType.Image && source.type !== CanvasNodeType.Video) return;
const spec = getNodeSpec(CanvasNodeType.MediaConversion);
const pending: PendingConnectionCreate = {
connection: { nodeId: source.id, handleType: "source", anchorRatio: 0.5 },
position: {
x: source.position.x + source.width + 96 + spec.width / 2,
y: source.position.y + source.height / 2,
},
quick: true,
};
void createConnectedNode(CanvasNodeType.MediaConversion, pending);
}, [createConnectedNode]);
const handleCanvasSelectionStart = useCallback(() => {
setContextMenu(null);
}, []);
@@ -1048,6 +1062,8 @@ function InfiniteCanvasPage() {
} else if (node.type === ART_CRITIQUE_NODE_TYPE) {
setDialogNodeId(null);
setArtCritiqueNodeId(node.id);
} else if (node.type === CanvasNodeType.MediaConversion) {
setDialogNodeId(null);
} else {
// 选择参考媒体时保留当前工作流配置面板,避免点击图片后配置“返回/消失”。
// 没有工作流配置面板时,媒体节点仍按原逻辑打开自己的面板。
@@ -1065,7 +1081,7 @@ function InfiniteCanvasPage() {
const handleNodeDragEnd = useCallback((nodeId: string) => {
const node = nodesRef.current.find((item) => item.id === nodeId);
if (!node || node.type === CanvasNodeType.Script || node.type === CanvasNodeType.Drawing || node.type === PORTRAIT_CLEARANCE_NODE_TYPE || node.type === ART_CRITIQUE_NODE_TYPE) {
if (!node || node.type === CanvasNodeType.Script || node.type === CanvasNodeType.Drawing || node.type === CanvasNodeType.MediaConversion || node.type === PORTRAIT_CLEARANCE_NODE_TYPE || node.type === ART_CRITIQUE_NODE_TYPE) {
setDialogNodeId(null);
return;
}
@@ -1895,7 +1911,7 @@ function InfiniteCanvasPage() {
const renderCanvasNodePanel = useCallback(
(panelNode: CanvasNodeData) => {
if (panelNode.type === CanvasNodeType.Script || panelNode.type === CanvasNodeType.Drawing) return null;
if (panelNode.type === CanvasNodeType.Script || panelNode.type === CanvasNodeType.Drawing || panelNode.type === CanvasNodeType.MediaConversion) return null;
return panelNode.type === CanvasNodeType.Config ? (
<CanvasConfigComposer
value={panelNode.metadata?.composerContent ?? panelNode.metadata?.prompt ?? ""}
@@ -2572,6 +2588,7 @@ function InfiniteCanvasPage() {
onUpload={(node) => handleUploadRequest(node.id)}
onDownload={downloadNodeImage}
onSaveAsset={(node) => void saveNodeAsset(node)}
onCreateConversion={createConversionFromSource}
onAnnotate={(node) => setAnnotationNodeId(node.id)}
onMaskEdit={(node) => setMaskEditNodeId(node.id)}
onEmotion={(node) => {