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 <noreply@anthropic.com>
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
__pycache__/
|
||||
*.pyc
|
||||
10
__init__.py
Normal file
10
__init__.py
Normal file
@@ -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 = {}
|
||||
198
js/queue_toggle.js
Normal file
198
js/queue_toggle.js
Normal file
@@ -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");
|
||||
},
|
||||
});
|
||||
13
pyproject.toml
Normal file
13
pyproject.toml
Normal file
@@ -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 = ""
|
||||
Reference in New Issue
Block a user