Commit Graph
19 Commits
Author SHA1 Message Date
PozzettiAndrea 15e6ffa02a Batch + Multi bars: remove Mode selector
Mode is controlled solely by each node's own mode input widget, matching the
Dual and VTK preview policy. Bars keep navigation/layout controls + fullscreen.
2026-08-14 15:32:32 +02:00
PozzettiAndrea 7f927324b6 Control bars move below the canvas; Dual bar loses Mode
- Dual / Multi / VTK preview: the control bar now sits BETWEEN the canvas and
  the info panel (canvas on top, controls below), matching the batch node's
  nav bar. Border flipped bottom->top to suit the new position.
- Preview Mesh Dual: Mode selector removed from the bar -- the node's own
  `mode` input widget is the only mode control (same policy as Preview Mesh
  VTK). Bar keeps Layout, per-mesh color/opacity (overlay), and fullscreen.
2026-08-14 14:57:45 +02:00
PozzettiAndrea 9dbd869c9d Dual overlay: per-mesh color + opacity via two actors; VTK preview bar loses Mode
Preview Mesh Dual (fields overlay):
- The overlay now renders from the TWO per-mesh files (already exported every
  run) as two actors in one renderer, replacing the combined-file single
  actor. Per-mesh opacity finally works for real (the old path could only
  apply the AVERAGE of both opacities to one merged actor).
- New color pickers (1: / 2:) next to the opacity inputs in the bar, overlay
  only. Colors + opacities are pure client-side (viewerState-persisted,
  re-posted on change); defaults red/blue match the old baked colors.
- Viewer (viewer_dual.html): second overlay actor; layout-aware mapper +
  render helpers (mapper1Active/mapper2Active/renderActiveViews) so field
  selection, colormaps, and edge toggles now work in overlay too (previously
  field application only targeted the side-by-side mappers); settings apply
  point size/edges/representation to both overlay actors (color stays
  per-mesh). Legacy combined-file overlay path kept for old widgets; the
  textured overlay still uses the combined GLB (colors don't apply to
  textures).

Preview Mesh (VTK): Mode selector removed from the JS bar -- the node's own
`mode` input is the only control (bar keeps just the fullscreen button).
2026-08-14 14:45:43 +02:00
PozzettiAndrea fe3c01f585 Preview Mesh Dual: remove layout + opacity from Python inputs
layout and opacity_1/opacity_2 are gone from the schema; mode stays a real
node input (as directed).

Python: with no layout to branch on, every run exports BOTH view sets -- the
separate pair (side_by_side / slider) and the combined overlay file -- using
the same untouched export helpers (_export_mesh, _export_combined_mesh; the
combined helper just loses its never-used opacity params). ui_data carries
both file sets; opacity keys dropped (Python only ever echoed them);
common_fields_overlay added (the combined export injects mesh_id).

