Files

194 lines
8.9 KiB
JavaScript

// IMGNR-Utils/js/catch_edit_text.js
// Version: Status Color + Toggle
// Support Soft + Hard Mute
// Fix: Disabled native tooltip popup on text widget
// NEW: Bypass/Mute visual handling (Opacity toggle and invisible Glass Pane to block clicks)
// FIXED: Zooming (Scroll) passthrough to LiteGraph canvas over text widgets
import { app } from "../../../scripts/app.js";
app.registerExtension({
name: "Comfy.CatchEditTextNode.JS",
async beforeRegisterNodeDef(nodeType, nodeData, app) {
if (nodeData.name === "CatchEditTextNode") {
const onNodeCreated = nodeType.prototype.onNodeCreated;
nodeType.prototype.onNodeCreated = function () {
const r = onNodeCreated ? onNodeCreated.apply(this, arguments) : undefined;
const node = this;
const actionWidget = node.widgets.find(w => w.name === "action");
const colorToggleWidget = node.widgets.find(w => w.name === "use_status_color");
// --- DISABLE NATIVE BROWSER TOOLTIP ---
// Prevents the full text content from popping up when hovering
const textWidget = node.widgets.find(w => w.name === "editable_text_widget");
if (textWidget && textWidget.inputEl) {
textWidget.inputEl.title = "";
Object.defineProperty(textWidget.inputEl, "title", {
get() { return ""; },
set(_) { }
});
}
// -------------------------------------------
// --- HELPER: Set Upstream Mode & Visuals ---
const updateStatus = () => {
const mode = actionWidget ? actionWidget.value : "use_input";
const useColor = colorToggleWidget ? colorToggleWidget.value : true;
// 1. Logic: Handle Upstream Mode
if (node.inputs && node.inputs[0] && node.inputs[0].link !== null) {
const linkInfo = app.graph.links[node.inputs[0].link];
if (linkInfo) {
const originNode = app.graph.getNodeById(linkInfo.origin_id);
if (originNode) {
// Hard Mute = Mode 2 (Never)
// Soft Mute & Input = Mode 0 (Always/Normal)
const targetMode = (mode === "use_edit_block_inputnode") ? 2 : 0;
if (originNode.mode !== targetMode) {
originNode.mode = targetMode;
}
}
}
}
// 2. Visuals: Update Title Bar Color
if (useColor) {
if (mode === "use_input") {
node.color = "#2d4a2d"; // Forest Green
} else if (mode === "use_edit_mute_input") {
node.color = "#5e5e24"; // Muted Gold/Olive
} else {
node.color = "#5e2424"; // Muted Red
}
} else {
// Reset to default theme color
node.color = undefined;
}
// Force Canvas Redraw
node.setDirtyCanvas(true, true);
};
// --- WIDGET CALLBACKS ---
// 1. Action Dropdown Callback
if (actionWidget) {
const originalCallback = actionWidget.callback;
actionWidget.callback = function (value) {
updateStatus();
if (originalCallback) {
return originalCallback.apply(this, arguments);
}
};
}
// 2. Color Toggle Callback
if (colorToggleWidget) {
const originalCallback = colorToggleWidget.callback;
colorToggleWidget.callback = function (value) {
updateStatus();
if (originalCallback) {
return originalCallback.apply(this, arguments);
}
};
}
// Trigger once on startup
setTimeout(() => {
updateStatus();
}, 100);
// --- NEW: Scroll Passthrough & Glass Pane Setup ---
requestAnimationFrame(() => {
if (node.widgets) {
for (let w of node.widgets) {
if (w.inputEl && !w.glassPane) {
// 1. Scroll Passthrough for Active State
w.inputEl.addEventListener("wheel", (e) => {
e.preventDefault();
e.stopPropagation();
app.canvas.canvas.dispatchEvent(new WheelEvent(e.type, e));
});
// 2. Glass Pane for Muted/Bypassed State
const glassPane = document.createElement("div");
Object.assign(glassPane.style, {
position: "absolute", top: "0", left: "0", width: "100%", height: "100%",
zIndex: "9999", display: "none"
});
glassPane.addEventListener("wheel", (e) => {
e.preventDefault();
e.stopPropagation();
app.canvas.canvas.dispatchEvent(new WheelEvent(e.type, e));
});
if (w.inputEl.parentElement) {
w.inputEl.parentElement.appendChild(glassPane);
w.glassPane = glassPane;
}
}
}
}
});
return r;
};
// --- AUTO-SWITCH ON CONNECT ---
const onConnectionsChange = nodeType.prototype.onConnectionsChange;
nodeType.prototype.onConnectionsChange = function (type, index, connected, link_info, ioSlot) {
if (onConnectionsChange) onConnectionsChange.apply(this, arguments);
// If connecting input (index 0), switch to "use_input"
if (type === 1 && index === 0 && connected) {
const actionWidget = this.widgets.find(w => w.name === "action");
if (actionWidget && actionWidget.value !== "use_input") {
actionWidget.value = "use_input";
if (actionWidget.callback) actionWidget.callback("use_input");
}
}
};
// --- UPDATE TEXT ON EXECUTE ---
const onExecuted = nodeType.prototype.onExecuted;
nodeType.prototype.onExecuted = function (message) {
onExecuted?.apply(this, arguments);
if (message?.text && Array.isArray(message.text) && message.text.length > 0) {
const newText = message.text[0];
const targetWidget = this.widgets.find(w => w.name === "editable_text_widget");
if (targetWidget && targetWidget.value !== newText) {
targetWidget.value = newText;
}
}
};
// --- NEW: HANDLE BYPASS/MUTE STATE VISUALS ---
const onDrawBackground = nodeType.prototype.onDrawBackground;
nodeType.prototype.onDrawBackground = function (ctx) {
if (onDrawBackground) onDrawBackground.apply(this, arguments);
if (this.lastMode !== this.mode) {
this.lastMode = this.mode;
// LiteGraph.NEVER = 2 (Mute), LiteGraph.BYPASS = 4 (Bypass)
const isInactive = (this.mode === 2 || this.mode === 4);
if (this.widgets) {
for (let w of this.widgets) {
if (w.inputEl) {
w.inputEl.style.opacity = isInactive ? "0.5" : "1";
}
if (w.glassPane) {
w.glassPane.style.display = isInactive ? "block" : "none";
}
}
}
}
};
}
},
});