fix(nodes): restore autocomplete widget sync after metadata insertion (#879)

This commit is contained in:
Will Miao
2026-03-29 10:09:39 +08:00
parent 2626dbab8e
commit 301ab14781
6 changed files with 110 additions and 17 deletions

View File

@@ -37,6 +37,13 @@ const updateConnectedTriggerWords = vi.fn();
const mergeLoras = vi.fn();
const getAllGraphNodes = vi.fn();
const getNodeFromGraph = vi.fn();
const getWidgetByName = vi.fn((node, name) =>
node?.widgets?.find((widget) => widget?.name === name) ?? null
);
const getWidgetSerializedValue = vi.fn((node, name) => {
const index = node?.widgets?.findIndex((widget) => widget?.name === name) ?? -1;
return index >= 0 ? node.widgets_values?.[index] : undefined;
});
vi.mock(UTILS_MODULE, () => ({
collectActiveLorasFromChain,
@@ -47,6 +54,8 @@ vi.mock(UTILS_MODULE, () => ({
},
getAllGraphNodes,
getNodeFromGraph,
getWidgetByName,
getWidgetSerializedValue,
LORA_PATTERN: /<lora:([^:]+):([-\d.]+)(?::([-\d.]+))?>/g,
}));
@@ -71,6 +80,9 @@ describe("Lora Loader trigger word updates", () => {
mergeLoras.mockClear();
mergeLoras.mockImplementation(() => [{ name: "Alpha", active: true }]);
getWidgetByName.mockClear();
getWidgetSerializedValue.mockClear();
addLorasWidget.mockClear();
addLorasWidget.mockImplementation((_node, _name, _opts, callback) => ({
widget: { value: [], callback },
@@ -89,14 +101,21 @@ describe("Lora Loader trigger word updates", () => {
// Create mock widget (AUTOCOMPLETE_TEXT_LORAS type created by Vue widgets)
const inputWidget = {
name: "text",
value: "",
options: {},
callback: null, // Will be set by onNodeCreated
};
const metadataWidget = {
name: "__autocomplete_metadata_text",
value: { version: 1, textWidgetName: "text" },
options: {},
};
const node = {
comfyClass: "Lora Loader (LoraManager)",
widgets: [inputWidget],
widgets: [metadataWidget, inputWidget],
addInput: vi.fn(),
graph: {},
};
@@ -106,6 +125,7 @@ describe("Lora Loader trigger word updates", () => {
// The widget is now the AUTOCOMPLETE_TEXT_LORAS type, created automatically by Vue widgets
expect(node.inputWidget).toBe(inputWidget);
expect(node.lorasWidget).toBeDefined();
expect(getWidgetByName).toHaveBeenCalledWith(node, "text");
// The callback should have been set up by onNodeCreated
const inputCallback = inputWidget.callback;

View File

@@ -50,6 +50,13 @@ const getAllGraphNodes = vi.fn();
const getNodeFromGraph = vi.fn();
const getNodeKey = vi.fn();
const getLinkFromGraph = vi.fn();
const getWidgetByName = vi.fn((node, name) =>
node?.widgets?.find((widget) => widget?.name === name) ?? null
);
const getWidgetSerializedValue = vi.fn((node, name) => {
const index = node?.widgets?.findIndex((widget) => widget?.name === name) ?? -1;
return index >= 0 ? node.widgets_values?.[index] : undefined;
});
const chainCallback = vi.fn((proto, property, callback) => {
proto[property] = callback;
});
@@ -68,6 +75,8 @@ vi.mock(UTILS_MODULE, async (importOriginal) => {
getNodeFromGraph,
getNodeKey,
getLinkFromGraph,
getWidgetByName,
getWidgetSerializedValue,
};
});
@@ -98,6 +107,9 @@ describe("Node mode change handling", () => {
mergeLoras.mockClear();
mergeLoras.mockImplementation(() => [{ name: "Alpha", active: true }]);
getWidgetByName.mockClear();
getWidgetSerializedValue.mockClear();
addLorasWidget.mockClear();
addLorasWidget.mockImplementation((_node, _name, _opts, callback) => ({
widget: { value: [], callback },
@@ -119,8 +131,13 @@ describe("Node mode change handling", () => {
await extension.beforeRegisterNodeDef(nodeType, nodeData, {});
// Create widgets with proper structure for lora_stacker.js
// Widget at index 0 is the AUTOCOMPLETE_TEXT_LORAS widget (created by Vue widgets)
// Include a hidden metadata widget ahead of the actual text widget to match runtime ordering.
const metadataWidget = {
name: "__autocomplete_metadata_text",
value: { version: 1, textWidgetName: "text" },
options: {},
};
const inputWidget = {
name: "text",
value: "",
@@ -139,7 +156,7 @@ describe("Node mode change handling", () => {
node = {
comfyClass: "Lora Stacker (LoraManager)",
widgets: [inputWidget, lorasWidget],
widgets: [metadataWidget, inputWidget, lorasWidget],
lorasWidget,
addInput: vi.fn(),
mode: 0, // Initial mode
@@ -189,11 +206,18 @@ describe("Node mode change handling", () => {
const nodeType = { comfyClass: "Lora Loader (LoraManager)", prototype: {} };
await extension.beforeRegisterNodeDef(nodeType, {}, {});
// Widget at index 0 is the AUTOCOMPLETE_TEXT_LORAS widget (created by Vue widgets)
const metadataWidget = {
name: "__autocomplete_metadata_text",
value: { version: 1, textWidgetName: "text" },
options: {},
};
node = {
comfyClass: "Lora Loader (LoraManager)",
widgets: [
metadataWidget,
{
name: "text",
value: "",
options: {},
callback: null, // Will be set by onNodeCreated