ComfyUI-Hand-Tie-Clips / tools /build_notes.py
Sandpies
Hand Tie Clips 0.3.3
7c2113f
Raw
History Blame Contribute Delete
4.44 kB
"""Write the instruction board into the Starter workflow, in place.
Idempotent: cards are identified by `properties.htc_card`, so re-running
replaces the board rather than stacking a second copy beside it. Operates on the
shipped `.json` itself rather than rebuilding from a private source workflow, so
anyone with the pack can run it.
It also strips `widgets_values_named` from every node. Both shipped workflows
carried a stale copy inherited from the dev workflow they were cloned out of --
`chains: 3`, `duration: 10 s`, `control_after_generate: randomize`, and a legacy
`ref_plan` naming pictures that do not ship, 25 entries against a 28-widget node.
It is dormant while `Comfy.Workflow.NamedValuesRestore` stays off (experimental,
default false), but anyone who turns that on would load a Starter that randomizes
its seed and dies on a missing reference. The frontend re-emits the block
correctly on the next save.
"""
import io
import json
import os
import shutil
import sys
HERE = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import notes # noqa: E402
WF = os.path.join(HERE, "workflows", "HandTieClips_Starter.json")
# The board is Starter-only, but the stale-named-values bug is in both shipped
# workflows, so the strip runs over both.
ALSO_STRIP = [os.path.join(HERE, "workflows", "HandTieClips_Showcase.json")]
MARKER = "htc_card"
FIRST_ID = 20
def strip_named(path):
"""Drop `widgets_values_named`, and refresh the Showcase's plain Note.
The Showcase carries one `Note` rather than the card board; its text lives
in `notes.SHOWCASE_NOTE` so there is still a single source for it.
"""
wf = json.load(io.open(path, encoding="utf-8"))
n = sum(1 for node in wf["nodes"]
if node.pop("widgets_values_named", None) is not None)
for node in wf["nodes"]:
if node["type"] == "Note" and node.get("widgets_values") != [notes.SHOWCASE_NOTE]:
node["widgets_values"] = [notes.SHOWCASE_NOTE]
# The dependency block pushed it past what 400x700 shows without
# scrolling.
node["size"] = [440, 900]
n += 1
if n:
bak = path + ".bak-notes"
if not os.path.exists(bak):
shutil.copyfile(path, bak)
io.open(path, "w", encoding="utf-8", newline="\n").write(
json.dumps(wf, indent=2) + "\n")
return n
def main():
wf = json.load(io.open(WF, encoding="utf-8"))
bak = WF + ".bak-notes"
if not os.path.exists(bak):
shutil.copyfile(WF, bak)
# Out with the old board, and with the single legacy `Note` whose text the
# cards now carry between them.
before = len(wf["nodes"])
wf["nodes"] = [n for n in wf["nodes"]
if MARKER not in (n.get("properties") or {})
and n["type"] != "Note"]
dropped = before - len(wf["nodes"])
order = max((n.get("order", 0) for n in wf["nodes"]), default=0)
for i, (key, title, pos, size, colour, text) in enumerate(notes.CARDS):
order += 1
wf["nodes"].append({
"id": FIRST_ID + i,
"type": "MarkdownNote",
"pos": list(pos),
"size": list(size),
"flags": {},
"order": order,
"mode": 0,
"inputs": [],
"outputs": [],
"title": title,
"properties": {MARKER: key},
"widgets_values": [text],
"color": colour[0],
"bgcolor": colour[1],
})
wf["groups"] = [dict(notes.GROUP)]
wf["extra"]["ds"] = dict(notes.DS)
wf["last_node_id"] = max(wf["last_node_id"], FIRST_ID + len(notes.CARDS) - 1)
stripped = 0
for n in wf["nodes"]:
if n.pop("widgets_values_named", None) is not None:
stripped += 1
io.open(WF, "w", encoding="utf-8", newline="\n").write(
json.dumps(wf, indent=2) + "\n")
print("%s\n dropped %d old note/card node(s), wrote %d card(s)"
% (os.path.basename(WF), dropped, len(notes.CARDS)))
print(" stripped widgets_values_named from %d node(s)" % stripped)
print(" %d nodes, %d bytes" % (len(wf["nodes"]), os.path.getsize(WF)))
for path in ALSO_STRIP:
print("%s\n stripped widgets_values_named from %d node(s)"
% (os.path.basename(path), strip_named(path)))
if __name__ == "__main__":
main()