mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-01-31 13:29:55 +00:00
Chore: TypeScript cleanup - remove 254 @ts-expect-error suppressions (#7884)
## Summary Removes **254** `@ts-expect-error` suppressions through proper type fixes rather than type assertions. ## Key Changes ### Type System Improvements - Add `globalDefs` and `groupNodes` types to `ComfyAppWindowExtension` - Extract interfaces for group node handling (`GroupNodeHandler`, `InnerNodeOutput`, etc.) - Add `getHandler()` helper to consolidate GROUP symbol access pattern ### Files Fixed - **pnginfo.ts**: 39 suppressions removed via proper typing of workflow/prompt data - **app.ts**: 39 suppressions removed via interface extraction and type narrowing - **Tier 1 files**: 17 suppressions removed (maskeditor, imageDrawer, groupNode, etc.) - **groupNode.ts**: Major refactoring with proper interface organization ## Approach Following established constraints: - No `any` types - No `as unknown as T` casts (except legacy API boundaries) - Priority: Fix actual types > Type narrowing > Targeted suppressions as last resort - Prefix unused callback parameters with underscore - Extract repeated inline types into named interfaces ## Validation - ✅ `pnpm typecheck` passes - ✅ `pnpm lint` passes - ✅ `pnpm knip` passes ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7884-Chore-TypeScript-cleanup-remove-254-ts-expect-error-suppressions-2e26d73d3650812e9b48da203ce1d296) by [Unito](https://www.unito.io) --------- Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -483,23 +483,30 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Rewrite externals
|
||||
if (type.external) {
|
||||
for (const ext of type.external) {
|
||||
ext[0] = type.nodes[ext[0]]
|
||||
if (ext[0] != null) {
|
||||
// @ts-expect-error ext[0] used as node index
|
||||
ext[0] = type.nodes[ext[0]].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]
|
||||
}
|
||||
// @ts-expect-error id used as config key
|
||||
delete config[id]
|
||||
}
|
||||
|
||||
@@ -529,7 +536,7 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
|
||||
if (nodes) recreateNodes.push(...nodes)
|
||||
}
|
||||
|
||||
await GroupNodeConfig.registerFromWorkflow(types, {})
|
||||
await GroupNodeConfig.registerFromWorkflow(types, [])
|
||||
|
||||
for (const node of recreateNodes) {
|
||||
node.recreate()
|
||||
|
||||
@@ -370,9 +370,10 @@ const ext: ComfyExtension = {
|
||||
const node = app.canvas.graph?.getNodeById(nodeIds[i])
|
||||
const nodeData = node?.constructor.nodeData
|
||||
|
||||
let groupData = GroupNodeHandler.getGroupData(node)
|
||||
if (groupData) {
|
||||
groupData = groupData.nodeData
|
||||
if (!node) continue
|
||||
const groupConfig = GroupNodeHandler.getGroupData(node)
|
||||
if (groupConfig) {
|
||||
const groupData = groupConfig.nodeData
|
||||
// @ts-expect-error
|
||||
if (!data.groupNodes) {
|
||||
// @ts-expect-error
|
||||
@@ -402,7 +403,10 @@ const ext: ComfyExtension = {
|
||||
callback: () => {
|
||||
clipboardAction(async () => {
|
||||
const data = JSON.parse(t.data)
|
||||
await GroupNodeConfig.registerFromWorkflow(data.groupNodes, {})
|
||||
await GroupNodeConfig.registerFromWorkflow(
|
||||
data.groupNodes ?? {},
|
||||
[]
|
||||
)
|
||||
|
||||
// Check for old clipboard format
|
||||
if (!data.reroutes) {
|
||||
|
||||
@@ -15,7 +15,7 @@ import { LGraphGroup } from './LGraphGroup'
|
||||
import { LGraphNode } from './LGraphNode'
|
||||
import type { NodeId } from './LGraphNode'
|
||||
import { LLink } from './LLink'
|
||||
import type { LinkId } from './LLink'
|
||||
import type { LinkId, SerialisedLLinkArray } from './LLink'
|
||||
import { MapProxyHandler } from './MapProxyHandler'
|
||||
import { Reroute } from './Reroute'
|
||||
import type { RerouteId } from './Reroute'
|
||||
@@ -102,11 +102,24 @@ export interface LGraphConfig {
|
||||
links_ontop?: any
|
||||
}
|
||||
|
||||
export interface GroupNodeWorkflowData {
|
||||
external: (number | string)[][]
|
||||
links: SerialisedLLinkArray[]
|
||||
nodes: {
|
||||
index?: number
|
||||
type?: string
|
||||
inputs?: unknown[]
|
||||
outputs?: unknown[]
|
||||
}[]
|
||||
config?: Record<number, unknown>
|
||||
}
|
||||
|
||||
export interface LGraphExtra extends Dictionary<unknown> {
|
||||
reroutes?: SerialisableReroute[]
|
||||
linkExtensions?: { id: number; parentId: number | undefined }[]
|
||||
ds?: DragAndScaleState
|
||||
workflowRendererVersion?: RendererType
|
||||
groupNodes?: Record<string, GroupNodeWorkflowData>
|
||||
}
|
||||
|
||||
export interface BaseLGraph {
|
||||
|
||||
@@ -452,7 +452,6 @@ const zSubgraphDefinition = zComfyWorkflow1
|
||||
.passthrough()
|
||||
|
||||
export type ModelFile = z.infer<typeof zModelFile>
|
||||
export type ComfyLink = z.infer<typeof zComfyLink>
|
||||
export type ComfyLinkObject = z.infer<typeof zComfyLinkObject>
|
||||
export type ComfyNode = z.infer<typeof zComfyNode>
|
||||
export type Reroute = z.infer<typeof zReroute>
|
||||
|
||||
@@ -34,6 +34,7 @@ import {
|
||||
import type {
|
||||
ExecutionErrorWsMessage,
|
||||
NodeError,
|
||||
NodeExecutionOutput,
|
||||
ResultItem
|
||||
} from '@/schemas/apiSchema'
|
||||
import {
|
||||
@@ -153,10 +154,8 @@ export class ComfyApp {
|
||||
vueAppReady: boolean
|
||||
api: ComfyApi
|
||||
ui: ComfyUI
|
||||
// @ts-expect-error fixme ts strict error
|
||||
extensionManager: ExtensionManager
|
||||
// @ts-expect-error fixme ts strict error
|
||||
_nodeOutputs: Record<string, any>
|
||||
extensionManager!: ExtensionManager
|
||||
private _nodeOutputs!: Record<string, NodeExecutionOutput>
|
||||
nodePreviewImages: Record<string, string[]>
|
||||
|
||||
private rootGraphInternal: LGraph | undefined
|
||||
@@ -174,8 +173,7 @@ export class ComfyApp {
|
||||
return this.rootGraphInternal!
|
||||
}
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
canvas: LGraphCanvas
|
||||
canvas!: LGraphCanvas
|
||||
dragOverNode: LGraphNode | null = null
|
||||
readonly canvasElRef = shallowRef<HTMLCanvasElement>()
|
||||
get canvasEl() {
|
||||
@@ -187,8 +185,7 @@ export class ComfyApp {
|
||||
get configuringGraph() {
|
||||
return this.configuringGraphLevel > 0
|
||||
}
|
||||
// @ts-expect-error fixme ts strict error
|
||||
ctx: CanvasRenderingContext2D
|
||||
ctx!: CanvasRenderingContext2D
|
||||
bodyTop: HTMLElement
|
||||
bodyLeft: HTMLElement
|
||||
bodyRight: HTMLElement
|
||||
@@ -491,18 +488,17 @@ export class ComfyApp {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ComfyApp.clipspace.widgets) {
|
||||
if (ComfyApp.clipspace.widgets && node.widgets) {
|
||||
ComfyApp.clipspace.widgets.forEach(({ type, name, value }) => {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
const prop = Object.values(node.widgets).find(
|
||||
const prop = node.widgets?.find(
|
||||
(obj) => obj.type === type && obj.name === name
|
||||
)
|
||||
if (prop && prop.type != 'button') {
|
||||
const valueObj = value as Record<string, unknown> | undefined
|
||||
if (
|
||||
prop.type != 'image' &&
|
||||
typeof prop.value == 'string' &&
|
||||
// @ts-expect-error Custom widget value
|
||||
value.filename
|
||||
valueObj?.filename
|
||||
) {
|
||||
const resultItem = value as ResultItem
|
||||
prop.value =
|
||||
@@ -752,16 +748,11 @@ export class ComfyApp {
|
||||
* Set up the app on the page
|
||||
*/
|
||||
async setup(canvasEl: HTMLCanvasElement) {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
this.bodyTop = document.getElementById('comfyui-body-top')
|
||||
// @ts-expect-error fixme ts strict error
|
||||
this.bodyLeft = document.getElementById('comfyui-body-left')
|
||||
// @ts-expect-error fixme ts strict error
|
||||
this.bodyRight = document.getElementById('comfyui-body-right')
|
||||
// @ts-expect-error fixme ts strict error
|
||||
this.bodyBottom = document.getElementById('comfyui-body-bottom')
|
||||
// @ts-expect-error fixme ts strict error
|
||||
this.canvasContainer = document.getElementById('graph-canvas-container')
|
||||
this.bodyTop = document.getElementById('comfyui-body-top')!
|
||||
this.bodyLeft = document.getElementById('comfyui-body-left')!
|
||||
this.bodyRight = document.getElementById('comfyui-body-right')!
|
||||
this.bodyBottom = document.getElementById('comfyui-body-bottom')!
|
||||
this.canvasContainer = document.getElementById('graph-canvas-container')!
|
||||
|
||||
this.canvasElRef.value = canvasEl
|
||||
|
||||
@@ -798,8 +789,7 @@ export class ComfyApp {
|
||||
// Make canvas states reactive so we can observe changes on them.
|
||||
this.canvas.state = reactive(this.canvas.state)
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
this.ctx = canvasEl.getContext('2d')
|
||||
this.ctx = canvasEl.getContext('2d')!
|
||||
|
||||
LiteGraph.alt_drag_do_clone_nodes = true
|
||||
LiteGraph.macGesturesRequireMac = false
|
||||
@@ -887,8 +877,7 @@ export class ComfyApp {
|
||||
const { width, height } = canvas.getBoundingClientRect()
|
||||
canvas.width = Math.round(width * scale)
|
||||
canvas.height = Math.round(height * scale)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
canvas.getContext('2d').scale(scale, scale)
|
||||
canvas.getContext('2d')?.scale(scale, scale)
|
||||
this.canvas?.draw(true, true)
|
||||
}
|
||||
|
||||
@@ -981,16 +970,15 @@ export class ComfyApp {
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
loadTemplateData(templateData) {
|
||||
loadTemplateData(templateData: {
|
||||
templates?: { name?: string; data?: string }[]
|
||||
}): void {
|
||||
if (!templateData?.templates) {
|
||||
return
|
||||
}
|
||||
|
||||
const old = localStorage.getItem('litegrapheditor_clipboard')
|
||||
|
||||
var maxY, nodeBottom, node
|
||||
|
||||
for (const template of templateData.templates) {
|
||||
if (!template?.data) {
|
||||
continue
|
||||
@@ -1006,26 +994,24 @@ export class ComfyApp {
|
||||
}
|
||||
|
||||
// Move mouse position down to paste the next template below
|
||||
|
||||
maxY = false
|
||||
let maxY: number | undefined
|
||||
|
||||
for (const i in app.canvas.selected_nodes) {
|
||||
node = app.canvas.selected_nodes[i]
|
||||
|
||||
nodeBottom = node.pos[1] + node.size[1]
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
if (maxY === false || nodeBottom > maxY) {
|
||||
const node = app.canvas.selected_nodes[i]
|
||||
const nodeBottom = node.pos[1] + node.size[1]
|
||||
if (maxY === undefined || nodeBottom > maxY) {
|
||||
maxY = nodeBottom
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
app.canvas.graph_mouse[1] = maxY + 50
|
||||
if (maxY !== undefined) {
|
||||
app.canvas.graph_mouse[1] = maxY + 50
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
localStorage.setItem('litegrapheditor_clipboard', old)
|
||||
if (old !== null) {
|
||||
localStorage.setItem('litegrapheditor_clipboard', old)
|
||||
}
|
||||
}
|
||||
|
||||
private showMissingNodesError(missingNodeTypes: MissingNodeType[]) {
|
||||
@@ -1034,8 +1020,10 @@ export class ComfyApp {
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
private showMissingModelsError(missingModels, paths) {
|
||||
private showMissingModelsError(
|
||||
missingModels: ModelFile[],
|
||||
paths: Record<string, string[]>
|
||||
): void {
|
||||
if (useSettingStore().get('Comfy.Workflow.ShowMissingModelsWarning')) {
|
||||
useDialogService().showMissingModelsWarning({
|
||||
missingModels,
|
||||
@@ -1191,8 +1179,9 @@ export class ComfyApp {
|
||||
await modelStore.loadModelFolders()
|
||||
for (const m of uniqueModels) {
|
||||
const modelFolder = await modelStore.getLoadedModelFolder(m.directory)
|
||||
// @ts-expect-error
|
||||
if (!modelFolder) m.directory_invalid = true
|
||||
if (!modelFolder)
|
||||
(m as ModelFile & { directory_invalid?: boolean }).directory_invalid =
|
||||
true
|
||||
|
||||
const modelsAvailable = modelFolder?.models
|
||||
const modelExists =
|
||||
@@ -1288,14 +1277,15 @@ export class ComfyApp {
|
||||
}
|
||||
if (reset_invalid_values) {
|
||||
if (widget.type == 'combo') {
|
||||
const values = widget.options.values as
|
||||
| (string | number | boolean)[]
|
||||
| undefined
|
||||
if (
|
||||
// @ts-expect-error fixme ts strict error
|
||||
!widget.options.values.includes(widget.value as string) &&
|
||||
// @ts-expect-error fixme ts strict error
|
||||
widget.options.values.length > 0
|
||||
values &&
|
||||
values.length > 0 &&
|
||||
!values.includes(widget.value as string | number | boolean)
|
||||
) {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
widget.value = widget.options.values[0]
|
||||
widget.value = values[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1455,8 +1445,14 @@ export class ComfyApp {
|
||||
|
||||
const { workflow, prompt, parameters, templates } = workflowData
|
||||
|
||||
if (templates) {
|
||||
this.loadTemplateData({ templates })
|
||||
if (
|
||||
templates &&
|
||||
typeof templates === 'object' &&
|
||||
Array.isArray(templates)
|
||||
) {
|
||||
this.loadTemplateData({
|
||||
templates: templates as { name?: string; data?: string }[]
|
||||
})
|
||||
}
|
||||
|
||||
// Check workflow first - it should take priority over parameters
|
||||
@@ -1505,11 +1501,9 @@ export class ComfyApp {
|
||||
}
|
||||
|
||||
// Use parameters strictly as the final fallback
|
||||
if (parameters) {
|
||||
// Note: Not putting this in `importA1111` as it is mostly not used
|
||||
// by external callers, and `importA1111` has no access to `app`.
|
||||
if (parameters && typeof parameters === 'string') {
|
||||
useWorkflowService().beforeLoadNewGraph()
|
||||
importA1111(this.graph, parameters)
|
||||
importA1111(this.rootGraph, parameters)
|
||||
useWorkflowService().afterLoadNewGraph(
|
||||
fileName,
|
||||
this.rootGraph.serialize() as unknown as ComfyWorkflowJSON
|
||||
@@ -1560,35 +1554,40 @@ export class ComfyApp {
|
||||
app.rootGraph.add(node)
|
||||
}
|
||||
|
||||
//TODO: Investigate repeat of for loop. Can compress?
|
||||
for (const id of ids) {
|
||||
const processNodeInputs = (id: string) => {
|
||||
const data = apiData[id]
|
||||
const node = app.rootGraph.getNodeById(id)
|
||||
if (!node) return
|
||||
|
||||
for (const input in data.inputs ?? {}) {
|
||||
const value = data.inputs[input]
|
||||
if (value instanceof Array) {
|
||||
const [fromId, fromSlot] = value
|
||||
const fromNode = app.rootGraph.getNodeById(fromId)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
let toSlot = node.inputs?.findIndex((inp) => inp.name === input)
|
||||
if (toSlot == null || toSlot === -1) {
|
||||
if (!fromNode) continue
|
||||
|
||||
let toSlot = node.inputs?.findIndex((inp) => inp.name === input) ?? -1
|
||||
if (toSlot === -1) {
|
||||
try {
|
||||
// Target has no matching input, most likely a converted widget
|
||||
// @ts-expect-error fixme ts strict error
|
||||
const widget = node.widgets?.find((w) => w.name === input)
|
||||
// @ts-expect-error
|
||||
if (widget && node.convertWidgetToInput?.(widget)) {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
toSlot = node.inputs?.length - 1
|
||||
const convertFn = (
|
||||
node as LGraphNode & {
|
||||
convertWidgetToInput?: (w: IBaseWidget) => boolean
|
||||
}
|
||||
).convertWidgetToInput
|
||||
if (widget && convertFn?.(widget)) {
|
||||
// Re-find the target slot by name after conversion
|
||||
toSlot =
|
||||
node.inputs?.findIndex((inp) => inp.name === input) ?? -1
|
||||
}
|
||||
} catch (error) {}
|
||||
} catch (_error) {
|
||||
// Ignore conversion errors
|
||||
}
|
||||
}
|
||||
if (toSlot != null || toSlot !== -1) {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
if (toSlot !== -1) {
|
||||
fromNode.connect(fromSlot, node, toSlot)
|
||||
}
|
||||
} else {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
const widget = node.widgets?.find((w) => w.name === input)
|
||||
if (widget) {
|
||||
widget.value = value
|
||||
@@ -1597,45 +1596,10 @@ export class ComfyApp {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const id of ids) processNodeInputs(id)
|
||||
app.rootGraph.arrange()
|
||||
|
||||
for (const id of ids) {
|
||||
const data = apiData[id]
|
||||
const node = app.rootGraph.getNodeById(id)
|
||||
for (const input in data.inputs ?? {}) {
|
||||
const value = data.inputs[input]
|
||||
if (value instanceof Array) {
|
||||
const [fromId, fromSlot] = value
|
||||
const fromNode = app.rootGraph.getNodeById(fromId)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
let toSlot = node.inputs?.findIndex((inp) => inp.name === input)
|
||||
if (toSlot == null || toSlot === -1) {
|
||||
try {
|
||||
// Target has no matching input, most likely a converted widget
|
||||
// @ts-expect-error fixme ts strict error
|
||||
const widget = node.widgets?.find((w) => w.name === input)
|
||||
// @ts-expect-error
|
||||
if (widget && node.convertWidgetToInput?.(widget)) {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
toSlot = node.inputs?.length - 1
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
if (toSlot != null || toSlot !== -1) {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
fromNode.connect(fromSlot, node, toSlot)
|
||||
}
|
||||
} else {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
const widget = node.widgets?.find((w) => w.name === input)
|
||||
if (widget) {
|
||||
widget.value = value
|
||||
widget.callback?.(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const id of ids) processNodeInputs(id)
|
||||
app.rootGraph.arrange()
|
||||
|
||||
useWorkflowService().afterLoadNewGraph(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
|
||||
import { LGraph, LGraphNode, LiteGraph } from '@/lib/litegraph/src/litegraph'
|
||||
import type { IBaseWidget } from '@/lib/litegraph/src/types/widgets'
|
||||
|
||||
import { api } from './api'
|
||||
import { getFromAvifFile } from './metadata/avif'
|
||||
@@ -18,14 +19,16 @@ export function getAvifMetadata(file: File): Promise<Record<string, string>> {
|
||||
return getFromAvifFile(file)
|
||||
}
|
||||
|
||||
// @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: 2 | 4
|
||||
): number {
|
||||
let arr = exifData.slice(offset, offset + length)
|
||||
if (length === 2) {
|
||||
return new DataView(arr.buffer, arr.byteOffset, arr.byteLength).getUint16(
|
||||
@@ -38,17 +41,16 @@ function parseExifData(exifData) {
|
||||
isLittleEndian
|
||||
)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// 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): Record<number, string | undefined> {
|
||||
const numEntries = readInt(offset, isLittleEndian, 2)
|
||||
const result = {}
|
||||
const result: Record<number, string | undefined> = {}
|
||||
|
||||
// @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)
|
||||
@@ -61,12 +63,10 @@ function parseExifData(exifData) {
|
||||
if (type === 2) {
|
||||
// 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
|
||||
}
|
||||
|
||||
@@ -78,13 +78,11 @@ function parseExifData(exifData) {
|
||||
return ifdData
|
||||
}
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
export function getWebpMetadata(file) {
|
||||
export function getWebpMetadata(file: File) {
|
||||
return new Promise<Record<string, string>>((r) => {
|
||||
const reader = new FileReader()
|
||||
reader.onload = (event) => {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
const webp = new Uint8Array(event.target.result as ArrayBuffer)
|
||||
const webp = new Uint8Array(event.target?.result as ArrayBuffer)
|
||||
const dataView = new DataView(webp.buffer)
|
||||
|
||||
// Check that the WEBP signature is present
|
||||
@@ -99,7 +97,7 @@ export function getWebpMetadata(file) {
|
||||
|
||||
// Start searching for chunks after the WEBP signature
|
||||
let offset = 12
|
||||
let txt_chunks = {}
|
||||
const txt_chunks: Record<string, string> = {}
|
||||
// Loop through the chunks in the WEBP file
|
||||
while (offset < webp.length) {
|
||||
const chunk_length = dataView.getUint32(offset + 4, true)
|
||||
@@ -116,12 +114,10 @@ export function getWebpMetadata(file) {
|
||||
let data = parseExifData(
|
||||
webp.slice(offset + 8, offset + 8 + chunk_length)
|
||||
)
|
||||
for (var key in data) {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
const value = data[key] as string
|
||||
for (const key in data) {
|
||||
const value = data[Number(key)]
|
||||
if (typeof value === 'string') {
|
||||
const index = value.indexOf(':')
|
||||
// @ts-expect-error fixme ts strict error
|
||||
txt_chunks[value.slice(0, index)] = value.slice(index + 1)
|
||||
}
|
||||
}
|
||||
@@ -161,26 +157,42 @@ export function getLatentMetadata(file: File): Promise<Record<string, string>> {
|
||||
})
|
||||
}
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
export async function importA1111(graph, parameters) {
|
||||
interface NodeConnection {
|
||||
node: LGraphNode
|
||||
index: number
|
||||
}
|
||||
|
||||
interface LoraEntry {
|
||||
name: string
|
||||
weight: number
|
||||
}
|
||||
|
||||
export async function importA1111(
|
||||
graph: LGraph,
|
||||
parameters: string
|
||||
): Promise<void> {
|
||||
const p = parameters.lastIndexOf('\nSteps:')
|
||||
if (p > -1) {
|
||||
const embeddings = await api.getEmbeddings()
|
||||
const opts = parameters
|
||||
const matchResult = parameters
|
||||
.substr(p)
|
||||
.split('\n')[1]
|
||||
.match(
|
||||
new RegExp('\\s*([^:]+:\\s*([^"\\{].*?|".*?"|\\{.*?\\}))\\s*(,|$)', 'g')
|
||||
)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
.reduce((p, n) => {
|
||||
if (!matchResult) return
|
||||
|
||||
const opts: Record<string, string> = matchResult.reduce(
|
||||
(acc: Record<string, string>, n: string) => {
|
||||
const s = n.split(':')
|
||||
if (s[1].endsWith(',')) {
|
||||
s[1] = s[1].substr(0, s[1].length - 1)
|
||||
}
|
||||
p[s[0].trim().toLowerCase()] = s[1].trim()
|
||||
return p
|
||||
}, {})
|
||||
acc[s[0].trim().toLowerCase()] = s[1].trim()
|
||||
return acc
|
||||
},
|
||||
{}
|
||||
)
|
||||
const p2 = parameters.lastIndexOf('\nNegative prompt:', p)
|
||||
if (p2 > -1) {
|
||||
let positive = parameters.substr(0, p2).trim()
|
||||
@@ -194,25 +206,45 @@ export async function importA1111(graph, parameters) {
|
||||
const imageNode = LiteGraph.createNode('EmptyLatentImage')
|
||||
const vaeNode = LiteGraph.createNode('VAEDecode')
|
||||
const saveNode = LiteGraph.createNode('SaveImage')
|
||||
// @ts-expect-error fixme ts strict error
|
||||
let hrSamplerNode = null
|
||||
let hrSteps = null
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
const ceil64 = (v) => Math.ceil(v / 64) * 64
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
const getWidget = (node, name) => {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
return node.widgets.find((w) => w.name === name)
|
||||
if (
|
||||
!ckptNode ||
|
||||
!clipSkipNode ||
|
||||
!positiveNode ||
|
||||
!negativeNode ||
|
||||
!samplerNode ||
|
||||
!imageNode ||
|
||||
!vaeNode ||
|
||||
!saveNode
|
||||
) {
|
||||
console.error('Failed to create required nodes for A1111 import')
|
||||
return
|
||||
}
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
const setWidgetValue = (node, name, value, isOptionPrefix?) => {
|
||||
let hrSamplerNode: LGraphNode | null = null
|
||||
let hrSteps: string | null = null
|
||||
|
||||
const ceil64 = (v: number) => Math.ceil(v / 64) * 64
|
||||
|
||||
function getWidget(
|
||||
node: LGraphNode | null,
|
||||
name: string
|
||||
): IBaseWidget | undefined {
|
||||
return node?.widgets?.find((w) => w.name === name)
|
||||
}
|
||||
|
||||
function setWidgetValue(
|
||||
node: LGraphNode | null,
|
||||
name: string,
|
||||
value: string | number,
|
||||
isOptionPrefix?: boolean
|
||||
): void {
|
||||
const w = getWidget(node, name)
|
||||
if (!w) return
|
||||
|
||||
if (isOptionPrefix) {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
const o = w.options.values.find((w) => w.startsWith(value))
|
||||
const values = w.options.values as string[] | undefined
|
||||
const o = values?.find((v) => v.startsWith(String(value)))
|
||||
if (o) {
|
||||
w.value = o
|
||||
} else {
|
||||
@@ -224,25 +256,28 @@ export async function importA1111(graph, parameters) {
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
const createLoraNodes = (clipNode, text, prevClip, prevModel) => {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
const loras = []
|
||||
// @ts-expect-error fixme ts strict error
|
||||
text = text.replace(/<lora:([^:]+:[^>]+)>/g, function (m, c) {
|
||||
function createLoraNodes(
|
||||
clipNode: LGraphNode,
|
||||
text: string,
|
||||
prevClip: NodeConnection,
|
||||
prevModel: NodeConnection,
|
||||
targetSamplerNode: LGraphNode
|
||||
): { text: string; prevModel: NodeConnection; prevClip: NodeConnection } {
|
||||
const loras: LoraEntry[] = []
|
||||
text = text.replace(/<lora:([^:]+:[^>]+)>/g, (_m, c: string) => {
|
||||
const s = c.split(':')
|
||||
const weight = parseFloat(s[1])
|
||||
if (isNaN(weight)) {
|
||||
console.warn('Invalid LORA', m)
|
||||
console.warn('Invalid LORA', _m)
|
||||
} else {
|
||||
loras.push({ name: s[0], weight })
|
||||
}
|
||||
return ''
|
||||
})
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
for (const l of loras) {
|
||||
const loraNode = LiteGraph.createNode('LoraLoader')
|
||||
if (!loraNode) continue
|
||||
graph.add(loraNode)
|
||||
setWidgetValue(loraNode, 'lora_name', l.name, true)
|
||||
setWidgetValue(loraNode, 'strength_model', l.weight)
|
||||
@@ -254,8 +289,7 @@ export async function importA1111(graph, parameters) {
|
||||
}
|
||||
|
||||
prevClip.node.connect(1, clipNode, 0)
|
||||
prevModel.node.connect(0, samplerNode, 0)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
prevModel.node.connect(0, targetSamplerNode, 0)
|
||||
if (hrSamplerNode) {
|
||||
prevModel.node.connect(0, hrSamplerNode, 0)
|
||||
}
|
||||
@@ -263,8 +297,7 @@ export async function importA1111(graph, parameters) {
|
||||
return { text, prevModel, prevClip }
|
||||
}
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
const replaceEmbeddings = (text) => {
|
||||
function replaceEmbeddings(text: string): string {
|
||||
if (!embeddings.length) return text
|
||||
return text.replaceAll(
|
||||
new RegExp(
|
||||
@@ -279,8 +312,7 @@ export async function importA1111(graph, parameters) {
|
||||
)
|
||||
}
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
const popOpt = (name) => {
|
||||
function popOpt(name: string): string | undefined {
|
||||
const v = opts[name]
|
||||
delete opts[name]
|
||||
return v
|
||||
@@ -296,43 +328,29 @@ export async function importA1111(graph, parameters) {
|
||||
graph.add(vaeNode)
|
||||
graph.add(saveNode)
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
ckptNode.connect(1, clipSkipNode, 0)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
clipSkipNode.connect(0, positiveNode, 0)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
clipSkipNode.connect(0, negativeNode, 0)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
ckptNode.connect(0, samplerNode, 0)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
positiveNode.connect(0, samplerNode, 1)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
negativeNode.connect(0, samplerNode, 2)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
imageNode.connect(0, samplerNode, 3)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
vaeNode.connect(0, saveNode, 0)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
samplerNode.connect(0, vaeNode, 0)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
ckptNode.connect(2, vaeNode, 1)
|
||||
|
||||
const handlers = {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
model(v) {
|
||||
const handlers: Record<string, (v: string) => void> = {
|
||||
model(v: string) {
|
||||
setWidgetValue(ckptNode, 'ckpt_name', v, true)
|
||||
},
|
||||
vae() {},
|
||||
// @ts-expect-error fixme ts strict error
|
||||
'cfg scale'(v) {
|
||||
'cfg scale'(v: string) {
|
||||
setWidgetValue(samplerNode, 'cfg', +v)
|
||||
},
|
||||
// @ts-expect-error fixme ts strict error
|
||||
'clip skip'(v) {
|
||||
setWidgetValue(clipSkipNode, 'stop_at_clip_layer', -v)
|
||||
'clip skip'(v: string) {
|
||||
setWidgetValue(clipSkipNode, 'stop_at_clip_layer', -Number(v))
|
||||
},
|
||||
// @ts-expect-error fixme ts strict error
|
||||
sampler(v) {
|
||||
sampler(v: string) {
|
||||
let name = v.toLowerCase().replace('++', 'pp').replaceAll(' ', '_')
|
||||
if (name.includes('karras')) {
|
||||
name = name.replace('karras', '').replace(/_+$/, '')
|
||||
@@ -341,45 +359,44 @@ export async function importA1111(graph, parameters) {
|
||||
setWidgetValue(samplerNode, 'scheduler', 'normal')
|
||||
}
|
||||
const w = getWidget(samplerNode, 'sampler_name')
|
||||
const o = w.options.values.find(
|
||||
// @ts-expect-error fixme ts strict error
|
||||
(w) => w === name || w === 'sample_' + name
|
||||
)
|
||||
const values = w?.options.values as string[] | undefined
|
||||
const o = values?.find((v) => v === name || v === 'sample_' + name)
|
||||
if (o) {
|
||||
setWidgetValue(samplerNode, 'sampler_name', o)
|
||||
}
|
||||
},
|
||||
// @ts-expect-error fixme ts strict error
|
||||
size(v) {
|
||||
size(v: string) {
|
||||
const wxh = v.split('x')
|
||||
const w = ceil64(+wxh[0])
|
||||
const h = ceil64(+wxh[1])
|
||||
const hrUp = popOpt('hires upscale')
|
||||
const hrSz = popOpt('hires resize')
|
||||
hrSteps = popOpt('hires steps')
|
||||
hrSteps = popOpt('hires steps') ?? null
|
||||
let hrMethod = popOpt('hires upscaler')
|
||||
|
||||
setWidgetValue(imageNode, 'width', w)
|
||||
setWidgetValue(imageNode, 'height', h)
|
||||
|
||||
if (hrUp || hrSz) {
|
||||
let uw, uh
|
||||
let uw: number, uh: number
|
||||
if (hrUp) {
|
||||
uw = w * hrUp
|
||||
uh = h * hrUp
|
||||
} else {
|
||||
uw = w * Number(hrUp)
|
||||
uh = h * Number(hrUp)
|
||||
} else if (hrSz) {
|
||||
const s = hrSz.split('x')
|
||||
uw = +s[0]
|
||||
uh = +s[1]
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
let upscaleNode
|
||||
let latentNode
|
||||
let upscaleNode: LGraphNode | null
|
||||
let latentNode: LGraphNode | null
|
||||
|
||||
if (hrMethod.startsWith('Latent')) {
|
||||
if (hrMethod?.startsWith('Latent')) {
|
||||
latentNode = upscaleNode = LiteGraph.createNode('LatentUpscale')
|
||||
if (!upscaleNode) return
|
||||
graph.add(upscaleNode)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
samplerNode.connect(0, upscaleNode, 0)
|
||||
|
||||
switch (hrMethod) {
|
||||
@@ -390,37 +407,40 @@ export async function importA1111(graph, parameters) {
|
||||
setWidgetValue(upscaleNode, 'upscale_method', hrMethod, true)
|
||||
} else {
|
||||
const decode = LiteGraph.createNode('VAEDecodeTiled')
|
||||
if (!decode) return
|
||||
graph.add(decode)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
samplerNode.connect(0, decode, 0)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
ckptNode.connect(2, decode, 1)
|
||||
|
||||
const upscaleLoaderNode =
|
||||
LiteGraph.createNode('UpscaleModelLoader')
|
||||
if (!upscaleLoaderNode) return
|
||||
graph.add(upscaleLoaderNode)
|
||||
setWidgetValue(upscaleLoaderNode, 'model_name', hrMethod, true)
|
||||
setWidgetValue(
|
||||
upscaleLoaderNode,
|
||||
'model_name',
|
||||
hrMethod ?? '',
|
||||
true
|
||||
)
|
||||
|
||||
const modelUpscaleNode = LiteGraph.createNode(
|
||||
'ImageUpscaleWithModel'
|
||||
)
|
||||
if (!modelUpscaleNode) return
|
||||
graph.add(modelUpscaleNode)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
decode.connect(0, modelUpscaleNode, 1)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
upscaleLoaderNode.connect(0, modelUpscaleNode, 0)
|
||||
|
||||
upscaleNode = LiteGraph.createNode('ImageScale')
|
||||
if (!upscaleNode) return
|
||||
graph.add(upscaleNode)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
modelUpscaleNode.connect(0, upscaleNode, 0)
|
||||
|
||||
const vaeEncodeNode = (latentNode =
|
||||
LiteGraph.createNode('VAEEncodeTiled'))
|
||||
const vaeEncodeNode = LiteGraph.createNode('VAEEncodeTiled')
|
||||
if (!vaeEncodeNode) return
|
||||
latentNode = vaeEncodeNode
|
||||
graph.add(vaeEncodeNode)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
upscaleNode.connect(0, vaeEncodeNode, 0)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
ckptNode.connect(2, vaeEncodeNode, 1)
|
||||
}
|
||||
|
||||
@@ -428,33 +448,28 @@ export async function importA1111(graph, parameters) {
|
||||
setWidgetValue(upscaleNode, 'height', ceil64(uh))
|
||||
|
||||
hrSamplerNode = LiteGraph.createNode('KSampler')
|
||||
if (!hrSamplerNode || !latentNode) return
|
||||
graph.add(hrSamplerNode)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
ckptNode.connect(0, hrSamplerNode, 0)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
positiveNode.connect(0, hrSamplerNode, 1)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
negativeNode.connect(0, hrSamplerNode, 2)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
latentNode.connect(0, hrSamplerNode, 3)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
hrSamplerNode.connect(0, vaeNode, 0)
|
||||
}
|
||||
},
|
||||
// @ts-expect-error fixme ts strict error
|
||||
steps(v) {
|
||||
steps(v: string) {
|
||||
setWidgetValue(samplerNode, 'steps', +v)
|
||||
},
|
||||
// @ts-expect-error fixme ts strict error
|
||||
seed(v) {
|
||||
seed(v: string) {
|
||||
setWidgetValue(samplerNode, 'seed', +v)
|
||||
}
|
||||
}
|
||||
|
||||
for (const opt in opts) {
|
||||
if (opt in handlers) {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
handlers[opt](popOpt(opt))
|
||||
const handler = handlers[opt]
|
||||
if (handler) {
|
||||
const value = popOpt(opt)
|
||||
if (value !== undefined) handler(value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -462,27 +477,29 @@ export async function importA1111(graph, parameters) {
|
||||
setWidgetValue(
|
||||
hrSamplerNode,
|
||||
'steps',
|
||||
hrSteps ? +hrSteps : getWidget(samplerNode, 'steps').value
|
||||
hrSteps
|
||||
? +hrSteps
|
||||
: (getWidget(samplerNode, 'steps')?.value as number)
|
||||
)
|
||||
setWidgetValue(
|
||||
hrSamplerNode,
|
||||
'cfg',
|
||||
getWidget(samplerNode, 'cfg').value
|
||||
getWidget(samplerNode, 'cfg')?.value as number
|
||||
)
|
||||
setWidgetValue(
|
||||
hrSamplerNode,
|
||||
'scheduler',
|
||||
getWidget(samplerNode, 'scheduler').value
|
||||
getWidget(samplerNode, 'scheduler')?.value as string
|
||||
)
|
||||
setWidgetValue(
|
||||
hrSamplerNode,
|
||||
'sampler_name',
|
||||
getWidget(samplerNode, 'sampler_name').value
|
||||
getWidget(samplerNode, 'sampler_name')?.value as string
|
||||
)
|
||||
setWidgetValue(
|
||||
hrSamplerNode,
|
||||
'denoise',
|
||||
+(popOpt('denoising strength') || '1')
|
||||
+(popOpt('denoising strength') ?? '1')
|
||||
)
|
||||
}
|
||||
|
||||
@@ -490,10 +507,17 @@ export async function importA1111(graph, parameters) {
|
||||
positiveNode,
|
||||
positive,
|
||||
{ node: clipSkipNode, index: 0 },
|
||||
{ node: ckptNode, index: 0 }
|
||||
{ node: ckptNode, index: 0 },
|
||||
samplerNode
|
||||
)
|
||||
positive = n.text
|
||||
n = createLoraNodes(negativeNode, negative, n.prevClip, n.prevModel)
|
||||
n = createLoraNodes(
|
||||
negativeNode,
|
||||
negative,
|
||||
n.prevClip,
|
||||
n.prevModel,
|
||||
samplerNode
|
||||
)
|
||||
negative = n.text
|
||||
|
||||
setWidgetValue(positiveNode, 'text', replaceEmbeddings(positive))
|
||||
|
||||
Reference in New Issue
Block a user