* 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>