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