From ee5685f8cd51edae0258db146d9d15ce1a44e976 Mon Sep 17 00:00:00 2001 From: Aleksander Grygier Date: Fri, 2 Oct 2026 11:19:10 +0200 Subject: [PATCH] ui : refine the model pane and the manager filters The pane no longer says the server withholds data: it says the metadata and the chat template appear once the model is loaded, and the template block grows with its content instead of scrolling inside a fixed box. The pane ends with a Delete this model from disk action, confirmed by the same dialog the table row uses and hovering like a destructive dropdown entry. The filter toggles list only the modalities the models at hand actually carry, and starting a new chat from the pane closes the dialog. Assisted-by: pi --- .../models/ModelsManager/ModelsManager.svelte | 3 +- .../ModelsManager/ModelsManagerFilters.svelte | 37 +++++++++++++++---- .../ModelsManagerModelConfiguration.svelte | 27 +++++++++++++- ...anagerModelConfigurationInformation.svelte | 8 ++-- 4 files changed, 62 insertions(+), 13 deletions(-) diff --git a/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManager.svelte b/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManager.svelte index 54624352d8..f1220f245b 100644 --- a/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManager.svelte +++ b/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManager.svelte @@ -225,7 +225,8 @@ async function useInNewChat(option: ModelOption): Promise { await modelsStore.selectModelById(option.id); await conversationsStore.openNewChat(); - // the chat is behind the dialog, so it takes focus once the dialog is out of the way + // the chat is behind the dialog: close it, then let the composer take focus + uiStore.manageModelsOpen = false; uiStore.requestComposerFocus(); } diff --git a/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManagerFilters.svelte b/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManagerFilters.svelte index be3cbe6f4a..534e7cf672 100644 --- a/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManagerFilters.svelte +++ b/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManagerFilters.svelte @@ -9,11 +9,14 @@ FILTER_TRIGGER_CLASS, MODALITY_FLAG_KEYS, MODALITY_ICONS, + MODALITY_KEYS, MODALITY_LABELS, MODALITY_ORDER, type ModalityKey } from '$lib/constants'; import { ModelCapability } from '$lib/enums'; + import { modelsStore } from '$lib/stores'; + import { SvelteSet } from 'svelte/reactivity'; interface Props { /** Capabilities a model must have every one of. */ @@ -45,16 +48,34 @@ value })); - const MODALITY_TOGGLES = MODALITY_ORDER.map((modality) => ({ - icon: MODALITY_ICONS[modality], - label: MODALITY_LABELS[modality], - value: MODALITY_FLAG_KEYS[modality] - })); + // only the modalities the models at hand actually carry: a toggle for something + // none of them supports could only ever empty the table + let detectedModalities = $derived.by(() => { + const keys = new SvelteSet(); + + for (const option of modelsStore.models) { + for (const key of MODALITY_KEYS) { + if (option.modalities?.[key]) keys.add(key); + } + } + + return keys; + }); + let modalityToggles = $derived( + MODALITY_ORDER.filter((modality) => detectedModalities.has(MODALITY_FLAG_KEYS[modality])).map( + (modality) => ({ + icon: MODALITY_ICONS[modality], + label: MODALITY_LABELS[modality], + value: MODALITY_FLAG_KEYS[modality] + }) + ) + ); // one group holds what a model can do and what it can accept - const TOGGLES = [...CAPABILITY_TOGGLES, ...MODALITY_TOGGLES]; + let toggles = $derived([...CAPABILITY_TOGGLES, ...modalityToggles]); const CAPABILITY_VALUES = new Set(CAPABILITY_TOGGLES.map((entry) => entry.value)); - const MODALITY_VALUES = new Set(MODALITY_TOGGLES.map((entry) => entry.value)); + // every flag value, so a choice made before a model disappeared still round-trips + const MODALITY_VALUES = new Set(MODALITY_KEYS); // the group holds one flat list, so a change splits back into the two filters function setToggles(values: string[]): void { @@ -93,7 +114,7 @@ value={[...capabilities, ...modalities]} variant="outline" > - {#each TOGGLES as toggle (toggle.value)} + {#each toggles as toggle (toggle.value)} import ModelsManagerModelConfigurationHeader from './ModelsManagerModelConfigurationHeader.svelte'; import ModelsManagerModelConfigurationInformation from './ModelsManagerModelConfigurationInformation.svelte'; + import { Trash2 } from '@lucide/svelte'; + import { DialogConfirmDownload } from '$lib/components/app/dialogs'; + import { Button } from '$lib/components/ui/button'; import { MODEL_ID, SETTINGS_KEYS } from '$lib/constants'; - import { ServerModelStatus } from '$lib/enums'; + import { ModelDownloadConfirmAction, ServerModelStatus } from '$lib/enums'; import { HuggingFaceService } from '$lib/services'; import { modelsStore, settingsStore } from '$lib/stores'; import type { HfModelDetailInfo } from '$lib/types/huggingface'; @@ -17,6 +20,9 @@ let { onClose, onToggleLoad, onUseInNewChat, option }: Props = $props(); + // the same removal the table row offers, confirmed by the same dialog + let deleteOpen = $state(false); + // the props cache is a plain Map, so the read has to name its version to stay reactive let serverProps = $derived.by(() => { void modelsStore.props.cacheVersion; @@ -69,5 +75,24 @@
+ +
+ +
+ + (deleteOpen = false)} + open={deleteOpen} + repoWithTag={option.model} + /> diff --git a/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManagerModelConfiguration/ModelsManagerModelConfigurationInformation.svelte b/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManagerModelConfiguration/ModelsManagerModelConfigurationInformation.svelte index ac341f8059..6f70d9c947 100644 --- a/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManagerModelConfiguration/ModelsManagerModelConfigurationInformation.svelte +++ b/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManagerModelConfiguration/ModelsManagerModelConfigurationInformation.svelte @@ -162,7 +162,9 @@ {/each} {#if visibleRows.length === 0} -

This model reports no metadata.

+

+ The model reports its metadata once it is loaded. +

{/if} {#if modalities.length > 0} @@ -188,8 +190,8 @@ {/snippet}
{serverProps?.chat_template ??
+			class="mt-1 rounded-md bg-muted/50 p-2 text-xs whitespace-pre-wrap">{serverProps?.chat_template ??
 				gguf?.chat_template ??
-				'Not reported by the server.'}
+ 'Shown once the model is loaded.'}