Files
VibeVoice-Modifications/js/vibevoice_wrapper_ui.js
2025-09-03 19:24:26 +10:00

108 lines
3.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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; }
// dont 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");
},
});