From 37dc35e7e47402cfe17f18229bb2c6b2209aad62 Mon Sep 17 00:00:00 2001 From: Raykosan <71210053+Raykosan@users.noreply.github.com> Date: Mon, 20 Apr 2026 20:58:21 +0300 Subject: [PATCH] Added the RS Color Picker node --- Rayko_Color_Picker.py | 20 +++++++++ Rayko_Outpaint.py | 4 +- __init__.py | 2 +- package.json | 3 +- pyproject.toml | 2 +- web/rs_colorpicker.js | 98 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 123 insertions(+), 6 deletions(-) create mode 100644 Rayko_Color_Picker.py create mode 100644 web/rs_colorpicker.js diff --git a/Rayko_Color_Picker.py b/Rayko_Color_Picker.py new file mode 100644 index 0000000..840f0b8 --- /dev/null +++ b/Rayko_Color_Picker.py @@ -0,0 +1,20 @@ +class RSColorPicker: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "color": ("STRING", {"default": "#ff0000", "multiline": False, "tooltip": "Hex color value", "hidden": True}), + }, + } + + RETURN_TYPES = () + FUNCTION = "noop" + OUTPUT_NODE = True + CATEGORY = "🦊 RaykoStudio" + DESCRIPTION = "Interactive color picker widget with pipette support" + + def noop(self, color): + return () + +NODE_CLASS_MAPPINGS = {"RSColorPicker": RSColorPicker} +NODE_DISPLAY_NAME_MAPPINGS = {"RSColorPicker": "🦊 RS Color Picker"} \ No newline at end of file diff --git a/Rayko_Outpaint.py b/Rayko_Outpaint.py index 50e1b26..3fb999a 100644 --- a/Rayko_Outpaint.py +++ b/Rayko_Outpaint.py @@ -264,6 +264,4 @@ async def rs_outpaint_heartbeat(request): return web.Response(status=200, text="OK") return web.Response(status=404, text="Not found") except Exception as e: - return web.Response(status=500, text=str(e)) - -print("🦊 [RS Outpaint] ✅ Routes registered") \ No newline at end of file + return web.Response(status=500, text=str(e)) \ No newline at end of file diff --git a/__init__.py b/__init__.py index f0d978a..84c469b 100644 --- a/__init__.py +++ b/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.15.10" +__version__ = "0.15.11" import importlib import logging from pathlib import Path diff --git a/package.json b/package.json index f117a54..74e370c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ComfyUI_RaykoStudio", - "version": "0.15.10", + "version": "0.15.11", "description": "Set of custom nodes for ComfyUI providing additional image processing capabilities", "author": "Raykosan", "license": "MIT", @@ -10,6 +10,7 @@ }, "comfyui": { "nodes": [ + "Rayko_Color_Picker.py" "Rayko_Crop_Image.py" "Rayko_Image_Selector.py" "Rayko_Image_Text.py" diff --git a/pyproject.toml b/pyproject.toml index 30925fe..31ab5f0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "ComfyUI_RaykoStudio" description = "Set of custom nodes for ComfyUI providing additional image processing capabilities" -version = "0.15.10" +version = "0.15.11" license = { file = "LICENSE" } dependencies = ["torch>=1.7.0", "numpy>=1.19.0", "Pillow>=8.0.0", "freetype-py>=2.5.1"] diff --git a/web/rs_colorpicker.js b/web/rs_colorpicker.js new file mode 100644 index 0000000..c2202af --- /dev/null +++ b/web/rs_colorpicker.js @@ -0,0 +1,98 @@ +console.log("[SPLINE 🦊] rs_colorpicker.js LOADED!"); +import { app } from "../../scripts/app.js"; + +const NODE_CLASS = "RSColorPicker"; +const WIDGET_W = 150; +const WIDGET_H = 32; +const NODE_CHROME_H = 32; // ComfyUI title bar + padding overhead + +function mkEl(tag, css, extra) { + const el = document.createElement(tag); + if (css) el.style.cssText = css; + if (extra) Object.assign(el, extra); + return el; +} + +function buildUI(defaultColor) { + const root = mkEl("div", "width:100%;height:100%;display:flex;align-items:center;gap:6px;padding:3px 6px;box-sizing:border-box;font-family:system-ui,sans-serif;"); + + const swatch = mkEl("div", `width:26px;height:26px;border-radius:4px;border:1.5px solid #444;cursor:pointer;background:${defaultColor};flex-shrink:0;transition:border-color 0.1s;`); + swatch.onmouseenter = () => { swatch.style.borderColor = "#6688aa"; }; + swatch.onmouseleave = () => { swatch.style.borderColor = "#444"; }; + + const colorPicker = mkEl("input", "width:0;height:0;opacity:0;position:absolute;pointer-events:none;", { type: "color", value: defaultColor }); + + const hexInput = mkEl("input", "width:74px;padding:4px 5px;font-size:12px;font-family:monospace;background:#1e1e1e;color:#ccc;border:1px solid #444;border-radius:4px;text-align:center;outline:none;", { value: defaultColor.toUpperCase() }); + hexInput.addEventListener("focus", () => { hexInput.style.borderColor = "#5090cc"; }); + hexInput.addEventListener("blur", () => { hexInput.style.borderColor = "#444"; }); + + swatch.onclick = () => colorPicker.click(); + + root.append(swatch, colorPicker, hexInput); + return { root, swatch, colorPicker, hexInput }; +} + +app.registerExtension({ + name: "RSColorPicker", + async beforeRegisterNodeDef(nodeType, nodeData) { + if (nodeData.name !== NODE_CLASS) return; + + const origOnNodeCreated = nodeType.prototype.onNodeCreated; + nodeType.prototype.onNodeCreated = function () { + const result = origOnNodeCreated?.apply(this, arguments); + const node = this; + const colorWidget = node.widgets?.find(w => w.name === "color"); + + if (colorWidget) { + colorWidget.hidden = true; + colorWidget.computeSize = () => [0, -4]; + } + + const initialColor = colorWidget?.value || "#ff0000"; + const dom = buildUI(initialColor); + + const syncToWidget = (color) => { + if (colorWidget) colorWidget.value = color; + if (node.graph) node.graph.setDirtyCanvas(true, true); + }; + + dom.colorPicker.addEventListener("input", (e) => { + const color = e.target.value; + dom.swatch.style.background = color; + dom.hexInput.value = color.toUpperCase(); + syncToWidget(color); + }); + + dom.hexInput.addEventListener("change", (e) => { + let val = e.target.value.trim(); + if (!val.startsWith("#")) val = "#" + val; + if (/^#[0-9A-Fa-f]{6}$/.test(val)) { + dom.swatch.style.background = val; + dom.colorPicker.value = val; + dom.hexInput.value = val.toUpperCase(); + syncToWidget(val); + } else { + dom.hexInput.value = dom.colorPicker.value.toUpperCase(); + } + }); + + const domWidget = node.addDOMWidget("rs_colorpicker", "custom", dom.root, { + serialize: false, + hideOnZoom: false, + }); + + domWidget.computeSize = () => [WIDGET_W, WIDGET_H]; + node.setSize([WIDGET_W + 20, WIDGET_H + NODE_CHROME_H]); + + if (node.widgets && colorWidget) { + const idx = node.widgets.indexOf(colorWidget); + if (idx >= 0) { + node.widgets.splice(idx, 1); + node.widgets.push(colorWidget); + } + } + + return result; + }; + }, +}); \ No newline at end of file