From e05a33cb178300e8955a97803e645c47a5a9f080 Mon Sep 17 00:00:00 2001 From: Chenlei Hu Date: Fri, 5 Jul 2024 21:18:32 -0400 Subject: [PATCH] Rename to ts (#92) Rename Remove ts-nocheck, fix errors Update state when graph cleared via UI (#88) Convert to ts, fix imports Co-authored-by: pythongosssss <125205205+pythongosssss@users.noreply.github.com> --- src/scripts/app.ts | 13 +- .../{changeTracker.js => changeTracker.ts} | 22 ++- src/scripts/ui.ts | 18 ++- .../{asyncDialog.js => asyncDialog.ts} | 10 +- .../ui/components/{button.js => button.ts} | 65 +++++---- .../{buttonGroup.js => buttonGroup.ts} | 18 +-- src/scripts/ui/components/index.ts | 3 + .../ui/components/{popup.js => popup.ts} | 35 ++--- .../{splitButton.js => splitButton.ts} | 32 +++-- src/scripts/ui/menu/{index.js => index.ts} | 48 ++++--- ...{interruptButton.js => interruptButton.ts} | 4 +- .../menu/{queueButton.js => queueButton.ts} | 12 +- .../menu/{queueOptions.js => queueOptions.ts} | 11 +- .../menu/{viewHistory.js => viewHistory.ts} | 5 +- .../ui/menu/{viewList.js => viewList.ts} | 37 ++++- .../ui/menu/{viewQueue.js => viewQueue.ts} | 5 +- .../ui/menu/{workflows.js => workflows.ts} | 129 +++++++++--------- src/scripts/ui/settings.ts | 2 +- src/scripts/ui/{utils.js => utils.ts} | 37 ++--- src/scripts/utils.ts | 19 +-- src/scripts/{workflows.js => workflows.ts} | 80 +++-------- 21 files changed, 305 insertions(+), 300 deletions(-) rename src/scripts/{changeTracker.js => changeTracker.ts} (94%) rename src/scripts/ui/components/{asyncDialog.js => asyncDialog.ts} (81%) rename src/scripts/ui/components/{button.js => button.ts} (78%) rename src/scripts/ui/components/{buttonGroup.js => buttonGroup.ts} (68%) create mode 100644 src/scripts/ui/components/index.ts rename src/scripts/ui/components/{popup.js => popup.ts} (86%) rename src/scripts/ui/components/{splitButton.js => splitButton.ts} (68%) rename src/scripts/ui/menu/{index.js => index.ts} (90%) rename src/scripts/ui/menu/{interruptButton.js => interruptButton.ts} (88%) rename src/scripts/ui/menu/{queueButton.js => queueButton.ts} (91%) rename src/scripts/ui/menu/{queueOptions.js => queueOptions.ts} (88%) rename src/scripts/ui/menu/{viewHistory.js => viewHistory.ts} (89%) rename src/scripts/ui/menu/{viewList.js => viewList.ts} (86%) rename src/scripts/ui/menu/{viewQueue.js => viewQueue.ts} (94%) rename src/scripts/ui/menu/{workflows.js => workflows.ts} (90%) rename src/scripts/ui/{utils.js => utils.ts} (59%) rename src/scripts/{workflows.js => workflows.ts} (89%) diff --git a/src/scripts/app.ts b/src/scripts/app.ts index fb1b4220d..ab71b2435 100644 --- a/src/scripts/app.ts +++ b/src/scripts/app.ts @@ -20,9 +20,9 @@ import { parseComfyWorkflow, } from "../types/comfyWorkflow"; import { ComfyNodeDef } from "/types/apiTypes"; -import { ComfyAppMenu } from "./ui/menu/index.js"; -import { getStorageValue, setStorageValue } from "./utils.js"; -import { ComfyWorkflowManager } from "./workflows.js"; +import { ComfyAppMenu } from "./ui/menu/index"; +import { getStorageValue } from "./utils"; +import { ComfyWorkflowManager, ComfyWorkflow } from "./workflows"; import { LGraphCanvas, LGraph, @@ -2179,16 +2179,11 @@ export class ComfyApp { } } - /** - * Populates the graph with the specified workflow data - * @param {*} graphData A serialized graph object - * @param { boolean } clean If the graph state, e.g. images, should be cleared - */ async loadGraphData( graphData?: ComfyWorkflowJSON, clean: boolean = true, restore_view: boolean = true, - workflow: string | null = null + workflow: string | null | ComfyWorkflow = null ) { if (clean !== false) { this.clean(); diff --git a/src/scripts/changeTracker.js b/src/scripts/changeTracker.ts similarity index 94% rename from src/scripts/changeTracker.js rename to src/scripts/changeTracker.ts index 6f5e22e5a..ce4adbbb8 100644 --- a/src/scripts/changeTracker.js +++ b/src/scripts/changeTracker.ts @@ -1,27 +1,26 @@ -// @ts-nocheck - +import type { ComfyApp } from "./app"; import { api } from "./api"; import { clone } from "./utils"; import { LGraphCanvas, LiteGraph } from "@comfyorg/litegraph"; +import { ComfyWorkflow } from "./workflows"; export class ChangeTracker { static MAX_HISTORY = 50; - #app; + #app: ComfyApp; undo = []; redo = []; activeState = null; isOurLoad = false; - /** @type { import("./workflows").ComfyWorkflow | null } */ - workflow; + workflow: ComfyWorkflow | null; - ds; - nodeOutputs; + ds: { scale: number; offset: [number, number]; }; + nodeOutputs: any; get app() { return this.#app ?? this.workflow.manager.app; } - constructor(workflow) { + constructor(workflow: ComfyWorkflow) { this.workflow = workflow; } @@ -90,8 +89,7 @@ export class ChangeTracker { } } - /** @param { import("./app").ComfyApp } app */ - static init(app) { + static init(app: ComfyApp) { const changeTracker = () => app.workflowManager.activeWorkflow?.changeTracker ?? globalTracker; globalTracker.#setApp(app); @@ -137,7 +135,7 @@ export class ChangeTracker { if (await changeTracker().undoRedo(e)) return; // If our active element is some type of input then handle changes after they're done - if (ChangeTracker.bindInput(activeEl)) return; + if (ChangeTracker.bindInput(app, activeEl)) return; changeTracker().checkState(); }); }, @@ -277,4 +275,4 @@ export class ChangeTracker { } } -const globalTracker = new ChangeTracker({}); +const globalTracker = new ChangeTracker({} as ComfyWorkflow); diff --git a/src/scripts/ui.ts b/src/scripts/ui.ts index 0893aff52..588056dfa 100644 --- a/src/scripts/ui.ts +++ b/src/scripts/ui.ts @@ -24,11 +24,15 @@ type Props = { type Children = Element[] | Element | string | string[]; -export function $el( - tag: string, +type ElementType = K extends keyof HTMLElementTagNameMap + ? HTMLElementTagNameMap[K] + : HTMLElement; + +export function $el( + tag: TTag, propsOrChildren?: Children | Props, children?: Children -): HTMLElement { +): ElementType { const split = tag.split("."); const element = document.createElement(split.shift() as string); if (split.length > 0) { @@ -78,10 +82,10 @@ export function $el( } } } - return element; + return element as ElementType; } -function dragElement(dragEl, settings) { +function dragElement(dragEl, settings): () => void { var posDiffX = 0, posDiffY = 0, posStartX = 0, @@ -340,6 +344,8 @@ export class ComfyUI { menuHamburger: HTMLDivElement; menuContainer: HTMLDivElement; queueSize: Element; + restoreMenuPosition: () => void; + loadFile: () => void; constructor(app) { this.app = app; @@ -425,7 +431,6 @@ export class ComfyUI { }, }) as HTMLInputElement; - // @ts-ignore this.loadFile = () => fileInput.click(); const autoQueueModeEl = toggleSwitch( @@ -740,7 +745,6 @@ export class ComfyUI { }, }); - // @ts-ignore this.restoreMenuPosition = dragElement(this.menuContainer, this.settings); this.setStatus({ exec_info: { queue_remaining: "X" } }); diff --git a/src/scripts/ui/components/asyncDialog.js b/src/scripts/ui/components/asyncDialog.ts similarity index 81% rename from src/scripts/ui/components/asyncDialog.js rename to src/scripts/ui/components/asyncDialog.ts index 4c22afd21..1bb1889a7 100644 --- a/src/scripts/ui/components/asyncDialog.js +++ b/src/scripts/ui/components/asyncDialog.ts @@ -1,10 +1,10 @@ import { ComfyDialog } from "../dialog"; import { $el } from "../../ui"; -export class ComfyAsyncDialog extends ComfyDialog { - #resolve; +export class ComfyAsyncDialog extends ComfyDialog { + #resolve: (value: any) => void; - constructor(actions) { + constructor(actions?: Array) { super( "dialog.comfy-dialog.comfyui-dialog", actions?.map((opt) => { @@ -20,7 +20,7 @@ export class ComfyAsyncDialog extends ComfyDialog { ); } - show(html) { + show(html: string | HTMLElement | HTMLElement[]) { this.element.addEventListener("close", () => { this.close(); }); @@ -32,7 +32,7 @@ export class ComfyAsyncDialog extends ComfyDialog { }); } - showModal(html) { + showModal(html: string | HTMLElement | HTMLElement[]) { this.element.addEventListener("close", () => { this.close(); }); diff --git a/src/scripts/ui/components/button.js b/src/scripts/ui/components/button.ts similarity index 78% rename from src/scripts/ui/components/button.js rename to src/scripts/ui/components/button.ts index e3749731b..ce5f8417d 100644 --- a/src/scripts/ui/components/button.js +++ b/src/scripts/ui/components/button.ts @@ -1,37 +1,41 @@ -// @ts-nocheck - import { $el } from "../../ui"; -import { applyClasses, toggleElement } from "../utils"; +import { applyClasses, ClassList, toggleElement } from "../utils"; import { prop } from "../../utils"; +import type { ComfyPopup } from "./popup"; +import type { ComfyComponent } from "."; +import type { ComfyApp } from "scripts/app"; -/** - * @typedef {{ - * icon?: string; - * overIcon?: string; - * iconSize?: number; - * content?: string | HTMLElement; - * tooltip?: string; - * enabled?: boolean; - * action?: (e: Event, btn: ComfyButton) => void, - * classList?: import("../utils").ClassList, - * visibilitySetting?: { id: string, showValue: any }, - * app?: import("../../app").ComfyApp - * }} ComfyButtonProps - */ -export class ComfyButton { +type ComfyButtonProps = { + icon?: string; + overIcon?: string; + iconSize?: number; + content?: string | HTMLElement; + tooltip?: string; + enabled?: boolean; + action?: (e: Event, btn: ComfyButton) => void; + classList?: ClassList; + visibilitySetting?: { id: string; showValue: any }; + app?: ComfyApp; +}; + +export class ComfyButton implements ComfyComponent { #over = 0; #popupOpen = false; isOver = false; iconElement = $el("i.mdi"); contentElement = $el("span"); - /** - * @type {import("./popup").ComfyPopup} - */ - popup; + popup: ComfyPopup; + element: HTMLElement; + overIcon: string; + iconSize: number; + content: string | HTMLElement; + icon: string; + tooltip: string; + classList: ClassList; + hidden: boolean; + enabled: boolean; + action: (e: Event, btn: ComfyButton) => void; - /** - * @param {ComfyButtonProps} opts - */ constructor({ icon, overIcon, @@ -43,7 +47,7 @@ export class ComfyButton { visibilitySetting, app, enabled = true, - }) { + }: ComfyButtonProps) { this.element = $el( "button", { @@ -101,7 +105,7 @@ export class ComfyButton { this.hidden = prop(this, "hidden", false, this.updateClasses); this.enabled = prop(this, "enabled", enabled, () => { this.updateClasses(); - this.element.disabled = !this.enabled; + (this.element as HTMLButtonElement).disabled = !this.enabled; }); this.action = prop(this, "action", action); this.element.addEventListener("click", (e) => { @@ -148,12 +152,7 @@ export class ComfyButton { applyClasses(this.element, this.classList, ...internalClasses); }; - /** - * - * @param { import("./popup").ComfyPopup } popup - * @param { "click" | "hover" } mode - */ - withPopup(popup, mode = "click") { + withPopup(popup: ComfyPopup, mode: "click" | "hover" = "click") { this.popup = popup; if (mode === "hover") { diff --git a/src/scripts/ui/components/buttonGroup.js b/src/scripts/ui/components/buttonGroup.ts similarity index 68% rename from src/scripts/ui/components/buttonGroup.js rename to src/scripts/ui/components/buttonGroup.ts index 335c70910..b4028b11f 100644 --- a/src/scripts/ui/components/buttonGroup.js +++ b/src/scripts/ui/components/buttonGroup.ts @@ -1,34 +1,26 @@ -// @ts-nocheck - import { $el } from "../../ui"; import { ComfyButton } from "./button"; import { prop } from "../../utils"; export class ComfyButtonGroup { element = $el("div.comfyui-button-group"); + buttons: (HTMLElement | ComfyButton)[]; - /** @param {Array} buttons */ - constructor(...buttons) { + constructor(...buttons: (HTMLElement | ComfyButton)[]) { this.buttons = prop(this, "buttons", buttons, () => this.update()); } - /** - * @param {ComfyButton} button - * @param {number} index - */ - insert(button, index) { + insert(button: ComfyButton, index: number) { this.buttons.splice(index, 0, button); this.update(); } - /** @param {ComfyButton} button */ - append(button) { + append(button: ComfyButton) { this.buttons.push(button); this.update(); } - /** @param {ComfyButton|number} indexOrButton */ - remove(indexOrButton) { + remove(indexOrButton: ComfyButton | number) { if (typeof indexOrButton !== "number") { indexOrButton = this.buttons.indexOf(indexOrButton); } diff --git a/src/scripts/ui/components/index.ts b/src/scripts/ui/components/index.ts new file mode 100644 index 000000000..178b4edea --- /dev/null +++ b/src/scripts/ui/components/index.ts @@ -0,0 +1,3 @@ +export interface ComfyComponent { + element: T; +} diff --git a/src/scripts/ui/components/popup.js b/src/scripts/ui/components/popup.ts similarity index 86% rename from src/scripts/ui/components/popup.js rename to src/scripts/ui/components/popup.ts index e02e9620b..14b2afc77 100644 --- a/src/scripts/ui/components/popup.js +++ b/src/scripts/ui/components/popup.ts @@ -1,24 +1,19 @@ -// @ts-nocheck - import { prop } from "../../utils"; import { $el } from "../../ui"; -import { applyClasses } from "../utils"; +import { applyClasses, ClassList } from "../utils"; export class ComfyPopup extends EventTarget { element = $el("div.comfyui-popup"); + open: boolean; + children: HTMLElement[]; + target: HTMLElement; + ignoreTarget: boolean; + container: HTMLElement; + position: string; + closeOnEscape: boolean; + horizontal: string; + classList: ClassList; - /** - * @param {{ - * target: HTMLElement, - * container?: HTMLElement, - * classList?: import("../utils").ClassList, - * ignoreTarget?: boolean, - * closeOnEscape?: boolean, - * position?: "absolute" | "relative", - * horizontal?: "left" | "right" - * }} param0 - * @param {...HTMLElement} children - */ constructor( { target, @@ -28,8 +23,16 @@ export class ComfyPopup extends EventTarget { closeOnEscape = true, position = "absolute", horizontal = "left", + }: { + target: HTMLElement; + container?: HTMLElement; + classList?: ClassList; + ignoreTarget?: boolean; + closeOnEscape?: boolean; + position?: "absolute" | "relative"; + horizontal?: "left" | "right"; }, - ...children + ...children: HTMLElement[] ) { super(); this.target = target; diff --git a/src/scripts/ui/components/splitButton.js b/src/scripts/ui/components/splitButton.ts similarity index 68% rename from src/scripts/ui/components/splitButton.js rename to src/scripts/ui/components/splitButton.ts index 789063350..b5b78fc63 100644 --- a/src/scripts/ui/components/splitButton.js +++ b/src/scripts/ui/components/splitButton.ts @@ -1,23 +1,27 @@ -// @ts-nocheck - import { $el } from "../../ui"; import { ComfyButton } from "./button"; import { prop } from "../../utils"; import { ComfyPopup } from "./popup"; export class ComfySplitButton { - /** - * @param {{ - * primary: ComfyButton, - * mode?: "hover" | "click", - * horizontal?: "left" | "right", - * position?: "relative" | "absolute" - * }} param0 - * @param {Array | Array} items - */ + arrow: ComfyButton; + element: HTMLElement; + popup: ComfyPopup; + items: Array; + constructor( - { primary, mode, horizontal = "left", position = "relative" }, - ...items + { + primary, + mode, + horizontal = "left", + position = "relative", + }: { + primary: ComfyButton; + mode?: "hover" | "click"; + horizontal?: "left" | "right"; + position?: "relative" | "absolute"; + }, + ...items: Array ) { this.arrow = new ComfyButton({ icon: "chevron-down", @@ -46,7 +50,7 @@ export class ComfySplitButton { update() { this.popup.element.replaceChildren( - ...this.items.map((b) => b.element ?? b) + ...this.items.map((b) => ("element" in b ? b.element : b)) ); } } diff --git a/src/scripts/ui/menu/index.js b/src/scripts/ui/menu/index.ts similarity index 90% rename from src/scripts/ui/menu/index.js rename to src/scripts/ui/menu/index.ts index 1171da67d..1aadd1037 100644 --- a/src/scripts/ui/menu/index.js +++ b/src/scripts/ui/menu/index.ts @@ -1,5 +1,4 @@ -// @ts-nocheck - +import type { ComfyApp } from "scripts/app"; import { api } from "../../api"; import { $el } from "../../ui"; import { downloadBlob } from "../../utils"; @@ -12,6 +11,9 @@ import { ComfyWorkflowsMenu } from "./workflows"; import { ComfyViewQueueButton } from "./viewQueue"; import { getInteruptButton } from "./interruptButton"; import "./menu.css"; +import type { ComfySettingsDialog } from "../settings"; + +type MenuPosition = "Disabled" | "Top" | "Bottom"; const collapseOnMobile = (t) => { (t.element ?? t).classList.add("comfyui-menu-mobile-collapse"); @@ -33,15 +35,23 @@ export class ComfyAppMenu { #sizeBreaks = Object.keys(this.#lastSizeBreaks); #cachedInnerSize = null; #cacheTimeout = null; + app: ComfyApp; + workflows: ComfyWorkflowsMenu; + logo: HTMLElement; + saveButton: ComfySplitButton; + actionsGroup: ComfyButtonGroup; + settingsGroup: ComfyButtonGroup; + viewGroup: ComfyButtonGroup; + mobileMenuButton: ComfyButton; + element: HTMLElement; + menuPositionSetting: ReturnType; + position: MenuPosition; - /** - * @param { import("../../app").ComfyApp } app - */ - constructor(app) { + constructor(app: ComfyApp) { this.app = app; this.workflows = new ComfyWorkflowsMenu(app); - const getSaveButton = (t) => + const getSaveButton = (t?: string) => new ComfyButton({ icon: "content-save", tooltip: "Save the current workflow", @@ -158,14 +168,14 @@ export class ComfyAppMenu { showOnMobile(this.mobileMenuButton).element, ]); - let resizeHandler; + let resizeHandler: () => void; this.menuPositionSetting = app.ui.settings.addSetting({ id: "Comfy.UseNewMenu", defaultValue: "Disabled", name: "[Beta] Use new menu and workflow management. Note: On small screens the menu will always be at the top.", type: "combo", options: ["Disabled", "Top", "Bottom"], - onChange: async (v) => { + onChange: async (v: MenuPosition) => { if (v && v !== "Disabled") { if (!resizeHandler) { resizeHandler = () => { @@ -189,7 +199,7 @@ export class ComfyAppMenu { }); } - updatePosition(v) { + updatePosition(v: MenuPosition) { document.body.style.display = "grid"; this.app.ui.menuContainer.style.display = "none"; this.element.style.removeProperty("display"); @@ -202,7 +212,7 @@ export class ComfyAppMenu { this.calculateSizeBreak(); } - updateSizeBreak(idx, prevIdx, direction) { + updateSizeBreak(idx: number, prevIdx: number, direction: number) { const newSize = this.#sizeBreaks[idx]; if (newSize === this.#sizeBreak) return; this.#cachedInnerSize = null; @@ -264,7 +274,7 @@ export class ComfyAppMenu { this.updateSizeBreak(idx, currIdx, direction); } - calculateInnerSize(idx) { + calculateInnerSize(idx: number) { // Cache the inner size to prevent too much calculation when resizing the window clearTimeout(this.#cacheTimeout); if (this.#cachedInnerSize) { @@ -293,10 +303,7 @@ export class ComfyAppMenu { return this.#cachedInnerSize; } - /** - * @param {string} defaultName - */ - getFilename(defaultName) { + getFilename(defaultName: string) { if (this.app.ui.settings.getSettingValue("Comfy.PromptFilename", true)) { defaultName = prompt("Save workflow as:", defaultName); if (!defaultName) return; @@ -307,11 +314,10 @@ export class ComfyAppMenu { return defaultName; } - /** - * @param {string} [filename] - * @param { "workflow" | "output" } [promptProperty] - */ - async exportWorkflow(filename, promptProperty) { + async exportWorkflow( + filename: string, + promptProperty: "workflow" | "output" + ) { if (this.app.workflowManager.activeWorkflow?.path) { filename = this.app.workflowManager.activeWorkflow.name; } diff --git a/src/scripts/ui/menu/interruptButton.js b/src/scripts/ui/menu/interruptButton.ts similarity index 88% rename from src/scripts/ui/menu/interruptButton.js rename to src/scripts/ui/menu/interruptButton.ts index 353c5dd51..c4861b997 100644 --- a/src/scripts/ui/menu/interruptButton.js +++ b/src/scripts/ui/menu/interruptButton.ts @@ -1,9 +1,7 @@ -// @ts-nocheck - import { api } from "../../api"; import { ComfyButton } from "../components/button"; -export function getInteruptButton(visibility) { +export function getInteruptButton(visibility: string) { const btn = new ComfyButton({ icon: "close", tooltip: "Cancel current generation", diff --git a/src/scripts/ui/menu/queueButton.js b/src/scripts/ui/menu/queueButton.ts similarity index 91% rename from src/scripts/ui/menu/queueButton.js rename to src/scripts/ui/menu/queueButton.ts index ad204f09b..bedd0ebc7 100644 --- a/src/scripts/ui/menu/queueButton.js +++ b/src/scripts/ui/menu/queueButton.ts @@ -1,17 +1,16 @@ -// @ts-nocheck - import { ComfyButton } from "../components/button"; import { $el } from "../../ui"; import { api } from "../../api"; import { ComfySplitButton } from "../components/splitButton"; import { ComfyQueueOptions } from "./queueOptions"; import { prop } from "../../utils"; +import type { ComfyApp } from "scripts/app"; export class ComfyQueueButton { element = $el("div.comfyui-queue-button"); #internalQueueSize = 0; - queuePrompt = async (e) => { + queuePrompt = async (e?: MouseEvent) => { this.#internalQueueSize += this.queueOptions.batchCount; // Hold shift to queue front, event is undefined when auto-queue is enabled await this.app.queuePrompt( @@ -19,8 +18,13 @@ export class ComfyQueueButton { this.queueOptions.batchCount ); }; + queueOptions: ComfyQueueOptions; + app: ComfyApp; + queueSizeElement: HTMLElement; + autoQueueMode: string; + graphHasChanged: boolean; - constructor(app) { + constructor(app: ComfyApp) { this.app = app; this.queueSizeElement = $el("span.comfyui-queue-count", { textContent: "?", diff --git a/src/scripts/ui/menu/queueOptions.js b/src/scripts/ui/menu/queueOptions.ts similarity index 88% rename from src/scripts/ui/menu/queueOptions.js rename to src/scripts/ui/menu/queueOptions.ts index 47baf9791..ba97f4743 100644 --- a/src/scripts/ui/menu/queueOptions.js +++ b/src/scripts/ui/menu/queueOptions.ts @@ -1,12 +1,17 @@ -// @ts-nocheck - +import type { ComfyApp } from "scripts/app"; import { $el } from "../../ui"; import { prop } from "../../utils"; export class ComfyQueueOptions extends EventTarget { element = $el("div.comfyui-queue-options"); + app: ComfyApp; + batchCountInput: HTMLInputElement; + batchCount: number; + batchCountRange: HTMLInputElement; + autoQueueMode: string; + autoQueueEl: HTMLElement; - constructor(app) { + constructor(app: ComfyApp) { super(); this.app = app; diff --git a/src/scripts/ui/menu/viewHistory.js b/src/scripts/ui/menu/viewHistory.ts similarity index 89% rename from src/scripts/ui/menu/viewHistory.js rename to src/scripts/ui/menu/viewHistory.ts index bc38d74f6..7672c87f7 100644 --- a/src/scripts/ui/menu/viewHistory.js +++ b/src/scripts/ui/menu/viewHistory.ts @@ -1,10 +1,9 @@ -// @ts-nocheck - +import type { ComfyApp } from "scripts/app"; import { ComfyButton } from "../components/button"; import { ComfyViewList, ComfyViewListButton } from "./viewList"; export class ComfyViewHistoryButton extends ComfyViewListButton { - constructor(app) { + constructor(app: ComfyApp) { super(app, { button: new ComfyButton({ content: "View History", diff --git a/src/scripts/ui/menu/viewList.js b/src/scripts/ui/menu/viewList.ts similarity index 86% rename from src/scripts/ui/menu/viewList.js rename to src/scripts/ui/menu/viewList.ts index 07ad1b879..b3f737516 100644 --- a/src/scripts/ui/menu/viewList.js +++ b/src/scripts/ui/menu/viewList.ts @@ -1,11 +1,18 @@ -// @ts-nocheck - import { ComfyButton } from "../components/button"; import { $el } from "../../ui"; import { api } from "../../api"; import { ComfyPopup } from "../components/popup"; +import type { ComfyApp } from "scripts/app"; + +type ViewListMode = "Queue" | "History"; export class ComfyViewListButton { + popup: ComfyPopup; + app: ComfyApp; + button: ComfyButton; + element: HTMLDivElement; + list: ComfyViewList; + get open() { return this.popup.open; } @@ -14,10 +21,20 @@ export class ComfyViewListButton { this.popup.open = open; } - constructor(app, { button, list, mode }) { + constructor( + app: ComfyApp, + { + button, + list, + mode, + }: { button: ComfyButton; list: typeof ComfyViewList; mode: ViewListMode } + ) { this.app = app; this.button = button; - this.element = $el("div.comfyui-button-wrapper", this.button.element); + this.element = $el( + "div.comfyui-button-wrapper", + this.button.element + ) as HTMLDivElement; this.popup = new ComfyPopup({ target: this.element, container: this.element, @@ -42,9 +59,15 @@ export class ComfyViewListButton { } export class ComfyViewList { - popup; - - constructor(app, mode, popup) { + app: ComfyApp; + mode: ViewListMode; + popup: ComfyPopup; + type: string; + items: HTMLElement; + clear: ComfyButton; + refresh: ComfyButton; + element: HTMLElement; + constructor(app: ComfyApp, mode: ViewListMode, popup: ComfyPopup) { this.app = app; this.mode = mode; this.popup = popup; diff --git a/src/scripts/ui/menu/viewQueue.js b/src/scripts/ui/menu/viewQueue.ts similarity index 94% rename from src/scripts/ui/menu/viewQueue.js rename to src/scripts/ui/menu/viewQueue.ts index 85df4d31a..5803b610a 100644 --- a/src/scripts/ui/menu/viewQueue.js +++ b/src/scripts/ui/menu/viewQueue.ts @@ -1,11 +1,10 @@ -// @ts-nocheck - import { ComfyButton } from "../components/button"; import { ComfyViewList, ComfyViewListButton } from "./viewList"; import { api } from "../../api"; +import type { ComfyApp } from "scripts/app"; export class ComfyViewQueueButton extends ComfyViewListButton { - constructor(app) { + constructor(app: ComfyApp) { super(app, { button: new ComfyButton({ content: "View Queue", diff --git a/src/scripts/ui/menu/workflows.js b/src/scripts/ui/menu/workflows.ts similarity index 90% rename from src/scripts/ui/menu/workflows.js rename to src/scripts/ui/menu/workflows.ts index a500fa34f..edb33214b 100644 --- a/src/scripts/ui/menu/workflows.js +++ b/src/scripts/ui/menu/workflows.ts @@ -1,5 +1,3 @@ -// @ts-nocheck - import { ComfyButton } from "../components/button"; import { prop, getStorageValue, setStorageValue } from "../../utils"; import { $el } from "../../ui"; @@ -8,10 +6,19 @@ import { ComfyPopup } from "../components/popup"; import { createSpinner } from "../spinner"; import { ComfyWorkflow, trimJsonExt } from "../../workflows"; import { ComfyAsyncDialog } from "../components/asyncDialog"; +import type { ComfyApp } from "scripts/app"; +import type { ComfyComponent } from "../components"; export class ComfyWorkflowsMenu { #first = true; element = $el("div.comfyui-workflows"); + popup: ComfyPopup; + app: ComfyApp; + buttonProgress: HTMLElement; + workflowLabel: HTMLElement; + button: ComfyButton; + content: ComfyWorkflowsContent; + unsaved: boolean; get open() { return this.popup.open; @@ -21,10 +28,7 @@ export class ComfyWorkflowsMenu { this.popup.open = open; } - /** - * @param {import("../../app").ComfyApp} app - */ - constructor(app) { + constructor(app: ComfyApp) { this.app = app; this.#bindEvents(); @@ -158,10 +162,7 @@ export class ComfyWorkflowsMenu { return menu; } - /** - * @param {import("../../app").ComfyApp} app - */ - registerExtension(app) { + registerExtension(app: ComfyApp) { const self = this; app.registerExtension({ name: "Comfy.Workflows", @@ -192,11 +193,10 @@ export class ComfyWorkflowsMenu { app.graph.setDirtyCanvas(true, true); } - /** - * @param {HTMLImageElement} img - * @param {ComfyWorkflow} workflow - */ - async function sendToWorkflow(img, workflow) { + async function sendToWorkflow( + img: HTMLImageElement, + workflow: ComfyWorkflow + ) { await workflow.load(); let options = []; const nodes = app.graph.computeExecutionOrder(false); @@ -226,22 +226,23 @@ export class ComfyWorkflowsMenu { } const getExtraMenuOptions = nodeType.prototype["getExtraMenuOptions"]; - nodeType.prototype["getExtraMenuOptions"] = function (_, options) { + nodeType.prototype["getExtraMenuOptions"] = function ( + this: { imageIndex?: number; overIndex?: number; imgs: string[] }, + _, + options + ) { const r = getExtraMenuOptions?.apply?.(this, arguments); if ( app.ui.settings.getSettingValue("Comfy.UseNewMenu", false) === true ) { - const t = - /** @type { {imageIndex?: number, overIndex?: number, imgs: string[]} } */ /** @type {any} */ ( - this - ); + const t = this; let img; if (t.imageIndex != null) { // An image is selected so select that img = t.imgs?.[t.imageIndex]; } else if (t.overIndex != null) { // No image is selected but one is hovered - img = t.img?.s[t.overIndex]; + img = t.imgs?.[t.overIndex]; } if (img) { @@ -283,17 +284,20 @@ export class ComfyWorkflowsMenu { export class ComfyWorkflowsContent { element = $el("div.comfyui-workflows-panel"); treeState = {}; - treeFiles = {}; - /** @type { Map } */ - openFiles = new Map(); - /** @type {WorkflowElement} */ - activeElement = null; + treeFiles: Record = {}; + openFiles: Map> = new Map(); + activeElement: WorkflowElement = null; + spinner: Element; + openElement: HTMLElement; + favoritesElement: HTMLElement; + treeElement: HTMLElement; + app: ComfyApp; + popup: ComfyPopup; + actions: HTMLElement; + filterText: string | undefined; + treeRoot: HTMLElement; - /** - * @param {import("../../app").ComfyApp} app - * @param {ComfyPopup} popup - */ - constructor(app, popup) { + constructor(app: ComfyApp, popup: ComfyPopup) { this.app = app; this.popup = popup; this.actions = $el("div.comfyui-workflows-actions", [ @@ -511,7 +515,7 @@ export class ComfyWorkflowsContent { $el("input", { placeholder: "Search", value: this.filterText ?? "", - oninput: (e) => { + oninput: (e: InputEvent) => { this.filterText = e.target["value"]?.trim(); clearTimeout(typingTimeout); typingTimeout = setTimeout(() => this.filterTree(), 250); @@ -589,25 +593,21 @@ export class ComfyWorkflowsContent { this.activeElement.primary.element.classList.remove("mdi-play"); } - /** @param {ComfyWorkflow} workflow */ - #getFavoriteIcon(workflow) { + #getFavoriteIcon(workflow: ComfyWorkflow) { return workflow.isFavorite ? "star" : "file-outline"; } - /** @param {ComfyWorkflow} workflow */ - #getFavoriteOverIcon(workflow) { + #getFavoriteOverIcon(workflow: ComfyWorkflow) { return workflow.isFavorite ? "star-off" : "star-outline"; } - /** @param {ComfyWorkflow} workflow */ - #getFavoriteTooltip(workflow) { + #getFavoriteTooltip(workflow: ComfyWorkflow) { return workflow.isFavorite ? "Remove this workflow from your favorites" : "Add this workflow to your favorites"; } - /** @param {ComfyWorkflow} workflow */ - #getFavoriteButton(workflow, primary) { + #getFavoriteButton(workflow: ComfyWorkflow, primary: boolean) { return new ComfyButton({ icon: this.#getFavoriteIcon(workflow), overIcon: this.#getFavoriteOverIcon(workflow), @@ -623,8 +623,7 @@ export class ComfyWorkflowsContent { }); } - /** @param {ComfyWorkflow} workflow */ - #getDeleteButton(workflow) { + #getDeleteButton(workflow: ComfyWorkflow) { const deleteButton = new ComfyButton({ icon: "delete", tooltip: "Delete this workflow", @@ -650,8 +649,7 @@ export class ComfyWorkflowsContent { return deleteButton; } - /** @param {ComfyWorkflow} workflow */ - #getInsertButton(workflow) { + #getInsertButton(workflow: ComfyWorkflow) { return new ComfyButton({ icon: "file-move-outline", iconSize: 18, @@ -671,7 +669,7 @@ export class ComfyWorkflowsContent { } /** @param {ComfyWorkflow} workflow */ - #getRenameButton(workflow) { + #getRenameButton(workflow: ComfyWorkflow) { return new ComfyButton({ icon: "pencil", tooltip: workflow.path @@ -690,8 +688,7 @@ export class ComfyWorkflowsContent { }); } - /** @param {ComfyWorkflow} workflow */ - #getWorkflowElement(workflow) { + #getWorkflowElement(workflow: ComfyWorkflow) { return new WorkflowElement(this, workflow, { primary: this.#getFavoriteButton(workflow, true), buttons: [ @@ -702,8 +699,7 @@ export class ComfyWorkflowsContent { }); } - /** @param {ComfyWorkflow} workflow */ - #createLeafNode(workflow) { + #createLeafNode(workflow: ComfyWorkflow) { const fileNode = this.#getWorkflowElement(workflow); this.treeFiles[workflow.path] = fileNode; return fileNode; @@ -741,12 +737,21 @@ export class ComfyWorkflowsContent { } } -class WorkflowElement { - /** - * @param { ComfyWorkflowsContent } parent - * @param { ComfyWorkflow } workflow - */ - constructor(parent, workflow, { tagName = "li", primary, buttons }) { +class WorkflowElement { + parent: ComfyWorkflowsContent; + workflow: ComfyWorkflow; + primary: TPrimary; + buttons: ComfyButton[]; + element: HTMLElement; + constructor( + parent: ComfyWorkflowsContent, + workflow: ComfyWorkflow, + { + tagName = "li", + primary, + buttons, + }: { tagName?: string; primary: TPrimary; buttons: ComfyButton[] } + ) { this.parent = parent; this.workflow = workflow; this.primary = primary; @@ -770,13 +775,15 @@ class WorkflowElement { } } -class WidgetSelectionDialog extends ComfyAsyncDialog { - #options; +type WidgetSelectionDialogOptions = Array<{ + widget: { name: string }; + node: { pos: [number, number]; title: string; id: string; type: string }; +}>; - /** - * @param {Array<{widget: {name: string}, node: {pos: [number, number], title: string, id: string, type: string}}>} options - */ - constructor(options) { +class WidgetSelectionDialog extends ComfyAsyncDialog { + #options: WidgetSelectionDialogOptions; + + constructor(options: WidgetSelectionDialogOptions) { super(); this.#options = options; } diff --git a/src/scripts/ui/settings.ts b/src/scripts/ui/settings.ts index ceb941947..98b9599cc 100644 --- a/src/scripts/ui/settings.ts +++ b/src/scripts/ui/settings.ts @@ -31,7 +31,7 @@ interface SettingParams { onChange?: (newValue: any, oldValue?: any) => void; attrs?: any; tooltip?: string; - options?: SettingOption[] | ((value: any) => SettingOption[]); + options?: Array | ((value: any) => SettingOption[]); } export class ComfySettingsDialog extends ComfyDialog { diff --git a/src/scripts/ui/utils.js b/src/scripts/ui/utils.ts similarity index 59% rename from src/scripts/ui/utils.js rename to src/scripts/ui/utils.ts index 1dc0609ef..1030e5af8 100644 --- a/src/scripts/ui/utils.js +++ b/src/scripts/ui/utils.ts @@ -1,16 +1,13 @@ -/** - * @typedef { string | string[] | Record } ClassList - */ +export type ClassList = string | string[] | Record; -/** - * @param { HTMLElement } element - * @param { ClassList } classList - * @param { string[] } requiredClasses - */ -export function applyClasses(element, classList, ...requiredClasses) { +export function applyClasses( + element: HTMLElement, + classList: ClassList, + ...requiredClasses: string[] +) { classList ??= ""; - let str; + let str: string; if (typeof classList === "string") { str = classList; } else if (classList instanceof Array) { @@ -29,14 +26,18 @@ export function applyClasses(element, classList, ...requiredClasses) { } } -/** - * @param { HTMLElement } element - * @param { { onHide?: (el: HTMLElement) => void, onShow?: (el: HTMLElement, value) => void } } [param1] - * @returns - */ -export function toggleElement(element, { onHide, onShow } = {}) { - let placeholder; - let hidden; +export function toggleElement( + element: HTMLElement, + { + onHide, + onShow, + }: { + onHide?: (el: HTMLElement) => void; + onShow?: (el: HTMLElement, value) => void; + } = {} +) { + let placeholder: HTMLElement | Comment; + let hidden: boolean; return (value) => { if (value) { if (hidden) { diff --git a/src/scripts/utils.ts b/src/scripts/utils.ts index a97a986e5..9ba4dfa81 100644 --- a/src/scripts/utils.ts +++ b/src/scripts/utils.ts @@ -136,14 +136,17 @@ export function downloadBlob(filename, blob) { }, 0); } -/** - * @template T - * @param {string} name - * @param {T} [defaultValue] - * @param {(currentValue: any, previousValue: any)=>void} [onChanged] - * @returns {T} - */ -export function prop(target, name, defaultValue, onChanged) { +export function prop( + target: object, + name: string, + defaultValue: T, + onChanged?: ( + currentValue: T, + previousValue: T, + target: object, + name: string + ) => void +): T { let currentValue; Object.defineProperty(target, name, { get() { diff --git a/src/scripts/workflows.js b/src/scripts/workflows.ts similarity index 89% rename from src/scripts/workflows.js rename to src/scripts/workflows.ts index 963a1a26c..e027f9987 100644 --- a/src/scripts/workflows.js +++ b/src/scripts/workflows.ts @@ -1,36 +1,31 @@ -// @ts-nocheck - +import type { ComfyApp } from "./app"; import { api } from "./api"; import { ChangeTracker } from "./changeTracker"; import { ComfyAsyncDialog } from "./ui/components/asyncDialog"; import { getStorageValue, setStorageValue } from "./utils"; -import { LGraphCanvas } from "@comfyorg/litegraph"; +import { LGraphCanvas, LGraph } from "@comfyorg/litegraph"; -function appendJsonExt(path) { +function appendJsonExt(path: string) { if (!path.toLowerCase().endsWith(".json")) { path += ".json"; } return path; } -export function trimJsonExt(path) { +export function trimJsonExt(path: string) { return path?.replace(/\.json$/, ""); } export class ComfyWorkflowManager extends EventTarget { - /** @type {string | null} */ - #activePromptId = null; + #activePromptId: string | null = null; #unsavedCount = 0; - #activeWorkflow; + #activeWorkflow: ComfyWorkflow; - /** @type {Record} */ - workflowLookup = {}; - /** @type {Array} */ - workflows = []; - /** @type {Array} */ - openWorkflows = []; - /** @type {Record}>} */ - queuedPrompts = {}; + workflowLookup: Record = {}; + workflows: Array = []; + openWorkflows: Array = []; + queuedPrompts: Record; }> = {}; + app: ComfyApp; get activeWorkflow() { return this.#activeWorkflow ?? this.openWorkflows[0]; @@ -44,10 +39,7 @@ export class ComfyWorkflowManager extends EventTarget { return this.queuedPrompts[this.#activePromptId]; } - /** - * @param {import("./app").ComfyApp} app - */ - constructor(app) { + constructor(app: ComfyApp) { super(); this.app = app; ChangeTracker.init(app); @@ -238,9 +230,9 @@ export class ComfyWorkflow { #path; #pathParts; #isFavorite = false; - /** @type {ChangeTracker | null} */ - changeTracker = null; + changeTracker: ChangeTracker | null = null; unsaved = false; + manager: ComfyWorkflowManager; get name() { return this.#name; @@ -262,25 +254,7 @@ export class ComfyWorkflow { return !!this.changeTracker; } - /** - * @overload - * @param {ComfyWorkflowManager} manager - * @param {string} path - */ - /** - * @overload - * @param {ComfyWorkflowManager} manager - * @param {string} path - * @param {string[]} pathParts - * @param {boolean} isFavorite - */ - /** - * @param {ComfyWorkflowManager} manager - * @param {string} path - * @param {string[]} [pathParts] - * @param {boolean} [isFavorite] - */ - constructor(manager, path, pathParts, isFavorite) { + constructor(manager: ComfyWorkflowManager, path: string, pathParts?: string[], isFavorite?: boolean) { this.manager = manager; if (pathParts) { this.#updatePath(path, pathParts); @@ -291,11 +265,7 @@ export class ComfyWorkflow { } } - /** - * @param {string} path - * @param {string[]} [pathParts] - */ - #updatePath(path, pathParts) { + #updatePath(path: string, pathParts: string[]) { this.#path = path; if (!pathParts) { @@ -344,10 +314,7 @@ export class ComfyWorkflow { } } - /** - * @param {boolean} value - */ - async favorite(value) { + async favorite(value: boolean) { try { if (this.#isFavorite === value) return; this.#isFavorite = value; @@ -363,10 +330,7 @@ export class ComfyWorkflow { } } - /** - * @param {string} path - */ - async rename(path) { + async rename(path: string) { path = appendJsonExt(path); let resp = await api.moveUserData( "workflows/" + this.path, @@ -412,8 +376,10 @@ export class ComfyWorkflow { if (!data) return; const old = localStorage.getItem("litegrapheditor_clipboard"); + // @ts-ignore const graph = new LGraph(data); const canvas = new LGraphCanvas(null, graph, { + // @ts-ignore skip_events: true, skip_render: true, }); @@ -449,11 +415,7 @@ export class ComfyWorkflow { } } - /** - * @param {string|null} path - * @param {boolean} overwrite - */ - async #save(path, overwrite) { + async #save(path: string | null, overwrite: boolean) { if (!path) { path = prompt( "Save workflow as:",