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

120 lines
5.4 KiB
JavaScript

/**
* ComfyUI GeomPack - UV Mesh Preview Widget
* Synchronized 3D mesh and 2D UV layout viewer with interactive picking
*/
import { app } from "../../../scripts/app.js";
// Auto-detect extension folder name (handles ComfyUI-GeometryPack or comfyui-geometrypack)
const EXTENSION_FOLDER = (() => {
const url = import.meta.url;
const match = url.match(/\/extensions\/([^/]+)\//);
return match ? match[1] : "ComfyUI-GeometryPack";
})();
console.log("[GeomPack] Loading UV mesh preview extension...");
app.registerExtension({
name: "geompack.meshpreviewuv",
async beforeRegisterNodeDef(nodeType, nodeData, app) {
if (nodeData.name === "GeomPackPreviewMeshUV") {
console.log("[GeomPack] Registering Preview Mesh UV node");
const onNodeCreated = nodeType.prototype.onNodeCreated;
nodeType.prototype.onNodeCreated = function() {
const r = onNodeCreated ? onNodeCreated.apply(this, arguments) : undefined;
console.log("[GeomPack UV] Node created, adding widget");
// Create iframe for UV viewer
const iframe = document.createElement("iframe");
iframe.style.width = "100%";
iframe.style.height = "100%";
iframe.style.border = "none";
iframe.style.backgroundColor = "#2a2a2a";
iframe.style.aspectRatio = "2"; // Wide aspect for split view
// Point to our UV viewer HTML
iframe.src = `/extensions/${EXTENSION_FOLDER}/viewer_uv.html?v=` + Date.now();
// Add widget
const widget = this.addDOMWidget("uv_preview", "MESH_UV_PREVIEW", iframe, {
getValue() { return ""; },
setValue(v) { }
});
// Set widget size - wider for split view
widget.computeSize = function(width) {
const w = width || 600;
const h = w / 2; // 2:1 aspect ratio
return [w, h];
};
widget.element = iframe;
// Store iframe reference
this.uvViewerIframe = iframe;
// Set initial node size (wide for split view)
this.setSize([600, 350]);
// Handle execution
const onExecuted = this.onExecuted;
this.onExecuted = function(message) {
console.log("[GeomPack UV] onExecuted called with message:", message);
onExecuted?.apply(this, arguments);
if (message?.mesh_file && message.mesh_file[0]) {
const meshFilename = message.mesh_file[0];
const uvDataFilename = message.uv_data_file ? message.uv_data_file[0] : null;
const showChecker = message.show_checker ? message.show_checker[0] : false;
const showWireframe = message.show_wireframe ? message.show_wireframe[0] : true;
console.log(`[GeomPack UV] Loading mesh: ${meshFilename}`);
console.log(`[GeomPack UV] UV data: ${uvDataFilename}`);
console.log(`[GeomPack UV] Checker: ${showChecker}, Wireframe: ${showWireframe}`);
// Build file URLs
const meshPath = `/view?filename=${encodeURIComponent(meshFilename)}&type=output&subfolder=`;
const uvDataPath = uvDataFilename ?
`/view?filename=${encodeURIComponent(uvDataFilename)}&type=output&subfolder=` : null;
// Send message to iframe
setTimeout(() => {
if (iframe.contentWindow) {
console.log(`[GeomPack UV] Sending postMessage to iframe`);
iframe.contentWindow.postMessage({
type: "LOAD_MESH_UV",
meshPath: meshPath,
uvDataPath: uvDataPath,
checker: showChecker,
wireframe: showWireframe,
timestamp: Date.now()
}, "*");
} else {
console.error("[GeomPack UV] Iframe contentWindow not available");
}
}, 100);
// Update node title with UV info
if (message.has_uvs && message.has_uvs[0]) {
const coverage = message.uv_coverage ? message.uv_coverage[0].toFixed(3) : "?";
const inUnit = message.uv_in_unit_square ? message.uv_in_unit_square[0] : false;
console.log(`[GeomPack UV] UV Coverage: ${coverage}, In Unit Square: ${inUnit}`);
} else {
console.warn("[GeomPack UV] No UV data found on mesh");
}
} else {
console.log("[GeomPack UV] No mesh_file in message data. Keys:", Object.keys(message || {}));
}
};
return r;
};
}
}
});
console.log("[GeomPack] UV mesh preview extension registered");