diff --git a/static/js/modelPicker.js b/static/js/modelPicker.js index 07a1766af..3c57a80e4 100644 --- a/static/js/modelPicker.js +++ b/static/js/modelPicker.js @@ -35,6 +35,11 @@ function _pushRecent(mid) { next.unshift(mid); _saveList(RECENT_KEY, next.slice(0, RECENT_MAX)); } +function _removeRecent(mid) { + if (!mid) return; + const next = _loadRecent().filter(x => x !== mid); + _saveList(RECENT_KEY, next); +} function _loadFavorites() { return _loadList(FAVORITES_KEY); } function _toggleFavorite(mid) { const favs = _loadFavorites(); @@ -304,7 +309,7 @@ function _initModelPickerDropdown() { empty.textContent = text; listEl.appendChild(empty); } - function _addRow(m) { + function _addRow(m, onRemove) { const row = document.createElement('div'); row.className = 'model-switch-item'; if (m.stale) { @@ -373,6 +378,20 @@ function _initModelPickerDropdown() { }); row.appendChild(favDot); + // Remove-from-recent button (shown only for Recent section items). + if (onRemove) { + const rmBtn = document.createElement('button'); + rmBtn.type = 'button'; + rmBtn.className = 'mp-remove-dot'; + rmBtn.textContent = '×'; + rmBtn.title = 'Remove from recent'; + rmBtn.addEventListener('click', (e) => { + e.stopPropagation(); + onRemove(); + }); + row.appendChild(rmBtn); + } + row.addEventListener('click', () => _pick(m)); listEl.appendChild(row); } @@ -389,8 +408,7 @@ function _initModelPickerDropdown() { return; } - // ── Browse mode: Favorites (manual) + Recent (auto), with dedupe. ── - // Rules: + // ── Browse mode: sections in order: Favorites → Recent (big catalogs only) → All / Providers ── // 1. Never list the same model twice in the dropdown. Favorites // win over Recent (if you favorited it, that's where it // belongs — Recent shouldn't show it again as duplicate). @@ -415,7 +433,13 @@ function _initModelPickerDropdown() { .slice(0, RECENT_MAX); if (recentModels.length) { _addSection('Recent'); - recentModels.forEach(m => { shown.add(m.mid); _addRow(m); }); + recentModels.forEach(m => { + shown.add(m.mid); + _addRow(m, () => { + _removeRecent(m.mid); + _populate(''); + }); + }); } } diff --git a/static/style.css b/static/style.css index 103aecb6b..6703685a4 100644 --- a/static/style.css +++ b/static/style.css @@ -2940,6 +2940,34 @@ body.bg-pattern-sparkles { 45% { text-shadow: 0 0 10px color-mix(in srgb, var(--accent, var(--red)) 60%, transparent); } 100% { text-shadow: 0 0 0 color-mix(in srgb, var(--accent, var(--red)) 0%, transparent); } } + /* Inline remove-from-recent button — only shown on Recent rows. */ + .model-picker-list .mp-remove-dot { + flex: 0 0 auto; + display: inline-flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + margin: -4px -4px -4px 2px; + padding: 0; + border: none; + background: transparent; + cursor: pointer; + color: color-mix(in srgb, var(--fg) 28%, transparent); + font-family: inherit; + font-size: 15px; + line-height: 1; + transition: color 0.15s ease, opacity 0.15s ease, transform 0.12s ease; + -webkit-tap-highlight-color: transparent; + } + .model-picker-list .mp-remove-dot:hover { + color: var(--red, #ff5555); + transform: scale(1.1); + } + .model-picker-list .mp-remove-dot:focus-visible { + outline: none; + color: var(--red, #ff5555); + } /* First-run hint when a large catalog has no Recent/Favorites yet. */ .model-picker-list .mp-empty-hint { flex-direction: column;