mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-29 10:42:44 +00:00
Graph canvas menu (#1023)
* add graph canvas menu * Move to corner * Remove action bar reset zoom button * nit * Add setting --------- Co-authored-by: huchenlei <chenlei.hu@mail.utoronto.ca>
This commit is contained in:
@@ -9,8 +9,8 @@
|
|||||||
>
|
>
|
||||||
<slot name="side-bar-panel"></slot>
|
<slot name="side-bar-panel"></slot>
|
||||||
</SplitterPanel>
|
</SplitterPanel>
|
||||||
<SplitterPanel class="graph-canvas-panel" :size="100">
|
<SplitterPanel class="graph-canvas-panel relative" :size="100">
|
||||||
<div></div>
|
<slot name="graph-canvas-panel"></slot>
|
||||||
</SplitterPanel>
|
</SplitterPanel>
|
||||||
<SplitterPanel
|
<SplitterPanel
|
||||||
class="side-bar-panel"
|
class="side-bar-panel"
|
||||||
|
|||||||
@@ -61,12 +61,6 @@
|
|||||||
commandStore.getCommandFunction('Comfy.RefreshNodeDefinitions')()
|
commandStore.getCommandFunction('Comfy.RefreshNodeDefinitions')()
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
<Button
|
|
||||||
v-tooltip.bottom="$t('menu.resetView')"
|
|
||||||
icon="pi pi-expand"
|
|
||||||
severity="secondary"
|
|
||||||
@click="() => commandStore.getCommandFunction('Comfy.ResetView')()"
|
|
||||||
/>
|
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
</div>
|
</div>
|
||||||
</Panel>
|
</Panel>
|
||||||
|
|||||||
@@ -4,6 +4,9 @@
|
|||||||
<template #side-bar-panel>
|
<template #side-bar-panel>
|
||||||
<SideToolbar />
|
<SideToolbar />
|
||||||
</template>
|
</template>
|
||||||
|
<template #graph-canvas-panel>
|
||||||
|
<GraphCanvasMenu v-if="canvasMenuEnabled" />
|
||||||
|
</template>
|
||||||
</LiteGraphCanvasSplitterOverlay>
|
</LiteGraphCanvasSplitterOverlay>
|
||||||
<TitleEditor />
|
<TitleEditor />
|
||||||
<canvas ref="canvasRef" id="graph-canvas" tabindex="1" />
|
<canvas ref="canvasRef" id="graph-canvas" tabindex="1" />
|
||||||
@@ -44,6 +47,7 @@ import { useNodeBookmarkStore } from '@/stores/nodeBookmarkStore'
|
|||||||
import { useCanvasStore } from '@/stores/graphStore'
|
import { useCanvasStore } from '@/stores/graphStore'
|
||||||
import { ComfyModelDef } from '@/stores/modelStore'
|
import { ComfyModelDef } from '@/stores/modelStore'
|
||||||
import { useModelToNodeStore } from '@/stores/modelToNodeStore'
|
import { useModelToNodeStore } from '@/stores/modelToNodeStore'
|
||||||
|
import GraphCanvasMenu from '@/components/graph/GraphCanvasMenu.vue'
|
||||||
|
|
||||||
const emit = defineEmits(['ready'])
|
const emit = defineEmits(['ready'])
|
||||||
const canvasRef = ref<HTMLCanvasElement | null>(null)
|
const canvasRef = ref<HTMLCanvasElement | null>(null)
|
||||||
@@ -55,6 +59,9 @@ const modelToNodeStore = useModelToNodeStore()
|
|||||||
const betaMenuEnabled = computed(
|
const betaMenuEnabled = computed(
|
||||||
() => settingStore.get('Comfy.UseNewMenu') !== 'Disabled'
|
() => settingStore.get('Comfy.UseNewMenu') !== 'Disabled'
|
||||||
)
|
)
|
||||||
|
const canvasMenuEnabled = computed(() =>
|
||||||
|
settingStore.get('Comfy.Graph.CanvasMenu')
|
||||||
|
)
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
const canvasInfoEnabled = settingStore.get('Comfy.Graph.CanvasInfo')
|
const canvasInfoEnabled = settingStore.get('Comfy.Graph.CanvasInfo')
|
||||||
|
|||||||
81
src/components/graph/GraphCanvasMenu.vue
Normal file
81
src/components/graph/GraphCanvasMenu.vue
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
<template>
|
||||||
|
<ButtonGroup
|
||||||
|
class="p-buttongroup-vertical absolute bottom-[10px] right-[10px] z-[1000] pointer-events-auto"
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
severity="secondary"
|
||||||
|
icon="pi pi-plus"
|
||||||
|
v-tooltip.left="t('graphCanvasMenu.zoomIn')"
|
||||||
|
@mousedown="repeat('Comfy.Canvas.ZoomIn')"
|
||||||
|
@mouseup="stopRepeat"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
severity="secondary"
|
||||||
|
icon="pi pi-minus"
|
||||||
|
v-tooltip.left="t('graphCanvasMenu.zoomOut')"
|
||||||
|
@mousedown="repeat('Comfy.Canvas.ZoomOut')"
|
||||||
|
@mouseup="stopRepeat"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
severity="secondary"
|
||||||
|
icon="pi pi-expand"
|
||||||
|
v-tooltip.left="t('graphCanvasMenu.resetView')"
|
||||||
|
@click="() => commandStore.getCommandFunction('Comfy.Canvas.ResetView')()"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
severity="secondary"
|
||||||
|
v-tooltip.left="
|
||||||
|
t('graphCanvasMenu.' + (canvasStore.readOnly ? 'unlock' : 'lock'))
|
||||||
|
"
|
||||||
|
@click="
|
||||||
|
() => commandStore.getCommandFunction('Comfy.Canvas.ToggleLock')()
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<i-material-symbols:lock-outline v-if="canvasStore.readOnly" />
|
||||||
|
<i-material-symbols:lock-open-outline v-else />
|
||||||
|
</template>
|
||||||
|
</Button>
|
||||||
|
</ButtonGroup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import ButtonGroup from 'primevue/buttongroup'
|
||||||
|
import Button from 'primevue/button'
|
||||||
|
import { useCommandStore } from '@/stores/commandStore'
|
||||||
|
import { useCanvasStore } from '@/stores/graphStore'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
const commandStore = useCommandStore()
|
||||||
|
const canvasStore = useCanvasStore()
|
||||||
|
|
||||||
|
let interval: number | null = null
|
||||||
|
const repeat = (command: string) => {
|
||||||
|
if (interval) return
|
||||||
|
const cmd = commandStore.getCommandFunction(command)
|
||||||
|
cmd()
|
||||||
|
interval = window.setInterval(cmd, 100)
|
||||||
|
}
|
||||||
|
const stopRepeat = () => {
|
||||||
|
if (interval) {
|
||||||
|
clearInterval(interval)
|
||||||
|
interval = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.p-buttongroup-vertical {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border-radius: var(--p-button-border-radius);
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid var(--p-panel-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-buttongroup-vertical .p-button {
|
||||||
|
margin: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -94,6 +94,13 @@ const messages = {
|
|||||||
upscale: '2 Pass Upscale',
|
upscale: '2 Pass Upscale',
|
||||||
flux_schnell: 'Flux Schnell'
|
flux_schnell: 'Flux Schnell'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
graphCanvasMenu: {
|
||||||
|
zoomIn: 'Zoom In',
|
||||||
|
zoomOut: 'Zoom Out',
|
||||||
|
resetView: 'Reset View',
|
||||||
|
lock: 'Lock Graph',
|
||||||
|
unlock: 'Unlock Graph'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
zh: {
|
zh: {
|
||||||
|
|||||||
@@ -354,7 +354,8 @@ LGraphNode.prototype.addDOMWidget = function (
|
|||||||
width: `${widgetWidth - margin * 2}px`,
|
width: `${widgetWidth - margin * 2}px`,
|
||||||
height: `${(widget.computedHeight ?? 50) - margin * 2}px`,
|
height: `${(widget.computedHeight ?? 50) - margin * 2}px`,
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
zIndex: app.graph.nodes.indexOf(node)
|
zIndex: app.graph.nodes.indexOf(node),
|
||||||
|
pointerEvents: app.canvas.read_only ? 'none' : 'auto'
|
||||||
})
|
})
|
||||||
|
|
||||||
if (enableDomClipping) {
|
if (enableDomClipping) {
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ export const useCommandStore = defineStore('command', () => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'Comfy.ResetView',
|
id: 'Comfy.Canvas.ResetView',
|
||||||
icon: 'pi pi-expand',
|
icon: 'pi pi-expand',
|
||||||
label: 'Reset View',
|
label: 'Reset View',
|
||||||
function: () => {
|
function: () => {
|
||||||
@@ -180,6 +180,32 @@ export const useCommandStore = defineStore('command', () => {
|
|||||||
icon: 'pi pi-folder-open',
|
icon: 'pi pi-folder-open',
|
||||||
label: 'Browse Templates',
|
label: 'Browse Templates',
|
||||||
function: showTemplateWorkflowsDialog
|
function: showTemplateWorkflowsDialog
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'Comfy.Canvas.ZoomIn',
|
||||||
|
icon: 'pi pi-plus',
|
||||||
|
label: 'Zoom In',
|
||||||
|
function: () => {
|
||||||
|
app.canvas.ds.changeScale(app.canvas.ds.scale + 0.1)
|
||||||
|
app.canvas.setDirty(true, true)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'Comfy.Canvas.ZoomOut',
|
||||||
|
icon: 'pi pi-minus',
|
||||||
|
label: 'Zoom Out',
|
||||||
|
function: () => {
|
||||||
|
app.canvas.ds.changeScale(app.canvas.ds.scale - 0.1)
|
||||||
|
app.canvas.setDirty(true, true)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'Comfy.Canvas.ToggleLock',
|
||||||
|
icon: 'pi pi-lock',
|
||||||
|
label: 'Toggle Lock',
|
||||||
|
function: () => {
|
||||||
|
app.canvas['read_only'] = !app.canvas['read_only']
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ export const CORE_SETTINGS: SettingParams[] = [
|
|||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
defaultValue: true
|
defaultValue: true
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
id: 'Comfy.NodeSearchBoxImpl',
|
id: 'Comfy.NodeSearchBoxImpl',
|
||||||
category: ['Comfy', 'Node Search Box', 'Implementation'],
|
category: ['Comfy', 'Node Search Box', 'Implementation'],
|
||||||
@@ -381,5 +380,11 @@ export const CORE_SETTINGS: SettingParams[] = [
|
|||||||
type: 'hidden',
|
type: 'hidden',
|
||||||
options: ['Sidebar', 'Topbar'],
|
options: ['Sidebar', 'Topbar'],
|
||||||
defaultValue: 'Sidebar'
|
defaultValue: 'Sidebar'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'Comfy.Graph.CanvasMenu',
|
||||||
|
name: 'Show graph canvas menu',
|
||||||
|
type: 'boolean',
|
||||||
|
defaultValue: true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { LGraphNode, LGraphGroup, LGraphCanvas } from '@comfyorg/litegraph'
|
import { LGraphNode, LGraphGroup, LGraphCanvas } from '@comfyorg/litegraph'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { shallowRef } from 'vue'
|
import { ref, shallowRef } from 'vue'
|
||||||
|
|
||||||
export const useTitleEditorStore = defineStore('titleEditor', () => {
|
export const useTitleEditorStore = defineStore('titleEditor', () => {
|
||||||
const titleEditorTarget = shallowRef<LGraphNode | LGraphGroup | null>(null)
|
const titleEditorTarget = shallowRef<LGraphNode | LGraphGroup | null>(null)
|
||||||
@@ -12,8 +12,19 @@ export const useTitleEditorStore = defineStore('titleEditor', () => {
|
|||||||
|
|
||||||
export const useCanvasStore = defineStore('canvas', () => {
|
export const useCanvasStore = defineStore('canvas', () => {
|
||||||
const canvas = shallowRef<LGraphCanvas | null>(null)
|
const canvas = shallowRef<LGraphCanvas | null>(null)
|
||||||
|
const readOnly = ref(false)
|
||||||
|
|
||||||
|
document.addEventListener(
|
||||||
|
'litegraph:canvas',
|
||||||
|
(e: CustomEvent<{ subType: string; readOnly: boolean }>) => {
|
||||||
|
if (e.detail?.subType === 'read-only') {
|
||||||
|
readOnly.value = e.detail.readOnly
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
canvas
|
canvas,
|
||||||
|
readOnly
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user