mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2026-10-02 10:57:33 -05:00
The table grows a section per enabled provider, each with its own mark, error and loading state, and a provider filter with live repo counts. Rows gain their capability gates back, the draft column with its use-as-draft action, and the quant badge names the provider on its rows. Assisted-by: pi:llama.cpp/DeepSeek-V4.1-Flash
140 lines
4.2 KiB
Svelte
140 lines
4.2 KiB
Svelte
<script lang="ts">
|
|
import { ActionIcon } from '$lib/components/app';
|
|
import { BackendIcon } from '$lib/components/app/backends';
|
|
import { ICON_CLASS_DEFAULT, MODEL_LOAD_ICONS } from '$lib/constants';
|
|
import { backendsModelsStore, modelsStore } from '$lib/stores';
|
|
import type { ModelOption } from '$lib/types/models';
|
|
import { getBackend } from '$lib/utils/api-base';
|
|
|
|
interface Props {
|
|
/** Backend can load and unload models, llama-compat servers only. */
|
|
canLoad: boolean;
|
|
class?: string;
|
|
isFailed?: boolean;
|
|
isLoaded: boolean;
|
|
isLoading?: boolean;
|
|
isSleeping?: boolean;
|
|
option: ModelOption;
|
|
/** Table rows keep the load action visible, selector rows reveal it on hover. */
|
|
revealOnHover?: boolean;
|
|
/** Renders the state alone, for a caller that moves load and unload elsewhere. */
|
|
showAction?: boolean;
|
|
/** Non-loadable rows show the provider mark, which identifies them in a flat list. */
|
|
showBackendMark?: boolean;
|
|
/** Table rows mark a remote provider, which this UI cannot load or unload. */
|
|
showRemoteMark?: boolean;
|
|
}
|
|
|
|
let {
|
|
canLoad,
|
|
class: className = '',
|
|
isFailed = false,
|
|
isLoaded,
|
|
isLoading = false,
|
|
isSleeping = false,
|
|
option,
|
|
revealOnHover = true,
|
|
showAction = true,
|
|
showBackendMark = false,
|
|
showRemoteMark = false
|
|
}: Props = $props();
|
|
|
|
let backendName = $derived(getBackend(option.backendId)?.name ?? 'Remote provider');
|
|
/** The provider's listing failed, so nothing it serves is selectable right now. */
|
|
let isBackendFailed = $derived(
|
|
Boolean(option.backendId && backendsModelsStore.get(option.backendId).error)
|
|
);
|
|
</script>
|
|
|
|
<div class={['flex w-5 shrink-0 items-center justify-center', className]}>
|
|
{#if !canLoad}
|
|
{#if showBackendMark}
|
|
<BackendIcon backend={getBackend(option.backendId)} class="h-3.5 w-3.5" />
|
|
{:else if showRemoteMark}
|
|
<span
|
|
class="flex items-center gap-1 {isBackendFailed ? 'opacity-50 grayscale' : ''}"
|
|
title={isBackendFailed ? `${backendName} is unavailable` : backendName}
|
|
>
|
|
<BackendIcon backend={getBackend(option.backendId)} class="h-3.5 w-3.5" />
|
|
|
|
{#if isBackendFailed}
|
|
<MODEL_LOAD_ICONS.failed class="h-3 w-3 text-destructive" />
|
|
{/if}
|
|
</span>
|
|
{/if}
|
|
{:else if isLoading}
|
|
<MODEL_LOAD_ICONS.loading class="{ICON_CLASS_DEFAULT} animate-spin text-muted-foreground" />
|
|
{:else}
|
|
<!-- the state dot is what the row shows at rest; the action takes its place on hover -->
|
|
{#if showAction && revealOnHover}
|
|
{#if isFailed}
|
|
<MODEL_LOAD_ICONS.failed
|
|
class="h-3.5 w-3.5 text-red-500 group-hover:hidden [@media(pointer:coarse)]:hidden"
|
|
/>
|
|
{:else}
|
|
<span
|
|
class="h-2 w-2 rounded-full group-hover:hidden [@media(pointer:coarse)]:hidden {isSleeping
|
|
? 'bg-orange-400'
|
|
: isLoaded
|
|
? 'bg-green-500'
|
|
: 'bg-muted-foreground/50'}"
|
|
></span>
|
|
{/if}
|
|
{:else}
|
|
{#if isFailed}
|
|
<MODEL_LOAD_ICONS.failed class="h-3.5 w-3.5 text-red-500" />
|
|
{:else}
|
|
<span
|
|
class="h-2 w-2 rounded-full {isSleeping
|
|
? 'bg-orange-400'
|
|
: isLoaded
|
|
? 'bg-green-500'
|
|
: 'bg-muted-foreground/50'}"
|
|
></span>
|
|
{/if}
|
|
{/if}
|
|
|
|
<!-- the wrapper has to pick one display value: flex and hidden together
|
|
leave the winner to stylesheet order -->
|
|
<div
|
|
class={!showAction
|
|
? 'hidden'
|
|
: revealOnHover
|
|
? 'hidden group-hover:flex [@media(pointer:coarse)]:flex'
|
|
: 'flex'}
|
|
>
|
|
{#if isFailed}
|
|
<ActionIcon
|
|
class="h-5 w-5 text-red-500 hover:text-foreground"
|
|
icon={MODEL_LOAD_ICONS.retry}
|
|
iconSize="h-4 w-4"
|
|
onclick={() => modelsStore.status.load(option.model)}
|
|
stopPropagationOnClick
|
|
tooltip="Retry loading model"
|
|
tooltipAsTitle
|
|
/>
|
|
{:else if isLoaded || isSleeping}
|
|
<ActionIcon
|
|
class="h-5 w-5 hover:text-foreground"
|
|
icon={MODEL_LOAD_ICONS.unload}
|
|
iconSize="h-4 w-4"
|
|
onclick={() => modelsStore.status.unload(option.model)}
|
|
stopPropagationOnClick
|
|
tooltip="Unload model"
|
|
tooltipAsTitle
|
|
/>
|
|
{:else}
|
|
<ActionIcon
|
|
class="h-5 w-5 hover:text-foreground"
|
|
icon={MODEL_LOAD_ICONS.load}
|
|
iconSize="h-4 w-4"
|
|
onclick={() => modelsStore.status.load(option.model)}
|
|
stopPropagationOnClick
|
|
tooltip="Load model"
|
|
tooltipAsTitle
|
|
/>
|
|
{/if}
|
|
</div>
|
|
{/if}
|
|
</div>
|