Visual edits of the RS Bypass Manager node
This commit is contained in:
+124
-49
@@ -157,35 +157,49 @@ app.registerExtension({
|
||||
return { x: toggleX, y: toggleY, w: toggleW, h: toggleH };
|
||||
};
|
||||
|
||||
self.drawBypassedItem = function(ctx, title, x, y, w, h, index, isBypassed) {
|
||||
const toggleZone = self.drawToggle(ctx, isBypassed, x, y, 36, h);
|
||||
|
||||
self.drawBypassedItem = function(ctx, title, x, y, w, h, index, state, isChild, isLastChild) {
|
||||
const toggleOn = state !== "none";
|
||||
const toggleZone = self.drawToggle(ctx, toggleOn, x, y, 36, h);
|
||||
|
||||
const pad = 8;
|
||||
const removeW = 16;
|
||||
const gap = 8;
|
||||
|
||||
|
||||
const textX = x + 28 + pad;
|
||||
const removeX = x + w - removeW + 4;
|
||||
const textW = removeX - gap - textX;
|
||||
|
||||
|
||||
const bgHeight = 20;
|
||||
const bgRadius = 4;
|
||||
const bgY = y + (h - bgHeight) / 2;
|
||||
|
||||
|
||||
ctx.fillStyle = index % 2 === 0 ? "rgba(34,34,34,1)" : "rgba(34,34,34,0.5)";
|
||||
ctx.beginPath();
|
||||
ctx.roundRect(textX, bgY, textW, bgHeight, bgRadius);
|
||||
ctx.fill();
|
||||
|
||||
ctx.fillStyle = isBypassed ? "#ff4444" : "#bbb";
|
||||
|
||||
let titleColor = "#bbb";
|
||||
if (state === "all") titleColor = "#ff4444";
|
||||
else if (state === "partial") titleColor = "#ff9800";
|
||||
|
||||
ctx.font = "11px sans-serif";
|
||||
ctx.textAlign = "left";
|
||||
ctx.fillText(title, textX + pad, y + h/2 + 4);
|
||||
|
||||
|
||||
let cursorX = textX + pad;
|
||||
if (isChild) {
|
||||
const prefix = isLastChild ? "└ " : "├ ";
|
||||
ctx.fillStyle = "#eee";
|
||||
ctx.fillText(prefix, cursorX, y + h/2 + 4);
|
||||
cursorX += ctx.measureText(prefix).width;
|
||||
}
|
||||
|
||||
ctx.fillStyle = titleColor;
|
||||
ctx.fillText(title, cursorX, y + h/2 + 4);
|
||||
|
||||
ctx.fillStyle = "#ff6666";
|
||||
ctx.textAlign = "center";
|
||||
ctx.fillText("✕", removeX + removeW / 2, y + h/2 + 4);
|
||||
|
||||
|
||||
return {
|
||||
toggle: toggleZone,
|
||||
remove: { x: removeX, y: y, w: removeW, h: h }
|
||||
@@ -206,6 +220,15 @@ app.registerExtension({
|
||||
}
|
||||
};
|
||||
|
||||
self._rs_getGroupState = function(group, nodes) {
|
||||
const groupNodes = nodes.filter(n => n.comfyClass !== "RS_Bypass" && isNodeInGroup(n, group));
|
||||
if (groupNodes.length === 0) return "none";
|
||||
const bypassed = groupNodes.filter(n => n.mode === 4).length;
|
||||
if (bypassed === 0) return "none";
|
||||
if (bypassed === groupNodes.length) return "all";
|
||||
return "partial";
|
||||
};
|
||||
|
||||
self._rs_broadcastRemove = function(itemType, itemId) {
|
||||
RS_BYPASS_INSTANCES.forEach(instance => {
|
||||
if (instance === self) return;
|
||||
@@ -290,25 +313,45 @@ app.registerExtension({
|
||||
|
||||
nodes.forEach(n => {
|
||||
if (n.comfyClass === "RS_Bypass") return;
|
||||
if (n.mode !== 4) return;
|
||||
|
||||
const managedByBypassedGroup = self.data.bypassedGroups.some(gItem => {
|
||||
if (!gItem.enabled) return false;
|
||||
|
||||
// Ищем группу из кэша, в которую входит нода (если есть)
|
||||
const groupEntry = self.data.bypassedGroups.find(gItem => {
|
||||
const group = groups.find(g => g.id === gItem.id);
|
||||
if (!group || !isNodeInGroup(n, group)) return false;
|
||||
const groupNodes = nodes.filter(nn => nn.comfyClass !== "RS_Bypass" && isNodeInGroup(nn, group));
|
||||
return groupNodes.length > 0 && groupNodes.every(nn => nn.mode === 4);
|
||||
return group && isNodeInGroup(n, group);
|
||||
});
|
||||
|
||||
if (managedByBypassedGroup) return;
|
||||
|
||||
if (!self.data.bypassedNodes.some(item => item.id === n.id)) {
|
||||
self.data.bypassedNodes.push({ id: n.id, enabled: true });
|
||||
changed = true;
|
||||
} else {
|
||||
const entry = self.data.bypassedNodes.find(item => item.id === n.id);
|
||||
if (entry && !entry.enabled) {
|
||||
entry.enabled = true;
|
||||
|
||||
if (n.mode === 4) {
|
||||
const managedByBypassedGroup = self.data.bypassedGroups.some(gItem => {
|
||||
if (!gItem.enabled) return false;
|
||||
const group = groups.find(g => g.id === gItem.id);
|
||||
if (!group || !isNodeInGroup(n, group)) return false;
|
||||
const groupNodes = nodes.filter(nn => nn.comfyClass !== "RS_Bypass" && isNodeInGroup(nn, group));
|
||||
return groupNodes.length > 0 && groupNodes.every(nn => nn.mode === 4);
|
||||
});
|
||||
|
||||
if (managedByBypassedGroup) return;
|
||||
|
||||
if (!self.data.bypassedNodes.some(item => item.id === n.id)) {
|
||||
self.data.bypassedNodes.push({ id: n.id, enabled: true });
|
||||
changed = true;
|
||||
} else {
|
||||
const entry = self.data.bypassedNodes.find(item => item.id === n.id);
|
||||
if (entry && !entry.enabled) {
|
||||
entry.enabled = true;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
} else if (n.mode === 0 && groupEntry && !groupEntry.enabled) {
|
||||
// Нода выключена, но её группа из кэша в статусе partial.
|
||||
// Считаем её отслеживаемой: добавляем с enabled=false.
|
||||
// Отсекаем случай «группа выключена целиком» — там нет ни одной забайпашенной соседки.
|
||||
const group = groups.find(g => g.id === groupEntry.id);
|
||||
if (!group) return;
|
||||
const groupNodes = nodes.filter(nn => nn.comfyClass !== "RS_Bypass" && isNodeInGroup(nn, group));
|
||||
const hasBypassedSibling = groupNodes.some(nn => nn.mode === 4);
|
||||
|
||||
if (hasBypassedSibling && !self.data.bypassedNodes.some(item => item.id === n.id)) {
|
||||
self.data.bypassedNodes.push({ id: n.id, enabled: false });
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
@@ -356,35 +399,62 @@ app.registerExtension({
|
||||
const groups = app.graph._groups || [];
|
||||
|
||||
const displayItems = [];
|
||||
|
||||
const nodesPlacedInGroups = new Set();
|
||||
|
||||
self.data.bypassedGroups.forEach(item => {
|
||||
const group = groups.find(g => g.id === item.id);
|
||||
if (group) {
|
||||
displayItems.push({
|
||||
type: "group",
|
||||
id: group.id,
|
||||
title: "📁 " + (group.title || "Group"),
|
||||
isBypassed: item.enabled
|
||||
if (!group) return;
|
||||
const state = self._rs_getGroupState(group, nodes);
|
||||
displayItems.push({
|
||||
type: "group",
|
||||
id: group.id,
|
||||
title: "📁 " + (group.title || "Group"),
|
||||
state: state
|
||||
});
|
||||
|
||||
if (state === "partial" || state === "none") {
|
||||
// Собираем только «свои» ноды этой группы
|
||||
const childItems = [];
|
||||
self.data.bypassedNodes.forEach(nItem => {
|
||||
const targetNode = nodes.find(n => n.id === nItem.id);
|
||||
if (!targetNode) return;
|
||||
if (!isNodeInGroup(targetNode, group)) return;
|
||||
if (nodesPlacedInGroups.has(targetNode.id)) return;
|
||||
childItems.push({ node: targetNode, entry: nItem });
|
||||
});
|
||||
|
||||
childItems.forEach((child, ci) => {
|
||||
displayItems.push({
|
||||
type: "node",
|
||||
id: child.node.id,
|
||||
title: " ️ " + (child.node.title || child.node.type),
|
||||
state: child.entry.enabled ? "all" : "none",
|
||||
isChild: true,
|
||||
isLastChild: ci === childItems.length - 1
|
||||
});
|
||||
nodesPlacedInGroups.add(child.node.id);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Остальные ноды — standalone или чья группа не в кэше
|
||||
self.data.bypassedNodes.forEach(item => {
|
||||
const targetNode = nodes.find(n => n.id === item.id);
|
||||
if (!targetNode) return;
|
||||
|
||||
if (nodesPlacedInGroups.has(targetNode.id)) return;
|
||||
|
||||
const coveredByEnabledGroup = self.data.bypassedGroups.some(gItem => {
|
||||
if (!gItem.enabled) return false;
|
||||
const group = groups.find(g => g.id === gItem.id);
|
||||
return group && isNodeInGroup(targetNode, group);
|
||||
});
|
||||
|
||||
|
||||
if (!coveredByEnabledGroup) {
|
||||
displayItems.push({
|
||||
type: "node",
|
||||
id: targetNode.id,
|
||||
title: "️ " + (targetNode.title || targetNode.type),
|
||||
isBypassed: item.enabled
|
||||
state: item.enabled ? "all" : "none"
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -404,7 +474,7 @@ app.registerExtension({
|
||||
|
||||
displayItems.forEach((item, index) => {
|
||||
const itemY = y + (index * rowH);
|
||||
const zones = self.drawBypassedItem(ctx, item.title, pad, itemY, self.size[0] - pad*2, rowH, index, item.isBypassed);
|
||||
const zones = self.drawBypassedItem(ctx, item.title, pad, itemY, self.size[0] - pad*2, rowH, index, item.state, item.isChild, item.isLastChild);
|
||||
|
||||
self.clickZones.push({
|
||||
type: "itemToggle",
|
||||
@@ -505,11 +575,12 @@ app.registerExtension({
|
||||
if (query && !title.toLowerCase().includes(query)) return;
|
||||
|
||||
const cachedGroup = self.data.bypassedGroups.find(item => item.id === group.id);
|
||||
const isAllBypassed = cachedGroup ? cachedGroup.enabled : false;
|
||||
const isExpanded = self.data.expandedGroups[group.id] || false;
|
||||
|
||||
|
||||
const bypassedCount = groupNodes.filter(n => n.mode === 4).length;
|
||||
let groupColor = "#ddd";
|
||||
if (isAllBypassed) groupColor = "#ff4444";
|
||||
if (bypassedCount === groupNodes.length) groupColor = "#ff4444";
|
||||
else if (bypassedCount > 0) groupColor = "#ff9800";
|
||||
|
||||
const groupItem = document.createElement("div");
|
||||
groupItem.style.cssText = "padding:10px 12px;cursor:pointer;color:" + groupColor + ";border-bottom:1px solid #333;font-size:12px;display:flex;align-items:center;transition:background-color 0.15s;";
|
||||
@@ -559,11 +630,12 @@ app.registerExtension({
|
||||
groupNodes.forEach(n => {
|
||||
const nodeTitle = n.title || n.type;
|
||||
if (query && !nodeTitle.toLowerCase().includes(query)) return;
|
||||
|
||||
|
||||
const cachedNode = self.data.bypassedNodes.find(item => item.id === n.id);
|
||||
const isNodeBypassed = cachedNode ? cachedNode.enabled : false;
|
||||
// Если персональной записи нет — считаем состояние по реальному mode (может быть забайпашено через родительскую группу)
|
||||
const isNodeBypassed = cachedNode ? cachedNode.enabled : (n.mode === 4);
|
||||
const nodeColor = isNodeBypassed ? "#ff4444" : "#ddd";
|
||||
|
||||
|
||||
const nodeItem = document.createElement("div");
|
||||
nodeItem.textContent = "️ " + nodeTitle;
|
||||
nodeItem.style.cssText = "padding:10px 12px 10px 32px;cursor:pointer;color:" + nodeColor + ";border-bottom:1px solid #333;font-size:12px;transition:background-color 0.15s;";
|
||||
@@ -572,13 +644,16 @@ app.registerExtension({
|
||||
nodeItem.onclick = (ev) => {
|
||||
ev.stopPropagation();
|
||||
let entry = self.data.bypassedNodes.find(item => item.id === n.id);
|
||||
// Текущее состояние — либо из личной записи, либо из реального mode
|
||||
const currentlyBypassed = entry ? entry.enabled : (n.mode === 4);
|
||||
const newState = !currentlyBypassed;
|
||||
if (entry) {
|
||||
entry.enabled = !entry.enabled;
|
||||
entry.enabled = newState;
|
||||
} else {
|
||||
entry = { id: n.id, enabled: true };
|
||||
entry = { id: n.id, enabled: newState };
|
||||
self.data.bypassedNodes.push(entry);
|
||||
}
|
||||
setNodeBypass(n, entry.enabled);
|
||||
setNodeBypass(n, newState);
|
||||
self._rs_syncToggleAllState();
|
||||
self._rs_syncData();
|
||||
self._rs_updateUI();
|
||||
|
||||
Reference in New Issue
Block a user