Files
2025-08-27 03:46:38 -07:00

216 lines
7.6 KiB
JavaScript

import { findPropertyPython } from "./configProperties.js"
import { DtButtonsTypeHandler } from './lora.js'
import { DtModelTypeHandler } from "./models.js"
import { checkVersion } from './upgrade.js'
import { setCallback, updateProto } from "./util.js"
export const nodePackVersion = "1.6.2"
let previewMethod = undefined
// this holds the node definition from python
let dtSamplerNodeData = null
/** @type {import("@comfyorg/comfyui-frontend-types").ComfyExtension} */
export default {
name: "core",
getCustomWidgets(app) {
return {
DT_MODEL: DtModelTypeHandler,
"DT_BUTTONS": DtButtonsTypeHandler
}
},
beforeConfigureGraph(graph) {
for (const node of graph.nodes) {
if (node.type === "DrawThingsPositive" || node.type === "DrawThingsNegative") {
node.type = "DrawThingsPrompt"
node.properties['Node name for S&R'] = "DrawThingsPrompt"
delete node.properties.ver
node.widgets_values.unshift("...")
}
}
},
async beforeRegisterNodeDef(nodeType, nodeData, app) {
if (nodeType.comfyClass === "DrawThingsSampler") {
updateProto(nodeType, samplerProto)
dtSamplerNodeData = nodeData
}
},
// beforeConfigureGraph: function (graph) {
// const samplerNodes = graph.nodes.filter(n => n.type === "DrawThingsSampler")
// if (samplerNodes.some(n => n.nodePackVersion !== nodePackVersion)) {
// console.log("Nodes in workflow are from different version of ComfyUI-DrawThings-gRPC")
// }
// },
async setup(app) {
// query the api for the preview setting
await updatePreviewSetting()
// listen to the manager ui to update preview setting if necessary
const closeHandler = async () => {
await updatePreviewSetting()
}
setTimeout(() => document.querySelector('button[title="ComfyUI Manager"]').addEventListener("click", async () => {
document.getElementById('cm-close-button')?.addEventListener("click", closeHandler, { once: true })
}), 3000)
// if the prompt is cancelled, send a signal to the server to cancel the grpc request
setCallback(app.api, "interrupt", async (e) => {
if (app.graph.nodes.some(n => n.type === "DrawThingsSampler")) {
await app.api.fetchApi(`/dt_grpc/interrupt`, {
method: "POST",
})
}
})
}
}
/** @type {import("@comfyorg/litegraph").LGraphNode} */
const samplerProto = {
async onNodeCreated() {
const inputPos = this.inputs.find((inputPos) => inputPos.name == "positive")
const inputNeg = this.inputs.find((inputNeg) => inputNeg.name == "negative")
inputPos.color_on =
inputPos.color_off =
inputNeg.color_on =
inputNeg.color_off =
app.canvas.default_connection_color_byType["CONDITIONING"]
app.canvas.default_connection_color_byType["DT_LORA"] = app.canvas.default_connection_color_byType["MODEL"]
app.canvas.default_connection_color_byType["DT_CNET"] =
app.canvas.default_connection_color_byType["CONTROL_NET"]
setTimeout(() => checkVersion(nodePackVersion), 2000)
},
onMouseDown(e, pos, canvas) {
// this exists for easier debugging in devtools
console.debug("Click!", this)
},
onSerialize(serialised) {
serialised.nodePackVersion = nodePackVersion
serialised.widget_values_keyed = Object.fromEntries(this.widgets.map(w => ([w.name, w.value])))
},
onConfigure(serialised) {
// at this point, the node should already be loaded with values from the values array
// if there is keyed data, apply that
if (serialised.widget_values_keyed) {
for (const [name, value] of Object.entries(serialised.widget_values_keyed)) {
const widget = this.widgets.find((w) => w.name === name)
if (widget) widget.value = value
}
}
// check each widget value
const corrections = []
for (const w of this.widgets) {
const prop = findPropertyPython(w.name)
if (!prop) {
continue
}
const coerced = prop.coerce(w.value)
if (coerced !== w.value) {
corrections.push({ name: w.name, value: w.value, coerced })
w.value = coerced
}
}
if (corrections.length) {
const message = 'The Draw Things Sampler node contained invalid values - they have been corrected:'
const list = corrections.map((c) => `${c.name}: ${c.value} -> ${c.coerced}`)
const detail = message + "\n\n" + list.join("\n")
app.extensionManager.toast.add({
severity: "info",
summary: "Draw Things gRPC",
detail,
life: 8000
})
}
delete this.widget_values_keyed
this.updateDynamicWidgets?.()
},
getExtraMenuOptions(canvas, options) {
const keepNodeShrunk = app.extensionManager.setting.get("drawthings.node.keep_shrunk")
options.push(
null,
{
content: "Paste Draw Things config",
callback: () => {
navigator.clipboard.readText().then(async (text) => {
try {
const config = JSON.parse(text)
for (const w of this.widgets) {
const prop = findPropertyPython(w.name)
if (!prop)
continue
await prop.import(w.name, config[prop.json], w, this, config)
}
this.updateDynamicWidgets?.()
} catch (e) {
alert("Failed to parse Draw Things config from clipboard\n\n" + e)
console.warn(e)
}
})
},
},
{
content: "Copy Draw Things config",
callback: () => {
const config = {}
for (const w of this.widgets) {
const prop = findPropertyPython(w.name)
if (!prop)
continue
prop.export(w, this, config)
}
config.loras = []
config.control = []
navigator.clipboard.writeText(JSON.stringify(config))
},
},
{
content: (keepNodeShrunk ? "✓ " : "") + "Keep node shrunk when widgets change",
callback: async () => {
try {
await app.extensionManager.setting.set("drawthings.node.keep_shrunk", !keepNodeShrunk)
} catch (error) {
console.error(`Error changing setting: ${error}`)
}
},
},
null
)
},
}
async function updatePreviewSetting() {
const api = window.comfyAPI.api.api
const res = await api.fetchApi('/manager/preview_method')
previewMethod = await res.text()
const body = new FormData()
body.append("preview", previewMethod)
await api.fetchApi(`/dt_grpc/preview`, {
method: "POST",
body,
})
}
/** @import { LGraphCanvas, LGraphNode, WidgetCallback, IWidget } from "litegraph.js"; */