Manage canvas element in Vue (#255)

* Manage canvas element in Vue

* nit

* Fix unittest
This commit is contained in:
Chenlei Hu
2024-07-29 10:29:29 -04:00
committed by GitHub
parent 6e27b884fc
commit 66b690e5c8
5 changed files with 72 additions and 56 deletions

View File

@@ -1,22 +1,11 @@
<template> <template>
<ProgressSpinner v-if="isLoading" class="spinner"></ProgressSpinner> <ProgressSpinner v-if="isLoading" class="spinner"></ProgressSpinner>
<div v-else> <GraphCanvas v-else />
<NodeSearchboxPopover v-if="nodeSearchEnabled" />
<teleport to=".graph-canvas-container">
<LiteGraphCanvasSplitterOverlay v-if="betaMenuEnabled">
<template #side-bar-panel>
<SideToolBar />
</template>
</LiteGraphCanvasSplitterOverlay>
</teleport>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, markRaw, onMounted, onUnmounted, ref, watch } from 'vue' import { computed, markRaw, onMounted, ref, watch } from 'vue'
import NodeSearchboxPopover from '@/components/NodeSearchBoxPopover.vue' import GraphCanvas from '@/components/graph/GraphCanvas.vue'
import SideToolBar from '@/components/sidebar/SideToolBar.vue'
import LiteGraphCanvasSplitterOverlay from '@/components/LiteGraphCanvasSplitterOverlay.vue'
import QueueSideBarTab from '@/components/sidebar/tabs/QueueSideBarTab.vue' import QueueSideBarTab from '@/components/sidebar/tabs/QueueSideBarTab.vue'
import ProgressSpinner from 'primevue/progressspinner' import ProgressSpinner from 'primevue/progressspinner'
import { app } from './scripts/app' import { app } from './scripts/app'
@@ -24,13 +13,8 @@ import { useSettingStore } from './stores/settingStore'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useWorkspaceStore } from './stores/workspaceStateStore' import { useWorkspaceStore } from './stores/workspaceStateStore'
import NodeLibrarySideBarTab from './components/sidebar/tabs/NodeLibrarySideBarTab.vue' import NodeLibrarySideBarTab from './components/sidebar/tabs/NodeLibrarySideBarTab.vue'
import { dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter'
import { useNodeDefStore } from './stores/nodeDefStore'
const isLoading = ref(true) const isLoading = ref(true)
const nodeSearchEnabled = computed<boolean>(
() => useSettingStore().get('Comfy.NodeSearchBoxImpl') === 'default'
)
const theme = computed<string>(() => const theme = computed<string>(() =>
useSettingStore().get('Comfy.ColorPalette') useSettingStore().get('Comfy.ColorPalette')
) )
@@ -47,15 +31,10 @@ watch(
}, },
{ immediate: true } { immediate: true }
) )
const betaMenuEnabled = computed(
() => useSettingStore().get('Comfy.UseNewMenu') !== 'Disabled'
)
const { t } = useI18n() const { t } = useI18n()
let dropTargetCleanup = () => {}
const init = () => { const init = () => {
useSettingStore().addSettings(app.ui.settings) useSettingStore().addSettings(app.ui.settings)
app.vueAppReady = true
app.extensionManager = useWorkspaceStore() app.extensionManager = useWorkspaceStore()
app.extensionManager.registerSidebarTab({ app.extensionManager.registerSidebarTab({
id: 'queue', id: 'queue',
@@ -73,21 +52,6 @@ const init = () => {
component: markRaw(NodeLibrarySideBarTab), component: markRaw(NodeLibrarySideBarTab),
type: 'vue' type: 'vue'
}) })
dropTargetCleanup = dropTargetForElements({
element: document.querySelector('.graph-canvas-container'),
onDrop: (event) => {
const loc = event.location.current.input
// Add an offset on x to make sure after adding the node, the cursor
// is on the node (top left corner)
const pos = app.clientPosToCanvasPos([loc.clientX - 20, loc.clientY])
const comfyNodeName = event.source.element.getAttribute(
'data-comfy-node-name'
)
const nodeDef = useNodeDefStore().nodeDefsByName[comfyNodeName]
app.addNodeOnGraph(nodeDef, { pos })
}
})
} }
onMounted(() => { onMounted(() => {
@@ -99,10 +63,6 @@ onMounted(() => {
isLoading.value = false isLoading.value = false
} }
}) })
onUnmounted(() => {
dropTargetCleanup()
})
</script> </script>
<style scoped> <style scoped>

View File

@@ -0,0 +1,61 @@
<template>
<teleport to=".graph-canvas-container">
<LiteGraphCanvasSplitterOverlay v-if="betaMenuEnabled">
<template #side-bar-panel>
<SideToolBar />
</template>
</LiteGraphCanvasSplitterOverlay>
<canvas ref="canvasRef" id="graph-canvas" tabindex="1" />
</teleport>
<NodeSearchboxPopover v-if="nodeSearchEnabled" />
</template>
<script setup lang="ts">
import SideToolBar from '@/components/sidebar/SideToolBar.vue'
import LiteGraphCanvasSplitterOverlay from '@/components/LiteGraphCanvasSplitterOverlay.vue'
import NodeSearchboxPopover from '@/components/NodeSearchBoxPopover.vue'
import { ref, onMounted, computed, onUnmounted } from 'vue'
import { app as comfyApp } from '@/scripts/app'
import { useSettingStore } from '@/stores/settingStore'
import { dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter'
import { useNodeDefStore } from '@/stores/nodeDefStore'
const emit = defineEmits(['ready'])
const canvasRef = ref<HTMLCanvasElement | null>(null)
const betaMenuEnabled = computed(
() => useSettingStore().get('Comfy.UseNewMenu') !== 'Disabled'
)
const nodeSearchEnabled = computed<boolean>(
() => useSettingStore().get('Comfy.NodeSearchBoxImpl') === 'default'
)
let dropTargetCleanup = () => {}
onMounted(async () => {
comfyApp.vueAppReady = true
await comfyApp.setup(canvasRef.value)
window['app'] = comfyApp
window['graph'] = comfyApp.graph
dropTargetCleanup = dropTargetForElements({
element: canvasRef.value,
onDrop: (event) => {
const loc = event.location.current.input
// Add an offset on x to make sure after adding the node, the cursor
// is on the node (top left corner)
const pos = comfyApp.clientPosToCanvasPos([loc.clientX - 20, loc.clientY])
const comfyNodeName = event.source.element.getAttribute(
'data-comfy-node-name'
)
const nodeDef = useNodeDefStore().nodeDefsByName[comfyNodeName]
comfyApp.addNodeOnGraph(nodeDef, { pos })
}
})
emit('ready')
})
onUnmounted(() => {
dropTargetCleanup()
})
</script>

View File

@@ -8,7 +8,6 @@ import Tooltip from 'primevue/tooltip'
import 'primeicons/primeicons.css' import 'primeicons/primeicons.css'
import App from './App.vue' import App from './App.vue'
import { app as comfyApp } from '@/scripts/app'
import { createPinia } from 'pinia' import { createPinia } from 'pinia'
import { i18n } from './i18n' import { i18n } from './i18n'
@@ -40,8 +39,3 @@ app
.use(pinia) .use(pinia)
.use(i18n) .use(i18n)
.mount('#vue-app') .mount('#vue-app')
comfyApp.setup().then(() => {
window['app'] = comfyApp
window['graph'] = comfyApp.graph
})

View File

@@ -1853,17 +1853,13 @@ export class ComfyApp {
/** /**
* Set up the app on the page * Set up the app on the page
*/ */
async setup() { async setup(canvasEl: HTMLCanvasElement) {
this.canvasEl = canvasEl
await this.#setUser() await this.#setUser()
// Create and mount the LiteGraph in the DOM // Create and mount the LiteGraph in the DOM
const mainCanvas = document.createElement('canvas') const mainCanvas = document.createElement('canvas')
mainCanvas.style.touchAction = 'none' mainCanvas.style.touchAction = 'none'
const canvasEl = (this.canvasEl = Object.assign(mainCanvas, {
id: 'graph-canvas'
}))
canvasEl.tabIndex = 1
this.canvasContainer.prepend(canvasEl)
this.resizeCanvas() this.resizeCanvas()

View File

@@ -43,7 +43,12 @@ export async function start(config: StartConfig = {}): Promise<StartResult> {
const { app } = await import('../../src/scripts/app') const { app } = await import('../../src/scripts/app')
const { LiteGraph, LGraphCanvas } = await import('@comfyorg/litegraph') const { LiteGraph, LGraphCanvas } = await import('@comfyorg/litegraph')
config.preSetup?.(app) config.preSetup?.(app)
await app.setup() const canvasEl = document.createElement('canvas')
canvasEl.style.touchAction = 'none'
canvasEl.id = 'graph-canvas'
canvasEl.tabIndex = 1
app.canvasContainer.prepend(canvasEl)
await app.setup(canvasEl)
// @ts-ignore // @ts-ignore
return { ...Ez.graph(app, LiteGraph, LGraphCanvas), app } return { ...Ez.graph(app, LiteGraph, LGraphCanvas), app }