fix(canvas): 节点复制 - 修复内部粘贴和批量状态残留 (#60)
Co-authored-by: Danborad <Danborad@users.noreply.github.com>
This commit is contained in:
1 parent
14cfdbed4a
commit
a3b35290de
10 files changed
+129
-31
No files matched your search
@@ -15,6 +15,10 @@ type CanvasClipboard = {
|
||||
connections: CanvasConnection[];
|
||||
};
|
||||
|
||||
const CANVAS_NODES_CLIPBOARD_PREFIX = "open-ai-canvas-nodes:";
|
||||
const CANVAS_NODES_JSON_CLIPBOARD_PREFIX = "open-ai-canvas-nodes-json:";
|
||||
const CANVAS_NODES_CLIPBOARD_STORAGE_KEY = "open-ai-canvas:nodes-clipboard";
|
||||
|
||||
type UseCanvasNodeOperationsOptions = {
|
||||
projectId: string;
|
||||
nodesRef: { current: CanvasNodeData[] };
|
||||
@@ -49,9 +53,11 @@ export function useCanvasNodeOperations({
|
||||
const preferCopiedNodesRef = useRef(false);
|
||||
const markerWritePendingRef = useRef(false);
|
||||
const markerWriteSequenceRef = useRef(0);
|
||||
const internalMarkerWriteRef = useRef(false);
|
||||
const [hasCopiedNodes, setHasCopiedNodes] = useState(false);
|
||||
|
||||
const releaseCopiedNodesPastePriority = useCallback(() => {
|
||||
if (internalMarkerWriteRef.current) return;
|
||||
markerWriteSequenceRef.current += 1;
|
||||
markerWritePendingRef.current = false;
|
||||
preferCopiedNodesRef.current = false;
|
||||
@@ -330,26 +336,33 @@ export function useCanvasNodeOperations({
|
||||
.filter((node) => copyIds.has(node.id))
|
||||
.map((node) => ({ ...node, position: { ...node.position }, metadata: node.metadata ? { ...node.metadata, frame: node.metadata.frame ? { ...node.metadata.frame } : undefined } : undefined }));
|
||||
if (!copiedNodes.length) return;
|
||||
clipboardRef.current = {
|
||||
nodes: copiedNodes,
|
||||
connections: connectionsRef.current.filter((connection) => copyIds.has(connection.fromNodeId) && copyIds.has(connection.toNodeId)).map((connection) => ({ ...connection })),
|
||||
};
|
||||
const copiedConnections = connectionsRef.current.filter((connection) => copyIds.has(connection.fromNodeId) && copyIds.has(connection.toNodeId)).map((connection) => ({ ...connection }));
|
||||
clipboardRef.current = { nodes: copiedNodes, connections: copiedConnections };
|
||||
try {
|
||||
sessionStorage.setItem(CANVAS_NODES_CLIPBOARD_STORAGE_KEY, JSON.stringify(clipboardRef.current));
|
||||
} catch {
|
||||
// 大型媒体节点可能超过浏览器存储配额,当前页面内仍可正常粘贴。
|
||||
}
|
||||
setHasCopiedNodes(true);
|
||||
// 写入完成前或写入失败时优先内部节点,避免快速粘贴读到系统残留图片。
|
||||
const marker = `open-ai-canvas-nodes:${Date.now()}:${copiedNodes.length}`;
|
||||
const marker = `${CANVAS_NODES_CLIPBOARD_PREFIX}${Date.now()}:${copiedNodes.length}`;
|
||||
const sequence = markerWriteSequenceRef.current + 1;
|
||||
markerWriteSequenceRef.current = sequence;
|
||||
markerWritePendingRef.current = true;
|
||||
preferCopiedNodesRef.current = true;
|
||||
void copyToClipboard(marker, { format: "text/plain" }).then((written) => {
|
||||
if (markerWriteSequenceRef.current !== sequence) return;
|
||||
markerWritePendingRef.current = false;
|
||||
preferCopiedNodesRef.current = !written;
|
||||
}, () => {
|
||||
try {
|
||||
internalMarkerWriteRef.current = true;
|
||||
copyToClipboard(marker, { format: "text/plain" });
|
||||
internalMarkerWriteRef.current = false;
|
||||
if (markerWriteSequenceRef.current !== sequence) return;
|
||||
markerWritePendingRef.current = false;
|
||||
preferCopiedNodesRef.current = true;
|
||||
});
|
||||
} catch {
|
||||
internalMarkerWriteRef.current = false;
|
||||
if (markerWriteSequenceRef.current !== sequence) return;
|
||||
markerWritePendingRef.current = false;
|
||||
preferCopiedNodesRef.current = true;
|
||||
}
|
||||
}, [connectionsRef, nodesRef]);
|
||||
|
||||
const copySelectedNodes = useCallback(() => {
|
||||
@@ -417,6 +430,25 @@ export function useCanvasNodeOperations({
|
||||
return true;
|
||||
}, [cloneDrawingForNode, commitConnections, commitNodes, connectionsRef, getCanvasCenter, nodesRef, selectNodes, setContextMenu, setDialogNodeId]);
|
||||
|
||||
const restoreCopiedNodesFromText = useCallback((value: string) => {
|
||||
const isMarker = value.startsWith(CANVAS_NODES_CLIPBOARD_PREFIX);
|
||||
const isLegacyJSON = value.startsWith(CANVAS_NODES_JSON_CLIPBOARD_PREFIX);
|
||||
if (!isMarker && !isLegacyJSON) return false;
|
||||
try {
|
||||
const serialized = isLegacyJSON ? value.slice(CANVAS_NODES_JSON_CLIPBOARD_PREFIX.length) : sessionStorage.getItem(CANVAS_NODES_CLIPBOARD_STORAGE_KEY);
|
||||
if (!serialized) return false;
|
||||
const parsed = JSON.parse(serialized) as Partial<CanvasClipboard>;
|
||||
if (!parsed.nodes?.length) return false;
|
||||
clipboardRef.current = { nodes: parsed.nodes, connections: parsed.connections || [] };
|
||||
setHasCopiedNodes(true);
|
||||
preferCopiedNodesRef.current = true;
|
||||
markerWritePendingRef.current = false;
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}, []);
|
||||
|
||||
return {
|
||||
alignSelectedNodes,
|
||||
arrangeSelectedNodes,
|
||||
@@ -430,6 +462,7 @@ export function useCanvasNodeOperations({
|
||||
duplicateNode,
|
||||
hasCopiedNodes,
|
||||
pasteCopiedNodes,
|
||||
restoreCopiedNodesFromText,
|
||||
releaseCopiedNodesPastePriority,
|
||||
setPrimaryVersion,
|
||||
shouldPreferCopiedNodes,
|
||||
|
||||
Reference in new issue
Block a user