fix(types): remove @ts-expect-error suppressions from src/scripts

This commit is contained in:
DrJKL
2026-01-12 09:59:36 -08:00
parent 168af5310b
commit 1b4dd57d32
19 changed files with 303 additions and 353 deletions

View File

@@ -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
}
})

View File

@@ -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
}
})

View File

@@ -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
}
})

View File

@@ -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<typeof this.rootGraph.configure>[0]
)
// Save original renderer version before scaling (it gets modified during scaling)
const originalMainGraphRenderer =

View File

@@ -198,13 +198,15 @@ abstract class BaseDOMWidgetImpl<V extends object | string>
}
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<T extends HTMLElement, V extends object | string>
}
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

View File

@@ -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<number, string | undefined> = {}
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
}

View File

@@ -6,8 +6,7 @@ export function getFromFlacBuffer(buffer: ArrayBuffer): Record<string, string> {
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<string, string> {
if (isLastBlock) break
}
// @ts-expect-error fixme ts strict error
return vorbisComment
return vorbisComment ?? {}
}
export function getFromFlacFile(file: File): Promise<Record<string, string>> {
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<Record<string, string>> {
function parseVorbisComment(dataView: DataView): Record<string, string> {
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<string, string> = {}
for (let i = 0; i < userCommentListLength; i++) {
const commentLength = dataView.getUint32(offset, true)
offset += 4
@@ -67,7 +64,6 @@ function parseVorbisComment(dataView: DataView): Record<string, string> {
const ind = comment.indexOf('=')
const key = comment.substring(0, ind)
// @ts-expect-error fixme ts strict error
comments[key] = comment.substring(ind + 1)
}

View File

@@ -46,8 +46,9 @@ export function getFromPngFile(file: File) {
return new Promise<Record<string, string>>((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)

View File

@@ -94,17 +94,19 @@ export function $el<TTag extends string>(
return element as ElementType<TTag>
}
// @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 <input> 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 <input> 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) {

View File

@@ -2,21 +2,17 @@ import { $el } from '../../ui'
import { ComfyDialog } from '../dialog'
export class ComfyAsyncDialog extends ComfyDialog<HTMLDialogElement> {
// @ts-expect-error fixme ts strict error
#resolve: (value: any) => void
#resolve!: (value: any) => void
constructor(actions?: Array<string | { value?: any; text: string }>) {
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)
})
})
)

View File

@@ -26,18 +26,17 @@ export class ComfyButton implements ComfyComponent<HTMLElement> {
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<HTMLElement> {
[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<HTMLElement> {
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<HTMLElement> {
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<HTMLElement> {
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
})
}
}

View File

@@ -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))
)
}
}

View File

@@ -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) &&

View File

@@ -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])
)

View File

@@ -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<HTMLElement>(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<K extends keyof DocumentEventMap>(
source: Document,
event: K,
listener: (e: DocumentEventMap[K]) => void,
options?: AddEventListenerOptions
): () => void
on<K extends keyof HTMLElementEventMap>(
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<HTMLElement>(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())
}
}

View File

@@ -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
}
}

View File

@@ -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<string> | 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)
}

View File

@@ -26,21 +26,19 @@ export function applyClasses(
}
}
export function toggleElement(
export function toggleElement<T = unknown>(
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

View File

@@ -76,15 +76,12 @@ export function prop<T>(
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)