Files
ComfyAssets-ComfyUI-KikoTools/web/display_any.js
T
Vito Sansevero 5485aa8c19 feat(xyz-helpers): add ComfyUI_essentials nodes adaptation
BREAKING CHANGE: Node categories now use emoji-based organization

Add 6 new xyz-helper nodes adapted from comfyui-essentials-nodes:
- FluxSamplerParams: FLUX-optimized parameter generator with batch support
- LoRAFolderBatch: Batch process multiple LoRAs from folders
- PlotParameters: Visualize parameter effects with graphs
- SamplerSelectHelper: Intelligent sampler selection with recommendations
- SchedulerSelectHelper: Optimal scheduler selection for samplers
- TextEncodeSamplerParams: Combined text encoding and parameter management

Changes:
- Port and enhance nodes from comfyui-essentials (now in maintenance mode)
- Add comprehensive documentation with attribution to original author (cubiq)
- Create example workflows for xyz-helpers tools
- Update all node categories to use emoji-based organization
- Fix all unit tests to pass with new category system
- Update README with xyz-helpers section and attribution

Attribution: xyz-helpers adapted from github.com/cubiq/ComfyUI_essentials

All tests passing (318 pass, 2 skip)
2025-08-07 05:41:23 -07:00

195 lines
9.5 KiB
JavaScript

import { app } from "../../../scripts/app.js";
app.registerExtension({
name: "ComfyAssets.DisplayAny",
async beforeRegisterNodeDef(nodeType, nodeData, app) {
if (nodeData.name === "DisplayAny") {
const onExecuted = nodeType.prototype.onExecuted;
nodeType.prototype.onExecuted = function(message) {
onExecuted?.apply(this, arguments);
console.log("DisplayAny onExecuted message:", message);
if (message?.text && message.text.length > 0) {
const displayText = message.text[0];
console.log("DisplayAny displayText:", displayText);
// Update the display widget with the value
this.updateDisplay(displayText);
// Also show a condensed version in the title
const firstLine = displayText.split('\n')[0];
const condensed = firstLine.length > 50
? firstLine.substring(0, 50) + "..."
: firstLine;
this.title = `DisplayAny: ${condensed}`;
} else {
console.log("DisplayAny no text in message");
}
};
nodeType.prototype.updateDisplay = function(text) {
// Remove existing display widget if any
const existingWidget = this.widgets?.find(w => w.name === "display_value");
if (existingWidget) {
const index = this.widgets.indexOf(existingWidget);
this.widgets.splice(index, 1);
}
// Create display widget
const widget = {
type: "custom_display",
name: "display_value",
size: [this.size[0] - 20, 80],
displayText: text || "",
scrollY: 0,
maxScrollHeight: 0,
draw: function(ctx, node, widget_width, y, H) {
const margin = 10;
const padding = 10;
const lineHeight = 16;
const minHeight = 60;
// Fixed viewport height for scrollable area
const viewportHeight = 200; // Fixed height for display area
ctx.font = "12px monospace";
const lines = this.displayText ? this.displayText.split('\n') : [""];
// Draw background
ctx.fillStyle = "#2a2a2a";
ctx.fillRect(margin, y, widget_width - margin * 2, viewportHeight);
// Draw border
ctx.strokeStyle = "#444";
ctx.strokeRect(margin, y, widget_width - margin * 2, viewportHeight);
// Draw text area background
ctx.fillStyle = "#1e1e1e";
ctx.fillRect(margin + 1, y + 1, widget_width - margin * 2 - 2, viewportHeight - 2);
// Save context for clipping
ctx.save();
ctx.beginPath();
ctx.rect(margin + 1, y + 1, widget_width - margin * 2 - 2, viewportHeight - 2);
ctx.clip();
// Prepare text
ctx.fillStyle = "#ddd";
ctx.textAlign = "left";
ctx.textBaseline = "top";
// Draw each line with scrolling
const maxWidth = widget_width - margin * 2 - padding * 2;
let currentY = y + padding - this.scrollY;
// Process text - wrap long lines for JSON
let processedLines = [];
for (const line of lines) {
if (line.length > 0) {
// Split long lines into chunks that fit
let remaining = line;
while (remaining.length > 0) {
let chunkSize = remaining.length;
while (chunkSize > 0 && ctx.measureText(remaining.substring(0, chunkSize)).width > maxWidth) {
chunkSize--;
}
if (chunkSize === 0) chunkSize = 1; // At least one character
processedLines.push(remaining.substring(0, chunkSize));
remaining = remaining.substring(chunkSize);
}
} else {
processedLines.push(line);
}
}
// Calculate total content height for scrolling
const totalContentHeight = processedLines.length * lineHeight + padding * 2;
this.maxScrollHeight = Math.max(0, totalContentHeight - viewportHeight);
// Draw all visible lines
for (let i = 0; i < processedLines.length; i++) {
// Only draw if line is in viewport
if (currentY > y - lineHeight && currentY < y + viewportHeight) {
ctx.fillText(processedLines[i], margin + padding, currentY);
}
currentY += lineHeight;
}
// Restore context
ctx.restore();
// Draw scrollbar if needed
if (this.maxScrollHeight > 0) {
const scrollbarWidth = 6;
const scrollbarX = margin + widget_width - margin * 2 - scrollbarWidth - 2;
const scrollbarHeight = Math.max(20, (viewportHeight / totalContentHeight) * viewportHeight);
const scrollbarY = y + 2 + (this.scrollY / this.maxScrollHeight) * (viewportHeight - scrollbarHeight - 4);
// Scrollbar track
ctx.fillStyle = "#333";
ctx.fillRect(scrollbarX, y + 2, scrollbarWidth, viewportHeight - 4);
// Scrollbar thumb
ctx.fillStyle = "#666";
ctx.fillRect(scrollbarX, scrollbarY, scrollbarWidth, scrollbarHeight);
}
return viewportHeight;
},
computeSize: function(width) {
// Fixed height for scrollable viewport
const viewportHeight = 200;
return [width, viewportHeight];
}
};
// Add the widget
if (!this.widgets) {
this.widgets = [];
}
this.widgets.push(widget);
// Adjust node size
this.computeSize();
this.setDirtyCanvas(true);
};
// Initialize on node creation
const onNodeCreated = nodeType.prototype.onNodeCreated;
nodeType.prototype.onNodeCreated = function() {
onNodeCreated?.apply(this, arguments);
// Set minimum size - make it wider for better JSON display
this.size[0] = Math.max(this.size[0], 400);
this.size[1] = Math.max(this.size[1], 250); // Increased for viewport
// Add placeholder text
this.updateDisplay("Value will appear here...");
// Mark this node as having a scrollable widget
this.flags = this.flags || {};
this.flags.allow_interaction = true;
};
// Handle mouse wheel events on the node
const onMouseWheel = nodeType.prototype.onMouseWheel;
nodeType.prototype.onMouseWheel = function(event, local_pos, delta) {
// Check if we have a display widget
const displayWidget = this.widgets?.find(w => w.name === "display_value");
if (displayWidget && displayWidget.maxScrollHeight > 0) {
// Scroll by 3 lines at a time
const scrollStep = 48; // 3 lines * 16px
displayWidget.scrollY = Math.max(0, Math.min(displayWidget.maxScrollHeight, displayWidget.scrollY - delta[1] * scrollStep));
this.setDirtyCanvas(true);
return true; // Consume the event
}
// Call original handler if exists
return onMouseWheel?.apply(this, arguments) || false;
};
}
}
});