Added the 'Paste' button to the RS Load Images From Dir node

This commit is contained in:
Raykosan
2026-07-31 09:41:27 +03:00
parent 0f08ab8f3e
commit 1243b67d16
2 changed files with 85 additions and 79 deletions
+18 -4
View File
@@ -15,6 +15,7 @@
import os
import glob
import json
import torch
import numpy as np
import folder_paths
@@ -27,6 +28,7 @@ class RSLoadImagesFromDir:
def INPUT_TYPES(cls) -> Dict[str, Dict[str, Any]]:
return {
"required": {
"node_data": ("STRING", {"default": "{}", "hidden": True}),
"folder_path": ("STRING", {"default": "", "multiline": False}),
"filter_type": (["*.*", "*.png", "*.jpg", "*.jpeg", "*.webp", "*.bmp", "*.gif", "custom"], {"default": "*.png"}),
"start_index": ("INT", {"default": 1, "min": 1, "max": 999999, "step": 1}),
@@ -46,12 +48,24 @@ class RSLoadImagesFromDir:
def load_images(
self,
folder_path: str,
filter_type: str,
start_index: int,
end_index: int,
node_data: str = "{}",
folder_path: str = "",
filter_type: str = "*.png",
start_index: int = 1,
end_index: int = 1,
custom_filter: str = "*.png"
):
try:
data = json.loads(node_data) if node_data else {}
if data:
folder_path = data.get("folder_path", folder_path)
filter_type = data.get("filter_type", filter_type)
start_index = data.get("start_index", start_index)
end_index = data.get("end_index", end_index)
custom_filter = data.get("custom_filter", custom_filter)
except Exception:
pass
if not folder_path or not os.path.isdir(folder_path):
raise ValueError(f"Invalid folder path: {folder_path}")
+67 -75
View File
@@ -30,44 +30,52 @@ app.registerExtension({
node.setSize([NODE_WIDTH, MIN_HEIGHT_NORMAL]);
node.min_size = [NODE_WIDTH, MIN_HEIGHT_NORMAL];
const wNodeData = node.widgets?.find(w => w.name === "node_data");
const wPath = node.widgets?.find(w => w.name === "folder_path");
const wFilter = node.widgets?.find(w => w.name === "filter_type");
const wStart = node.widgets?.find(w => w.name === "start_index");
const wEnd = node.widgets?.find(w => w.name === "end_index");
const wCustom = node.widgets?.find(w => w.name === "custom_filter");
[wPath, wFilter, wStart, wEnd, wCustom].forEach(w => {
[wNodeData, wPath, wFilter, wStart, wEnd, wCustom].forEach(w => {
if (w) {
w.hidden = true;
if (w.element) w.element.style.display = "none";
}
});
if (wPath) wPath.serializeValue = () => node.data.folder_path;
if (wFilter) wFilter.serializeValue = () => node.data.filter_type;
if (wStart) wStart.serializeValue = () => parseInt(node.data.start_index);
if (wEnd) wEnd.serializeValue = () => parseInt(node.data.end_index);
if (wCustom) wCustom.serializeValue = () => node.data.custom_filter;
if (wPath?.value) node.data.folder_path = wPath.value;
if (wFilter?.value) node.data.filter_type = wFilter.value;
if (wStart?.value) node.data.start_index = parseInt(wStart.value);
if (wEnd?.value) node.data.end_index = parseInt(wEnd.value);
if (wCustom?.value) node.data.custom_filter = wCustom.value;
if (wNodeData) {
try {
const savedData = JSON.parse(wNodeData.value || "{}");
if (savedData && typeof savedData === 'object') {
node.data = { ...node.data, ...savedData };
}
} catch (e) {}
wNodeData.serializeValue = () => {
node.syncData();
return JSON.stringify(node.data);
};
}
node.uiElements = [];
node.dropdownMenu = null;
node.customModal = null;
const syncData = () => {
node.syncData = function() {
if (wPath) wPath.value = node.data.folder_path;
if (wFilter) wFilter.value = node.data.filter_type;
if (wStart) wStart.value = parseInt(node.data.start_index);
if (wEnd) wEnd.value = parseInt(node.data.end_index);
if (wCustom) wCustom.value = node.data.custom_filter;
if (node.graph) node.graph.changeTracker?.dispatchEvent(new Event("change"));
node.setDirtyCanvas(true, true);
if (wNodeData) {
wNodeData.value = JSON.stringify(node.data);
}
if (node.graph) {
node.graph.changeTracker?.dispatchEvent(new Event("change"));
}
};
const updateNodeSize = () => {
@@ -91,6 +99,21 @@ app.registerExtension({
this.setDirtyCanvas(true, true);
};
const onConfigure = node.onConfigure;
node.onConfigure = function(o) {
if (onConfigure) onConfigure.apply(this, arguments);
if (wNodeData && wNodeData.value) {
try {
const restoredData = JSON.parse(wNodeData.value);
if (restoredData) {
node.data = { ...node.data, ...restoredData };
node.syncData();
updateNodeSize();
}
} catch (e) {}
}
};
const closeDropdown = (e) => {
if (node.dropdownMenu && !node.dropdownMenu.contains(e.target)) {
node.dropdownMenu.remove();
@@ -155,11 +178,9 @@ app.registerExtension({
user-select: none; -moz-user-select: none;
display: inline-flex; align-items: center; justify-content: center;
`;
btnPaste.onmousedown = (e) => e.preventDefault();
btnPaste.onmouseup = (e) => e.preventDefault();
btnPaste.oncontextmenu = (e) => e.preventDefault();
btnPaste.onmouseover = () => { if (!btnPaste.disabled) btnPaste.style.background = "#3a3a3a"; };
btnPaste.onmouseout = () => { if (!btnPaste.disabled) btnPaste.style.background = "#2a2a2a"; };
@@ -182,7 +203,6 @@ app.registerExtension({
btnPaste.style.color = "#4CAF50";
}
} catch (err) {
console.log("Clipboard read blocked by browser:", err);
btnPaste.textContent = "⌨️ Ctrl+V";
btnPaste.style.borderColor = "#FF9800";
btnPaste.style.color = "#FF9800";
@@ -265,18 +285,14 @@ app.registerExtension({
const truncatePath = (ctx, path, maxWidth) => {
if (!path) return "Folder not selected";
const ellipsis = "...";
if (ctx.measureText(path).width <= maxWidth) {
return path;
}
let truncated = path;
while (truncated.length > 0 && ctx.measureText(ellipsis + truncated).width > maxWidth) {
truncated = truncated.substring(1);
}
return ellipsis + truncated;
};
@@ -310,27 +326,21 @@ app.registerExtension({
const padding = 10;
const rowHeight = 23;
const borderRadius = 3;
let currentY = 35 + 60;
ctx.font = "12px sans-serif";
ctx.textBaseline = "middle";
const pathDisplayHeight = 20;
const pathDisplayY = currentY;
ctx.fillStyle = "#1a1a1a";
ctx.strokeStyle = "#333";
drawRoundedRect(ctx, padding, pathDisplayY, w - padding * 2, pathDisplayHeight, borderRadius);
drawRoundedRect(ctx, padding, currentY, w - padding * 2, pathDisplayHeight, borderRadius);
const pathText = node.data.folder_path || "Folder not selected";
const maxTextWidth = w - padding * 2 - 10;
const displayPath = truncatePath(ctx, pathText, maxTextWidth);
const displayPath = truncatePath(ctx, pathText, w - padding * 2 - 10);
ctx.fillStyle = node.data.folder_path ? "#aaa" : "#666";
ctx.textAlign = "left";
ctx.fillText(displayPath, padding + 5, pathDisplayY + pathDisplayHeight / 2);
ctx.fillText(displayPath, padding + 5, currentY + pathDisplayHeight / 2);
currentY += pathDisplayHeight + 7;
ctx.fillStyle = "#2a2a2a";
@@ -339,7 +349,6 @@ app.registerExtension({
ctx.fillStyle = "#2196F3";
ctx.textAlign = "center";
ctx.fillText("📁 Select folder", w / 2, currentY + rowHeight / 2);
this.uiElements.push({ type: "btn_folder", x: padding, y: currentY, w: w - padding * 2, h: rowHeight });
currentY += rowHeight + 7;
@@ -350,7 +359,6 @@ app.registerExtension({
ctx.fillStyle = "#4CAF50";
ctx.textAlign = "center";
ctx.fillText(filterLabel, w / 2, currentY + rowHeight / 2);
this.uiElements.push({ type: "btn_filter", x: padding, y: currentY, w: w - padding * 2, h: rowHeight });
currentY += rowHeight + 7;
@@ -427,16 +435,12 @@ app.registerExtension({
for (const el of this.uiElements) {
if (x >= el.x && x <= el.x + el.w && y >= el.y && y <= el.y + el.h) {
if (el.type === "btn_folder") {
showCustomPrompt(
"Enter folder path",
this.data.folder_path,
(result) => {
if (result !== null) {
this.data.folder_path = result.trim();
syncData();
}
showCustomPrompt("Enter folder path", this.data.folder_path, (result) => {
if (result !== null) {
this.data.folder_path = result.trim();
node.syncData();
}
);
});
return true;
}
@@ -463,7 +467,7 @@ app.registerExtension({
item.onclick = (e) => {
e.stopPropagation();
this.data.filter_type = f;
syncData();
node.syncData();
updateNodeSize();
menu.remove();
this.dropdownMenu = null;
@@ -487,63 +491,51 @@ app.registerExtension({
if (el.type === "btn_start_minus") {
this.data.start_index = Math.max(1, parseInt(this.data.start_index) - 1);
syncData();
node.syncData();
return true;
}
if (el.type === "btn_start_plus") {
this.data.start_index = parseInt(this.data.start_index) + 1;
syncData();
node.syncData();
return true;
}
if (el.type === "val_start") {
showCustomPrompt(
"Start index (min 1)",
String(this.data.start_index),
(result) => {
if (result !== null) {
this.data.start_index = Math.max(1, parseInt(result) || 1);
syncData();
}
showCustomPrompt("Start index (min 1)", String(this.data.start_index), (result) => {
if (result !== null) {
this.data.start_index = Math.max(1, parseInt(result) || 1);
node.syncData();
}
);
});
return true;
}
if (el.type === "btn_end_minus") {
this.data.end_index = Math.max(1, parseInt(this.data.end_index) - 1);
syncData();
node.syncData();
return true;
}
if (el.type === "btn_end_plus") {
this.data.end_index = parseInt(this.data.end_index) + 1;
syncData();
node.syncData();
return true;
}
if (el.type === "val_end") {
showCustomPrompt(
"End index",
String(this.data.end_index),
(result) => {
if (result !== null) {
this.data.end_index = Math.max(1, parseInt(result) || 1);
syncData();
}
showCustomPrompt("End index", String(this.data.end_index), (result) => {
if (result !== null) {
this.data.end_index = Math.max(1, parseInt(result) || 1);
node.syncData();
}
);
});
return true;
}
if (el.type === "val_custom") {
showCustomPrompt(
"Custom filter (e.g. *_mask.png)",
this.data.custom_filter,
(result) => {
if (result !== null) {
this.data.custom_filter = result.trim() || "*.png";
syncData();
}
showCustomPrompt("Custom filter (e.g. *_mask.png)", this.data.custom_filter, (result) => {
if (result !== null) {
this.data.custom_filter = result.trim() || "*.png";
node.syncData();
}
);
});
return true;
}
}
@@ -565,7 +557,7 @@ app.registerExtension({
if (originalOnRemoved) originalOnRemoved.apply(this, arguments);
};
syncData();
node.syncData();
updateNodeSize();
return result;