From b022a09097ae06a47fd5c5810536358e288229ac Mon Sep 17 00:00:00 2001 From: Ethanfel Date: Tue, 24 Feb 2026 13:22:55 +0100 Subject: [PATCH] Initial release: toggle button for Run/Run (Instant) mode switching Adds a one-click toggle button next to the queue button in the ComfyUI action bar, restoring easy access to instant queue mode toggling. Co-Authored-By: Claude Opus 4.6 --- .gitignore | 2 + __init__.py | 10 +++ js/queue_toggle.js | 198 +++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 13 +++ 4 files changed, 223 insertions(+) create mode 100644 .gitignore create mode 100644 __init__.py create mode 100644 js/queue_toggle.js create mode 100644 pyproject.toml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7a60b85 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +__pycache__/ +*.pyc diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..46df78a --- /dev/null +++ b/__init__.py @@ -0,0 +1,10 @@ +""" +Return Queue Logic - ComfyUI Extension + +Adds a toggle button to quickly switch between Run and Run (Instant) modes +without needing to open the queue button dropdown menu. +""" + +WEB_DIRECTORY = "./js" +NODE_CLASS_MAPPINGS = {} +NODE_DISPLAY_NAME_MAPPINGS = {} diff --git a/js/queue_toggle.js b/js/queue_toggle.js new file mode 100644 index 0000000..8ed4be3 --- /dev/null +++ b/js/queue_toggle.js @@ -0,0 +1,198 @@ +/** + * Return Queue Logic - Toggle Button Extension + * + * Adds a toggle button next to the queue button to quickly switch + * between "Run" (disabled) and "Run (Instant)" modes without + * opening the dropdown menu. + */ + +import { app } from "../../scripts/app.js"; + +const EXTENSION_NAME = "ReturnQueueLogic"; +const BUTTON_ID = "queue-instant-toggle-btn"; +const DEBUG = false; + +function debugLog(...args) { + if (DEBUG) console.log(`[${EXTENSION_NAME}]`, ...args); +} + +let cachedStore = null; + +function getQueueStore() { + if (cachedStore) return cachedStore; + + const vueApp = document.getElementById("vue-app")?.__vue_app__; + if (!vueApp) { + debugLog("Vue app not found"); + return null; + } + const pinia = vueApp.config.globalProperties.$pinia; + if (!pinia) { + debugLog("Pinia not found"); + return null; + } + const store = pinia._s.get("queueSettingsStore"); + if (!store) { + debugLog("Queue settings store not found"); + return null; + } + cachedStore = store; + return store; +} + +function createToggleButton(store) { + const btn = document.createElement("button"); + btn.id = BUTTON_ID; + + Object.assign(btn.style, { + display: "inline-flex", + alignItems: "center", + justifyContent: "center", + border: "none", + cursor: "pointer", + padding: "4px 8px", + borderRadius: "4px", + fontSize: "12px", + fontWeight: "600", + fontFamily: "system-ui, -apple-system, sans-serif", + lineHeight: "1", + transition: "background 0.15s ease, color 0.15s ease", + whiteSpace: "nowrap", + userSelect: "none", + height: "30px", + marginLeft: "4px", + }); + + btn.addEventListener("click", (e) => { + e.preventDefault(); + e.stopPropagation(); + + if (store.mode === "instant") { + store.mode = "disabled"; + } else { + store.mode = "instant"; + } + }); + + // Set initial state + applyButtonState(btn, store.mode); + + return btn; +} + +function applyButtonState(btn, mode) { + const isInstant = mode === "instant"; + + if (isInstant) { + btn.textContent = "Instant: ON"; + btn.title = "Click to switch to Run (normal queue)"; + Object.assign(btn.style, { + background: "#e67e22", + color: "#fff", + }); + } else { + btn.textContent = "Instant: OFF"; + btn.title = "Click to switch to Run (Instant)"; + Object.assign(btn.style, { + background: "#3a3a3a", + color: "#aaa", + }); + } +} + +let storeUnsubscribe = null; + +function injectToggleButton() { + if (document.getElementById(BUTTON_ID)) { + debugLog("Button already exists"); + return; + } + + const store = getQueueStore(); + if (!store) { + debugLog("Store not available yet, deferring injection"); + return; + } + + const queueButton = document.querySelector( + '[data-testid="queue-button"]' + ); + if (!queueButton) { + debugLog("Queue button not found"); + return; + } + + const buttonGroup = queueButton.closest(".queue-button-group"); + const insertTarget = buttonGroup || queueButton.parentElement; + if (!insertTarget?.parentElement) { + debugLog("No valid parent to insert into"); + return; + } + + const btn = createToggleButton(store); + insertTarget.parentElement.insertBefore(btn, insertTarget.nextSibling); + + // Clean up previous subscription if any (e.g. button was removed and re-injected) + if (storeUnsubscribe) { + storeUnsubscribe(); + storeUnsubscribe = null; + } + + // Subscribe reactively to store changes so the button stays in sync + // when the user changes mode via the dropdown menu. + storeUnsubscribe = store.$subscribe((_mutation, state) => { + const existing = document.getElementById(BUTTON_ID); + if (existing) { + applyButtonState(existing, state.mode); + } + }); + + debugLog("Toggle button injected"); +} + +function setupObserver() { + let pending = false; + + const observer = new MutationObserver(() => { + // Debounce: skip if we already have a pending check + if (pending) return; + // Fast check: skip if button already exists + if (document.getElementById(BUTTON_ID)) return; + + pending = true; + requestAnimationFrame(() => { + pending = false; + if (!document.getElementById(BUTTON_ID)) { + injectToggleButton(); + } + }); + }); + + observer.observe(document.body, { + childList: true, + subtree: true, + }); + + debugLog("MutationObserver set up"); + + // Try immediately + injectToggleButton(); + + // Retry after delays as fallback (Vue/Pinia may not be ready yet) + setTimeout(() => { + if (!document.getElementById(BUTTON_ID)) injectToggleButton(); + }, 2000); + setTimeout(() => { + if (!document.getElementById(BUTTON_ID)) injectToggleButton(); + }, 5000); +} + +app.registerExtension({ + name: "Comfy.ReturnQueueLogic", + + async setup() { + debugLog("Setting up Return Queue Logic extension"); + setupObserver(); + debugLog("Setup complete"); + }, +}); diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..22a7404 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,13 @@ +[project] +name = "comfyui-return-run-logic" +description = "Adds a toggle button to quickly switch between Run and Run (Instant) modes without opening the queue button dropdown menu." +version = "1.0.0" +license = {text = "MIT"} + +[project.urls] +Repository = "https://github.com/ethanfel/Comfyui-Return-Run-Logic" + +[tool.comfy] +PublisherId = "ethanfel" +DisplayName = "Comfyui-Return-Run-Logic" +Icon = ""