Add frontend preset sync for LCSToneAdjust sliders
Selecting a preset now updates slider values in real-time via a frontend JS extension, and users can tweak values after selecting. Sliders are always visible (removed optional=True). Preset values live only in JS (single source of truth); Python keeps just the name list for the combo widget. Sync only fires on explicit user dropdown interaction, preserving saved slider tweaks on workflow load.
This commit is contained in:
@@ -34,9 +34,12 @@ async def comfy_entrypoint() -> LCSExtension:
|
||||
# V2 backward compatibility
|
||||
from .nodes import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
|
||||
|
||||
WEB_DIRECTORY = "./web"
|
||||
|
||||
__all__ = [
|
||||
"NODE_CLASS_MAPPINGS",
|
||||
"NODE_DISPLAY_NAME_MAPPINGS",
|
||||
"WEB_DIRECTORY",
|
||||
"LCSExtension",
|
||||
"comfy_entrypoint",
|
||||
]
|
||||
|
||||
+21
-36
@@ -440,18 +440,12 @@ def _build_tone_fn(lcs_data, contrast, brightness, saturation, color_temperature
|
||||
return post_cfg_fn
|
||||
|
||||
|
||||
TONE_PRESETS = {
|
||||
"Cinematic": {"contrast": 1.20, "brightness": -0.05, "saturation": 0.90, "color_temperature": 0.15},
|
||||
"HDR": {"contrast": 1.40, "brightness": 0.0, "saturation": 1.20, "color_temperature": 0.0},
|
||||
"Vivid": {"contrast": 1.10, "brightness": 0.0, "saturation": 1.50, "color_temperature": 0.0},
|
||||
"Dramatic": {"contrast": 1.50, "brightness": -0.10, "saturation": 0.85, "color_temperature": 0.0},
|
||||
"Low Key": {"contrast": 1.30, "brightness": -0.20, "saturation": 0.80, "color_temperature": 0.0},
|
||||
"High Key": {"contrast": 0.80, "brightness": 0.20, "saturation": 0.90, "color_temperature": 0.0},
|
||||
"Warm": {"contrast": 1.0, "brightness": 0.0, "saturation": 1.0, "color_temperature": 0.50},
|
||||
"Cool": {"contrast": 1.0, "brightness": 0.0, "saturation": 1.0, "color_temperature": -0.50},
|
||||
"Desaturated": {"contrast": 1.0, "brightness": 0.0, "saturation": 0.40, "color_temperature": 0.0},
|
||||
"Custom": None,
|
||||
}
|
||||
# Preset names for the combo widget. Actual values live in web/js/tone_preset.js
|
||||
# which syncs them into the slider widgets on the frontend.
|
||||
TONE_PRESET_NAMES = [
|
||||
"Cinematic", "HDR", "Vivid", "Dramatic", "Low Key",
|
||||
"High Key", "Warm", "Cool", "Desaturated", "Custom",
|
||||
]
|
||||
|
||||
|
||||
class LCSToneAdjust(io.ComfyNode):
|
||||
@@ -476,16 +470,16 @@ class LCSToneAdjust(io.ComfyNode):
|
||||
inputs=[
|
||||
io.Model.Input("model"),
|
||||
LCS_DATA.Input("lcs_data", tooltip="Calibration data from LCSCalibrate or LCSLoadData"),
|
||||
io.Combo.Input("preset", options=list(TONE_PRESETS.keys()), default="Custom",
|
||||
io.Combo.Input("preset", options=TONE_PRESET_NAMES, default="Custom",
|
||||
tooltip="Select a tonal preset or Custom to use the sliders below"),
|
||||
io.Float.Input("contrast", default=1.0, min=0.0, max=3.0, step=0.05, optional=True,
|
||||
tooltip="[Custom only] Lightness contrast multiplier (>1 = more contrast, <1 = less, 1 = no change)"),
|
||||
io.Float.Input("brightness", default=0.0, min=-1.0, max=1.0, step=0.05, optional=True,
|
||||
tooltip="[Custom only] Lightness shift (>0 = brighter, <0 = darker)"),
|
||||
io.Float.Input("saturation", default=1.0, min=0.0, max=3.0, step=0.05, optional=True,
|
||||
tooltip="[Custom only] Saturation multiplier (>1 = more vivid, <1 = more muted, 0 = grayscale)"),
|
||||
io.Float.Input("color_temperature", default=0.0, min=-2.0, max=2.0, step=0.05, optional=True,
|
||||
tooltip="[Custom only] Color temperature shift (>0 = warmer/amber, <0 = cooler/blue)"),
|
||||
io.Float.Input("contrast", default=1.0, min=0.0, max=3.0, step=0.05,
|
||||
tooltip="Lightness contrast multiplier (>1 = more contrast, <1 = less, 1 = no change)"),
|
||||
io.Float.Input("brightness", default=0.0, min=-1.0, max=1.0, step=0.05,
|
||||
tooltip="Lightness shift (>0 = brighter, <0 = darker)"),
|
||||
io.Float.Input("saturation", default=1.0, min=0.0, max=3.0, step=0.05,
|
||||
tooltip="Saturation multiplier (>1 = more vivid, <1 = more muted, 0 = grayscale)"),
|
||||
io.Float.Input("color_temperature", default=0.0, min=-2.0, max=2.0, step=0.05,
|
||||
tooltip="Color temperature shift (>0 = warmer/amber, <0 = cooler/blue)"),
|
||||
io.Int.Input("start_step", default=5, min=0, max=50,
|
||||
tooltip="First step to apply adjustment"),
|
||||
io.Int.Input("end_step", default=15, min=0, max=50,
|
||||
@@ -499,22 +493,13 @@ class LCSToneAdjust(io.ComfyNode):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def execute(cls, model, lcs_data, preset, start_step, end_step,
|
||||
contrast=None, brightness=None, saturation=None, color_temperature=None,
|
||||
mask=None) -> io.NodeOutput:
|
||||
"""Clone model, attach LCS tone adjustment hook. Returns patched MODEL."""
|
||||
if preset != "Custom":
|
||||
p = TONE_PRESETS[preset]
|
||||
contrast = p["contrast"]
|
||||
brightness = p["brightness"]
|
||||
saturation = p["saturation"]
|
||||
color_temperature = p["color_temperature"]
|
||||
else:
|
||||
contrast = contrast if contrast is not None else 1.0
|
||||
brightness = brightness if brightness is not None else 0.0
|
||||
saturation = saturation if saturation is not None else 1.0
|
||||
color_temperature = color_temperature if color_temperature is not None else 0.0
|
||||
def execute(cls, model, lcs_data, preset, contrast, brightness, saturation,
|
||||
color_temperature, start_step, end_step, mask=None) -> io.NodeOutput:
|
||||
"""Clone model, attach LCS tone adjustment hook. Returns patched MODEL.
|
||||
|
||||
The preset combo sets slider values via frontend JS; execute() always
|
||||
uses the actual slider values so the user can tweak after selecting a preset.
|
||||
"""
|
||||
m = model.clone()
|
||||
# Skip hook entirely when all parameters are at default (true no-op)
|
||||
if not _is_default_tone(contrast, brightness, saturation, color_temperature):
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import { app } from "../../../scripts/app.js";
|
||||
|
||||
// Preset values — the single source of truth for preset parameters.
|
||||
// TONE_PRESET_NAMES in nodes/intervene.py must list matching keys.
|
||||
const TONE_PRESETS = {
|
||||
"Cinematic": { contrast: 1.20, brightness: -0.05, saturation: 0.90, color_temperature: 0.15 },
|
||||
"HDR": { contrast: 1.40, brightness: 0.0, saturation: 1.20, color_temperature: 0.0 },
|
||||
"Vivid": { contrast: 1.10, brightness: 0.0, saturation: 1.50, color_temperature: 0.0 },
|
||||
"Dramatic": { contrast: 1.50, brightness: -0.10, saturation: 0.85, color_temperature: 0.0 },
|
||||
"Low Key": { contrast: 1.30, brightness: -0.20, saturation: 0.80, color_temperature: 0.0 },
|
||||
"High Key": { contrast: 0.80, brightness: 0.20, saturation: 0.90, color_temperature: 0.0 },
|
||||
"Warm": { contrast: 1.0, brightness: 0.0, saturation: 1.0, color_temperature: 0.50 },
|
||||
"Cool": { contrast: 1.0, brightness: 0.0, saturation: 1.0, color_temperature: -0.50 },
|
||||
"Desaturated": { contrast: 1.0, brightness: 0.0, saturation: 0.40, color_temperature: 0.0 },
|
||||
};
|
||||
|
||||
const SLIDER_NAMES = ["contrast", "brightness", "saturation", "color_temperature"];
|
||||
|
||||
function syncPreset(node, presetName) {
|
||||
const preset = TONE_PRESETS[presetName];
|
||||
if (!preset) return; // "Custom" — leave sliders as-is
|
||||
|
||||
for (const name of SLIDER_NAMES) {
|
||||
const widget = node.widgets?.find(w => w.name === name);
|
||||
if (widget && name in preset) {
|
||||
widget.value = preset[name];
|
||||
}
|
||||
}
|
||||
node.graph?.setDirtyCanvas(true, true);
|
||||
}
|
||||
|
||||
app.registerExtension({
|
||||
name: "ComfyUI-LCS.TonePresetSync",
|
||||
|
||||
nodeCreated(node) {
|
||||
if (node.comfyClass !== "LCSToneAdjust") return;
|
||||
|
||||
const presetWidget = node.widgets?.find(w => w.name === "preset");
|
||||
if (!presetWidget) return;
|
||||
|
||||
// Only sync on explicit user interaction (dropdown change), not on
|
||||
// workflow load or paste — those restore saved slider values directly.
|
||||
const origCallback = presetWidget.callback;
|
||||
presetWidget.callback = function (value, canvas, nodeRef, pos, event) {
|
||||
if (origCallback) origCallback.call(this, value, canvas, nodeRef, pos, event);
|
||||
syncPreset(node, value);
|
||||
};
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user