diff --git a/folder_file_pro.py b/folder_file_pro.py index 0d8bb2c..9440422 100644 --- a/folder_file_pro.py +++ b/folder_file_pro.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# DAO_master - Folder File Pro +# DAO_master - Folder File Pro (v4.1) import os import io @@ -80,7 +80,7 @@ class FInfo: mtime: float # -------------------------------------------------------------------------------------- -# Helpers (même pipeline que l'UI) +# Helpers def _norm_exts(exts: str) -> List[str]: if not exts: @@ -208,7 +208,7 @@ def _open_in_explorer(target: str) -> None: subprocess.Popen(["xdg-open", target], close_fds=True) # -------------------------------------------------------------------------------------- -# HTTP endpoints (si serveur dispo) +# HTTP endpoints if HAVE_SERVER: @PromptServer.instance.routes.get("/folder_file_pro/list") @@ -304,7 +304,7 @@ if HAVE_SERVER: return web.Response(status=404) try: img = Image.open(filepath) - # Si alpha -> PNG, sinon JPEG + # alpha -> PNG, sinon JPEG has_alpha = (img.mode == "RGBA") or (img.mode == "P" and "transparency" in img.info) img = img.convert("RGBA") if has_alpha else img.convert("RGB") img.thumbnail([320, 320], Image.LANCZOS) @@ -365,8 +365,10 @@ class FolderFilePro: "regex_ignore_case": ("BOOLEAN", {"default": True}), "sort_by": (["name", "mtime", "size"], {"default": "name"}), "descending": ("BOOLEAN", {"default": False}), + # Sélection "seed_mode": (["manual", "fixed", "increment", "decrement", "randomize"], {"default": "manual"}), "seed": ("INT", {"default": 0, "min": -2147483648, "max": 2147483647}), + "index": ("INT", {"default": 0, "min": 0, "max": 1_000_000_000}), } } @@ -378,7 +380,7 @@ class FolderFilePro: def pick(self, directory: str, extensions: str, name_regex: str, regex_mode: str, regex_ignore_case: bool, sort_by: str, descending: bool, - seed_mode: str, seed: int): + seed_mode: str, seed: int, index: int): exts = _norm_exts(extensions) files = _list_files_current_dir(directory, exts) @@ -392,16 +394,17 @@ class FolderFilePro: n = len(files) sm = (seed_mode or "manual").lower() - if sm == "randomize": - idx = random.Random(int(seed)).randrange(n) + if sm == "manual": + sel = max(0, min(int(index), n - 1)) + elif sm == "randomize": + sel = random.Random(int(seed) & 0xFFFFFFFF).randrange(n) + elif sm == "decrement": + sel = (int(seed) - 1) % n else: - try: - idx = int(seed) - except Exception: - idx = 0 - idx %= n + # fixed / increment → même base (Comfy ne fournit pas de hook post-run ici) + sel = int(seed) % n - chosen = files[idx] + chosen = files[sel] files_json = json.dumps( [{"name": f.name, "path": f.path, "size": f.size, "mtime": f.mtime} for f in files], diff --git a/web/dao_folder_file_pro.js b/web/dao_folder_file_pro.js index 0dfebd8..b5d13bd 100644 --- a/web/dao_folder_file_pro.js +++ b/web/dao_folder_file_pro.js @@ -1,7 +1,8 @@ "use strict"; -// DAO_master — Folder File Pro UI (v4.0) -// - Folder icon from web/ico_dossier.png (non déformée) +// DAO_master — Folder File Pro UI (v4.1) +// - Icône dossier PNG /web/ico_dossier.png non déformée // - Grid/List, scroll fiable, double-clic, type-to-select, Explorer +// - Clic sur un fichier => seed_mode = "manual" + mise à jour de l'index (aligné Python) import { app } from "/scripts/app.js"; import { api } from "/scripts/api.js"; @@ -41,10 +42,10 @@ function ensureLightbox() { }); } -/* ---------- Icône dossier PNG depuis /web ---------- */ +/* ---------- Icône dossier ---------- */ const FFP_FOLDER_URL = new URL("./ico_dossier.png", import.meta.url).href; -/* ---------- Badge d'extension pour non-images ---------- */ +/* ---------- Badge d'extension ---------- */ function extBadge(ext) { if (!ext) ext = ".file"; if (ext[0] !== ".") ext = "." + ext; @@ -52,7 +53,7 @@ function extBadge(ext) { } app.registerExtension({ - name: "DAO_master.FolderFilePro.UI.v4_0", + name: "DAO_master.FolderFilePro.UI.v4_1", async beforeRegisterNodeDef(nodeType, nodeData) { if (!nodeData || nodeData.name !== "Folder File Pro") return; @@ -69,8 +70,10 @@ app.registerExtension({ const wReIC = this.widgets.find((w) => w.name === "regex_ignore_case"); const wSort = this.widgets.find((w) => w.name === "sort_by"); const wDesc = this.widgets.find((w) => w.name === "descending"); - const wIdx = this.widgets.find((w) => w.name === "index"); - if (wIdx) wIdx.hidden = true; + const wSeedMode = this.widgets.find((w) => w.name === "seed_mode"); + const wSeed = this.widgets.find((w) => w.name === "seed"); + const wIndex = this.widgets.find((w) => w.name === "index"); + if (wIndex) wIndex.hidden = true; /* --------- UI DOM --------- */ const root = document.createElement("div"); @@ -81,7 +84,6 @@ app.registerExtension({ .ffp-input,.ffp-btn,.ffp-select{background:#333;color:#ccc;border:1px solid #555;border-radius:6px;padding:6px 8px;font-size:12px} .ffp-input{flex:1 1 auto;min-width:240px} - /* conteneur résultats → occupe tout l'espace disponible; min-height:0 pour activer le scroll */ .ffp-grid{flex:1 1 auto;min-height:0;overflow-y:auto;background:#222;border-radius:10px;padding:10px; display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:10px} .ffp-grid.ffp-list{display:block} @@ -91,14 +93,13 @@ app.registerExtension({ .ffp-card.selected{border-color:#00ffc9} .ffp-media{display:flex;align-items:center;justify-content:center;height:150px;border-radius:10px 10px 0 0;background:#1b1b1b;overflow:hidden;color:#bbb;font-size:22px} - .ffp-media img{max-width:100%;max-height:100%;object-fit:contain;image-rendering:auto} + .ffp-media img{max-width:100%;max-height:100%;object-fit:contain} - /* taille conforme Local_Image_Gallery (Grid ~72px haut) */ + /* taille proche de Local_Image_Gallery (≈72px de haut) */ .ffp-media .ffp-folder-img{max-height:72px;max-width:96px;width:auto;height:auto} .ffp-info{padding:8px 10px;font-size:12px;word-break:break-all;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden} - /* List mode */ .ffp-grid.ffp-list .ffp-card{flex-direction:row;align-items:center;padding:6px 8px;margin-bottom:6px} .ffp-grid.ffp-list .ffp-media{width:52px;height:52px;border-radius:8px;margin-right:10px} .ffp-grid.ffp-list .ffp-media .ffp-folder-img{max-height:40px;max-width:56px} @@ -157,8 +158,12 @@ app.registerExtension({ card.classList.add("selected"); selectedPath = card.dataset.path || null; if (scroll) card.scrollIntoView({ block: "nearest" }); - if (updateIndex && card.dataset.type === "file") + if (updateIndex && card.dataset.type === "file") { + // IMPORTANT : aligner le moteur Python sur la sélection + if (wSeedMode) wSeedMode.value = "manual"; resolveAndSelect.call(this, selectedPath); + this.setDirtyCanvas(true, true); + } } function selectByIndex(idx) { const list = cardsArray(); @@ -187,40 +192,21 @@ app.registerExtension({ } function fileHTML(f) { if (f.type === "image") { - const src = - "/folder_file_pro/thumbnail?filepath=" + - encodeURIComponent(f.path); + const src = "/folder_file_pro/thumbnail?filepath=" + encodeURIComponent(f.path); return ( - '
' + - f.name + - "
" + '
' + f.name + "
" ); } else if (f.type === "svg") { - const src = - "/folder_file_pro/view?filepath=" + - encodeURIComponent(f.path); + const src = "/folder_file_pro/view?filepath=" + encodeURIComponent(f.path); return ( - '
' + - f.name + - "
" + '
' + f.name + "
" ); } else { return ( - '
' + - extBadge(f.ext || ".file") + - '
' + - f.name + - "
" + '
' + extBadge(f.ext || ".file") + + '
' + f.name + "
" ); } } @@ -258,9 +244,7 @@ app.registerExtension({ pathInput.value = curDir; parentDir = data.parent_directory || null; upBtn.disabled = !parentDir; - countLbl.textContent = data.total_count - ? String(data.total_count) + " fichier(s)" - : ""; + countLbl.textContent = data.total_count ? String(data.total_count) + " fichier(s)" : ""; grid.classList.toggle("ffp-list", curView === "list"); @@ -286,10 +270,7 @@ app.registerExtension({ const sameView = curView === prevView; if (!sameDir || !sameView || forceResetScroll) grid.scrollTop = 0; else { - const maxScroll = Math.max( - 0, - grid.scrollHeight - grid.clientHeight - ); + const maxScroll = Math.max(0, grid.scrollHeight - grid.clientHeight); grid.scrollTop = Math.min(prevScroll, maxScroll); } lastDir = curDir; @@ -298,10 +279,7 @@ app.registerExtension({ grid.focus(); }); } catch (e) { - grid.innerHTML = - '

