Cleanup: YAGNI readonly props, private swap on ComfyApp, Canvas resize events simplification, v-memos on individual instances (#5869)

## Summary

Assorted cleanup opportunities found while working through the Vue node
rendering logic cleanup.

## Review Focus

Am I wrong that the readonly logic was never actually executing because
it was defined as False in GraphCanvas when making each LGraphNode?

Is there an edge case or some other reason that the ResizeObserver
wouldn't work as a single signal to resize the canvas?

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5869-Cleanup-YAGNI-readonly-props-private-swap-on-ComfyApp-Canvas-resize-events-simplificat-27e6d73d3650811ba1dcf29e8d43091e)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
Alexander Brown
2025-10-02 10:35:10 -07:00
committed by GitHub
parent 706ff953de
commit 37fab21daf
35 changed files with 88 additions and 247 deletions

View File

@@ -1,6 +1,8 @@
import { useResizeObserver } from '@vueuse/core'
import _ from 'es-toolkit/compat'
import type { ToastMessageOptions } from 'primevue/toast'
import { reactive } from 'vue'
import { reactive, unref } from 'vue'
import { shallowRef } from 'vue'
import { useCanvasPositionConversion } from '@/composables/element/useCanvasPositionConversion'
import { st, t } from '@/i18n'
@@ -129,7 +131,7 @@ export class ComfyApp {
/**
* List of entries to queue
*/
#queueItems: {
private queueItems: {
number: number
batchCount: number
queueNodeIds?: NodeExecutionId[]
@@ -137,7 +139,7 @@ export class ComfyApp {
/**
* If the queue is currently being processed
*/
#processingQueue: boolean = false
private processingQueue: boolean = false
/**
* Content Clipboard
@@ -176,12 +178,15 @@ export class ComfyApp {
// @ts-expect-error fixme ts strict error
canvas: LGraphCanvas
dragOverNode: LGraphNode | null = null
// @ts-expect-error fixme ts strict error
canvasEl: HTMLCanvasElement
readonly canvasElRef = shallowRef<HTMLCanvasElement>()
get canvasEl() {
// TODO: Fix possibly undefined reference
return unref(this.canvasElRef)!
}
#configuringGraphLevel: number = 0
private configuringGraphLevel: number = 0
get configuringGraph() {
return this.#configuringGraphLevel > 0
return this.configuringGraphLevel > 0
}
// @ts-expect-error fixme ts strict error
ctx: CanvasRenderingContext2D
@@ -195,7 +200,7 @@ export class ComfyApp {
// Set by Comfy.Clipspace extension
openClipspace: () => void = () => {}
#positionConversion?: {
private positionConversion?: {
clientPosToCanvasPos: (pos: Vector2) => Vector2
canvasPosToClientPos: (pos: Vector2) => Vector2
}
@@ -517,7 +522,7 @@ export class ComfyApp {
/**
* Adds a handler allowing drag+drop of files onto the window to load workflows
*/
#addDropHandler() {
private addDropHandler() {
// Get prompt from dropped PNG or json
document.addEventListener('drop', async (event) => {
try {
@@ -593,7 +598,7 @@ export class ComfyApp {
/**
* Handle keypress
*/
#addProcessKeyHandler() {
private addProcessKeyHandler() {
const origProcessKey = LGraphCanvas.prototype.processKey
LGraphCanvas.prototype.processKey = function (e: KeyboardEvent) {
if (!this.graph) return
@@ -640,7 +645,7 @@ export class ComfyApp {
}
}
#addDrawNodeHandler() {
private addDrawNodeHandler() {
const origDrawNode = LGraphCanvas.prototype.drawNode
LGraphCanvas.prototype.drawNode = function (node) {
const editor_alpha = this.editor_alpha
@@ -689,7 +694,7 @@ export class ComfyApp {
/**
* Handles updates from the API socket
*/
#addApiUpdateHandlers() {
private addApiUpdateHandlers() {
api.addEventListener('status', ({ detail }) => {
this.ui.setStatus(detail)
})
@@ -763,15 +768,15 @@ export class ComfyApp {
}
/** Flag that the graph is configuring to prevent nodes from running checks while its still loading */
#addConfigureHandler() {
private addConfigureHandler() {
const app = this
const configure = LGraph.prototype.configure
LGraph.prototype.configure = function (...args) {
app.#configuringGraphLevel++
app.configuringGraphLevel++
try {
return configure.apply(this, args)
} finally {
app.#configuringGraphLevel--
app.configuringGraphLevel--
}
}
}
@@ -808,16 +813,15 @@ export class ComfyApp {
// @ts-expect-error fixme ts strict error
this.canvasContainer = document.getElementById('graph-canvas-container')
this.canvasEl = canvasEl
this.resizeCanvas()
this.canvasElRef.value = canvasEl
await useWorkspaceStore().workflow.syncWorkflows()
await useSubgraphStore().fetchSubgraphs()
await useExtensionService().loadExtensions()
this.#addProcessKeyHandler()
this.#addConfigureHandler()
this.#addApiUpdateHandlers()
this.addProcessKeyHandler()
this.addConfigureHandler()
this.addApiUpdateHandlers()
const graph = new LGraph()
@@ -883,39 +887,37 @@ export class ComfyApp {
this.graph.start()
// Ensure the canvas fills the window
this.resizeCanvas()
window.addEventListener('resize', () => this.resizeCanvas())
const ro = new ResizeObserver(() => this.resizeCanvas())
ro.observe(this.bodyTop)
ro.observe(this.bodyLeft)
ro.observe(this.bodyRight)
ro.observe(this.bodyBottom)
useResizeObserver(this.canvasElRef, ([canvasEl]) => {
if (canvasEl.target instanceof HTMLCanvasElement) {
this.resizeCanvas(canvasEl.target)
}
})
await useExtensionService().invokeExtensionsAsync('init')
await this.registerNodes()
this.#addDrawNodeHandler()
this.#addDropHandler()
this.addDrawNodeHandler()
this.addDropHandler()
await useExtensionService().invokeExtensionsAsync('setup')
this.#positionConversion = useCanvasPositionConversion(
this.positionConversion = useCanvasPositionConversion(
this.canvasContainer,
this.canvas
)
}
resizeCanvas() {
private resizeCanvas(canvas: HTMLCanvasElement) {
// Limit minimal scale to 1, see https://github.com/comfyanonymous/ComfyUI/pull/845
const scale = Math.max(window.devicePixelRatio, 1)
// Clear fixed width and height while calculating rect so it uses 100% instead
this.canvasEl.height = this.canvasEl.width = NaN
const { width, height } = this.canvasEl.getBoundingClientRect()
this.canvasEl.width = Math.round(width * scale)
this.canvasEl.height = Math.round(height * scale)
canvas.height = canvas.width = NaN
const { width, height } = canvas.getBoundingClientRect()
canvas.width = Math.round(width * scale)
canvas.height = Math.round(height * scale)
// @ts-expect-error fixme ts strict error
this.canvasEl.getContext('2d').scale(scale, scale)
canvas.getContext('2d').scale(scale, scale)
this.canvas?.draw(true, true)
}
@@ -963,7 +965,7 @@ export class ComfyApp {
nodeDefStore.updateNodeDefs(nodeDefArray)
}
async #getNodeDefs(): Promise<Record<string, ComfyNodeDefV1>> {
async getNodeDefs(): Promise<Record<string, ComfyNodeDefV1>> {
const translateNodeDef = (def: ComfyNodeDefV1): ComfyNodeDefV1 => ({
...def,
display_name: st(
@@ -987,7 +989,7 @@ export class ComfyApp {
*/
async registerNodes() {
// Load node definitions from the backend
const defs = await this.#getNodeDefs()
const defs = await this.getNodeDefs()
await this.registerNodesFromDefs(defs)
await useExtensionService().invokeExtensionsAsync('registerCustomNodes')
if (this.vueAppReady) {
@@ -1055,14 +1057,14 @@ export class ComfyApp {
localStorage.setItem('litegrapheditor_clipboard', old)
}
#showMissingNodesError(missingNodeTypes: MissingNodeType[]) {
private showMissingNodesError(missingNodeTypes: MissingNodeType[]) {
if (useSettingStore().get('Comfy.Workflow.ShowMissingNodesWarning')) {
useDialogService().showLoadWorkflowWarning({ missingNodeTypes })
}
}
// @ts-expect-error fixme ts strict error
#showMissingModelsError(missingModels, paths) {
private showMissingModelsError(missingModels, paths) {
if (useSettingStore().get('Comfy.Workflow.ShowMissingModelsWarning')) {
useDialogService().showMissingModelsWarning({
missingModels,
@@ -1295,11 +1297,11 @@ export class ComfyApp {
}
if (missingNodeTypes.length && showMissingNodesDialog) {
this.#showMissingNodesError(missingNodeTypes)
this.showMissingNodesError(missingNodeTypes)
}
if (missingModels.length && showMissingModelsDialog) {
const paths = await api.getFolderPaths()
this.#showMissingModelsError(missingModels, paths)
this.showMissingModelsError(missingModels, paths)
}
await useExtensionService().invokeExtensionsAsync(
'afterConfigureGraph',
@@ -1325,14 +1327,14 @@ export class ComfyApp {
batchCount: number = 1,
queueNodeIds?: NodeExecutionId[]
): Promise<boolean> {
this.#queueItems.push({ number, batchCount, queueNodeIds })
this.queueItems.push({ number, batchCount, queueNodeIds })
// Only have one action process the items so each one gets a unique seed correctly
if (this.#processingQueue) {
if (this.processingQueue) {
return false
}
this.#processingQueue = true
this.processingQueue = true
const executionStore = useExecutionStore()
executionStore.lastNodeErrors = null
@@ -1340,8 +1342,8 @@ export class ComfyApp {
let comfyOrgApiKey = useApiKeyAuthStore().getApiKey()
try {
while (this.#queueItems.length) {
const { number, batchCount, queueNodeIds } = this.#queueItems.pop()!
while (this.queueItems.length) {
const { number, batchCount, queueNodeIds } = this.queueItems.pop()!
for (let i = 0; i < batchCount; i++) {
// Allow widgets to run callbacks before a prompt has been queued
@@ -1406,7 +1408,7 @@ export class ComfyApp {
}
}
} finally {
this.#processingQueue = false
this.processingQueue = false
}
api.dispatchCustomEvent('promptQueued', { number, batchCount })
return !executionStore.lastNodeErrors
@@ -1610,7 +1612,7 @@ export class ComfyApp {
(n) => !LiteGraph.registered_node_types[n.class_type]
)
if (missingNodeTypes.length) {
this.#showMissingNodesError(missingNodeTypes.map((t) => t.class_type))
this.showMissingNodesError(missingNodeTypes.map((t) => t.class_type))
return
}
@@ -1729,7 +1731,7 @@ export class ComfyApp {
useToastStore().add(requestToastMessage)
}
const defs = await this.#getNodeDefs()
const defs = await this.getNodeDefs()
for (const nodeId in defs) {
this.registerNodeDef(nodeId, defs[nodeId])
}
@@ -1805,17 +1807,17 @@ export class ComfyApp {
}
clientPosToCanvasPos(pos: Vector2): Vector2 {
if (!this.#positionConversion) {
if (!this.positionConversion) {
throw new Error('clientPosToCanvasPos called before setup')
}
return this.#positionConversion.clientPosToCanvasPos(pos)
return this.positionConversion.clientPosToCanvasPos(pos)
}
canvasPosToClientPos(pos: Vector2): Vector2 {
if (!this.#positionConversion) {
if (!this.positionConversion) {
throw new Error('canvasPosToClientPos called before setup')
}
return this.#positionConversion.canvasPosToClientPos(pos)
return this.positionConversion.canvasPosToClientPos(pos)
}
}