Serialize node links instead of widget values
Syncing litegraph widget state is kinda annoying, and unnecessary since everything will be moved to separate UI component nodes. Instead I modified the input slot type to store the min/max/step to be copied into the default UI node later. Now nothing uses litegraph's widgets anymore
This commit is contained in:
@@ -1,144 +1,70 @@
|
||||
import type { IWidget, LGraphNode } from "@litegraph-js/core";
|
||||
import type ComfyApp from "$lib/components/ComfyApp";
|
||||
import ComfyValueControlWidget from "./widgets/ComfyValueControlWidget";
|
||||
import type { ComfyInputConfig } from "./IComfyInputSlot";
|
||||
import type IComfyInputSlot from "./IComfyInputSlot";
|
||||
import { BuiltInSlotShape } from "@litegraph-ts/core";
|
||||
|
||||
export interface WidgetData {
|
||||
widget: IWidget,
|
||||
minWidth?: number,
|
||||
minHeight?: number
|
||||
}
|
||||
type WidgetFactory = (node: LGraphNode, inputName: string, inputData: any, app: ComfyApp) => IComfyInputSlot;
|
||||
|
||||
type WidgetFactory = (node: LGraphNode, inputName: string, inputData: any, app: ComfyApp) => WidgetData;
|
||||
|
||||
|
||||
type NumberConfig = { min: number, max: number, step: number, precision: number }
|
||||
type NumberDefaults = { val: number, config: NumberConfig }
|
||||
|
||||
function getNumberDefaults(inputData: any, defaultStep: number): NumberDefaults {
|
||||
let defaultVal = inputData[1]["default"];
|
||||
function getNumberDefaults(inputData: any, defaultStep: number): ComfyInputConfig {
|
||||
let defaultValue = inputData[1]["default"];
|
||||
let { min, max, step } = inputData[1];
|
||||
|
||||
if (defaultVal == undefined) defaultVal = 0;
|
||||
if (defaultValue == undefined) defaultValue = 0;
|
||||
if (min == undefined) min = 0;
|
||||
if (max == undefined) max = 2048;
|
||||
if (step == undefined) step = defaultStep;
|
||||
|
||||
return { val: defaultVal, config: { min, max, step: step, precision: 0 } };
|
||||
return { min, max, step: step, precision: 0, defaultValue };
|
||||
}
|
||||
|
||||
|
||||
const FLOAT: WidgetFactory = (node: LGraphNode, inputName: string, inputData: any): WidgetData => {
|
||||
const { val, config } = getNumberDefaults(inputData, 0.5);
|
||||
return { widget: node.addWidget("number", inputName, val, () => { }, config) };
|
||||
function addComfyInput(node: LGraphNode, inputName: string, extraInfo: Partial<IComfyInputSlot> = {}): IComfyInputSlot {
|
||||
const input = node.addInput(inputName) as IComfyInputSlot
|
||||
for (const [k, v] of Object.entries(extraInfo))
|
||||
input[k] = v
|
||||
input.serialize = true;
|
||||
input.shape = BuiltInSlotShape.CARD_SHAPE;
|
||||
input.color_off = "lightblue"
|
||||
input.color_on = "lightblue"
|
||||
return input;
|
||||
}
|
||||
|
||||
const FLOAT: WidgetFactory = (node: LGraphNode, inputName: string, inputData: any): IComfyInputSlot => {
|
||||
const config = getNumberDefaults(inputData, 0.5);
|
||||
return addComfyInput(node, inputName, { type: "number", config })
|
||||
}
|
||||
|
||||
const INT: WidgetFactory = (node: LGraphNode, inputName: string, inputData: any): WidgetData => {
|
||||
const { val, config } = getNumberDefaults(inputData, 1);
|
||||
return {
|
||||
widget: node.addWidget(
|
||||
"number",
|
||||
inputName,
|
||||
val,
|
||||
function(v) {
|
||||
const s = this.options.step;
|
||||
this.value = Math.round(v / s) * s;
|
||||
},
|
||||
config
|
||||
),
|
||||
};
|
||||
const INT: WidgetFactory = (node: LGraphNode, inputName: string, inputData: any): IComfyInputSlot => {
|
||||
const config = getNumberDefaults(inputData, 1);
|
||||
return addComfyInput(node, inputName, { type: "number", config })
|
||||
};
|
||||
|
||||
function seedWidget(node, inputName, inputData, app) {
|
||||
const seed = INT(node, inputName, inputData, app);
|
||||
const seedControl = new ComfyValueControlWidget("control_after_generate", "randomize", node, seed.widget);
|
||||
node.addCustomWidget(seedControl);
|
||||
|
||||
// seed.widget.linkedWidgets = [seedControl];
|
||||
return seed;
|
||||
}
|
||||
|
||||
const STRING: WidgetFactory = (node: LGraphNode, inputName: string, inputData: any, app: ComfyApp): WidgetData => {
|
||||
const defaultVal = inputData[1].default || "";
|
||||
const STRING: WidgetFactory = (node: LGraphNode, inputName: string, inputData: any, app: ComfyApp): IComfyInputSlot => {
|
||||
const defaultValue = inputData[1].default || "";
|
||||
const multiline = !!inputData[1].multiline;
|
||||
|
||||
// if (multiline) {
|
||||
// return addMultilineWidget(node, inputName, { defaultVal, ...inputData[1] }, app);
|
||||
// } else {
|
||||
return { widget: node.addWidget("text", inputName, defaultVal, () => { }, { multiline }) };
|
||||
// }
|
||||
return addComfyInput(node, inputName, { type: "string", config: { defaultValue, multiline } })
|
||||
};
|
||||
|
||||
const COMBO: WidgetFactory = (node: LGraphNode, inputName: string, inputData: any): WidgetData => {
|
||||
const COMBO: WidgetFactory = (node: LGraphNode, inputName: string, inputData: any): IComfyInputSlot => {
|
||||
const type = inputData[0];
|
||||
let defaultValue = type[0];
|
||||
if (inputData[1] && inputData[1].default) {
|
||||
defaultValue = inputData[1].default;
|
||||
}
|
||||
return { widget: node.addWidget("combo", inputName, defaultValue, () => { }, { values: type }) };
|
||||
return addComfyInput(node, inputName, { type: "string", config: { values: type, defaultValue } })
|
||||
}
|
||||
|
||||
const IMAGEUPLOAD: WidgetFactory = (node: LGraphNode, inputName: string, inputData: any, app): WidgetData => {
|
||||
const imageWidget = node.widgets.find((w) => w.name === "image");
|
||||
let uploadWidget: IWidget;
|
||||
|
||||
// async function uploadFile(file: File, updateNode: boolean) {
|
||||
// try {
|
||||
// // Wrap file in formdata so it includes filename
|
||||
// const body = new FormData();
|
||||
// body.append("image", file);
|
||||
// const resp = await fetch("/upload/image", {
|
||||
// method: "POST",
|
||||
// body,
|
||||
// });
|
||||
|
||||
// if (resp.status === 200) {
|
||||
// const data = await resp.json();
|
||||
// // Add the file as an option and update the widget value
|
||||
// if (!imageWidget.options.values.includes(data.name)) {
|
||||
// imageWidget.options.values.push(data.name);
|
||||
// }
|
||||
|
||||
// if (updateNode) {
|
||||
// // showImage(data.name);
|
||||
// imageWidget.value = data.name;
|
||||
// }
|
||||
// } else {
|
||||
// alert(resp.status + " - " + resp.statusText);
|
||||
// }
|
||||
// } catch (error) {
|
||||
// alert(error);
|
||||
// }
|
||||
// }
|
||||
|
||||
// const fileInput = document.createElement("input");
|
||||
// Object.assign(fileInput, {
|
||||
// type: "file",
|
||||
// accept: "image/jpeg,image/png",
|
||||
// style: "display: none",
|
||||
// onchange: async () => {
|
||||
// if (fileInput.files.length) {
|
||||
// await uploadFile(fileInput.files[0], true);
|
||||
// }
|
||||
// },
|
||||
// });
|
||||
// document.body.append(fileInput);
|
||||
|
||||
// Create the button widget for selecting the files
|
||||
uploadWidget = node.addWidget("button", "choose file to upload", "image", () => {
|
||||
// fileInput.click();
|
||||
});
|
||||
uploadWidget.options = { serialize: false };
|
||||
|
||||
return { widget: uploadWidget };
|
||||
const IMAGEUPLOAD: WidgetFactory = (node: LGraphNode, inputName: string, inputData: any, app): IComfyInputSlot => {
|
||||
return addComfyInput(node, inputName, { type: "number", config: {} })
|
||||
}
|
||||
|
||||
|
||||
export type WidgetRepository = Record<string, WidgetFactory>
|
||||
|
||||
export const ComfyWidgets: WidgetRepository = {
|
||||
"INT:seed": seedWidget,
|
||||
"INT:noise_seed": seedWidget,
|
||||
"INT:seed": INT,
|
||||
"INT:noise_seed": INT,
|
||||
FLOAT,
|
||||
INT,
|
||||
STRING,
|
||||
|
||||
Reference in New Issue
Block a user