JS: layout and opacities move into viewerState (persisted via the DOM
widget's serialization, like show_edges already was). Every layout switch is
now CLIENT-SIDE -- overlay included, since its file always exists -- and
opacity changes re-post immediately. Only a mode change re-runs (different
export format). No widgets are hidden; mode renders as a normal node input.

Cost: one extra combined export per run. Old workflows whose saved widget
values included layout/opacity load with those values ignored.
2026-08-14 14:27:20 +02:00
PozzettiAndrea 9d25f10b25 Delete dead widget mesh_preview_vtk_textured.js
It registered geometrypack.meshpreview.vtk.textured for node
GeomPackPreviewMeshVTKWithTexture, which the pack no longer ships (textured
preview is now a `mode` on GeomPackPreviewMeshVTK / ...VTKBatch). So its
beforeRegisterNodeDef never matched -- dead weight that squatted a removed
node. Surfaced by comfy-test's new foreign-node-hook rule; missed in the
earlier squat cleanup because its GeomPack-prefixed name looked like ours.

Nothing imports it; viewer_vtk_textured.html stays (still used by the VTK
single + batch widgets for texture mode). Pack is back to 0 lint errors.
2026-08-14 14:26:35 +02:00
PozzettiAndrea 4b5486076a Revert "Hide bar-driven widgets from node bodies (JS only; Python untouched)"
This reverts commit 54c3ad01aa.
2026-08-14 14:21:57 +02:00
PozzettiAndrea 54c3ad01aa Hide bar-driven widgets from node bodies (JS only; Python untouched)
The control bars are now the ONLY visible controls; the node input widgets
they drive are hidden via a new hideWidgets() util (uiComponents.js). The
widgets keep existing -- values still serialize into the workflow and flow to
Python as inputs -- they just stop rendering on the node body.

- Multi / VTK preview / Batch: `mode` widget hidden (bar Mode selector drives
  it). Batch keeps its `index` widget visible (useful for typing an exact
  index).
- Dual: `layout`, `mode`, `opacity_1`, `opacity_2` all hidden. Opacity moves
  into the bar (shown only for the Overlay layout) and is applied
  CLIENT-SIDE: changing it updates the hidden widgets (persistence + next
  run) and re-posts to the viewer immediately -- no re-run. render() now
  prefers widget values over the last message's, which also fixes the
  `|| 1.0` bug that made opacity 0 impossible.

No Python or export-format changes, per direction.
2026-08-14 14:18:15 +02:00
PozzettiAndrea 6047a30e1b Dual/VTK/Multi preview bars: layout+mode selectors, opacity gating, shared fullscreen
Preview Mesh Dual:
- New control bar with Layout (Side by side | Overlay | Slider) and Mode
  (Fields | Texture), two-way synced with the node widgets.
- side_by_side <-> slider reuse the SAME exported files, so that switch is
  CLIENT-SIDE: onExecuted was refactored into render(message, layoutOverride)
  and the last message is stored -- switching re-renders instantly, no re-run.
  Overlay needs the combined export and mode changes the export format, so
  those queue a re-run (same cost as editing the widget).
- opacity_1/opacity_2 widgets are hidden unless layout is Overlay (the
  hidden-type trick from preview_mesh_batch_render.js), restored when it is.

Preview Mesh (VTK): new bar with Mode (Fields | Texture | Texture (PBR)),
two-way synced, re-runs on change.

Fullscreen: new createFullscreenButton(container) in utils/uiComponents.js
(fullscreens the whole widget so bar controls stay usable; listeners on the
container only, isolation-clean). Wired into Dual, VTK, and Multi bars (in
Multi it sits outside the collapsible group so it survives collapse). The
batch node keeps its own earlier implementation with arrow-key nav.

Note: plain Preview Mesh (three.js, GeomPackPreviewMesh) has no mode input on
the Python side, so there is nothing to select there; the VTK preview is the
one with modes. comfy-3d-viewers was checked for fullscreen prior art -- its
"FullScreen" hits are VTK class names, not browser fullscreen; this is new.
2026-08-14 14:01:37 +02:00
PozzettiAndrea e9c7bbceaf 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.
2026-08-14 13:57:18 +02:00
PozzettiAndrea 19487d73b1 Remove save-preview route/button; batch preview passes TRIMESH through; add Get Mesh From Batch
- Drop /gpack/save_preview and the VTK viewer's "Save Mesh" button (added one
  commit ago): previews are temp exports, and persisting a mesh is what the
  SaveMesh nodes are for. The textured viewer's "Save GLB" stays -- it is a
  pure client-side download, no route.
- Preview Mesh Batch (VTK) now outputs the TRIMESH batch (is_output_list),
  same passthrough pattern as Preview Mesh Multi, so it can sit mid-graph.
- New GeomPackGetMeshFromBatch (nodes/io/get_mesh_from_batch.py): extract one
  mesh from a TRIMESH batch by index (clamped), logging the source file_name
  when present. Registered in nodes/io/__init__.py.
2026-08-14 13:51:37 +02:00
PozzettiAndrea ce11ca310d Fix save-mesh 405 (missing route) + Multi grid dims not matching inputs
Save mesh: the VTK viewer's Save button POSTed to /geometrypack/save_preview,
a route that was never ported into GeometryPack (inherited caller from
comfy-3d-viewers; only /gpack/getpath existed). ComfyUI's GET catch-all
matched the path, hence 405 instead of 404. Added POST /gpack/save_preview in
__init__.py next to getpath: basename()-sanitized lookup of the temp preview
export in ComfyUI output/ (tempdir fallback), copied to output/saved_<name>;
returns {success, saved_filename}. Viewer fetch updated to the new path.

