Preview Mesh Batch + Multi: mode selector (Fields/Texture) in the control bar

Both bars get a Mode dropdown mirroring the node's `mode` input, two-way
synced with the widget. Unlike index navigation this is NOT client-side: mode
changes what gets exported (VTP/STL vs GLB), so changing it queues a re-run --
the same cost as editing the node widget, just reachable from the bar. After
a run the bar reflects the mode actually executed.
This commit is contained in:
PozzettiAndrea
2026-08-14 13:57:18 +02:00
parent 19487d73b1
commit e9c7bbceaf
2 changed files with 55 additions and 0 deletions
+26
View File
@@ -81,8 +81,18 @@ app.registerExtension({
gridControls.appendChild(Object.assign(document.createElement("span"), { textContent: "Rows:" }));
gridControls.appendChild(rowsInput);
// Mode selector (fields | texture) mirroring the node's `mode`
// input. NOT client-side: mode changes what gets EXPORTED, so
// changing it queues a re-run (same as editing the node widget).
const modeSel = document.createElement("select");
modeSel.style.cssText = "background:#333;color:#ccc;border:1px solid #555;border-radius:3px;font:11px monospace;padding:2px 6px;";
modeSel.innerHTML = '<option value="fields">Fields</option><option value="texture">Texture</option>';
modeSel.title = "Visualization mode (re-runs the node)";
controls.appendChild(Object.assign(document.createElement("span"), { textContent: "Layout:" }));
controls.appendChild(layoutSel);
controls.appendChild(Object.assign(document.createElement("span"), { textContent: "Mode:" }));
controls.appendChild(modeSel);
controls.appendChild(gridControls);
bar.appendChild(collapseBtn);
bar.appendChild(controls);
@@ -203,6 +213,22 @@ app.registerExtension({
colsInput.addEventListener('change', onDim("cols"));
rowsInput.addEventListener('change', onDim("rows"));
// Mode: bar select <-> node widget, two-way (bar change re-runs).
const modeWidget = this.widgets?.find(w => w.name === "mode");
if (modeWidget) {
modeSel.value = modeWidget.value || "fields";
const origModeCb = modeWidget.callback;
modeWidget.callback = function(value) {
const res = origModeCb?.apply(this, arguments);
modeSel.value = value;
return res;
};
}
modeSel.addEventListener("change", () => {
if (modeWidget) modeWidget.value = modeSel.value;
app.queuePrompt();
});
syncLayoutUI();
applyCollapsed();
+29
View File
@@ -100,6 +100,16 @@ app.registerExtension({
dropdown.innerHTML = '<option value="0">1 / 1</option>';
// Assemble navigation bar
// Mode selector (fields | texture) mirroring the node's `mode`
// input. NOT client-side: mode changes what gets EXPORTED (VTP/STL
// vs GLB), so changing it queues a re-run -- exactly like editing
// the node widget, just reachable from the nav bar.
const modeSel = document.createElement("select");
modeSel.style.cssText = "background:#333;color:#ccc;border:1px solid #555;" +
"border-radius:3px;font:11px monospace;padding:2px 6px;cursor:pointer;";
modeSel.innerHTML = '<option value="fields">Fields</option><option value="texture">Texture</option>';
modeSel.title = "Visualization mode (re-runs the node)";
// Fullscreen toggle -- expands the whole widget (viewer + this nav
// bar) so the arrows and dropdown stay usable in fullscreen.
const fsButton = document.createElement("button");
@@ -111,6 +121,7 @@ app.registerExtension({
navBar.appendChild(indexLabel);
navBar.appendChild(dropdown);
navBar.appendChild(nextButton);
navBar.appendChild(modeSel);
navBar.appendChild(fsButton);
// Create mesh info panel
@@ -196,6 +207,23 @@ app.registerExtension({
nextButton.addEventListener("click", () => showIndex(currentIndex + 1));
dropdown.addEventListener("change", () => showIndex(Number(dropdown.value)));
// Mode: bar select <-> node widget, two-way. Bar change re-runs
// (new exports needed); widget edits keep the bar in sync.
const modeWidget = this.widgets?.find(w => w.name === "mode");
if (modeWidget) {
modeSel.value = modeWidget.value || "fields";
const origModeCb = modeWidget.callback;
modeWidget.callback = function(value) {
const res = origModeCb?.apply(this, arguments);
modeSel.value = value;
return res;
};
}
modeSel.addEventListener("change", () => {
if (modeWidget) modeWidget.value = modeSel.value;
app.queuePrompt();
});
// Fullscreen the whole widget. The arrow BUTTONS stay clickable in
// fullscreen; arrow KEYS also navigate while the widget (nav bar) has
// focus. All listeners are on our own `container`, never document, so
@@ -411,6 +439,7 @@ app.registerExtension({
currentBatchSize = message.batch_size?.[0] || files.length;
if (indexWidget) indexWidget.options.max = currentBatchSize - 1;
modeSel.value = batchData.mode; // reflect the mode actually run
// (Re)populate the dropdown: one option per mesh, labelled by its
// source name (e.g. "apple.ply"), falling back to its position.