108 lines
3.3 KiB
JavaScript
108 lines
3.3 KiB
JavaScript
// custom_nodes/YourPkg/js/vibevoice_wrapper_ui.js
|
||
import { app } from "../../scripts/app.js";
|
||
|
||
app.registerExtension({
|
||
name: "vibevoice.wrapper.ui",
|
||
|
||
async beforeRegisterNodeDef(nodeType, nodeData) {
|
||
const isWrapper =
|
||
nodeType?.comfyClass === "VibeVoiceTTS_Wrapper" ||
|
||
nodeData?.name === "VibeVoice TTS (Chunked Wrapper)";
|
||
if (!isWrapper) return;
|
||
|
||
const origOnCreated = nodeType.prototype.onNodeCreated;
|
||
nodeType.prototype.onNodeCreated = function () {
|
||
origOnCreated?.apply(this, arguments);
|
||
// only set up handlers here; do NOT mutate slots yet
|
||
wireUpHandlers(this);
|
||
};
|
||
|
||
function wireUpHandlers(node) {
|
||
const findW = (n) => node.widgets?.find((w) => w.name === n);
|
||
const wNum = findW("num_speakers");
|
||
const wChunk = findW("chunk_lines");
|
||
const wLines = findW("lines_per_chunk");
|
||
|
||
function ensureSpeakerInputs(count) {
|
||
// add missing inputs
|
||
for (let i = 1; i <= count; i++) {
|
||
const name = `speaker_${i}_voice`;
|
||
if (node.findInputSlot(name) === -1) node.addInput(name, "AUDIO");
|
||
}
|
||
// remove extras
|
||
for (let i = count + 1; i <= 4; i++) {
|
||
const name = `speaker_${i}_voice`;
|
||
const idx = node.findInputSlot(name);
|
||
if (idx !== -1) node.removeInput(idx);
|
||
}
|
||
}
|
||
|
||
// guard: only mutate once node.graph exists (prevents NullGraphError)
|
||
function safeMutate(fn) {
|
||
const doIt = () => {
|
||
if (!node.graph) {
|
||
// defer until the node is actually attached to a graph
|
||
setTimeout(doIt, 0);
|
||
return;
|
||
}
|
||
fn();
|
||
app.graph.setDirtyCanvas(true, true);
|
||
};
|
||
doIt();
|
||
}
|
||
|
||
function refresh() {
|
||
const n = Math.max(1, Math.min(4, Number(wNum?.value ?? 1)));
|
||
safeMutate(() => ensureSpeakerInputs(n));
|
||
|
||
if (wLines) wLines.hidden = !(wChunk?.value);
|
||
}
|
||
|
||
// robust wiring (some frontends only call one of these)
|
||
if (wNum) { wNum.callback = refresh; wNum.onChange = refresh; }
|
||
if (wChunk) { wChunk.callback = refresh; wChunk.onChange = refresh; }
|
||
|
||
// don’t call refresh yet; node may not be in graph during configure
|
||
node.__vv_refresh = refresh;
|
||
}
|
||
},
|
||
|
||
// Called for brand-new nodes added from the menu (node has a graph here)
|
||
async nodeCreated(node) {
|
||
if (
|
||
node?.comfyClass === "VibeVoiceTTS_Wrapper" ||
|
||
node?.title === "VibeVoice TTS (Chunked Wrapper)"
|
||
) {
|
||
// next tick to ensure widgets fully exist
|
||
setTimeout(() => node.__vv_refresh?.(), 0);
|
||
}
|
||
},
|
||
|
||
// Called when nodes are created as part of loading a workflow
|
||
loadedGraphNode(node) {
|
||
if (
|
||
node?.comfyClass === "VibeVoiceTTS_Wrapper" ||
|
||
node?.title === "VibeVoice TTS (Chunked Wrapper)"
|
||
) {
|
||
setTimeout(() => node.__vv_refresh?.(), 0);
|
||
}
|
||
},
|
||
|
||
// After the graph finishes configuring (safe point to mutate slots)
|
||
async afterConfigureGraph() {
|
||
// final pass in case anything was deferred
|
||
for (const node of app.graph._nodes) {
|
||
if (
|
||
node?.comfyClass === "VibeVoiceTTS_Wrapper" ||
|
||
node?.title === "VibeVoice TTS (Chunked Wrapper)"
|
||
) {
|
||
node.__vv_refresh?.();
|
||
}
|
||
}
|
||
},
|
||
|
||
async setup() {
|
||
console.log("[vibevoice.wrapper.ui] setup complete");
|
||
},
|
||
});
|