diff --git a/README.md b/README.md
index a40463a..f7c7b53 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@ A lightweight, high-performance ComfyUI extension that replaces the cluttered st
* A compact topbar layout displaying both workflow tabs and the control panel on a single unified top bar. See the 'Comfy Layout' toggle in the settings.
* Toggles to hide buttons you don't need from the side panel.
* **[NEW]** Preview compare slider - just click the result image preview in the sidebar, and then `Shift+Click` another to load it alongside the first one for pixel by pixel comparison.
-* **[NEW]** Prevent certain nodes from showing the output in the queue bar and intermediate results. `Ctrl+Q` to toggle.
+* **[NEW]** Prevent selected nodes from showing the output in the queue bar and intermediate results. `Ctrl+Q` to toggle.
@@ -33,8 +33,10 @@ Restart your ComfyUI server and refresh the browser tab.
## ⚙️ Configuration
-Additional tweaks are seamlessly integrated into the standard ComfyUI Settings panel. From there, you can adjust thresholds or fully replace the stock V2 panel by enabling options like **"Override Stock Job History Tab"**.
+See additional tweaks in ComfyUI settings menu > Comfy Sidebar. From there, you can adjust the layout or fully replace the stock V2 panel by enabling options like **"Comfy Layout"** and **"Replace the stock Job History sidebar with Comfy Queue"**.
## ⌨️ Shortcuts
* **`Q`**: Instantly toggles the sidebar panel open or closed. Safely ignores inputs if you are currently typing inside a text field, input area, or combo box.
+* **`Ctrl+Q on a selected node`**: Sets the node to be ignored in the sidebar panel results.
+* **`Click`** on a card to enter preview. **`Shift+Click`** on another card in preview to enter compare mode.
\ No newline at end of file
diff --git a/js/dragdrop.js b/js/dragdrop.js
index f8346a6..f2bbb06 100644
--- a/js/dragdrop.js
+++ b/js/dragdrop.js
@@ -1,4 +1,5 @@
import { app } from "/scripts/app.js";
+import { State } from "./state.js";
async function uploadDroppedImageToInput(imageObj) {
const src = `/view?filename=${encodeURIComponent(imageObj.filename)}&type=${imageObj.type || 'output'}&subfolder=${encodeURIComponent(imageObj.subfolder || '')}`;
@@ -26,6 +27,13 @@ export function setupDragAndDrop() {
});
document.addEventListener("drop", async (e) => {
+ // Cancel loading entirely if dropping inside the sidebar area
+ const isSidebarDrop = e.target.closest('.comfyui-sidebar, .comfy-sidebar, [class*="sidebar"]') ||
+ (State.sidebarContainer && State.sidebarContainer.contains(e.target));
+ if (isSidebarDrop) {
+ return;
+ }
+
const canvas = app.canvas;
if (!canvas || !canvas.graph) return;
diff --git a/js/queue.js b/js/queue.js
index b4d61eb..e72e75c 100644
--- a/js/queue.js
+++ b/js/queue.js
@@ -1,6 +1,6 @@
import { app } from "/scripts/app.js";
import { api } from "/scripts/api.js";
-import { State, promptStates, pruneHistory, loadStatesFromLocalStorage } from "./state.js";
+import { State, promptStates, pruneHistory, loadStatesFromLocalStorage, cardElements } from "./state.js";
import { findImagesInOutputs, findTextsInOutputs } from "./utils.js";
export let renderDOMFn = () => {};
@@ -203,11 +203,14 @@ export async function initSessionAndHistory() {
}
const storedSessionId = localStorage.getItem("comfy_sidebar_backend_session_id");
- if (backendSessionId && backendSessionId === storedSessionId) {
- loadStatesFromLocalStorage();
- } else {
+ if (backendSessionId && backendSessionId !== storedSessionId) {
+ // Session changed (server restarted)! Clear cached memory.
+ promptStates.clear();
+ cardElements.clear();
localStorage.removeItem("comfy_sidebar_prompt_states");
if (backendSessionId) localStorage.setItem("comfy_sidebar_backend_session_id", backendSessionId);
+ } else if (!storedSessionId && backendSessionId) {
+ localStorage.setItem("comfy_sidebar_backend_session_id", backendSessionId);
}
const historyData = await api.getHistory();
diff --git a/js/state.js b/js/state.js
index fd635ec..f872925 100644
--- a/js/state.js
+++ b/js/state.js
@@ -66,4 +66,7 @@ export function loadStatesFromLocalStorage() {
} catch (e) {
console.error("Comfy Sidebar: Failed to load state from localStorage", e);
}
-}
\ No newline at end of file
+}
+
+// Load instantly and synchronously on script import to prevent race conditions during early renders!
+loadStatesFromLocalStorage();
\ No newline at end of file
diff --git a/js/ui.js b/js/ui.js
index c5d9dc4..216163d 100644
--- a/js/ui.js
+++ b/js/ui.js
@@ -367,26 +367,32 @@ function renderCardImages(cardObj, state, keepAspect) {
mediaEl.onmouseenter = () => { playIcon.style.opacity = "0"; mediaEl.play().catch(()=>{}); };
mediaEl.onmouseleave = () => { playIcon.style.opacity = "1"; mediaEl.pause(); };
} else {
- mediaEl.setAttribute("draggable", "true");
- mediaEl.onload = () => {
- applyDimensions(mediaEl.naturalWidth, mediaEl.naturalHeight);
- };
+ const isUnfinished = state.status && state.status !== "completed";
+ if (isUnfinished) {
+ mediaEl.setAttribute("draggable", "false");
+ mediaEl.style.cursor = "grab";
+ mediaEl.removeEventListener("dragstart", mediaEl._currentDragStart);
+ delete mediaEl._currentDragStart;
+ } else {
+ mediaEl.setAttribute("draggable", "true");
+ mediaEl.style.cursor = "zoom-in";
+
+ const dragStartHandler = (e) => {
+ State.currentDraggedImgData = { ...img, workflow: state.workflow };
+ try {
+ e.dataTransfer.setData("text/uri-list", src);
+ e.dataTransfer.setData("text/plain", src);
+ } catch (err) {}
+ e.dataTransfer.effectAllowed = "copy";
+ e.stopPropagation();
+ };
+ mediaEl.removeEventListener("dragstart", mediaEl._currentDragStart);
+ mediaEl.addEventListener("dragstart", dragStartHandler);
+ mediaEl._currentDragStart = dragStartHandler;
+ }
const playIcon = wrapper.querySelector(".comfy-sidebar-play-icon");
if (playIcon) playIcon.remove();
-
- const dragStartHandler = (e) => {
- State.currentDraggedImgData = { ...img, workflow: state.workflow };
- try {
- e.dataTransfer.setData("text/uri-list", src);
- e.dataTransfer.setData("text/plain", src);
- } catch (err) {}
- e.dataTransfer.effectAllowed = "copy";
- e.stopPropagation();
- };
- mediaEl.removeEventListener("dragstart", mediaEl._currentDragStart);
- mediaEl.addEventListener("dragstart", dragStartHandler);
- mediaEl._currentDragStart = dragStartHandler;
}
// Smooth in-memory source swapping for blob live previews
@@ -924,9 +930,10 @@ export function renderDOM() {
});
card.addEventListener("dragstart", (e) => {
- // Custom dragstart is only used for JSON-only workflows to supply a local DownloadURL.
- // For image cards, dragging is handled natively by the browser on the child
tag.
- if (state.workflow && (!state.images || state.images.length === 0)) {
+ // Custom dragstart is used for JSON-only or unfinished workflows to supply a local DownloadURL.
+ // For completed image cards, dragging is handled natively by the browser on the child
tag.
+ const isUnfinished = state.status && state.status !== "completed";
+ if (state.workflow && (!state.images || state.images.length === 0 || isUnfinished)) {
if (cardObj.firstImgElement) e.dataTransfer.setDragImage(cardObj.firstImgElement, 15, 15);
const jsonStr = JSON.stringify(state.workflow, null, 2);
try {
@@ -943,9 +950,10 @@ export function renderDOM() {
cardObj.element.className = `comfy-sidebar-card ${state.status}`;
- // Native drag isolation: completely remove draggable flags on image containers.
+ // Native drag isolation: completely remove draggable flags on image containers for finished cards.
// This exposes the child
tag directly to Chrome's native filesystem drag APIs.
- if (state.images && state.images.length > 0) {
+ const isUnfinished = state.status && state.status !== "completed";
+ if (state.images && state.images.length > 0 && !isUnfinished) {
cardObj.element.removeAttribute("draggable");
} else {
cardObj.element.setAttribute("draggable", "true");