feat: Sidecar + Save Image (Sidecars) nodes + registration

Sidecar chains {content,name,ext} specs over a SIDECAR-typed link; the save node
mirrors SaveImageKJ (folder_paths.get_save_image_path) and writes each sidecar as
base+name+ext next to the image. build_plan validates the whole chain before any
I/O so duplicates/bad extensions write nothing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-01 14:00:38 +02:00
parent 31a7112052
commit b2f5850b46
3 changed files with 166 additions and 2 deletions
+34
View File
@@ -0,0 +1,34 @@
from gates import sidecar_node as sn
def test_sidecar_run_builds_chain():
node = sn.Sidecar()
(chain,) = node.run(content="hello", name="", extension=".txt", sidecar=None)
assert chain == [{"content": "hello", "name": "", "ext": ".txt"}]
(chain2,) = node.run(content="{}", name="meta", extension=".json", sidecar=chain)
assert len(chain2) == 2
assert chain2[1] == {"content": "{}", "name": "meta", "ext": ".json"}
def test_sidecar_io_shape():
assert sn.Sidecar.RETURN_TYPES == ("SIDECAR",)
it = sn.Sidecar.INPUT_TYPES()
assert "content" in it["required"]
assert "name" in it["required"]
assert "extension" in it["required"]
assert "sidecar" in it["optional"]
def test_save_node_io_shape():
assert sn.SaveImageSidecars.OUTPUT_NODE is True
assert sn.SaveImageSidecars.RETURN_TYPES == ()
it = sn.SaveImageSidecars.INPUT_TYPES()
for k in ("images", "filename_prefix", "output_folder"):
assert k in it["required"]
assert "sidecar" in it["optional"]
def test_mappings_present():
assert "Sidecar" in sn.NODE_CLASS_MAPPINGS
assert "SaveImageSidecars" in sn.NODE_CLASS_MAPPINGS
assert sn.NODE_DISPLAY_NAME_MAPPINGS["SaveImageSidecars"]