Fix live list updates: install graph hooks in onAdded + title-hash rename polling (new FE compat)

This commit is contained in:
solidlime
2026-08-02 14:19:41 +09:00
parent 85d168818a
commit f0856213ce
+85 -17
View File
@@ -74,6 +74,44 @@ function applyValue(targetNode, mode, value) {
app.graph.setDirtyCanvas(true, true);
}
// ── rename polling (title hash) ─────────────────────────
// The new frontend renames nodes by assigning node.title directly (no
// graph.change() / onPropertyChanged fired), so renames are detected by
// polling a title hash (cg-use-everywhere approach). One shared 500ms
// interval: started when the first panel installs its hooks, stopped when
// the last panel is removed.
const panels = new Set();
let titlesPollTimer = null;
let lastTitlesKey = "";
function titlesKey() {
return (app.graph?._nodes || [])
.map((n) => (n.title || "") + "\u0000" + (n.type || ""))
.join("|");
}
function ensurePolling() {
if (titlesPollTimer == null && panels.size > 0) {
lastTitlesKey = titlesKey();
titlesPollTimer = setInterval(() => {
const k = titlesKey();
if (k !== lastTitlesKey) {
lastTitlesKey = k;
panels.forEach((p) => {
if (typeof p.__scheduleRefresh === "function") p.__scheduleRefresh();
});
}
}, 500);
}
}
function stopPollingIfEmpty() {
if (panels.size === 0 && titlesPollTimer != null) {
clearInterval(titlesPollTimer);
titlesPollTimer = null;
}
}
// ── panel widget builder ─────────────────────────────────
function buildPanel(panelNode) {
@@ -261,21 +299,50 @@ function buildPanel(panelNode) {
// ── event hooks ───────────────────────────────────────
// Graph changes (add/remove/rename nodes) — LiteGraph has no graph.on("change")
// event; chain the on_change property hook instead (works on old and new litegraph).
const graph = panelNode.graph;
const prevOnChange = graph ? graph.on_change : null;
const onGraphChangeWrapper = (g) => {
if (prevOnChange) prevOnChange(g);
scheduleRefresh();
};
if (graph) graph.on_change = onGraphChangeWrapper;
// Expose scheduleRefresh so the module-level rename poller can trigger
// refreshes on all live panels.
panelNode.__scheduleRefresh = scheduleRefresh;
// Title / property changes
const origOnPropertyChanged = panelNode.onPropertyChanged;
panelNode.onPropertyChanged = function (prop, value) {
if (prop === "title") scheduleRefresh();
if (origOnPropertyChanged) origOnPropertyChanged.call(this, prop, value);
// nodeCreated runs inside the ComfyNode constructor, where node.graph is
// always null — so graph hooks must be installed in onAdded (fires with the
// graph argument; node.graph is set by then). Add/remove both fire
// graph.change(), so chaining graph.on_change plus onNodeAdded/onNodeRemoved
// (belt & suspenders) covers structural changes.
let prevChange = null;
let prevNodeAdded = null;
let prevNodeRemoved = null;
const onChangeWrapper = (g) => { prevChange?.(g); scheduleRefresh(); };
const onNodeAddedWrapper = (n) => { prevNodeAdded?.(n); scheduleRefresh(); };
const onNodeRemovedWrapper = (n) => { prevNodeRemoved?.(n); scheduleRefresh(); };
function installGraphHooks(g) {
if (!g || panelNode.__hooksInstalled) return;
panelNode.__hooksInstalled = true;
prevChange = g.on_change;
prevNodeAdded = g.onNodeAdded;
prevNodeRemoved = g.onNodeRemoved;
g.on_change = onChangeWrapper;
g.onNodeAdded = onNodeAddedWrapper;
g.onNodeRemoved = onNodeRemovedWrapper;
}
function uninstallGraphHooks() {
const g = panelNode.graph;
if (!panelNode.__hooksInstalled || !g) return;
// Restore only when our wrapper is still the current handler.
if (g.on_change === onChangeWrapper) g.on_change = prevChange;
if (g.onNodeAdded === onNodeAddedWrapper) g.onNodeAdded = prevNodeAdded;
if (g.onNodeRemoved === onNodeRemovedWrapper) g.onNodeRemoved = prevNodeRemoved;
panelNode.__hooksInstalled = false;
prevChange = prevNodeAdded = prevNodeRemoved = null;
}
const origOnAdded = panelNode.onAdded;
panelNode.onAdded = function (graph) {
installGraphHooks(graph || panelNode.graph);
panels.add(panelNode);
ensurePolling();
if (origOnAdded) origOnAdded.call(this, graph);
};
// Mode widget changes — re-evaluate enabled/disabled rows
@@ -294,9 +361,10 @@ function buildPanel(panelNode) {
// Cleanup on removal
const origOnRemoved = panelNode.onRemoved;
panelNode.onRemoved = function () {
if (panelNode.graph && panelNode.graph.on_change === onGraphChangeWrapper) {
panelNode.graph.on_change = prevOnChange;
}
uninstallGraphHooks();
panels.delete(panelNode);
panelNode.__scheduleRefresh = null;
stopPollingIfEmpty();
if (origOnRemoved) origOnRemoved.call(this);
};
}