Files
PozzettiAndrea-ComfyUI-Geom…/web_src/load_mesh_batch_path.js
T
PozzettiAndreaandClaude Opus 4.8 b5fb441e3e Load Mesh Batch: folder-path autocomplete via own /gpack/getpath route
Inspired by VHS Load Video (Path), but GeometryPack-owned: register
/gpack/getpath in __init__ (main process, after PromptServer exists) to
list directory contents, and a web_src widget that replaces folder_path
with an <input>+<datalist> showing live FOLDER suggestions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 14:58:36 +02:00

82 lines
3.7 KiB
JavaScript

// Load Mesh Batch: folder-path autocomplete (type to navigate directories),
// inspired by VHS "Load Video (Path)" but using GeometryPack's own /gpack/getpath
// route and restricted to FOLDERS. Replaces the plain folder_path text widget
// with a real <input> + <datalist> so the browser shows live folder suggestions.
import { app } from "../../../scripts/app.js";
const TAG = "[MeshBatchPath]";
console.log(`${TAG} script loaded`);
// Split "output/meshes/fo" -> ["output/meshes/", "fo"] (dir, remainder)
function pathStem(value) {
const v = String(value || "");
const i = v.lastIndexOf("/");
return i < 0 ? ["", v] : [v.slice(0, i + 1), v.slice(i + 1)];
}
app.registerExtension({
name: "geompack.loadmeshbatch.path",
async beforeRegisterNodeDef(nodeType, nodeData) {
if (nodeData.name !== "GeomPackLoadMeshBatch") return;
console.log(`${TAG} registering for GeomPackLoadMeshBatch`);
const onCreated = nodeType.prototype.onNodeCreated;
nodeType.prototype.onNodeCreated = function () {
const r = onCreated?.apply(this, arguments);
const node = this;
// Remove the original folder_path string widget; we replace it with a
// DOM input of the same name so it still serializes as folder_path.
const idx = (node.widgets || []).findIndex((w) => w.name === "folder_path");
const initial = idx >= 0 ? (node.widgets[idx].value ?? "3d") : "3d";
if (idx >= 0) node.widgets.splice(idx, 1);
const wrap = document.createElement("div");
wrap.style.cssText = "width:100%;box-sizing:border-box;padding:0 6px;";
const input = document.createElement("input");
input.type = "text";
input.value = initial;
input.placeholder = "folder path — type to autocomplete (e.g. output/…)";
const listId = `gpack_paths_${node.id}_${Math.floor(performance.now())}`;
input.setAttribute("list", listId);
input.style.cssText = "width:100%;box-sizing:border-box;background:#222;color:#ddd;" +
"border:1px solid #444;border-radius:4px;padding:3px 6px;font:12px monospace;";
const dl = document.createElement("datalist");
dl.id = listId;
wrap.appendChild(input);
wrap.appendChild(dl);
const widget = node.addDOMWidget("folder_path", "gpack_path", wrap, {
getValue() { return input.value; },
setValue(v) { input.value = v ?? ""; },
serialize: true,
});
widget.computeSize = (w) => [w, 30];
// Refetch directory contents only when the directory part changes.
let lastDir = null;
const refresh = async () => {
const [dir] = pathStem(input.value);
if (dir === lastDir) return;
lastDir = dir;
try {
const resp = await fetch(`/gpack/getpath?path=${encodeURIComponent(dir || ".")}`);
if (!resp.ok) return;
const items = await resp.json();
dl.innerHTML = "";
for (const it of items) {
if (!it.endsWith("/")) continue; // folders only
const opt = document.createElement("option");
opt.value = dir + it; // full path so it chains
dl.appendChild(opt);
}
} catch (e) { /* route missing / offline -> stays plain text */ }
};
input.addEventListener("input", refresh);
input.addEventListener("focus", () => { lastDir = null; refresh(); });
return r;
};
},
});