Road to No Explicit Any Part 7: Scripts and Dialog Cleanup (#8092)

## Summary

Continues the TypeScript strict typing improvements by removing `any`
types from core scripts and dialog components.

### Changes

**api.ts (6 instances)**
- Define `V1RawPrompt` and `CloudRawPrompt` tuple types for queue prompt
formats
- Export `QueueIndex`, `PromptInputs`, `ExtraData`, `OutputsToExecute`
from apiSchema
- Type `#postItem` body, `storeUserData` data, and `getCustomNodesI18n`
return

**groupNodeManage.ts (all @ts-expect-error removed)**
- Add `GroupNodeConfigEntry` interface to LGraph.ts
- Extend `GroupNodeWorkflowData` with `title`, `widgets_values`, and
typed `config`
- Type all class properties with definite assignment assertions
- Type all method parameters and event handlers
- Fix save button callback with proper generic types for node ordering

**changeTracker.ts (4 instances)**
- Type `nodeOutputs` as `Record<string, ExecutedWsMessage['output']>`
- Type prompt callback with `CanvasPointerEvent` and proper value types

**asyncDialog.ts and dialog.ts**
- Make `ComfyAsyncDialog` generic with `DialogAction<T>` type
- Type `ComfyDialog` constructor and show method parameters
- Update `ManageGroupDialog.show` signature to match base class

## Test plan
- [x] `pnpm typecheck` passes
- [x] `pnpm lint` passes
- [x] Sourcegraph checks for external usage

---
Related: Continues from #8083

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8092-Road-to-No-Explicit-Any-Part-7-Scripts-and-Dialog-Cleanup-2ea6d73d365081fbb890e73646a6ad16)
by [Unito](https://www.unito.io)
This commit is contained in:
Johnpaul Chiwetelu
2026-01-20 01:41:40 +01:00
committed by GitHub
parent d5f17f7d9f
commit 7ef4ea6f25
9 changed files with 294 additions and 278 deletions

View File

@@ -368,7 +368,7 @@ export class GroupNodeConfig {
}
getNodeDef(
node: GroupNodeData
node: GroupNodeData | GroupNodeWorkflowData['nodes'][number]
): GroupNodeDef | ComfyNodeDef | null | undefined {
if (node.type) {
const def = globalDefs[node.type]
@@ -386,7 +386,8 @@ export class GroupNodeConfig {
let type: string | number | null = linksFrom[0]?.[0]?.[5] ?? null
if (type === 'COMBO') {
// Use the array items
const source = node.outputs?.[0]?.widget?.name
const output = node.outputs?.[0] as GroupNodeOutput | undefined
const source = output?.widget?.name
const nodeIdx = linksFrom[0]?.[0]?.[2]
if (source && nodeIdx != null) {
const fromTypeName = this.nodeData.nodes[Number(nodeIdx)]?.type

View File

@@ -1,9 +1,11 @@
import { PREFIX, SEPARATOR } from '@/constants/groupNodeConstants'
import {
type LGraphNode,
type LGraphNodeConstructor,
LiteGraph
import type {
GroupNodeConfigEntry,
GroupNodeWorkflowData,
LGraphNode,
LGraphNodeConstructor
} from '@/lib/litegraph/src/litegraph'
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
import { useToastStore } from '@/platform/updates/common/toastStore'
import { type ComfyApp, app } from '../../scripts/app'
@@ -15,18 +17,20 @@ import './groupNodeManage.css'
const ORDER: symbol = Symbol()
// @ts-expect-error fixme ts strict error
function merge(target, source) {
if (typeof target === 'object' && typeof source === 'object') {
for (const key in source) {
const sv = source[key]
if (typeof sv === 'object') {
let tv = target[key]
if (!tv) tv = target[key] = {}
merge(tv, source[key])
} else {
target[key] = sv
function merge(
target: Record<string, unknown>,
source: Record<string, unknown>
): Record<string, unknown> {
for (const key in source) {
const sv = source[key]
if (typeof sv === 'object' && sv !== null) {
let tv = target[key] as Record<string, unknown> | undefined
if (!tv) {
tv = target[key] = {}
}
merge(tv, sv as Record<string, unknown>)
} else {
target[key] = sv
}
}
@@ -34,8 +38,7 @@ function merge(target, source) {
}
export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
// @ts-expect-error fixme ts strict error
tabs: Record<
tabs!: Record<
'Inputs' | 'Outputs' | 'Widgets',
{ tab: HTMLAnchorElement; page: HTMLElement }
>
@@ -52,31 +55,26 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
>
>
> = {}
// @ts-expect-error fixme ts strict error
nodeItems: any[]
nodeItems!: HTMLLIElement[]
app: ComfyApp
// @ts-expect-error fixme ts strict error
groupNodeType: LGraphNodeConstructor<LGraphNode>
groupNodeDef: any
groupData: any
groupNodeType!: LGraphNodeConstructor<LGraphNode>
groupData!: GroupNodeConfig
// @ts-expect-error fixme ts strict error
innerNodesList: HTMLUListElement
// @ts-expect-error fixme ts strict error
widgetsPage: HTMLElement
// @ts-expect-error fixme ts strict error
inputsPage: HTMLElement
// @ts-expect-error fixme ts strict error
outputsPage: HTMLElement
draggable: any
innerNodesList!: HTMLUListElement
widgetsPage!: HTMLElement
inputsPage!: HTMLElement
outputsPage!: HTMLElement
draggable: DraggableList | undefined
get selectedNodeInnerIndex() {
// @ts-expect-error fixme ts strict error
return +this.nodeItems[this.selectedNodeIndex].dataset.nodeindex
get selectedNodeInnerIndex(): number {
const index = this.selectedNodeIndex
if (index == null) throw new Error('No node selected')
const item = this.nodeItems[index]
if (!item?.dataset.nodeindex) throw new Error('Invalid node item')
return +item.dataset.nodeindex
}
// @ts-expect-error fixme ts strict error
constructor(app) {
constructor(app: ComfyApp) {
super()
this.app = app
this.element = $el('dialog.comfy-group-manage', {
@@ -84,19 +82,15 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
}) as HTMLDialogElement
}
// @ts-expect-error fixme ts strict error
changeTab(tab) {
changeTab(tab: keyof ManageGroupDialog['tabs']): void {
this.tabs[this.selectedTab].tab.classList.remove('active')
this.tabs[this.selectedTab].page.classList.remove('active')
// @ts-expect-error fixme ts strict error
this.tabs[tab].tab.classList.add('active')
// @ts-expect-error fixme ts strict error
this.tabs[tab].page.classList.add('active')
this.selectedTab = tab
}
// @ts-expect-error fixme ts strict error
changeNode(index, force?) {
changeNode(index: number, force?: boolean): void {
if (!force && this.selectedNodeIndex === index) return
if (this.selectedNodeIndex != null) {
@@ -122,43 +116,41 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
this.groupNodeType = LiteGraph.registered_node_types[
`${PREFIX}${SEPARATOR}` + this.selectedGroup
] as unknown as LGraphNodeConstructor<LGraphNode>
this.groupNodeDef = this.groupNodeType.nodeData
this.groupData = GroupNodeHandler.getGroupData(this.groupNodeType)
this.groupData = GroupNodeHandler.getGroupData(this.groupNodeType)!
}
// @ts-expect-error fixme ts strict error
changeGroup(group, reset = true) {
changeGroup(group: string, reset = true): void {
this.selectedGroup = group
this.getGroupData()
const nodes = this.groupData.nodeData.nodes
// @ts-expect-error fixme ts strict error
this.nodeItems = nodes.map((n, i) =>
$el(
'li.draggable-item',
{
dataset: {
nodeindex: n.index + ''
},
onclick: () => {
this.changeNode(i)
}
},
[
$el('span.drag-handle'),
$el(
'div',
{
textContent: n.title ?? n.type
this.nodeItems = nodes.map(
(n, i) =>
$el(
'li.draggable-item',
{
dataset: {
nodeindex: n.index + ''
},
n.title
? $el('span', {
textContent: n.type
})
: []
)
]
)
onclick: () => {
this.changeNode(i)
}
},
[
$el('span.drag-handle'),
$el(
'div',
{
textContent: n.title ?? n.type
},
n.title
? $el('span', {
textContent: n.type
})
: []
)
]
) as HTMLLIElement
)
this.innerNodesList.replaceChildren(...this.nodeItems)
@@ -167,47 +159,46 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
this.selectedNodeIndex = null
this.changeNode(0)
} else {
const items = this.draggable.getAllItems()
// @ts-expect-error fixme ts strict error
let index = items.findIndex((item) => item.classList.contains('selected'))
if (index === -1) index = this.selectedNodeIndex
const items = this.draggable!.getAllItems()
let index = items.findIndex((item: Element) =>
item.classList.contains('selected')
)
if (index === -1) index = this.selectedNodeIndex!
this.changeNode(index, true)
}
const ordered = [...nodes]
this.draggable?.dispose()
this.draggable = new DraggableList(this.innerNodesList, 'li')
this.draggable.addEventListener(
'dragend',
// @ts-expect-error fixme ts strict error
({ detail: { oldPosition, newPosition } }) => {
if (oldPosition === newPosition) return
ordered.splice(newPosition, 0, ordered.splice(oldPosition, 1)[0])
for (let i = 0; i < ordered.length; i++) {
this.storeModification({
nodeIndex: ordered[i].index,
section: ORDER,
prop: 'order',
value: i
})
}
this.draggable.addEventListener('dragend', (e: Event) => {
const { oldPosition, newPosition } = (e as CustomEvent).detail
if (oldPosition === newPosition) return
ordered.splice(newPosition, 0, ordered.splice(oldPosition, 1)[0])
for (let i = 0; i < ordered.length; i++) {
this.storeModification({
nodeIndex: ordered[i].index,
section: ORDER,
prop: 'order',
value: i
})
}
)
})
}
storeModification(props: {
nodeIndex?: number
section: symbol
section: string | symbol
prop: string
value: any
value: unknown
}) {
const { nodeIndex, section, prop, value } = props
// @ts-expect-error fixme ts strict error
const groupMod = (this.modifications[this.selectedGroup] ??= {})
const nodesMod = (groupMod.nodes ??= {})
const groupKey = this.selectedGroup!
const groupMod = (this.modifications[groupKey] ??= {})
const nodesMod = ((groupMod as Record<string, unknown>).nodes ??=
{}) as Record<string, Record<symbol | string, Record<string, unknown>>>
const nodeMod = (nodesMod[nodeIndex ?? this.selectedNodeInnerIndex] ??= {})
const typeMod = (nodeMod[section] ??= {})
if (typeof value === 'object') {
if (typeof value === 'object' && value !== null) {
const objMod = (typeMod[prop] ??= {})
Object.assign(objMod, value)
} else {
@@ -215,35 +206,45 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
}
}
// @ts-expect-error fixme ts strict error
getEditElement(section, prop, value, placeholder, checked, checkable = true) {
if (value === placeholder) value = ''
getEditElement(
section: string,
prop: string | number,
value: unknown,
placeholder: string,
checked: boolean,
checkable = true
): HTMLDivElement {
let displayValue = value === placeholder ? '' : value
const mods =
// @ts-expect-error fixme ts strict error
this.modifications[this.selectedGroup]?.nodes?.[
this.selectedNodeInnerIndex
]?.[section]?.[prop]
if (mods) {
if (mods.name != null) {
value = mods.name
const groupKey = this.selectedGroup!
const mods = (
this.modifications[groupKey] as Record<string, unknown> | undefined
)?.nodes as
| Record<
number,
Record<string, Record<string, { name?: string; visible?: boolean }>>
>
| undefined
const modEntry = mods?.[this.selectedNodeInnerIndex]?.[section]?.[prop]
if (modEntry) {
if (modEntry.name != null) {
displayValue = modEntry.name
}
if (mods.visible != null) {
checked = mods.visible
if (modEntry.visible != null) {
checked = modEntry.visible
}
}
return $el('div', [
$el('input', {
value,
value: displayValue as string,
placeholder,
type: 'text',
// @ts-expect-error fixme ts strict error
onchange: (e) => {
onchange: (e: Event) => {
this.storeModification({
section,
prop,
value: { name: e.target.value }
prop: String(prop),
value: { name: (e.target as HTMLInputElement).value }
})
}
}),
@@ -252,25 +253,23 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
type: 'checkbox',
checked,
disabled: !checkable,
// @ts-expect-error fixme ts strict error
onchange: (e) => {
onchange: (e: Event) => {
this.storeModification({
section,
prop,
value: { visible: !!e.target.checked }
prop: String(prop),
value: { visible: !!(e.target as HTMLInputElement).checked }
})
}
})
])
])
]) as HTMLDivElement
}
buildWidgetsPage() {
const widgets =
this.groupData.oldToNewWidgetMap[this.selectedNodeInnerIndex]
const items = Object.keys(widgets ?? {})
// @ts-expect-error fixme ts strict error
const type = app.rootGraph.extra.groupNodes[this.selectedGroup]
const type = app.rootGraph.extra.groupNodes![this.selectedGroup!]!
const config = type.config?.[this.selectedNodeInnerIndex]?.input
this.widgetsPage.replaceChildren(
...items.map((oldName) => {
@@ -289,28 +288,25 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
buildInputsPage() {
const inputs = this.groupData.nodeInputs[this.selectedNodeInnerIndex]
const items = Object.keys(inputs ?? {})
// @ts-expect-error fixme ts strict error
const type = app.rootGraph.extra.groupNodes[this.selectedGroup]
const type = app.rootGraph.extra.groupNodes![this.selectedGroup!]!
const config = type.config?.[this.selectedNodeInnerIndex]?.input
this.inputsPage.replaceChildren(
// @ts-expect-error fixme ts strict error
...items
.map((oldName) => {
let value = inputs[oldName]
if (!value) {
return
}
const elements = items
.map((oldName) => {
const value = inputs[oldName]
if (!value) {
return null
}
return this.getEditElement(
'input',
oldName,
value,
oldName,
config?.[oldName]?.visible !== false
)
})
.filter(Boolean)
)
return this.getEditElement(
'input',
oldName,
value,
oldName,
config?.[oldName]?.visible !== false
)
})
.filter((el): el is HTMLDivElement => el !== null)
this.inputsPage.replaceChildren(...elements)
return !!items.length
}
@@ -323,38 +319,35 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
const groupOutputs =
this.groupData.oldToNewOutputMap[this.selectedNodeInnerIndex]
// @ts-expect-error fixme ts strict error
const type = app.rootGraph.extra.groupNodes[this.selectedGroup]
const type = app.rootGraph.extra.groupNodes![this.selectedGroup!]!
const config = type.config?.[this.selectedNodeInnerIndex]?.output
const node = this.groupData.nodeData.nodes[this.selectedNodeInnerIndex]
const checkable = node.type !== 'PrimitiveNode'
this.outputsPage.replaceChildren(
...outputs
// @ts-expect-error fixme ts strict error
.map((type, slot) => {
const groupOutputIndex = groupOutputs?.[slot]
const oldName = innerNodeDef.output_name?.[slot] ?? type
let value = config?.[slot]?.name
const visible = config?.[slot]?.visible || groupOutputIndex != null
if (!value || value === oldName) {
value = ''
}
return this.getEditElement(
'output',
slot,
value,
oldName,
visible,
checkable
)
})
.filter(Boolean)
)
const elements = outputs.map((outputType: unknown, slot: number) => {
const groupOutputIndex = groupOutputs?.[slot]
const oldName = innerNodeDef?.output_name?.[slot] ?? String(outputType)
let value = config?.[slot]?.name
const visible = config?.[slot]?.visible || groupOutputIndex != null
if (!value || value === oldName) {
value = ''
}
return this.getEditElement(
'output',
slot,
value,
oldName,
visible,
checkable
)
})
this.outputsPage.replaceChildren(...elements)
return !!outputs.length
}
// @ts-expect-error fixme ts strict error
show(type?) {
override show(groupNodeType?: string | HTMLElement | HTMLElement[]): void {
// Extract string type - this method repurposes the show signature
const nodeType =
typeof groupNodeType === 'string' ? groupNodeType : undefined
const groupNodes = Object.keys(app.rootGraph.extra?.groupNodes ?? {}).sort(
(a, b) => a.localeCompare(b)
)
@@ -371,24 +364,27 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
this.outputsPage
])
this.tabs = [
type TabName = 'Inputs' | 'Widgets' | 'Outputs'
const tabEntries: [TabName, HTMLElement][] = [
['Inputs', this.inputsPage],
['Widgets', this.widgetsPage],
['Outputs', this.outputsPage]
// @ts-expect-error fixme ts strict error
].reduce((p, [name, page]: [string, HTMLElement]) => {
// @ts-expect-error fixme ts strict error
p[name] = {
tab: $el('a', {
onclick: () => {
this.changeTab(name)
},
textContent: name
}),
page
}
return p
}, {}) as any
]
this.tabs = tabEntries.reduce(
(p, [name, page]) => {
p[name] = {
tab: $el('a', {
onclick: () => {
this.changeTab(name)
},
textContent: name
}) as HTMLAnchorElement,
page
}
return p
},
{} as ManageGroupDialog['tabs']
)
const outer = $el('div.comfy-group-manage-outer', [
$el('header', [
@@ -396,15 +392,14 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
$el(
'select',
{
// @ts-expect-error fixme ts strict error
onchange: (e) => {
this.changeGroup(e.target.value)
onchange: (e: Event) => {
this.changeGroup((e.target as HTMLSelectElement).value)
}
},
groupNodes.map((g) =>
$el('option', {
textContent: g,
selected: `${PREFIX}${SEPARATOR}${g}` === type,
selected: `${PREFIX}${SEPARATOR}${g}` === nodeType,
value: g
})
)
@@ -439,8 +434,7 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
`Are you sure you want to remove the node: "${this.selectedGroup}"`
)
) {
// @ts-expect-error fixme ts strict error
delete app.rootGraph.extra.groupNodes[this.selectedGroup]
delete app.rootGraph.extra.groupNodes![this.selectedGroup!]
LiteGraph.unregisterNodeType(
`${PREFIX}${SEPARATOR}` + this.selectedGroup
)
@@ -454,97 +448,106 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
'button.comfy-btn',
{
onclick: async () => {
let nodesByType
let recreateNodes = []
const types = {}
type NodesByType = Record<string, LGraphNode[]>
let nodesByType: NodesByType | undefined
const recreateNodes: LGraphNode[] = []
const types: Record<string, GroupNodeWorkflowData> = {}
for (const g in this.modifications) {
// @ts-expect-error fixme ts strict error
const type = app.rootGraph.extra.groupNodes[g]
let config = (type.config ??= {})
const groupNodeData = app.rootGraph.extra.groupNodes![g]!
let config = (groupNodeData.config ??= {})
let nodeMods = this.modifications[g]?.nodes
type NodeMods = Record<
string,
Record<symbol | string, Record<string, unknown>>
>
let nodeMods = this.modifications[g]?.nodes as
| NodeMods
| undefined
if (nodeMods) {
const keys = Object.keys(nodeMods)
// @ts-expect-error fixme ts strict error
if (nodeMods[keys[0]][ORDER]) {
if (nodeMods[keys[0]]?.[ORDER]) {
// If any node is reordered, they will all need sequencing
const orderedNodes = []
const orderedMods = {}
const orderedConfig = {}
const orderedNodes: GroupNodeWorkflowData['nodes'] = []
const orderedMods: NodeMods = {}
const orderedConfig: Record<number, GroupNodeConfigEntry> =
{}
for (const n of keys) {
// @ts-expect-error fixme ts strict error
const order = nodeMods[n][ORDER].order
orderedNodes[order] = type.nodes[+n]
// @ts-expect-error fixme ts strict error
const order = (nodeMods[n][ORDER] as { order: number })
.order
orderedNodes[order] = groupNodeData.nodes[+n]
orderedMods[order] = nodeMods[n]
orderedNodes[order].index = order
}
// Rewrite links
for (const l of type.links) {
// @ts-expect-error l[0]/l[2] used as node index
if (l[0] != null) l[0] = type.nodes[l[0]].index
// @ts-expect-error l[0]/l[2] used as node index
if (l[2] != null) l[2] = type.nodes[l[2]].index
const nodesLen = groupNodeData.nodes.length
for (const l of groupNodeData.links) {
const srcIdx = l[0] as number
const dstIdx = l[2] as number
if (srcIdx != null && srcIdx < nodesLen)
l[0] = groupNodeData.nodes[srcIdx].index!
if (dstIdx != null && dstIdx < nodesLen)
l[2] = groupNodeData.nodes[dstIdx].index!
}
// Rewrite externals
if (type.external) {
for (const ext of type.external) {
if (ext[0] != null) {
// @ts-expect-error ext[0] used as node index
ext[0] = type.nodes[ext[0]].index
if (groupNodeData.external) {
for (const ext of groupNodeData.external) {
const extIdx = ext[0] as number
if (extIdx != null && extIdx < nodesLen) {
ext[0] = groupNodeData.nodes[extIdx].index!
}
}
}
// Rewrite modifications
for (const id of keys) {
// @ts-expect-error id used as node index
if (config[id]) {
// @ts-expect-error fixme ts strict error
orderedConfig[type.nodes[id].index] = config[id]
if (config[+id]) {
orderedConfig[groupNodeData.nodes[+id].index!] =
config[+id]
}
// @ts-expect-error id used as config key
delete config[id]
delete config[+id]
}
type.nodes = orderedNodes
groupNodeData.nodes = orderedNodes
nodeMods = orderedMods
type.config = config = orderedConfig
groupNodeData.config = config = orderedConfig
}
merge(config, nodeMods)
merge(
config as Record<string, unknown>,
nodeMods as Record<string, unknown>
)
}
// @ts-expect-error fixme ts strict error
types[g] = type
types[g] = groupNodeData
if (!nodesByType) {
nodesByType = app.rootGraph.nodes.reduce((p, n) => {
// @ts-expect-error fixme ts strict error
p[n.type] ??= []
// @ts-expect-error fixme ts strict error
p[n.type].push(n)
return p
}, {})
nodesByType = app.rootGraph.nodes.reduce<NodesByType>(
(p, n) => {
const nodeType = n.type ?? ''
p[nodeType] ??= []
p[nodeType].push(n)
return p
},
{}
)
}
// @ts-expect-error fixme ts strict error
const nodes = nodesByType[`${PREFIX}${SEPARATOR}` + g]
if (nodes) recreateNodes.push(...nodes)
const groupTypeNodes = nodesByType[`${PREFIX}${SEPARATOR}` + g]
if (groupTypeNodes) recreateNodes.push(...groupTypeNodes)
}
await GroupNodeConfig.registerFromWorkflow(types, [])
for (const node of recreateNodes) {
node.recreate()
node.recreate?.()
}
this.modifications = {}
this.app.canvas.setDirty(true, true)
this.changeGroup(this.selectedGroup, false)
this.changeGroup(this.selectedGroup!, false)
}
},
'Save'
@@ -559,8 +562,8 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
this.element.replaceChildren(outer)
this.changeGroup(
type
? (groupNodes.find((g) => `${PREFIX}${SEPARATOR}${g}` === type) ??
nodeType
? (groupNodes.find((g) => `${PREFIX}${SEPARATOR}${g}` === nodeType) ??
groupNodes[0])
: groupNodes[0]
)

View File

@@ -102,16 +102,23 @@ export interface LGraphConfig {
links_ontop?: boolean
}
export interface GroupNodeConfigEntry {
input?: Record<string, { name?: string; visible?: boolean }>
output?: Record<number, { name?: string; visible?: boolean }>
}
export interface GroupNodeWorkflowData {
external: (number | string)[][]
links: SerialisedLLinkArray[]
nodes: {
index?: number
type?: string
title?: string
inputs?: unknown[]
outputs?: unknown[]
widgets_values?: unknown[]
}[]
config?: Record<number, unknown>
config?: Record<number, GroupNodeConfigEntry>
}
export interface LGraphExtra extends Dictionary<unknown> {

View File

@@ -104,6 +104,8 @@ export type {
} from './interfaces'
export {
LGraph,
type GroupNodeConfigEntry,
type GroupNodeWorkflowData,
type LGraphTriggerAction,
type LGraphTriggerParam
} from './LGraph'

View File

@@ -13,6 +13,9 @@ export type PromptId = z.infer<typeof zPromptId>
export const resultItemType = z.enum(['input', 'output', 'temp'])
export type ResultItemType = z.infer<typeof resultItemType>
const zCustomNodesI18n = z.record(z.string(), z.unknown())
export type CustomNodesI18n = z.infer<typeof zCustomNodesI18n>
const zResultItem = z.object({
filename: z.string().optional(),
subfolder: z.string().optional(),

View File

@@ -22,6 +22,7 @@ import type {
} from '@/platform/workflow/validation/schemas/workflowSchema'
import type {
AssetDownloadWsMessage,
CustomNodesI18n,
EmbeddingsResponse,
ExecutedWsMessage,
ExecutingWsMessage,
@@ -35,6 +36,7 @@ import type {
LogsRawResponse,
LogsWsMessage,
NotificationWsMessage,
PreviewMethod,
ProgressStateWsMessage,
ProgressTextWsMessage,
ProgressWsMessage,
@@ -44,8 +46,7 @@ import type {
StatusWsMessageStatus,
SystemStats,
User,
UserDataFullInfo,
PreviewMethod
UserDataFullInfo
} from '@/schemas/apiSchema'
import type {
JobDetail,
@@ -951,7 +952,7 @@ export class ComfyApi extends EventTarget {
* @param {*} type The endpoint to post to
* @param {*} body Optional POST data
*/
async #postItem(type: string, body: any) {
async #postItem(type: string, body?: Record<string, unknown>) {
try {
await this.fetchApi('/' + type, {
method: 'POST',
@@ -1074,7 +1075,7 @@ export class ComfyApi extends EventTarget {
*/
async storeUserData(
file: string,
data: any,
data: unknown,
options: RequestInit & {
overwrite?: boolean
stringify?: boolean
@@ -1091,7 +1092,7 @@ export class ComfyApi extends EventTarget {
`/userdata/${encodeURIComponent(file)}?overwrite=${options.overwrite}&full_info=${options.full_info}`,
{
method: 'POST',
body: options?.stringify ? JSON.stringify(data) : data,
body: options?.stringify ? JSON.stringify(data) : (data as BodyInit),
...options
}
)
@@ -1251,7 +1252,7 @@ export class ComfyApi extends EventTarget {
*
* @returns The custom nodes i18n data
*/
async getCustomNodesI18n(): Promise<Record<string, any>> {
async getCustomNodesI18n(): Promise<CustomNodesI18n> {
return (await axios.get(this.apiURL('/i18n'))).data
}

View File

@@ -2,6 +2,7 @@ import _ from 'es-toolkit/compat'
import * as jsondiffpatch from 'jsondiffpatch'
import log from 'loglevel'
import type { CanvasPointerEvent } from '@/lib/litegraph/src/litegraph'
import { LGraphCanvas, LiteGraph } from '@/lib/litegraph/src/litegraph'
import {
ComfyWorkflow,
@@ -40,7 +41,7 @@ export class ChangeTracker {
_restoringState: boolean = false
ds?: { scale: number; offset: [number, number] }
nodeOutputs?: Record<string, any>
nodeOutputs?: Record<string, ExecutedWsMessage['output']>
private subgraphState?: {
navigation: string[]
@@ -303,11 +304,11 @@ export class ChangeTracker {
const prompt = LGraphCanvas.prototype.prompt
LGraphCanvas.prototype.prompt = function (
title: string,
value: any,
callback: (v: any) => void,
event: any
value: string | number,
callback: (v: string) => void,
event: CanvasPointerEvent
) {
const extendedCallback = (v: any) => {
const extendedCallback = (v: string) => {
callback(v)
checkState()
}

View File

@@ -1,28 +1,28 @@
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
type DialogAction<T> = string | { value?: T; text: string }
constructor(actions?: Array<string | { value?: any; text: string }>) {
export class ComfyAsyncDialog<
T = string | null
> extends ComfyDialog<HTMLDialogElement> {
#resolve: (value: T | null) => void = () => {}
constructor(actions?: Array<DialogAction<T>>) {
super(
'dialog.comfy-dialog.comfyui-dialog',
// @ts-expect-error fixme ts strict error
actions?.map((opt) => {
if (typeof opt === 'string') {
opt = { text: opt }
}
const action = typeof opt === 'string' ? { text: opt } : opt
return $el('button.comfyui-button', {
type: 'button',
textContent: opt.text,
onclick: () => this.close(opt.value ?? opt.text)
})
textContent: action.text,
onclick: () => this.close((action.value ?? action.text) as T)
}) as HTMLButtonElement
})
)
}
override show(html: string | HTMLElement | HTMLElement[]) {
override show(html: string | HTMLElement | HTMLElement[]): Promise<T | null> {
this.element.addEventListener('close', () => {
this.close()
})
@@ -34,7 +34,7 @@ export class ComfyAsyncDialog extends ComfyDialog<HTMLDialogElement> {
})
}
showModal(html: string | HTMLElement | HTMLElement[]) {
showModal(html: string | HTMLElement | HTMLElement[]): Promise<T | null> {
this.element.addEventListener('close', () => {
this.close()
})
@@ -47,22 +47,22 @@ export class ComfyAsyncDialog extends ComfyDialog<HTMLDialogElement> {
})
}
override close(result = null) {
override close(result: T | null = null) {
this.#resolve(result)
this.element.close()
super.close()
}
static async prompt({
static async prompt<U = string>({
title = null,
message,
actions
}: {
title: string | null
message: string
actions: Array<string | { value?: any; text: string }>
}) {
const dialog = new ComfyAsyncDialog(actions)
actions: Array<DialogAction<U>>
}): Promise<U | null> {
const dialog = new ComfyAsyncDialog<U>(actions)
const content = [$el('span', message)]
if (title) {
content.unshift($el('h3', title))

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
textElement!: HTMLElement
#buttons: HTMLButtonElement[] | null
constructor(type = 'div', buttons = null) {
constructor(type = 'div', buttons: HTMLButtonElement[] | null = null) {
super()
this.#buttons = buttons
this.element = $el(type + '.comfy-modal', { parent: document.body }, [
@@ -35,8 +34,7 @@ export class ComfyDialog<
this.element.style.display = 'none'
}
// @ts-expect-error fixme ts strict error
show(html) {
show(html: string | HTMLElement | HTMLElement[]): void {
if (typeof html === 'string') {
this.textElement.innerHTML = html
} else {