chore(release): 版本发布 - publish v1.0.12
This commit is contained in:
@@ -28,20 +28,22 @@ export async function executeTextGeneration({
|
||||
}: CanvasGenerationExecution) {
|
||||
let streamed = "";
|
||||
const isConfigNode = sourceNode?.type === CanvasNodeType.Config;
|
||||
const textCount = isConfigNode ? getGenerationCount(generationConfig.count) : 1;
|
||||
const isDirectTextTarget = sourceNode?.type === CanvasNodeType.Text && !sourceNode.metadata?.content?.trim() && !editingTextNode;
|
||||
const textCount = isConfigNode || (isDirectTextTarget && sourceNode?.metadata?.count) ? getGenerationCount(generationConfig.count) : 1;
|
||||
const parentConfig = NODE_DEFAULT_SIZE[isConfigNode ? CanvasNodeType.Config : CanvasNodeType.Text];
|
||||
const textConfig = NODE_DEFAULT_SIZE[CanvasNodeType.Text];
|
||||
const parentPosition = sourceNode?.position || { x: 0, y: 0 };
|
||||
const childIds = isConfigNode || editingTextNode ? Array.from({ length: textCount }, () => nanoid()) : [];
|
||||
const generateInPlace = !isConfigNode && !editingTextNode;
|
||||
const childIds = Array.from({ length: generateInPlace ? Math.max(0, textCount - 1) : textCount }, () => nanoid());
|
||||
registerPendingNodeIds(childIds);
|
||||
if (isConfigNode || editingTextNode) {
|
||||
if (childIds.length) {
|
||||
const childNodes: CanvasNodeData[] = childIds.map((id, index) => ({
|
||||
id,
|
||||
type: CanvasNodeType.Text,
|
||||
title: effectivePrompt.slice(0, 32) || "Generated Text",
|
||||
position: {
|
||||
x: parentPosition.x + parentConfig.width + 96,
|
||||
y: parentPosition.y + parentConfig.height / 2 - textConfig.height / 2 + (index - (textCount - 1) / 2) * (textConfig.height + 36),
|
||||
y: parentPosition.y + parentConfig.height / 2 - textConfig.height / 2 + (index - (childIds.length - 1) / 2) * (textConfig.height + 36),
|
||||
},
|
||||
width: textConfig.width,
|
||||
height: textConfig.height,
|
||||
@@ -51,7 +53,7 @@ export async function executeTextGeneration({
|
||||
setConnections((current) => [...current, ...childIds.map((childId) => ({ id: nanoid(), fromNodeId: nodeId, toNodeId: childId }))]);
|
||||
}
|
||||
|
||||
const textTargetIds = childIds.length ? childIds : [nodeId];
|
||||
const textTargetIds = generateInPlace ? [nodeId, ...childIds] : childIds;
|
||||
textTargetIds.forEach((targetNodeId) => startGenerationRequest(targetNodeId, nodeId, nodeId, controller));
|
||||
const answers = await Promise.all(
|
||||
textTargetIds.map((targetNodeId) => {
|
||||
@@ -75,7 +77,7 @@ export async function executeTextGeneration({
|
||||
: node.id === nodeId && isConfigNode
|
||||
? { ...node, metadata: { ...node.metadata, status: NODE_STATUS_SUCCESS, errorDetails: undefined, generationErrorCode: undefined, failedPromptFingerprint: undefined } }
|
||||
: node.id === nodeId && !editingTextNode
|
||||
? { ...node, type: CanvasNodeType.Text, title: prompt.slice(0, 32) || "Generated Text", metadata: { ...node.metadata, content: answerByNodeId.get(node.id) || streamed, richText: undefined, status: NODE_STATUS_SUCCESS, errorDetails: undefined, generationErrorCode: undefined, failedPromptFingerprint: undefined } }
|
||||
? { ...node, type: CanvasNodeType.Text, title: effectivePrompt.slice(0, 32) || "Generated Text", metadata: { ...node.metadata, content: answerByNodeId.get(node.id) || streamed, richText: undefined, status: NODE_STATUS_SUCCESS, errorDetails: undefined, generationErrorCode: undefined, failedPromptFingerprint: undefined } }
|
||||
: node,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1152,15 +1152,16 @@ function InfiniteCanvasPage() {
|
||||
}
|
||||
const sourceNode = nodesRef.current.find((item) => item.id === node.id);
|
||||
if (!sourceNode) return;
|
||||
const nodeSize = getNodeSpec(CanvasNodeType.Config);
|
||||
const configNode = createCanvasNode(
|
||||
CanvasNodeType.Config,
|
||||
const nodeSize = getNodeSpec(CanvasNodeType.Image);
|
||||
const imageNode = createCanvasNode(
|
||||
CanvasNodeType.Image,
|
||||
{
|
||||
x: sourceNode.position.x + sourceNode.width + 96 + nodeSize.width / 2,
|
||||
y: sourceNode.position.y + sourceNode.height / 2,
|
||||
},
|
||||
{
|
||||
prompt: "",
|
||||
prompt: `@[node:${sourceNode.id}]`,
|
||||
composerContent: `@[node:${sourceNode.id}]`,
|
||||
model: effectiveConfig.imageModel || effectiveConfig.model,
|
||||
size: effectiveConfig.size,
|
||||
quality: effectiveConfig.quality,
|
||||
@@ -1168,16 +1169,17 @@ function InfiniteCanvasPage() {
|
||||
count: getGenerationCount(effectiveConfig.canvasImageCount || effectiveConfig.count),
|
||||
},
|
||||
);
|
||||
const connection = { id: nanoid(), fromNodeId: sourceNode.id, toNodeId: configNode.id };
|
||||
const nextNodes = nodesRef.current.map((item) => (item.id === sourceNode.id ? { ...item, metadata: { ...item.metadata, content: prompt, richText: undefined, prompt, status: NODE_STATUS_SUCCESS } } : item)).concat(configNode);
|
||||
imageNode.title = "图片生成";
|
||||
const connection = { id: nanoid(), fromNodeId: sourceNode.id, toNodeId: imageNode.id };
|
||||
const nextNodes = nodesRef.current.map((item) => (item.id === sourceNode.id ? { ...item, metadata: { ...item.metadata, content: prompt, richText: undefined, prompt, status: NODE_STATUS_SUCCESS } } : item)).concat(imageNode);
|
||||
const nextConnections = [...connectionsRef.current, connection];
|
||||
nodesRef.current = nextNodes;
|
||||
connectionsRef.current = nextConnections;
|
||||
setNodes(nextNodes);
|
||||
setConnections(nextConnections);
|
||||
setSelectedNodeIds(new Set([configNode.id]));
|
||||
setSelectedNodeIds(new Set([imageNode.id]));
|
||||
setSelectedConnectionId(null);
|
||||
setDialogNodeId(configNode.id);
|
||||
setDialogNodeId(imageNode.id);
|
||||
},
|
||||
[effectiveConfig, message],
|
||||
);
|
||||
@@ -1554,7 +1556,6 @@ function InfiniteCanvasPage() {
|
||||
onAddScript={() => createNode(CanvasNodeType.Script)}
|
||||
onAddFrame={() => createNode(CanvasNodeType.Frame)}
|
||||
onAddDrawing={() => createNode(CanvasNodeType.Drawing)}
|
||||
onAddConfig={() => createNode(CanvasNodeType.Config)}
|
||||
onOpenDirector={() => createDirectorShot()}
|
||||
onUndo={undoCanvas}
|
||||
onRedo={redoCanvas}
|
||||
|
||||
@@ -6,8 +6,6 @@ import type { PendingConnectionCreate } from "@/components/canvas/canvas-workspa
|
||||
import { attachNodeToStoryboardRow, createCanvasNode, getConnectionTargetAnchor, isHiddenBatchChild, normalizeConnection, storyboardHandleAtY, storyboardRowFromHandle } from "@/lib/canvas/canvas-project-domain";
|
||||
import { createCanvasDrawingFromImage } from "@/lib/canvas/canvas-drawing-storage";
|
||||
import { isFrameNode, isNodeHiddenByCollapsedFrame } from "@/lib/canvas/canvas-frame";
|
||||
import { getGenerationCount } from "@/lib/canvas/canvas-project-generation";
|
||||
import { useEffectiveConfig } from "@/stores/use-config-store";
|
||||
import { CanvasNodeType, type CanvasConnection, type CanvasNodeData, type ConnectionHandle, type ContextMenuState, type Position, type ViewportTransform } from "@/types/canvas";
|
||||
|
||||
type UseCanvasConnectionControllerOptions = {
|
||||
@@ -52,7 +50,6 @@ export function useCanvasConnectionController({
|
||||
setDrawingNodeId,
|
||||
}: UseCanvasConnectionControllerOptions) {
|
||||
const { message } = App.useApp();
|
||||
const effectiveConfig = useEffectiveConfig();
|
||||
const [connectingParams, setConnectingParams] = useState<ConnectionHandle | null>(null);
|
||||
const [connectionTargetNodeId, setConnectionTargetNodeId] = useState<string | null>(null);
|
||||
const [pendingConnectionCreate, setPendingConnectionCreate] = useState<PendingConnectionCreate | null>(null);
|
||||
@@ -103,14 +100,12 @@ export function useCanvasConnectionController({
|
||||
setContextMenu(null);
|
||||
}, [connectionsRef, message, nodesRef, setConnections, setContextMenu, setNodes]);
|
||||
|
||||
const createConnectedNode = useCallback(async (type: CanvasNodeType.Image | CanvasNodeType.Text | CanvasNodeType.Script | CanvasNodeType.Config | CanvasNodeType.Video | CanvasNodeType.Audio | CanvasNodeType.Drawing, pending: PendingConnectionCreate) => {
|
||||
const createConnectedNode = useCallback(async (type: CanvasNodeType.Image | CanvasNodeType.Text | CanvasNodeType.Script | CanvasNodeType.Video | CanvasNodeType.Audio | CanvasNodeType.Drawing, pending: PendingConnectionCreate) => {
|
||||
const storyboardRow = type === CanvasNodeType.Video ? storyboardRowFromHandle(nodesRef.current, pending.connection.nodeId, pending.connection.handleId) : undefined;
|
||||
const videoPrompt = storyboardRow ? (storyboardRow.videoMotionPrompt || storyboardRow.plotDescription).trim() : "";
|
||||
const sourceNode = pending.connection.handleType === "source" ? nodesRef.current.find((node) => node.id === pending.connection.nodeId) : undefined;
|
||||
const scriptPrompt = type === CanvasNodeType.Script && sourceNode?.type === CanvasNodeType.Text ? (sourceNode.metadata?.content || sourceNode.metadata?.prompt || "").trim() : "";
|
||||
const metadata = type === CanvasNodeType.Config
|
||||
? { model: effectiveConfig.imageModel || effectiveConfig.model, size: effectiveConfig.size, count: getGenerationCount(effectiveConfig.canvasImageCount || effectiveConfig.count) }
|
||||
: type === CanvasNodeType.Script && scriptPrompt
|
||||
const metadata = type === CanvasNodeType.Script && scriptPrompt
|
||||
? { prompt: scriptPrompt, composerContent: scriptPrompt }
|
||||
: type === CanvasNodeType.Video && storyboardRow
|
||||
? { prompt: videoPrompt, composerContent: videoPrompt, generationMode: "video" as const, videoEditOperation: "text_to_video" as const, workflowKind: "shot" as const, workflowTitle: `镜头 ${storyboardRow.shotNumber} 视频`, shotIndex: storyboardRow.shotNumber, seconds: String(storyboardRow.durationSeconds), status: NODE_STATUS_IDLE }
|
||||
@@ -162,7 +157,7 @@ export function useCanvasConnectionController({
|
||||
else if (type !== CanvasNodeType.Text && type !== CanvasNodeType.Script && type !== CanvasNodeType.Audio) setDialogNodeId(newNode.id);
|
||||
closeConnectionCreateMenu();
|
||||
setConnecting(null);
|
||||
}, [closeConnectionCreateMenu, effectiveConfig.canvasImageCount, effectiveConfig.count, effectiveConfig.imageModel, effectiveConfig.model, effectiveConfig.size, message, nodesRef, projectId, setConnecting, setConnections, setDialogNodeId, setDrawingNodeId, setNodes, setSelectedConnectionId, setSelectedNodeIds]);
|
||||
}, [closeConnectionCreateMenu, message, nodesRef, projectId, setConnecting, setConnections, setDialogNodeId, setDrawingNodeId, setNodes, setSelectedConnectionId, setSelectedNodeIds]);
|
||||
|
||||
const getConnectionDropTarget = useCallback((clientX: number, clientY: number, current: ConnectionHandle): ConnectionDropTarget => {
|
||||
const world = screenToCanvas(clientX, clientY);
|
||||
|
||||
@@ -49,7 +49,7 @@ export function useCanvasDirector({
|
||||
let scene = createDirectorScene(`镜头 ${shotIndex}`);
|
||||
const shot = scene.shots[0];
|
||||
scene = { ...scene, shots: [{ ...shot, name: `镜头 ${shotIndex}` }] };
|
||||
const node = createCanvasNode(CanvasNodeType.Config, position || getCanvasCenter(), {
|
||||
const node = createCanvasNode(CanvasNodeType.Video, position || getCanvasCenter(), {
|
||||
workflowKind: "shot",
|
||||
workflowTitle: `镜头 ${shotIndex}`,
|
||||
shotIndex,
|
||||
@@ -61,6 +61,7 @@ export function useCanvasDirector({
|
||||
directorShotId: shot.id,
|
||||
});
|
||||
node.title = `镜头 ${shotIndex}`;
|
||||
node.height = 300;
|
||||
const nextNodes = [...nodesRef.current, node];
|
||||
nodesRef.current = nextNodes;
|
||||
setNodes(nextNodes);
|
||||
|
||||
@@ -105,26 +105,27 @@ export function useCanvasMediaTools({
|
||||
}
|
||||
const gap = 96;
|
||||
const textSpec = NODE_DEFAULT_SIZE[CanvasNodeType.Text];
|
||||
const configSpec = NODE_DEFAULT_SIZE[CanvasNodeType.Config];
|
||||
const resultSpec = NODE_DEFAULT_SIZE[CanvasNodeType.Text];
|
||||
const centerY = node.position.y + node.height / 2;
|
||||
const textNode = {
|
||||
...createCanvasNode(CanvasNodeType.Text, { x: node.position.x + node.width + gap + textSpec.width / 2, y: centerY }, { content: IMAGE_PROMPT_REVERSE_PRESET, prompt: IMAGE_PROMPT_REVERSE_PRESET, status: NODE_STATUS_SUCCESS, fontSize: 14 }),
|
||||
title: "反推提示词",
|
||||
};
|
||||
const configNode = {
|
||||
...createCanvasNode(CanvasNodeType.Config, { x: textNode.position.x + textNode.width + gap + configSpec.width / 2, y: centerY }, {
|
||||
const resultNode = {
|
||||
...createCanvasNode(CanvasNodeType.Text, { x: textNode.position.x + textNode.width + gap + resultSpec.width / 2, y: centerY }, {
|
||||
content: "",
|
||||
generationMode: "text",
|
||||
model: effectiveConfig.textModel || effectiveConfig.model || defaultConfig.textModel,
|
||||
count: 1,
|
||||
composerContent: `参考图片:@[node:${node.id}]\n任务说明:@[node:${textNode.id}]`,
|
||||
}),
|
||||
title: "反推提示词配置",
|
||||
title: "反推提示词结果",
|
||||
};
|
||||
setNodes((current) => [...current, textNode, configNode]);
|
||||
setConnections((current) => [...current, { id: nanoid(), fromNodeId: node.id, toNodeId: configNode.id }, { id: nanoid(), fromNodeId: textNode.id, toNodeId: configNode.id }]);
|
||||
setSelectedNodeIds(new Set([configNode.id]));
|
||||
setNodes((current) => [...current, textNode, resultNode]);
|
||||
setConnections((current) => [...current, { id: nanoid(), fromNodeId: node.id, toNodeId: resultNode.id }, { id: nanoid(), fromNodeId: textNode.id, toNodeId: resultNode.id }]);
|
||||
setSelectedNodeIds(new Set([resultNode.id]));
|
||||
setSelectedConnectionId(null);
|
||||
setDialogNodeId(configNode.id);
|
||||
setDialogNodeId(resultNode.id);
|
||||
setContextMenu(null);
|
||||
}, [effectiveConfig.model, effectiveConfig.textModel, message, setConnections, setContextMenu, setDialogNodeId, setNodes, setSelectedConnectionId, setSelectedNodeIds]);
|
||||
|
||||
|
||||
@@ -6,9 +6,7 @@ import { nanoid } from "nanoid";
|
||||
import { FRAME_HEADER_HEIGHT, getFrameChildIds, getFrameChildren, isFrameNode } from "@/lib/canvas/canvas-frame";
|
||||
import { alignCanvasNodes, layoutCanvasFlow, layoutCanvasNodes, nextCanvasVersionLabel, type CanvasAlignmentMode } from "@/lib/canvas/canvas-layout";
|
||||
import { createCanvasNode, removeCanvasNodes } from "@/lib/canvas/canvas-project-domain";
|
||||
import { getGenerationCount } from "@/lib/canvas/canvas-project-generation";
|
||||
import { isolateCopiedNodeMetadata } from "@/lib/canvas/canvas-node-copy";
|
||||
import { useEffectiveConfig } from "@/stores/use-config-store";
|
||||
import { CanvasNodeType, type CanvasConnection, type CanvasNodeData, type ContextMenuState, type Position } from "@/types/canvas";
|
||||
import { cloneCanvasDrawing } from "@/lib/canvas/canvas-drawing-storage";
|
||||
|
||||
@@ -47,7 +45,6 @@ export function useCanvasNodeOperations({
|
||||
onNodesDeleted,
|
||||
}: UseCanvasNodeOperationsOptions) {
|
||||
const { message } = App.useApp();
|
||||
const effectiveConfig = useEffectiveConfig();
|
||||
const clipboardRef = useRef<CanvasClipboard | null>(null);
|
||||
const preferCopiedNodesRef = useRef(false);
|
||||
const markerWritePendingRef = useRef(false);
|
||||
@@ -113,18 +110,11 @@ export function useCanvasNodeOperations({
|
||||
}, [selectedNodeIdsRef, setSelectedConnectionId, setSelectedNodeIds]);
|
||||
|
||||
const createNode = useCallback((type: CanvasNodeType, position?: Position) => {
|
||||
const configMetadata = type === CanvasNodeType.Config
|
||||
? {
|
||||
model: effectiveConfig.imageModel || effectiveConfig.model,
|
||||
size: effectiveConfig.size,
|
||||
count: getGenerationCount(effectiveConfig.canvasImageCount || effectiveConfig.count),
|
||||
}
|
||||
: undefined;
|
||||
const node = createCanvasNode(type, position || getCanvasCenter(), configMetadata);
|
||||
const node = createCanvasNode(type, position || getCanvasCenter());
|
||||
commitNodes([...nodesRef.current, node]);
|
||||
selectNodes(new Set([node.id]));
|
||||
if (type !== CanvasNodeType.Text && type !== CanvasNodeType.Script && type !== CanvasNodeType.Audio && type !== CanvasNodeType.Frame && type !== CanvasNodeType.Drawing) setDialogNodeId(node.id);
|
||||
}, [commitNodes, effectiveConfig.canvasImageCount, effectiveConfig.count, effectiveConfig.imageModel, effectiveConfig.model, effectiveConfig.size, getCanvasCenter, nodesRef, selectNodes, setDialogNodeId]);
|
||||
}, [commitNodes, getCanvasCenter, nodesRef, selectNodes, setDialogNodeId]);
|
||||
|
||||
const arrangeSelectedNodes = useCallback((mode: "row" | "column" | "grid" | "flow") => {
|
||||
const selected = nodesRef.current.filter((node) => selectedNodeIdsRef.current.has(node.id) && !node.metadata?.locked && !isFrameNode(node));
|
||||
|
||||
Reference in New Issue
Block a user