mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2026-09-30 09:57:38 -05:00
ui : mark favorites with a colored heart in the model list
Assisted-by: pi:llama.cpp/DeepSeek-V4.1-Flash
This commit is contained in:
@@ -323,7 +323,11 @@
|
||||
<p class="px-4 py-3 text-sm text-muted-foreground">{ms.emptyMessage}</p>
|
||||
{/if}
|
||||
|
||||
{#snippet modelOption(item: ModelItem, hideOrgName: boolean)}
|
||||
{#snippet modelOption(
|
||||
item: ModelItem,
|
||||
hideOrgName: boolean,
|
||||
showFavIndicator: boolean
|
||||
)}
|
||||
{@const { option } = item}
|
||||
{@const isSelected = currentModel === option.model || ms.activeId === option.id}
|
||||
{@const isHighlighted = option.id === highlightedId}
|
||||
@@ -345,6 +349,7 @@
|
||||
onSelect={ms.handleSelect}
|
||||
{option}
|
||||
showBaseModelAvatar
|
||||
{showFavIndicator}
|
||||
/>
|
||||
{/snippet}
|
||||
|
||||
@@ -358,7 +363,7 @@
|
||||
onProviderOpen={ms.openProvider}
|
||||
onSelect={ms.handleSelect}
|
||||
renderOption={modelOption}
|
||||
sectionHeaderClass="[&:not(:first-child)]:mt-3 mb-1 px-2 py-2 text-[13px] font-semibold text-foreground select-none"
|
||||
sectionHeaderClass="[&:not(:first-child)]:mt-3 mb-1 px-2 py-2 text-[13px] font-semibold text-foreground/80 select-none"
|
||||
{showOrgName}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
sectionHeaderClass?: string;
|
||||
onSelect: (modelId: string) => void;
|
||||
onInfoClick: (modelName: string) => void;
|
||||
renderOption?: import('svelte').Snippet<[ModelItem, boolean]>;
|
||||
/** Favorite models of every backend; shown on the favorites tab. */
|
||||
renderOption?: import('svelte').Snippet<[ModelItem, boolean, boolean]>;
|
||||
/** Favorite models of every backend, listed in their own section. */
|
||||
favorites?: ModelItem[];
|
||||
/** Show the organization name in every model id of the list. */
|
||||
showOrgName?: boolean;
|
||||
@@ -62,7 +62,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
{#snippet defaultOption(item: ModelItem, hideOrgName: boolean)}
|
||||
{#snippet defaultOption(item: ModelItem, hideOrgName: boolean, showFavIndicator: boolean)}
|
||||
{@const { option } = item}
|
||||
{@const isSelected = currentModel === option.model || activeId === option.id}
|
||||
{@const isFav = modelsStore.favoriteModelIds.has(option.model)}
|
||||
@@ -78,6 +78,7 @@
|
||||
{onSelect}
|
||||
{option}
|
||||
showBaseModelAvatar
|
||||
{showFavIndicator}
|
||||
/>
|
||||
{/snippet}
|
||||
|
||||
@@ -89,7 +90,7 @@
|
||||
</p>
|
||||
|
||||
{#each favorites as item (`fav-${item.option.id}`)}
|
||||
{@render render(item, !showOrgName)}
|
||||
{@render render(item, !showOrgName, false)}
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
@@ -111,12 +112,12 @@
|
||||
|
||||
<!-- Local models: one list, the loaded ones first. -->
|
||||
{#each groups.loaded as item (`loaded-${item.option.id}`)}
|
||||
{@render render(item, !showOrgName)}
|
||||
{@render render(item, !showOrgName, true)}
|
||||
{/each}
|
||||
|
||||
{#each groups.available as group (group.orgName)}
|
||||
{#each group.items as item (item.option.id)}
|
||||
{@render render(item, !showOrgName)}
|
||||
{@render render(item, !showOrgName, true)}
|
||||
{/each}
|
||||
{/each}
|
||||
|
||||
@@ -149,7 +150,7 @@
|
||||
|
||||
{#if provider.items.length > 0}
|
||||
{#each provider.items as item (`${provider.backendId}-${item.option.id}`)}
|
||||
{@render render(item, !showOrgName)}
|
||||
{@render render(item, !showOrgName, true)}
|
||||
{/each}
|
||||
|
||||
{#if onProviderOpen && provider.matched > provider.items.length}
|
||||
|
||||
@@ -26,6 +26,9 @@
|
||||
isSelected: boolean;
|
||||
isHighlighted: boolean;
|
||||
isFav: boolean;
|
||||
/** Show the filled heart at rest. Off inside the favorites section, where
|
||||
* every row is a favorite. */
|
||||
showFavIndicator?: boolean;
|
||||
hideOrgName?: boolean;
|
||||
onSelect: (modelId: string) => void;
|
||||
onMouseEnter: () => void;
|
||||
@@ -45,7 +48,8 @@
|
||||
onMouseEnter,
|
||||
onSelect,
|
||||
option,
|
||||
showBaseModelAvatar = false
|
||||
showBaseModelAvatar = false,
|
||||
showFavIndicator = true
|
||||
}: Props = $props();
|
||||
|
||||
// row actions follow the backend that serves the row, not the selected one
|
||||
@@ -163,18 +167,53 @@
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
<div
|
||||
class="pointer-events-none flex items-center justify-center gap-0.75 pl-2 opacity-0 group-hover:pointer-events-auto group-hover:opacity-100 [@media(pointer:coarse)]:pointer-events-auto [@media(pointer:coarse)]:opacity-100"
|
||||
class="pointer-events-none flex items-center justify-center gap-2 pl-2 group-hover:pointer-events-auto [@media(pointer:coarse)]:pointer-events-auto [@media(pointer:coarse)]:opacity-100 {isFav &&
|
||||
showFavIndicator
|
||||
? ''
|
||||
: 'opacity-0 group-hover:opacity-100'}"
|
||||
onclick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<!-- info button: only shown when model is loaded and callback is provided -->
|
||||
{#if isLoaded && onInfoClick}
|
||||
<!-- the wrapper above stays visible for a favorite, this one does not -->
|
||||
<span class="flex opacity-0 group-hover:opacity-100 [@media(pointer:coarse)]:opacity-100">
|
||||
<ActionIcon
|
||||
class="h-3 w-3 hover:text-foreground"
|
||||
icon={Info}
|
||||
iconSize="h-2.5 w-2.5"
|
||||
onclick={() => onInfoClick(option.model)}
|
||||
tooltip="Model information"
|
||||
tooltipAsTitle
|
||||
/>
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
{#if isFav}
|
||||
<ActionIcon
|
||||
class="h-3 w-3 hover:text-foreground"
|
||||
icon={HeartOff}
|
||||
iconSize="h-2.5 w-2.5"
|
||||
onclick={() => modelsStore.toggleFavorite(option.model)}
|
||||
tooltip="Remove from favorites"
|
||||
tooltipAsTitle
|
||||
/>
|
||||
<!-- a favorite keeps a colored stroke heart at rest; hovering the icon
|
||||
itself swaps it for the crossed one -->
|
||||
<span class="group/heart flex h-3 w-3 items-center justify-center">
|
||||
<span class="flex group-hover/heart:hidden">
|
||||
<ActionIcon
|
||||
class="h-3 w-3 text-rose-500 hover:text-foreground"
|
||||
icon={Heart}
|
||||
iconSize="h-2.5 w-2.5"
|
||||
onclick={() => modelsStore.toggleFavorite(option.model)}
|
||||
tooltip="Remove from favorites"
|
||||
tooltipAsTitle
|
||||
/>
|
||||
</span>
|
||||
|
||||
<span class="hidden group-hover/heart:flex">
|
||||
<ActionIcon
|
||||
class="h-3 w-3 hover:text-foreground"
|
||||
icon={HeartOff}
|
||||
iconSize="h-2.5 w-2.5"
|
||||
onclick={() => modelsStore.toggleFavorite(option.model)}
|
||||
tooltip="Remove from favorites"
|
||||
tooltipAsTitle
|
||||
/>
|
||||
</span>
|
||||
</span>
|
||||
{:else}
|
||||
<ActionIcon
|
||||
class="h-3 w-3 hover:text-foreground"
|
||||
@@ -185,18 +224,6 @@
|
||||
tooltipAsTitle
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<!-- info button: only shown when model is loaded and callback is provided -->
|
||||
{#if isLoaded && onInfoClick}
|
||||
<ActionIcon
|
||||
class="h-3 w-3 hover:text-foreground"
|
||||
icon={Info}
|
||||
iconSize="h-2.5 w-2.5"
|
||||
onclick={() => onInfoClick(option.model)}
|
||||
tooltip="Model information"
|
||||
tooltipAsTitle
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if !canLoad}
|
||||
|
||||
Reference in New Issue
Block a user