Files
malkuthro-ComfyUI-Koolook/web/ai_pipeline.js
T
malkuthroandClaude Opus 4.7 d763b36adc chore(legal): add NOTICE + SPDX headers across web/ (#93)
Codify attribution before the sidebar gets more visibility. NOTICE is the
single human-readable source of truth — copyright statement, plain-English
attribution request with a copy-pasteable credit line, pointer to LICENSE
and forks/THIRD_PARTY.md so it shows the project respects others'
attribution too.

SPDX-FileCopyrightText / SPDX-License-Identifier headers added at the top
of every web/*.js (entry point + sidebar/ + ai_pipeline) so license
scanners (GitHub, FOSSA, reuse-tool) parse each file correctly and
stripped-attribution forks become an obviously deliberate act rather than
"I forgot to copy a file."

GPL-3.0-only matches the LICENSE file (no "or later" wording in it). Year
range 2025-2026 spans first commit (2025-09-28) to today.

Comment-only change — no runtime impact.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 11:28:20 +02:00

140 lines
7.6 KiB
JavaScript

// SPDX-FileCopyrightText: 2025-2026 Kforge Labs <https://github.com/malkuthro/ComfyUI-Koolook>
// SPDX-License-Identifier: GPL-3.0-only
import { app } from "../../../scripts/app.js";
import { ComfyWidgets } from "../../../scripts/widgets.js";
app.registerExtension({
name: "koolook.ai_pipeline",
async beforeRegisterNodeDef(nodeType, nodeData) {
if (nodeData.name === "EasyAIPipeline") {
const originalOnNodeCreated = nodeType.prototype.onNodeCreated;
nodeType.prototype.onNodeCreated = function() {
if (originalOnNodeCreated) {
originalOnNodeCreated.call(this);
}
// Make instruction widget read-only and dimmed
const instructionWidget = this.widgets.find(w => w.name === "instruction");
if (instructionWidget && instructionWidget.inputEl) {
instructionWidget.inputEl.readOnly = true;
instructionWidget.inputEl.style.opacity = 0.6;
}
// Add multiline display widget using ComfyWidgets.STRING
const displayWidget = ComfyWidgets["STRING"](this, "Output Preview", ["STRING", { multiline: true }], app).widget;
displayWidget.inputEl.readOnly = true;
displayWidget.inputEl.style.height = "100px";
// Helper function to get effective value (from widget or upstream if connected and simple)
const getEffectiveValue = (node, name) => {
const widget = node.widgets.find(w => w.name === name);
const input = node.inputs?.find(i => i.name === name);
if (!input || input.link === null) {
return widget?.value;
}
const linkId = input.link;
const link = app.graph.links[linkId];
if (!link) return widget?.value;
const originNode = app.graph.getNodeById(link.origin_id);
const originWidget = originNode.widgets ? originNode.widgets[link.origin_slot] : null;
if (originWidget && 'value' in originWidget) {
return originWidget.value;
}
return null; // Cannot resolve
};
// Button to get and display output_directory (with cleaning for duplicate '/')
this.addWidget("button", "Get output directory path", null, () => {
const values = {
base_directory_path: getEffectiveValue(this, "base_directory_path"),
shot_name: getEffectiveValue(this, "shot_name"),
ai_method: getEffectiveValue(this, "ai_method"),
version: getEffectiveValue(this, "version"),
disable_versioning: getEffectiveValue(this, "disable_versioning"),
enable_overwrite: getEffectiveValue(this, "enable_overwrite")
};
if (Object.values(values).some(v => v === null)) {
displayWidget.value = "Cannot preview: complex inputs - execute node for accurate path.";
} else {
let parts = [values.base_directory_path, values.shot_name, values.ai_method];
if (!values.disable_versioning) {
const version_str = `v${values.version.toString().padStart(3, '0')}`;
parts.push(version_str);
}
let output_directory = parts.filter(part => part.toString().trim() !== "").join("/").replace(/\\/g, "/");
output_directory = output_directory.replace(/\/+/g, '/');
displayWidget.value = output_directory;
}
this.setDirtyCanvas(true, true);
}, { serialize: false });
// New button to get and display full file_path (with cleaning for duplicate '/')
this.addWidget("button", "Get output file path", null, () => {
const values = {
base_directory_path: getEffectiveValue(this, "base_directory_path"),
shot_name: getEffectiveValue(this, "shot_name"),
ai_method: getEffectiveValue(this, "ai_method"),
extension: getEffectiveValue(this, "extension"),
version: getEffectiveValue(this, "version"),
disable_versioning: getEffectiveValue(this, "disable_versioning"),
enable_overwrite: getEffectiveValue(this, "enable_overwrite")
};
if (Object.values(values).some(v => v === null)) {
displayWidget.value = "Cannot preview: complex inputs - execute node for accurate path.";
} else {
let parts = [values.base_directory_path, values.shot_name, values.ai_method];
let version_str = "";
if (!values.disable_versioning) {
version_str = `v${values.version.toString().padStart(3, '0')}`;
parts.push(version_str);
}
let output_directory = parts.filter(part => part.toString().trim() !== "").join("/").replace(/\\/g, "/");
output_directory = output_directory.replace(/\/+/g, '/');
const name = !values.disable_versioning ? `${values.shot_name}_${values.ai_method}_${version_str}${values.extension}` : `${values.shot_name}_${values.ai_method}${values.extension}`;
let file_path = `${output_directory}/${name}`.replace(/\\/g, "/");
file_path = file_path.replace(/\/+/g, '/');
displayWidget.value = file_path;
}
this.setDirtyCanvas(true, true);
}, { serialize: false });
};
}
},
async nodeCreated(node) {
if (node.comfyClass !== "EasyResize") return;
// Function to update widgets for a mode
const updateWidgets = (modeName, colorName, value) => {
const colorWidgetIndex = node.widgets.findIndex(w => w.name === colorName);
if (value === "Custom") {
if (colorWidgetIndex === -1) {
node.addWidget("string", colorName, "0, 0, 0", null, { multiline: false });
}
} else {
if (colorWidgetIndex !== -1) {
node.widgets.splice(colorWidgetIndex, 1); // Remove
}
}
node.setDirtyCanvas(true, true);
};
// Pad color mode
const padModeWidget = node.widgets.find(w => w.name === "pad_color_mode");
const origPadCallback = padModeWidget.callback;
padModeWidget.callback = (value) => {
if (origPadCallback) origPadCallback.call(node, value);
updateWidgets("pad_color_mode", "pad_color", value);
};
updateWidgets("pad_color_mode", "pad_color", padModeWidget.value);
// Panel color mode
const panelModeWidget = node.widgets.find(w => w.name === "panel_color_mode");
const origPanelCallback = panelModeWidget.callback;
panelModeWidget.callback = (value) => {
if (origPanelCallback) origPanelCallback.call(node, value);
updateWidgets("panel_color_mode", "panel_color", value);
};
updateWidgets("panel_color_mode", "panel_color", panelModeWidget.value);
}
});