Show Bad type badge when local match file type is not accepted by node catalog
This commit is contained in:
@@ -1642,6 +1642,74 @@ test('local match bad folder badge treats unet gguf as diffusion models', () =>
|
||||
assert.equal(html, '');
|
||||
});
|
||||
|
||||
test('local match bad type badge warns when node catalog does not accept gguf', () => {
|
||||
const getModelFileTypeInfo = eval(`(${extractMethod(searchPanelMethodsSource, 'getModelFileTypeInfo')})`);
|
||||
const getMissingAcceptedModelFileTypes = eval(`(${extractMethod(searchPanelMethodsSource, 'getMissingAcceptedModelFileTypes')})`);
|
||||
const getLocalMatchBadTypeWarning = eval(`(${extractMethod(searchPanelMethodsSource, 'getLocalMatchBadTypeWarning')})`);
|
||||
const renderLocalMatchBadTypeBadge = eval(`(${extractMethod(searchPanelMethodsSource, 'renderLocalMatchBadTypeBadge')})`);
|
||||
const dialog = {
|
||||
getModelFileTypeInfo,
|
||||
getMissingAcceptedModelFileTypes,
|
||||
getLocalMatchBadTypeWarning,
|
||||
getCurrentComfyCatalogValues(nodeType, widgetName) {
|
||||
assert.equal(nodeType, 'CLIPLoader');
|
||||
assert.equal(widgetName, 'clip_name');
|
||||
return [
|
||||
'QWEN/qwen3vl_4b_fp8_scaled.safetensors',
|
||||
'FLUX/t5xxl_fp16.safetensors',
|
||||
];
|
||||
},
|
||||
escapeHtml(value) {
|
||||
return String(value).replace(/"/g, '"');
|
||||
},
|
||||
};
|
||||
|
||||
const html = renderLocalMatchBadTypeBadge.call(
|
||||
dialog,
|
||||
{
|
||||
node_type: 'CLIPLoader',
|
||||
node_title: 'Load CLIP',
|
||||
widget_name: 'clip_name',
|
||||
widget_index: 0,
|
||||
},
|
||||
{
|
||||
confidence: 76.7,
|
||||
model: { relative_path: 'QWEN/Qwen3-4B-abliterated-iq4_nl.gguf' },
|
||||
}
|
||||
);
|
||||
|
||||
assert.match(html, /mr-match-status-bad-type[^>]*>Bad type</);
|
||||
assert.match(html, /This file is GGUF \(.gguf\)/);
|
||||
assert.match(html, /Load CLIP only lists Safetensors \(.safetensors\) files/);
|
||||
assert.match(html, /use a loader made for GGUF models/);
|
||||
});
|
||||
|
||||
test('local match bad type badge is omitted for a file type accepted by node catalog', () => {
|
||||
const getModelFileTypeInfo = eval(`(${extractMethod(searchPanelMethodsSource, 'getModelFileTypeInfo')})`);
|
||||
const getMissingAcceptedModelFileTypes = eval(`(${extractMethod(searchPanelMethodsSource, 'getMissingAcceptedModelFileTypes')})`);
|
||||
const getLocalMatchBadTypeWarning = eval(`(${extractMethod(searchPanelMethodsSource, 'getLocalMatchBadTypeWarning')})`);
|
||||
const renderLocalMatchBadTypeBadge = eval(`(${extractMethod(searchPanelMethodsSource, 'renderLocalMatchBadTypeBadge')})`);
|
||||
const dialog = {
|
||||
getModelFileTypeInfo,
|
||||
getMissingAcceptedModelFileTypes,
|
||||
getLocalMatchBadTypeWarning,
|
||||
getCurrentComfyCatalogValues() {
|
||||
return ['QWEN/qwen3vl_4b_fp8_scaled.safetensors'];
|
||||
},
|
||||
escapeHtml(value) {
|
||||
return String(value);
|
||||
},
|
||||
};
|
||||
|
||||
const html = renderLocalMatchBadTypeBadge.call(
|
||||
dialog,
|
||||
{ node_type: 'CLIPLoader', widget_name: 'clip_name', widget_index: 0 },
|
||||
{ model: { relative_path: 'QWEN/another_text_encoder.safetensors' } }
|
||||
);
|
||||
|
||||
assert.equal(html, '');
|
||||
});
|
||||
|
||||
test('download category normalization maps unet gguf to diffusion models', () => {
|
||||
const normalizeDownloadCategory = eval(`(${extractMethod(downloadTargetMethodsSource, 'normalizeDownloadCategory')})`);
|
||||
|
||||
|
||||
@@ -1916,7 +1916,8 @@
|
||||
background: rgba(241, 207, 100, 0.10);
|
||||
}
|
||||
|
||||
.mr-match-status-bad-folder {
|
||||
.mr-match-status-bad-folder,
|
||||
.mr-match-status-bad-type {
|
||||
color: #fca5a5;
|
||||
background: rgba(248, 113, 113, 0.12);
|
||||
box-shadow: inset 0 0 0 1px rgba(248, 113, 113, 0.22);
|
||||
@@ -1930,7 +1931,8 @@
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.mr-match-status-bad-folder:focus-visible {
|
||||
.mr-match-status-bad-folder:focus-visible,
|
||||
.mr-match-status-bad-type:focus-visible {
|
||||
outline: 2px solid rgba(248, 113, 113, 0.58);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
@@ -2493,6 +2493,95 @@ export const searchPanelMethods = {
|
||||
return `<span class="mr-match-status mr-match-status-bad-folder" tabindex="0" data-tooltip="${this.escapeHtml(warning.tooltip)}">Bad folder</span>`;
|
||||
},
|
||||
|
||||
getModelFileTypeInfo(value = '') {
|
||||
const match = String(value || '').trim().match(/\.([a-z0-9]+)(?:$|[?#])/i);
|
||||
if (!match) return null;
|
||||
|
||||
const extension = match[1].toLowerCase();
|
||||
const labels = {
|
||||
safetensors: 'Safetensors',
|
||||
gguf: 'GGUF',
|
||||
ckpt: 'Checkpoint',
|
||||
pt: 'PyTorch',
|
||||
pt2: 'PyTorch',
|
||||
pth: 'PyTorch',
|
||||
bin: 'Binary model',
|
||||
onnx: 'ONNX',
|
||||
pkl: 'Pickle',
|
||||
sft: 'SFT'
|
||||
};
|
||||
if (!labels[extension]) return null;
|
||||
|
||||
return {
|
||||
extension,
|
||||
label: labels[extension],
|
||||
display: `${labels[extension]} (.${extension})`
|
||||
};
|
||||
},
|
||||
|
||||
getMissingAcceptedModelFileTypes(missing = {}) {
|
||||
const explicitExtensions = missing.accepted_extensions
|
||||
|| missing.allowed_extensions
|
||||
|| missing.accepted_model_extensions;
|
||||
let acceptedValues = Array.isArray(explicitExtensions) ? explicitExtensions : [];
|
||||
|
||||
if (!acceptedValues.length) {
|
||||
const widgetName = missing.widget_name
|
||||
|| this.getComfyWidgetNameByIndex?.(missing.node_type || '', Number(missing.widget_index))
|
||||
|| '';
|
||||
acceptedValues = this.getCurrentComfyCatalogValues?.(
|
||||
missing.node_type || '',
|
||||
widgetName
|
||||
) || [];
|
||||
}
|
||||
|
||||
const byExtension = new Map();
|
||||
for (const value of acceptedValues) {
|
||||
const typeInfo = this.getModelFileTypeInfo?.(value);
|
||||
if (typeInfo && !byExtension.has(typeInfo.extension)) {
|
||||
byExtension.set(typeInfo.extension, typeInfo);
|
||||
}
|
||||
}
|
||||
return Array.from(byExtension.values());
|
||||
},
|
||||
|
||||
getLocalMatchBadTypeWarning(missing = {}, match = {}) {
|
||||
const model = match.model && typeof match.model === 'object' ? match.model : {};
|
||||
const matchValue = model.relative_path
|
||||
|| model.path
|
||||
|| match.relative_path
|
||||
|| match.path
|
||||
|| model.filename
|
||||
|| match.filename
|
||||
|| model.name
|
||||
|| match.name
|
||||
|| '';
|
||||
const matchType = this.getModelFileTypeInfo?.(matchValue);
|
||||
if (!matchType) return null;
|
||||
|
||||
const acceptedTypes = this.getMissingAcceptedModelFileTypes?.(missing) || [];
|
||||
if (!acceptedTypes.length || acceptedTypes.some(type => type.extension === matchType.extension)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const acceptedText = acceptedTypes.length === 1
|
||||
? acceptedTypes[0].display
|
||||
: acceptedTypes.map(type => type.display).join(', ');
|
||||
const nodeLabel = String(missing.node_title || missing.node_type || 'this node').trim();
|
||||
|
||||
return {
|
||||
matchType,
|
||||
acceptedTypes,
|
||||
tooltip: `This file is ${matchType.display}, but ${nodeLabel} only lists ${acceptedText} files.\n\nLinking it may not work. Choose a supported file type or use a loader made for ${matchType.label} models.`
|
||||
};
|
||||
},
|
||||
|
||||
renderLocalMatchBadTypeBadge(missing = {}, match = {}) {
|
||||
const warning = this.getLocalMatchBadTypeWarning?.(missing, match);
|
||||
if (!warning) return '';
|
||||
return `<span class="mr-match-status mr-match-status-bad-type" tabindex="0" data-tooltip="${this.escapeHtml(warning.tooltip)}">Bad type</span>`;
|
||||
},
|
||||
|
||||
getActiveDownloadInfoForLocalMatch(match = {}) {
|
||||
const model = match.model || {};
|
||||
const normalizePath = (value = '') => normalizePathIdentity(value);
|
||||
@@ -2580,7 +2669,8 @@ export const searchPanelMethods = {
|
||||
},
|
||||
|
||||
renderLocalMatchStatusGroup(missing = {}, match = {}, hashLabelMap = null) {
|
||||
return `<span class="mr-match-status-group">${this.renderLocalMatchStatus(match, hashLabelMap)}${this.renderLocalMatchDownloadingBadge(match)}${this.renderLocalMatchBadFolderBadge(missing, match)}</span>`;
|
||||
const badTypeBadge = this.renderLocalMatchBadTypeBadge?.(missing, match) || '';
|
||||
return `<span class="mr-match-status-group">${this.renderLocalMatchStatus(match, hashLabelMap)}${this.renderLocalMatchDownloadingBadge(match)}${this.renderLocalMatchBadFolderBadge(missing, match)}${badTypeBadge}</span>`;
|
||||
},
|
||||
|
||||
areLocalMatchAlternativesCollapsed() {
|
||||
|
||||
Reference in New Issue
Block a user