commit 84149c7886a7cd944d0fa804bdfcb3148f880633 Author: loficoding Date: Wed Jan 21 16:34:39 2026 -0700 Initial release of Node HUD diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..63b4b68 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) [year] [fullname] + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.html b/README.html new file mode 100644 index 0000000..6239e13 --- /dev/null +++ b/README.html @@ -0,0 +1,249 @@ + + + + + ComfyUI Node HUD 🛠️ + + + + + + + + + + + + +

ComfyUI Node HUD 🛠️

+

Stop the spaghetti. Keep the data.

+

A lightweight UI extension for ComfyUI that adds a smart "Heads-Up Display" (HUD) to your nodes. It allows you to stack nodes behind one another to clean up your workspace, while still seeing exactly what is happening inside the stack via a sleek, tinted-glass dock.

+

Main Screenshot

+

✨ Features

+

1. Hiding Nodes

+

Hide utility nodes (like String Primitives, Notes, or multiple LoRAs) behind a single "Parent" node. Clean Layout Dock detects these hidden nodes and generates a live status bar attached to the bottom of the parent.

+

2. Smart I/O Indicators 🚦

+

No more guessing if a hidden node is connected. The dock displays Smart Dots that visualize the data flow:

+ +

3. Live Status Feedback

+

The dock updates in real-time to reflect the state of your nodes:

+ +

4. "Tinted Glass" UI

+

The dock uses a semi-transparent dark background (85% opacity) to ensure readability without feeling "heavy" or blocking the grid entirely.

+
+

📥 Installation

+

Method 1: Manager (Coming Soon)

+

Once indexed, you can install this via the ComfyUI Manager.

+

Method 2: Manual Install

