Files
PozzettiAndrea-ComfyUI-Geom…/javascript/js/pointcloud_preview_vtk.js
T
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

113 lines
4.8 KiB
JavaScript

/**
* VTK.js Point Cloud Preview Widget
* For point cloud visualization nodes (SAM3, DepthAnythingV3, etc.)
*/
import { app } from "../../../scripts/app.js";
import { EXTENSION_FOLDER, getViewerUrl } from "./utils/extensionFolder.js";
import { createContainer, createIframe, createInfoPanel, createWidgetOptions } from "./utils/uiComponents.js";
import { createViewerManager, createErrorHandler, buildViewUrl } from "./utils/postMessage.js";
const POINTCLOUD_NODES = ["SAM3D_PreviewPointCloud", "DA3_PreviewPointCloud"];
app.registerExtension({
name: "comfy3d.pointcloud.vtk",
async beforeRegisterNodeDef(nodeType, nodeData, app) {
if (POINTCLOUD_NODES.includes(nodeData.name)) {
const onNodeCreated = nodeType.prototype.onNodeCreated;
nodeType.prototype.onNodeCreated = function() {
const r = onNodeCreated ? onNodeCreated.apply(this, arguments) : undefined;
// Create container for viewer + info panel
const container = createContainer();
// Create iframe for VTK.js viewer
const iframe = createIframe(getViewerUrl("viewer_vtk"));
// Create info panel
const infoPanel = createInfoPanel("Point cloud will appear after execution");
// Add iframe and info panel to container
container.appendChild(iframe);
container.appendChild(infoPanel);
// Add widget
const widget = this.addDOMWidget("preview_vtk", "POINTCLOUD_PREVIEW_VTK", container, createWidgetOptions());
widget.computeSize = () => [512, 580];
// Store references
this._vtkIframe = iframe;
this._infoPanel = infoPanel;
// Create viewer manager
const viewerManager = createViewerManager(iframe, "[PointCloud VTK]");
// Listen for error messages
window.addEventListener('message', createErrorHandler(infoPanel, "[PointCloud VTK]"));
// Set initial node size
this.setSize([512, 580]);
return r;
};
// Handle execution
const onExecuted = nodeType.prototype.onExecuted;
nodeType.prototype.onExecuted = function(message) {
onExecuted?.apply(this, arguments);
// Get file path from ui.file_path
let filePath = null;
if (message?.ui?.file_path) {
filePath = Array.isArray(message.ui.file_path) ? message.ui.file_path[0] : message.ui.file_path;
} else if (message?.file_path) {
filePath = Array.isArray(message.file_path) ? message.file_path[0] : message.file_path;
}
if (filePath && filePath.trim() !== '') {
// Normalize path
filePath = filePath.replace(/\\/g, '/').trim();
// Build URL for ComfyUI view endpoint
const outputMatch = filePath.match(/(?:^|\/)(output|input)\/(.+)$/);
let url;
if (outputMatch) {
const [, type, relativePath] = outputMatch;
const pathParts = relativePath.split('/');
const filename = pathParts.pop();
const subfolder = pathParts.join('/');
url = `/view?filename=${encodeURIComponent(filename)}&type=${type}&subfolder=${encodeURIComponent(subfolder)}`;
} else {
const filename = filePath.split('/').pop();
url = `/view?filename=${encodeURIComponent(filename)}&type=output&subfolder=`;
}
// Update info panel
if (this._infoPanel) {
const filename = filePath.split('/').pop();
this._infoPanel.innerHTML = `<strong>File:</strong> ${filename}`;
}
// Send message to iframe
if (this._vtkIframe?.contentWindow) {
const sendMessage = () => {
this._vtkIframe.contentWindow.postMessage({
type: 'LOAD_MESH',
filepath: url
}, '*');
};
if (this._vtkIframe.contentDocument?.readyState === 'complete') {
sendMessage();
} else {
this._vtkIframe.addEventListener('load', () => setTimeout(sendMessage, 100), { once: true });
}
}
}
};
}
}
});