feat: auto-sync resolution/aspect_ratio/duration from upstream trigger node

When ByteDance (or any node) is wired to the trigger input, the JS extension
reads its resolution, aspect_ratio and duration_s widgets by name (with positional
fallback at indices 2/3/4) and mirrors them into CreditDisplay automatically.
Widget callbacks on the upstream node are patched on connection so any change
to ByteDance settings immediately recalculates the cost display.

Made-with: Cursor
This commit is contained in:
Av007
2026-04-17 11:52:55 +03:00
parent f273ee135e
commit ec370fc17b
@@ -150,7 +150,52 @@ app.registerExtension({
return self.widgets?.find((w) => w.name === name);
}
/**
* Try to read a setting from the node connected to our "trigger" input.
* Falls back to undefined if nothing is wired or the widget isn't found.
* Uses widget.name for lookup, then positional index as a fallback
* (ByteDance widget order: 0=model, 1=prompt, 2=resolution, 3=aspect_ratio,
* 4=duration, …).
*/
const UPSTREAM_IDX = { resolution: 2, aspect_ratio: 3, duration_s: 4 };
function getUpstreamWidget(settingName) {
const triggerInput = self.inputs?.find((i) => i.name === "trigger");
if (!triggerInput?.link) return undefined;
const link = app.graph.links[triggerInput.link];
if (!link) return undefined;
const srcNode = app.graph.getNodeById(link.origin_id);
if (!srcNode) return undefined;
// Prefer exact name match, fall back to known positional index
return (
srcNode.widgets?.find((w) => w.name === settingName) ??
srcNode.widgets?.[UPSTREAM_IDX[settingName]]
);
}
/** Mirror a value from upstream into a local widget (if it differs). */
function syncFromUpstream(localName) {
const up = getUpstreamWidget(localName);
const own = getWidget(localName);
if (!up || !own) return;
const parsed = localName === "duration_s"
? parseFloat(up.value)
: String(up.value);
if (own.value !== parsed) {
own.value = parsed;
}
}
function refresh() {
// Pull resolution / aspect_ratio / duration_s from the upstream node
// (ByteDance or any other node wired to "trigger") when available.
syncFromUpstream("resolution");
syncFromUpstream("aspect_ratio");
syncFromUpstream("duration_s");
const model = getWidget("model")?.value;
const resolution = getWidget("resolution")?.value;
const aspectRatio = getWidget("aspect_ratio")?.value;
@@ -172,7 +217,8 @@ app.registerExtension({
}
}
// Attach live listener to every widget
// Attach live listener to every widget on this node AND re-run refresh
// whenever any widget on the upstream (trigger-source) node changes.
const patchCallback = (widget) => {
const orig = widget.callback;
widget.callback = function (...args) {
@@ -185,6 +231,25 @@ app.registerExtension({
patchCallback(w);
}
// Also watch for connection changes so that wiring / unwiring trigger
// triggers an immediate sync.
const origConnected = self.onConnectionsChange;
self.onConnectionsChange = function (...args) {
origConnected?.apply(this, args);
// Patch upstream widgets after a connection is made
queueMicrotask(() => {
const triggerInput = self.inputs?.find((i) => i.name === "trigger");
if (!triggerInput?.link) return;
const link = app.graph.links[triggerInput.link];
const srcNode = link && app.graph.getNodeById(link.origin_id);
if (!srcNode) return;
for (const w of srcNode.widgets ?? []) {
patchCallback(w);
}
refresh();
});
};
// Initial render after the node fully initialises
queueMicrotask(refresh);
};