ui : show a plain status indicator in the manager table

Assisted-by: pi:llama.cpp/DeepSeek-V4.1-Flash
This commit is contained in:
Aleksander Grygier
2026-09-28 12:10:11 +02:00
parent e401b00ee0
commit d5d033e5d6
2 changed files with 41 additions and 8 deletions
@@ -1,9 +1,9 @@
<script lang="ts">
import { CircleAlert, Cloud, Loader2, Power, RotateCw, Upload } from '@lucide/svelte';
import { CircleAlert, Loader2, Power, RotateCw, Upload } from '@lucide/svelte';
import { ActionIcon } from '$lib/components/app';
import { BackendIcon } from '$lib/components/app/backends';
import { ICON_CLASS_DEFAULT } from '$lib/constants';
import { modelsStore } from '$lib/stores';
import { backendsModelsStore, modelsStore } from '$lib/stores';
import type { ModelOption } from '$lib/types/models';
import { getBackend } from '$lib/utils/api-base';
@@ -17,6 +17,8 @@
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. */
@@ -31,9 +33,16 @@
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">
@@ -41,15 +50,22 @@
{#if showBackendMark}
<BackendIcon backend={getBackend(option.backendId)} class="h-3.5 w-3.5" />
{:else if showRemoteMark}
<span class="text-muted-foreground" title="Served remotely"
><Cloud class="h-3.5 w-3.5" /></span
<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}
<CircleAlert class="h-3 w-3 text-destructive" />
{/if}
</span>
{/if}
{:else if isLoading}
<Loader2 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 revealOnHover}
{#if showAction && revealOnHover}
{#if isFailed}
<CircleAlert
class="h-3.5 w-3.5 text-red-500 group-hover:hidden [@media(pointer:coarse)]:hidden"
@@ -63,9 +79,26 @@
: 'bg-muted-foreground/50'}"
></span>
{/if}
{:else}
{#if isFailed}
<CircleAlert 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}
<div class={revealOnHover ? 'hidden group-hover:flex [@media(pointer:coarse)]:flex' : 'flex'}>
<div
class={[
showAction ? 'flex' : 'hidden',
revealOnHover ? 'hidden group-hover:flex [@media(pointer:coarse)]:flex' : ''
]}
>
{#if isFailed}
<ActionIcon
class="h-5 w-5 text-red-500 hover:text-foreground"
@@ -162,7 +162,7 @@
{isLoading}
{isSleeping}
{option}
revealOnHover={false}
showAction={false}
showRemoteMark
/>
@@ -201,7 +201,7 @@
<span>Last used</span>
<span class="text-center">State</span>
<span class="text-center">Status</span>
<span class="text-right">Actions</span>
</div>