Multi cols/rows: entering dims whose product was smaller than the mesh count
(e.g. 1x1 for 2 meshes) overflowed viewports into IMPLICIT CSS grid rows
(auto height), so the rendered layout stopped matching the inputs. Now the
edited field is authoritative and the other dimension auto-raises to fit
(ceil(n/edited)); both inputs are updated to show the grid actually in
effect. Oversized grids (empty cells) remain allowed.
2026-08-14 13:47:14 +02:00
PozzettiAndrea 78b0378f1d Preview Mesh Batch (VTK): fullscreen with in-fullscreen navigation
Adds a fullscreen button to the nav bar that expands the whole widget (viewer
+ nav bar), so the < > arrows and the dropdown stay visible and clickable in
fullscreen. Arrow KEYS (left/right) also navigate while the widget has focus
(container is made focusable and focused on entering fullscreen).

Navigation is already client-side (showIndex), so it works identically in
fullscreen with no re-execution. All listeners are on the pack's own
container element (never document), keeping it isolation-clean.

Caveat: when the mouse focus is inside the iframe 3D view, arrow KEYS go to
the iframe, not the container -- the on-screen arrow buttons always work; key
nav works when the nav bar/container holds focus. (Forwarding keys out of the
iframe would be a follow-up.)
2026-08-14 13:38:37 +02:00
PozzettiAndrea a9e14f5dc2 VTK viewer: restore Surface after a point cloud forces Points mode
Loading a point cloud correctly locks the shared actor to Points
representation, but the surface-mesh branch of the LOAD_MESH handler never
reset it -- so navigating from a point cloud back to a surface mesh (e.g.
scrolling a batch) left the mesh stuck rendering as points until you manually
picked Surface in settings.

Fix: in the non-point-cloud branch, restore property.setRepresentation to the
user's chosen mode (currentSettings.representation, default Surface). Point
clouds still switch to Points; switching back now returns to the chosen mode.
2026-08-14 13:35:17 +02:00
PozzettiAndrea 7f0c97cd89 Preview Mesh Multi: rename Overlay->Grid (default), add cols/rows + collapsible bar
- "Overlay" is the tiled grid viewer, so rename it to "Grid" and make it the
  default layout (was "wipe"). Saved workflows carrying the old "overlay" value
  migrate to "grid" on load.
- Grid gets Cols / Rows number inputs (shown only for the grid layout). They
  default to the auto dims Python computes and, when set, override them; the
  viewer (viewer_multi.html) already honours gridCols/gridRows from the
  LOAD_MULTI_MESH message, so changing them re-tiles the SAME iframe with no
  reload and no Python/viewer change.
