diff --git a/static/js/modelPicker.js b/static/js/modelPicker.js
index 41dcfca..6bf4c83 100644
--- a/static/js/modelPicker.js
+++ b/static/js/modelPicker.js
@@ -11,7 +11,7 @@ const API_BASE = window.location.origin;
// ── Recent + Favorites persistence ──
// Recent is auto-tracked (last 5 picks, most-recent-first) and lives in its
// own key. Favorites is the SAME key the sidebar Models section uses, so a
-// star toggled here shows up there and vice-versa.
+// favorite toggled here shows up there and vice-versa.
const RECENT_KEY = 'odysseus-model-recent';
const FAVORITES_KEY = 'odysseus-model-favorites';
const RECENT_MAX = 5;
@@ -51,11 +51,6 @@ function _toggleFavorite(mid) {
return i < 0; // true when now favorited
}
-// Filled star (favorited) + outline star (not) — CSS toggles which shows.
-const _STAR_SVG =
- ''
- + '';
-
// ── Shared keyboard nav for model pickers ──
function _handlePickerKeydown(e, listEl, itemSelector, closeFn) {
if (e.key === 'Escape') { closeFn(); return; }
@@ -200,6 +195,12 @@ function _initModelPickerDropdown() {
url: item.url,
endpointId: item.endpoint_id,
epName: item.endpoint_name || '',
+ providerText: [
+ item.endpoint_name || '',
+ item.category || '',
+ item.host || '',
+ item.url || '',
+ ].filter(Boolean).join(' '),
stale: isLocalDead,
staleReason: isLocalDead ? (probeResult.error || 'not responding') : '',
});
@@ -277,22 +278,22 @@ function _initModelPickerDropdown() {
epSpan.textContent = _epDisplay;
row.appendChild(epSpan);
- // Inline favorite star — toggles favorite, never picks the model.
- const star = document.createElement('button');
- star.type = 'button';
- star.className = 'mp-fav-star' + (favs.includes(m.mid) ? ' active' : '');
- const _setStarState = (on) => {
- star.classList.toggle('active', on);
- star.title = on ? 'Remove from favorites' : 'Add to favorites';
- star.setAttribute('aria-label', on ? 'Remove from favorites' : 'Add to favorites');
- star.setAttribute('aria-pressed', on ? 'true' : 'false');
+ // Inline favorite dot — toggles favorite, never picks the model.
+ const favDot = document.createElement('button');
+ favDot.type = 'button';
+ favDot.className = 'mp-fav-dot' + (favs.includes(m.mid) ? ' active' : '');
+ favDot.textContent = '●';
+ const _setFavState = (on) => {
+ favDot.classList.toggle('active', on);
+ favDot.title = on ? 'Remove from favorites' : 'Add to favorites';
+ favDot.setAttribute('aria-label', on ? 'Remove from favorites' : 'Add to favorites');
+ favDot.setAttribute('aria-pressed', on ? 'true' : 'false');
};
- star.innerHTML = _STAR_SVG;
- _setStarState(favs.includes(m.mid));
- star.addEventListener('click', (e) => {
+ _setFavState(favs.includes(m.mid));
+ favDot.addEventListener('click', (e) => {
e.stopPropagation();
const nowFav = _toggleFavorite(m.mid);
- _setStarState(nowFav);
+ _setFavState(nowFav);
// Keep our in-memory copy aligned so a follow-up re-render is correct.
const idx = favs.indexOf(m.mid);
if (nowFav && idx < 0) favs.push(m.mid);
@@ -300,14 +301,14 @@ function _initModelPickerDropdown() {
if (uiModule && uiModule.showToast) uiModule.showToast(nowFav ? 'Favorited' : 'Unfavorited');
// In browse mode the Favorites section membership changed — rebuild
// (cheap: Recent + Favorites). In search mode the row stays put, so
- // the in-place star update above is enough.
+ // the in-place favorite update above is enough.
if (!q) {
const st = listEl.scrollTop;
_populate('');
listEl.scrollTop = st;
}
});
- row.appendChild(star);
+ row.appendChild(favDot);
row.addEventListener('click', () => _pick(m));
listEl.appendChild(row);
@@ -316,7 +317,12 @@ function _initModelPickerDropdown() {
// ── Search mode: flat, filtered results across the whole catalog ──
if (q) {
const matches = all.filter(m =>
- m.mid.toLowerCase().includes(q) || m.display.toLowerCase().includes(q));
+ [
+ m.mid,
+ m.display,
+ m.epName,
+ m.providerText,
+ ].filter(Boolean).join(' ').toLowerCase().includes(q));
if (matches.length === 0) _addEmpty('No matching models');
else matches.forEach(_addRow);
return;
@@ -352,7 +358,7 @@ function _initModelPickerDropdown() {
hint.className = 'model-switch-empty mp-empty-hint';
hint.innerHTML =
'Search ' + all.length + ' models'
- + 'Picks land in Recent · tap ☆ to favorite';
+ + 'Picks land in Recent · tap the dot to favorite';
listEl.appendChild(hint);
}
}
@@ -441,6 +447,7 @@ function _initModelPickerDropdown() {
url: item.url || detail.url || '',
endpointId: item.endpoint_id || detail.endpointId || '',
epName: item.endpoint_name || detail.endpointName || '',
+ providerText: [item.endpoint_name || detail.endpointName || '', item.url || detail.url || ''].filter(Boolean).join(' '),
};
break;
}
@@ -452,6 +459,7 @@ function _initModelPickerDropdown() {
url: detail.url,
endpointId: detail.endpointId || '',
epName: detail.endpointName || '',
+ providerText: [detail.endpointName || '', detail.url || ''].filter(Boolean).join(' '),
};
}
if (match) await _pick(match);
diff --git a/static/style.css b/static/style.css
index 8cff262..472ef25 100644
--- a/static/style.css
+++ b/static/style.css
@@ -2718,7 +2718,7 @@ body.bg-pattern-sparkles {
.model-picker-list .mp-section-label:first-child {
padding-top: 2px;
}
- /* Model name takes the slack so the endpoint label + star sit on the right. */
+ /* Model name takes the slack so the endpoint label + favorite dot sit on the right. */
.model-picker-list .model-switch-item .mp-model-name {
flex: 1 1 auto;
min-width: 0;
@@ -2739,41 +2739,42 @@ body.bg-pattern-sparkles {
.model-picker-list .model-switch-item.kb-active {
background: color-mix(in srgb, var(--red) 14%, transparent);
}
- /* Inline favorite star — always visible (works on touch), filled when on. */
- .model-picker-list .mp-fav-star {
+ /* Inline favorite dot — always visible (works on touch), active when on. */
+ .model-picker-list .mp-fav-dot {
flex: 0 0 auto;
display: inline-flex;
align-items: center;
justify-content: center;
- width: 24px;
+ width: 30px;
height: 24px;
- margin: -5px -4px -5px 2px;
+ margin: -5px 0 -5px 0;
padding: 0;
border: none;
background: none;
cursor: pointer;
- color: color-mix(in srgb, var(--fg) 26%, transparent);
- transition: color 0.15s ease, transform 0.12s ease;
+ color: color-mix(in srgb, var(--fg) 22%, transparent);
+ font-family: inherit;
+ font-size: 13px;
+ 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-fav-star:hover {
- color: var(--fg);
- transform: scale(1.18);
+ .model-picker-list .mp-fav-dot:hover {
+ color: color-mix(in srgb, var(--fg) 68%, transparent);
+ transform: scale(1.15);
}
- .model-picker-list .mp-fav-star:focus-visible {
+ .model-picker-list .mp-fav-dot:focus-visible {
outline: none;
- color: var(--fg);
+ color: color-mix(in srgb, var(--fg) 68%, transparent);
}
- .model-picker-list .mp-fav-star.active {
- color: var(--red);
+ .model-picker-list .mp-fav-dot.active {
+ color: var(--accent, var(--red));
+ opacity: 1;
}
- .model-picker-list .mp-fav-star.active:hover {
- color: var(--red);
- opacity: 0.7;
+ .model-picker-list .mp-fav-dot.active:hover {
+ color: var(--accent, var(--red));
+ opacity: 0.72;
}
- .model-picker-list .mp-fav-star .mp-star-filled { display: none; }
- .model-picker-list .mp-fav-star.active .mp-star-filled { display: inline-flex; }
- .model-picker-list .mp-fav-star.active .mp-star-outline { display: none; }
/* First-run hint when a large catalog has no Recent/Favorites yet. */
.model-picker-list .mp-empty-hint {
flex-direction: column;
@@ -2795,10 +2796,10 @@ body.bg-pattern-sparkles {
padding-top: 8px;
padding-bottom: 8px;
}
- .model-picker-list .mp-fav-star {
+ .model-picker-list .mp-fav-dot {
width: 30px;
height: 30px;
- margin: -7px -4px -7px 2px;
+ margin: -7px 0 -7px 0;
}
}
/* Overflow "+" menu */