Stabilize MSR character form syncing
This commit is contained in:
@@ -34,9 +34,7 @@ function resolvePreviewUrlFromOrigin(originNode) {
|
|||||||
if (preview) return preview;
|
if (preview) return preview;
|
||||||
|
|
||||||
const widgets = originNode.widgets || [];
|
const widgets = originNode.widgets || [];
|
||||||
const fileWidget = widgets.find((widget) =>
|
const fileWidget = widgets.find((widget) => ["image", "filename", "file"].includes(widget.name));
|
||||||
["image", "filename", "file"].includes(widget.name)
|
|
||||||
);
|
|
||||||
const subfolderWidget = widgets.find((widget) => widget.name === "subfolder");
|
const subfolderWidget = widgets.find((widget) => widget.name === "subfolder");
|
||||||
const filename = typeof fileWidget?.value === "string" ? fileWidget.value : "";
|
const filename = typeof fileWidget?.value === "string" ? fileWidget.value : "";
|
||||||
const subfolder = typeof subfolderWidget?.value === "string" ? subfolderWidget.value : "";
|
const subfolder = typeof subfolderWidget?.value === "string" ? subfolderWidget.value : "";
|
||||||
@@ -91,27 +89,38 @@ function setWidgetValue(node, widget, value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function syncFormFromWidgets(node) {
|
||||||
|
const description = findWidget(node, "description")?.value || "";
|
||||||
|
const alias = findWidget(node, "alias")?.value || "";
|
||||||
|
|
||||||
|
if (node._msrAliasInput && node._msrAliasInput.value !== alias) {
|
||||||
|
node._msrAliasInput.value = alias;
|
||||||
|
}
|
||||||
|
if (node._msrDescriptionInput && node._msrDescriptionInput.value !== description) {
|
||||||
|
node._msrDescriptionInput.value = description;
|
||||||
|
}
|
||||||
|
if (node._msrMeta) {
|
||||||
|
node._msrMeta.innerHTML = [
|
||||||
|
alias
|
||||||
|
? `Alias: <span style="color:#e8e8e8">@${String(alias).replace(/^@/, "")}</span>`
|
||||||
|
: "Alias: <span style=\"color:#666\">none</span>",
|
||||||
|
description
|
||||||
|
? `<div style="margin-top:4px;color:#d0d0d0">${description}</div>`
|
||||||
|
: `<div style="margin-top:4px;color:#666">Description will be used for prompt replacement outside MSR prefix mode.</div>`,
|
||||||
|
].join("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function refreshCharacterPreview(node) {
|
function refreshCharacterPreview(node) {
|
||||||
if (!node._msrPreviewContainer) return;
|
if (!node._msrPreviewRow) return;
|
||||||
const container = node._msrPreviewContainer;
|
|
||||||
container.innerHTML = "";
|
|
||||||
|
|
||||||
const inputNames = ["image_1", "image_2"];
|
const inputNames = ["image_1", "image_2"];
|
||||||
const previewUrls = inputNames
|
const previewUrls = inputNames
|
||||||
.map((inputName) => resolvePreviewUrlFromOrigin(getOriginNodeForInput(node, inputName)))
|
.map((inputName) => resolvePreviewUrlFromOrigin(getOriginNodeForInput(node, inputName)))
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
|
|
||||||
const description = findWidget(node, "description")?.value || "";
|
const previewRow = node._msrPreviewRow;
|
||||||
const alias = findWidget(node, "alias")?.value || "";
|
previewRow.innerHTML = "";
|
||||||
const aliasWidget = findWidget(node, "alias");
|
|
||||||
const descriptionWidget = findWidget(node, "description");
|
|
||||||
|
|
||||||
const topRow = document.createElement("div");
|
|
||||||
Object.assign(topRow.style, {
|
|
||||||
display: "flex",
|
|
||||||
gap: "6px",
|
|
||||||
minHeight: "74px",
|
|
||||||
});
|
|
||||||
|
|
||||||
if (previewUrls.length) {
|
if (previewUrls.length) {
|
||||||
previewUrls.forEach((previewUrl) => {
|
previewUrls.forEach((previewUrl) => {
|
||||||
@@ -126,7 +135,7 @@ function refreshCharacterPreview(node) {
|
|||||||
border: "1px solid #444",
|
border: "1px solid #444",
|
||||||
background: "#111",
|
background: "#111",
|
||||||
});
|
});
|
||||||
topRow.appendChild(image);
|
previewRow.appendChild(image);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const placeholder = document.createElement("div");
|
const placeholder = document.createElement("div");
|
||||||
@@ -143,9 +152,27 @@ function refreshCharacterPreview(node) {
|
|||||||
border: "1px dashed #444",
|
border: "1px dashed #444",
|
||||||
background: "#181818",
|
background: "#181818",
|
||||||
});
|
});
|
||||||
topRow.appendChild(placeholder);
|
previewRow.appendChild(placeholder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
syncFormFromWidgets(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildCharacterUi(node) {
|
||||||
|
const container = document.createElement("div");
|
||||||
|
Object.assign(container.style, {
|
||||||
|
boxSizing: "border-box",
|
||||||
|
width: "100%",
|
||||||
|
padding: "6px 2px 2px 2px",
|
||||||
|
});
|
||||||
|
|
||||||
|
const previewRow = document.createElement("div");
|
||||||
|
Object.assign(previewRow.style, {
|
||||||
|
display: "flex",
|
||||||
|
gap: "6px",
|
||||||
|
minHeight: "74px",
|
||||||
|
});
|
||||||
|
|
||||||
const meta = document.createElement("div");
|
const meta = document.createElement("div");
|
||||||
Object.assign(meta.style, {
|
Object.assign(meta.style, {
|
||||||
marginTop: "8px",
|
marginTop: "8px",
|
||||||
@@ -153,15 +180,6 @@ function refreshCharacterPreview(node) {
|
|||||||
color: "#aaa",
|
color: "#aaa",
|
||||||
lineHeight: "1.4",
|
lineHeight: "1.4",
|
||||||
});
|
});
|
||||||
meta.innerHTML = [
|
|
||||||
alias ? `Alias: <span style="color:#e8e8e8">@${String(alias).replace(/^@/, "")}</span>` : "Alias: <span style=\"color:#666\">none</span>",
|
|
||||||
description
|
|
||||||
? `<div style="margin-top:4px;color:#d0d0d0">${description}</div>`
|
|
||||||
: `<div style="margin-top:4px;color:#666">Description will be used for prompt replacement outside MSR prefix mode.</div>`,
|
|
||||||
].join("");
|
|
||||||
|
|
||||||
container.appendChild(topRow);
|
|
||||||
container.appendChild(meta);
|
|
||||||
|
|
||||||
const form = document.createElement("div");
|
const form = document.createElement("div");
|
||||||
Object.assign(form.style, {
|
Object.assign(form.style, {
|
||||||
@@ -180,7 +198,6 @@ function refreshCharacterPreview(node) {
|
|||||||
|
|
||||||
const aliasInput = document.createElement("input");
|
const aliasInput = document.createElement("input");
|
||||||
aliasInput.type = "text";
|
aliasInput.type = "text";
|
||||||
aliasInput.value = alias || "";
|
|
||||||
aliasInput.placeholder = "optional alias, e.g. bob";
|
aliasInput.placeholder = "optional alias, e.g. bob";
|
||||||
Object.assign(aliasInput.style, {
|
Object.assign(aliasInput.style, {
|
||||||
width: "100%",
|
width: "100%",
|
||||||
@@ -193,7 +210,8 @@ function refreshCharacterPreview(node) {
|
|||||||
fontSize: "12px",
|
fontSize: "12px",
|
||||||
});
|
});
|
||||||
aliasInput.addEventListener("input", () => {
|
aliasInput.addEventListener("input", () => {
|
||||||
setWidgetValue(node, aliasWidget, aliasInput.value);
|
setWidgetValue(node, findWidget(node, "alias"), aliasInput.value);
|
||||||
|
syncFormFromWidgets(node);
|
||||||
});
|
});
|
||||||
|
|
||||||
const descLabel = document.createElement("label");
|
const descLabel = document.createElement("label");
|
||||||
@@ -204,7 +222,6 @@ function refreshCharacterPreview(node) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const descInput = document.createElement("textarea");
|
const descInput = document.createElement("textarea");
|
||||||
descInput.value = description || "";
|
|
||||||
descInput.placeholder = "Character description...";
|
descInput.placeholder = "Character description...";
|
||||||
Object.assign(descInput.style, {
|
Object.assign(descInput.style, {
|
||||||
width: "100%",
|
width: "100%",
|
||||||
@@ -220,14 +237,25 @@ function refreshCharacterPreview(node) {
|
|||||||
lineHeight: "1.35",
|
lineHeight: "1.35",
|
||||||
});
|
});
|
||||||
descInput.addEventListener("input", () => {
|
descInput.addEventListener("input", () => {
|
||||||
setWidgetValue(node, descriptionWidget, descInput.value);
|
setWidgetValue(node, findWidget(node, "description"), descInput.value);
|
||||||
|
syncFormFromWidgets(node);
|
||||||
});
|
});
|
||||||
|
|
||||||
form.appendChild(aliasLabel);
|
form.appendChild(aliasLabel);
|
||||||
form.appendChild(aliasInput);
|
form.appendChild(aliasInput);
|
||||||
form.appendChild(descLabel);
|
form.appendChild(descLabel);
|
||||||
form.appendChild(descInput);
|
form.appendChild(descInput);
|
||||||
|
|
||||||
|
container.appendChild(previewRow);
|
||||||
|
container.appendChild(meta);
|
||||||
container.appendChild(form);
|
container.appendChild(form);
|
||||||
|
|
||||||
|
node._msrPreviewContainer = container;
|
||||||
|
node._msrPreviewRow = previewRow;
|
||||||
|
node._msrMeta = meta;
|
||||||
|
node._msrAliasInput = aliasInput;
|
||||||
|
node._msrDescriptionInput = descInput;
|
||||||
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function analyzeCharacterNode(node, buttonWidget) {
|
async function analyzeCharacterNode(node, buttonWidget) {
|
||||||
@@ -261,7 +289,12 @@ async function analyzeCharacterNode(node, buttonWidget) {
|
|||||||
throw new Error(result.message || "Unknown analysis error");
|
throw new Error(result.message || "Unknown analysis error");
|
||||||
}
|
}
|
||||||
|
|
||||||
setWidgetValue(node, descriptionWidget, result.description || "");
|
const description = result.description || "";
|
||||||
|
setWidgetValue(node, descriptionWidget, description);
|
||||||
|
if (node._msrDescriptionInput) {
|
||||||
|
node._msrDescriptionInput.value = description;
|
||||||
|
}
|
||||||
|
syncFormFromWidgets(node);
|
||||||
refreshCharacterPreview(node);
|
refreshCharacterPreview(node);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("[MSRCharacter] analyze failed", error);
|
console.error("[MSRCharacter] analyze failed", error);
|
||||||
@@ -272,6 +305,16 @@ async function analyzeCharacterNode(node, buttonWidget) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hideNodeWidget(widget) {
|
||||||
|
if (!widget) return;
|
||||||
|
widget.hidden = true;
|
||||||
|
if (!widget.options) widget.options = {};
|
||||||
|
widget.options.hidden = true;
|
||||||
|
if (widget.element) {
|
||||||
|
widget.element.style.display = "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
app.registerExtension({
|
app.registerExtension({
|
||||||
name: "Comfy.MSRCharacterCS",
|
name: "Comfy.MSRCharacterCS",
|
||||||
async beforeRegisterNodeDef(nodeType, nodeData) {
|
async beforeRegisterNodeDef(nodeType, nodeData) {
|
||||||
@@ -290,13 +333,7 @@ app.registerExtension({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this._msrPreviewWidget) {
|
if (!this._msrPreviewWidget) {
|
||||||
const previewContainer = document.createElement("div");
|
const previewContainer = buildCharacterUi(this);
|
||||||
Object.assign(previewContainer.style, {
|
|
||||||
boxSizing: "border-box",
|
|
||||||
width: "100%",
|
|
||||||
padding: "6px 2px 2px 2px",
|
|
||||||
});
|
|
||||||
this._msrPreviewContainer = previewContainer;
|
|
||||||
this._msrPreviewWidget = this.addDOMWidget("msr_character_ui", "msr_character_ui", previewContainer, {
|
this._msrPreviewWidget = this.addDOMWidget("msr_character_ui", "msr_character_ui", previewContainer, {
|
||||||
getValue: () => "",
|
getValue: () => "",
|
||||||
setValue: () => {},
|
setValue: () => {},
|
||||||
@@ -314,19 +351,19 @@ app.registerExtension({
|
|||||||
const originalConfigure = this.onConfigure;
|
const originalConfigure = this.onConfigure;
|
||||||
this.onConfigure = function () {
|
this.onConfigure = function () {
|
||||||
const result = originalConfigure?.apply(this, arguments);
|
const result = originalConfigure?.apply(this, arguments);
|
||||||
setTimeout(() => refreshCharacterPreview(this), 0);
|
setTimeout(() => {
|
||||||
|
syncFormFromWidgets(this);
|
||||||
|
refreshCharacterPreview(this);
|
||||||
|
}, 0);
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
["alias", "description"].forEach((widgetName) => {
|
["alias", "description"].forEach((widgetName) => {
|
||||||
const widget = findWidget(this, widgetName);
|
hideNodeWidget(findWidget(this, widgetName));
|
||||||
if (!widget) return;
|
|
||||||
widget.hidden = true;
|
|
||||||
if (!widget.options) widget.options = {};
|
|
||||||
widget.options.hidden = true;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.size[0] = Math.max(this.size[0] || 0, 330);
|
this.size[0] = Math.max(this.size[0] || 0, 330);
|
||||||
|
syncFormFromWidgets(this);
|
||||||
refreshCharacterPreview(this);
|
refreshCharacterPreview(this);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user