From e040e1084ea5f0e25df49dea80688d2e889f77ea Mon Sep 17 00:00:00 2001 From: Kelly Jerrell Date: Thu, 12 Jun 2025 18:38:15 -0700 Subject: [PATCH] improvement: all properties should be applied correctly when pasting a DT config --- web/ComfyUI-DrawThings-gRPC.js | 38 ++++----- web/configProperties.js | 151 ++++++++++++++++++++++----------- web/widgets.js | 11 ++- 3 files changed, 125 insertions(+), 75 deletions(-) diff --git a/web/ComfyUI-DrawThings-gRPC.js b/web/ComfyUI-DrawThings-gRPC.js index 51ad080..bbad328 100644 --- a/web/ComfyUI-DrawThings-gRPC.js +++ b/web/ComfyUI-DrawThings-gRPC.js @@ -51,32 +51,28 @@ const samplerProto = { { content: "Paste Draw Things config", callback: () => { - navigator.clipboard.readText().then((text) => { + navigator.clipboard.readText().then(async (text) => { try { - for (const [k, v] of Object.entries(JSON.parse(text))) { + const config = JSON.parse(text) + + for (const [k, v] of Object.entries(config)) { const prop = findPropertyJson(k) + + if (!prop) { + console.log('unknown prop in config:', k) + continue + } + const name = prop?.python - if (!name) { - console.log("couldn't find property", k) - continue - } + const widget = this.widgets.find((w) => w.name === name) - const w = this.widgets.find((w) => w.name === name) - if (!w) { - console.log("couldn't find widget for property", name) - continue - } - - if (k === "sampler") w.value = samplers[v] - else if (name === "model") { - const model = w.options.values.find((val) => val?.value?.file === v) - if (model) w.value = model - else w.value = w.options.values[0] - } else if (name === "seed_mode") w.value = seedModes[v] - else w.value = v + await prop.import(k, v, widget, this, config) } + + this.updateDynamicWidgets?.(); + } catch (e) { - alert("Failed to parse Draw Things config from clipboard") + alert("Failed to parse Draw Things config from clipboard\n\n" + e) console.warn(e) } }) @@ -156,6 +152,6 @@ export const samplers = [ "DDIM Trailing", ] -const seedModes = ["Legacy", "TorchCpuCompatible", "ScaleAlike", "NvidiaGpuCompatible"]; +export const seedModes = ["Legacy", "TorchCpuCompatible", "ScaleAlike", "NvidiaGpuCompatible"]; /** @import { LGraphCanvas, LGraphNode, WidgetCallback, IWidget } from "litegraph.js"; */ diff --git a/web/configProperties.js b/web/configProperties.js index 387520c..0155f8c 100644 --- a/web/configProperties.js +++ b/web/configProperties.js @@ -1,3 +1,7 @@ +import { updateNodeModels } from './models.js' +import { samplers, seedModes } from './ComfyUI-DrawThings-gRPC.js' +import { calcShift } from './widgets.js' + const propertyData = [ ['start_width', 'width', 'DrawThingsSampler', 'width'], ['start_height', 'height', 'DrawThingsSampler', 'height'], @@ -104,75 +108,122 @@ function roundBy64(value) { return Math.round(value / 64) * 64 } -const converters = { - start_width: { - toComfy: roundBy64, - toJson: roundBy64 +/** @type {Record void>} */ +const importers = { + model: async (k, v, w, n, c) => { + await updateNodeModels(n) + const matchingOption = w?.options?.values?.find(ov => ov.value?.file === v) + if (matchingOption) w.value = matchingOption }, - start_height: { - toComfy: roundBy64, - toJson: roundBy64 + start_width: (k, v, w) => { + if (w) w.value = roundBy64(v) }, - sampler: { - toComfy: (value, ctx) => 0, - toJson: (value, ctx) => 0 + start_height: (k, v, w) => { + if (w) w.value = roundBy64(v) }, - hires_fix_start_width: { - toComfy: roundBy64, - toJson: roundBy64 + sampler: (k, v, w) => { + w.value = samplers[v] }, - hires_fix_start_height: { - toComfy: roundBy64, - toJson: roundBy64 + seed: (k, v, w, n) => { + if (typeof v === "number" && v >= 0) { + w.value = v + const controlWidget = n.widgets.find((w) => w.name === "control_after_generate") + if (controlWidget) controlWidget.value = "fixed" + } }, - seed_mode: { - toComfy: (value, ctx) => 0, - toJson: (value, ctx) => 0 + hires_fix_start_width: (k, v, w) => { + if (w) w.value = roundBy64(v) }, - decoding_tile_width: { - toComfy: roundBy64, - toJson: roundBy64 + hires_fix_start_height: (k, v, w) => { + if (w) w.value = roundBy64(v) }, - decoding_tile_height: { - toComfy: roundBy64, - toJson: roundBy64 + seed_mode: (k, v, w) => { + if (w && seedModes[v]) w.value = seedModes[v] }, - decoding_tile_overlap: { - toComfy: roundBy64, - toJson: roundBy64 + decoding_tile_width: (k, v, w) => { + if (w) w.value = roundBy64(v) }, - diffusion_tile_width: { - toComfy: roundBy64, - toJson: roundBy64 + decoding_tile_height: (k, v, w) => { + if (w) w.value = roundBy64(v) }, - diffusion_tile_height: { - toComfy: roundBy64, - toJson: roundBy64 + decoding_tile_overlap: (k, v, w) => { + if (w) w.value = roundBy64(v) }, - diffusion_tile_overlap: { - toComfy: roundBy64, - toJson: roundBy64 + diffusion_tile_width: (k, v, w) => { + if (w) w.value = roundBy64(v) }, - guidance_embed: { - toComfy: (value, ctx) => 0, - toJson: (value, ctx) => 0 + diffusion_tile_height: (k, v, w) => { + if (w) w.value = roundBy64(v) }, - resolution_dependent_shift: { - toComfy: (value, ctx) => 0, - toJson: (value, ctx) => 0 + diffusion_tile_overlap: (k, v, w) => { + if (w) w.value = roundBy64(v) }, - causal_inference_enabled: { - toComfy: (value, ctx) => 0, - toJson: (value, ctx) => 0 + guidance_embed: (k, v, w, n, c) => { + if (w) w.value = v }, - causal_inference: { - toComfy: (value, ctx) => 0, - toJson: (value, ctx) => 0 + resolution_dependent_shift: (k, v, w, n, c) => { + if (w) w.value = v + if (v) { + const shiftWidget = n.widgets.find((w) => w.name === "shift") + const width = c.width || n.widgets.find((w) => w.name === "width")?.value + const height = c.height || n.widgets.find((w) => w.name === "height")?.value + if (shiftWidget && width && height) shiftWidget.value = calcShift(width, height) + } + }, + causal_inference: (k, v, w, n, c) => { + // only set if enabled in the config + if (w && typeof v === 'number') w.value = v * 4 - 3 }, } +const exporters = { + start_width: {}, + start_height: {}, + sampler: {}, + hires_fix_start_width: {}, + hires_fix_start_height: {}, + seed_mode: {}, + decoding_tile_width: {}, + decoding_tile_height: {}, + decoding_tile_overlap: {}, + diffusion_tile_width: {}, + diffusion_tile_height: {}, + diffusion_tile_overlap: {}, + guidance_embed: {}, + resolution_dependent_shift: {}, + causal_inference_enabled: {}, + causal_inference: {}, +} + +class DTProperty { + constructor(fbs, python, node, json) { + this.fbs = fbs + this.python = python + this.node = node + this.json = json + } + + customImport = undefined + + async import(key, value, widget, node, config) { + if (this.customImport) + return await this.customImport(key, value, widget, node, config) + else { + if (widget) + widget.value = value + } + + } +} + /** @type {DTProperty[]} */ -export const properties = propertyData.map(([fbs, python, node, json]) => ({ fbs, python, node, json })) +export const properties = propertyData.map(([fbs, python, node, json]) => { + + const prop = new DTProperty(fbs, python, node, json) + prop.customImport = importers[fbs] + prop.customExport = exporters[fbs] + return prop +}) export function findPropertyJson(name) { return properties.find(p => p.json === name) diff --git a/web/widgets.js b/web/widgets.js index 4bd3203..6663c54 100644 --- a/web/widgets.js +++ b/web/widgets.js @@ -64,7 +64,7 @@ const advancedWidgets = [ let origProps = {}; // From flux-auto-workflow.js -function calcShift(h, w) { +export function calcShift(h, w) { const step1 = (h * w) / 256; const step2 = (1.15 - 0.5) / (4096 - 256); const step3 = (step1 - 256) * step2; @@ -279,17 +279,20 @@ app.registerExtension({ /** @type {import("@comfyorg/litegraph").LGraphNode} */ const samplerWidgetsProto = { + updateDynamicWidgets() { + updateSamplerWidgets(this) + }, onNodeCreated() { - updateSamplerWidgets(this); + this.updateDynamicWidgets(); }, onConfigure(data) { - updateSamplerWidgets(this); + this.updateDynamicWidgets() }, onInputAdded(input) { if (input.widget) updateInput(input, this) }, onWidgetChanged(name, value, old_Value, widget) { - updateSamplerWidgets(this, widget); + this.updateDynamicWidgets(); if (name === "res_dpt_shift") { const resDPTShiftAvailable = ["flux1", "sd3", "hidream_i1"].includes(this.getModelVersion());