From 8189859fa3ec891500daa63e8e865d02636e13ed Mon Sep 17 00:00:00 2001
From: Raykosan <71210053+Raykosan@users.noreply.github.com>
Date: Fri, 1 May 2026 18:38:48 +0300
Subject: [PATCH] A switch has been added that allows you to enable/disable
external text input with a single mouse click.
---
Rayko_Prompts.py | 14 ++--
package.json | 2 +-
pyproject.toml | 2 +-
web/rs_prompts.js | 158 ++++++++++++++++++++++++++++++++++++----------
4 files changed, 136 insertions(+), 40 deletions(-)
diff --git a/Rayko_Prompts.py b/Rayko_Prompts.py
index 2401e5c..859409a 100644
--- a/Rayko_Prompts.py
+++ b/Rayko_Prompts.py
@@ -24,6 +24,7 @@ class RSPrompts:
},
"optional": {
"text_input": ("STRING", {"forceInput": True}),
+ "disable_text_input": ("BOOLEAN", {"default": False, "label": "π Disable text input"}),
"pause_for_edit": ("BOOLEAN", {"default": False, "label": "βΈοΈ Pause for manual edit"}),
},
"hidden": {
@@ -37,17 +38,22 @@ class RSPrompts:
CATEGORY = "π¦ RaykoStudio"
DESCRIPTION = "Text encoder with visual prompt controls and pause-for-edit mode."
- def encode_prompts(self, clip, pause_for_edit=False, text="", text_input=None, unique_id=None):
- current_text = text_input if text_input is not None else text
+ def encode_prompts(self, clip, disable_text_input=False, pause_for_edit=False, text="", text_input=None, unique_id=None):
+ if disable_text_input:
+ current_text = text
+ effective_text_input = None
+ else:
+ current_text = text_input if text_input is not None else text
+ effective_text_input = text_input
- if text_input is not None and not pause_for_edit and unique_id:
+ if effective_text_input is not None and not pause_for_edit and unique_id:
from server import PromptServer
PromptServer.instance.send_sync("rs.prompt.update", {
"node_id": unique_id,
"prompt": current_text
})
- if pause_for_edit and text_input is not None and current_text and unique_id:
+ if pause_for_edit and effective_text_input is not None and effective_text_input and unique_id:
print(f"[RS Prompts π¦] Pause mode enabled for node {unique_id}")
PENDING_PROMPTS[unique_id] = {
diff --git a/package.json b/package.json
index 1d2d0a0..a850ab9 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "ComfyUI_RaykoStudio",
- "version": "0.17.4",
+ "version": "0.17.5",
"description": "Set of custom nodes for ComfyUI providing additional image processing capabilities",
"author": "Raykosan",
"license": "MIT",
diff --git a/pyproject.toml b/pyproject.toml
index 167db36..6b2398c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,7 @@
[project]
name = "ComfyUI_RaykoStudio"
description = "Set of custom nodes for ComfyUI providing additional image processing capabilities"
-version = "0.17.4"
+version = "0.17.5"
license = { file = "LICENSE" }
dependencies = ["torch>=1.7.0", "numpy>=1.19.0", "Pillow>=8.0.0", "freetype-py>=2.5.1"]
diff --git a/web/rs_prompts.js b/web/rs_prompts.js
index f403d49..022463f 100644
--- a/web/rs_prompts.js
+++ b/web/rs_prompts.js
@@ -33,7 +33,7 @@ styleBlock.innerHTML = `
display: inline-flex;
align-items: center;
cursor: pointer;
- gap: 8px;
+ gap: 6px;
}
.rs-toggle-switch input {
opacity: 0;
@@ -44,18 +44,18 @@ styleBlock.innerHTML = `
.rs-toggle-slider {
position: relative;
display: inline-block;
- width: 40px;
- height: 20px;
+ width: 36px;
+ height: 18px;
background-color: #444;
- border-radius: 20px;
+ border-radius: 18px;
transition: 0.3s;
cursor: pointer;
}
.rs-toggle-slider:before {
position: absolute;
content: "";
- height: 16px;
- width: 16px;
+ height: 14px;
+ width: 14px;
left: 2px;
bottom: 2px;
background-color: white;
@@ -66,7 +66,7 @@ styleBlock.innerHTML = `
background-color: #28a745;
}
input:checked + .rs-toggle-slider:before {
- transform: translateX(20px);
+ transform: translateX(18px);
}
.rs-toggle-label {
font-size: 11px;
@@ -108,11 +108,16 @@ app.registerExtension({
const textWidget = node.widgets?.find(w => w.name === "text");
const pauseWidget = node.widgets?.find(w => w.name === "pause_for_edit");
+ const disableWidget = node.widgets?.find(w => w.name === "disable_text_input");
if (pauseWidget) {
pauseWidget.hidden = true;
}
+ if (disableWidget) {
+ disableWidget.hidden = true;
+ }
+
if (textWidget) textWidget.hidden = true;
const enableWidgets = () => {
@@ -127,6 +132,14 @@ app.registerExtension({
node.onConnectionsChange = function(slotType, slotIndex, isConnected, link, linkInfo) {
if (origOnConnectionsChange) origOnConnectionsChange.apply(this, arguments);
setTimeout(enableWidgets, 50);
+ setTimeout(() => updateStatusIndicator(), 10);
+ setTimeout(() => {
+ if (pauseModeEnabled && !canPauseBeActive()) {
+ pauseModeEnabled = false;
+ if (pauseWidget) pauseWidget.value = false;
+ updateUIForPauseMode(false);
+ }
+ }, 10);
};
let waitingOverlay = null;
@@ -158,7 +171,20 @@ app.registerExtension({
const root = mkEl("div", "display:flex;flex-direction:column;height:100%;padding:0;margin:0;box-sizing:border-box;overflow:hidden;position:relative;");
- const controlBar = mkEl("div", "display:flex;align-items:center;justify-content:space-between;padding:6px 8px;background:#1a1a1a;border-bottom:1px solid #333;gap:8px;");
+ const disableRow = mkEl("div", "display:flex;align-items:center;justify-content:flex-start;padding:4px 6px 2px 6px;background:#1a1a1a;");
+ const disableToggleContainer = mkEl("label", "rs-toggle-switch");
+ const disableToggleInput = document.createElement("input");
+ disableToggleInput.type = "checkbox";
+ const disableToggleSlider = mkEl("span", "rs-toggle-slider");
+ const disableToggleLabel = mkEl("span", "rs-toggle-label");
+ disableToggleLabel.textContent = "π Disable text input";
+
+ disableToggleContainer.appendChild(disableToggleInput);
+ disableToggleContainer.appendChild(disableToggleSlider);
+ disableToggleContainer.appendChild(disableToggleLabel);
+ disableRow.appendChild(disableToggleContainer);
+
+ const pauseRow = mkEl("div", "display:flex;align-items:center;justify-content:space-between;padding:2px 6px 4px 6px;background:#1a1a1a;border-bottom:1px solid #333;gap:4px;");
const toggleContainer = mkEl("label", "rs-toggle-switch");
const toggleInput = document.createElement("input");
@@ -171,12 +197,14 @@ app.registerExtension({
toggleContainer.appendChild(toggleSlider);
toggleContainer.appendChild(toggleLabel);
- const statusIndicator = mkEl("div", "font-size:10px;padding:2px 6px;border-radius:4px;background:#2a2a2a;color:#888;");
- statusIndicator.textContent = "β‘ Normal mode";
+ const statusIndicator = mkEl("div", "font-size:10px;padding:2px 6px;border-radius:4px;background:#2a2a2a;color:#ccc;");
+ statusIndicator.textContent = "π Local prompt";
- controlBar.appendChild(toggleContainer);
- controlBar.appendChild(statusIndicator);
- root.appendChild(controlBar);
+ pauseRow.appendChild(toggleContainer);
+ pauseRow.appendChild(statusIndicator);
+
+ root.appendChild(disableRow);
+ root.appendChild(pauseRow);
const customTextarea = document.createElement("textarea");
customTextarea.className = "rs-custom-textarea";
@@ -258,6 +286,37 @@ app.registerExtension({
if (size[1] < 350) size[1] = 350;
};
+ const hasTextInputConnection = () => {
+ return node.inputs?.some(i => i.name === "text_input" && i.link !== null) || false;
+ };
+
+ const canPauseBeActive = () => {
+ const hasConnection = hasTextInputConnection();
+ const isDisabled = disableToggleInput.checked;
+ return hasConnection && !isDisabled;
+ };
+
+ const updateStatusIndicator = () => {
+ if (pauseModeEnabled) {
+ statusIndicator.innerHTML = `βΈοΈ WAITING FOR EDIT`;
+ statusIndicator.style.background = "#1a3a1a";
+ return;
+ }
+
+ const hasConnection = hasTextInputConnection();
+ const isDisabled = disableToggleInput.checked;
+
+ if (!hasConnection) {
+ statusIndicator.innerHTML = `π Local prompt`;
+ } else if (hasConnection && !isDisabled) {
+ statusIndicator.innerHTML = `π External input`;
+ } else if (hasConnection && isDisabled) {
+ statusIndicator.innerHTML = `π Local prompt`;
+ }
+
+ statusIndicator.style.background = "#2a2a2a";
+ };
+
const updateUIForPauseMode = (isPaused) => {
if (isPaused) {
clearBtn.disabled = true;
@@ -278,8 +337,8 @@ app.registerExtension({
rejectEditBtn.style.cursor = "pointer";
customTextarea.style.border = "2px solid #28a745";
- statusIndicator.innerHTML = "βΈοΈ WAITING FOR EDIT";
- statusIndicator.style.background = "#1a3a1a";
+
+ updateStatusIndicator();
showWaitingOverlay();
} else {
@@ -301,25 +360,57 @@ app.registerExtension({
rejectEditBtn.style.cursor = "not-allowed";
customTextarea.style.border = "1px solid #444";
- statusIndicator.innerHTML = "β‘ Active";
- statusIndicator.style.background = "#2a2a2a";
+
+ updateStatusIndicator();
+
removeWaitingOverlay();
}
};
+ if (disableWidget) {
+ disableToggleInput.checked = disableWidget.value;
+
+ if (disableToggleInput.checked) {
+ disableToggleLabel.textContent = "π΄ Disable text input";
+ } else {
+ disableToggleLabel.textContent = "π Disable text input";
+ }
+
+ updateStatusIndicator();
+
+ disableToggleInput.addEventListener("change", (e) => {
+ disableWidget.value = e.target.checked;
+ if (disableWidget.callback) disableWidget.callback(e.target.checked);
+
+ if (e.target.checked) {
+ disableToggleLabel.textContent = "π΄ Disable text input";
+ } else {
+ disableToggleLabel.textContent = "π Disable text input";
+ }
+
+ if (pauseModeEnabled && !canPauseBeActive()) {
+ pauseModeEnabled = false;
+ if (pauseWidget) pauseWidget.value = false;
+ toggleInput.checked = false;
+ updateUIForPauseMode(false);
+ }
+
+ updateStatusIndicator();
+
+ node.graph?.setDirtyCanvas(true, true);
+ });
+ }
+
+ if (pauseWidget) {
+ toggleInput.checked = pauseWidget.value;
+ }
+
toggleInput.addEventListener("change", (e) => {
- pauseModeEnabled = e.target.checked;
if (pauseWidget) {
pauseWidget.value = e.target.checked;
if (pauseWidget.callback) pauseWidget.callback(e.target.checked);
}
- if (pauseModeEnabled) {
- statusIndicator.innerHTML = "π Pause ON";
- statusIndicator.style.background = "#3a2a1a";
- } else {
- statusIndicator.innerHTML = "β‘ Active";
- statusIndicator.style.background = "#2a2a2a";
- }
+ node.graph?.setDirtyCanvas(true, true);
});
acceptEditBtn.addEventListener("click", async () => {
@@ -332,6 +423,7 @@ app.registerExtension({
prompt: currentPrompt
})
});
+ pauseModeEnabled = false;
updateUIForPauseMode(false);
node.graph?.setDirtyCanvas(true, true);
});
@@ -344,6 +436,7 @@ app.registerExtension({
node_id: node.id.toString()
})
});
+ pauseModeEnabled = false;
updateUIForPauseMode(false);
node.graph?.setDirtyCanvas(true, true);
});
@@ -454,7 +547,12 @@ app.registerExtension({
if (event.detail.node_id == node.id) {
customTextarea.value = event.detail.prompt;
if (textWidget) textWidget.value = event.detail.prompt;
- updateUIForPauseMode(true);
+ if (canPauseBeActive()) {
+ pauseModeEnabled = true;
+ toggleInput.checked = true;
+ if (pauseWidget) pauseWidget.value = true;
+ updateUIForPauseMode(true);
+ }
node.graph?.setDirtyCanvas(true, true);
}
});
@@ -471,14 +569,6 @@ app.registerExtension({
updateUIForPauseMode(false);
- if (pauseWidget && pauseWidget.value !== undefined) {
- toggleInput.checked = pauseWidget.value;
- if (pauseWidget.value) {
- statusIndicator.innerHTML = "π Pause ON";
- statusIndicator.style.background = "#3a2a1a";
- }
- }
-
return result;
};
}