From 86c06429a28a4ffe6d251ccf65ccb0bc807d471c Mon Sep 17 00:00:00 2001 From: nofunstudio Date: Sat, 1 Feb 2025 19:55:09 -0700 Subject: [PATCH] no auto prompt test --- js/iframe_view.js | 566 +++++++++++++++++++++++++--------------------- 1 file changed, 307 insertions(+), 259 deletions(-) diff --git a/js/iframe_view.js b/js/iframe_view.js index ef6ad2f..194febd 100644 --- a/js/iframe_view.js +++ b/js/iframe_view.js @@ -7,290 +7,338 @@ window.TEXTURE_STORE = window.TEXTURE_STORE || {}; // Function to create the iframe widget async function widgetIframe(node, nodeData, inputData, app) { - // Create iframe wrapper with some styling - const iframeWrapper = $el("div.iframeWrapper", { - style: { - width: "100%", - height: "100%", - overflow: "hidden", - border: "1px solid var(--border-color)", - borderRadius: "4px" - } - }); + // Create iframe wrapper with some styling + const iframeWrapper = $el("div.iframeWrapper", { + style: { + width: "100%", + height: "100%", + overflow: "hidden", + border: "1px solid var(--border-color)", + borderRadius: "4px", + }, + }); - // Create the iframe element - const iframe = $el("iframe", { - style: { - width: "100%", - height: "100%", - border: "none" - }, - sandbox: "allow-scripts allow-same-origin allow-forms", - loading: "lazy" - }); + // Create the iframe element + const iframe = $el("iframe", { + style: { + width: "100%", + height: "100%", + border: "none", + }, + sandbox: "allow-scripts allow-same-origin allow-forms", + loading: "lazy", + }); - iframeWrapper.appendChild(iframe); + iframeWrapper.appendChild(iframe); - // Locate our parameter widgets - const urlWidget = node.widgets.find((w) => w.name === "url"); - const widthWidget = node.widgets.find((w) => w.name === "width"); - const heightWidget = node.widgets.find((w) => w.name === "height"); - const stateWidget = node.widgets.find((w) => w.name === "scene_state"); + // Locate our parameter widgets + const urlWidget = node.widgets.find((w) => w.name === "url"); + const widthWidget = node.widgets.find((w) => w.name === "width"); + const heightWidget = node.widgets.find((w) => w.name === "height"); + const stateWidget = node.widgets.find((w) => w.name === "scene_state"); - // Set initial URL if provided - if (urlWidget.value) { - iframe.src = urlWidget.value; - } + // Set initial URL if provided + if (urlWidget.value) { + iframe.src = urlWidget.value; + } - // Message handler for communication from the iframe - function messageHandler(event) { - // Ensure the message comes from our iframe - if (event.source !== iframe.contentWindow) return; - - console.log("[messageHandler] Received message:", event.data); - - const { type, data } = event.data; - switch (type) { - case 'IFRAME_READY': - sendParamsToIframe(); - break; - case 'TEXTURE_OUTPUT': - console.log("[messageHandler] TEXTURE_OUTPUT received with data:", data); - handleTextureOutput(data); - break; - case 'SCENE_STATE_UPDATED': - if (data.state) { - stateWidget.value = JSON.stringify(data.state, null, 2); - } - break; - default: - console.warn("[messageHandler] Unhandled message type:", type); - } - } + // Message handler for communication from the iframe + function messageHandler(event) { + // Ensure the message comes from our iframe + if (event.source !== iframe.contentWindow) return; - // Attach the message handler - window.addEventListener("message", messageHandler, false); + console.log("[messageHandler] Received message:", event.data); - // Function to send parameters (width, height, scene state) to the iframe - function sendParamsToIframe() { - let sceneState; - try { - sceneState = JSON.parse(stateWidget.value || "{}"); - } catch (e) { - console.error("Error parsing scene state, using default:", e); - sceneState = {}; - } - const params = { - type: 'PARAMS_UPDATE', - data: { - width: parseInt(widthWidget.value, 10) || 512, - height: parseInt(heightWidget.value, 10) || 512, - sceneState, - } - }; + const { type, data } = event.data; + switch (type) { + case "IFRAME_READY": + sendParamsToIframe(); + break; + case "TEXTURE_OUTPUT": + console.log( + "[messageHandler] TEXTURE_OUTPUT received with data:", + data + ); + handleTextureOutput(data); + break; + case "SCENE_STATE_UPDATED": + if (data.state) { + stateWidget.value = JSON.stringify(data.state, null, 2); + } + break; + default: + console.warn("[messageHandler] Unhandled message type:", type); + } + } - if (iframe.contentWindow) { - // If you know the target origin, replace '*' with that string - iframe.contentWindow.postMessage(params, '*'); - } - } + // Attach the message handler + window.addEventListener("message", messageHandler, false); - // Function to handle texture output coming from the iframe - async function handleTextureOutput(data) { - // Make sure the node has a valid unique_id - if (!node.unique_id) { - console.warn("[handleTextureOutput] Node unique_id is not set, cannot update texture store."); - return; - } - // For each expected texture key, update the hidden widget and the global texture store. - const textureKeys = ["color", "canny", "depth", "normal"]; - textureKeys.forEach((key) => { - if (data[key]) { - node.setHiddenWidgetValue(key, { - data: data[key].data, - width: data[key].width, - height: data[key].height, - format: data[key].format, - type: data[key].type, - shape: data[key].shape - }); - } - }); + // Function to send parameters (width, height, scene state) to the iframe + function sendParamsToIframe() { + let sceneState; + try { + sceneState = JSON.parse(stateWidget.value || "{}"); + } catch (e) { + console.error("Error parsing scene state, using default:", e); + sceneState = {}; + } + const params = { + type: "PARAMS_UPDATE", + data: { + width: parseInt(widthWidget.value, 10) || 512, + height: parseInt(heightWidget.value, 10) || 512, + sceneState, + }, + }; - // Optionally clear heavy data from the global store if it is no longer needed - if (node.unique_id && window.TEXTURE_STORE[node.unique_id]) { - // You can conditionally delete or nullify large arrays after processing. - delete window.TEXTURE_STORE[node.unique_id]; - } - // Delay the prompt so that the global store has updated - setTimeout(() => { - console.log("[handleTextureOutput] Final global texture store for unique_id", node.unique_id, window.TEXTURE_STORE[node.unique_id]); - app.queuePrompt(); - }, 50); - } + if (iframe.contentWindow) { + // If you know the target origin, replace '*' with that string + iframe.contentWindow.postMessage(params, "*"); + } + } - // Create the DOM widget for this node and register cleanup - const widget = node.addDOMWidget("iframe", "custom_widget", iframeWrapper, { - getValue() { - return { - url: urlWidget.value, - width: widthWidget.value, - height: heightWidget.value, - scene_state: stateWidget.value - }; - }, - setValue(v) { - if (v.url) { - iframe.src = v.url; - urlWidget.value = v.url; - } - } - }); + // Function to handle texture output coming from the iframe + async function handleTextureOutput(data) { + // Make sure the node has a valid unique_id + if (!node.unique_id) { + console.warn( + "[handleTextureOutput] Node unique_id is not set, cannot update texture store." + ); + return; + } + // For each expected texture key, update the hidden widget and the global texture store. + const textureKeys = ["color", "canny", "depth", "normal"]; + textureKeys.forEach((key) => { + if (data[key]) { + node.setHiddenWidgetValue(key, { + data: data[key].data, + width: data[key].width, + height: data[key].height, + format: data[key].format, + type: data[key].type, + shape: data[key].shape, + }); + } + }); + console.log( + "[handleTextureOutput] Final global texture store for unique_id", + node.unique_id, + window.TEXTURE_STORE[node.unique_id] + ); + // Optionally clear heavy data from the global store if it is no longer needed - // Attach a reference to the iframe on the widget so we can access it later - widget.iframe = iframe; + // Delay the prompt so that the global store has updated + setTimeout(() => { + if (node.unique_id && window.TEXTURE_STORE[node.unique_id]) { + // You can conditionally delete or nullify large arrays after processing. + //delete window.TEXTURE_STORE[node.unique_id]; + } + const topApp = + (window.top && window.top !== window && window.top.app) || + (window.parent && window.parent !== window && window.parent.app) || + app; - // Handle resizing: update both node size and iframe dimensions, then re-send params - node.onResize = function () { - const width = parseInt(widthWidget.value, 10) || 512; - const height = parseInt(heightWidget.value, 10) || 512; - node.size = [width, Math.max(height + 40, 140)]; - iframe.style.width = width + "px"; - iframe.style.height = (height - 40) + "px"; - sendParamsToIframe(); - }; + if (topApp && typeof topApp.queuePrompt === "function") { + topApp.queuePrompt(); + } else { + console.error( + "queuePrompt is not available in the current app context." + ); + } + }, 50); + } - // When the URL widget changes, update the iframe's src - urlWidget.callback = function () { - iframe.src = this.value; - }; + // Create the DOM widget for this node and register cleanup + const widget = node.addDOMWidget("iframe", "custom_widget", iframeWrapper, { + getValue() { + return { + url: urlWidget.value, + width: widthWidget.value, + height: heightWidget.value, + scene_state: stateWidget.value, + }; + }, + setValue(v) { + if (v.url) { + iframe.src = v.url; + urlWidget.value = v.url; + } + }, + }); - // When any of the parameter widgets change, update the iframe with the new parameters - widthWidget.callback = heightWidget.callback = stateWidget.callback = function () { - sendParamsToIframe(); - }; + // Attach a reference to the iframe on the widget so we can access it later + widget.iframe = iframe; - // Override the default setHiddenWidgetValue to also update our global TEXTURE_STORE. - node.setHiddenWidgetValue = function (name, value) { - let widgetEntry = this.widgets.find(w => w.name === name); - if (widgetEntry) { - widgetEntry.value = value; - console.log(`[setHiddenWidgetValue] Updated widget '${name}' to:`, value); - } else { - // Create the widget if it does not exist. - widgetEntry = { name, value, isHidden: true }; - this.widgets.push(widgetEntry); - console.log(`[setHiddenWidgetValue] Created and set widget '${name}' to:`, value); - } - if (this.unique_id) { - window.TEXTURE_STORE = window.TEXTURE_STORE || {}; - window.TEXTURE_STORE[this.unique_id] = window.TEXTURE_STORE[this.unique_id] || {}; - window.TEXTURE_STORE[this.unique_id][name] = value; - console.log(`[setHiddenWidgetValue] Global texture store for unique_id '${this.unique_id}':`, window.TEXTURE_STORE[this.unique_id]); - } - }; + // Handle resizing: update both node size and iframe dimensions, then re-send params + node.onResize = function () { + const width = parseInt(widthWidget.value, 10) || 512; + const height = parseInt(heightWidget.value, 10) || 512; + node.size = [width, Math.max(height + 40, 140)]; + iframe.style.width = width + "px"; + iframe.style.height = height - 40 + "px"; + sendParamsToIframe(); + }; - // When creating the widget -node.cleanup = () => { - window.removeEventListener("message", messageHandler, false); - if (node.unique_id && window.TEXTURE_STORE[node.unique_id]) { - delete window.TEXTURE_STORE[node.unique_id]; - } - console.log("[cleanup] Cleaned up resources for node:", node.unique_id); - }; + // When the URL widget changes, update the iframe's src + urlWidget.callback = function () { + iframe.src = this.value; + }; - // Updated onSerialize: Strip out the heavy "data" field to avoid localStorage quota problems. - node.onSerialize = (nodeData) => { - const textureKeys = ["color", "canny", "depth", "normal"]; - nodeData.hidden = nodeData.hidden || {}; - textureKeys.forEach((key) => { - const widgetEntry = node.widgets.find(w => w.name === key); - if (widgetEntry && widgetEntry.value) { - // If the widget's value includes a "data" field, remove it from the serialization. - if ("data" in widgetEntry.value) { - const { data, ...metadata } = widgetEntry.value; - nodeData.hidden[key] = metadata; - } else { - nodeData.hidden[key] = widgetEntry.value; - } - } - }); - }; + // When any of the parameter widgets change, update the iframe with the new parameters + widthWidget.callback = + heightWidget.callback = + stateWidget.callback = + function () { + sendParamsToIframe(); + }; - return widget; + // Override the default setHiddenWidgetValue to also update our global TEXTURE_STORE. + node.setHiddenWidgetValue = function (name, value) { + let widgetEntry = this.widgets.find((w) => w.name === name); + if (widgetEntry) { + widgetEntry.value = value; + console.log(`[setHiddenWidgetValue] Updated widget '${name}' to:`, value); + } else { + // Create the widget if it does not exist. + widgetEntry = { name, value, isHidden: true }; + this.widgets.push(widgetEntry); + console.log( + `[setHiddenWidgetValue] Created and set widget '${name}' to:`, + value + ); + } + if (this.unique_id) { + window.TEXTURE_STORE = window.TEXTURE_STORE || {}; + window.TEXTURE_STORE[this.unique_id] = + window.TEXTURE_STORE[this.unique_id] || {}; + window.TEXTURE_STORE[this.unique_id][name] = value; + console.log( + `[setHiddenWidgetValue] Global texture store for unique_id '${this.unique_id}':`, + window.TEXTURE_STORE[this.unique_id] + ); + } + }; + + // When creating the widget + node.cleanup = () => { + window.removeEventListener("message", messageHandler, false); + if (node.unique_id && window.TEXTURE_STORE[node.unique_id]) { + delete window.TEXTURE_STORE[node.unique_id]; + } + console.log("[cleanup] Cleaned up resources for node:", node.unique_id); + }; + + // Updated onSerialize: Strip out the heavy "data" field to avoid localStorage quota problems. + node.onSerialize = (nodeData) => { + const textureKeys = ["color", "canny", "depth", "normal"]; + nodeData.hidden = nodeData.hidden || {}; + textureKeys.forEach((key) => { + const widgetEntry = node.widgets.find((w) => w.name === key); + if (widgetEntry && widgetEntry.value) { + // If the widget's value includes a "data" field, remove it from the serialization. + if ("data" in widgetEntry.value) { + const { data, ...metadata } = widgetEntry.value; + nodeData.hidden[key] = metadata; + } else { + nodeData.hidden[key] = widgetEntry.value; + } + } + }); + }; + + return widget; } // Register the extension and add extra menu options for the IframeView node app.registerExtension({ - name: "Iframe View", - async beforeRegisterNodeDef(nodeType, nodeData, app) { - if (nodeType.comfyClass === "IframeView") { - // Add extra menu options to refresh the iframe and trigger texture capture - nodeType.prototype.getExtraMenuOptions = function (_, options) { - options.unshift( - { - content: "🔄 Refresh Iframe", - callback: () => { - const domWidget = this.widgets.find(w => w.name === "iframe"); - if (domWidget && domWidget.iframe) { - const url = this.widgets.find(w => w.name === "url").value; - domWidget.iframe.src = url; - } - } - }, - { - content: "📸 Capture Textures", - callback: () => { - const domWidget = this.widgets.find(w => w.name === "iframe"); - if (domWidget?.iframe?.contentWindow) { - domWidget.iframe.contentWindow.postMessage( - { type: 'CAPTURE_TEXTURES' }, - '*' - ); - } - } - } - ); - }; + name: "Iframe View", + async beforeRegisterNodeDef(nodeType, nodeData, app) { + if (nodeType.comfyClass === "IframeView") { + // Add extra menu options to refresh the iframe and trigger texture capture + nodeType.prototype.getExtraMenuOptions = function (_, options) { + options.unshift( + { + content: "🔄 Refresh Iframe", + callback: () => { + const domWidget = this.widgets.find((w) => w.name === "iframe"); + if (domWidget && domWidget.iframe) { + const url = this.widgets.find((w) => w.name === "url").value; + domWidget.iframe.src = url; + } + }, + }, + { + content: "📸 Capture Textures", + callback: () => { + const domWidget = this.widgets.find((w) => w.name === "iframe"); + if (domWidget?.iframe?.contentWindow) { + domWidget.iframe.contentWindow.postMessage( + { type: "CAPTURE_TEXTURES" }, + "*" + ); + } + }, + } + ); + }; - const originalOnNodeCreated = nodeType.prototype.onNodeCreated; - nodeType.prototype.onNodeCreated = async function () { - // If nodeData.hidden definitions exist, add any missing widgets. - if (nodeData && nodeData.hidden) { - Object.keys(nodeData.hidden).forEach((key) => { - if (!this.widgets.find(widget => widget.name === key)) { - this.widgets.push({ name: key, value: nodeData.hidden[key] || null, isHidden: true }); - console.log(`[onNodeCreated] Hidden widget '${key}' added.`); - } - }); - } - const r = originalOnNodeCreated ? originalOnNodeCreated.apply(this, arguments) : undefined; - const widget = await widgetIframe(this, nodeData, nodeData, app); - this.onResize(); + const originalOnNodeCreated = nodeType.prototype.onNodeCreated; + nodeType.prototype.onNodeCreated = async function () { + // If nodeData.hidden definitions exist, add any missing widgets. + if (nodeData && nodeData.hidden) { + Object.keys(nodeData.hidden).forEach((key) => { + if (!this.widgets.find((widget) => widget.name === key)) { + this.widgets.push({ + name: key, + value: nodeData.hidden[key] || null, + isHidden: true, + }); + console.log(`[onNodeCreated] Hidden widget '${key}' added.`); + } + }); + } + const r = originalOnNodeCreated + ? originalOnNodeCreated.apply(this, arguments) + : undefined; + const widget = await widgetIframe(this, nodeData, nodeData, app); + this.onResize(); - // Synchronize the node's unique_id with the one passed from Python. - if (nodeData && nodeData.hidden && nodeData.hidden.unique_id) { - this.unique_id = nodeData.hidden.unique_id; - console.log(`[onNodeCreated] Using provided unique_id from nodeData: ${this.unique_id}`); - let uniqueWidget = this.widgets.find(w => w.name === "unique_id"); - if (uniqueWidget) { - uniqueWidget.value = this.unique_id; - } else { - this.widgets.push({ name: "unique_id", value: this.unique_id, isHidden: true }); - } - } else { - // Fallback: generate a unique id. - this.unique_id = Math.random().toString(36).substr(2, 9); - console.log(`[onNodeCreated] Generated fallback unique_id: ${this.unique_id}`); - let uniqueWidget = this.widgets.find(w => w.name === "unique_id"); - if (uniqueWidget) uniqueWidget.value = this.unique_id; - else this.widgets.push({ name: "unique_id", value: this.unique_id, isHidden: true }); - } - return r; - }; - } - } + // Synchronize the node's unique_id with the one passed from Python. + if (nodeData && nodeData.hidden && nodeData.hidden.unique_id) { + this.unique_id = nodeData.hidden.unique_id; + console.log( + `[onNodeCreated] Using provided unique_id from nodeData: ${this.unique_id}` + ); + let uniqueWidget = this.widgets.find((w) => w.name === "unique_id"); + if (uniqueWidget) { + uniqueWidget.value = this.unique_id; + } else { + this.widgets.push({ + name: "unique_id", + value: this.unique_id, + isHidden: true, + }); + } + } else { + // Fallback: generate a unique id. + this.unique_id = Math.random().toString(36).substr(2, 9); + console.log( + `[onNodeCreated] Generated fallback unique_id: ${this.unique_id}` + ); + let uniqueWidget = this.widgets.find((w) => w.name === "unique_id"); + if (uniqueWidget) uniqueWidget.value = this.unique_id; + else + this.widgets.push({ + name: "unique_id", + value: this.unique_id, + isHidden: true, + }); + } + return r; + }; + } + }, }); export { widgetIframe }; -