Update Litegraph: Canvas Pointer (#1556)
* Litegraph: canvas.pointer Clear @ts-expect-error Fix exception thrown on slot double-click Long-standing bug but has no real impact in prod - just logs an error. Required for new connecting_link features. Add settings: CanvasPointer options Update litegraph 0.8.28 Fix regression in snap to grid render Fix snap to grid marker always on Update snap to grid to use Positionable API Fix test clicks registering as double-click Improve test precision Current test proves it has changed to something smaller. New test proves it is exactly what was specified. Will need refinement when a limit is put on latent width. Fix test expects collapse node to select node Remove redundant code Resolved by CanvasPointer Fix flaky test - ContextMenu Fix settings group * Update litegraph * Remove snapToGrid extension * Update test expectations [skip ci] --------- Co-authored-by: huchenlei <huchenlei@proton.me> Co-authored-by: github-actions <github-actions@github.com>
@@ -62,6 +62,7 @@ test.describe('Change Tracker', () => {
|
||||
expect(await getRedoQueueSize()).toBe(0)
|
||||
|
||||
const node = (await comfyPage.getFirstNodeRef())!
|
||||
await node.click('title')
|
||||
await node.click('collapse')
|
||||
await expect(node).toBeCollapsed()
|
||||
expect(await isModified()).toBe(true)
|
||||
@@ -98,6 +99,7 @@ test.describe('Change Tracker', () => {
|
||||
|
||||
// Make changes outside set
|
||||
// Bypass + collapse node
|
||||
await node.click('title')
|
||||
await node.click('collapse')
|
||||
await comfyPage.ctrlB()
|
||||
await expect(node).toBeCollapsed()
|
||||
@@ -111,6 +113,10 @@ test.describe('Change Tracker', () => {
|
||||
await expect(node).not.toBeBypassed()
|
||||
await expect(node).not.toBeCollapsed()
|
||||
|
||||
// Prevent clicks registering a double-click
|
||||
await comfyPage.clickEmptySpace()
|
||||
await node.click('title')
|
||||
|
||||
// Run again, but within a change transaction
|
||||
await beforeChange(comfyPage)
|
||||
|
||||
@@ -152,6 +158,7 @@ test.describe('Change Tracker', () => {
|
||||
const multipleChanges = async () => {
|
||||
await beforeChange(comfyPage)
|
||||
// Call other actions that uses begin/endChange
|
||||
await node.click('title')
|
||||
await collapse()
|
||||
await bypassAndPin()
|
||||
await afterChange(comfyPage)
|
||||
|
||||
@@ -24,14 +24,6 @@ test.describe('Copy Paste', () => {
|
||||
|
||||
test('Can copy and paste widget value', async ({ comfyPage }) => {
|
||||
// Copy width value (512) from empty latent node to KSampler's seed.
|
||||
// Empty latent node's width
|
||||
await comfyPage.canvas.click({
|
||||
position: {
|
||||
x: 718,
|
||||
y: 643
|
||||
}
|
||||
})
|
||||
await comfyPage.ctrlC(null)
|
||||
// KSampler's seed
|
||||
await comfyPage.canvas.click({
|
||||
position: {
|
||||
@@ -39,6 +31,14 @@ test.describe('Copy Paste', () => {
|
||||
y: 281
|
||||
}
|
||||
})
|
||||
await comfyPage.ctrlC(null)
|
||||
// Empty latent node's width
|
||||
await comfyPage.canvas.click({
|
||||
position: {
|
||||
x: 718,
|
||||
y: 643
|
||||
}
|
||||
})
|
||||
await comfyPage.ctrlV(null)
|
||||
await comfyPage.page.keyboard.press('Enter')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('copied-widget-value.png')
|
||||
|
||||
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 101 KiB |
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 98 KiB |
@@ -526,9 +526,6 @@ export class ComfyPage {
|
||||
safeSpot = safeSpot || { x: 10, y: 10 }
|
||||
await this.page.mouse.move(safeSpot.x, safeSpot.y)
|
||||
await this.page.mouse.down()
|
||||
// TEMPORARY HACK: Multiple pans open the search menu, so cheat and keep it closed.
|
||||
// TODO: Fix that (double-click at not-the-same-coordinations should not open the menu)
|
||||
await this.page.keyboard.press('Escape')
|
||||
await this.page.mouse.move(offset.x + safeSpot.x, offset.y + safeSpot.y)
|
||||
await this.page.mouse.up()
|
||||
await this.nextFrame()
|
||||
@@ -557,6 +554,11 @@ export class ComfyPage {
|
||||
await this.nextFrame()
|
||||
}
|
||||
|
||||
async clickContextMenuItem(name: string): Promise<void> {
|
||||
await this.page.getByRole('menuitem', { name }).click()
|
||||
await this.nextFrame()
|
||||
}
|
||||
|
||||
async doubleClickCanvas() {
|
||||
await this.page.mouse.dblclick(10, 10, { delay: 5 })
|
||||
await this.nextFrame()
|
||||
|
||||
|
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 103 KiB |
|
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 100 KiB |
@@ -36,7 +36,7 @@ test.describe('Canvas Right Click Menu', () => {
|
||||
await dialog.accept('GroupNode2CLIP')
|
||||
})
|
||||
await comfyPage.rightClickCanvas()
|
||||
await comfyPage.page.getByText('Convert to Group Node').click()
|
||||
await comfyPage.clickContextMenuItem('Convert to Group Node')
|
||||
await comfyPage.nextFrame()
|
||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||
'right-click-node-group-node.png'
|
||||
|
||||
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 98 KiB |
8
package-lock.json
generated
@@ -10,7 +10,7 @@
|
||||
"dependencies": {
|
||||
"@atlaskit/pragmatic-drag-and-drop": "^1.3.1",
|
||||
"@comfyorg/comfyui-electron-types": "^0.2.16",
|
||||
"@comfyorg/litegraph": "^0.8.27",
|
||||
"@comfyorg/litegraph": "^0.8.29",
|
||||
"@primevue/themes": "^4.0.5",
|
||||
"@vueuse/core": "^11.0.0",
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
@@ -1923,9 +1923,9 @@
|
||||
"license": "GPL-3.0-only"
|
||||
},
|
||||
"node_modules/@comfyorg/litegraph": {
|
||||
"version": "0.8.27",
|
||||
"resolved": "https://registry.npmjs.org/@comfyorg/litegraph/-/litegraph-0.8.27.tgz",
|
||||
"integrity": "sha512-EMQ3jsny+3gUQL4+vSVwJAFxrLq4IpuyjCvAiErLY4wLZZu2Mi+7cELmhrNS0MajhZqfN1M0GPmdcBRwSWbarw==",
|
||||
"version": "0.8.29",
|
||||
"resolved": "https://registry.npmjs.org/@comfyorg/litegraph/-/litegraph-0.8.29.tgz",
|
||||
"integrity": "sha512-h7c+sW/BEAPfWSDYATNk2YtB1kduQ0v85z2Rq8q11UoLHRkTa0A1zosDFSwIdCul/prdG51wbXr99EHq3Dsfnw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@cspotcode/source-map-support": {
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
"dependencies": {
|
||||
"@atlaskit/pragmatic-drag-and-drop": "^1.3.1",
|
||||
"@comfyorg/comfyui-electron-types": "^0.2.16",
|
||||
"@comfyorg/litegraph": "^0.8.27",
|
||||
"@comfyorg/litegraph": "^0.8.29",
|
||||
"@primevue/themes": "^4.0.5",
|
||||
"@vueuse/core": "^11.0.0",
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
|
||||
@@ -47,7 +47,8 @@ import {
|
||||
DragAndScale,
|
||||
LGraphCanvas,
|
||||
ContextMenu,
|
||||
LGraphBadge
|
||||
LGraphBadge,
|
||||
CanvasPointer
|
||||
} from '@comfyorg/litegraph'
|
||||
import type { RenderedTreeExplorerNode } from '@/types/treeExplorerTypes'
|
||||
import { useCanvasStore } from '@/stores/graphStore'
|
||||
@@ -162,6 +163,29 @@ watchEffect(() => {
|
||||
}
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
CanvasPointer.doubleClickTime = settingStore.get(
|
||||
'Comfy.Pointer.DoubleClickTime'
|
||||
)
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
CanvasPointer.bufferTime = settingStore.get('Comfy.Pointer.ClickBufferTime')
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
CanvasPointer.maxClickDrift = settingStore.get('Comfy.Pointer.ClickDrift')
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
LiteGraph.CANVAS_GRID_SIZE = settingStore.get('Comfy.SnapToGrid.GridSize')
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
comfyApp.graph.config.alwaysSnapToGrid =
|
||||
settingStore.get('pysssss.SnapToGrid')
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
if (!canvasStore.canvas) return
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ import './rerouteNode'
|
||||
import './saveImageExtraOutput'
|
||||
import './simpleTouchSupport'
|
||||
import './slotDefaults'
|
||||
import './snapToGrid'
|
||||
import './uploadImage'
|
||||
import './webcamCapture'
|
||||
import './widgetInputs'
|
||||
|
||||
@@ -44,7 +44,7 @@ app.registerExtension({
|
||||
lastTouch = getMultiTouchCenter(e)
|
||||
|
||||
touchDist = getMultiTouchPos(e)
|
||||
app.canvas.pointer_is_down = false
|
||||
app.canvas.pointer.isDown = false
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -78,7 +78,7 @@ app.registerExtension({
|
||||
touchTime = null
|
||||
if (e.touches?.length === 2 && lastTouch && !e.ctrlKey && !e.shiftKey) {
|
||||
e.preventDefault() // Prevent browser from zooming when two textareas are touched
|
||||
app.canvas.pointer_is_down = false
|
||||
app.canvas.pointer.isDown = false
|
||||
touchZooming = true
|
||||
|
||||
LiteGraph.closeAllContextMenus(window)
|
||||
@@ -137,7 +137,7 @@ LGraphCanvas.prototype.processMouseDown = function (e) {
|
||||
if (touchZooming || touchCount) {
|
||||
return
|
||||
}
|
||||
app.canvas.pointer_is_down = false // Prevent context menu from opening on second tap
|
||||
app.canvas.pointer.isDown = false // Prevent context menu from opening on second tap
|
||||
return processMouseDown.apply(this, arguments)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
import { app } from '../../scripts/app'
|
||||
import { LiteGraph } from '@comfyorg/litegraph'
|
||||
|
||||
app.registerExtension({
|
||||
name: 'Comfy.SnapToGrid',
|
||||
init() {
|
||||
// Add setting to control grid size
|
||||
app.ui.settings.addSetting({
|
||||
id: 'Comfy.SnapToGrid.GridSize',
|
||||
category: ['LiteGraph', 'Canvas', 'GridSize'],
|
||||
name: 'Snap to grid size',
|
||||
type: 'slider',
|
||||
attrs: {
|
||||
min: 1,
|
||||
max: 500
|
||||
},
|
||||
tooltip:
|
||||
'When dragging and resizing nodes while holding shift they will be aligned to the grid, this controls the size of that grid.',
|
||||
defaultValue: LiteGraph.CANVAS_GRID_SIZE,
|
||||
onChange(value) {
|
||||
LiteGraph.CANVAS_GRID_SIZE = +value || 10
|
||||
}
|
||||
})
|
||||
|
||||
// Keep the 'pysssss.SnapToGrid' setting id so we don't need to migrate setting values.
|
||||
// Using a new setting id can cause existing users to lose their existing settings.
|
||||
app.ui.settings.addSetting({
|
||||
id: 'pysssss.SnapToGrid',
|
||||
category: ['LiteGraph', 'Canvas', 'AlwaysSnapToGrid'],
|
||||
name: 'Always snap to grid',
|
||||
type: 'boolean',
|
||||
defaultValue: false,
|
||||
versionAdded: '1.3.13',
|
||||
versionModified: '1.3.42',
|
||||
onChange(value) {
|
||||
app.graph.config.alwaysSnapToGrid = !!value
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -894,7 +894,7 @@ app.registerExtension({
|
||||
// Not a widget input or already handled input
|
||||
if (
|
||||
!(input.type in ComfyWidgets) &&
|
||||
!(input.widget[GET_CONFIG]?.()?.[0] instanceof Array)
|
||||
!(input.widget?.[GET_CONFIG]?.()?.[0] instanceof Array)
|
||||
) {
|
||||
return r //also Not a ComfyWidgets input or combo (do nothing)
|
||||
}
|
||||
|
||||
@@ -1089,7 +1089,6 @@ export class ComfyApp {
|
||||
'dragover',
|
||||
(e) => {
|
||||
this.canvas.adjustMouseEvent(e)
|
||||
// @ts-expect-error: canvasX and canvasY are added by adjustMouseEvent in litegraph
|
||||
const node = this.graph.getNodeOnPos(e.canvasX, e.canvasY)
|
||||
if (node) {
|
||||
// @ts-expect-error This is not a standard event. TODO fix it.
|
||||
|
||||
@@ -535,5 +535,75 @@ export const CORE_SETTINGS: SettingParams[] = [
|
||||
type: 'boolean',
|
||||
defaultValue: true,
|
||||
versionAdded: '1.4.0'
|
||||
},
|
||||
{
|
||||
id: 'Comfy.Pointer.ClickDrift',
|
||||
category: ['LiteGraph', 'Pointer', 'ClickDrift'],
|
||||
name: 'Pointer click drift (maximum distance)',
|
||||
tooltip:
|
||||
'If the pointer moves more than this distance while holding a button down, it is considered dragging (rather than clicking).\n\nHelps prevent objects from being unintentionally nudged if the pointer is moved whilst clicking.',
|
||||
experimental: true,
|
||||
type: 'slider',
|
||||
attrs: {
|
||||
min: 0,
|
||||
max: 20,
|
||||
step: 1
|
||||
},
|
||||
defaultValue: 6,
|
||||
versionAdded: '1.4.3'
|
||||
},
|
||||
{
|
||||
id: 'Comfy.Pointer.ClickBufferTime',
|
||||
category: ['LiteGraph', 'Pointer', 'ClickBufferTime'],
|
||||
name: 'Pointer click drift delay',
|
||||
tooltip:
|
||||
'After pressing a pointer button down, this is the maximum time (in milliseconds) that pointer movement can be ignored for.\n\nHelps prevent objects from being unintentionally nudged if the pointer is moved whilst clicking.',
|
||||
experimental: true,
|
||||
type: 'slider',
|
||||
attrs: {
|
||||
min: 0,
|
||||
max: 1000,
|
||||
step: 25
|
||||
},
|
||||
defaultValue: 150,
|
||||
versionAdded: '1.4.3'
|
||||
},
|
||||
{
|
||||
id: 'Comfy.Pointer.DoubleClickTime',
|
||||
category: ['LiteGraph', 'Pointer', 'DoubleClickTime'],
|
||||
name: 'Double click interval (maximum)',
|
||||
tooltip:
|
||||
'The maximum time in milliseconds between the two clicks of a double-click. Increasing this value may assist if double-clicks are sometimes not registered.',
|
||||
type: 'slider',
|
||||
attrs: {
|
||||
min: 100,
|
||||
max: 1000,
|
||||
step: 50
|
||||
},
|
||||
defaultValue: 300,
|
||||
versionAdded: '1.4.3'
|
||||
},
|
||||
{
|
||||
id: 'Comfy.SnapToGrid.GridSize',
|
||||
category: ['LiteGraph', 'Canvas', 'GridSize'],
|
||||
name: 'Snap to grid size',
|
||||
type: 'slider',
|
||||
attrs: {
|
||||
min: 1,
|
||||
max: 500
|
||||
},
|
||||
tooltip:
|
||||
'When dragging and resizing nodes while holding shift they will be aligned to the grid, this controls the size of that grid.',
|
||||
defaultValue: LiteGraph.CANVAS_GRID_SIZE
|
||||
},
|
||||
// Keep the 'pysssss.SnapToGrid' setting id so we don't need to migrate setting values.
|
||||
// Using a new setting id can cause existing users to lose their existing settings.
|
||||
{
|
||||
id: 'pysssss.SnapToGrid',
|
||||
category: ['LiteGraph', 'Canvas', 'AlwaysSnapToGrid'],
|
||||
name: 'Always snap to grid',
|
||||
type: 'boolean',
|
||||
defaultValue: false,
|
||||
versionAdded: '1.3.13'
|
||||
}
|
||||
]
|
||||
|
||||