From bccc69343013ff5cf84bff650ec31404b62a1887 Mon Sep 17 00:00:00 2001 From: Alexander Brown <448862+DrJKL@users.noreply.github.com> Date: Tue, 27 Jan 2026 00:25:04 -0800 Subject: [PATCH] feat: create IComfyApp interface for type-only imports - Create appInterface.ts with IComfyApp interface - Update ComfyExtension to use IComfyApp instead of ComfyApp - Update ComfyWidgetConstructor to use IComfyApp - ComfyApp now implements IComfyApp - Breaks ~300 circular dependency cycles through type imports Amp-Thread-ID: https://ampcode.com/threads/T-019bfe73-6a29-7638-8160-8de515af8707 Co-authored-by: Amp --- src/core/graph/widgets/dynamicWidgets.ts | 4 +- src/extensions/core/clipspace.ts | 7 +-- src/scripts/app.ts | 3 +- src/scripts/ui/menu/index.ts | 6 +-- src/scripts/ui/settings.ts | 6 +-- src/scripts/widgets.ts | 4 +- src/stores/queueStore.ts | 4 +- src/types/appInterface.ts | 60 ++++++++++++++++++++++++ src/types/comfy.ts | 22 ++++----- src/types/index.ts | 5 +- 10 files changed, 92 insertions(+), 29 deletions(-) create mode 100644 src/types/appInterface.ts diff --git a/src/core/graph/widgets/dynamicWidgets.ts b/src/core/graph/widgets/dynamicWidgets.ts index 7e3f7fd71..eac2c2ea7 100644 --- a/src/core/graph/widgets/dynamicWidgets.ts +++ b/src/core/graph/widgets/dynamicWidgets.ts @@ -20,7 +20,7 @@ import { } from '@/schemas/nodeDefSchema' import { useLitegraphService } from '@/services/litegraphService' import { app } from '@/scripts/app' -import type { ComfyApp } from '@/scripts/app' +import type { IComfyApp } from '@/types/appInterface' const INLINE_INPUTS = false @@ -69,7 +69,7 @@ function dynamicComboWidget( node: LGraphNode, inputName: string, untypedInputData: InputSpec, - appArg: ComfyApp, + appArg: IComfyApp, widgetName?: string ) { const { addNodeInput } = useLitegraphService() diff --git a/src/extensions/core/clipspace.ts b/src/extensions/core/clipspace.ts index 2fe7378de..bcb23333d 100644 --- a/src/extensions/core/clipspace.ts +++ b/src/extensions/core/clipspace.ts @@ -187,8 +187,9 @@ export class ClipspaceDialog extends ComfyDialog { app.registerExtension({ name: 'Comfy.Clipspace', - init(app) { - app.openClipspace = function () { + init(appArg) { + const comfyApp = appArg as ComfyApp + comfyApp.openClipspace = function () { if (!ClipspaceDialog.instance) { ClipspaceDialog.instance = new ClipspaceDialog() ComfyApp.clipspace_invalidate_handler = ClipspaceDialog.invalidate @@ -196,7 +197,7 @@ app.registerExtension({ if (ComfyApp.clipspace) { ClipspaceDialog.instance.show() - } else app.ui.dialog.show('Clipspace is Empty!') + } else comfyApp.ui.dialog.show('Clipspace is Empty!') } } }) diff --git a/src/scripts/app.ts b/src/scripts/app.ts index 106603a93..0a0eca309 100644 --- a/src/scripts/app.ts +++ b/src/scripts/app.ts @@ -65,6 +65,7 @@ import { SYSTEM_NODE_DEFS, useNodeDefStore } from '@/stores/nodeDefStore' import { useSubgraphStore } from '@/stores/subgraphStore' import { useWidgetStore } from '@/stores/widgetStore' import { useWorkspaceStore } from '@/stores/workspaceStore' +import type { IComfyApp } from '@/types/appInterface' import type { ComfyExtension, MissingNodeType } from '@/types/comfy' import { type ExtensionManager } from '@/types/extensionTypes' import type { NodeExecutionId } from '@/types/nodeIdentification' @@ -127,7 +128,7 @@ type Clipspace = { combinedIndex: number } -export class ComfyApp { +export class ComfyApp implements IComfyApp { /** * List of entries to queue */ diff --git a/src/scripts/ui/menu/index.ts b/src/scripts/ui/menu/index.ts index e776685e1..6c403e254 100644 --- a/src/scripts/ui/menu/index.ts +++ b/src/scripts/ui/menu/index.ts @@ -1,4 +1,4 @@ -import type { ComfyApp } from '@/scripts/app' +import type { IComfyApp } from '@/types/appInterface' import { $el } from '../../ui' import { ComfyButtonGroup } from '../components/buttonGroup' @@ -13,13 +13,13 @@ export { DraggableList } from '@/scripts/ui/draggableList' export { applyTextReplacements, addStylesheet } from '@/scripts/utils' export class ComfyAppMenu { - app: ComfyApp + app: IComfyApp actionsGroup: ComfyButtonGroup settingsGroup: ComfyButtonGroup viewGroup: ComfyButtonGroup element: HTMLElement - constructor(app: ComfyApp) { + constructor(app: IComfyApp) { this.app = app // Keep the group as there are custom scripts attaching extra diff --git a/src/scripts/ui/settings.ts b/src/scripts/ui/settings.ts index 8f1bfca16..ca607c057 100644 --- a/src/scripts/ui/settings.ts +++ b/src/scripts/ui/settings.ts @@ -3,14 +3,14 @@ import { useSettingStore } from '@/platform/settings/settingStore' import type { SettingParams } from '@/platform/settings/types' import { useToastStore } from '@/platform/updates/common/toastStore' import type { Settings } from '@/schemas/apiSchema' -import type { ComfyApp } from '@/scripts/app' +import type { IComfyApp } from '@/types/appInterface' import { ComfyDialog } from './dialog' export class ComfySettingsDialog extends ComfyDialog { - app: ComfyApp + app: IComfyApp - constructor(app: ComfyApp) { + constructor(app: IComfyApp) { super() this.app = app } diff --git a/src/scripts/widgets.ts b/src/scripts/widgets.ts index 408555a19..c03183b25 100644 --- a/src/scripts/widgets.ts +++ b/src/scripts/widgets.ts @@ -24,7 +24,7 @@ import { transformInputSpecV1ToV2 } from '@/schemas/nodeDef/migration' import type { InputSpec as InputSpecV2 } from '@/schemas/nodeDef/nodeDefSchemaV2' import type { InputSpec } from '@/schemas/nodeDefSchema' -import type { ComfyApp } from './app' +import type { IComfyApp } from '@/types/appInterface' import './domWidget' import './errorNodeWidgets' @@ -37,7 +37,7 @@ export type ComfyWidgetConstructor = ( node: LGraphNode, inputName: string, inputData: InputSpec, - app: ComfyApp, + app: IComfyApp, widgetName?: string ) => { widget: IBaseWidget; minWidth?: number; minHeight?: number } diff --git a/src/stores/queueStore.ts b/src/stores/queueStore.ts index c302a1076..ee692e587 100644 --- a/src/stores/queueStore.ts +++ b/src/stores/queueStore.ts @@ -15,7 +15,7 @@ import type { TaskOutput } from '@/schemas/apiSchema' import { api } from '@/scripts/api' -import type { ComfyApp } from '@/scripts/app' +import type { IComfyApp } from '@/types/appInterface' import { useExtensionService } from '@/services/extensionService' import { getJobDetail } from '@/services/jobOutputCache' import { useNodeOutputStore } from '@/stores/imagePreviewStore' @@ -408,7 +408,7 @@ export class TaskItemImpl { return new TaskItemImpl(this.job, jobDetail.outputs) } - public async loadWorkflow(app: ComfyApp) { + public async loadWorkflow(app: IComfyApp) { if (!this.isHistory) { return } diff --git a/src/types/appInterface.ts b/src/types/appInterface.ts new file mode 100644 index 000000000..81631517f --- /dev/null +++ b/src/types/appInterface.ts @@ -0,0 +1,60 @@ +import type { LGraph, LGraphCanvas } from '@/lib/litegraph/src/litegraph' +import type { NodeExecutionOutput } from '@/schemas/apiSchema' +import type { ComfyWorkflow } from '@/platform/workflow/management/stores/workflowStore' +import type { WorkflowOpenSource } from '@/platform/telemetry/types' +import type { ComfyWorkflowJSON } from '@/platform/workflow/validation/schemas/workflowSchema' +import type { ComfyNodeDef } from '@/schemas/nodeDefSchema' +import type { ComfyExtension } from '@/types/comfy' + +import type { ComfyWidgetConstructor } from '@/scripts/widgets' + +export interface IComfyApp { + vueAppReady: boolean + rootGraph: LGraph + canvas: LGraphCanvas + configuringGraph: boolean + nodeOutputs: Record + + /** @deprecated storageLocation is always 'server' */ + readonly storageLocation: string + /** @deprecated storage migration is no longer needed */ + readonly isNewUserSession: boolean + /** @deprecated Use useExecutionStore().lastExecutionError instead */ + readonly lastExecutionError: unknown + /** @deprecated Use useWidgetStore().widgets instead */ + readonly widgets: Record + + getPreviewFormatParam(): string + + loadGraphData( + graphData?: ComfyWorkflowJSON, + clean?: boolean, + restore_view?: boolean, + workflow?: string | null | ComfyWorkflow, + options?: { + showMissingNodesDialog?: boolean + showMissingModelsDialog?: boolean + checkForRerouteMigration?: boolean + openSource?: WorkflowOpenSource + } + ): Promise + + graphToPrompt(graph?: LGraph): Promise<{ + workflow: ComfyWorkflowJSON + output: Record + }> + + queuePrompt( + number: number, + batchCount?: number, + queueNodeIds?: string[] + ): Promise + + clean(): void + + handleFile(file: File, openSource?: WorkflowOpenSource): Promise + + registerExtension(extension: ComfyExtension): void + + registerNodeDef(nodeId: string, nodeDef: ComfyNodeDef): Promise +} diff --git a/src/types/comfy.ts b/src/types/comfy.ts index 436e504ef..691a4f610 100644 --- a/src/types/comfy.ts +++ b/src/types/comfy.ts @@ -7,7 +7,7 @@ import type { SettingParams } from '@/platform/settings/types' import type { ComfyWorkflowJSON } from '@/platform/workflow/validation/schemas/workflowSchema' import type { Keybinding } from '@/schemas/keyBindingSchema' import type { ComfyNodeDef } from '@/schemas/nodeDefSchema' -import type { ComfyApp } from '@/scripts/app' +import type { IComfyApp } from './appInterface' import type { ComfyWidgetConstructor } from '@/scripts/widgets' import type { ComfyCommand } from '@/stores/commandStore' import type { AuthUserInfo } from '@/types/authTypes' @@ -136,12 +136,12 @@ export interface ComfyExtension { * Allows any initialisation, e.g. loading resources. Called after the canvas is created but before nodes are added * @param app The ComfyUI app instance */ - init?(app: ComfyApp): Promise | void + init?(app: IComfyApp): Promise | void /** * Allows any additional setup, called after the application is fully set up and running * @param app The ComfyUI app instance */ - setup?(app: ComfyApp): Promise | void + setup?(app: IComfyApp): Promise | void /** * Called before nodes are registered with the graph * @param defs The collection of node definitions, add custom ones or edit existing ones @@ -149,7 +149,7 @@ export interface ComfyExtension { */ addCustomNodeDefs?( defs: Record, - app: ComfyApp + app: IComfyApp ): Promise | void // TODO(huchenlei): We should deprecate the async return value of // getCustomWidgets. @@ -158,7 +158,7 @@ export interface ComfyExtension { * @param app The ComfyUI app instance * @returns An array of {[widget name]: widget data} */ - getCustomWidgets?(app: ComfyApp): Promise | Widgets + getCustomWidgets?(app: IComfyApp): Promise | Widgets /** * Allows the extension to add additional commands to the selection toolbox @@ -190,7 +190,7 @@ export interface ComfyExtension { beforeRegisterNodeDef?( nodeType: typeof LGraphNode, nodeData: ComfyNodeDef, - app: ComfyApp + app: IComfyApp ): Promise | void /** @@ -200,7 +200,7 @@ export interface ComfyExtension { * @param defs The node definitions * @param app The ComfyUI app instance */ - beforeRegisterVueAppNodeDefs?(defs: ComfyNodeDef[], app: ComfyApp): void + beforeRegisterVueAppNodeDefs?(defs: ComfyNodeDef[], app: IComfyApp): void /** * Allows the extension to register additional nodes with LGraph after standard nodes are added. @@ -208,7 +208,7 @@ export interface ComfyExtension { * * @param app The ComfyUI app instance */ - registerCustomNodes?(app: ComfyApp): Promise | void + registerCustomNodes?(app: IComfyApp): Promise | void /** * Allows the extension to modify a node that has been reloaded onto the graph. * If you break something in the backend and want to patch workflows in the frontend @@ -216,13 +216,13 @@ export interface ComfyExtension { * @param node The node that has been loaded * @param app The ComfyUI app instance */ - loadedGraphNode?(node: LGraphNode, app: ComfyApp): void + loadedGraphNode?(node: LGraphNode, app: IComfyApp): void /** * Allows the extension to run code after the constructor of the node * @param node The node that has been created * @param app The ComfyUI app instance */ - nodeCreated?(node: LGraphNode, app: ComfyApp): void + nodeCreated?(node: LGraphNode, app: IComfyApp): void /** * Allows the extension to modify the graph data before it is configured. @@ -247,7 +247,7 @@ export interface ComfyExtension { * Extensions can register at any time and will receive the latest value immediately. * This is an experimental API and may be changed or removed in the future. */ - onAuthUserResolved?(user: AuthUserInfo, app: ComfyApp): Promise | void + onAuthUserResolved?(user: AuthUserInfo, app: IComfyApp): Promise | void /** * Fired whenever the auth token is refreshed. diff --git a/src/types/index.ts b/src/types/index.ts index 947c9b3ad..f6779d718 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -13,7 +13,7 @@ import type { UserData, UserDataFullInfo } from '@/schemas/apiSchema' -import type { ComfyApp } from '@/scripts/app' +import type { IComfyApp } from './appInterface' import type { BottomPanelExtension, @@ -27,6 +27,7 @@ import type { export type { ComfyExtension } from './comfy' export type { ComfyApi } from '@/scripts/api' export type { ComfyApp } from '@/scripts/app' +export type { IComfyApp } from './appInterface' export type { ComfyNodeDef } from '@/schemas/nodeDefSchema' export type { InputSpec } from '@/schemas/nodeDefSchema' export type { @@ -78,7 +79,7 @@ interface AppReadiness { declare global { interface Window { /** For use by extensions and in the browser console. Where possible, import `app` from '@/scripts/app' instead. */ - app?: ComfyApp + app?: IComfyApp /** For use by extensions and in the browser console. Where possible, import `app` and access via `app.graph` instead. */ graph?: unknown