First commit

This commit is contained in:
2025-09-03 19:24:26 +10:00
parent 2bf7123ae2
commit 1e1402795f
30 changed files with 7582 additions and 33 deletions

107
js/vibevoice_wrapper_ui.js Normal file
View File

@ -0,0 +1,107 @@
// 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");
},
});