diff --git a/web/css/linker-main.css b/web/css/linker-main.css index ba806ac..9a2ba65 100644 --- a/web/css/linker-main.css +++ b/web/css/linker-main.css @@ -648,8 +648,9 @@ /* Download Section */ .ml-download-section { + --ml-download-control-height: 36px; display: grid; - gap: var(--ml-spacing-8); + gap: 10px; padding: var(--ml-spacing-10); background: var(--ml-surface-elevated); border-radius: 0; @@ -673,39 +674,64 @@ margin-left: var(--ml-spacing-8); } .ml-search-source-bar { - display: flex; + display: grid; + grid-template-columns: auto minmax(178px, 1fr); align-items: center; - justify-content: space-between; - gap: 8px; - margin-bottom: 8px; + gap: 10px; + margin-bottom: 0; +} +.ml-download-section .ml-search-source-bar > .ml-btn { + height: var(--ml-download-control-height); + min-height: var(--ml-download-control-height); + padding: 0 12px; + border-radius: 9px; + font-size: 12px; + line-height: 1; } .ml-search-source-picker { - display: flex; + display: grid; + grid-template-columns: auto minmax(0, 1fr); align-items: center; - gap: 8px; + gap: 10px; min-width: 0; margin-left: auto; + width: 100%; } .ml-search-source-picker-label { color: var(--ml-text-muted); - font-size: 11px; + font-size: 10px; + font-weight: 700; + letter-spacing: 0.04em; + text-transform: uppercase; white-space: nowrap; } .ml-search-source-select { - min-height: 30px; - padding: 5px 10px; + width: 100%; + height: var(--ml-download-control-height); + min-height: var(--ml-download-control-height); + padding: 0 11px; background: rgba(255,255,255,0.04); color: var(--ml-text); border: 1px solid var(--ml-border); - border-radius: 10px; - font-size: 11px; + border-radius: 9px; + font-size: 12px; + line-height: 1; outline: none; appearance: none; + box-sizing: border-box; } .ml-search-source-select:focus { border-color: rgba(114,211,138,0.4); box-shadow: 0 0 0 3px rgba(114,211,138,0.12); } +@media (max-width: 720px) { + .ml-search-source-bar { + grid-template-columns: 1fr; + } + .ml-download-section .ml-search-source-bar > .ml-btn { + justify-self: start; + } +} .ml-search-results { margin-top: 8px; display: none; @@ -2260,7 +2286,7 @@ } } .ml-download-target { - margin-top: 10px; + margin-top: 0; padding-top: 10px; border-top: 1px solid rgba(255,255,255,0.06); display: grid; @@ -2270,7 +2296,7 @@ display: grid; grid-template-columns: minmax(120px, 33%) minmax(0, 1fr); grid-template-rows: auto auto; - gap: 10px; + gap: 8px 10px; align-items: start; } .ml-download-target-row { @@ -2297,15 +2323,16 @@ .ml-download-target-select { display: block; width: 100%; - height: 40px; - margin: 3px; - padding: 8px 10px; + height: var(--ml-download-control-height, 36px); + min-height: var(--ml-download-control-height, 36px); + margin: 0; + padding: 0 11px; background: rgba(255,255,255,0.04); color: var(--ml-text); border: 1px solid var(--ml-border); - border-radius: 10px; + border-radius: 9px; font-size: 12px; - line-height: 1.2; + line-height: 1; outline: none; box-sizing: border-box; } @@ -2313,6 +2340,10 @@ appearance: none; -webkit-appearance: none; -moz-appearance: none; + cursor: pointer; +} +.ml-search-source-select { + cursor: pointer; } .ml-download-target-input:focus, .ml-download-target-select:focus { @@ -2323,6 +2354,8 @@ position: relative; display: flex; align-items: stretch; + min-width: 0; + width: 100%; } @media (max-width: 720px) { .ml-download-target-label { @@ -2357,9 +2390,3 @@ .ml-download-target-option.is-highlighted { background: rgba(114,211,138,0.12); } -.ml-search-source-select option, -.ml-download-target-select option { - background: var(--ml-panel-bg-strong); - color: var(--ml-text); -} - diff --git a/web/linker.js b/web/linker.js index b438bae..136bffa 100644 --- a/web/linker.js +++ b/web/linker.js @@ -389,7 +389,7 @@ class LinkerManagerDialog extends ComfyDialog { const state = this.getSearchState(missing); const selectEl = container.querySelector(`#search-source-select-${missing.node_id}-${missing.widget_index}`); if (selectEl) { - selectEl.value = state.selectedSource; + this.setDropdownValue(selectEl, state.selectedSource, this.getSearchSourceLabel(state.selectedSource)); } } @@ -402,6 +402,16 @@ class LinkerManagerDialog extends ComfyDialog { this.syncSearchSourceUi(missing, container); } + getDropdownValue(el) { + return el?.dataset?.value || el?.value || ''; + } + + setDropdownValue(el, value, label = value) { + if (!el) return; + el.dataset.value = value || ''; + el.value = label || value || ''; + } + normalizeVersionName(versionName) { return String(versionName || '').trim().replace(/^v{2,}(?=\d)/i, 'v'); } @@ -693,7 +703,10 @@ class LinkerManagerDialog extends ComfyDialog { renderSearchControls(missing, options = {}) { const searchSourcesId = `search-sources-${missing.node_id}-${missing.widget_index}`; const searchSourceSelectId = `search-source-select-${missing.node_id}-${missing.widget_index}`; + const searchSourceListId = `search-source-list-${missing.node_id}-${missing.widget_index}`; const buttonText = options.buttonText || 'Search'; + const state = this.getSearchState(missing); + const selectedSource = state.selectedSource || 'all'; let html = `
`; return html; @@ -2206,6 +2214,17 @@ class LinkerManagerDialog extends ComfyDialog { return ordered.length > 0 ? ordered : [preferred]; } + getSearchSourceOptions() { + const sources = ['all', 'local', 'huggingface', 'civitai']; + if (this.isSourceAvailable('lora_manager_archive')) { + sources.push('lora_manager_archive'); + } + return sources.map(source => ({ + value: source, + label: this.getSearchSourceLabel(source) + })); + } + getAvailableSubfolders(category = '') { return this.downloadSubfolders.get((category || '').toLowerCase()) || []; } @@ -2278,9 +2297,10 @@ class LinkerManagerDialog extends ComfyDialog { async applySuggestedCivitaiSubfolder(missing, categoryEl, subfolderEl) { if (!categoryEl || !subfolderEl || subfolderEl.value.trim()) return; - await this.ensureDownloadSubfoldersLoaded(categoryEl.value); - const folders = this.getAvailableSubfolders(categoryEl.value); - const suggestion = this.getSuggestedCivitaiSubfolder(missing, categoryEl.value, folders); + const category = this.getDropdownValue(categoryEl); + await this.ensureDownloadSubfoldersLoaded(category); + const folders = this.getAvailableSubfolders(category); + const suggestion = this.getSuggestedCivitaiSubfolder(missing, category, folders); if (suggestion) { subfolderEl.value = suggestion; } @@ -2321,16 +2341,18 @@ class LinkerManagerDialog extends ComfyDialog { renderDownloadTargetControls(missing, defaultCategory = 'checkpoints') { const selectId = `download-category-${missing.node_id}-${missing.widget_index}`; const subfolderId = `download-subfolder-${missing.node_id}-${missing.widget_index}`; + const categoryListId = `download-category-list-${missing.node_id}-${missing.widget_index}`; const subfolderListId = `download-subfolder-list-${missing.node_id}-${missing.widget_index}`; - const options = this.getDownloadCategoryOptions(defaultCategory) - .map(category => ``) - .join(''); + const selectedCategory = defaultCategory || 'checkpoints'; let html = `