diff --git a/py/nodes/debug_metadata.py b/py/nodes/debug_metadata.py
index 7489c7dc..e9175508 100644
--- a/py/nodes/debug_metadata.py
+++ b/py/nodes/debug_metadata.py
@@ -1,15 +1,15 @@
import logging
-from server import PromptServer # type: ignore
from ..metadata_collector.metadata_processor import MetadataProcessor
logger = logging.getLogger(__name__)
+
class DebugMetadata:
NAME = "Debug Metadata (LoraManager)"
CATEGORY = "Lora Manager/utils"
DESCRIPTION = "Debug node to verify metadata_processor functionality"
OUTPUT_NODE = True
-
+
@classmethod
def INPUT_TYPES(cls):
return {
@@ -25,21 +25,37 @@ class DebugMetadata:
FUNCTION = "process_metadata"
def process_metadata(self, images, id):
+ """
+ Process metadata from the execution context and return it for UI display.
+
+ The metadata is returned via the 'ui' key in the return dict, which triggers
+ node.onExecuted on the frontend to update the JsonDisplayWidget.
+
+ Args:
+ images: Input images (required for execution flow)
+ id: Node's unique ID (hidden)
+
+ Returns:
+ Dict with 'result' (empty tuple) and 'ui' (metadata dict for widget display)
+ """
try:
# Get the current execution context's metadata
from ..metadata_collector import get_metadata
+
metadata = get_metadata()
-
- # Use the MetadataProcessor to convert it to JSON string
- metadata_json = MetadataProcessor.to_json(metadata, id)
-
- # Send metadata to frontend for display
- PromptServer.instance.send_sync("metadata_update", {
- "id": id,
- "metadata": metadata_json
- })
-
+
+ # Use the MetadataProcessor to convert it to dict
+ metadata_dict = MetadataProcessor.to_dict(metadata, id)
+
+ return {
+ "result": (),
+ # ComfyUI expects ui values to be lists, wrap the dict in a list
+ "ui": {"metadata": [metadata_dict]},
+ }
+
except Exception as e:
logger.error(f"Error processing metadata: {e}")
-
- return ()
+ return {
+ "result": (),
+ "ui": {"metadata": [{"error": str(e)}]},
+ }
diff --git a/vue-widgets/src/components/JsonDisplayWidget.vue b/vue-widgets/src/components/JsonDisplayWidget.vue
new file mode 100644
index 00000000..80818a72
--- /dev/null
+++ b/vue-widgets/src/components/JsonDisplayWidget.vue
@@ -0,0 +1,159 @@
+
+
+
+
+
+
+
diff --git a/vue-widgets/src/main.ts b/vue-widgets/src/main.ts
index d22fb8c7..66455640 100644
--- a/vue-widgets/src/main.ts
+++ b/vue-widgets/src/main.ts
@@ -2,6 +2,7 @@ import { createApp, type App as VueApp } from 'vue'
import PrimeVue from 'primevue/config'
import LoraPoolWidget from '@/components/LoraPoolWidget.vue'
import LoraRandomizerWidget from '@/components/LoraRandomizerWidget.vue'
+import JsonDisplayWidget from '@/components/JsonDisplayWidget.vue'
import type { LoraPoolConfig, LegacyLoraPoolConfig, RandomizerConfig } from './composables/types'
const LORA_POOL_WIDGET_MIN_WIDTH = 500
@@ -9,6 +10,8 @@ const LORA_POOL_WIDGET_MIN_HEIGHT = 400
const LORA_RANDOMIZER_WIDGET_MIN_WIDTH = 500
const LORA_RANDOMIZER_WIDGET_MIN_HEIGHT = 510
const LORA_RANDOMIZER_WIDGET_MAX_HEIGHT = LORA_RANDOMIZER_WIDGET_MIN_HEIGHT
+const JSON_DISPLAY_WIDGET_MIN_WIDTH = 300
+const JSON_DISPLAY_WIDGET_MIN_HEIGHT = 200
// @ts-ignore - ComfyUI external module
import { app } from '../../../scripts/app.js'
@@ -207,6 +210,72 @@ function createLoraRandomizerWidget(node) {
return { widget }
}
+// @ts-ignore
+function createJsonDisplayWidget(node) {
+ const container = document.createElement('div')
+ container.id = `json-display-widget-${node.id}`
+ container.style.width = '100%'
+ container.style.height = '100%'
+ container.style.display = 'flex'
+ container.style.flexDirection = 'column'
+ container.style.overflow = 'hidden'
+
+ forwardMiddleMouseToCanvas(container)
+
+ let internalValue: Record | undefined
+
+ const widget = node.addDOMWidget(
+ 'metadata',
+ 'JSON_DISPLAY',
+ container,
+ {
+ getValue() {
+ return internalValue
+ },
+ setValue(v: Record) {
+ internalValue = v
+ if (typeof widget.onSetValue === 'function') {
+ widget.onSetValue(v)
+ }
+ },
+ serialize: false, // Display-only widget - don't save metadata in workflows
+ getMinHeight() {
+ return JSON_DISPLAY_WIDGET_MIN_HEIGHT
+ }
+ }
+ )
+
+ const vueApp = createApp(JsonDisplayWidget, {
+ widget,
+ node
+ })
+
+ vueApp.use(PrimeVue, {
+ unstyled: true,
+ ripple: false
+ })
+
+ vueApp.mount(container)
+ vueApps.set(node.id + 20000, vueApp) // Offset to avoid collision with other widgets
+
+ widget.computeLayoutSize = () => {
+ const minWidth = JSON_DISPLAY_WIDGET_MIN_WIDTH
+ const minHeight = JSON_DISPLAY_WIDGET_MIN_HEIGHT
+
+ return { minHeight, minWidth }
+ }
+
+ widget.onRemove = () => {
+ const vueApp = vueApps.get(node.id + 20000)
+ if (vueApp) {
+ vueApp.unmount()
+ vueApps.delete(node.id + 20000)
+ }
+ }
+
+ return { widget }
+}
+
app.registerExtension({
name: 'LoraManager.VueWidgets',
@@ -238,5 +307,20 @@ app.registerExtension({
return addLorasWidgetCache(node, 'loras', { isRandomizerNode }, callback)
}
}
+ },
+
+ // Add display-only widget to Debug Metadata node
+ // @ts-ignore
+ async beforeRegisterNodeDef(nodeType, nodeData) {
+ if (nodeData.name === 'Debug Metadata (LoraManager)') {
+ const onNodeCreated = nodeType.prototype.onNodeCreated
+
+ nodeType.prototype.onNodeCreated = function () {
+ onNodeCreated?.apply(this, [])
+
+ // Add the JSON display widget
+ createJsonDisplayWidget(this)
+ }
+ }
}
})
diff --git a/web/comfyui/debug_metadata.js b/web/comfyui/debug_metadata.js
deleted file mode 100644
index bcbb893f..00000000
--- a/web/comfyui/debug_metadata.js
+++ /dev/null
@@ -1,53 +0,0 @@
-import { app } from "../../scripts/app.js";
-import { api } from "../../scripts/api.js";
-import { addJsonDisplayWidget } from "./json_display_widget.js";
-import { getNodeFromGraph } from "./utils.js";
-
-app.registerExtension({
- name: "LoraManager.DebugMetadata",
-
- setup() {
- // Add message handler to listen for metadata updates from Python
- api.addEventListener("metadata_update", (event) => {
- const { id, graph_id: graphId, metadata } = event.detail;
- this.handleMetadataUpdate(id, graphId, metadata);
- });
- },
-
- async nodeCreated(node) {
- if (node.comfyClass === "Debug Metadata (LoraManager)") {
- // Enable widget serialization
- node.serialize_widgets = true;
-
- // Add a widget to display metadata
- const jsonWidget = addJsonDisplayWidget(node, "metadata", {
- defaultVal: "",
- }).widget;
-
- // Store reference to the widget
- node.jsonWidget = jsonWidget;
-
- // Restore saved value if exists
- if (node.widgets_values && node.widgets_values.length > 0) {
- const savedValue = node.widgets_values[0];
- if (savedValue) {
- jsonWidget.value = savedValue;
- }
- }
- }
- },
-
- // Handle metadata updates from Python
- handleMetadataUpdate(id, graphId, metadata) {
- const node = getNodeFromGraph(graphId, id);
- if (!node || node.comfyClass !== "Debug Metadata (LoraManager)") {
- console.warn("Node not found or not a DebugMetadata node:", id);
- return;
- }
-
- if (node.jsonWidget) {
- // Update the widget with the received metadata
- node.jsonWidget.value = metadata;
- }
- }
-});
\ No newline at end of file
diff --git a/web/comfyui/json_display_widget.js b/web/comfyui/json_display_widget.js
deleted file mode 100644
index c0b3f7c4..00000000
--- a/web/comfyui/json_display_widget.js
+++ /dev/null
@@ -1,144 +0,0 @@
-import { forwardMiddleMouseToCanvas } from "./utils.js";
-
-export function addJsonDisplayWidget(node, name, opts) {
- // Create container for JSON display
- const container = document.createElement("div");
- container.className = "comfy-json-display-container";
-
- forwardMiddleMouseToCanvas(container);
-
- // Set initial height
- const defaultHeight = 200;
-
- Object.assign(container.style, {
- display: "block",
- padding: "8px",
- backgroundColor: "rgba(40, 44, 52, 0.6)",
- borderRadius: "6px",
- width: "100%",
- boxSizing: "border-box",
- overflow: "auto",
- overflowY: "scroll",
- maxHeight: `${defaultHeight}px`,
- fontFamily: "monospace",
- fontSize: "12px",
- lineHeight: "1.5",
- whiteSpace: "pre-wrap",
- color: "rgba(226, 232, 240, 0.9)"
- });
-
- // Initialize default value
- const initialValue = opts?.defaultVal || "";
-
- // Function to format and display JSON content with syntax highlighting
- const displayJson = (jsonString, widget) => {
- try {
- // If string is empty, show placeholder
- if (!jsonString || jsonString.trim() === '') {
- container.textContent = "No metadata available";
- container.style.fontStyle = "italic";
- container.style.color = "rgba(226, 232, 240, 0.6)";
- container.style.textAlign = "center";
- container.style.padding = "20px 0";
- return;
- }
-
- // Try to parse and pretty-print if it's valid JSON
- try {
- const jsonObj = JSON.parse(jsonString);
- container.innerHTML = syntaxHighlight(JSON.stringify(jsonObj, null, 2));
- } catch (e) {
- // If not valid JSON, display as-is
- container.textContent = jsonString;
- }
-
- container.style.fontStyle = "normal";
- container.style.textAlign = "left";
- container.style.padding = "8px";
- } catch (error) {
- console.error("Error displaying JSON:", error);
- container.textContent = "Error displaying content";
- }
- };
-
- // Function to add syntax highlighting to JSON
- function syntaxHighlight(json) {
- // Color scheme
- const colors = {
- key: "#6ad6f5", // Light blue for keys
- string: "#98c379", // Soft green for strings
- number: "#e5c07b", // Amber for numbers
- boolean: "#c678dd", // Purple for booleans
- null: "#7f848e" // Gray for null
- };
-
- // Replace JSON syntax with highlighted HTML
- json = json.replace(/&/g, '&').replace(//g, '>');
-
- return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
- let cls = 'number';
- let color = colors.number;
-
- if (/^"/.test(match)) {
- if (/:$/.test(match)) {
- cls = 'key';
- color = colors.key;
- // Remove the colon from the key and add it back without color
- match = match.replace(/:$/, '');
- return '' + match + ':';
- } else {
- cls = 'string';
- color = colors.string;
- }
- } else if (/true|false/.test(match)) {
- cls = 'boolean';
- color = colors.boolean;
- } else if (/null/.test(match)) {
- cls = 'null';
- color = colors.null;
- }
-
- return '' + match + '';
- });
- }
-
- // Store the value
- let widgetValue = initialValue;
-
- // Create widget with DOM Widget API
- const widget = node.addDOMWidget(name, "custom", container, {
- getValue: function() {
- return widgetValue;
- },
- setValue: function(v) {
- widgetValue = v;
- displayJson(widgetValue, widget);
- },
- hideOnZoom: true
- });
-
- // Set initial value
- widget.value = initialValue;
-
- widget.serializeValue = () => {
- return widgetValue;
- };
-
- // Update widget when node is resized
- const onNodeResize = node.onResize;
- node.onResize = function(size) {
- if(onNodeResize) {
- onNodeResize.call(this, size);
- }
-
- // Adjust container height to node height
- if(size && size[1]) {
- // Reduce the offset to minimize the gap at the bottom
- const widgetHeight = Math.min(size[1] - 30, defaultHeight * 2); // Reduced from 80 to 30
- container.style.maxHeight = `${widgetHeight}px`;
- container.style.setProperty('--comfy-widget-height', `${widgetHeight}px`);
- }
- };
-
- return { minWidth: 300, minHeight: defaultHeight, widget };
-}