mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-02 22:37:32 +00:00
Double click group title to edit (#714)
* Double click group title to edit * Add playwright test * Update test expectations [skip ci] --------- Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
88
browser_tests/assets/single_group.json
Normal file
88
browser_tests/assets/single_group.json
Normal file
@@ -0,0 +1,88 @@
|
||||
{
|
||||
"last_node_id": 9,
|
||||
"last_link_id": 13,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 3,
|
||||
"type": "KSampler",
|
||||
"pos": {
|
||||
"0": 10.321063995361328,
|
||||
"1": 73.14462280273438
|
||||
},
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 262
|
||||
},
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "model",
|
||||
"type": "MODEL",
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"name": "positive",
|
||||
"type": "CONDITIONING",
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"name": "negative",
|
||||
"type": "CONDITIONING",
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"name": "latent_image",
|
||||
"type": "LATENT",
|
||||
"link": null
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"links": [],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "KSampler"
|
||||
},
|
||||
"widgets_values": [
|
||||
156680208700286,
|
||||
"randomize",
|
||||
20,
|
||||
8,
|
||||
"euler",
|
||||
"normal",
|
||||
1
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"groups": [
|
||||
{
|
||||
"title": "Group",
|
||||
"bounding": [
|
||||
0,
|
||||
0,
|
||||
335,
|
||||
346
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24
|
||||
}
|
||||
],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 1,
|
||||
"offset": [
|
||||
0,
|
||||
0
|
||||
]
|
||||
}
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
@@ -169,6 +169,21 @@ test.describe('Node Interaction', () => {
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Group Interaction', () => {
|
||||
test('Can double click group title to edit', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('single_group')
|
||||
await comfyPage.canvas.dblclick({
|
||||
position: {
|
||||
x: 50,
|
||||
y: 10
|
||||
}
|
||||
})
|
||||
await comfyPage.page.keyboard.type('Hello World')
|
||||
await comfyPage.page.keyboard.press('Enter')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('group-title-edited.png')
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Canvas Interaction', () => {
|
||||
test('Can zoom in/out', async ({ comfyPage }) => {
|
||||
await comfyPage.zoom(-100)
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 63 KiB |
8
package-lock.json
generated
8
package-lock.json
generated
@@ -9,7 +9,7 @@
|
||||
"version": "1.2.43",
|
||||
"dependencies": {
|
||||
"@atlaskit/pragmatic-drag-and-drop": "^1.2.1",
|
||||
"@comfyorg/litegraph": "^0.7.58",
|
||||
"@comfyorg/litegraph": "^0.7.59",
|
||||
"@primevue/themes": "^4.0.0-rc.2",
|
||||
"@vitejs/plugin-vue": "^5.0.5",
|
||||
"@vueuse/core": "^11.0.0",
|
||||
@@ -1883,9 +1883,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@comfyorg/litegraph": {
|
||||
"version": "0.7.58",
|
||||
"resolved": "https://registry.npmjs.org/@comfyorg/litegraph/-/litegraph-0.7.58.tgz",
|
||||
"integrity": "sha512-0+anFQgfGVLy33jyTKfvMFQ7mhv51wvBvqvG9Jc23aJHKW8/FJw6rdrsWDRNhSM1zfxhMPWWvDCoHhQt3mnR1g==",
|
||||
"version": "0.7.59",
|
||||
"resolved": "https://registry.npmjs.org/@comfyorg/litegraph/-/litegraph-0.7.59.tgz",
|
||||
"integrity": "sha512-Qy4uRa/16MxvsNubBXs+hOVVXKpmNHiQCg/VanFEv2koYrXi4mf/9zKWNNNukoDgL2aMoFpVkXS5O/TiWzFWbg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@cspotcode/source-map-support": {
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@atlaskit/pragmatic-drag-and-drop": "^1.2.1",
|
||||
"@comfyorg/litegraph": "^0.7.58",
|
||||
"@comfyorg/litegraph": "^0.7.59",
|
||||
"@primevue/themes": "^4.0.0-rc.2",
|
||||
"@vitejs/plugin-vue": "^5.0.5",
|
||||
"@vueuse/core": "^11.0.0",
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
</template>
|
||||
</LiteGraphCanvasSplitterOverlay>
|
||||
<NodeTitleEditor />
|
||||
<GroupTitleEditor />
|
||||
<canvas ref="canvasRef" id="graph-canvas" tabindex="1" />
|
||||
</teleport>
|
||||
<NodeSearchboxPopover v-if="nodeSearchEnabled" />
|
||||
@@ -14,6 +15,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import NodeTitleEditor from '@/components/graph/NodeTitleEditor.vue'
|
||||
import GroupTitleEditor from '@/components/graph/GroupTitleEditor.vue'
|
||||
import SideToolbar from '@/components/sidebar/SideToolbar.vue'
|
||||
import LiteGraphCanvasSplitterOverlay from '@/components/LiteGraphCanvasSplitterOverlay.vue'
|
||||
import NodeSearchboxPopover from '@/components/searchbox/NodeSearchBoxPopover.vue'
|
||||
|
||||
104
src/components/graph/GroupTitleEditor.vue
Normal file
104
src/components/graph/GroupTitleEditor.vue
Normal file
@@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<div v-if="showInput" class="group-title-editor" :style="inputStyle">
|
||||
<EditableText
|
||||
:isEditing="showInput"
|
||||
:modelValue="editedTitle"
|
||||
@edit="onEdit"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, CSSProperties } from 'vue'
|
||||
import { app } from '@/scripts/app'
|
||||
import { LGraphGroup } from '@comfyorg/litegraph'
|
||||
import EditableText from '@/components/common/EditableText.vue'
|
||||
import { useSettingStore } from '@/stores/settingStore'
|
||||
import type { LiteGraphCanvasEvent } from '@comfyorg/litegraph'
|
||||
|
||||
const settingStore = useSettingStore()
|
||||
|
||||
const showInput = ref(false)
|
||||
const editedTitle = ref('')
|
||||
const inputStyle = ref<CSSProperties>({
|
||||
position: 'fixed',
|
||||
left: '0px',
|
||||
top: '0px',
|
||||
width: '200px',
|
||||
height: '20px',
|
||||
fontSize: '12px'
|
||||
})
|
||||
|
||||
const currentGroup = ref<LGraphGroup | null>(null)
|
||||
|
||||
const onEdit = (newValue: string) => {
|
||||
if (currentGroup.value && newValue.trim() !== '') {
|
||||
currentGroup.value.title = newValue.trim()
|
||||
app.graph.setDirtyCanvas(true, true)
|
||||
}
|
||||
showInput.value = false
|
||||
}
|
||||
|
||||
const canvasEventHandler = (event: LiteGraphCanvasEvent) => {
|
||||
if (!settingStore.get('Comfy.Group.DoubleClickTitleToEdit')) {
|
||||
return
|
||||
}
|
||||
|
||||
if (event.detail.subType === 'group-double-click') {
|
||||
const group: LGraphGroup = event.detail.group
|
||||
const [x, y] = group.pos
|
||||
const [w, h] = group.size
|
||||
|
||||
const e: MouseEvent = event.detail.originalEvent
|
||||
// @ts-expect-error LiteGraphCanvasEvent is not typed
|
||||
const relativeY = e.canvasY - y
|
||||
// Only allow editing if the click is on the title bar
|
||||
if (relativeY > group.titleHeight) {
|
||||
return
|
||||
}
|
||||
|
||||
currentGroup.value = group
|
||||
editedTitle.value = group.title
|
||||
showInput.value = true
|
||||
|
||||
const [left, top] = app.canvasPosToClientPos([x, y])
|
||||
inputStyle.value.left = `${left}px`
|
||||
inputStyle.value.top = `${top}px`
|
||||
|
||||
const width = w * app.canvas.ds.scale
|
||||
const height = group.titleHeight * app.canvas.ds.scale
|
||||
inputStyle.value.width = `${width}px`
|
||||
inputStyle.value.height = `${height}px`
|
||||
|
||||
const fontSize = group.font_size * app.canvas.ds.scale
|
||||
inputStyle.value.fontSize = `${fontSize}px`
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('litegraph:canvas', canvasEventHandler)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
document.removeEventListener('litegraph:canvas', canvasEventHandler)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.node-title-editor {
|
||||
z-index: 9999;
|
||||
padding: 0.25rem;
|
||||
}
|
||||
|
||||
:deep(.editable-text) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:deep(.editable-text input) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
/* Override the default font size */
|
||||
font-size: inherit;
|
||||
}
|
||||
</style>
|
||||
@@ -55,8 +55,7 @@ const extension: ComfyExtension = {
|
||||
showInput.value = true
|
||||
|
||||
const isCollapsed = node.flags?.collapsed
|
||||
const [x1, y1, x2, y2] = this.getBounding()
|
||||
const [nodeWidth, nodeHeight] = this.size
|
||||
const [x1, y1, nodeWidth, nodeHeight] = this.getBounding()
|
||||
const canvasWidth =
|
||||
// @ts-expect-error Remove after collapsed_width is exposed in LiteGraph
|
||||
isCollapsed && node._collapsed_width ? node._collapsed_width : nodeWidth
|
||||
|
||||
@@ -100,6 +100,10 @@ const linkReleaseTriggerMode = computed(() => {
|
||||
})
|
||||
|
||||
const canvasEventHandler = (e: LiteGraphCanvasEvent) => {
|
||||
if (!['empty-release', 'empty-double-click'].includes(e.detail.subType)) {
|
||||
return
|
||||
}
|
||||
|
||||
const shiftPressed = (e.detail.originalEvent as KeyboardEvent).shiftKey
|
||||
|
||||
if (e.detail.subType === 'empty-release') {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { LGraphGroup } from '@comfyorg/litegraph'
|
||||
import { app } from '../../scripts/app'
|
||||
import { LGraphCanvas, LiteGraph } from '@comfyorg/litegraph'
|
||||
|
||||
@@ -77,8 +78,7 @@ app.registerExtension({
|
||||
content: 'Add Group For Selected Nodes',
|
||||
disabled: !Object.keys(app.canvas.selected_nodes || {}).length,
|
||||
callback: () => {
|
||||
// @ts-expect-error
|
||||
var group = new LiteGraph.LGraphGroup()
|
||||
const group = new LGraphGroup()
|
||||
addNodesToGroup(group, this.selected_nodes)
|
||||
app.canvas.graph.add(group)
|
||||
this.graph.change()
|
||||
|
||||
@@ -156,13 +156,11 @@ app.registerExtension({
|
||||
if (this.selected_group_resizing) {
|
||||
roundVectorToGrid(this.selected_group.size)
|
||||
} else if (selectedAndMovingGroup) {
|
||||
// @ts-expect-error
|
||||
const [x, y] = roundVectorToGrid([...selectedAndMovingGroup.pos])
|
||||
const f = ctx.fillStyle
|
||||
const s = ctx.strokeStyle
|
||||
ctx.fillStyle = 'rgba(100, 100, 100, 0.33)'
|
||||
ctx.strokeStyle = 'rgba(100, 100, 100, 0.66)'
|
||||
// @ts-expect-error
|
||||
ctx.rect(x, y, ...selectedAndMovingGroup.size)
|
||||
ctx.fill()
|
||||
ctx.stroke()
|
||||
@@ -183,9 +181,7 @@ app.registerExtension({
|
||||
// @ts-expect-error
|
||||
const lastGroup = app.graph._groups[app.graph._groups.length - 1]
|
||||
if (lastGroup) {
|
||||
// @ts-expect-error
|
||||
roundVectorToGrid(lastGroup.pos)
|
||||
// @ts-expect-error
|
||||
roundVectorToGrid(lastGroup.size)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,6 +277,13 @@ export const useSettingStore = defineStore('setting', {
|
||||
defaultValue: true
|
||||
})
|
||||
|
||||
app.ui.settings.addSetting({
|
||||
id: 'Comfy.Group.DoubleClickTitleToEdit',
|
||||
name: 'Double click group title to edit',
|
||||
type: 'boolean',
|
||||
defaultValue: true
|
||||
})
|
||||
|
||||
app.ui.settings.addSetting({
|
||||
id: 'Comfy.Window.UnloadConfirmation',
|
||||
name: 'Show confirmation when closing window',
|
||||
|
||||
Reference in New Issue
Block a user