Bugs fixed

This commit is contained in:
IAMCCS
2026-03-01 13:28:32 +01:00
parent 6790ffb429
commit 33a538455e
2 changed files with 228 additions and 46 deletions
+1
View File
@@ -136,6 +136,7 @@ class IAMCCS_LTX2_ExtensionModule:
# Default is 'custom' to preserve existing workflows.
"preset": ([
"custom",
"target_extension_ltx2",
"cut_bestofk_16",
"cut_bestofk_16_luma",
"cut_bestofk_32",
+227 -46
View File
@@ -1,8 +1,41 @@
import { app } from "../../scripts/app.js";
console.log("[IAMCCS LTX2] Loading ExtensionModule presets...");
const IAMCCS_LTX2_EXTENSION_PRESETS_VERSION = "2026-03-01-1";
console.log(
`[IAMCCS LTX2] Loading ExtensionModule presets... v=${IAMCCS_LTX2_EXTENSION_PRESETS_VERSION}`
);
const PRESETS = {
// Target workflow parity: matches `tarket extension ltx2.json` stitch settings
// - overlap = 10
// - overlap_side = source
// - overlap_mode = linear_blend
// - start_images = last (overlap-1) frames from the overlap window
target_extension_ltx2: {
overlap_frames: 10,
overlap_mode: "linear_blend",
overlap_side: "source",
// Match the original graph math: num_frames = overlap - 1
enable_math: true,
math_operation: "a-1",
math_value_b: 1,
safe_mode: "none",
start_frames_rule: "none",
// Keep quality upgrades disabled (parity)
seam_search_mode: "none",
k_search: 0,
color_match_mode: "none",
color_match_strength: 0.0,
color_reference_window: 8,
// Keep defaults explicit for stability
metric_weight_color: 1.0,
metric_weight_edges: 0.5,
},
// Prova 1: cut seam + best_of_k (no crossfade)
cut_bestofk_16: {
overlap_frames: 10,
@@ -52,6 +85,8 @@ const PRESETS = {
},
};
console.log(`[IAMCCS LTX2] Extension presets keys: ${Object.keys(PRESETS).join(", ")}`);
function getWidget(node, name) {
return node?.widgets?.find((w) => w?.name === name);
}
@@ -110,51 +145,92 @@ function hideWidget(widget) {
}
}
function ensureTopPresetWidget(node) {
// Create a visible preset control at the top, without changing serialization order.
// This avoids breaking older workflows that rely on widgets_values ordering.
const existingTop = getWidget(node, "preset_ui");
if (existingTop) return;
const PRESET_UI_WIDGET = "preset_ui";
const backendPreset = getWidget(node, "preset");
node.properties = node.properties || {};
const saved = node.properties.iamccs_extension_preset;
const initial = backendPreset?.value ?? saved ?? "custom";
const values = [
"custom",
"cut_bestofk_16",
"cut_bestofk_16_luma",
"cut_bestofk_32",
"micro_crossfade_3",
];
const ALLOWED_PRESET_KEYS = [
"custom",
"target_extension_ltx2",
"cut_bestofk_16",
"cut_bestofk_16_luma",
"cut_bestofk_32",
"micro_crossfade_3",
];
// Insert widget at the very top.
const w = node.addWidget(
"combo",
"Preset",
initial,
(v) => {
node.properties.iamccs_extension_preset = String(v || "custom");
// Sync backend widget if present.
if (backendPreset) {
backendPreset.value = v;
try {
backendPreset.callback?.(v, app.canvas, node);
} catch {
// ignore
}
}
// Apply overrides (updates other widgets live)
applyPreset(node, v);
},
{ values }
);
w.name = "preset_ui";
w.serialize = false;
const ALLOWED_MATH_OPERATION = [
"none",
"a-b",
"a-1",
"a+b",
"a*b",
"a/b",
"min(a,b)",
"max(a,b)",
];
// Move it to index 0 in the widgets array (top of UI)
const ALLOWED_SAFE_MODE = ["none", "native_workflow_safe"];
function _coerceInt(value, fallback) {
const n = Number(value);
if (!Number.isFinite(n)) return fallback;
return Math.trunc(n);
}
function _sanitizeNodeValues(node) {
// If a workflow was saved while widgets_values were index-shifted,
// ComfyUI may restore impossible values (e.g. strings into INT widgets).
// This causes prompt validation to fail and prevents the node from running.
let changed = false;
const setCombo = (name, allowed, def) => {
const w = getWidget(node, name);
if (!w) return;
const v = String(w.value ?? "");
if (!allowed.includes(v)) {
w.value = def;
changed = true;
}
};
const setInt = (name, def, min, max) => {
const w = getWidget(node, name);
if (!w) return;
const n = _coerceInt(w.value, def);
const clamped = Math.max(min, Math.min(max, n));
if (w.value !== clamped) {
w.value = clamped;
changed = true;
}
};
// Fix the exact fields seen in prompt validation errors.
setInt("math_value_b", 1, 0, 256);
setCombo("math_operation", ALLOWED_MATH_OPERATION, "a-b");
setCombo("safe_mode", ALLOWED_SAFE_MODE, "none");
setCombo("preset", ALLOWED_PRESET_KEYS, "custom");
if (changed) {
try {
node.setDirtyCanvas(true, true);
} catch {
// ignore
}
}
}
function _removePresetUiWidget(node) {
const idx = getWidgetIndex(node, PRESET_UI_WIDGET);
if (idx < 0) return null;
try {
const idx = getWidgetIndex(node, "preset_ui");
const [item] = node.widgets.splice(idx, 1);
return item || null;
} catch {
return null;
}
}
function _movePresetUiWidgetToTop(node) {
try {
const idx = getWidgetIndex(node, PRESET_UI_WIDGET);
if (idx > 0) {
const [item] = node.widgets.splice(idx, 1);
node.widgets.unshift(item);
@@ -162,12 +238,58 @@ function ensureTopPresetWidget(node) {
} catch {
// ignore
}
}
function _createPresetUiWidgetAtEnd(node) {
const existing = getWidget(node, PRESET_UI_WIDGET);
if (existing) return existing;
const backendPreset = getWidget(node, "preset");
node.properties = node.properties || {};
const saved = node.properties.iamccs_extension_preset;
const initial = String(backendPreset?.value ?? saved ?? "custom");
const values = [
"custom",
"target_extension_ltx2",
"cut_bestofk_16",
"cut_bestofk_16_luma",
"cut_bestofk_32",
"micro_crossfade_3",
];
const w = node.addWidget(
"combo",
"Preset",
initial,
(v) => {
const key = String(v || "custom");
node.properties = node.properties || {};
node.properties.iamccs_extension_preset = key;
// Sync backend widget if present.
const backend = getWidget(node, "preset");
if (backend) {
backend.value = key;
try {
backend.callback?.(key, app.canvas, node);
} catch {
// ignore
}
}
// Apply overrides (updates other widgets live)
applyPreset(node, key);
},
{ values }
);
w.name = PRESET_UI_WIDGET;
w.serialize = false;
// Hide the backend preset widget if it exists (avoid duplicate controls).
if (backendPreset) hideWidget(backendPreset);
// Apply immediately for loaded workflows.
applyPreset(node, w.value);
return w;
}
app.registerExtension({
@@ -177,13 +299,72 @@ app.registerExtension({
const nodeName = nodeData?.name;
if (nodeName !== "IAMCCS_LTX2_ExtensionModule" && nodeName !== "IAMCCS_LTX2_ExtensionModule_simple") return;
const serialize = nodeType.prototype.serialize;
nodeType.prototype.serialize = function () {
// Strip the UI-only widget while serializing to avoid any chance
// of widgets_values index shifting across ComfyUI/LiteGraph builds.
const node = this;
const removed = _removePresetUiWidget(node);
const r = serialize?.apply(this, arguments);
if (removed) {
node.widgets.push(removed);
_movePresetUiWidgetToTop(node);
} else {
_createPresetUiWidgetAtEnd(node);
_movePresetUiWidgetToTop(node);
}
return r;
};
const onNodeCreated = nodeType.prototype.onNodeCreated;
nodeType.prototype.onNodeCreated = function () {
const r = onNodeCreated?.apply(this, arguments);
const node = this;
// Always install top preset widget (even if backend widget isn't present yet).
ensureTopPresetWidget(node);
// Create the UI-only preset widget at the end.
// DO NOT move to top here: on loaded workflows, configure() will run after onNodeCreated,
// and any widget index shift before widgets_values restore can corrupt persistence.
_removePresetUiWidget(node);
_createPresetUiWidgetAtEnd(node);
// For brand-new nodes (no configure path), move it to top in a microtask.
queueMicrotask(() => {
try {
if (!node._iamccs_ltx2_did_configure) {
_movePresetUiWidgetToTop(node);
}
} catch {
// ignore
}
});
return r;
};
const onConfigure = nodeType.prototype.onConfigure;
nodeType.prototype.onConfigure = function () {
const r = onConfigure?.apply(this, arguments);
const node = this;
node._iamccs_ltx2_did_configure = true;
// widgets_values have been restored at this point.
const backendPreset = getWidget(node, "preset");
node.properties = node.properties || {};
const restored = String(backendPreset?.value ?? node.properties.iamccs_extension_preset ?? "custom");
node.properties.iamccs_extension_preset = restored;
if (backendPreset) backendPreset.value = restored;
// Guard against previously-corrupted widgets_values causing invalid prompt inputs.
_sanitizeNodeValues(node);
_removePresetUiWidget(node);
const ui = _createPresetUiWidgetAtEnd(node);
ui.value = restored;
_movePresetUiWidgetToTop(node);
// Apply preset AFTER restore so UI reflects backend-enforced behavior.
applyPreset(node, restored);
return r;
};