- The control bar is now collapsible via a small arrow (state persists in the
  widget's serialized viewerState).

Client-side only (mesh_preview_multi.js). JS parses, isolation lint 0 errors.
Needs a runtime pass in ComfyUI.
2026-08-14 13:33:45 +02:00
PozzettiAndrea 7ce6f73269 Preview Mesh Batch (VTK): label the dropdown with mesh names
The dropdown showed positions ("3 / 16"); show the source name ("apple.ply")
instead. Loaders already stash the basename in mesh.metadata['file_name']
(mesh_io.py) and it survives the worker boundary (serialization.py forwards
metadata), so the node just reads it per mesh and returns a mesh_names array.

- Python: collect names (metadata['file_name'] -> metadata['name'] -> a
  positional fallback) and add "mesh_names" to the UI arrays.
- JS: dropdown options are now built as real <option> elements with
  textContent = name (falling back to "k / N"), so a filename cannot inject
  markup. Also added a "Name" row to the info panel (escaped).

Verified: python compiles, JS parses, isolation lint still 0 errors.
2026-08-14 13:26:35 +02:00
PozzettiAndrea 923da524b9 Preview Mesh Batch (VTK): client-side navigation + dropdown
Bug: clicking a navigation arrow called app.queuePrompt() (a whole-graph
re-run). Preview nodes are V3 output nodes with no fingerprint_inputs, so
they re-execute on every queue -- which is why navigating this node reloaded
OTHER preview nodes (Preview Mesh Multi, etc.).

Fix: navigate the batch entirely client-side. The Python node now exports
EVERY mesh in the batch once and returns per-index metadata arrays
(mesh_files, vertex_counts, bounds, field_names, ...); the widget stores them
and switches meshes by posting LOAD_MESH to its OWN iframe -- no
app.queuePrompt(), so navigation never re-runs the graph or touches another
node. viewer_type is global (a function of the mode input), so switching
never needs an iframe reload either.

Also adds the requested dropdown next to the arrows: one option per mesh,
jump to any index. Arrows, dropdown, and the index widget all route through a
single client-side showIndex(); the index widget value still persists via
serialization (guarded against a re-entrant callback).

Trade-off (per the chosen approach): the batch is exported up front, so first
render is heavier for very large batches -- but navigation is then instant and
fully decoupled from the rest of the graph.

Verified: python compiles, JS parses, isolation lint still 0 errors. Needs a
runtime pass in ComfyUI (I cannot drive the node graph here).
2026-08-13 17:22:14 +02:00
PozzettiAndrea feb273ad0a Isolation-clean frontend: delete dead JS, guard listeners, leave shared DOM
- Delete 7 dead-squat widgets (registered UI only for other packs' nodes:
  UniRig/SAM3D/Mocap/FBXPreview/BVH/pointcloud) + their 6 orphaned viewer
  HTMLs + the entire unused js/viewer tree (21 files, imported by nothing;
  only ui/styles.css survives) + unused utils barrel.
- Rename namespace geompack -> geometrypack (= DisplayName, lowercased);
  all 17 surviving extensions now geometrypack.*.
- Add event.source provenance guards to every message listener (2 inline,
  2 factory handlers via new iframe param).
- Stop touching document.body: upload file input stays detached (click()
  works detached in modern browsers); folder autocomplete dropdown becomes
  a manual popover inside the widget's own DOM (top layer escapes canvas
  transforms without shared-DOM appends).

Passes comfy-test's javascript isolation lint with 0 errors.
2026-08-13 14:31:51 +02:00
PozzettiAndrea 9ebf36b571 Rename viewer bundles .js -> .mjs so they stop leaking into the main realm
ComfyUI auto-imports every web/**/*.js into the shared main page; the four
big bundles (three, vtk-gltf, viewer-bundle-vtk, gsplat) only ever run
inside the viewer iframes but were getting pulled into the main document
too, where they wrote window.THREE / window.vtk / globalThis.* and could
collide with any other 3D pack. The **/*.js glob only matches .js, so
renaming to .mjs drops them from the auto-import while the iframes still
load them via classic <script src> (mime resolves to text/javascript).
window.* pollution from these bundles is gone from the main realm.
2026-08-13 11:41:01 +02:00
PozzettiAndrea 414f759791 Vendor frontend JS into javascript/; drop the prestartup copy dance
The viewer JS/HTML/bundles are now committed in javascript/ (declared via
pyproject [tool.comfy] web = "javascript") instead of being copied from the
shared comfy-3d-viewers pip package at prestartup. Consequences:

- requirements.txt is now just comfy-env (host env installs nothing else,
  per the never-touch-the-host-env principle)
- prestartup_script.py no longer copies viewers or web_src; it keeps only
  setup_env() and the input-asset copies
- WEB_DIRECTORY attribute removed; the pyproject declaration is the single
  source (served at /extensions/comfyui-geometrypack/, keyed by project name)
- web_src/ deleted; its 6 widgets now live in javascript/js/ as the tracked
  source, edited directly
- viewer_pbr.html hardcoded-fallback URL updated to the new mount name

Iframe URLs auto-detect the mount from import.meta.url, so the folder rename
and mount-name change are transparent at runtime.
2026-08-12 17:37:09 +02:00