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.
63 lines
1.8 KiB
JavaScript
63 lines
1.8 KiB
JavaScript
// Synthwave: 80s retro-futurism. Neon pink-to-purple header gradient,
|
||
// cyan accent. Distinguished from Cyberpunk by hot horizontal gradients
|
||
// rather than corner clips + glow.
|
||
const HEADER_STOPS = ["#ff007f", "#7900ff"];
|
||
|
||
export default {
|
||
id: "synthwave",
|
||
displayName: "Synthwave",
|
||
motion: "low",
|
||
staticConfig: {
|
||
bgcolor: "#120424",
|
||
color: "#1a0533",
|
||
titlecolor: "#1a0533",
|
||
titletext: "#ffffff",
|
||
boxcolor: "#00f0ff",
|
||
},
|
||
wirePalette: {
|
||
default: "#ff007f",
|
||
IMAGE: "#ff61dc",
|
||
LATENT: "#bf5af2",
|
||
MASK: "#00f0ff",
|
||
CONDITIONING: "#ffea00",
|
||
MODEL: "#ff5e5e",
|
||
},
|
||
categoryAccents: {
|
||
image: "#ff61dc",
|
||
latent: "#bf5af2",
|
||
conditioning: "#ffea00",
|
||
sampling: "#00f0ff",
|
||
default: "#ff007f",
|
||
},
|
||
groupColors: { color: "#ff007f", bgcolor: "#1a0533" },
|
||
bgGridColor: "#08011a",
|
||
drawForeground(ctx, node, themeApi) {
|
||
const w = node.size?.[0] ?? 0;
|
||
const h = node.size?.[1] ?? 0;
|
||
if (w <= 0 || h <= 0) return;
|
||
ctx.save();
|
||
// Cache the pink→purple gradient per (node, width). Reallocating a
|
||
// CanvasGradient every frame was measurable churn at 200Hz × many nodes.
|
||
let cached = node._mp_synthwave_grad;
|
||
if (!cached || cached.w !== w) {
|
||
const grad = ctx.createLinearGradient(0, 0, w, 0);
|
||
grad.addColorStop(0, HEADER_STOPS[0]);
|
||
grad.addColorStop(1, HEADER_STOPS[1]);
|
||
cached = node._mp_synthwave_grad = { w, grad };
|
||
}
|
||
ctx.fillStyle = cached.grad;
|
||
ctx.fillRect(0, -3, w, 3);
|
||
if (themeApi && themeApi.motionAllowed("low")) {
|
||
ctx.strokeStyle = "#00f0ff";
|
||
ctx.globalAlpha = 0.25;
|
||
ctx.lineWidth = 3;
|
||
ctx.strokeRect(0.5, 0.5, w - 1, h - 1);
|
||
ctx.globalAlpha = 0.8;
|
||
ctx.lineWidth = 1;
|
||
ctx.strokeRect(0.5, 0.5, w - 1, h - 1);
|
||
}
|
||
ctx.restore();
|
||
},
|
||
themeApiVersion: 2,
|
||
};
|