Files
VrchStudio-comfyui-web-viewer/web/comfyui/node_utils.js
T

277 lines
11 KiB
JavaScript

// Enable debug logging
export const ENABLE_DEBUG = false;
// Load QRCode library immediately when this utility module loads
// This is better than loading it in websocket_nodes.js since it's only used here
const script = document.createElement('script');
script.src = new URL('./lib/qrcode.js', import.meta.url).href;
document.head.appendChild(script);
// Helper functions to hide and show widgets
export function hideWidget(node, widget) {
// If widget is already hidden, do nothing
if (widget.type === "hidden") return;
// Save original type and computeSize so it can be restored later
widget.origType = widget.type;
widget.origComputeSize = widget.computeSize;
widget.type = "hidden";
}
export function showWidget(node, widget) {
// Restore the widget's original type and computeSize
widget.type = widget.origType;
widget.computeSize = widget.origComputeSize;
}
/**
* Helper function to build the URL based on configuration.
*
* @param {Object} config - The configuration object with the following properties:
* serverWidget: widget for server address
* sslWidget: widget for SSL flag (boolean)
* extraParamsWidget: widget for extra parameters (string)
* mode: string specifying the viewer mode (e.g., "image", "video", etc.)
* path: string specifying the path (default is "web_viewer")
* protocol: (optional) protocol to use (default is "http", also support "websocket")
* channel: (optional) channel number (default is "1", [1-8] based)
* fileGenerator: a function that returns the filename (receives the config as argument)
* additionalParams: (optional) extra query parameters as an object (keys with widget values) or string
*
* @returns {string} The constructed URL.
*/
export function buildUrl(config) {
// Retrieve server address and SSL flag (with default values)
const server = config.serverWidget ? config.serverWidget.value : "127.0.0.1:8188";
const ssl = config.sslWidget ? config.sslWidget.value : false;
const sslStr = ssl ? "true" : "false";
const scheme = ssl ? "https" : "http";
// Process extra parameters; if not empty and not starting with '&', prepend '&'
let extraParams = config.extraParamsWidget ? config.extraParamsWidget.value : "";
if (extraParams && extraParams[0] !== "&") {
extraParams = "&" + extraParams;
}
// Use provided mode and path (with a default for path)
const mode = config.mode || "";
const path = config.path || "web_viewer";
const pathStr = path ? `&path=${path}` : "";
// Generate the filename using the provided fileGenerator callback
const filename = config.fileGenerator ? config.fileGenerator(config) : "";
const filenameStr = filename ? `&filename=${filename}` : "";
// Process additional query parameters if provided
let additionalQuery = "";
if (config.additionalParams && typeof config.additionalParams === "object") {
// Assume each value in the object is a widget; extract its value
additionalQuery = Object.entries(config.additionalParams)
.map(([key, widget]) => `&${key}=${widget.value}`)
.join("");
} else if (typeof config.additionalParams === "string") {
additionalQuery = "&" + config.additionalParams;
}
let protocolStr = "";
let channelStr = "";
// If config.protocol is not http, insert the protocol in the URL before the server
if (config.protocol) {
protocolStr = `&protocol=${config.protocol}`;
}
if (config.channel) {
channelStr = `&channel=${config.channel}`;
}
// Check if dev mode is enabled
const devMode = config.devMode ? config.devMode.value : false;
const viewerPath = devMode ? "dev/viewer" : "viewer";
// Construct the URL using the provided parameters
const url = `${scheme}://vrch.ai/${viewerPath}?mode=${mode}&server=${server}&ssl=${sslStr}${protocolStr}${channelStr}${filenameStr}${pathStr}${extraParams}${additionalQuery}`;
// Return the final URL
return url;
}
/**
* Helper function to initialize the URL and widget visibility after a delay.
*
* @param {Object} node - The current node.
* @param {Object} showUrlWidget - The widget controlling URL visibility.
* @param {Object} urlWidget - The URL widget.
* @param {Function} updateUrl - The updateUrl callback.
* @param {Object} showQrCodeWidget - The widget controlling QR code visibility.
* @param {Object} qrCodeControl - The QR code control object with updateQRCode method.
*/
export function delayInit(node, showUrlWidget, urlWidget, updateUrl, showQrCodeWidget = null, qrCodeControl = null) {
setTimeout(() => {
// Handle URL widget visibility
if (showUrlWidget) {
showUrlWidget.value ? showWidget(node, urlWidget) : hideWidget(node, urlWidget);
}
// Handle QR code widget visibility
if (showQrCodeWidget && qrCodeControl) {
showQrCodeWidget.value ? qrCodeControl.showQrCode() : qrCodeControl.hideQrCode();
}
// Update URL (which will also update QR code if needed)
updateUrl();
}, 1000);
}
/**
* Helper function to create the "Open Web Viewer" button.
*
* @param {Object} node - The current node.
* @param {Object} urlWidget - The widget containing the URL.
* @param {Object} widthWidget - The widget for window width.
* @param {Object} heightWidget - The widget for window height.
*/
export function createOpenWebViewerButton(node, urlWidget, widthWidget, heightWidget) {
// Create a container with fixed height for the button
const container = document.createElement("div");
container.classList.add("vrch-button-container");
const button = document.createElement("button");
button.textContent = "Open Web Viewer";
button.classList.add("vrch-big-button");
button.onclick = () => {
if (urlWidget && urlWidget.value) {
const width = widthWidget ? widthWidget.value : 1280;
const height = heightWidget ? heightWidget.value : 960;
window.open(urlWidget.value, "_blank", `width=${width},height=${height}`);
} else {
console.error("URL widget not found or empty");
}
};
// Add button to container
container.appendChild(button);
// Add the container (with button inside) as widget
const widget = node.addDOMWidget("button_widget", "Open Web Viewer", container);
// Override the computeSize method to force correct sizing
widget.computeSize = function(width) {
return [width, 60]; // Force height to be 48px
};
}
/**
* Helper function to create the QR Code display widget.
*
* @param {Object} node - The current node.
* @param {Object} urlWidget - The widget containing the URL to encode (used as fallback).
* @param {Object} showQrCodeWidget - The widget controlling QR code visibility.
* @returns {Object} Object with updateQRCode(text) method for external updates.
*/
export function createQRCodeWidget(node, urlWidget, showQrCodeWidget) {
const container = document.createElement("div");
container.classList.add("vrch-qr-widget-container");
container.style.display = "none"; // Initially hidden
const qrContainer = document.createElement("div");
qrContainer.classList.add("vrch-qr-container");
const qrDiv = document.createElement("div");
qrDiv.classList.add("vrch-qr-code");
qrContainer.appendChild(qrDiv);
container.appendChild(qrContainer);
// Function to update QR code content based on provided text
const updateQRCode = (text = null) => {
// Use provided text or fallback to urlWidget value
const qrText = text || (urlWidget && urlWidget.value) || "";
if (qrText) {
try {
qrDiv.innerHTML = ""; // Clear previous QR code
// Check if QRCode class is available - simple check like official example
if (typeof window.QRCode !== 'undefined') {
// Generate new QR code - just like the official example: var qrcode = new QRCode(document.getElementById("qrcode"))
new window.QRCode(qrDiv, {
text: qrText,
width: 200,
height: 200,
colorDark: "#000000",
colorLight: "#ffffff",
correctLevel: window.QRCode.CorrectLevel.L
});
} else {
console.error("QRCode library not available");
qrDiv.textContent = "QR code library not available";
}
} catch (error) {
console.error("Failed to generate QR code:", error);
qrDiv.textContent = "QR code generation failed: " + error.message;
}
} else {
qrDiv.textContent = "No text to display";
}
};
const showQrCode = () => {
container.style.display = "block";
// When showing, always update with the current URL from urlWidget as fallback
updateQRCode();
};
const hideQrCode = () => {
container.style.display = "none";
};
// Setup visibility control using the new methods
if (showQrCodeWidget) {
showQrCodeWidget.callback = (value) => {
if (value) {
showQrCode();
} else {
hideQrCode();
}
};
}
// Add the container to the node
const widget = node.addDOMWidget("qr_code_widget", "QR Code", container);
// Override the computeSize method to force correct sizing
widget.computeSize = function(width) {
return [width, 0];
};
// Return the control object with all methods
return { updateQRCode, showQrCode, hideQrCode };
}
/**
* Helper function to set up widget callbacks for updating the URL and toggling visibility.
*
* @param {Object} node - The current node.
* @param {Function} updateUrl - The updateUrl function to be called on widget change.
* @param {Object} urlWidget - The URL widget to be toggled.
* @param {Object} showUrlWidget - The widget controlling URL visibility.
* @param {Array} widgets - An array of widgets whose callbacks will trigger updateUrl.
* @param {string} [logPrefix] - (Optional) A prefix to log for debugging.
*/
export function setupWidgetCallback(node, updateUrl, urlWidget, showUrlWidget, widgets, logPrefix) {
widgets.forEach(widget => {
if (widget) {
widget.callback = () => {
if (logPrefix && ENABLE_DEBUG) {
console.log(`${logPrefix}:::${widget.name}`);
}
updateUrl();
};
}
});
if (showUrlWidget) {
showUrlWidget.callback = (value) => {
if (value) {
showWidget(node, urlWidget);
} else {
hideWidget(node, urlWidget);
}
};
}
}