336 lines
12 KiB
JavaScript
336 lines
12 KiB
JavaScript
import { app } from "../../scripts/app.js";
|
||
|
||
app.registerExtension({
|
||
name: "RS.MultiLatent",
|
||
|
||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||
if (nodeData.name !== "RS_Image_MultiLatent") return;
|
||
|
||
const onNodeCreated = nodeType.prototype.onNodeCreated;
|
||
const onConfigure = nodeType.prototype.onConfigure;
|
||
|
||
nodeType.prototype.onNodeCreated = function() {
|
||
const result = onNodeCreated ? onNodeCreated.apply(this) : undefined;
|
||
initCustomPresets(this);
|
||
|
||
const minWidth = 260;
|
||
const minHeight = 310;
|
||
const origOnResize = this.onResize;
|
||
this.onResize = function(size) {
|
||
if (size[0] < minWidth) size[0] = minWidth;
|
||
if (size[1] < minHeight) size[1] = minHeight;
|
||
if (origOnResize) {
|
||
origOnResize.apply(this, [size]);
|
||
} else {
|
||
this.size = size;
|
||
this.setDirtyCanvas(true, true);
|
||
}
|
||
};
|
||
|
||
return result;
|
||
};
|
||
|
||
nodeType.prototype.onConfigure = function(info) {
|
||
const result = onConfigure ? onConfigure.apply(this, [info]) : undefined;
|
||
queueMicrotask(() => {
|
||
hideSystemWidgets(this);
|
||
if (this.updatePresetUI) {
|
||
this.updatePresetUI();
|
||
}
|
||
});
|
||
return result;
|
||
};
|
||
}
|
||
});
|
||
|
||
function hideSystemWidgets(node) {
|
||
const wActive = node.widgets.find(w => w.name === "active_preset");
|
||
const wStd = node.widgets.find(w => w.name === "preset_standard");
|
||
const wQwen = node.widgets.find(w => w.name === "preset_qwen");
|
||
const wKrea = node.widgets.find(w => w.name === "preset_krea2");
|
||
|
||
[wStd, wQwen, wKrea, wActive].forEach(w => {
|
||
if (w) {
|
||
w.type = "hidden";
|
||
w.hidden = true;
|
||
w.computeSize = () => [0, 0];
|
||
}
|
||
});
|
||
}
|
||
|
||
function saveNodeState(node) {
|
||
const wActive = node.widgets.find(w => w.name === "active_preset");
|
||
const wStd = node.widgets.find(w => w.name === "preset_standard");
|
||
const wQwen = node.widgets.find(w => w.name === "preset_qwen");
|
||
const wKrea = node.widgets.find(w => w.name === "preset_krea2");
|
||
|
||
if (wActive && wStd && wQwen && wKrea) {
|
||
const state = {
|
||
active: wActive.value,
|
||
std: wStd.value,
|
||
qwen: wQwen.value,
|
||
krea: wKrea.value
|
||
};
|
||
try {
|
||
localStorage.setItem(`rs_multilatent_${node.id}`, JSON.stringify(state));
|
||
} catch (e) {}
|
||
}
|
||
}
|
||
|
||
function loadNodeState(node) {
|
||
try {
|
||
const stateStr = localStorage.getItem(`rs_multilatent_${node.id}`);
|
||
if (stateStr) {
|
||
return JSON.parse(stateStr);
|
||
}
|
||
} catch (e) {}
|
||
return null;
|
||
}
|
||
|
||
function initCustomPresets(node) {
|
||
hideSystemWidgets(node);
|
||
|
||
const wActive = node.widgets.find(w => w.name === "active_preset");
|
||
const wStd = node.widgets.find(w => w.name === "preset_standard");
|
||
const wQwen = node.widgets.find(w => w.name === "preset_qwen");
|
||
const wKrea = node.widgets.find(w => w.name === "preset_krea2");
|
||
|
||
if (!wActive || !wStd || !wQwen || !wKrea) return;
|
||
|
||
const savedState = loadNodeState(node);
|
||
if (savedState) {
|
||
wActive.value = savedState.active;
|
||
wStd.value = savedState.std;
|
||
wQwen.value = savedState.qwen;
|
||
wKrea.value = savedState.krea;
|
||
}
|
||
|
||
const container = document.createElement("div");
|
||
container.style.display = "none";
|
||
container.style.flexDirection = "column";
|
||
container.style.gap = "4px";
|
||
container.style.padding = "4px 0";
|
||
container.style.width = "100%";
|
||
|
||
const placeholderDiv = document.createElement("div");
|
||
placeholderDiv.textContent = "SELECT A PRESET CATEGORY";
|
||
placeholderDiv.style.textAlign = "center";
|
||
placeholderDiv.style.fontSize = "10px";
|
||
placeholderDiv.style.color = "#00FF00";
|
||
placeholderDiv.style.padding = "2px 0";
|
||
placeholderDiv.style.fontWeight = "bold";
|
||
|
||
const buttonsContainer = document.createElement("div");
|
||
buttonsContainer.style.display = "flex";
|
||
buttonsContainer.style.gap = "8px";
|
||
buttonsContainer.style.width = "100%";
|
||
|
||
const infoDiv = document.createElement("div");
|
||
infoDiv.style.textAlign = "center";
|
||
infoDiv.style.padding = "6px 8px";
|
||
infoDiv.style.backgroundColor = "rgba(74, 158, 255, 0.1)";
|
||
infoDiv.style.borderRadius = "4px";
|
||
infoDiv.style.fontSize = "11px";
|
||
infoDiv.style.color = "#aaa";
|
||
infoDiv.style.fontWeight = "500";
|
||
|
||
const lists = [
|
||
{ id: "Standard", w: wStd, label: "STANDARD", color: "#4a9eff" },
|
||
{ id: "Qwen", w: wQwen, label: "QWEN", color: "#ff6b4a" },
|
||
{ id: "Krea2", w: wKrea, label: "KREA2", color: "#4aff9e" }
|
||
];
|
||
|
||
let currentOpen = null;
|
||
const buttons = [];
|
||
|
||
document.addEventListener("click", (e) => {
|
||
if (currentOpen && !container.contains(e.target)) {
|
||
currentOpen.remove();
|
||
currentOpen = null;
|
||
}
|
||
});
|
||
|
||
const updateInfo = () => {
|
||
const activeList = lists.find(l => l.id === wActive.value);
|
||
const presetValue = activeList ? activeList.w.value : "Not selected";
|
||
|
||
const orientationMatch = presetValue.match(/^(Square|Portrait|Landscape)\s*-\s*/);
|
||
const sizeMatch = presetValue.match(/(\d+)\s*[×x]\s*(\d+)/);
|
||
const aspectMatch = presetValue.match(/\(([^)]+)\)$/);
|
||
|
||
let infoText = "";
|
||
if (orientationMatch && sizeMatch) {
|
||
const orientation = orientationMatch[1];
|
||
const width = sizeMatch[1];
|
||
const height = sizeMatch[2];
|
||
const aspect = aspectMatch ? ` ${aspectMatch[1]}` : "";
|
||
infoText = `${orientation} - ${width}×${height}${aspect} — ${activeList ? activeList.label : ""}`;
|
||
} else {
|
||
infoText = presetValue;
|
||
}
|
||
|
||
infoDiv.textContent = infoText;
|
||
if (activeList) {
|
||
infoDiv.style.backgroundColor = `${activeList.color}15`;
|
||
infoDiv.style.color = activeList.color;
|
||
}
|
||
};
|
||
|
||
lists.forEach(list => {
|
||
const wrapper = document.createElement("div");
|
||
wrapper.style.flex = "1";
|
||
wrapper.style.position = "relative";
|
||
|
||
const btn = document.createElement("button");
|
||
btn.textContent = list.label;
|
||
btn.style.width = "100%";
|
||
btn.style.padding = "6px 4px";
|
||
btn.style.borderRadius = "4px";
|
||
btn.style.cursor = "pointer";
|
||
btn.style.fontSize = "10px";
|
||
btn.style.textAlign = "center";
|
||
btn.style.overflow = "hidden";
|
||
btn.style.textOverflow = "ellipsis";
|
||
btn.style.whiteSpace = "nowrap";
|
||
btn.style.transition = "all 0.15s";
|
||
|
||
btn.onclick = (e) => {
|
||
e.stopPropagation();
|
||
if (currentOpen) { currentOpen.remove(); currentOpen = null; }
|
||
wActive.value = list.id;
|
||
updateAllButtons();
|
||
updateInfo();
|
||
saveNodeState(node);
|
||
node.setDirtyCanvas(true, true);
|
||
|
||
const dropdown = document.createElement("div");
|
||
dropdown.style.position = "absolute";
|
||
dropdown.style.left = "0";
|
||
dropdown.style.width = "220px";
|
||
dropdown.style.maxHeight = "250px";
|
||
dropdown.style.overflowY = "auto";
|
||
dropdown.style.backgroundColor = "#1a1a1a";
|
||
dropdown.style.border = `1px solid ${list.color}`;
|
||
dropdown.style.borderRadius = "4px";
|
||
dropdown.style.boxShadow = "0 4px 12px rgba(0,0,0,0.8)";
|
||
dropdown.style.zIndex = "9999";
|
||
dropdown.style.fontSize = "11px";
|
||
|
||
const btnRect = btn.getBoundingClientRect();
|
||
const windowHeight = window.innerHeight;
|
||
const spaceBelow = windowHeight - btnRect.bottom;
|
||
const spaceAbove = btnRect.top;
|
||
|
||
if (spaceBelow < 250 && spaceAbove > spaceBelow) {
|
||
dropdown.style.bottom = "100%";
|
||
dropdown.style.marginBottom = "4px";
|
||
dropdown.style.maxHeight = `${Math.min(250, spaceAbove - 20)}px`;
|
||
} else {
|
||
dropdown.style.top = "100%";
|
||
dropdown.style.marginTop = "4px";
|
||
}
|
||
|
||
list.w.options.values.forEach(opt => {
|
||
const item = document.createElement("div");
|
||
item.textContent = opt;
|
||
item.style.padding = "6px 8px";
|
||
item.style.cursor = "pointer";
|
||
item.style.color = "#ddd";
|
||
item.style.borderBottom = "1px solid #2a2a2a";
|
||
item.style.whiteSpace = "nowrap";
|
||
item.style.overflow = "hidden";
|
||
item.style.textOverflow = "ellipsis";
|
||
|
||
item.onmouseenter = () => item.style.backgroundColor = "#333";
|
||
item.onmouseleave = () => item.style.backgroundColor = "transparent";
|
||
|
||
item.onclick = (ev) => {
|
||
ev.stopPropagation();
|
||
list.w.value = opt;
|
||
dropdown.remove();
|
||
currentOpen = null;
|
||
updateAllButtons();
|
||
updateInfo();
|
||
saveNodeState(node);
|
||
node.setDirtyCanvas(true, true);
|
||
};
|
||
|
||
dropdown.appendChild(item);
|
||
});
|
||
|
||
wrapper.appendChild(dropdown);
|
||
currentOpen = dropdown;
|
||
};
|
||
|
||
wrapper.appendChild(btn);
|
||
buttonsContainer.appendChild(wrapper);
|
||
buttons.push({ btn, list });
|
||
});
|
||
|
||
const updateAllButtons = () => {
|
||
buttons.forEach(({ btn, list }) => {
|
||
const isActive = wActive.value === list.id;
|
||
btn.style.border = `2px solid ${isActive ? list.color : "#555"}`;
|
||
btn.style.backgroundColor = isActive ? `${list.color}22` : "#1e1e1e";
|
||
btn.style.color = isActive ? "#fff" : "#777";
|
||
btn.style.fontWeight = isActive ? "bold" : "normal";
|
||
btn.title = list.w.value;
|
||
});
|
||
};
|
||
|
||
node.updatePresetUI = () => {
|
||
updateAllButtons();
|
||
updateInfo();
|
||
if (container.style.display === "none") {
|
||
container.style.display = "flex";
|
||
}
|
||
};
|
||
|
||
updateAllButtons();
|
||
updateInfo();
|
||
|
||
container.appendChild(placeholderDiv);
|
||
container.appendChild(buttonsContainer);
|
||
container.appendChild(infoDiv);
|
||
|
||
node.addDOMWidget("preset_ui", "preset_ui", container, {
|
||
serialize: false,
|
||
hideOnZoom: false,
|
||
computeSize: () => [node.size[0] - 20, 70]
|
||
});
|
||
|
||
const originalCallback = wActive.callback;
|
||
wActive.callback = function(value) {
|
||
if (originalCallback) originalCallback.apply(this, arguments);
|
||
updateAllButtons();
|
||
updateInfo();
|
||
saveNodeState(node);
|
||
};
|
||
|
||
container.style.display = "flex";
|
||
node.size = [260, 310];
|
||
}
|
||
|
||
document.addEventListener("visibilitychange", () => {
|
||
if (!document.hidden && app && app.graph) {
|
||
queueMicrotask(() => {
|
||
app.graph._nodes.forEach(node => {
|
||
if (node.type === "RS_Image_MultiLatent" && node.updatePresetUI) {
|
||
node.updatePresetUI();
|
||
}
|
||
});
|
||
});
|
||
}
|
||
});
|
||
|
||
window.addEventListener("focus", () => {
|
||
if (app && app.graph) {
|
||
queueMicrotask(() => {
|
||
app.graph._nodes.forEach(node => {
|
||
if (node.type === "RS_Image_MultiLatent" && node.updatePresetUI) {
|
||
node.updatePresetUI();
|
||
}
|
||
});
|
||
});
|
||
}
|
||
}); |