diff --git a/src/components/graph/GraphCanvas.vue b/src/components/graph/GraphCanvas.vue index c6ce24919..1bf3f5355 100644 --- a/src/components/graph/GraphCanvas.vue +++ b/src/components/graph/GraphCanvas.vue @@ -45,6 +45,7 @@ import { useCanvasDrop } from '@/composables/useCanvasDrop' import { useContextMenuTranslation } from '@/composables/useContextMenuTranslation' import { useCopy } from '@/composables/useCopy' import { useGlobalLitegraph } from '@/composables/useGlobalLitegraph' +import { usePaste } from '@/composables/usePaste' import { useWorkflowPersistence } from '@/composables/useWorkflowPersistence' import { CORE_SETTINGS } from '@/constants/coreSettings' import { i18n } from '@/i18n' @@ -265,6 +266,7 @@ onMounted(async () => { useGlobalLitegraph() useContextMenuTranslation() useCopy() + usePaste() comfyApp.vueAppReady = true diff --git a/src/composables/usePaste.ts b/src/composables/usePaste.ts new file mode 100644 index 000000000..5e8c31b17 --- /dev/null +++ b/src/composables/usePaste.ts @@ -0,0 +1,94 @@ +import { LiteGraph } from '@comfyorg/litegraph' +import type { LGraphNode } from '@comfyorg/litegraph' +import { useEventListener } from '@vueuse/core' + +import { app } from '@/scripts/app' +import { useCanvasStore } from '@/stores/graphStore' +import { useWorkspaceStore } from '@/stores/workspaceStore' +import { ComfyWorkflowJSON } from '@/types/comfyWorkflow' +import { isImageNode } from '@/utils/litegraphUtil' + +/** + * Adds a handler on paste that extracts and loads images or workflows from pasted JSON data + */ +export const usePaste = () => { + const workspaceStore = useWorkspaceStore() + const canvasStore = useCanvasStore() + + useEventListener(document, 'paste', async (e: ClipboardEvent) => { + // ctrl+shift+v is used to paste nodes with connections + // this is handled by litegraph + if (workspaceStore.shiftDown) return + + const canvas = canvasStore.canvas + if (!canvas) return + + const graph = canvas.graph + // @ts-expect-error: Property 'clipboardData' does not exist on type 'Window & typeof globalThis'. + // Did you mean 'Clipboard'?ts(2551) + // TODO: Not sure what the code wants to do. + let data = e.clipboardData || window.clipboardData + const items = data.items + + // Look for image paste data + for (const item of items) { + if (item.type.startsWith('image/')) { + let imageNode: LGraphNode | null = null + + // If an image node is selected, paste into it + const currentNode = canvas.current_node as LGraphNode + if ( + currentNode && + currentNode.is_selected && + isImageNode(currentNode) + ) { + imageNode = currentNode + } + + // No image node selected: add a new one + if (!imageNode) { + const newNode = LiteGraph.createNode('LoadImage') + // @ts-expect-error array to Float32Array + newNode.pos = [...canvas.graph_mouse] + imageNode = graph.add(newNode) ?? null + graph.change() + } + const blob = item.getAsFile() + // @ts-expect-error: Property 'pasteFile' does not exist on type 'LGraphNode'. + imageNode?.pasteFile(blob) + return + } + } + + // No image found. Look for node data + data = data.getData('text/plain') + let workflow: ComfyWorkflowJSON | null = null + try { + data = data.slice(data.indexOf('{')) + workflow = JSON.parse(data) + } catch (err) { + try { + data = data.slice(data.indexOf('workflow\n')) + data = data.slice(data.indexOf('{')) + workflow = JSON.parse(data) + } catch (error) { + workflow = null + } + } + + if (workflow && workflow.version && workflow.nodes && workflow.extra) { + await app.loadGraphData(workflow) + } else { + if ( + (e.target instanceof HTMLTextAreaElement && + e.target.type === 'textarea') || + (e.target instanceof HTMLInputElement && e.target.type === 'text') + ) { + return + } + + // Litegraph default paste + canvas.pasteFromClipboard() + } + }) +} diff --git a/src/scripts/app.ts b/src/scripts/app.ts index 15e02919a..7468bbca4 100644 --- a/src/scripts/app.ts +++ b/src/scripts/app.ts @@ -477,82 +477,6 @@ export class ComfyApp { ) } - /** - * Adds a handler on paste that extracts and loads images or workflows from pasted JSON data - */ - #addPasteHandler() { - document.addEventListener('paste', async (e: ClipboardEvent) => { - // ctrl+shift+v is used to paste nodes with connections - // this is handled by litegraph - if (this.shiftDown) return - - // @ts-expect-error: Property 'clipboardData' does not exist on type 'Window & typeof globalThis'. - // Did you mean 'Clipboard'?ts(2551) - // TODO: Not sure what the code wants to do. - let data = e.clipboardData || window.clipboardData - const items = data.items - - // Look for image paste data - for (const item of items) { - if (item.type.startsWith('image/')) { - var imageNode = null - - // If an image node is selected, paste into it - if ( - this.canvas.current_node && - this.canvas.current_node.is_selected && - isImageNode(this.canvas.current_node) - ) { - imageNode = this.canvas.current_node - } - - // No image node selected: add a new one - if (!imageNode) { - const newNode = LiteGraph.createNode('LoadImage') - // @ts-expect-error array to Float32Array - newNode.pos = [...this.canvas.graph_mouse] - imageNode = this.graph.add(newNode) - this.graph.change() - } - const blob = item.getAsFile() - imageNode.pasteFile(blob) - return - } - } - - // No image found. Look for node data - data = data.getData('text/plain') - let workflow: ComfyWorkflowJSON | null = null - try { - data = data.slice(data.indexOf('{')) - workflow = JSON.parse(data) - } catch (err) { - try { - data = data.slice(data.indexOf('workflow\n')) - data = data.slice(data.indexOf('{')) - workflow = JSON.parse(data) - } catch (error) { - workflow = null - } - } - - if (workflow && workflow.version && workflow.nodes && workflow.extra) { - await this.loadGraphData(workflow) - } else { - if ( - (e.target instanceof HTMLTextAreaElement && - e.target.type === 'textarea') || - (e.target instanceof HTMLInputElement && e.target.type === 'text') - ) { - return - } - - // Litegraph default paste - this.canvas.pasteFromClipboard() - } - }) - } - /** * Handle mouse * @@ -977,7 +901,6 @@ export class ComfyApp { this.#addDrawNodeHandler() this.#addDrawGroupsHandler() this.#addDropHandler() - this.#addPasteHandler() await useExtensionService().invokeExtensionsAsync('setup') }