diff --git a/web/resolver/actions/queue_methods.js b/web/resolver/actions/queue_methods.js
index 98b8012..919af32 100644
--- a/web/resolver/actions/queue_methods.js
+++ b/web/resolver/actions/queue_methods.js
@@ -1161,7 +1161,7 @@ export const queueMethods = {
? 'Right-click for workflow and download folder actions'
: 'Right-click to switch to workflow';
const contextData = contextModel
- ? ` data-model="${this.escapeHtml(encodeURIComponent(JSON.stringify(contextModel)))}" oncontextmenu="window.MLOpenContextMenu(event, this)" data-tooltip="${this.escapeHtml(contextTooltip)}"`
+ ? this.getContextMenuAttrs(contextModel, contextTooltip)
: '';
html += `
`;
@@ -1215,7 +1215,7 @@ export const queueMethods = {
? 'Right-click for workflow and download folder actions'
: (hasWorkflowAction ? 'Right-click to switch to workflow' : 'Right-click to open download folder');
const contextData = contextModel
- ? ` data-model="${this.escapeHtml(encodeURIComponent(JSON.stringify(contextModel)))}" oncontextmenu="window.MLOpenContextMenu(event, this)" data-tooltip="${this.escapeHtml(contextTooltip)}"`
+ ? this.getContextMenuAttrs(contextModel, contextTooltip)
: '';
const historyId = String(entry.id || '');
@@ -1322,9 +1322,7 @@ export const queueMethods = {
if (!selection) {
selectedBar.style.display = 'none';
selectedBar.innerHTML = '';
- selectedBar.removeAttribute('data-model');
- selectedBar.removeAttribute('data-tooltip');
- selectedBar.oncontextmenu = null;
+ this.setContextMenuElement(selectedBar, null);
return;
}
@@ -1343,18 +1341,12 @@ export const queueMethods = {
}
: null;
const selectedContextAttrs = selectedContext
- ? ` data-model="${this.escapeHtml(encodeURIComponent(JSON.stringify(selectedContext)))}" oncontextmenu="window.MLOpenContextMenu(event, this)" data-tooltip="Right-click to open containing folder"`
+ ? this.getContextMenuAttrs(selectedContext, 'Right-click to open containing folder')
: '';
if (selectedContext) {
- selectedBar.dataset.model = encodeURIComponent(JSON.stringify(selectedContext));
- selectedBar.dataset.tooltip = 'Right-click to open containing folder';
- selectedBar.oncontextmenu = (event) => {
- window.MLOpenContextMenu?.(event, selectedBar);
- };
+ this.setContextMenuElement(selectedBar, selectedContext, 'Right-click to open containing folder');
} else {
- selectedBar.removeAttribute('data-model');
- selectedBar.removeAttribute('data-tooltip');
- selectedBar.oncontextmenu = null;
+ this.setContextMenuElement(selectedBar, null);
}
const applyBtnId = `selected-apply-${domKey}`;
const removeBtnId = `selected-remove-${domKey}`;
diff --git a/web/resolver/search/download_target_methods.js b/web/resolver/search/download_target_methods.js
index ef8a499..6b79f58 100644
--- a/web/resolver/search/download_target_methods.js
+++ b/web/resolver/search/download_target_methods.js
@@ -641,17 +641,11 @@ export const downloadTargetMethods = {
setDownloadFolderContextTarget(element, contextMenuModel = null, tooltip = 'Right-click to open this folder') {
if (!element) return;
if (contextMenuModel) {
- element.dataset.model = encodeURIComponent(JSON.stringify(contextMenuModel));
- element.dataset.tooltip = tooltip;
+ this.setContextMenuElement(element, contextMenuModel, tooltip);
element.classList.add('mr-download-folder-context');
- element.oncontextmenu = (event) => {
- window.MLOpenContextMenu?.(event, element);
- };
} else {
- delete element.dataset.model;
- delete element.dataset.tooltip;
+ this.setContextMenuElement(element, null);
element.classList.remove('mr-download-folder-context');
- element.oncontextmenu = null;
}
},
@@ -1995,7 +1989,7 @@ export const downloadTargetMethods = {
...context,
open_folder_label: 'Open Folder'
};
- return ` data-model="${this.escapeHtml(encodeURIComponent(JSON.stringify(menuContext)))}" oncontextmenu="window.MLOpenContextMenu(event, this)" data-tooltip="${this.escapeHtml(tooltip)}"`;
+ return this.getContextMenuAttrs(menuContext, tooltip);
};
const buildFolderTree = (entries = []) => {
diff --git a/web/resolver/search/search_panel.js b/web/resolver/search/search_panel.js
index a88f339..3a6d44d 100644
--- a/web/resolver/search/search_panel.js
+++ b/web/resolver/search/search_panel.js
@@ -1771,7 +1771,7 @@ export const searchPanelMethods = {
| ${sourcePill} |
-
+
${modelHtml}
${secondary || filename ? `${secondary || filename}` : ''}
@@ -1841,13 +1841,8 @@ export const searchPanelMethods = {
} = {}) {
const safePercent = Math.max(0, Math.min(100, Number(percent) || 0));
const actionAttr = actionDataAttr ? ` ${actionDataAttr}` : '';
- const contextMenuData = contextMenuModel
- ? this.escapeHtml(encodeURIComponent(JSON.stringify(contextMenuModel)))
- : '';
- const contextMenuAttrs = contextMenuData
- ? ` data-model="${contextMenuData}" oncontextmenu="window.MLOpenContextMenu(event, this)" data-tooltip="${this.escapeHtml(contextMenuTooltip)}"`
- : '';
- const containerClass = contextMenuData
+ const contextMenuAttrs = this.getContextMenuAttrs(contextMenuModel, contextMenuTooltip);
+ const containerClass = contextMenuAttrs
? 'mr-progress-container mr-download-folder-context'
: 'mr-progress-container';
const actionHtml = actionClass || actionText
@@ -1931,9 +1926,8 @@ export const searchPanelMethods = {
const matchPath = match.model?.relative_path || match.model?.path || match.path || match.filename || '';
const isBestMatch = matchIndex === 0 && match.confidence >= 95;
const contextModel = this.buildContextMenuModelData(match.model || {}, match.filename || '');
- const modelData = encodeURIComponent(JSON.stringify(contextModel));
- html += ` `;
+ html += ` `;
html += this.getConfidenceBadge(match.confidence);
html += ` ${this.escapeHtml(matchPath)}`;
html += ` ${match.confidence === 100 ? 'Exact' : 'Partial'}`;
@@ -1957,9 +1951,8 @@ export const searchPanelMethods = {
const match = otherMatches[mIdx];
const altBtnId = `resolve-alt-${missingIndex}-${missing.node_id}-${missing.widget_index}-${mIdx}`;
const contextModel = this.buildContextMenuModelData(match.model || {}, match.filename || '');
- const modelData = encodeURIComponent(JSON.stringify(contextModel));
const matchPath = match.model?.relative_path || match.model?.path || match.path || match.filename || '';
- html += ` `;
+ html += ` `;
html += this.getConfidenceBadge(match.confidence);
html += `${this.escapeHtml(matchPath)}`;
html += `Partial`;
diff --git a/web/resolver/utils/render_format_methods.js b/web/resolver/utils/render_format_methods.js
index 63f18b4..a19e1fb 100644
--- a/web/resolver/utils/render_format_methods.js
+++ b/web/resolver/utils/render_format_methods.js
@@ -3,6 +3,59 @@ import { api } from "../../../../../scripts/api.js";
import { $el } from "../../../../../scripts/ui.js";
import { getSvgIcon } from "../../utils/icon_utils.js";
export const renderFormatMethods = {
+ encodeContextMenuModel(context = null) {
+ if (!context) return '';
+ try {
+ return encodeURIComponent(JSON.stringify(context));
+ } catch (error) {
+ console.warn('Model Resolver: failed to encode context menu model', error);
+ return '';
+ }
+ },
+
+ getContextMenuAttrs(context = null, tooltip = '') {
+ const data = this.encodeContextMenuModel(context);
+ if (!data) return '';
+ const tooltipAttr = tooltip ? ` data-tooltip="${this.escapeHtml(tooltip)}"` : '';
+ return ` data-model="${this.escapeHtml(data)}" oncontextmenu="window.MLOpenContextMenu(event, this)"${tooltipAttr}`;
+ },
+
+ getContextMenuProps(context = null, tooltip = '') {
+ const data = this.encodeContextMenuModel(context);
+ if (!data) return {};
+ const props = {
+ "data-model": data,
+ oncontextmenu: (event) => {
+ window.MLOpenContextMenu?.(event, event.currentTarget);
+ }
+ };
+ if (tooltip) {
+ props["data-tooltip"] = tooltip;
+ }
+ return props;
+ },
+
+ setContextMenuElement(element, context = null, tooltip = '') {
+ if (!element) return;
+ const data = this.encodeContextMenuModel(context);
+ if (!data) {
+ element.removeAttribute('data-model');
+ element.removeAttribute('data-tooltip');
+ element.oncontextmenu = null;
+ return;
+ }
+
+ element.dataset.model = data;
+ if (tooltip) {
+ element.dataset.tooltip = tooltip;
+ } else {
+ element.removeAttribute('data-tooltip');
+ }
+ element.oncontextmenu = (event) => {
+ window.MLOpenContextMenu?.(event, event.currentTarget);
+ };
+ },
+
/**
* Get a colored confidence badge HTML
* @param {number} confidence - Confidence percentage (0-100)
@@ -110,13 +163,8 @@ export const renderFormatMethods = {
const icon = icons[type] || icons.info;
const contextMenuModel = options?.contextMenuModel || null;
const contextMenuTooltip = options?.contextMenuTooltip || 'Right-click to open download folder';
- const contextMenuData = contextMenuModel
- ? this.escapeHtml(encodeURIComponent(JSON.stringify(contextMenuModel)))
- : '';
- const contextMenuAttrs = contextMenuData
- ? ` data-model="${contextMenuData}" oncontextmenu="window.MLOpenContextMenu(event, this)" data-tooltip="${this.escapeHtml(contextMenuTooltip)}"`
- : '';
- const className = contextMenuData
+ const contextMenuAttrs = this.getContextMenuAttrs(contextMenuModel, contextMenuTooltip);
+ const className = contextMenuAttrs
? `mr-status mr-status-${type} mr-download-folder-context`
: `mr-status mr-status-${type}`;
diff --git a/web/resolver/views/missing_browser_methods.js b/web/resolver/views/missing_browser_methods.js
index c75f8b5..60bac02 100644
--- a/web/resolver/views/missing_browser_methods.js
+++ b/web/resolver/views/missing_browser_methods.js
@@ -881,8 +881,7 @@ export const missingBrowserMethods = {
};
};
const getLocalPathContextAttrs = (context, tooltip = 'Right-click for options') => {
- if (!context) return '';
- return ` data-model="${this.escapeHtml(encodeURIComponent(JSON.stringify(context)))}" oncontextmenu="window.MLOpenContextMenu(event, this)" data-tooltip="${this.escapeHtml(tooltip)}"`;
+ return this.getContextMenuAttrs(context, tooltip);
};
const makeModelEntry = (model, index, preferredCategory = getPreferredModelCategory()) => {
const category = getModelCategory(model);
@@ -1542,10 +1541,7 @@ export const missingBrowserMethods = {
};
if (contextMenuModel) {
- notificationProps["data-model"] = encodeURIComponent(JSON.stringify(contextMenuModel));
- notificationProps.oncontextmenu = (event) => {
- window.MLOpenContextMenu?.(event, event.currentTarget);
- };
+ Object.assign(notificationProps, this.getContextMenuProps?.(contextMenuModel) || {});
}
if (type === 'success') {
diff --git a/web/resolver/views/tabs_loaded_methods.js b/web/resolver/views/tabs_loaded_methods.js
index 0a25b2a..372ace0 100644
--- a/web/resolver/views/tabs_loaded_methods.js
+++ b/web/resolver/views/tabs_loaded_methods.js
@@ -262,8 +262,7 @@ export const tabsLoadedMethods = {
const fullName = model.name || model.original_path?.split(/[\/\\]/).pop() || 'Unknown';
const name = fullName.replace(/\.(safetensors|ckpt|pt|pth|bin|pkl|sft|onnx|gguf)$/i, '');
const strength = model.strength !== null && model.strength !== undefined ? model.strength.toFixed(2) : null;
- const modelData = encodeURIComponent(JSON.stringify(model));
- html += `${this.escapeHtml(name)}${strength !== null ? `${this.escapeHtml(strength)}` : ''}`;
+ html += `${this.escapeHtml(name)}${strength !== null ? `${this.escapeHtml(strength)}` : ''}`;
}
html += ` `;
}
@@ -282,8 +281,7 @@ export const tabsLoadedMethods = {
const fullName = model.name || model.original_path?.split(/[\/\\]/).pop() || 'Unknown';
const name = fullName.replace(/\.(safetensors|ckpt|pt|pth|bin|pkl|sft|onnx|gguf)$/i, '');
const strength = model.strength !== null && model.strength !== undefined ? model.strength.toFixed(2) : null;
- const modelData = encodeURIComponent(JSON.stringify(model));
- html += ` ${this.escapeHtml(name)}${strength !== null ? `${this.escapeHtml(strength)}` : ''}`;
+ html += ` ${this.escapeHtml(name)}${strength !== null ? `${this.escapeHtml(strength)}` : ''}`;
}
html += ` `;
}
|