Compare commits

...
3 Commits
4 changed files with 169 additions and 136 deletions
+125 -91
View File
@@ -102,7 +102,6 @@ export function syncStockHistoryAndProgressSettings(enable) {
const dockedVal = !!enable;
const progressVal = !enable;
const QPOV2_ID = "Comfy.Queue.QPOV2";
try {
@@ -119,9 +118,7 @@ export function syncStockHistoryAndProgressSettings(enable) {
const isChecked = toggle.checked ||
toggle.getAttribute('data-p-checked') === 'true' ||
toggle.classList.contains('p-toggleswitch-checked');
if (isChecked !== dockedVal) {
toggle.click();
}
if (isChecked !== dockedVal) toggle.click();
}
}
} catch (e) {}
@@ -136,36 +133,29 @@ export function syncStockHistoryAndProgressSettings(enable) {
const progressBarBtn = document.querySelector('[data-testid="show-run-progress-bar-action"]');
if (progressBarBtn) {
const isChecked = !!progressBarBtn.querySelector('[class*="lucide--check"], .icon-\\[lucide--check\\]');
if (isChecked !== progressVal) {
progressBarBtn.click();
}
if (isChecked !== progressVal) progressBarBtn.click();
}
} catch (e) {}
try {
window.dispatchEvent(new Event("storage"));
} catch (e) {}
try { window.dispatchEvent(new Event("storage")); } catch (e) {}
}
export function applyClassicLayout(enable, updateSetting = false) {
let styleEl = document.getElementById(STYLE_ID);
if (enable) {
if (updateSetting && app.ui && app.ui.settings) {
if (updateSetting && app.ui?.settings) {
app.ui.settings.setSettingValue("Comfy.Workflow.WorkflowTabsPosition", "Topbar");
}
if (!styleEl) {
styleEl = document.createElement("style");
styleEl.id = STYLE_ID;
document.head.appendChild(styleEl);
}
styleEl.textContent = CLASSIC_LAYOUT_CSS_MEDIA;
} else {
if (styleEl) styleEl.remove();
if (updateSetting && app.ui && app.ui.settings) {
if (updateSetting && app.ui?.settings) {
app.ui.settings.setSettingValue("Comfy.Workflow.WorkflowTabsPosition", "Sidebar");
}
}
@@ -176,13 +166,22 @@ let domObserver = null;
let syncScheduled = false;
function findOriginalButton() {
const icon = document.querySelector('[class*="lucide--panel-right"], [class*="lucide--panel-left"]');
if (icon) {
const icons = document.querySelectorAll('[class*="lucide--panel-right"], [class*="lucide--panel-left"], [class*="lucide--panel-bottom"]');
for (const icon of icons) {
const btn = icon.closest('button, [role="button"], .comfyui-menu-item, .p-button');
if (btn && !btn.classList.contains("comfy-sidebar-custom-properties-toggle")) {
return btn;
}
}
const buttons = document.querySelectorAll('button, [role="button"], .p-button');
for (const btn of buttons) {
if (btn.classList.contains("comfy-sidebar-custom-properties-toggle")) continue;
const title = (btn.getAttribute("title") || btn.getAttribute("aria-label") || "").toLowerCase();
if (title.includes("workflow overview") || title.includes("properties") || title.includes("toggle panel")) {
return btn;
}
}
return null;
}
@@ -191,13 +190,13 @@ function findTopbarContainer() {
if (container) return container;
const orig = findOriginalButton();
if (orig && orig.parentNode) return orig.parentNode;
if (orig?.parentNode) return orig.parentNode;
const buttons = document.querySelectorAll("button, .p-button");
for (const btn of buttons) {
if (btn.classList.contains("comfy-sidebar-custom-properties-toggle")) continue;
const title = (btn.getAttribute("title") || btn.getAttribute("aria-label") || "").toLowerCase();
if (title.includes("run") || title.includes("queue") || title.includes("manager")) {
if (title.includes("run") || title.includes("manager")) {
if (btn.parentNode) return btn.parentNode;
}
}
@@ -205,10 +204,32 @@ function findTopbarContainer() {
}
function isPropertiesPanelOpen() {
const icon = document.querySelector('[class*="lucide--panel-right"], [class*="lucide--panel-left"]');
if (!icon) return false;
const classStr = icon.getAttribute("class") || "";
return classStr.includes("lucide--panel-left");
// 1. Direct check via data-testid
const panel = document.querySelector('[data-testid="properties-panel"]');
if (panel && panel.offsetWidth > 0 && panel.offsetHeight > 0) {
return true;
}
// 2. Fallback check for visible headings/containers
const headings = document.querySelectorAll('h1, h2, h3, h4');
for (const h of headings) {
if (h.textContent.trim() === "Workflow Overview" && h.offsetWidth > 0 && h.offsetHeight > 0) {
return true;
}
}
// 3. Fallback check for original button active state
const origBtn = findOriginalButton();
if (origBtn) {
if (origBtn.classList.contains("p-button-active") ||
origBtn.classList.contains("active") ||
origBtn.getAttribute("aria-expanded") === "true" ||
origBtn.getAttribute("aria-pressed") === "true") {
return true;
}
}
return false;
}
function findActiveQueueIndicator() {
@@ -218,7 +239,6 @@ function findActiveQueueIndicator() {
const buttons = container.querySelectorAll("button, .p-button, .comfyui-menu-item");
for (const btn of buttons) {
if (btn.classList.contains("comfy-sidebar-custom-properties-toggle")) continue;
const text = (btn.textContent || "").toLowerCase();
if (text.includes("active") || text.includes("queued") || text.includes("running") || text.includes("pending")) {
return btn;
@@ -236,7 +256,6 @@ function updateSidebarTabsVisibility() {
const sidebar = document.querySelector('.comfyui-sidebar, .comfy-sidebar, .sidebar, [class*="sidebar-nav"], [class*="sidebar"]');
if (!sidebar) return;
// NodesMap removed from tabIconSelectors
const tabIconSelectors = {
"Assets": '[class*="comfy--image-ai-edit"]',
"Nodes": '[class*="comfy--node"]',
@@ -248,19 +267,13 @@ function updateSidebarTabsVisibility() {
Object.entries(tabIconSelectors).forEach(([tab, selector]) => {
const shouldHide = app.ui?.settings?.getSettingValue(`Comfy Sidebar.Hide Junk.${tab}`) ?? false;
const icon = sidebar.querySelector(selector);
if (icon) {
const tabBtn = icon.closest('.comfyui-sidebar-tab, button, [role="tab"], li, a, .comfyui-sidebar-item') || icon;
if (shouldHide) {
if (tabBtn.style.display !== "none") {
tabBtn.style.setProperty("display", "none", "important");
}
if (tabBtn.style.display !== "none") tabBtn.style.setProperty("display", "none", "important");
} else {
if (tabBtn.style.display === "none") {
tabBtn.style.removeProperty("display");
}
if (tabBtn.style.display === "none") tabBtn.style.removeProperty("display");
}
}
});
@@ -270,13 +283,9 @@ function updateSidebarTabsVisibility() {
if (historyIcon) {
const historyBtn = historyIcon.closest('.comfyui-sidebar-tab, button, [role="tab"], li, a, .comfyui-sidebar-item') || historyIcon;
if (hideStockHistory) {
if (historyBtn.style.display !== "none") {
historyBtn.style.setProperty("display", "none", "important");
}
if (historyBtn.style.display !== "none") historyBtn.style.setProperty("display", "none", "important");
} else {
if (historyBtn.style.display === "none") {
historyBtn.style.removeProperty("display");
}
if (historyBtn.style.display === "none") historyBtn.style.removeProperty("display");
}
}
}
@@ -284,17 +293,13 @@ function updateSidebarTabsVisibility() {
function findGraphButton() {
const elements = document.querySelectorAll('.bg-secondary-background.rounded-lg.items-center.inline-flex.pointer-events-auto');
for (const el of elements) {
if (el.closest('.p-dialog, .comfy-modal, [role="dialog"], .p-sidebar, .comfy-settings')) {
continue;
}
if (el.closest('.p-dialog, .comfy-modal, [role="dialog"], .p-sidebar, .comfy-settings')) continue;
return el;
}
const fallbacks = document.querySelectorAll('button, .bg-secondary-background');
for (const el of fallbacks) {
if (el.closest('.p-dialog, .comfy-modal, [role="dialog"], .p-sidebar, .comfy-settings')) {
continue;
}
if (el.closest('.p-dialog, .comfy-modal, [role="dialog"], .p-sidebar, .comfy-settings')) continue;
const text = (el.textContent || el.getAttribute("title") || "").toLowerCase();
if (text.includes("graph") || text.includes("workflow") || el.querySelector('[class*="sitemap"], [class*="workflow"]')) {
return el;
@@ -303,6 +308,32 @@ function findGraphButton() {
return null;
}
function syncErrorBadge(originalBtn, customBtn) {
if (!customBtn) return;
const stockDot = document.querySelector('.comfy-sidebar-hide-original-properties-btn ~ span, .comfy-sidebar-hide-original-properties-btn + span');
let customDot = customBtn.querySelector('.comfy-sidebar-error-dot');
if (stockDot) {
if (!customDot) {
customDot = document.createElement("span");
customDot.className = "comfy-sidebar-error-dot inline-flex items-center justify-center rounded-full bg-destructive-background text-white size-2 absolute -top-1 -right-1";
customBtn.appendChild(customDot);
}
} else if (customDot) {
customDot.remove();
}
}
function cleanHTML(html) {
if (!html) return "";
const tempDiv = document.createElement("div");
tempDiv.innerHTML = html;
const tempDot = tempDiv.querySelector('[class*="destructive"]');
if (tempDot) tempDot.remove();
return tempDiv.innerHTML;
}
export function syncClassicLayout() {
if (domObserver) domObserver.disconnect();
@@ -313,13 +344,9 @@ export function syncClassicLayout() {
const graphBtn = findGraphButton();
if (graphBtn) {
if (hideGraphBtn) {
if (graphBtn.style.display !== "none") {
graphBtn.style.setProperty("display", "none", "important");
}
if (graphBtn.style.display !== "none") graphBtn.style.setProperty("display", "none", "important");
} else {
if (graphBtn.style.display === "none") {
graphBtn.style.removeProperty("display");
}
if (graphBtn.style.display === "none") graphBtn.style.removeProperty("display");
}
}
@@ -335,29 +362,23 @@ export function syncClassicLayout() {
container.appendChild(extensionsPanel);
extensionsPanel.classList.add("comfy-sidebar-extensions-override");
}
} else {
if (extensionsPanel._originalParent && extensionsPanel.parentNode !== extensionsPanel._originalParent) {
extensionsPanel._originalParent.insertBefore(extensionsPanel, extensionsPanel._originalNextSibling || null);
extensionsPanel.classList.remove("comfy-sidebar-extensions-override");
}
} else if (extensionsPanel._originalParent && extensionsPanel.parentNode !== extensionsPanel._originalParent) {
extensionsPanel._originalParent.insertBefore(extensionsPanel, extensionsPanel._originalNextSibling || null);
extensionsPanel.classList.remove("comfy-sidebar-extensions-override");
}
}
if (!isClassicLayoutEnabled) {
const originalBtn = findOriginalButton();
if (originalBtn && originalBtn.classList.contains("comfy-sidebar-hide-original-properties-btn")) {
if (originalBtn?.classList.contains("comfy-sidebar-hide-original-properties-btn")) {
originalBtn.classList.remove("comfy-sidebar-hide-original-properties-btn");
}
const customBtn = document.querySelector(".comfy-sidebar-custom-properties-toggle");
if (customBtn) {
customBtn.remove();
}
if (customBtn) customBtn.remove();
const indicator = findActiveQueueIndicator();
if (indicator && indicator.style.display === "none") {
indicator.style.removeProperty("display");
}
if (indicator && indicator.style.display === "none") indicator.style.removeProperty("display");
updateSidebarTabsVisibility();
return;
@@ -387,9 +408,8 @@ export function syncClassicLayout() {
if (!customBtn && savedButtonData && container) {
customBtn = document.createElement(savedButtonData.tagName);
customBtn.className = savedButtonData.className + " comfy-sidebar-custom-properties-toggle";
customBtn.innerHTML = savedButtonData.innerHTML;
customBtn.innerHTML = cleanHTML(savedButtonData.innerHTML);
for (const attr of savedButtonData.attributes) {
if (attr.name !== "class" && attr.name !== "id" && attr.name !== "style") {
@@ -397,28 +417,44 @@ export function syncClassicLayout() {
}
}
Object.assign(customBtn.style, {
position: "relative",
zIndex: "1000",
margin: "0",
boxSizing: "border-box",
opacity: "1",
pointerEvents: "auto",
display: "inline-flex"
});
customBtn.addEventListener("click", (e) => {
e.preventDefault();
e.stopPropagation();
const nativeBtn = findOriginalButton();
if (nativeBtn) nativeBtn.click();
if (nativeBtn) {
nativeBtn.click();
requestAnimationFrame(() => syncClassicLayout());
}
});
container.appendChild(customBtn);
}
if (customBtn) {
if (originalBtn) {
const cleanedHTML = cleanHTML(originalBtn.innerHTML);
if (cleanedHTML && customBtn.innerHTML !== cleanedHTML) {
const existingDot = customBtn.querySelector('.comfy-sidebar-error-dot');
customBtn.innerHTML = cleanedHTML;
if (existingDot) customBtn.appendChild(existingDot);
}
const titleVal = originalBtn.getAttribute("title") || originalBtn.getAttribute("aria-label") || "Toggle properties panel";
customBtn.setAttribute("title", titleVal);
customBtn.setAttribute("aria-label", titleVal);
for (const cls of originalBtn.classList) {
if (cls !== "comfy-sidebar-hide-original-properties-btn" && !customBtn.classList.contains(cls)) {
customBtn.classList.add(cls);
}
}
syncErrorBadge(originalBtn, customBtn);
} else if (!customBtn.getAttribute("title")) {
customBtn.setAttribute("title", "Toggle properties panel");
customBtn.setAttribute("aria-label", "Toggle properties panel");
}
customBtn.classList.toggle("comfy-panel-open", openState);
customBtn.style.display = "inline-flex";
}
@@ -427,13 +463,9 @@ export function syncClassicLayout() {
const indicator = findActiveQueueIndicator();
if (indicator) {
if (hideQueueIndicator) {
if (indicator.style.display !== "none") {
indicator.style.setProperty("display", "none", "important");
}
} else {
if (indicator.style.display === "none") {
indicator.style.removeProperty("display");
}
if (indicator.style.display !== "none") indicator.style.setProperty("display", "none", "important");
} else if (indicator.style.display === "none") {
indicator.style.removeProperty("display");
}
}
@@ -459,8 +491,15 @@ export function setupPropertiesPanelToggleFix() {
.comfy-sidebar-hide-original-properties-btn {
display: none !important;
}
/* Suppress stock red dot ONLY when original button is hidden by custom layout */
.comfy-sidebar-hide-original-properties-btn ~ span,
.comfy-sidebar-hide-original-properties-btn + span {
display: none !important;
}
.comfy-sidebar-custom-properties-toggle {
order: 99999 !important;
position: relative !important;
overflow: visible !important;
margin-left: -8px !important;
margin-right: 0px !important;
display: inline-flex !important;
@@ -469,21 +508,16 @@ export function setupPropertiesPanelToggleFix() {
padding: 0 !important;
}
.comfy-sidebar-custom-properties-toggle.comfy-panel-open {
order: 99999 !important;
margin-left: 0px !important;
margin-right: 0px !important;
}
.comfy-sidebar-custom-properties-toggle > span.comfy-sidebar-error-dot {
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
padding: 0 !important;
}
`;
document.head.appendChild(style);
}
if (domObserver) {
domObserver.disconnect();
}
if (domObserver) domObserver.disconnect();
domObserver = new MutationObserver((mutations) => {
let shouldSync = false;
+31
View File
@@ -55,6 +55,37 @@ export function injectStyles() {
background-color: #991b1b !important;
color: #ffffff !important;
}
/* Optimized Card Action Buttons */
.comfy-sidebar-card-action-btn {
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
width: 32px !important;
height: 32px !important;
background-color: rgba(0, 0, 0, 0.75) !important;
color: #e2e8f0 !important;
font-size: 14px !important;
border-radius: 6px !important;
cursor: pointer !important;
transition: all 0.15s ease !important;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3) !important;
z-index: 20 !important;
transform: translateZ(0);
}
.comfy-sidebar-card-action-btn:hover {
background-color: rgba(0, 0, 0, 0.95) !important;
color: #ffffff !important;
}
/* Delete Confirmation Active State */
.comfy-sidebar-card-action-btn.confirm-delete,
.comfy-sidebar-card-action-btn.confirm-delete:hover {
background-color: #dc3545 !important;
color: #ffffff !important;
box-shadow: 0 0 8px rgba(220, 53, 69, 0.6) !important;
}
`;
document.head.appendChild(style);
}
+12 -44
View File
@@ -252,7 +252,6 @@ export function setupSidebarUI() {
State.cardStack.style.columnGap = cols > 1 ? "12px" : "0";
}).observe(State.sidebarContainer);
// Optimized: Early-exit timer updates when no tasks are currently active
setInterval(() => {
if (State.currentlyActivePromptId === null) return;
for (const [pid, state] of promptStates.entries()) {
@@ -514,25 +513,6 @@ export function renderDOM() {
const headerSearchIcon = State.sidebarContainer.querySelector(".pi-search");
const headerActions = State.sidebarContainer.querySelector(".pi-eraser")?.parentNode;
const styleActionBtn = (btn) => {
Object.assign(btn.style, {
display: "inline-flex",
alignItems: "center", justifyContent: "center",
width: "32px", height: "32px",
backgroundColor: "rgba(0, 0, 0, 0.75)",
color: "#e2e8f0",
fontSize: "14px",
borderRadius: "6px",
cursor: "pointer",
transition: "all 0.15s ease",
boxShadow: "0 1px 2px rgba(0, 0, 0, 0.3)",
zIndex: "20",
transform: "translateZ(0)"
});
btn.onmouseenter = () => { btn.style.backgroundColor = "rgba(0, 0, 0, 0.95)"; btn.style.color = "#fff"; };
btn.onmouseleave = () => { btn.style.backgroundColor = "rgba(0, 0, 0, 0.75)"; btn.style.color = "#e2e8f0"; };
};
if (State.activeSubmenuBatchImages) {
const batchInfo = State.activeSubmenuBatchImages;
@@ -590,9 +570,8 @@ export function renderDOM() {
});
const btnImg = document.createElement("span");
btnImg.className = "pi pi-image";
btnImg.className = "pi pi-image comfy-sidebar-card-action-btn";
btnImg.title = "Download Object";
styleActionBtn(btnImg);
hoverPanel.appendChild(btnImg);
const leftHoverPanel = document.createElement("div");
@@ -602,9 +581,8 @@ export function renderDOM() {
});
const btnFocus = document.createElement("span");
btnFocus.className = "pi pi-eye";
btnFocus.className = "pi pi-eye comfy-sidebar-card-action-btn";
btnFocus.title = "Show Node";
styleActionBtn(btnFocus);
leftHoverPanel.appendChild(btnFocus);
card.append(timerEl, dimEl, grid, p, hoverPanel, leftHoverPanel);
@@ -721,9 +699,8 @@ export function renderDOM() {
});
const btnImg = document.createElement("span");
btnImg.className = "pi pi-image";
btnImg.className = "pi pi-image comfy-sidebar-card-action-btn";
btnImg.title = "Download Object";
styleActionBtn(btnImg);
hoverPanel.appendChild(btnImg);
const leftHoverPanel = document.createElement("div");
@@ -733,9 +710,8 @@ export function renderDOM() {
});
const btnFocus = document.createElement("span");
btnFocus.className = "pi pi-eye";
btnFocus.className = "pi pi-eye comfy-sidebar-card-action-btn";
btnFocus.title = "Show Node";
styleActionBtn(btnFocus);
leftHoverPanel.appendChild(btnFocus);
card.append(timerEl, dimEl, grid, p, hoverPanel, leftHoverPanel);
@@ -843,9 +819,9 @@ export function renderDOM() {
flexDirection: "column", gap: "4px", zIndex: "20"
});
const btnImg = document.createElement("span"); btnImg.className = "pi pi-image"; btnImg.title = "Download Object"; styleActionBtn(btnImg);
const btnJson = document.createElement("span"); btnJson.className = "pi pi-file"; btnJson.title = "Download JSON"; styleActionBtn(btnJson);
const btnDel = document.createElement("span"); btnDel.className = "pi pi-trash"; btnDel.title = "Delete Card"; styleActionBtn(btnDel);
const btnImg = document.createElement("span"); btnImg.className = "pi pi-image comfy-sidebar-card-action-btn"; btnImg.title = "Download Object";
const btnJson = document.createElement("span"); btnJson.className = "pi pi-file comfy-sidebar-card-action-btn"; btnJson.title = "Download JSON";
const btnDel = document.createElement("span"); btnDel.className = "pi pi-trash comfy-sidebar-card-action-btn"; btnDel.title = "Delete Card";
const leftHoverPanel = document.createElement("div");
Object.assign(leftHoverPanel.style, {
@@ -854,14 +830,12 @@ export function renderDOM() {
});
const btnFocus = document.createElement("span");
btnFocus.className = "pi pi-eye";
btnFocus.className = "pi pi-eye comfy-sidebar-card-action-btn";
btnFocus.title = "Show Node";
styleActionBtn(btnFocus);
const leftHoverBtn = document.createElement("span");
leftHoverBtn.className = "pi pi-images";
leftHoverBtn.className = "pi pi-images comfy-sidebar-card-action-btn";
leftHoverBtn.title = "View all intermediate outputs";
styleActionBtn(leftHoverBtn);
leftHoverBtn.onclick = (ev) => {
ev.stopPropagation();
@@ -982,10 +956,7 @@ export function renderDOM() {
let deleteTimeout = null, isDeletePending = false;
const resetDeleteBtn = () => {
isDeletePending = false;
Object.assign(cardObj.btnDel.style, {
color: "#e2e8f0",
backgroundColor: "rgba(0, 0, 0, 0.75)"
});
cardObj.btnDel.classList.remove("confirm-delete");
cardObj.btnDel.title = "Delete Card";
if (deleteTimeout) { clearTimeout(deleteTimeout); deleteTimeout = null; }
};
@@ -994,12 +965,9 @@ export function renderDOM() {
ev.stopPropagation();
if (!isDeletePending) {
isDeletePending = true;
Object.assign(cardObj.btnDel.style, {
color: "#fff",
backgroundColor: "#dc3545"
});
cardObj.btnDel.classList.add("confirm-delete");
cardObj.btnDel.title = "Click again to confirm deletion";
deleteTimeout = setTimeout(resetDeleteBtn, 1000);
deleteTimeout = setTimeout(resetDeleteBtn, 1500);
} else {
resetDeleteBtn();
promptStates.delete(state.pid);
+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "comfy-sidebar"
description = "A custom queue sidebar and image comparison extension that reintroduces and improves a sidebar queue menu and unified compact topbar layout for ComfyUI."
version = "1.2.7"
version = "1.2.8"
license = { file = "LICENSE" }
dependencies = []