diff --git a/Nodes/Rasterix.py b/Nodes/Rasterix.py index 9a2b897..380247f 100644 --- a/Nodes/Rasterix.py +++ b/Nodes/Rasterix.py @@ -53,27 +53,36 @@ class PrimereRasterix: LUT_DIR = os.path.join(PRIMERE_ROOT, 'components', 'images', 'luts') SECTION_TITLES = [ - {"before": "concepts", "title": "๐Ÿงญ Main Settings", "color": "#3A3F52", "text_color": "#F2F5FF"}, - {"after": "precision", "title": "๐ŸŽš Auto levels", "color": "#364556", "text_color": "#EAF1F8"}, - {"after": "gamma_target", "title": "๐Ÿ”ฆ White balance", "color": "#364556", "text_color": "#EAF1F8"}, - {"after": "wb_tint", "title": "๐Ÿ’ก Smart lightning", "color": "#364556", "text_color": "#EAF1F8"}, - {"after": "smart_lighting", "title": "๐ŸŒซ Dehaze", "color": "#364556", "text_color": "#EAF1F8"}, - {"after": "dehaze_contrast", "title": "๐Ÿช„ Depth blur", "color": "#364556", "text_color": "#EAF1F8"}, - {"after": "depth_gamma", "title": "๐Ÿซ— Blur", "color": "#364556", "text_color": "#EAF1F8"}, - {"after": "edge_threshold", "title": "๐ŸงŠ Primary Image Ops", "color": "#355443", "text_color": "#E8F6EF"}, - {"after": "use_legacy", "title": "๐Ÿช’ Portrait retouching", "color": "#355443", "text_color": "#E8F6EF"}, - {"after": "blend_mode", "title": "โš” Edge-Aware pyramid", "color": "#355443", "text_color": "#E8F6EF"}, - {"after": "levels", "title": "๐ŸŽž Analog film and CCD rendering", "color": "#355443", "text_color": "#E8F6EF"}, - {"after": "photo_paper", "title": "๐Ÿ“ท LUT .cube file reader", "color": "#355443", "text_color": "#E8F6EF"}, - {"after": "color_space", "title": "๐ŸŽฅ Filmic camera", "color": "#355443", "text_color": "#E8F6EF"}, - {"after": "pivot", "title": "๐ŸŽ› Selective tone", "color": "#355443", "text_color": "#E8F6EF"}, - {"after": "selective_tone_strength", "title": "โš– Color balance", "color": "#355443", "text_color": "#E8F6EF"}, - {"after": "color_balance_separation", "title": "๐Ÿช Hue Saturation Lightness", "color": "#355443", "text_color": "#E8F6EF"}, - {"after": "hsl_skin_protection", "title": "๐Ÿ’Ž Microcontrast - Shade detailer", "color": "#355443", "text_color": "#E8F6EF"}, - {"after": "shade_strength", "title": "๐Ÿงน Midtone claity", "color": "#355443", "text_color": "#E8F6EF"}, - {"after": "edge_preservation", "title": "๐Ÿ”› Black and light endpoints", "color": "#355443", "text_color": "#E8F6EF"}, - {"after": "skip_if_no_clip", "title": "๐Ÿ› Dithering / diffusion", "color": "#355443", "text_color": "#E8F6EF"}, - {"after": "error_diffusion", "title": "๐Ÿ“Š Histogram", "color": "#2F4F61", "text_color": "#E6F5FF"}, + {"before": "concepts", "name": "rasterix_main", "title": "๐Ÿงญ Main Settings", "color": "#A13C2F", "text_color": "#EAF1F8", "label": "Choose model concept or model to save and load settings, set precision for all features at once."}, + + {"after": "precision", "title": "๐ŸŽš Auto levels", "color": "#4A351B", "text_color": "#EAF1F8"}, + {"after": "gamma_target", "title": "๐Ÿ”ฆ White balance", "color": "#4A351B", "text_color": "#EAF1F8"}, + {"after": "wb_tint", "title": "๐Ÿ’ก Smart lightning", "color": "#4A351B", "text_color": "#EAF1F8"}, + + {"after": "smart_lighting", "title": "๐ŸŒซ Dehaze", "color": "#314A2D", "text_color": "#EAF1F8"}, + {"after": "dehaze_contrast", "title": "๐Ÿช„ Depth blur", "color": "#314A2D", "text_color": "#EAF1F8"}, + {"after": "depth_gamma", "title": "๐Ÿซ— Blur", "color": "#314A2D", "text_color": "#EAF1F8"}, + + {"after": "edge_threshold", "title": "๐ŸงŠ Brightness-contrast", "color": "#404A94", "text_color": "#EAF1F8"}, + + {"after": "use_legacy", "title": "๐Ÿช’ Portrait retouching", "color": "#404A94", "text_color": "#EAF1F8"}, + {"after": "blend_mode", "title": "โš” Edge-Aware pyramid", "color": "#404A94", "text_color": "#EAF1F8"}, + + {"after": "levels", "title": "๐ŸŽž Analog film and CCD rendering", "color": "#305157", "text_color": "#EAF1F8"}, + {"after": "photo_paper", "title": "๐Ÿ“ท LUT .cube file reader", "color": "#305157", "text_color": "#EAF1F8"}, + {"after": "color_space", "title": "๐ŸŽฅ Filmic camera", "color": "#305157", "text_color": "#EAF1F8"}, + + {"after": "pivot", "title": "๐ŸŽ› Selective tone", "color": "#52472C", "text_color": "#EAF1F8"}, + {"after": "selective_tone_strength", "title": "โš– Color balance", "color": "#52472C", "text_color": "#EAF1F8"}, + {"after": "color_balance_separation", "title": "๐Ÿช Hue Saturation Lightness", "color": "#52472C", "text_color": "#EAF1F8"}, + + {"after": "hsl_skin_protection", "title": "๐Ÿ’Ž Microcontrast - Shade detailer", "color": "#4A2D3A", "text_color": "#EAF1F8"}, + {"after": "shade_strength", "title": "๐Ÿงน Midtone claity", "color": "#355443", "text_color": "#EAF1F8"}, + + {"after": "edge_preservation", "title": "๐Ÿ”› Black and light endpoints", "color": "#454A2D", "text_color": "#EAF1F8"}, + {"after": "skip_if_no_clip", "title": "๐Ÿ› Dithering / diffusion", "color": "#454A2D", "text_color": "#EAF1F8"}, + + {"after": "error_diffusion", "title": "๐Ÿ“Š Histogram", "color": "#2F4F61", "text_color": "#EAF1F8"}, ] @classmethod diff --git a/front_end/title_manager.js b/front_end/title_manager.js index 98fa8e6..722a9c3 100644 --- a/front_end/title_manager.js +++ b/front_end/title_manager.js @@ -7,6 +7,7 @@ const TITLE_MANAGED_NODES = [ const titleConfigCache = {}; const titleConfigPromise = {}; +const TITLE_WIDGET_HEIGHT = 30; function hexToRgb(hex) { const clean = String(hex || "").trim().replace("#", ""); @@ -50,9 +51,11 @@ function makeTitleWidget(node, section) { const bgColor = section?.color ? String(section.color) : null; const textColor = section?.text_color ? String(section.text_color) : bestTextColor(bgColor); + const label = section?.label ? String(section.label) : ""; + const sectionName = section?.name ? String(section.name) : ""; if (bgColor || section?.text_color) { - const label = widget.name; + const titleText = widget.name; widget.draw = function (ctx, nodeObj, widgetWidth, y, h) { const x = 15; const w = widgetWidth - 30; @@ -79,10 +82,15 @@ function makeTitleWidget(node, section) { ctx.font = "12px Arial"; ctx.textAlign = "center"; ctx.textBaseline = "middle"; - ctx.fillText(label, widgetWidth / 2, yy + hh / 2 + 0.5); + ctx.fillText(titleText, widgetWidth / 2, yy + hh / 2 + 0.5); ctx.restore(); }; } + + widget.__primereTitleMeta = { + label, + name: sectionName, + }; return widget; } @@ -125,6 +133,92 @@ function insertTitleWidgets(node, sections) { node.setDirtyCanvas?.(true, true); } +function ensureTitleTooltip() { + let box = document.querySelector("div#primere_title_hover"); + if (box) return box; + box = document.createElement("div"); + box.id = "primere_title_hover"; + box.style.cssText = [ + "display:none", + "position:fixed", + "z-index:99999", + "max-width:360px", + "padding:10px 12px", + "border-radius:8px", + "background:rgba(22,24,28,0.96)", + "border:1px solid rgba(255,255,255,0.12)", + "box-shadow:0 8px 24px rgba(0,0,0,0.35)", + "color:#EDEFF4", + "font:12px/1.4 Arial, sans-serif", + "pointer-events:none", + "white-space:normal", + ].join(";"); + document.body.appendChild(box); + return box; +} + +function hideTitleTooltip() { + const box = ensureTitleTooltip(); + box.style.display = "none"; +} + +function showTitleTooltip(text, x, y) { + const box = ensureTitleTooltip(); + box.textContent = text; + box.style.left = `${x + 12}px`; + box.style.top = `${y + 12}px`; + box.style.display = "block"; +} + +function handleTitleHover(node, event, pos) { + if (!event || event.type !== "pointermove" || !Array.isArray(node?.widgets)) { + hideTitleTooltip(); + return; + } + + const nodeWidth = Number(node?.size?.[0] || 300); + const xMin = 15; + const xMax = Math.max(xMin + 20, nodeWidth - 15); + const rightHalfStart = xMin + (xMax - xMin) / 2; + + for (const w of node.widgets) { + const meta = w?.__primereTitleMeta; + if (!meta?.label) continue; + const y0 = Number(w.last_y || 0); + const y1 = y0 + TITLE_WIDGET_HEIGHT; + const insideY = pos[1] >= y0 && pos[1] <= y1; + const insideRightHalf = pos[0] >= rightHalfStart && pos[0] <= xMax; + if (insideY && insideRightHalf) { + showTitleTooltip(meta.label, event.clientX, event.clientY); + return; + } + } + + hideTitleTooltip(); +} + +function attachTitleHoverHandlers(node) { + if (!node || node.__primereTitleHoverBound) return; + const prevMove = node.onMouseMove; + const prevLeave = node.onMouseLeave; + + node.onMouseMove = function (event, pos, graphcanvas) { + if (typeof prevMove === "function") { + prevMove.call(this, event, pos, graphcanvas); + } + handleTitleHover(this, event, pos); + }; + + node.onMouseLeave = function (event, pos, graphcanvas) { + if (typeof prevLeave === "function") { + prevLeave.call(this, event, pos, graphcanvas); + } + hideTitleTooltip(); + }; + + node.__primereTitleHoverBound = true; +} + app.registerExtension({ name: "Primere.TitleManager", @@ -137,6 +231,7 @@ app.registerExtension({ const node = this; getNodeTitleConfig(nodeData.name).then((sections) => { insertTitleWidgets(node, sections); + attachTitleHoverHandlers(node); }); }; },