249 lines
7.8 KiB
JavaScript
249 lines
7.8 KiB
JavaScript
import { app } from "../../scripts/app.js";
|
|
|
|
// CONSTANTS
|
|
const GLOBAL_HIDE_SETTING_ID = "SATA_UtilityNode.GlobalPreviewHide";
|
|
const PREVIEW_MACHINE_CLASS = "Preview_Machine";
|
|
const SAVE_MACHINE_CLASS = "Save_Machine";
|
|
|
|
// Track hover state
|
|
const hoveredNodeIds = new Set();
|
|
|
|
// Helper to get settings for both ComfyUI V1 and V2
|
|
function getSettingValue(id, defaultValue) {
|
|
if (app.extensionManager && app.extensionManager.setting) {
|
|
const val = app.extensionManager.setting.get(id);
|
|
return val !== undefined ? val : defaultValue;
|
|
} else if (app.ui && app.ui.settings) {
|
|
return app.ui.settings.getSettingValue(id, defaultValue);
|
|
}
|
|
return defaultValue;
|
|
}
|
|
|
|
app.registerExtension({
|
|
name: "SATA_UtilityNode.Preview_Machine",
|
|
|
|
settings: [
|
|
{
|
|
id: GLOBAL_HIDE_SETTING_ID,
|
|
name: "SATA Utility: Global Hide Previews",
|
|
type: "boolean",
|
|
defaultValue: false,
|
|
onChange: (value) => {
|
|
if (app.graph) app.graph.setDirtyCanvas(true, true);
|
|
}
|
|
}
|
|
],
|
|
|
|
async setup() {
|
|
setupGlobalMouseTracker();
|
|
},
|
|
|
|
async nodeCreated(node) {
|
|
if (isTargetNode(node)) {
|
|
setupNodeHider(node);
|
|
}
|
|
},
|
|
|
|
async loadedGraphNode(node) {
|
|
if (isTargetNode(node)) {
|
|
setupNodeHider(node);
|
|
}
|
|
}
|
|
});
|
|
|
|
function isTargetNode(node) {
|
|
return node.comfyClass === "PreviewImage" ||
|
|
node.comfyClass === "SaveImage" ||
|
|
node.comfyClass === PREVIEW_MACHINE_CLASS ||
|
|
node.comfyClass === SAVE_MACHINE_CLASS;
|
|
}
|
|
|
|
function shouldHideNode(node) {
|
|
const globalHide = getSettingValue(GLOBAL_HIDE_SETTING_ID, false);
|
|
|
|
if (node.comfyClass === PREVIEW_MACHINE_CLASS) {
|
|
return true;
|
|
}
|
|
|
|
if (node.comfyClass === SAVE_MACHINE_CLASS) {
|
|
const w = node.widgets?.find(w => w.name === "hide_preview");
|
|
if (w) {
|
|
if (w.value === true) return true;
|
|
if (w.value === false && !globalHide) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return globalHide;
|
|
}
|
|
|
|
function isMouseOverNode(node) {
|
|
if (!app.canvas || !app.canvas.graph_mouse) return false;
|
|
|
|
const mouse = app.canvas.graph_mouse;
|
|
const x = mouse[0];
|
|
const y = mouse[1];
|
|
|
|
return x >= node.pos[0] &&
|
|
x <= (node.pos[0] + node.size[0]) &&
|
|
y >= node.pos[1] &&
|
|
y <= (node.pos[1] + node.size[1]);
|
|
}
|
|
|
|
function setupGlobalMouseTracker() {
|
|
let rafPending = false;
|
|
|
|
const checkAndUpdate = () => {
|
|
if (!app.graph || !app.graph._nodes) return;
|
|
|
|
let changed = false;
|
|
|
|
for (const node of app.graph._nodes) {
|
|
if (!isTargetNode(node)) continue;
|
|
|
|
const currentlyHovered = isMouseOverNode(node);
|
|
const wasHovered = hoveredNodeIds.has(node.id);
|
|
|
|
if (currentlyHovered !== wasHovered) {
|
|
if (currentlyHovered) {
|
|
hoveredNodeIds.add(node.id);
|
|
} else {
|
|
hoveredNodeIds.delete(node.id);
|
|
}
|
|
changed = true;
|
|
}
|
|
}
|
|
|
|
if (changed) {
|
|
app.graph.setDirtyCanvas(true, true);
|
|
}
|
|
};
|
|
|
|
window.addEventListener("mousemove", () => {
|
|
if (!rafPending) {
|
|
rafPending = true;
|
|
requestAnimationFrame(() => {
|
|
rafPending = false;
|
|
checkAndUpdate();
|
|
});
|
|
}
|
|
});
|
|
|
|
window.addEventListener("mouseout", (e) => {
|
|
if (e.relatedTarget === null) {
|
|
hoveredNodeIds.clear();
|
|
if (app.graph) app.graph.setDirtyCanvas(true, true);
|
|
}
|
|
});
|
|
}
|
|
|
|
function setupNodeHider(node) {
|
|
if (node._sata_hider_v6) return;
|
|
node._sata_hider_v6 = true;
|
|
|
|
// Function to check if node should be hidden right now
|
|
const shouldHideNow = () => {
|
|
const shouldHide = shouldHideNode(node);
|
|
const hovered = hoveredNodeIds.has(node.id);
|
|
return shouldHide && !hovered;
|
|
};
|
|
|
|
// Find and patch all image-related widgets on the node
|
|
const patchWidgets = () => {
|
|
if (!node.widgets) return;
|
|
|
|
for (const widget of node.widgets) {
|
|
// Check if this is an image widget (they typically have draw/drawWidget methods)
|
|
if (widget._sata_patched) continue;
|
|
|
|
// Patch the draw method if it exists
|
|
if (widget.draw && typeof widget.draw === 'function') {
|
|
const origDraw = widget.draw.bind(widget);
|
|
widget.draw = function (ctx, node, widgetWidth, y, widgetHeight) {
|
|
if (shouldHideNow()) {
|
|
// Draw hidden placeholder instead
|
|
ctx.save();
|
|
ctx.fillStyle = "#1a1a1a";
|
|
ctx.fillRect(0, y, widgetWidth, widgetHeight);
|
|
|
|
ctx.fillStyle = "#888";
|
|
ctx.font = "12px Arial";
|
|
ctx.textAlign = "center";
|
|
ctx.textBaseline = "middle";
|
|
ctx.fillText("Hidden", widgetWidth / 2, y + widgetHeight / 2 - 8);
|
|
ctx.font = "9px Arial";
|
|
ctx.fillStyle = "#666";
|
|
ctx.fillText("(Hover to Reveal)", widgetWidth / 2, y + widgetHeight / 2 + 8);
|
|
ctx.restore();
|
|
return;
|
|
}
|
|
return origDraw(ctx, node, widgetWidth, y, widgetHeight);
|
|
};
|
|
widget._sata_patched = true;
|
|
}
|
|
|
|
// Also patch drawWidget if it exists (some widgets use this)
|
|
if (widget.drawWidget && typeof widget.drawWidget === 'function') {
|
|
const origDrawWidget = widget.drawWidget.bind(widget);
|
|
widget.drawWidget = function (...args) {
|
|
if (shouldHideNow()) {
|
|
// Skip drawing - the onDrawForeground will handle the overlay
|
|
return;
|
|
}
|
|
return origDrawWidget(...args);
|
|
};
|
|
widget._sata_patched = true;
|
|
}
|
|
}
|
|
};
|
|
|
|
// Patch widgets initially and on any update
|
|
patchWidgets();
|
|
|
|
// Also intercept onDrawBackground to catch widget additions
|
|
const origOnDrawBackground = node.onDrawBackground;
|
|
node.onDrawBackground = function (ctx) {
|
|
patchWidgets(); // Re-check for new widgets
|
|
if (origOnDrawBackground) {
|
|
origOnDrawBackground.apply(this, arguments);
|
|
}
|
|
};
|
|
|
|
// Draw overlay in onDrawForeground as backup
|
|
const origOnDrawForeground = node.onDrawForeground;
|
|
node.onDrawForeground = function (ctx) {
|
|
if (origOnDrawForeground) {
|
|
origOnDrawForeground.apply(this, arguments);
|
|
}
|
|
|
|
if (shouldHideNow()) {
|
|
ctx.save();
|
|
ctx.fillStyle = "#1a1a1a";
|
|
ctx.fillRect(0, 0, this.size[0], this.size[1]);
|
|
|
|
ctx.fillStyle = "#888";
|
|
ctx.font = "bold 14px Arial";
|
|
ctx.textAlign = "center";
|
|
ctx.textBaseline = "middle";
|
|
ctx.fillText("Hidden", this.size[0] / 2, this.size[1] / 2 - 10);
|
|
|
|
ctx.font = "10px Arial";
|
|
ctx.fillStyle = "#666";
|
|
ctx.fillText("(Hover to Reveal)", this.size[0] / 2, this.size[1] / 2 + 10);
|
|
ctx.restore();
|
|
}
|
|
};
|
|
|
|
// Widget callback for Save_Machine
|
|
if (node.comfyClass === SAVE_MACHINE_CLASS) {
|
|
const hideWidget = node.widgets?.find(w => w.name === "hide_preview");
|
|
if (hideWidget) {
|
|
const origCallback = hideWidget.callback;
|
|
hideWidget.callback = function (value) {
|
|
if (origCallback) origCallback.call(this, value);
|
|
if (app.graph) app.graph.setDirtyCanvas(true, true);
|
|
};
|
|
}
|
|
}
|
|
}
|