+
    +
  1. Navigate to your ComfyUI custom_nodes directory:
    cd ComfyUI/custom_nodes/
    +
    +
  2. +
  3. Clone this repository:
    git clone [https://github.com/YOUR_USERNAME/ComfyUI-Clean-Dock.git](https://github.com/YOUR_USERNAME/ComfyUI-Clean-Dock.git)
    +
    +
  4. +
  5. Restart ComfyUI.
  6. +
+
+

🖱️ Usage

+
    +
  1. Stack Your Nodes: Drag utility nodes (like Primitive or Note) physically behind a larger "Parent" node (like Prompt Manager or KSampler).
  2. +
  3. Activate the Dock: +
      +
    • Right-click the Parent Node.
    • +
    • Select "Toggle Stack Info" from the menu.
    • +
    +
  4. +
  5. That's it! The dock will appear. If you move the nodes out from behind the parent, the dock updates instantly.
  6. +
+
+

🎨 Color Legend

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ColorMeaning
🟣Model (Checkpoint / LoRA)
🟡CLIP (Conditioning)
🔴VAE
🌸Latent
🔵Image
🟢String / Text
🔴 (Solid)Error / Disconnected
+
+

🤝 Contributing

+

This extension is pure JavaScript. Feel free to submit PRs for new features or styling tweaks!

+

📄 License

+

MIT License.

+ + + + + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..d35228f --- /dev/null +++ b/README.md @@ -0,0 +1,86 @@ +# ComfyUI Node HUD 🛠️ + +**Stop the spaghetti. Keep the data.** + +A lightweight UI extension for [ComfyUI](https://github.com/comfyanonymous/ComfyUI) that adds a smart "Heads-Up Display" (HUD) to your nodes. It allows you to stack nodes behind one another to clean up your workspace, while still seeing exactly what is happening inside the stack via a sleek, tinted-glass dock. + +![Main Screenshot](placeholder_for_your_screenshot.png) + +## ✨ Features + +### 1. Hiding Nodes +Hide utility nodes (like String Primitives, Notes, or multiple LoRAs) behind a single "Parent" node. Clean Layout Dock detects these hidden nodes and generates a live status bar attached to the bottom of the parent. + +### 2. Smart I/O Indicators 🚦 +No more guessing if a hidden node is connected. The dock displays **Smart Dots** that visualize the data flow: +* **Left Dots (Inputs):** Shows active incoming connections. +* **Right Dots (Outputs):** Shows active outgoing data. +* **Color Coded:** The dots match ComfyUI's standard wire colors: + * 🟣 **Purple:** Model + * 🟡 **Yellow:** CLIP + * 🔴 **Red:** VAE + * 🌸 **Pink:** Latent + * 🔵 **Blue:** Image + * 🟢 **Green:** String / Text + +### 3. Live Status Feedback +The dock updates in real-time to reflect the state of your nodes: +* **Active:** Green text / Standard border. +* **Muted:** Gray border & dimmed text. +* **Bypassed:** Purple border. +* **Disconnected:** If a node has no active inputs or outputs, a red error dot 🔴 appears. + +### 4. "Tinted Glass" UI +The dock uses a semi-transparent dark background (85% opacity) to ensure readability without feeling "heavy" or blocking the grid entirely. + +--- + +## 📥 Installation + +### Method 1: Manager (Coming Soon) +*Once indexed, you can install this via the ComfyUI Manager.* + +### Method 2: Manual Install +1. Navigate to your ComfyUI `custom_nodes` directory: + ```bash + cd ComfyUI/custom_nodes/ + ``` +2. Clone this repository: + ```bash + git clone [https://github.com/YOUR_USERNAME/ComfyUI-Clean-Dock.git](https://github.com/YOUR_USERNAME/ComfyUI-Clean-Dock.git) + ``` +3. **Restart ComfyUI**. + +--- + +## 🖱️ Usage + +1. **Stack Your Nodes:** Drag utility nodes (like *Primitive* or *Note*) physically behind a larger "Parent" node (like *Prompt Manager* or *KSampler*). +2. **Activate the Dock:** + * Right-click the Parent Node. + * Select **"Toggle Stack Info"** from the menu. +3. **That's it!** The dock will appear. If you move the nodes out from behind the parent, the dock updates instantly. + +--- + +## 🎨 Color Legend + +| Color | Meaning | +| :--- | :--- | +| 🟣 | **Model** (Checkpoint / LoRA) | +| 🟡 | **CLIP** (Conditioning) | +| 🔴 | **VAE** | +| 🌸 | **Latent** | +| 🔵 | **Image** | +| 🟢 | **String** / Text | +| 🔴 (Solid) | **Error** / Disconnected | + +--- + +## 🤝 Contributing + +This extension is pure JavaScript. Feel free to submit PRs for new features or styling tweaks! + +## 📄 License + +MIT License. \ No newline at end of file diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..49fe65b --- /dev/null +++ b/__init__.py @@ -0,0 +1,2 @@ +WEB_DIRECTORY = "./web/js" +__all__ = ["WEB_DIRECTORY"] \ No newline at end of file diff --git a/mainuse.gif b/mainuse.gif new file mode 100644 index 0000000..4167dd6 Binary files /dev/null and b/mainuse.gif differ diff --git a/web/js/node_hud.js b/web/js/node_hud.js new file mode 100644 index 0000000..9077554 --- /dev/null +++ b/web/js/node_hud.js @@ -0,0 +1,211 @@ +import { app } from "../../scripts/app.js"; + +app.registerExtension({ + name: "Comfy.CleanLayoutDock", + + // 1. Toggle Menu + async beforeRegisterNodeDef(nodeType, nodeData, app) { + 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; + app.graph.setDirtyCanvas(true, true); + } + }); + }; + + // 2. Draw Foreground + const onDrawForeground = nodeType.prototype.onDrawForeground; + nodeType.prototype.onDrawForeground = function(ctx) { + if (onDrawForeground) onDrawForeground.apply(this, arguments); + + if (!this.show_stack_info) return; + + const stackList = getHiddenNodes(this, app.graph); + if (stackList.length === 0) return; + + ctx.save(); + drawDock(this, ctx, stackList, app.graph); + ctx.restore(); + }; + } +}); + +// --- HELPER: Find Hidden Nodes --- +function getHiddenNodes(parent, graph) { + const stackList = []; + if (!graph || !graph._nodes) return stackList; + + 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 (const node of graph._nodes) { + if (node.id === parent.id) continue; + const nx = node.pos[0]; + const ny = node.pos[1]; + if (nx >= pLeft && nx < pRight && ny >= pTop && ny < pBottom) { + stackList.push(node); + } + } + return stackList; +} + +// --- HELPER: Get Link Color --- +function getLinkColor(link) { + const type = link.type; + const colorMap = LGraphCanvas.link_type_colors; + + // 1. Check Comfy Registry first + if (colorMap[type]) return colorMap[type]; + + // 2. Common Fallbacks + if (type === "STRING") return "#66ff66"; // <--- FIXED: Standard Green + if (type === "INT") return "#22cc22"; // Integer Green + if (type === "FLOAT") return "#22cc22"; // Float Green + 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 "Lint Blue" Default +} + +// --- DRAWER --- +function drawDock(node, ctx, stackList, graph) { + if (!node.size) return; + + const dockHeight = 26; + const chipHeight = 16; + const padding = 6; + const dotRadius = 3; + const dotSpacing = 8; + + const x = 0; + const y = node.size[1]; + + /* + // Background + ctx.fillStyle = "#000"; + ctx.beginPath(); + if (ctx.roundRect) ctx.roundRect(x, y, node.size[0], dockHeight, [0, 0, 6, 6]); + else ctx.rect(x, y, node.size[0], dockHeight); + ctx.fill(); + */ + + // Background (Tinted Glass Style) + ctx.fillStyle = "rgba(20, 20, 20, 0.85)"; // 85% Opaque Dark Grey + ctx.beginPath(); + if (ctx.roundRect) ctx.roundRect(x, y, node.size[0], dockHeight, [0, 0, 6, 6]); + else ctx.rect(x, y, node.size[0], dockHeight); + ctx.fill(); + + // Optional: Add a subtle glass border for definition + ctx.strokeStyle = "rgba(255, 255, 255, 0.1)"; + ctx.lineWidth = 1; + ctx.stroke(); + + let currentX = x + padding; + ctx.font = "10px Arial"; + ctx.textBaseline = "middle"; + + for (const item of stackList) { + // 1. Gather Dots + const leftDots = []; + const rightDots = []; + + // Check Inputs + if (item.inputs) { + for (const input of item.inputs) { + if (input.link) { + const link = graph.links[input.link]; + if (link) leftDots.push(getLinkColor(link)); + } + } + } + + // Check Outputs + if (item.outputs) { + for (const output of item.outputs) { + if (output.links && output.links.length > 0) { + // Use color of first link + 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 { + // Manual check for disconnected outputs that usually have colors + if (output.type === "STRING") rightDots.push("#66ff66"); + else rightDots.push("#cccccc"); + } + } + } + } + + // Fallback: Disconnected + if (leftDots.length === 0 && rightDots.length === 0) { + leftDots.push("#ff4444"); // Red error dot + } + + // 2. Calculate Size + let text = item.title || item.type; + if (text.length > 20) text = text.substring(0, 18) + ".."; + const textWidth = ctx.measureText(text).width; + + const leftSpace = 6 + (leftDots.length * dotSpacing); + const rightSpace = 6 + (rightDots.length * dotSpacing); + const chipWidth = leftSpace + textWidth + rightSpace; + + if (currentX + chipWidth > node.size[0]) break; + + // 3. Determine Border Color + let borderColor = "#444"; + if (item.mode === 2) borderColor = "#888888"; // Mute + else if (item.mode === 4) borderColor = "#cc66ff"; // Bypass + + const centerY = y + (dockHeight/2); + + // 4. Draw Chip + ctx.fillStyle = "#222"; + ctx.strokeStyle = borderColor; + ctx.lineWidth = 1; + + ctx.beginPath(); + if (ctx.roundRect) ctx.roundRect(currentX, y + (dockHeight - chipHeight)/2, chipWidth, chipHeight, 8); + else ctx.rect(currentX, y + (dockHeight - chipHeight)/2, chipWidth, chipHeight); + ctx.fill(); + ctx.stroke(); + + // 5. Draw Left Dots + let dotX = currentX + 6; + for (const color of leftDots) { + ctx.fillStyle = color; + ctx.beginPath(); + ctx.arc(dotX + dotRadius, centerY, dotRadius, 0, Math.PI * 2); + ctx.fill(); + dotX += dotSpacing; + } + + // 6. Draw Text + ctx.fillStyle = "#ccc"; + ctx.fillText(text, currentX + leftSpace, centerY); + + // 7. Draw Right Dots + dotX = currentX + leftSpace + textWidth + 6; + for (const color of rightDots) { + ctx.fillStyle = color; + ctx.beginPath(); + ctx.arc(dotX + dotRadius, centerY, dotRadius, 0, Math.PI * 2); + ctx.fill(); + dotX += dotSpacing; + } + + currentX += chipWidth + padding; + } +} \ No newline at end of file