* feat(nodes): add Easy_Utility — INT → padded STRING dispatcher First mode `int_to_padded_string`: takes an INT widget (with up/down arrows) and emits a zero-padded STRING (default `001`, no `v` prefix). The downstream `koolook_versioning.resolve_version_token` prepends `v` and re-pads, so wiring this into `EasyAIPipeline.version` / `Easy_VideoCombine.version` yields the standard `vNNN` token at execution. Designed for extension: the `mode` widget shipped from day one so adding further conversions (string→int, formatting variants, etc.) doesn't rewire saved workflows. Note: the previously-attempted JS preview-resolver refactor that aimed to fix the "preview shows stale version" bug was reverted in this worktree before commit — it didn't actually fix the bug (the real cause is the JS resolver not descending into ComfyUI subgraphs) and it broke shot_name resolution in the process. The preview-resolver work is NOT in this commit; only the Python node + tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(nodes): guard version widget against pysssss .replace crash PR #180 added a STRING `version` widget to EasyAIPipeline and Easy_VideoCombine. Three workflow states leave widget.value undefined or non-string when graphToPrompt iterates widgets: - Pre-#180 workflows replay an INT (e.g. `1`) into the new STRING slot. - Widget-to-input conversion voids the value in newer LiteGraph. - Easy_VideoCombine's dict-based widgets_values omits the new key entirely on workflows saved before #180. With pysssss/ComfyUI-Custom-Scripts installed, presetText.js patches every STRING TextWidget.serializeValue to run callbacks that do `value.replace(...)` for `{variable}` substitution. Any of the states above crashes graphToPrompt and turns Run into a no-op for the whole workflow ("Cannot read properties of undefined (reading 'replace')"). Fix: trap `widget.value` reads/writes on the version widget via `Object.defineProperty` so the property is always coerced to a string regardless of caller. `configurable: true` so downstream code can still override the descriptor if it needs to. Tests: 231 passing; both JS files parse OK. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(ai-pipeline): resolve subgraph version preview * feat(video): add path-aware load video node * fix(ai-pipeline): preview connected text names * fix(pr184): address review cleanup --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
33 lines
1.1 KiB
JavaScript
33 lines
1.1 KiB
JavaScript
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
//
|
|
// Shared frontend widget helpers for ComfyUI-Koolook nodes.
|
|
|
|
// Pysssss's presetText.js wraps every STRING widget's serializeValue with a
|
|
// callback chain that does `value.replace(...)`. If widget.value is undefined
|
|
// or null (widget-to-input conversion voiding the value, or a workflow saved
|
|
// before this widget existed), `.replace` throws and graphToPrompt aborts.
|
|
// Trap reads/writes via a property descriptor so widget.value is always a
|
|
// string regardless of caller.
|
|
export function bulletproofStringWidget(widget, fallback = "") {
|
|
if (!widget) return;
|
|
let stored;
|
|
const initial = widget.value;
|
|
if (typeof initial === "string") {
|
|
stored = initial;
|
|
} else if (initial == null) {
|
|
stored = fallback;
|
|
} else {
|
|
stored = String(initial);
|
|
}
|
|
Object.defineProperty(widget, "value", {
|
|
configurable: true,
|
|
enumerable: true,
|
|
get() { return stored; },
|
|
set(v) {
|
|
if (typeof v === "string") stored = v;
|
|
else if (v == null) stored = fallback;
|
|
else stored = String(v);
|
|
},
|
|
});
|
|
}
|