ui : collect hidden models into a collapsed block

Assisted-by: pi:llama.cpp/DeepSeek-V4.1-Flash
This commit is contained in:
Aleksander Grygier
2026-09-28 12:10:12 +02:00
parent d5d033e5d6
commit a1e5b15af5
4 changed files with 26 additions and 10 deletions
@@ -51,11 +51,14 @@
// only llama-compat servers report a load state
const isLlamaCompat = (option: ModelOption) =>
getBackendCapabilities(getBackend(option.backendId)).loadUnload;
const loaded = visible.filter(
// hidden models drop out of the sections and get their own block at the end
const shown = visible.filter((option) => !modelsStore.isHidden(option.id));
const hidden = visible.filter((option) => modelsStore.isHidden(option.id));
const loaded = shown.filter(
(option) => isLlamaCompat(option) && modelsStore.isModelLoaded(option.model)
);
const claimed = new SvelteSet(loaded.map((option) => option.id));
const favorites = visible.filter(
const favorites = shown.filter(
(option) => !claimed.has(option.id) && modelsStore.favoriteModelIds.has(option.model)
);
@@ -63,7 +66,7 @@
const byBackend = new SvelteMap<string, ModelOption[]>();
for (const option of visible) {
for (const option of shown) {
if (claimed.has(option.id)) continue;
const backendId = option.backendId ?? LOCAL_BACKEND_ID;
@@ -127,6 +130,17 @@
}
}
if (hidden.length) {
ordered.push({
backendId: null,
isLocal: false,
items: hidden,
key: 'hidden',
kind: 'hidden',
label: 'Hidden models'
});
}
return ordered;
});
let matches = $derived(groups.flatMap((group) => group.items));
@@ -89,7 +89,7 @@
: []),
{
icon: isHidden ? Eye : EyeOff,
label: isHidden ? 'Show in selector' : 'Hide from selector',
label: isHidden ? 'Unhide model' : 'Hide model',
onclick: () => modelsStore.toggleHidden(option.id),
separator: true
}
@@ -133,10 +133,6 @@
<span class="flex min-w-0 items-center gap-3">
<ModelAvatar {option} showBaseModelAvatar size="size-9" />
{#if isHidden}
<EyeOff class="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
{/if}
<ModelId
aliases={option.aliases}
class="min-w-0 flex-1"
@@ -214,6 +210,8 @@
<Heart class="h-3.5 w-3.5 shrink-0" />
{:else if group.kind === 'loaded'}
<Power class="h-3.5 w-3.5 shrink-0" />
{:else if group.kind === 'hidden'}
<EyeOff class="h-3.5 w-3.5 shrink-0" />
{:else if group.kind === 'local'}
<Logo class="shrink-0" style="--size: 0.875rem" />
{/if}
@@ -224,6 +222,7 @@
count={group.items.length}
icon={group.kind === 'provider' ? undefined : groupIcon}
label={group.label}
open={group.kind !== 'hidden'}
sticky
>
{#each group.items as option (option.id)}
@@ -45,7 +45,7 @@ export interface ModelsTableGroup {
items: ModelOption[];
key: string;
/** Picks the header icon; providers use their backend logo instead. */
kind: 'favorites' | 'loaded' | 'local' | 'provider';
kind: 'favorites' | 'hidden' | 'loaded' | 'local' | 'provider';
label: string;
}
@@ -19,6 +19,8 @@
loading?: boolean;
/** Renders the back control, for a drilled-in provider. */
onBack?: () => void;
/** Start expanded; the manager collapses its hidden block. */
open?: boolean;
revealChevronOnHover?: boolean;
sectionHeaderClass?: string;
/** Sticks the header to the top of the scrollport. */
@@ -34,6 +36,7 @@
label,
loading = false,
onBack,
open = true,
revealChevronOnHover = false,
sectionHeaderClass = 'm-0 px-2 py-2 text-[13px] font-semibold text-muted-foreground/70 select-none',
sticky = false
@@ -46,7 +49,7 @@
let triggerStyle = $derived(sticky ? 'top: var(--dropdown-sticky-height, 0px)' : '');
</script>
<CollapsibleSection {revealChevronOnHover} {triggerClass} {triggerStyle}>
<CollapsibleSection {open} {revealChevronOnHover} {triggerClass} {triggerStyle}>
{#snippet trigger()}
{#if onBack}
<button