From c31f959fe22f065db3c07f60d39bf562dc37885a Mon Sep 17 00:00:00 2001 From: loficoding Date: Fri, 23 Jan 2026 03:58:58 -0700 Subject: [PATCH] Adding web folder back --- web/css/hud_styles.css | 64 ++++++++++ web/node_hud.js | 270 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 334 insertions(+) create mode 100644 web/css/hud_styles.css create mode 100644 web/node_hud.js diff --git a/web/css/hud_styles.css b/web/css/hud_styles.css new file mode 100644 index 0000000..2f67615 --- /dev/null +++ b/web/css/hud_styles.css @@ -0,0 +1,64 @@ +/* main dock container */ +.hud-dock { + /* VISUALS ONLY - NO POSITIONS OR MARGINS */ + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 4px; + box-sizing: border-box; + pointer-events: none; + + /* Decoration */ + background-color: rgba(20, 20, 20, 0.85); + border: 1px solid rgba(255, 255, 255, 0.15); + border-radius: 8px; + backdrop-filter: blur(4px); + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3); +} + +.hud-dot { + width: 12px; + height: 12px; + border-radius: 50%; + border: 1px solid rgba(0, 0, 0, 0.5); + transition: transform 0.1s ease; + cursor: help; +} + +.hud-dot:hover { + transform: scale(1.3); + border-color: white; +} + +.hud-chip { + display: flex; + align-items: center; + gap: 4px; + background-color: #222; + border: 1px solid #444; + border-radius: 8px; + padding: 2px 6px; + height: 20px; + white-space: nowrap; + color: #ccc; + font-family: Arial, sans-serif; + font-size: 10px; + pointer-events: auto; +} + +/* --- New Zoomed Out State --- */ +.hud-dock-zoomed-out { + /* When chips are hidden, the container would collapse. + We force it to "1 chip height" (approx 20-24px) + so the user still sees the dock exists. + */ + min-height: 24px; + + /* Optional: Make it slightly subtler when zoomed out */ + background-color: rgba(20, 20, 20, 0.6); + border-color: rgba(255, 255, 255, 0.1); +} + +.hud-dock-zoomed-out .hud-chip { + display: none; +} \ No newline at end of file diff --git a/web/node_hud.js b/web/node_hud.js new file mode 100644 index 0000000..9b39760 --- /dev/null +++ b/web/node_hud.js @@ -0,0 +1,270 @@ +import { app } from "../../scripts/app.js"; + +// 1. Inject CSS +const link = document.createElement("link"); +link.rel = "stylesheet"; +link.type = "text/css"; +link.href = "extensions/ComfyUI-Node-HUD/css/hud_styles.css"; +document.head.appendChild(link); + +app.registerExtension({ + name: "Comfy.NodeHUD", + + async beforeRegisterNodeDef(nodeType, nodeData, app) { + + // --- A. Toggle Menu Option --- + const getExtraMenuOptions = nodeType.prototype.getExtraMenuOptions; + nodeType.prototype.getExtraMenuOptions = function(_, options) { + if (getExtraMenuOptions) getExtraMenuOptions.apply(this, arguments); + options.push({ + content: "Toggle Stack Info", + callback: () => { + this.show_stack_info = !this.show_stack_info; + + // Bring node to front of the array so it renders on top of everything + if (this.show_stack_info){ + const nodes = app.graph._nodes; + const index = nodes.indexOf(this); + if (index > -1){ + nodes.splice(index, 1); + nodes.push(this); + } + } + app.graph.setDirtyCanvas(true, true); + } + }); + }; + + // --- B. The Main Loop (Replaces Draw) --- + // We hook into onDrawForeground to handle Position updates + const onDrawForeground = nodeType.prototype.onDrawForeground; + nodeType.prototype.onDrawForeground = function(ctx) { + if (onDrawForeground) onDrawForeground.apply(this, arguments); + + // 1. Cleanup: If toggled off, empty, OR COLLAPSED, remove the dock + const stackList = getHiddenNodes(this, app.graph); + + // ADDED: Check !this.flags.collapsed so HUD hides when node is collapsed + const shouldShow = this.show_stack_info && stackList.length > 0 && !this.flags.collapsed; + + if (!shouldShow) { + if (this.hudElement) { + this.hudElement.remove(); + this.hudElement = null; + } + return; + } + + // 2. Update the Dock (Position & Content) + updateDock(this, stackList, app.graph); + + // If movement is laggy this ensures the HUD follows mouse during drag + if(this.is_selected){ + app.graph.setDirtyCanvas(true, false); + } + }; + } +}); + +// --- HELPER: Find Hidden Nodes --- +function getHiddenNodes(parent, graph) { + const stackList = []; + if (!graph || !graph._nodes) return stackList; + + const parentIndex = graph._nodes.indexOf(parent); + + const pLeft = parent.pos[0]; + const pRight = parent.pos[0] + parent.size[0]; + const pTop = parent.pos[1]; + const pBottom = parent.pos[1] + parent.size[1]; + + for (let i = 0; i < graph._nodes.length; i++) { + const node = graph._nodes[i]; + + if (node.id === parent.id) continue; + + // Only include nodes that are "below" the parent in the stack + if (i > parentIndex) continue; + + const nx = node.pos[0]; + const ny = node.pos[1]; + + // Check if center of node is inside parent (simple hit test) + if (nx >= pLeft && nx < pRight && ny >= pTop && ny < pBottom) { + stackList.push(node); + } + } + return stackList; +} + +// --- HELPER: Get Link Color --- +function getLinkColor(link) { + if (!link) return "#aaaaff"; + const type = link.type; + const colorMap = LGraphCanvas.link_type_colors; + + if (colorMap[type]) return colorMap[type]; + + if (type === "STRING") return "#66ff66"; + if (type === "INT") return "#22cc22"; + if (type === "FLOAT") return "#22cc22"; + if (type === "LATENT") return "#ff99aa"; + if (type === "IMAGE") return "#66aaff"; + if (type === "MODEL") return "#6666aa"; + if (type === "VAE") return "#ff4444"; + if (type === "CLIP") return "#ffff66"; + if (type === "CONDITIONING") return "#aa6666"; + + return "#aaaaff"; +} + +// --- THE NEW DOM BUILDER --- +function updateDock(node, stackList, graph) { + // 1. Create Element if missing + if (!node.hudElement) { + node.hudElement = document.createElement("div"); + node.hudElement.className = "hud-dock"; + + // Fix Zooming/Scrolling Issue: + // Intercept wheel events to prevent browser page zoom, but manually trigger canvas zoom. + node.hudElement.addEventListener("wheel", (e) => { + // Stop the event from reaching the window (prevents whole page zoom/scroll) + e.preventDefault(); + e.stopPropagation(); + + // Manually apply zoom to the canvas so the user can still zoom the graph + if (app.canvas && app.canvas.ds) { + // Determine zoom direction and factor (standard LiteGraph logic) + const zoomFactor = e.deltaY > 0 ? 0.9 : 1.1; + app.canvas.ds.changeScale(app.canvas.ds.scale * zoomFactor, [e.clientX, e.clientY]); + app.canvas.setDirty(true, true); + } + }, { passive: false }); + + // We append to document.body to ensure getBoundingClientRect + scroll math works reliably + document.body.appendChild(node.hudElement); + + node.lastStackSignature = ""; + } + const dock = node.hudElement; + + // ... [Content Update Section remains exactly the same] ... + const currentSignature = stackList.map(n => n.id).join(","); + if (currentSignature !== node.lastStackSignature) { + dock.innerHTML = ""; + node.lastStackSignature = currentSignature; + + // Build Chips (Keep existing logic) + for (const item of stackList) { + const chip = document.createElement("div"); + chip.className = "hud-chip"; + + if (item.mode === 2) chip.style.borderColor = "#888888"; + else if (item.mode === 4) chip.style.borderColor = "#cc66ff"; + + const leftDots = []; + const rightDots = []; + + if (item.inputs) { + for (const input of item.inputs) { + if (input.link) { + const link = graph.links[input.link]; + if (link) leftDots.push(getLinkColor(link)); + } + } + } + if (item.outputs) { + for (const output of item.outputs) { + if (output.links && output.links.length > 0) { + const link = graph.links[output.links[0]]; + if (link) rightDots.push(getLinkColor(link)); + else if (LGraphCanvas.link_type_colors[output.type]) { + rightDots.push(LGraphCanvas.link_type_colors[output.type]); + } else { + if (output.type === "STRING") rightDots.push("#66ff66"); + else rightDots.push("#cccccc"); + } + } + } + } + if (leftDots.length === 0 && rightDots.length === 0) { + leftDots.push("#ff4444"); + } + + leftDots.forEach(c => { + const d = document.createElement("div"); + d.className = "hud-dot"; + d.style.backgroundColor = c; + chip.appendChild(d); + }); + + const span = document.createElement("span"); + let text = item.title || item.type; + if (text.length > 20) text = text.substring(0, 18) + ".."; + span.innerText = text; + chip.appendChild(span); + + rightDots.forEach(c => { + const d = document.createElement("div"); + d.className = "hud-dot"; + d.style.backgroundColor = c; + chip.appendChild(d); + }); + + dock.appendChild(chip); + } + } + + // 3. Position Update (Run every frame) + const canvas = app.canvas; + const scale = canvas.ds.scale; + const offset = canvas.ds.offset; + + // Get the DOM position of the canvas element itself (fixes window offset issues) + const canvasRect = canvas.canvas.getBoundingClientRect(); + const scrollX = window.scrollX || window.pageXOffset; + const scrollY = window.scrollY || window.pageYOffset; + + // Graph Coordinates + const graphX = node.pos[0]; + const graphY = node.pos[1] + node.size[1] + 2; + + // --- CORRECTION: MATCH LITEGRAPH TRANSFORM --- + const screenX = (graphX + offset[0]) * scale + canvasRect.left + scrollX; + const screenY = (graphY + offset[1]) * scale + canvasRect.top + scrollY; + + // GEOMETRY ONLY + dock.style.position = "absolute"; + dock.style.left = screenX + "px"; + dock.style.top = screenY + "px"; + dock.style.width = (node.size[0] * scale) + "px"; + dock.style.transform = "none"; + dock.style.margin = "0"; + + // --- UPDATES: Zoom Class & Z-Index --- + + // 1. Zoom Logic (Around 57% zoom, text usually disappears) + if (scale < 0.58) { + dock.classList.add("hud-dock-zoomed-out"); + } else { + dock.classList.remove("hud-dock-zoomed-out"); + } + + // 2. Z-Index Logic (Refined) + // We base the z-index on the node's position in the graph stack (0, 1, 2...). + // Standard UI widgets (like active text boxes) usually have high z-indices (1000+). + // By keeping this low, we allow other DOM elements to sit on top if needed. + let zIndex = graph._nodes.indexOf(node); + + // Safety check: Ensure we don't accidentally get -1 or weird values + if (zIndex < 0) zIndex = 0; + + if (node.zIndex !== undefined) { + zIndex = node.zIndex; + } else if (node.style && node.style.zIndex) { + zIndex = node.style.zIndex; + } + + // Important: We assign this dynamically every frame to ensure it updates if node order changes + dock.style.zIndex = zIndex; +} \ No newline at end of file