mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-01 13:59:54 +00:00
Enable ts-strict for changeTracker (#1338)
* Fix app getter * nit * nit * nit * Fix rest of errors * nit
This commit is contained in:
@@ -1,33 +1,33 @@
|
||||
// @ts-strict-ignore
|
||||
import type { ComfyApp } from './app'
|
||||
import { api } from './api'
|
||||
import { clone } from './utils'
|
||||
import { LGraphCanvas, LiteGraph } from '@comfyorg/litegraph'
|
||||
import { ComfyWorkflow } from './workflows'
|
||||
import type { ComfyWorkflowJSON } from '@/types/comfyWorkflow'
|
||||
import { LGraphNode } from '@comfyorg/litegraph'
|
||||
import { ExecutedWsMessage } from '@/types/apiTypes'
|
||||
import { useExecutionStore } from '@/stores/executionStore'
|
||||
|
||||
export class ChangeTracker {
|
||||
static MAX_HISTORY = 50
|
||||
#app: ComfyApp
|
||||
undoQueue = []
|
||||
redoQueue = []
|
||||
#app?: ComfyApp
|
||||
undoQueue: ComfyWorkflowJSON[] = []
|
||||
redoQueue: ComfyWorkflowJSON[] = []
|
||||
activeState: ComfyWorkflowJSON | null = null
|
||||
isOurLoad = false
|
||||
workflow: ComfyWorkflow | null
|
||||
changeCount = 0
|
||||
isOurLoad: boolean = false
|
||||
changeCount: number = 0
|
||||
|
||||
ds: { scale: number; offset: [number, number] }
|
||||
nodeOutputs: any
|
||||
ds?: { scale: number; offset: [number, number] }
|
||||
nodeOutputs?: Record<string, any>
|
||||
|
||||
get app() {
|
||||
get app(): ComfyApp {
|
||||
// Global tracker has #app set, while other trackers have workflow bounded
|
||||
return this.#app ?? this.workflow.manager.app
|
||||
}
|
||||
|
||||
constructor(workflow: ComfyWorkflow) {
|
||||
this.workflow = workflow
|
||||
}
|
||||
constructor(public workflow: ComfyWorkflow) {}
|
||||
|
||||
#setApp(app) {
|
||||
#setApp(app: ComfyApp) {
|
||||
this.#app = app
|
||||
}
|
||||
|
||||
@@ -70,10 +70,10 @@ export class ChangeTracker {
|
||||
}
|
||||
}
|
||||
|
||||
async updateState(source, target) {
|
||||
async updateState(source: ComfyWorkflowJSON[], target: ComfyWorkflowJSON[]) {
|
||||
const prevState = source.pop()
|
||||
if (prevState) {
|
||||
target.push(this.activeState)
|
||||
target.push(this.activeState!)
|
||||
this.isOurLoad = true
|
||||
await this.app.loadGraphData(prevState, false, false, this.workflow, {
|
||||
showMissingModelsDialog: false,
|
||||
@@ -91,7 +91,7 @@ export class ChangeTracker {
|
||||
await this.updateState(this.redoQueue, this.undoQueue)
|
||||
}
|
||||
|
||||
async undoRedo(e) {
|
||||
async undoRedo(e: KeyboardEvent) {
|
||||
if (e.ctrlKey || e.metaKey) {
|
||||
if (e.key === 'y' || e.key == 'Z') {
|
||||
await this.redo()
|
||||
@@ -119,8 +119,8 @@ export class ChangeTracker {
|
||||
globalTracker.#setApp(app)
|
||||
|
||||
const loadGraphData = app.loadGraphData
|
||||
app.loadGraphData = async function () {
|
||||
const v = await loadGraphData.apply(this, arguments)
|
||||
app.loadGraphData = async function (...args) {
|
||||
const v = await loadGraphData.apply(this, args)
|
||||
const ct = changeTracker()
|
||||
if (ct.isOurLoad) {
|
||||
ct.isOurLoad = false
|
||||
@@ -140,12 +140,12 @@ export class ChangeTracker {
|
||||
|
||||
const activeEl = document.activeElement
|
||||
requestAnimationFrame(async () => {
|
||||
let bindInputEl
|
||||
let bindInputEl: Element | null = null
|
||||
// If we are auto queue in change mode then we do want to trigger on inputs
|
||||
if (!app.ui.autoQueueEnabled || app.ui.autoQueueMode === 'instant') {
|
||||
if (
|
||||
activeEl?.tagName === 'INPUT' ||
|
||||
activeEl?.['type'] === 'textarea'
|
||||
(activeEl && 'type' in activeEl && activeEl.type === 'textarea')
|
||||
) {
|
||||
// Ignore events on inputs, they have their native history
|
||||
return
|
||||
@@ -195,21 +195,26 @@ export class ChangeTracker {
|
||||
// Handle litegraph clicks
|
||||
const processMouseUp = LGraphCanvas.prototype.processMouseUp
|
||||
LGraphCanvas.prototype.processMouseUp = function (e) {
|
||||
const v = processMouseUp.apply(this, arguments)
|
||||
const v = processMouseUp.apply(this, [e])
|
||||
changeTracker().checkState()
|
||||
return v
|
||||
}
|
||||
const processMouseDown = LGraphCanvas.prototype.processMouseDown
|
||||
LGraphCanvas.prototype.processMouseDown = function (e) {
|
||||
const v = processMouseDown.apply(this, arguments)
|
||||
const v = processMouseDown.apply(this, [e])
|
||||
changeTracker().checkState()
|
||||
return v
|
||||
}
|
||||
|
||||
// Handle litegraph dialog popup for number/string widgets
|
||||
const prompt = LGraphCanvas.prototype.prompt
|
||||
LGraphCanvas.prototype.prompt = function (title, value, callback, event) {
|
||||
const extendedCallback = (v) => {
|
||||
LGraphCanvas.prototype.prompt = function (
|
||||
title: string,
|
||||
value: any,
|
||||
callback: (v: any) => void,
|
||||
event: any
|
||||
) {
|
||||
const extendedCallback = (v: any) => {
|
||||
callback(v)
|
||||
changeTracker().checkState()
|
||||
}
|
||||
@@ -218,16 +223,16 @@ export class ChangeTracker {
|
||||
|
||||
// Handle litegraph context menu for COMBO widgets
|
||||
const close = LiteGraph.ContextMenu.prototype.close
|
||||
LiteGraph.ContextMenu.prototype.close = function (e) {
|
||||
const v = close.apply(this, arguments)
|
||||
LiteGraph.ContextMenu.prototype.close = function (e: MouseEvent) {
|
||||
const v = close.apply(this, [e])
|
||||
changeTracker().checkState()
|
||||
return v
|
||||
}
|
||||
|
||||
// Detects nodes being added via the node search dialog
|
||||
const onNodeAdded = LiteGraph.LGraph.prototype.onNodeAdded
|
||||
LiteGraph.LGraph.prototype.onNodeAdded = function () {
|
||||
const v = onNodeAdded?.apply(this, arguments)
|
||||
LiteGraph.LGraph.prototype.onNodeAdded = function (node: LGraphNode) {
|
||||
const v = onNodeAdded?.apply(this, [node])
|
||||
if (!app?.configuringGraph) {
|
||||
const ct = changeTracker()
|
||||
if (!ct.isOurLoad) {
|
||||
@@ -238,20 +243,24 @@ export class ChangeTracker {
|
||||
}
|
||||
|
||||
// Handle multiple commands as a single transaction
|
||||
document.addEventListener('litegraph:canvas', (e: CustomEvent) => {
|
||||
if (e.detail.subType === 'before-change') {
|
||||
document.addEventListener('litegraph:canvas', (e: Event) => {
|
||||
const detail = (e as CustomEvent).detail
|
||||
if (detail.subType === 'before-change') {
|
||||
changeTracker().beforeChange()
|
||||
} else if (e.detail.subType === 'after-change') {
|
||||
} else if (detail.subType === 'after-change') {
|
||||
changeTracker().afterChange()
|
||||
}
|
||||
})
|
||||
|
||||
// Store node outputs
|
||||
api.addEventListener('executed', ({ detail }) => {
|
||||
const prompt =
|
||||
app.workflowManager.executionStore.queuedPrompts[detail.prompt_id]
|
||||
if (!prompt?.workflow) return
|
||||
const nodeOutputs = (prompt.workflow.changeTracker.nodeOutputs ??= {})
|
||||
api.addEventListener('executed', (e: CustomEvent<ExecutedWsMessage>) => {
|
||||
const detail = e.detail
|
||||
const workflow =
|
||||
useExecutionStore().queuedPrompts[detail.prompt_id]?.workflow
|
||||
const changeTracker = workflow?.changeTracker
|
||||
if (!changeTracker) return
|
||||
changeTracker.nodeOutputs ??= {}
|
||||
const nodeOutputs = changeTracker.nodeOutputs
|
||||
const output = nodeOutputs[detail.node]
|
||||
if (detail.merge && output) {
|
||||
for (const k in detail.output ?? {}) {
|
||||
@@ -268,26 +277,30 @@ export class ChangeTracker {
|
||||
})
|
||||
}
|
||||
|
||||
static bindInput(app, activeEl) {
|
||||
static bindInput(app: ComfyApp, activeEl: Element | null): boolean {
|
||||
if (
|
||||
activeEl &&
|
||||
activeEl.tagName !== 'CANVAS' &&
|
||||
activeEl.tagName !== 'BODY'
|
||||
!activeEl ||
|
||||
activeEl.tagName === 'CANVAS' ||
|
||||
activeEl.tagName === 'BODY'
|
||||
) {
|
||||
for (const evt of ['change', 'input', 'blur']) {
|
||||
if (`on${evt}` in activeEl) {
|
||||
const listener = () => {
|
||||
app.workflowManager.activeWorkflow.changeTracker.checkState()
|
||||
activeEl.removeEventListener(evt, listener)
|
||||
}
|
||||
activeEl.addEventListener(evt, listener)
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
for (const evt of ['change', 'input', 'blur']) {
|
||||
const htmlElement = activeEl as HTMLElement
|
||||
if (`on${evt}` in htmlElement) {
|
||||
const listener = () => {
|
||||
app.workflowManager.activeWorkflow?.changeTracker?.checkState()
|
||||
htmlElement.removeEventListener(evt, listener)
|
||||
}
|
||||
htmlElement.addEventListener(evt, listener)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
static graphEqual(a, b, path = '') {
|
||||
static graphEqual(a: any, b: any, path = '') {
|
||||
if (a === b) return true
|
||||
|
||||
if (typeof a == 'object' && a && typeof b == 'object' && b) {
|
||||
|
||||
@@ -47,7 +47,8 @@ const zExecutingWsMessage = z.object({
|
||||
})
|
||||
|
||||
const zExecutedWsMessage = zExecutingWsMessage.extend({
|
||||
outputs: zOutputs
|
||||
output: zOutputs,
|
||||
merge: z.boolean().optional()
|
||||
})
|
||||
|
||||
const zExecutionWsMessageBase = z.object({
|
||||
|
||||
Reference in New Issue
Block a user