From d4bc6b184939895df1ad1ee94eb0d5ba2eaa0d55 Mon Sep 17 00:00:00 2001 From: m0rtus59 Date: Sun, 27 Sep 2026 22:28:51 +0500 Subject: [PATCH] Improved tab stacking layout. Restored 'avatar' button with an optional toggle --- js/comfy/layout.js | 166 ++++++++++++++++++++++++++++++--------------- js/sidebar.js | 10 +++ 2 files changed, 122 insertions(+), 54 deletions(-) diff --git a/js/comfy/layout.js b/js/comfy/layout.js index 6989c1a..b081c2c 100644 --- a/js/comfy/layout.js +++ b/js/comfy/layout.js @@ -18,33 +18,46 @@ setRuntimePreviewStateListener(() => { const STYLE_ID = "comfy-sidebar-classic-layout-override"; const CLASSIC_LAYOUT_CSS_MEDIA = ` +/* Neutralize the leftover floating shell box to prevent empty dark square artifacts */ .actionbar-container, .shadow-interface.rounded-lg.bg-comfy-menu-bg, .shadow-interface.rounded-lg.border-interface-stroke, .shadow-interface.border.rounded-lg:has([class*="actionbar-buttons"]), .shadow-interface.border.rounded-lg:has(.actionbar-buttons), -div.border-interface-stroke.rounded-lg:has([class*="actionbar-buttons"]) { +div.border-interface-stroke.rounded-lg:has([class*="actionbar-buttons"]), +div.shadow-interface.py-1\\.75 { border: none !important; background: transparent !important; box-shadow: none !important; } +/* Dock actionbar buttons in the topbar row with matching dark background and seamless border */ [class*="actionbar"]:not(.actionbar), [class*="actionbar-buttons"], .actionbar-buttons { position: fixed !important; - top: 3px !important; - right: 4px !important; + top: 0 !important; + right: 0 !important; left: auto !important; + height: var(--topbar-height, 40px) !important; transform: none !important; z-index: 1010 !important; - background: transparent !important; + background: var(--comfy-menu-bg, #181818) !important; border: none !important; + border-bottom: 1px solid var(--interface-stroke, var(--border-color, rgba(255, 255, 255, 0.12))) !important; box-shadow: none !important; - padding: 0 !important; + border-radius: 0 !important; + padding: 0 8px !important; margin: 0 !important; display: flex !important; align-items: center !important; + box-sizing: border-box !important; +} + +/* Constrain tabs boundary directly to the start of the action bar */ +.workflow-tabs-container { + padding-right: var(--actionbar-width, 240px) !important; + box-sizing: border-box !important; } .comfy-sidebar-extensions-override { @@ -67,51 +80,6 @@ div.border-interface-stroke.rounded-lg:has([class*="actionbar-buttons"]) { display: inline-flex !important; align-items: center !important; } - -img[alt*="User Avatar"], -img[alt*="user avatar"], -button:has(img[alt*="User Avatar"]), -[role="button"]:has(img[alt*="User Avatar"]), -button:has(img[alt*="user avatar"]), -[role="button"]:has(img[alt*="user avatar"]), -.p-avatar, -[data-pc-name="avatar"], -button:has(.p-avatar), -[role="button"]:has(.p-avatar), -button:has([data-pc-name="avatar"]), -[role="button"]:has([data-pc-name="avatar"]) { - display: none !important; -} - -.p-tabview-nav-content, -[class*="tabview"] { - padding-left: 150px !important; - padding-right: 580px !important; -} - -@media (max-width: 1599px) { - .p-tabview-nav-content, - [class*="tabview"] { - padding-left: 100px !important; - padding-right: 420px !important; - } -} - -@media (max-width: 1199px) { - .p-tabview-nav-content, - [class*="tabview"] { - padding-left: 40px !important; - padding-right: 280px !important; - } -} - -@media (max-width: 899px) { - .p-tabview-nav-content, - [class*="tabview"] { - padding-left: 10px !important; - padding-right: 180px !important; - } -} `; export function syncStockHistoryAndProgressSettings(enable) { @@ -133,6 +101,39 @@ export function syncStockHistoryAndProgressSettings(enable) { try { window.dispatchEvent(new Event("storage")); } catch (e) {} } +let savedButtonData = null; +let domObserver = null; +let syncScheduled = false; +let actionbarResizeObserver = null; + +function updateTopMetrics() { + const bar = document.querySelector('.actionbar-buttons, [class*="actionbar-buttons"]') || findTopbarContainer(); + const tabsEl = document.querySelector('.workflow-tabs-container'); + const topbar = tabsEl?.closest('header, .topbar, [class*="topbar"]') || tabsEl; + + if (bar) { + const rect = bar.getBoundingClientRect(); + const width = Math.ceil(rect.width); + if (width > 80 && width < window.innerWidth * 0.8) { + document.documentElement.style.setProperty('--actionbar-width', `${Math.round(width / 2)}px`); + } + + if (window.ResizeObserver && !actionbarResizeObserver) { + actionbarResizeObserver = new ResizeObserver(() => { + updateTopMetrics(); + }); + actionbarResizeObserver.observe(bar); + } + } + + if (topbar) { + const height = Math.round(topbar.getBoundingClientRect().height); + if (height > 20) { + document.documentElement.style.setProperty('--topbar-height', `${height}px`); + } + } +} + export function applyClassicLayout(enable, updateSetting = false) { let styleEl = document.getElementById(STYLE_ID); @@ -150,8 +151,19 @@ export function applyClassicLayout(enable, updateSetting = false) { document.head.appendChild(styleEl); } styleEl.textContent = CLASSIC_LAYOUT_CSS_MEDIA; + updateTopMetrics(); + window.removeEventListener("resize", updateTopMetrics); + window.addEventListener("resize", updateTopMetrics); } else { if (styleEl) styleEl.remove(); + if (actionbarResizeObserver) { + actionbarResizeObserver.disconnect(); + actionbarResizeObserver = null; + } + window.removeEventListener("resize", updateTopMetrics); + document.documentElement.style.removeProperty('--actionbar-width'); + document.documentElement.style.removeProperty('--topbar-height'); + if (updateSetting) { if (app.extensionManager?.setting) { app.extensionManager.setting.set("Comfy.Workflow.WorkflowTabsPosition", "Sidebar"); @@ -162,10 +174,6 @@ export function applyClassicLayout(enable, updateSetting = false) { } } -let savedButtonData = null; -let domObserver = null; -let syncScheduled = false; - function updateSidebarTabsVisibility() { const sidebar = document.querySelector('.comfyui-sidebar, .comfy-sidebar, .sidebar, [class*="sidebar-nav"], [class*="sidebar"]'); if (!sidebar) return; @@ -242,6 +250,41 @@ function syncGraphButton() { } } +function syncAvatarVisibility() { + const hideAvatar = app.ui?.settings?.getSettingValue("Comfy Sidebar.Hide Junk.Avatar") ?? false; + const STYLE_AVATAR_ID = "comfy-sidebar-hide-avatar-override"; + let styleEl = document.getElementById(STYLE_AVATAR_ID); + + if (hideAvatar) { + if (!styleEl) { + styleEl = document.createElement("style"); + styleEl.id = STYLE_AVATAR_ID; + styleEl.textContent = ` + img[alt*="User Avatar"], + img[alt*="user avatar"], + button:has(img[alt*="User Avatar"]), + [role="button"]:has(img[alt*="User Avatar"]), + button:has(img[alt*="user avatar"]), + [role="button"]:has(img[alt*="user avatar"]), + .p-avatar, + [data-pc-name="avatar"], + button:has(.p-avatar), + [role="button"]:has(.p-avatar), + button:has([data-pc-name="avatar"]), + [role="button"]:has([data-pc-name="avatar"]), + button:has([class*="lucide--user"]), + button:has(svg.lucide-user), + [data-testid="user-profile-button"] { + display: none !important; + } + `; + document.head.appendChild(styleEl); + } + } else { + if (styleEl) styleEl.remove(); + } +} + function syncExtensionsPanel(isClassicLayoutEnabled) { const extensionsPanel = findNativeExtensionsPanel(); if (!extensionsPanel) return; @@ -448,7 +491,12 @@ export function syncClassicLayout() { try { const isClassicLayoutEnabled = app.ui?.settings?.getSettingValue("Comfy Sidebar.Comfy Layout") ?? false; + if (isClassicLayoutEnabled) { + updateTopMetrics(); + } + syncGraphButton(); + syncAvatarVisibility(); syncExtensionsPanel(isClassicLayoutEnabled); syncPropertiesButton(isClassicLayoutEnabled); syncActiveQueueIndicator(isClassicLayoutEnabled); @@ -545,8 +593,18 @@ export function destroyLayoutFix() { domObserver.disconnect(); domObserver = null; } + if (actionbarResizeObserver) { + actionbarResizeObserver.disconnect(); + actionbarResizeObserver = null; + } + window.removeEventListener("resize", updateTopMetrics); + document.documentElement.style.removeProperty('--actionbar-width'); + document.documentElement.style.removeProperty('--topbar-height'); + const styleEl = document.getElementById(STYLE_ID); if (styleEl) styleEl.remove(); const fixStyles = document.getElementById("comfy-sidebar-layout-fix-styles"); if (fixStyles) fixStyles.remove(); + const avatarStyles = document.getElementById("comfy-sidebar-hide-avatar-override"); + if (avatarStyles) avatarStyles.remove(); } \ No newline at end of file diff --git a/js/sidebar.js b/js/sidebar.js index b9e6037..58b2a1e 100644 --- a/js/sidebar.js +++ b/js/sidebar.js @@ -87,6 +87,16 @@ function registerSettings() { } }); + app.ui.settings.addSetting({ + id: "Comfy Sidebar.Hide Junk.Avatar", + name: "Hide Avatar", + type: "boolean", + defaultValue: false, + onChange: () => { + setTimeout(() => { syncClassicLayout(); }, 0); + } + }); + app.ui.settings.addSetting({ id: SettingIds.CLASSIC_LAYOUT, name: "Places the controls and the open workflow tabs on a single unified top bar",