Files
IxMxAMAR-ComfyUI-Utility-Me…/web/wire-engine.js
T
IxMxAMAR 3f9e39bb44 v0.3.0: security hardening, audit fixes, 10 new ops, theme UX (matchMedia perf fix + sandbox + leaks + features)
Critical security fixes
-----------------------
- Sandbox Jinja2 in template_render + prompt_template_render (was RCE via shared workflows)
- Drop user home dir from filesystem allow-list (was arbitrary delete/overwrite)
- Confine save_image_with_manifest + sanitize filename basename
- SSRF block on http_get/http_post/notify_webhook (catches 169.254.169.254 metadata)
- Case-insensitive path confinement on Windows

Critical correctness/perf
-------------------------
- halftone_dots: O(N^2) -> O(cells*36); was freezing worker on 1024+ images
- Preserve latent dict keys (noise_mask, batch_index) in latent_math/noise/upscale
- Preserve alpha channel in invert + threshold_binary on RGBA inputs
- Force odd kernel in mask_erode/mask_dilate to prevent H+1 shape mismatch
- Snap aspect_ratio_pick output to multiples of 8 (was crashing SD UNet)

Theme engine (JS)
-----------------
- Hoist window.matchMedia() out of per-frame hot path -> recovers 200 FPS from 25
- Wrap-once draw method to stop stomping other extensions wrappers
- nodeRemoved hook + WeakMap groups + in-place canvas reuse -> three memory leaks closed
- Wire thickness now uses connections_width (the wrap was a no-op vs LiteGraph)
- onGroupAdded auto-themes new groups; budget change triggers redraw
- Gradient caching in synthwave/glassmorphic

New ops (10)
------------
- json_path (programming): dotted/bracketed path extract
- latent_nan_guard, latent_pad_crop (mask_latent)
- mask_from_depth, mask_invert (mask_latent)
- color_match_histogram, image_composite_over (image_pro/value)
- frequency_separation (image_pro/style)
- pad_to_multiple (image_pro/spatial)
- save_images_zip (io_workflow)
- llm_enhance_prompt via Ollama / LM Studio / llama.cpp (prompt)

Theme UX
--------
- Right-click "Theme..." submenu on every node (including vanilla ComfyUI nodes)
- Auto-contrast title text (WCAG AA-based flip)

Tests: 373 Python + 33 JS = 406 passing (added 33 new tests for security + features).
Full review notes in CHANGELOG.md.
2026-05-17 15:41:19 +05:30

108 lines
3.6 KiB
JavaScript

/**
* Wire (link/connection) theming.
*
* 1. Per-data-type color palette: mutate `app.canvas.default_connection_color_byType`
* and store originals so we can restore on theme removal.
* 2. Render mode (bezier/straight/linear/manhattan): set `app.canvas.links_render_mode`.
* 3. Thickness: scale LiteGraph's native `connections_width` property. (v0.2.x
* tried to mutate `ctx.lineWidth` inside a renderLink wrap, but LiteGraph
* overwrites lineWidth from connections_width inside the original
* renderLink anyway, so the wrap was a no-op.)
* 4. Opacity: wrap renderLink to scale globalAlpha. There is no native
* LiteGraph property for link opacity, so a wrap is necessary here.
*
* Verified against existing packs:
* - ComfyUI-Custom-Scripts mutates `links_render_mode`.
* - ComfyUI-KJNodes mutates `default_connection_color_byType` and wraps `renderLink`.
*/
let _originalColorByType = null;
let _originalConnectionsWidth = null;
let _renderLinkPatched = false;
let _wireState = {
thickness: 1.0, // multiplier on connections_width
opacity: 1.0, // ctx.globalAlpha multiplier (applied in wrap)
};
export function applyWirePalette(app, palette) {
if (!app?.canvas) return;
if (!_originalColorByType) {
_originalColorByType = { ...(app.canvas.default_connection_color_byType || {}) };
}
if (!palette) {
app.canvas.default_connection_color_byType = { ..._originalColorByType };
return;
}
// Apply theme palette merged over originals — unknown link types keep
// their original colors.
app.canvas.default_connection_color_byType = {
..._originalColorByType,
...palette,
};
}
/**
* Set the link render mode globally.
* 1 = Straight, 2 = Linear, 3 = Spline/Bezier, 4 = L-Shape (ComfyUI Manhattan).
* null = leave ComfyUI's user setting alone.
*/
export function applyWireRenderMode(app, mode) {
if (!app?.canvas) return;
if (mode !== null && mode !== undefined) {
app.canvas.links_render_mode = Number(mode);
app.canvas.setDirty?.(true, true);
}
}
/** Scale LiteGraph's native connections_width by `multiplier`. */
export function applyWireThickness(app, multiplier) {
_wireState.thickness = Number(multiplier) || 1.0;
if (!app?.canvas) return;
if (_originalConnectionsWidth === null) {
// LiteGraph defaults to 3 in modern builds; snapshot whatever is live.
_originalConnectionsWidth = app.canvas.connections_width ?? 3;
}
app.canvas.connections_width = _originalConnectionsWidth * _wireState.thickness;
app.canvas.setDirty?.(true, false);
}
export function setWireOpacity(alpha) {
_wireState.opacity = Math.max(0, Math.min(1, Number(alpha) || 1.0));
}
/**
* Install a one-time wrapper around LGraphCanvas.prototype.renderLink that
* applies the live opacity setting. Thickness is handled by mutating
* `connections_width` (see applyWireThickness) and does NOT require a wrap.
*/
export function ensureRenderLinkWrapped(LGraphCanvas) {
if (_renderLinkPatched || !LGraphCanvas?.prototype?.renderLink) return;
const original = LGraphCanvas.prototype.renderLink;
LGraphCanvas.prototype.renderLink = function (...args) {
if (_wireState.opacity === 1.0) {
return original.apply(this, args);
}
const ctx = args[0];
const prev = ctx.globalAlpha ?? 1;
ctx.globalAlpha = prev * _wireState.opacity;
const result = original.apply(this, args);
ctx.globalAlpha = prev;
return result;
};
_renderLinkPatched = true;
}
/** For tests only. */
export function _resetForTests() {
_originalColorByType = null;
_originalConnectionsWidth = null;
_renderLinkPatched = false;
_wireState = { thickness: 1.0, opacity: 1.0 };
}