diff --git a/tests/test_image_metadata_transfer.mjs b/tests/test_image_metadata_transfer.mjs
index 0d070c4..f04a802 100644
--- a/tests/test_image_metadata_transfer.mjs
+++ b/tests/test_image_metadata_transfer.mjs
@@ -163,6 +163,43 @@ test('metadata transfer persists only mode and activity scope preferences', () =
});
});
+test('metadata transfer refresh updates only the transfer panel when it is mounted', () => {
+ const state = createTransferState([
+ { category: 'text_encoders', original_path: 'imported.safetensors' },
+ ]);
+ const currentPanel = { outerHTML: '' };
+ const result = {
+ querySelector(selector) {
+ assert.equal(selector, '.mr-image-transfer');
+ return currentPanel;
+ },
+ };
+ let fullRenderCount = 0;
+ let tooltipBindCount = 0;
+ const context = {
+ ...imageMetadataMethods,
+ imageInspectorState: state,
+ contentElement: {
+ querySelector(selector) {
+ assert.equal(selector, '[data-image-inspector-result]');
+ return result;
+ },
+ },
+ renderMetadataTransferPanel: () => '',
+ renderImageMetadataResult() {
+ fullRenderCount += 1;
+ },
+ bindTooltips() {
+ tooltipBindCount += 1;
+ },
+ };
+
+ assert.equal(context.renderMetadataTransferPanelInPlace(), true);
+ assert.equal(currentPanel.outerHTML, '');
+ assert.equal(fullRenderCount, 0);
+ assert.equal(tooltipBindCount, 1);
+});
+
test('metadata transfer exposes Undo transfer while the post-transfer workflow is unchanged', () => {
const context = createTransferContext();
const state = createTransferState([
@@ -220,7 +257,9 @@ test('metadata transfer undo restores the previous workflow and consumes its his
return true;
},
renderImageMetadataResult() {},
- openMetadataTransfer() {},
+ openMetadataTransfer() {
+ state.transfer = { undoing: false };
+ },
showNotification(message, type) {
notifications.push({ message, type });
},
@@ -241,7 +280,7 @@ test('metadata transfer undo restores the previous workflow and consumes its his
assert.deepEqual(restoredWorkflow, previousWorkflow);
assert.notEqual(restoredWorkflow, previousWorkflow);
assert.equal(context.imageInspectorState.lastTransfer, null);
- assert.equal(context.imageInspectorState.transfer, null);
+ assert.equal(context.imageInspectorState.transfer.undoing, false);
assert.equal(context.activeWorkflowSignature, 'before');
assert.equal(context.cachedLoadedModelsSignature, null);
assert.equal(context.cachedLoadedModelsData, null);
diff --git a/web/resolver/views/tabs_image_metadata_methods.js b/web/resolver/views/tabs_image_metadata_methods.js
index 8c9557e..ae7f267 100644
--- a/web/resolver/views/tabs_image_metadata_methods.js
+++ b/web/resolver/views/tabs_image_metadata_methods.js
@@ -286,14 +286,14 @@ export const imageMetadataMethods = {
else delete sourceSelections[selectionKey];
}
state.transfer.sourceSelections = sourceSelections;
- this.renderImageMetadataResult();
+ this.renderMetadataTransferPanelInPlace();
}
return;
}
if (input.matches?.('[data-image-transfer-mode]')) {
const state = getImageInspectorState(this);
if (state.transfer) state.transfer.mode = this.setMetadataTransferMode(input.value);
- this.renderImageMetadataResult();
+ this.renderMetadataTransferPanelInPlace();
return;
}
if (input.matches?.('[data-image-transfer-activity-scope]')) {
@@ -301,7 +301,7 @@ export const imageMetadataMethods = {
if (state.transfer) {
state.transfer.activityScope = this.setMetadataTransferActivityScope(input.value);
}
- this.renderImageMetadataResult();
+ this.renderMetadataTransferPanelInPlace();
return;
}
});
@@ -1482,6 +1482,19 @@ export const imageMetadataMethods = {
`;
},
+ renderMetadataTransferPanelInPlace({ fallback = true } = {}) {
+ const result = this.contentElement?.querySelector('[data-image-inspector-result]');
+ const currentPanel = result?.querySelector('.mr-image-transfer');
+ if (!currentPanel) {
+ if (fallback) this.renderImageMetadataResult();
+ return false;
+ }
+
+ currentPanel.outerHTML = this.renderMetadataTransferPanel(getImageInspectorState(this));
+ this.bindTooltips?.(result);
+ return true;
+ },
+
async openMetadataTransfer({ force = false, preserveContent = false } = {}) {
const state = getImageInspectorState(this);
const imageRequestToken = state.requestToken;
@@ -1540,14 +1553,13 @@ export const imageMetadataMethods = {
state.transfer.targetGroups = Array.isArray(previousTransfer.targetGroups)
? previousTransfer.targetGroups
: [];
- } else {
- this.renderImageMetadataResult();
}
+ this.renderMetadataTransferPanelInPlace({ fallback: !keepCurrentTransferContent });
if (!workflow) {
state.transfer.loading = false;
state.transfer.error = 'No current workflow is available.';
- this.renderImageMetadataResult();
+ this.renderMetadataTransferPanelInPlace();
return null;
}
@@ -1596,7 +1608,7 @@ export const imageMetadataMethods = {
Array.from(state.transfer.selectedNodeKeys || [])
.filter(nodeKey => selectableNodeKeys.has(nodeKey))
);
- this.renderImageMetadataResult();
+ this.renderMetadataTransferPanelInPlace();
return targetModels;
} catch (error) {
if (
@@ -1606,7 +1618,7 @@ export const imageMetadataMethods = {
state.transfer.loading = false;
state.transfer.refreshing = false;
state.transfer.error = error?.message || 'Could not scan the current workflow.';
- this.renderImageMetadataResult();
+ this.renderMetadataTransferPanelInPlace();
return null;
}
},
@@ -1615,7 +1627,7 @@ export const imageMetadataMethods = {
const state = getImageInspectorState(this);
if (!state.transfer) return;
state.transfer.selectedNodeKeys = new Set();
- this.renderImageMetadataResult();
+ this.renderMetadataTransferPanelInPlace();
},
toggleMetadataTransferCategory(category) {
@@ -1628,7 +1640,7 @@ export const imageMetadataMethods = {
if (collapsed.has(normalizedCategory)) collapsed.delete(normalizedCategory);
else collapsed.add(normalizedCategory);
state.transfer.collapsedCategories = collapsed;
- this.renderImageMetadataResult();
+ this.renderMetadataTransferPanelInPlace();
},
toggleMetadataTransferNode(nodeKey) {
@@ -1656,7 +1668,7 @@ export const imageMetadataMethods = {
selected.add(String(nodeKey));
}
state.transfer.selectedNodeKeys = selected;
- this.renderImageMetadataResult();
+ this.renderMetadataTransferPanelInPlace();
},
async undoMetadataTransfer() {
@@ -1667,7 +1679,7 @@ export const imageMetadataMethods = {
const undoState = this.getMetadataTransferUndoState();
if (!undoState.available) {
if (undoState.reason) this.showNotification(undoState.reason, 'warning');
- this.renderImageMetadataResult();
+ this.renderMetadataTransferPanelInPlace();
return null;
}
@@ -1680,7 +1692,7 @@ export const imageMetadataMethods = {
const transfer = state.transfer;
lastTransfer.undoing = true;
if (transfer) transfer.undoing = true;
- this.renderImageMetadataResult();
+ this.renderMetadataTransferPanelInPlace();
try {
const restored = await this.updateWorkflowInComfyUI(restoreWorkflow, []);
@@ -1694,9 +1706,7 @@ export const imageMetadataMethods = {
|| this.getWorkflowSignature?.(restoreWorkflow)
|| null;
state.lastTransfer = null;
- state.transfer = null;
- this.renderImageMetadataResult();
- void this.openMetadataTransfer();
+ void this.openMetadataTransfer({ preserveContent: true });
this.showNotification('Undid the last model transfer.', 'success');
window.dispatchEvent?.(new Event('model-resolver-active-workflowchange'));
return restoreWorkflow;
@@ -1706,7 +1716,7 @@ export const imageMetadataMethods = {
transfer.undoing = false;
transfer.error = error?.message || 'The previous workflow could not be restored.';
}
- this.renderImageMetadataResult();
+ this.renderMetadataTransferPanelInPlace();
return null;
}
},
@@ -1714,7 +1724,7 @@ export const imageMetadataMethods = {
async applyMetadataTransfer() {
const state = getImageInspectorState(this);
const transfer = state.transfer;
- if (!transfer || transfer.loading || transfer.refreshing || transfer.applying) return null;
+ if (!transfer || transfer.loading || transfer.refreshing || transfer.applying || transfer.undoing) return null;
const mode = normalizeTransferMode(transfer.mode);
const activityScope = normalizeTransferActivityScope(transfer.activityScope);
@@ -1821,7 +1831,7 @@ export const imageMetadataMethods = {
const previousWorkflowSignature = this.getWorkflowSignature?.(workflow) || null;
transfer.applying = true;
- this.renderImageMetadataResult();
+ this.renderMetadataTransferPanelInPlace();
try {
const data = await this.fetchJson(
'/model_resolver/transfer-models',
@@ -1858,9 +1868,7 @@ export const imageMetadataMethods = {
undoing: false,
};
this.activeWorkflowSignature = this.getWorkflowSignature?.(updatedWorkflow) || null;
- state.transfer = null;
- this.renderImageMetadataResult();
- void this.openMetadataTransfer();
+ void this.openMetadataTransfer({ preserveContent: true });
const updatedCount = Number(data.updated) || 0;
const skippedCount = Array.isArray(data.skipped) ? data.skipped.length : 0;
const suffix = skippedCount
@@ -1876,7 +1884,7 @@ export const imageMetadataMethods = {
if (state.transfer) {
state.transfer.applying = false;
state.transfer.error = error?.message || 'The metadata model transfer failed.';
- this.renderImageMetadataResult();
+ this.renderMetadataTransferPanelInPlace();
}
return null;
}