From 1b4dd57d32375d3b7e688efd29c805d1531c0a84 Mon Sep 17 00:00:00 2001 From: DrJKL Date: Mon, 12 Jan 2026 09:59:36 -0800 Subject: [PATCH] fix(types): remove @ts-expect-error suppressions from src/scripts --- .storybook/preview.ts | 2 +- apps/desktop-ui/.storybook/preview.ts | 2 +- apps/desktop-ui/src/main.ts | 2 +- src/scripts/app.ts | 12 +- src/scripts/domWidget.ts | 18 +- src/scripts/metadata/avif.ts | 27 +-- src/scripts/metadata/flac.ts | 22 +-- src/scripts/metadata/png.ts | 5 +- src/scripts/ui.ts | 201 ++++++++++------------- src/scripts/ui/components/asyncDialog.ts | 12 +- src/scripts/ui/components/button.ts | 57 +++---- src/scripts/ui/components/buttonGroup.ts | 5 +- src/scripts/ui/components/popup.ts | 8 +- src/scripts/ui/dialog.ts | 12 +- src/scripts/ui/draggableList.ts | 124 +++++++------- src/scripts/ui/imagePreview.ts | 85 +++++----- src/scripts/ui/toggleSwitch.ts | 49 +++--- src/scripts/ui/utils.ts | 8 +- src/scripts/utils.ts | 5 +- 19 files changed, 303 insertions(+), 353 deletions(-) diff --git a/.storybook/preview.ts b/.storybook/preview.ts index 95c3370902..5463b2ad46 100644 --- a/.storybook/preview.ts +++ b/.storybook/preview.ts @@ -15,7 +15,7 @@ import '@/assets/css/style.css' const ComfyUIPreset = definePreset(Aura, { semantic: { - // @ts-expect-error fix me + // @ts-expect-error PrimeVue type issue primary: Aura['primitive'].blue } }) diff --git a/apps/desktop-ui/.storybook/preview.ts b/apps/desktop-ui/.storybook/preview.ts index a0ead30cc1..0f7e8de18f 100644 --- a/apps/desktop-ui/.storybook/preview.ts +++ b/apps/desktop-ui/.storybook/preview.ts @@ -14,7 +14,7 @@ import { i18n } from '@/i18n' const ComfyUIPreset = definePreset(Aura, { semantic: { - // @ts-expect-error prime type quirk + // @ts-expect-error PrimeVue type issue primary: Aura['primitive'].blue } }) diff --git a/apps/desktop-ui/src/main.ts b/apps/desktop-ui/src/main.ts index a04ff8f810..bcee940567 100644 --- a/apps/desktop-ui/src/main.ts +++ b/apps/desktop-ui/src/main.ts @@ -15,7 +15,7 @@ import router from './router' const ComfyUIPreset = definePreset(Aura, { semantic: { - // @ts-expect-error fixme ts strict error + // @ts-expect-error PrimeVue type issue primary: Aura['primitive'].blue } }) diff --git a/src/scripts/app.ts b/src/scripts/app.ts index 454dabc5b1..da0af6f44f 100644 --- a/src/scripts/app.ts +++ b/src/scripts/app.ts @@ -162,8 +162,8 @@ export class ComfyApp { // TODO: Migrate internal usage to the /** @deprecated Use {@link rootGraph} instead */ - get graph(): unknown { - return this.rootGraphInternal! + get graph(): LGraph | undefined { + return this.rootGraphInternal } get rootGraph(): LGraph { @@ -1194,8 +1194,12 @@ export class ComfyApp { } try { - // @ts-expect-error Discrepancies between zod and litegraph - in progress - this.rootGraph.configure(graphData) + // TODO: Align ComfyWorkflowJSON (Zod schema) with ISerialisedGraph (litegraph types) + // The schemas are structurally compatible at runtime but TypeScript can't verify this. + // See: https://github.com/Comfy-Org/ComfyUI_frontend/issues/XXXX + this.rootGraph.configure( + graphData as Parameters[0] + ) // Save original renderer version before scaling (it gets modified during scaling) const originalMainGraphRenderer = diff --git a/src/scripts/domWidget.ts b/src/scripts/domWidget.ts index a1b57496dc..10463fefea 100644 --- a/src/scripts/domWidget.ts +++ b/src/scripts/domWidget.ts @@ -198,13 +198,15 @@ abstract class BaseDOMWidgetImpl } override createCopyForNode(node: LGraphNode): this { - // @ts-expect-error - const cloned: this = new (this.constructor as typeof this)({ + const constructorArg = { node: node, name: this.name, type: this.type, options: this.options - }) + } + const cloned: this = new (this.constructor as new ( + obj: typeof constructorArg + ) => this)(constructorArg) cloned.value = this.value // Preserve the Y position from the original widget to maintain proper positioning // when widgets are promoted through subgraph nesting @@ -231,14 +233,16 @@ export class DOMWidgetImpl } override createCopyForNode(node: LGraphNode): this { - // @ts-expect-error - const cloned: this = new (this.constructor as typeof this)({ + const constructorArg = { node: node, name: this.name, type: this.type, - element: this.element, // Include the element! + element: this.element, options: this.options - }) + } + const cloned: this = new (this.constructor as new ( + obj: typeof constructorArg + ) => this)(constructorArg) cloned.value = this.value // Preserve the Y position from the original widget to maintain proper positioning // when widgets are promoted through subgraph nesting diff --git a/src/scripts/metadata/avif.ts b/src/scripts/metadata/avif.ts index c0d747d9e2..269958fda2 100644 --- a/src/scripts/metadata/avif.ts +++ b/src/scripts/metadata/avif.ts @@ -318,14 +318,16 @@ function parseAvifMetadata(buffer: ArrayBuffer): ComfyMetadata { return metadata } -// @ts-expect-error fixme ts strict error -function parseExifData(exifData) { +function parseExifData(exifData: Uint8Array) { // Check for the correct TIFF header (0x4949 for little-endian or 0x4D4D for big-endian) const isLittleEndian = String.fromCharCode(...exifData.slice(0, 2)) === 'II' // Function to read 16-bit and 32-bit integers from binary data - // @ts-expect-error fixme ts strict error - function readInt(offset, isLittleEndian, length) { + function readInt( + offset: number, + isLittleEndian: boolean, + length: number + ): number | undefined { let arr = exifData.slice(offset, offset + length) if (length === 2) { return new DataView(arr.buffer, arr.byteOffset, arr.byteLength).getUint16( @@ -343,12 +345,12 @@ function parseExifData(exifData) { // Read the offset to the first IFD (Image File Directory) const ifdOffset = readInt(4, isLittleEndian, 4) - // @ts-expect-error fixme ts strict error - function parseIFD(offset) { + function parseIFD(offset: number) { const numEntries = readInt(offset, isLittleEndian, 2) - const result = {} + const result: Record = {} + + if (numEntries === undefined) return result - // @ts-expect-error fixme ts strict error for (let i = 0; i < numEntries; i++) { const entryOffset = offset + 2 + i * 12 const tag = readInt(entryOffset, isLittleEndian, 2) @@ -358,22 +360,23 @@ function parseExifData(exifData) { // Read the value(s) based on the data type let value - if (type === 2) { + if (type === 2 && valueOffset !== undefined && numValues !== undefined) { // ASCII string value = new TextDecoder('utf-8').decode( - // @ts-expect-error fixme ts strict error exifData.subarray(valueOffset, valueOffset + numValues - 1) ) } - // @ts-expect-error fixme ts strict error - result[tag] = value + if (tag !== undefined) { + result[tag] = value + } } return result } // Parse the first IFD + if (ifdOffset === undefined) return {} const ifdData = parseIFD(ifdOffset) return ifdData } diff --git a/src/scripts/metadata/flac.ts b/src/scripts/metadata/flac.ts index 5a3efa6ac6..b9c627351d 100644 --- a/src/scripts/metadata/flac.ts +++ b/src/scripts/metadata/flac.ts @@ -6,8 +6,7 @@ export function getFromFlacBuffer(buffer: ArrayBuffer): Record { const signature = String.fromCharCode(...new Uint8Array(buffer, 0, 4)) if (signature !== 'fLaC') { console.error('Not a valid FLAC file') - // @ts-expect-error fixme ts strict error - return + return {} } // Parse metadata blocks @@ -30,17 +29,18 @@ export function getFromFlacBuffer(buffer: ArrayBuffer): Record { if (isLastBlock) break } - // @ts-expect-error fixme ts strict error - return vorbisComment + return vorbisComment ?? {} } export function getFromFlacFile(file: File): Promise> { return new Promise((r) => { const reader = new FileReader() reader.onload = function (event) { - // @ts-expect-error fixme ts strict error - const arrayBuffer = event.target.result as ArrayBuffer - r(getFromFlacBuffer(arrayBuffer)) + if (event.target?.result instanceof ArrayBuffer) { + r(getFromFlacBuffer(event.target.result)) + } else { + r({}) + } } reader.readAsArrayBuffer(file) }) @@ -50,14 +50,11 @@ export function getFromFlacFile(file: File): Promise> { function parseVorbisComment(dataView: DataView): Record { let offset = 0 const vendorLength = dataView.getUint32(offset, true) - offset += 4 - // @ts-expect-error unused variable - const vendorString = getString(dataView, offset, vendorLength) - offset += vendorLength + offset += 4 + vendorLength const userCommentListLength = dataView.getUint32(offset, true) offset += 4 - const comments = {} + const comments: Record = {} for (let i = 0; i < userCommentListLength; i++) { const commentLength = dataView.getUint32(offset, true) offset += 4 @@ -67,7 +64,6 @@ function parseVorbisComment(dataView: DataView): Record { const ind = comment.indexOf('=') const key = comment.substring(0, ind) - // @ts-expect-error fixme ts strict error comments[key] = comment.substring(ind + 1) } diff --git a/src/scripts/metadata/png.ts b/src/scripts/metadata/png.ts index 2e911796a9..e87f33a915 100644 --- a/src/scripts/metadata/png.ts +++ b/src/scripts/metadata/png.ts @@ -46,8 +46,9 @@ export function getFromPngFile(file: File) { return new Promise>((r) => { const reader = new FileReader() reader.onload = (event) => { - // @ts-expect-error fixme ts strict error - r(getFromPngBuffer(event.target.result as ArrayBuffer)) + if (event.target?.result instanceof ArrayBuffer) { + r(getFromPngBuffer(event.target.result)) + } } reader.readAsArrayBuffer(file) diff --git a/src/scripts/ui.ts b/src/scripts/ui.ts index 6a428c974e..6b9795ea0c 100644 --- a/src/scripts/ui.ts +++ b/src/scripts/ui.ts @@ -94,17 +94,19 @@ export function $el( return element as ElementType } -// @ts-expect-error fixme ts strict error -function dragElement(dragEl): () => void { +function dragElement(dragEl: HTMLElement): () => void { var posDiffX = 0, posDiffY = 0, posStartX = 0, posStartY = 0, newPosX = 0, newPosY = 0 - if (dragEl.getElementsByClassName('drag-handle')[0]) { + const handle = dragEl.getElementsByClassName('drag-handle')[0] as + | HTMLElement + | undefined + if (handle) { // if present, the handle is where you move the DIV from: - dragEl.getElementsByClassName('drag-handle')[0].onmousedown = dragMouseDown + handle.onmousedown = dragMouseDown } else { // otherwise, move the DIV from anywhere inside the DIV: dragEl.onmousedown = dragMouseDown @@ -151,7 +153,6 @@ function dragElement(dragEl): () => void { dragEl.style.top = newPosY + 'px' dragEl.style.bottom = 'unset' - // @ts-expect-error fixme ts strict error if (savePos) { localStorage.setItem( 'Comfy.MenuPosition', @@ -174,13 +175,11 @@ function dragElement(dragEl): () => void { } } - // @ts-expect-error fixme ts strict error - let savePos = undefined + let savePos: boolean | undefined = undefined restorePos() savePos = true - // @ts-expect-error fixme ts strict error - function dragMouseDown(e) { + function dragMouseDown(e: MouseEvent) { e = e || window.event e.preventDefault() // get the mouse cursor position at startup: @@ -191,8 +190,7 @@ function dragElement(dragEl): () => void { document.onmousemove = elementDrag } - // @ts-expect-error fixme ts strict error - function elementDrag(e) { + function elementDrag(e: MouseEvent) { e = e || window.event e.preventDefault() @@ -230,16 +228,15 @@ function dragElement(dragEl): () => void { } class ComfyList { - #type - #text - #reverse + #type: 'queue' | 'history' + #text: string + #reverse: boolean element: HTMLDivElement button?: HTMLButtonElement - // @ts-expect-error fixme ts strict error - constructor(text, type?, reverse?) { + constructor(text: string, type?: 'queue' | 'history', reverse?: boolean) { this.#text = text - this.#type = type || text.toLowerCase() + this.#type = type || (text.toLowerCase() as 'queue' | 'history') this.#reverse = reverse || false this.element = $el('div.comfy-list') as HTMLDivElement this.element.style.display = 'none' @@ -257,46 +254,45 @@ class ComfyList { textContent: section }), $el('div.comfy-list-items', [ - // @ts-expect-error fixme ts strict error - ...(this.#reverse ? items[section].reverse() : items[section]).map( - (item: TaskItem) => { - // Allow items to specify a custom remove action (e.g. for interrupt current prompt) - const removeAction = - 'remove' in item - ? item.remove - : { - name: 'Delete', - cb: () => api.deleteItem(this.#type, item.prompt[1]) - } - return $el('div', { textContent: item.prompt[0] + ': ' }, [ - $el('button', { - textContent: 'Load', - onclick: async () => { - await app.loadGraphData( - // @ts-expect-error fixme ts strict error - item.prompt[3].extra_pnginfo.workflow, - true, - false - ) - if ('outputs' in item) { - app.nodeOutputs = {} - for (const [key, value] of Object.entries(item.outputs)) { - const realKey = item['meta']?.[key]?.display_node ?? key - app.nodeOutputs[realKey] = value - } + ...(this.#reverse + ? (items[section as keyof typeof items] as TaskItem[]).reverse() + : (items[section as keyof typeof items] as TaskItem[]) + ).map((item: TaskItem) => { + // Allow items to specify a custom remove action (e.g. for interrupt current prompt) + const removeAction = + 'remove' in item + ? item.remove + : { + name: 'Delete', + cb: () => api.deleteItem(this.#type, item.prompt[1]) + } + return $el('div', { textContent: item.prompt[0] + ': ' }, [ + $el('button', { + textContent: 'Load', + onclick: async () => { + await app.loadGraphData( + item.prompt[3].extra_pnginfo?.workflow, + true, + false + ) + if ('outputs' in item) { + app.nodeOutputs = {} + for (const [key, value] of Object.entries(item.outputs)) { + const realKey = item['meta']?.[key]?.display_node ?? key + app.nodeOutputs[realKey] = value } } - }), - $el('button', { - textContent: removeAction.name, - onclick: async () => { - await removeAction.cb() - await this.update() - } - }) - ]) - } - ) + } + }), + $el('button', { + textContent: removeAction.name, + onclick: async () => { + await removeAction.cb() + await this.update() + } + }) + ]) + }) ]) ]), $el('div.comfy-list-actions', [ @@ -320,16 +316,14 @@ class ComfyList { async show() { this.element.style.display = 'block' - // @ts-expect-error fixme ts strict error - this.button.textContent = 'Close' + if (this.button) this.button.textContent = 'Close' await this.load() } hide() { this.element.style.display = 'none' - // @ts-expect-error fixme ts strict error - this.button.textContent = 'View ' + this.#text + if (this.button) this.button.textContent = 'View ' + this.#text } toggle() { @@ -351,23 +345,15 @@ export class ComfyUI { lastQueueSize: number queue: ComfyList history: ComfyList - // @ts-expect-error fixme ts strict error - autoQueueMode: string - // @ts-expect-error fixme ts strict error - graphHasChanged: boolean - // @ts-expect-error fixme ts strict error - autoQueueEnabled: boolean - // @ts-expect-error fixme ts strict error - menuContainer: HTMLDivElement - // @ts-expect-error fixme ts strict error - queueSize: Element - // @ts-expect-error fixme ts strict error - restoreMenuPosition: () => void - // @ts-expect-error fixme ts strict error - loadFile: () => void + autoQueueMode!: string + graphHasChanged!: boolean + autoQueueEnabled!: boolean + menuContainer!: HTMLDivElement + queueSize!: Element + restoreMenuPosition!: () => void + loadFile!: () => void - // @ts-expect-error fixme ts strict error - constructor(app) { + constructor(app: ComfyApp) { this.app = app this.dialog = new ComfyDialog() this.settings = new ComfySettingsDialog(app) @@ -417,9 +403,8 @@ export class ComfyUI { } ], { - // @ts-expect-error fixme ts strict error onChange: (value) => { - this.autoQueueMode = value.item.value + this.autoQueueMode = value.item.value ?? value.item.text } } ) @@ -486,14 +471,13 @@ export class ComfyUI { $el('label', { innerHTML: 'Extra options' }, [ $el('input', { type: 'checkbox', - // @ts-expect-error fixme ts strict error - onchange: (i) => { - // @ts-expect-error fixme ts strict error - document.getElementById('extraOptions').style.display = i - .srcElement.checked - ? 'block' - : 'none' - this.batchCount = i.srcElement.checked + onchange: (e: Event) => { + const extraOptions = document.getElementById('extraOptions') + const target = e.target + if (!(target instanceof HTMLInputElement) || !extraOptions) + return + extraOptions.style.display = target.checked ? 'block' : 'none' + this.batchCount = target.checked ? Number.parseInt( ( document.getElementById( @@ -524,18 +508,15 @@ export class ComfyUI { value: this.batchCount, min: '1', style: { width: '35%', marginLeft: '0.4em' }, - // @ts-expect-error fixme ts strict error - oninput: (i) => { - this.batchCount = i.target.value - /* Even though an element with a type of range logically represents a number (since - it's used for numeric input), the value it holds is still treated as a string in HTML and - JavaScript. This behavior is consistent across all elements regardless of their type - (like text, number, or range), where the .value property is always a string. */ - ;( - document.getElementById( - 'batchCountInputRange' - ) as HTMLInputElement - ).value = this.batchCount.toString() + oninput: (e: Event) => { + if (!(e.target instanceof HTMLInputElement)) return + this.batchCount = Number.parseInt(e.target.value) || 1 + const rangeInput = document.getElementById( + 'batchCountInputRange' + ) + if (rangeInput instanceof HTMLInputElement) { + rangeInput.value = this.batchCount.toString() + } } }), $el('input', { @@ -544,15 +525,15 @@ export class ComfyUI { min: '1', max: '100', value: this.batchCount, - // @ts-expect-error fixme ts strict error - oninput: (i) => { - this.batchCount = i.srcElement.value - // Note - ;( - document.getElementById( - 'batchCountInputNumber' - ) as HTMLInputElement - ).value = i.srcElement.value + oninput: (e: Event) => { + if (!(e.target instanceof HTMLInputElement)) return + this.batchCount = Number.parseInt(e.target.value) || 1 + const numberInput = document.getElementById( + 'batchCountInputNumber' + ) + if (numberInput instanceof HTMLInputElement) { + numberInput.value = e.target.value + } } }) ]), @@ -566,8 +547,8 @@ export class ComfyUI { type: 'checkbox', checked: false, title: 'Automatically queue prompt when the queue size hits 0', - // @ts-expect-error fixme ts strict error - onchange: (e) => { + onchange: (e: Event) => { + if (!(e.target instanceof HTMLInputElement)) return this.autoQueueEnabled = e.target.checked autoQueueModeEl.style.display = this.autoQueueEnabled ? '' @@ -682,8 +663,8 @@ export class ComfyUI { this.restoreMenuPosition = dragElement(this.menuContainer) - // @ts-expect-error - this.setStatus({ exec_info: { queue_remaining: 'X' } }) + // Initialize with placeholder text before first status update + this.queueSize.textContent = 'Queue size: X' } setStatus(status: StatusWsMessageStatus | null) { diff --git a/src/scripts/ui/components/asyncDialog.ts b/src/scripts/ui/components/asyncDialog.ts index 1da1a5a4ef..d0575f82b7 100644 --- a/src/scripts/ui/components/asyncDialog.ts +++ b/src/scripts/ui/components/asyncDialog.ts @@ -2,21 +2,17 @@ import { $el } from '../../ui' import { ComfyDialog } from '../dialog' export class ComfyAsyncDialog extends ComfyDialog { - // @ts-expect-error fixme ts strict error - #resolve: (value: any) => void + #resolve!: (value: any) => void constructor(actions?: Array) { super( 'dialog.comfy-dialog.comfyui-dialog', - // @ts-expect-error fixme ts strict error actions?.map((opt) => { - if (typeof opt === 'string') { - opt = { text: opt } - } + const option = typeof opt === 'string' ? { text: opt } : opt return $el('button.comfyui-button', { type: 'button', - textContent: opt.text, - onclick: () => this.close(opt.value ?? opt.text) + textContent: option.text, + onclick: () => this.close(option.value ?? option.text) }) }) ) diff --git a/src/scripts/ui/components/button.ts b/src/scripts/ui/components/button.ts index 7a4aae6b67..c4aacc7df8 100644 --- a/src/scripts/ui/components/button.ts +++ b/src/scripts/ui/components/button.ts @@ -26,18 +26,17 @@ export class ComfyButton implements ComfyComponent { isOver = false iconElement = $el('i.mdi') contentElement = $el('span') - // @ts-expect-error fixme ts strict error - popup: ComfyPopup + popup: ComfyPopup | null = null 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 + overIcon!: string + iconSize!: number + content!: string | HTMLElement + icon!: string + tooltip!: string + classList!: ClassList + hidden!: boolean + enabled!: boolean + action!: (e: Event, btn: ComfyButton) => void constructor({ icon, @@ -70,22 +69,18 @@ export class ComfyButton implements ComfyComponent { [this.iconElement, this.contentElement] ) - // @ts-expect-error fixme ts strict error this.icon = prop( this, 'icon', icon, toggleElement(this.iconElement, { onShow: this.updateIcon }) - ) - // @ts-expect-error fixme ts strict error + )! this.overIcon = prop(this, 'overIcon', overIcon, () => { if (this.isOver) { this.updateIcon() } - }) - // @ts-expect-error fixme ts strict error - this.iconSize = prop(this, 'iconSize', iconSize, this.updateIcon) - // @ts-expect-error fixme ts strict error + })! + this.iconSize = prop(this, 'iconSize', iconSize, this.updateIcon)! this.content = prop( this, 'content', @@ -94,32 +89,30 @@ export class ComfyButton implements ComfyComponent { onShow: (el, v) => { if (typeof v === 'string') { el.textContent = v - } else { + } else if (v) { el.replaceChildren(v) } } }) - ) + )! - // @ts-expect-error fixme ts strict error this.tooltip = prop(this, 'tooltip', tooltip, (v) => { if (v) { this.element.title = v } else { this.element.removeAttribute('title') } - }) + })! if (tooltip !== undefined) { this.element.setAttribute('aria-label', tooltip) } - this.classList = prop(this, 'classList', classList, this.updateClasses) - this.hidden = prop(this, 'hidden', false, this.updateClasses) + this.classList = prop(this, 'classList', classList, this.updateClasses)! + this.hidden = prop(this, 'hidden', false, this.updateClasses)! this.enabled = prop(this, 'enabled', enabled, () => { this.updateClasses() ;(this.element as HTMLButtonElement).disabled = !this.enabled - }) - // @ts-expect-error fixme ts strict error - this.action = prop(this, 'action', action) + })! + this.action = prop(this, 'action', action)! this.element.addEventListener('click', (e) => { if (this.popup) { // we are either a touch device or triggered by click not hover @@ -130,14 +123,12 @@ export class ComfyButton implements ComfyComponent { this.action?.(e, this) }) - if (visibilitySetting?.id) { + if (visibilitySetting?.id && app?.ui?.settings) { const settingUpdated = () => { this.hidden = - // @ts-expect-error fixme ts strict error app.ui.settings.getSettingValue(visibilitySetting.id) !== visibilitySetting.showValue } - // @ts-expect-error fixme ts strict error app.ui.settings.addEventListener( visibilitySetting.id + '.change', settingUpdated @@ -170,12 +161,12 @@ export class ComfyButton implements ComfyComponent { this.popup = popup if (mode === 'hover') { - for (const el of [this.element, this.popup.element]) { + for (const el of [this.element, popup.element]) { el.addEventListener('mouseenter', () => { - this.popup.open = !!++this.#over + popup.open = !!++this.#over }) el.addEventListener('mouseleave', () => { - this.popup.open = !!--this.#over + popup.open = !!--this.#over }) } } diff --git a/src/scripts/ui/components/buttonGroup.ts b/src/scripts/ui/components/buttonGroup.ts index 06a1782f9e..7cbb4ddbe0 100644 --- a/src/scripts/ui/components/buttonGroup.ts +++ b/src/scripts/ui/components/buttonGroup.ts @@ -32,7 +32,8 @@ export class ComfyButtonGroup { } update() { - // @ts-expect-error fixme ts strict error - this.element.replaceChildren(...this.buttons.map((b) => b['element'] ?? b)) + this.element.replaceChildren( + ...this.buttons.map((b) => ('element' in b ? b.element : b)) + ) } } diff --git a/src/scripts/ui/components/popup.ts b/src/scripts/ui/components/popup.ts index 04bb38149c..d6ae2baf5f 100644 --- a/src/scripts/ui/components/popup.ts +++ b/src/scripts/ui/components/popup.ts @@ -89,8 +89,7 @@ export class ComfyPopup extends EventTarget { this.dispatchEvent(new CustomEvent('change')) } - // @ts-expect-error fixme ts strict error - #escHandler = (e) => { + #escHandler = (e: KeyboardEvent) => { if (e.key === 'Escape') { this.open = false e.preventDefault() @@ -98,9 +97,8 @@ export class ComfyPopup extends EventTarget { } } - // @ts-expect-error fixme ts strict error - #clickHandler = (e) => { - /** @type {any} */ + #clickHandler = (e: MouseEvent) => { + if (!(e.target instanceof Node)) return const target = e.target if ( !this.element.contains(target) && diff --git a/src/scripts/ui/dialog.ts b/src/scripts/ui/dialog.ts index 23d43c2bd3..2a72f9f0fa 100644 --- a/src/scripts/ui/dialog.ts +++ b/src/scripts/ui/dialog.ts @@ -4,11 +4,10 @@ export class ComfyDialog< T extends HTMLElement = HTMLElement > extends EventTarget { element: T - // @ts-expect-error fixme ts strict error - textElement: HTMLElement - #buttons: HTMLButtonElement[] | null + textElement!: HTMLElement + #buttons: HTMLElement[] | null - constructor(type = 'div', buttons = null) { + constructor(type = 'div', buttons: HTMLElement[] | null = null) { super() this.#buttons = buttons this.element = $el(type + '.comfy-modal', { parent: document.body }, [ @@ -35,11 +34,10 @@ export class ComfyDialog< this.element.style.display = 'none' } - // @ts-expect-error fixme ts strict error - show(html) { + show(html?: string | HTMLElement | HTMLElement[]) { if (typeof html === 'string') { this.textElement.innerHTML = html - } else { + } else if (html) { this.textElement.replaceChildren( ...(html instanceof Array ? html : [html]) ) diff --git a/src/scripts/ui/draggableList.ts b/src/scripts/ui/draggableList.ts index 895d153b7a..e0853486c8 100644 --- a/src/scripts/ui/draggableList.ts +++ b/src/scripts/ui/draggableList.ts @@ -40,87 +40,94 @@ styleElement.textContent = ` document.head.append(styleElement) export class DraggableList extends EventTarget { - listContainer - // @ts-expect-error fixme ts strict error - draggableItem - // @ts-expect-error fixme ts strict error - pointerStartX - // @ts-expect-error fixme ts strict error - pointerStartY - // @ts-expect-error fixme ts strict error - scrollYMax + listContainer: HTMLElement + draggableItem: HTMLElement | null = null + pointerStartX: number = 0 + pointerStartY: number = 0 + scrollYMax: number = 0 itemsGap = 0 - items = [] - itemSelector + items: HTMLElement[] = [] + itemSelector: string handleClass = 'drag-handle' - off = [] - offDrag = [] + off: (() => void)[] = [] + offDrag: (() => void)[] = [] - // @ts-expect-error fixme ts strict error - constructor(element, itemSelector) { + constructor(element: HTMLElement, itemSelector: string) { super() this.listContainer = element this.itemSelector = itemSelector if (!this.listContainer) return - // @ts-expect-error fixme ts strict error this.off.push(this.on(this.listContainer, 'mousedown', this.dragStart)) - // @ts-expect-error fixme ts strict error this.off.push(this.on(this.listContainer, 'touchstart', this.dragStart)) - // @ts-expect-error fixme ts strict error this.off.push(this.on(document, 'mouseup', this.dragEnd)) - // @ts-expect-error fixme ts strict error this.off.push(this.on(document, 'touchend', this.dragEnd)) } - getAllItems() { + getAllItems(): HTMLElement[] { if (!this.items?.length) { this.items = Array.from( - this.listContainer.querySelectorAll(this.itemSelector) + this.listContainer.querySelectorAll(this.itemSelector) ) this.items.forEach((element) => { - // @ts-expect-error fixme ts strict error element.classList.add('is-idle') }) } return this.items } - getIdleItems() { + getIdleItems(): HTMLElement[] { return this.getAllItems().filter((item) => - // @ts-expect-error fixme ts strict error item.classList.contains('is-idle') ) } - // @ts-expect-error fixme ts strict error - isItemAbove(item) { + isItemAbove(item: HTMLElement): boolean { return item.hasAttribute('data-is-above') } - // @ts-expect-error fixme ts strict error - isItemToggled(item) { + isItemToggled(item: HTMLElement): boolean { return item.hasAttribute('data-is-toggled') } - // @ts-expect-error fixme ts strict error - on(source, event, listener, options?) { - listener = listener.bind(this) - source.addEventListener(event, listener, options) - return () => source.removeEventListener(event, listener) + on( + source: Document, + event: K, + listener: (e: DocumentEventMap[K]) => void, + options?: AddEventListenerOptions + ): () => void + on( + source: HTMLElement, + event: K, + listener: (e: HTMLElementEventMap[K]) => void, + options?: AddEventListenerOptions + ): () => void + on( + source: Document | HTMLElement, + event: string, + listener: (e: Event) => void, + options?: AddEventListenerOptions + ): () => void { + const boundListener = listener.bind(this) + source.addEventListener(event, boundListener, options) + return () => source.removeEventListener(event, boundListener) } - // @ts-expect-error fixme ts strict error - dragStart(e) { - if (e.target.classList.contains(this.handleClass)) { - this.draggableItem = e.target.closest(this.itemSelector) - } + dragStart(e: MouseEvent | TouchEvent) { + const target = e.target + if (!(target instanceof HTMLElement)) return + if (!target.classList.contains(this.handleClass)) return + + this.draggableItem = target.closest(this.itemSelector) if (!this.draggableItem) return - this.pointerStartX = e.clientX || e.touches[0].clientX - this.pointerStartY = e.clientY || e.touches[0].clientY + const clientX = 'clientX' in e ? e.clientX : e.touches[0].clientX + const clientY = 'clientY' in e ? e.clientY : e.touches[0].clientY + + this.pointerStartX = clientX + this.pointerStartY = clientY this.scrollYMax = this.listContainer.scrollHeight - this.listContainer.clientHeight @@ -128,10 +135,8 @@ export class DraggableList extends EventTarget { this.initDraggableItem() this.initItemsState() - // @ts-expect-error fixme ts strict error this.offDrag.push(this.on(document, 'mousemove', this.drag)) this.offDrag.push( - // @ts-expect-error fixme ts strict error this.on(document, 'touchmove', this.drag, { passive: false }) ) @@ -139,7 +144,6 @@ export class DraggableList extends EventTarget { new CustomEvent('dragstart', { detail: { element: this.draggableItem, - // @ts-expect-error fixme ts strict error position: this.getAllItems().indexOf(this.draggableItem) } }) @@ -155,9 +159,7 @@ export class DraggableList extends EventTarget { const item1 = this.getIdleItems()[0] const item2 = this.getIdleItems()[1] - // @ts-expect-error fixme ts strict error const item1Rect = item1.getBoundingClientRect() - // @ts-expect-error fixme ts strict error const item2Rect = item2.getBoundingClientRect() this.itemsGap = Math.abs(item1Rect.bottom - item2Rect.top) @@ -165,27 +167,25 @@ export class DraggableList extends EventTarget { initItemsState() { this.getIdleItems().forEach((item, i) => { - // @ts-expect-error fixme ts strict error - if (this.getAllItems().indexOf(this.draggableItem) > i) { - // @ts-expect-error fixme ts strict error + if (this.getAllItems().indexOf(this.draggableItem!) > i) { item.dataset.isAbove = '' } }) } initDraggableItem() { + if (!this.draggableItem) return this.draggableItem.classList.remove('is-idle') this.draggableItem.classList.add('is-draggable') } - // @ts-expect-error fixme ts strict error - drag(e) { + drag(e: MouseEvent | TouchEvent) { if (!this.draggableItem) return e.preventDefault() - const clientX = e.clientX || e.touches[0].clientX - const clientY = e.clientY || e.touches[0].clientY + const clientX = 'clientX' in e ? e.clientX : e.touches[0].clientX + const clientY = 'clientY' in e ? e.clientY : e.touches[0].clientY const listRect = this.listContainer.getBoundingClientRect() @@ -207,28 +207,24 @@ export class DraggableList extends EventTarget { } updateIdleItemsStateAndPosition() { + if (!this.draggableItem) return const draggableItemRect = this.draggableItem.getBoundingClientRect() const draggableItemY = draggableItemRect.top + draggableItemRect.height / 2 // Update state this.getIdleItems().forEach((item) => { - // @ts-expect-error fixme ts strict error const itemRect = item.getBoundingClientRect() const itemY = itemRect.top + itemRect.height / 2 if (this.isItemAbove(item)) { if (draggableItemY <= itemY) { - // @ts-expect-error fixme ts strict error item.dataset.isToggled = '' } else { - // @ts-expect-error fixme ts strict error delete item.dataset.isToggled } } else { if (draggableItemY >= itemY) { - // @ts-expect-error fixme ts strict error item.dataset.isToggled = '' } else { - // @ts-expect-error fixme ts strict error delete item.dataset.isToggled } } @@ -238,10 +234,8 @@ export class DraggableList extends EventTarget { this.getIdleItems().forEach((item) => { if (this.isItemToggled(item)) { const direction = this.isItemAbove(item) ? 1 : -1 - // @ts-expect-error fixme ts strict error item.style.transform = `translateY(${direction * (draggableItemRect.height + this.itemsGap)}px)` } else { - // @ts-expect-error fixme ts strict error item.style.transform = '' } }) @@ -255,7 +249,8 @@ export class DraggableList extends EventTarget { } applyNewItemsOrder() { - const reorderedItems = [] + if (!this.draggableItem) return + const reorderedItems: HTMLElement[] = [] let oldPosition = -1 this.getAllItems().forEach((item, index) => { @@ -282,7 +277,6 @@ export class DraggableList extends EventTarget { this.listContainer.appendChild(item) }) - // @ts-expect-error fixme ts strict error this.items = reorderedItems this.dispatchEvent( @@ -302,13 +296,13 @@ export class DraggableList extends EventTarget { this.unsetDraggableItem() this.unsetItemState() - // @ts-expect-error fixme ts strict error this.offDrag.forEach((f) => f()) this.offDrag = [] } unsetDraggableItem() { - this.draggableItem.style = null + if (!this.draggableItem) return + this.draggableItem.style.transform = '' this.draggableItem.classList.remove('is-draggable') this.draggableItem.classList.add('is-idle') this.draggableItem = null @@ -316,17 +310,13 @@ export class DraggableList extends EventTarget { unsetItemState() { this.getIdleItems().forEach((item) => { - // @ts-expect-error fixme ts strict error delete item.dataset.isAbove - // @ts-expect-error fixme ts strict error delete item.dataset.isToggled - // @ts-expect-error fixme ts strict error item.style.transform = '' }) } dispose() { - // @ts-expect-error fixme ts strict error this.off.forEach((f) => f()) } } diff --git a/src/scripts/ui/imagePreview.ts b/src/scripts/ui/imagePreview.ts index da8516283a..e209a54701 100644 --- a/src/scripts/ui/imagePreview.ts +++ b/src/scripts/ui/imagePreview.ts @@ -3,13 +3,15 @@ import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode' import { app } from '../app' import { $el } from '../ui' +interface ImageLike { + naturalWidth: number + naturalHeight: number +} + export function calculateImageGrid( - // @ts-expect-error fixme ts strict error - imgs, - // @ts-expect-error fixme ts strict error - dw, - // @ts-expect-error fixme ts strict error - dh + imgs: ImageLike[], + dw: number, + dh: number ): { cellWidth: number cellHeight: number @@ -22,7 +24,11 @@ export function calculateImageGrid( let h = imgs[0].naturalHeight const numImages = imgs.length - let cellWidth, cellHeight, cols, rows, shiftX + let cellWidth = 0 + let cellHeight = 0 + let cols = 1 + let rows = 1 + let shiftX = 0 // compact style for (let c = 1; c <= numImages; c++) { const r = Math.ceil(numImages / c) @@ -46,63 +52,47 @@ export function calculateImageGrid( } } - // @ts-expect-error fixme ts strict error return { cellWidth, cellHeight, cols, rows, shiftX } } /** @knipIgnoreUnusedButUsedByCustomNodes */ export function createImageHost(node: LGraphNode) { const el = $el('div.comfy-img-preview') - // @ts-expect-error fixme ts strict error - let currentImgs + let currentImgs: HTMLImageElement[] | null = null let first = true function updateSize() { - let w = null - let h = null + if (!currentImgs) return - // @ts-expect-error fixme ts strict error - if (currentImgs) { - let elH = el.clientHeight - if (first) { - first = false - // On first run, if we are small then grow a bit - if (elH < 190) { - elH = 190 - } - el.style.setProperty('--comfy-widget-min-height', elH.toString()) - } else { - el.style.setProperty('--comfy-widget-min-height', null) + let elH = el.clientHeight + if (first) { + first = false + // On first run, if we are small then grow a bit + if (elH < 190) { + elH = 190 } - - const nw = node.size[0] - ;({ cellWidth: w, cellHeight: h } = calculateImageGrid( - currentImgs, - nw - 20, - elH - )) - // @ts-expect-error fixme ts strict error - w += 'px' - // @ts-expect-error fixme ts strict error - h += 'px' - - // @ts-expect-error fixme ts strict error - el.style.setProperty('--comfy-img-preview-width', w) - // @ts-expect-error fixme ts strict error - el.style.setProperty('--comfy-img-preview-height', h) + el.style.setProperty('--comfy-widget-min-height', elH.toString()) + } else { + el.style.setProperty('--comfy-widget-min-height', null) } + + const nw = node.size[0] + const { cellWidth, cellHeight } = calculateImageGrid( + currentImgs, + nw - 20, + elH + ) + + el.style.setProperty('--comfy-img-preview-width', `${cellWidth}px`) + el.style.setProperty('--comfy-img-preview-height', `${cellHeight}px`) } return { el, getCurrentImage() { - // @ts-expect-error fixme ts strict error return currentImgs?.[0] }, - // @ts-expect-error fixme ts strict error - updateImages(imgs) { - // @ts-expect-error fixme ts strict error + updateImages(imgs: HTMLImageElement[]) { if (imgs !== currentImgs) { - // @ts-expect-error fixme ts strict error if (currentImgs == null) { requestAnimationFrame(() => { updateSize() @@ -126,10 +116,9 @@ export function createImageHost(node: LGraphNode) { ) el.style.pointerEvents = 'none' - if (!over) return + if (!over || !currentImgs) return // Set the overIndex so Open Image etc work - // @ts-expect-error fixme ts strict error - const idx = currentImgs.indexOf(over) + const idx = currentImgs.indexOf(over as HTMLImageElement) node.overIndex = idx } } diff --git a/src/scripts/ui/toggleSwitch.ts b/src/scripts/ui/toggleSwitch.ts index 0d16c67277..b859946e0e 100644 --- a/src/scripts/ui/toggleSwitch.ts +++ b/src/scripts/ui/toggleSwitch.ts @@ -1,42 +1,42 @@ import { $el } from '../ui' -/** - * @typedef { { text: string, value?: string, tooltip?: string } } ToggleSwitchItem - */ +interface ToggleSwitchItem { + text: string + value?: string + tooltip?: string + selected?: boolean +} + /** * Creates a toggle switch element - * @param { string } name - * @param { Array | ToggleSwitchItem } items - * @param { Object } [opts] - * @param { (e: { item: ToggleSwitchItem, prev?: ToggleSwitchItem }) => void } [opts.onChange] */ -// @ts-expect-error fixme ts strict error -export function toggleSwitch(name, items, e?) { +export function toggleSwitch( + name: string, + items: (string | ToggleSwitchItem)[], + e?: { + onChange?: (e: { item: ToggleSwitchItem; prev?: ToggleSwitchItem }) => void + } +) { const onChange = e?.onChange - // @ts-expect-error fixme ts strict error - let selectedIndex - // @ts-expect-error fixme ts strict error - let elements + let selectedIndex: number | null = null + let elements: HTMLLabelElement[] - // @ts-expect-error fixme ts strict error - function updateSelected(index) { - // @ts-expect-error fixme ts strict error + function updateSelected(index: number) { if (selectedIndex != null) { - // @ts-expect-error fixme ts strict error elements[selectedIndex].classList.remove('comfy-toggle-selected') } onChange?.({ - item: items[index], - // @ts-expect-error fixme ts strict error - prev: selectedIndex == null ? undefined : items[selectedIndex] + item: items[index] as ToggleSwitchItem, + prev: + selectedIndex == null + ? undefined + : (items[selectedIndex] as ToggleSwitchItem) }) selectedIndex = index - // @ts-expect-error fixme ts strict error elements[selectedIndex].classList.add('comfy-toggle-selected') } - // @ts-expect-error fixme ts strict error elements = items.map((item, i) => { if (typeof item === 'string') item = { text: item } if (!item.value) item.value = item.text @@ -66,7 +66,10 @@ export function toggleSwitch(name, items, e?) { const container = $el('div.comfy-toggle-switch', elements) if (selectedIndex == null) { - elements[0].children[0].checked = true + const firstInput = elements[0].children[0] + if (firstInput instanceof HTMLInputElement) { + firstInput.checked = true + } updateSelected(0) } diff --git a/src/scripts/ui/utils.ts b/src/scripts/ui/utils.ts index a3fece0b4e..040402ca64 100644 --- a/src/scripts/ui/utils.ts +++ b/src/scripts/ui/utils.ts @@ -26,21 +26,19 @@ export function applyClasses( } } -export function toggleElement( +export function toggleElement( element: HTMLElement, { onHide, onShow }: { onHide?: (el: HTMLElement) => void - // @ts-expect-error fixme ts strict error - onShow?: (el: HTMLElement, value) => void + onShow?: (el: HTMLElement, value: T) => void } = {} ) { let placeholder: HTMLElement | Comment let hidden: boolean - // @ts-expect-error fixme ts strict error - return (value) => { + return (value: T) => { if (value) { if (hidden) { hidden = false diff --git a/src/scripts/utils.ts b/src/scripts/utils.ts index 6fbdc7dcc1..ec2b1931a0 100644 --- a/src/scripts/utils.ts +++ b/src/scripts/utils.ts @@ -76,15 +76,12 @@ export function prop( name: string ) => void ): T { - // @ts-expect-error fixme ts strict error - let currentValue + let currentValue: T = defaultValue Object.defineProperty(target, name, { get() { - // @ts-expect-error fixme ts strict error return currentValue }, set(newValue) { - // @ts-expect-error fixme ts strict error const prevValue = currentValue currentValue = newValue onChanged?.(currentValue, prevValue, target, name)