' + - (e.message || String(e)) + - "

"; + grid.innerHTML = '

' + (e.message || String(e)) + "

"; lastDir = String(wDir ? wDir.value : ""); lastView = viewSel.value; forceResetScroll = false; @@ -325,13 +303,11 @@ app.registerExtension({ regex_ic: String(!!(wReIC && wReIC.value)), path: String(targetPath), }).toString(); - const res = await api.fetchApi( - "/folder_file_pro/resolve_index?" + params - ); + const res = await api.fetchApi("/folder_file_pro/resolve_index?" + params); const data = await res.json(); - const idxW = this.widgets.find((w) => w.name === "index"); - if (idxW && data.index >= 0) { - idxW.value = data.index; + if (data && typeof data.index === "number" && data.index >= 0) { + const idxW = this.widgets.find((w) => w.name === "index"); + if (idxW) idxW.value = data.index; this.setDirtyCanvas(true, true); } } catch (e) { @@ -368,15 +344,11 @@ app.registerExtension({ v.pause(); a.pause(); if (isImg) { - i.src = - "/folder_file_pro/view?filepath=" + encodeURIComponent(p); + i.src = "/folder_file_pro/view?filepath=" + encodeURIComponent(p); i.style.display = "block"; lb.style.display = "flex"; } else { - window.open( - "/folder_file_pro/view?filepath=" + encodeURIComponent(p), - "_blank" - ); + window.open("/folder_file_pro/view?filepath=" + encodeURIComponent(p), "_blank"); } }); @@ -401,8 +373,7 @@ app.registerExtension({ } } grid.addEventListener("keydown", (e) => { - const tag = - (document.activeElement && document.activeElement.tagName) || ""; + const tag = (document.activeElement && document.activeElement.tagName) || ""; if (tag === "INPUT" || tag === "SELECT" || tag === "TEXTAREA") return; if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) { handleType.call(this, e.key); @@ -420,7 +391,7 @@ app.registerExtension({ } }); - // relier widgets aux refresh + // refresh auto quand un widget change function rewire(w) { if (!w) return; const prev = w.callback; @@ -451,8 +422,7 @@ app.registerExtension({ fetchList(); }; exploreBtn.onclick = async () => { - const toOpen = - (selectedPath || (wDir ? wDir.value : "") || "").toString(); + const toOpen = (selectedPath || (wDir ? wDir.value : "") || "").toString(); if (!toOpen) return; try { await api.fetchApi("/folder_file_pro/open_explorer", { @@ -485,4 +455,4 @@ app.registerExtension({ }, }); -console.log("[DAO_master] Folder File Pro UI v4.0 loaded"); +console.log("[DAO_master] Folder File Pro UI v4.1 loaded");