diff --git a/__init__.py b/__init__.py
index 822ec27..92aa4ee 100644
--- a/__init__.py
+++ b/__init__.py
@@ -1127,7 +1127,7 @@ class ModelLinkerExtension:
search_tasks.append(
asyncio.to_thread(search_civitai_source_task)
)
- if search_lora_manager_archive_source and not is_urn:
+ if search_lora_manager_archive_source and filename:
search_tasks.append(
asyncio.to_thread(
search_lora_manager_archive_source_task
diff --git a/web/css/linker-main.css b/web/css/linker-main.css
index 66b4731..cd41eeb 100644
--- a/web/css/linker-main.css
+++ b/web/css/linker-main.css
@@ -656,38 +656,6 @@
border: 1px solid transparent;
box-shadow: inset 0 0 0 1px rgba(255,255,255,0.05);
}
-.ml-download-actions {
- display: grid;
- grid-template-columns: minmax(0, 1fr) auto;
- gap: var(--ml-spacing-8);
- align-items: center;
-}
-.ml-download-actions .ml-btn-download,
-.ml-download-actions .ml-btn-download:hover:not(:disabled),
-.ml-btn.ml-btn-download.ml-footer-btn,
-.ml-btn.ml-btn-download.ml-footer-btn:hover:not(:disabled) {
- background: rgba(255,255,255,0.03);
- color: var(--ml-text);
- border-color: rgba(255,255,255,0.1);
- box-shadow: inset 0 1px 0 rgba(255,255,255,0.02);
-}
-.ml-download-actions .ml-btn-download:hover:not(:disabled),
-.ml-btn.ml-btn-download.ml-footer-btn:hover:not(:disabled) {
- background: rgba(255,255,255,0.05);
- color: var(--ml-text);
- border-color: rgba(255,255,255,0.16);
- box-shadow: inset 0 1px 0 rgba(255,255,255,0.02);
- transform: translateY(-1px);
-}
-.ml-btn-download-main {
- justify-content: flex-start;
- padding-inline: 10px;
- font-size: 8.9px;
-}
-.ml-btn-download-side {
- min-width: 132px;
- font-size: 8.6px;
-}
.ml-status-inline {
display: flex;
align-items: center;
@@ -700,10 +668,6 @@
margin-top: 0;
line-height: 1.55;
}
-.ml-download-source {
- color: var(--ml-accent);
- font-weight: 700;
-}
.ml-download-size {
color: var(--ml-text-dim);
margin-left: var(--ml-spacing-8);
diff --git a/web/linker.js b/web/linker.js
index 347a513..b09037b 100644
--- a/web/linker.js
+++ b/web/linker.js
@@ -233,7 +233,7 @@ class LinkerManagerDialog extends ComfyDialog {
}
const sources = ['local', 'huggingface', 'civitai'];
- if (!missing.is_urn && this.isSourceAvailable('lora_manager_archive')) {
+ if (this.isSourceAvailable('lora_manager_archive')) {
sources.push('lora_manager_archive');
}
return sources;
@@ -402,58 +402,6 @@ class LinkerManagerDialog extends ComfyDialog {
this.syncSearchSourceUi(missing, container);
}
- renderDownloadSourceSection(missing, downloadSource) {
- const filename = missing.original_path?.split('/').pop()?.split('\\').pop() || '';
- const isFromWorkflow = downloadSource.url_source === 'workflow';
- const isCivitaiSource = downloadSource.source === 'civitai';
- const sourceLabels = {
- popular: 'Popular Models',
- model_list: 'Model Database',
- huggingface: 'HuggingFace',
- civitai: 'CivitAI',
- workflow: 'Workflow'
- };
- const sourceLabel = isFromWorkflow ? 'Workflow' : (sourceLabels[downloadSource.source] || 'Online');
- const downloadFilename = downloadSource.filename || filename;
- const formattedDownloadName = this.formatFilename(downloadFilename, 45);
- const confidenceDisplay = typeof downloadSource.confidence === 'number'
- ? `${Math.round(downloadSource.confidence)}% match`
- : '';
-
- let sizeDisplay = '';
- if (downloadSource.size) {
- sizeDisplay = typeof downloadSource.size === 'number'
- ? this.formatBytes(downloadSource.size)
- : downloadSource.size;
- }
-
- const modelCardUrl = downloadSource.model_url || this.getModelCardUrl(downloadSource.url);
-
- let html = `
`;
- html += `
`;
- html += `
`;
- if (isCivitaiSource && modelCardUrl) {
- html += `
Open on CivitAI`;
- }
- html += `
`;
- html += `
`;
- html += `
${isFromWorkflow ? 'URL from workflow' : sourceLabel}`;
- if (confidenceDisplay) {
- html += `
${confidenceDisplay}`;
- }
- if (modelCardUrl) {
- html += `
${formattedDownloadName.display}${sizeDisplay ? `
(${sizeDisplay})` : ''}`;
- } else {
- html += `
${formattedDownloadName.display}${sizeDisplay ? `
(${sizeDisplay})` : ''}`;
- }
- html += `
`;
- html += this.renderDownloadTargetControls(missing, downloadSource.directory || downloadSource.category || 'checkpoints');
- html += `
`;
- return html;
- }
-
formatSearchResultSize(result = {}) {
if (result.size === 0) return '0 B';
if (!result.size) return '';
@@ -525,6 +473,22 @@ class LinkerManagerDialog extends ComfyDialog {
};
}
+ renderKnownDownloadPanel(missing, downloadSource) {
+ let html = ``;
+ html += this.renderSearchControls(missing, { buttonText: 'Search Again' });
+ html += this.renderDownloadTargetControls(
+ missing,
+ downloadSource.directory || downloadSource.category || missing.category || 'checkpoints'
+ );
+ html += `
`;
+
+ const downloadSourceRow = this.getDownloadSourceTableRow(missing, downloadSource);
+ html += ``;
+ html += this.renderSearchResultsTable(downloadSourceRow ? [downloadSourceRow] : []);
+ html += `
`;
+ return html;
+ }
+
renderSearchResultsTable(rows = []) {
if (!rows.length) return '';
@@ -3864,39 +3828,7 @@ class LinkerManagerDialog extends ComfyDialog {
sortedMissingModels.forEach((missing, missingIndex) => {
this.wireLocalMatchButtons(container, missing, missingIndex);
- // Attach download button listener
- const downloadBtnId = `download-${missing.node_id}-${missing.widget_index}`;
- const downloadBtn = container.querySelector(`#${downloadBtnId}`);
- if (downloadBtn && missing.download_source) {
- downloadBtn.addEventListener('click', () => {
- this.downloadModel(missing);
- });
- }
-
- const searchResultsId = `search-results-${missing.node_id}-${missing.widget_index}`;
- const searchResultsDiv = container.querySelector(`#${searchResultsId}`);
- if (searchResultsDiv) {
- this.wireSearchDownloadButtons(searchResultsDiv, missing);
- }
-
- // Attach search button listener
- const searchBtnId = `search-${missing.node_id}-${missing.widget_index}`;
- const searchBtn = container.querySelector(`#${searchBtnId}`);
- if (searchBtn) {
- searchBtn.addEventListener('click', () => {
- this.searchOnline(missing);
- });
- }
-
- const sourceSelect = container.querySelector(`#search-source-select-${missing.node_id}-${missing.widget_index}`);
- if (sourceSelect) {
- sourceSelect.addEventListener('change', () => {
- this.setSearchSource(missing, sourceSelect.value, container);
- });
- this.syncSearchSourceUi(missing, container);
- }
-
- this.wireDownloadTargetAutocomplete(container, missing);
+ this.wireDownloadSearchPanel(container, missing);
// Wire locate chip (only for top-level nodes)
const locateId = `locate-${missing.node_id}-${missing.widget_index}`;
@@ -4142,17 +4074,7 @@ class LinkerManagerDialog extends ComfyDialog {
html += ``;
html += ``;
} else if (downloadSource && downloadSource.url) {
- html += ``;
- html += this.renderSearchControls(missing, { buttonText: 'Search Again' });
- html += this.renderDownloadTargetControls(
- missing,
- downloadSource.directory || downloadSource.category || missing.category || 'checkpoints'
- );
- html += `
`;
- const downloadSourceRow = this.getDownloadSourceTableRow(missing, downloadSource);
- html += ``;
- html += this.renderSearchResultsTable(downloadSourceRow ? [downloadSourceRow] : []);
- html += `
`;
+ html += this.renderKnownDownloadPanel(missing, downloadSource);
} else if (missing.is_urn) {
html += ``;
html += `
Resolving CivitAI download for this URN...
`;
@@ -4809,11 +4731,14 @@ class LinkerManagerDialog extends ComfyDialog {
const selectedSourceLabel = this.getSearchSourceLabel(selectedSource);
const sourceIds = this.getSearchSourcesForSelection(selectedSource, missing);
- // For URNs, use the CivitAI model name for searching instead of the URN itself
+ // For URNs, use the resolved file/model name for searching instead of the URN itself
// and pass the URN type as category (CivitAI expects specific type names)
if (missing.is_urn) {
- if (missing.civitai_info?.model_name) {
- filename = missing.civitai_info.model_name;
+ const urnSearchName = missing.civitai_info?.expected_filename
+ || missing.download_source?.filename
+ || missing.civitai_info?.model_name;
+ if (urnSearchName) {
+ filename = urnSearchName;
}
// Pass URN type directly - CivitAPI expects types like 'Upscaler', 'Checkpoint'
const urnType = missing.urn_type || '';
@@ -4906,8 +4831,10 @@ class LinkerManagerDialog extends ComfyDialog {
let hadError = false;
const searchPromises = sourceIds.map(async (source) => {
+ const sourceIsUrn = isUrn && source !== 'lora_manager_archive';
const searchData = {
...baseSearchData,
+ is_urn: sourceIsUrn,
sources: [source]
};
@@ -5061,7 +4988,7 @@ class LinkerManagerDialog extends ComfyDialog {
const downloadContainerId = loadingElementId.replace('urn-loading-', 'urn-download-');
const downloadEl = document.getElementById(downloadContainerId);
- if (downloadEl && data.civitai) {
+ if (downloadEl && data.civitai?.download_url) {
const missing = this.missingModels.find(m =>
`urn-download-${m.node_id}-${m.widget_index}` === downloadContainerId
);
@@ -5088,18 +5015,10 @@ class LinkerManagerDialog extends ComfyDialog {
base_model: data.civitai.base_model,
tags: data.civitai.tags || []
};
- downloadEl.outerHTML = this.renderDownloadSourceSection(missing, missing.download_source);
-
- const refreshedBtn = this.contentElement?.querySelector(`#download-${missing.node_id}-${missing.widget_index}`);
- if (refreshedBtn) {
- refreshedBtn.addEventListener('click', () => {
- this.downloadModel(missing);
- });
- }
- if (this.contentElement) {
- this.wireDownloadTargetAutocomplete(this.contentElement, missing);
- }
- this.refreshUrnLocalMatches(missing);
+ const downloadParent = downloadEl.parentElement;
+ downloadEl.outerHTML = this.renderKnownDownloadPanel(missing, missing.download_source);
+ this.wireDownloadSearchPanel(downloadParent || this.contentElement, missing);
+ this.refreshUrnLocalMatches(missing);
}
} else if (downloadEl) {
downloadEl.innerHTML = `
Unable to resolve direct download for this URN.
`;
@@ -5145,6 +5064,35 @@ class LinkerManagerDialog extends ComfyDialog {
});
}
+ wireDownloadSearchPanel(container, missing) {
+ if (!container) return;
+
+ const searchResultsId = `search-results-${missing.node_id}-${missing.widget_index}`;
+ const searchResultsDiv = container.querySelector(`#${searchResultsId}`);
+ if (searchResultsDiv) {
+ this.wireSearchDownloadButtons(searchResultsDiv, missing);
+ }
+
+ const searchBtn = container.querySelector(`#search-${missing.node_id}-${missing.widget_index}`);
+ if (searchBtn && searchBtn.dataset.mlSearchBound !== 'true') {
+ searchBtn.dataset.mlSearchBound = 'true';
+ searchBtn.addEventListener('click', () => {
+ this.searchOnline(missing);
+ });
+ }
+
+ const sourceSelect = container.querySelector(`#search-source-select-${missing.node_id}-${missing.widget_index}`);
+ if (sourceSelect && sourceSelect.dataset.mlSearchSourceBound !== 'true') {
+ sourceSelect.dataset.mlSearchSourceBound = 'true';
+ sourceSelect.addEventListener('change', () => {
+ this.setSearchSource(missing, sourceSelect.value, container);
+ });
+ this.syncSearchSourceUi(missing, container);
+ }
+
+ this.wireDownloadTargetAutocomplete(container, missing);
+ }
+
/**
* Display search results
*/