mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2026-09-19 01:04:55 +02:00
ui : extract shared ModelCapabilityIcons and reuse in details header
Pull the tool-use / reasoning / vision / video / audio icon row out of ModelId into a shared ModelCapabilityIcons component and use it from the discover details header, replacing the header's hand-rolled icons. This fixes the divergent icon order and styling between the two surfaces and keeps every model-id capability row consistent. Assisted-by: llama-ui:Qwen3.8-Flash-Next
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
<script lang="ts">
|
||||
import { Image, Lightbulb, Mic, Video, Wrench } from '@lucide/svelte';
|
||||
import * as Tooltip from '$lib/components/ui/tooltip';
|
||||
import type { ModelModalities } from '$lib/types/models';
|
||||
|
||||
interface Props {
|
||||
/** Input modalities to render (vision / video / audio). */
|
||||
modalities?: ModelModalities;
|
||||
supportsThinking?: boolean;
|
||||
supportsToolUse?: boolean;
|
||||
/** Skip the reasoning (lightbulb) icon regardless of support. */
|
||||
hideReasoning?: boolean;
|
||||
/** Skip all modality icons regardless of support. */
|
||||
hideModalities?: boolean;
|
||||
/** Icon box size classes, e.g. `h-3 w-3` (default) or `h-4 w-4`. */
|
||||
iconSize?: string;
|
||||
/** Gap between the capability and modality icon groups. */
|
||||
gapClass?: string;
|
||||
}
|
||||
|
||||
let {
|
||||
gapClass = 'gap-1.25',
|
||||
hideModalities = false,
|
||||
hideReasoning = false,
|
||||
iconSize = 'h-3 w-3',
|
||||
modalities,
|
||||
supportsThinking = false,
|
||||
supportsToolUse = false
|
||||
}: Props = $props();
|
||||
|
||||
let hasModalityIcons = $derived(modalities?.vision || modalities?.video || modalities?.audio);
|
||||
</script>
|
||||
|
||||
{#if supportsToolUse}
|
||||
<Tooltip.Root>
|
||||
<Tooltip.Trigger>
|
||||
<Wrench class="{iconSize} text-muted-foreground" />
|
||||
</Tooltip.Trigger>
|
||||
|
||||
<Tooltip.Content>
|
||||
<p>Tool use</p>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
{/if}
|
||||
|
||||
{#if supportsThinking && !hideReasoning}
|
||||
<Tooltip.Root>
|
||||
<Tooltip.Trigger>
|
||||
<Lightbulb class="{iconSize} text-muted-foreground" />
|
||||
</Tooltip.Trigger>
|
||||
|
||||
<Tooltip.Content>
|
||||
<p>Reasoning</p>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
{/if}
|
||||
|
||||
{#if hasModalityIcons && !hideModalities}
|
||||
<span class="inline-flex items-center {gapClass} text-muted-foreground">
|
||||
{#if modalities?.vision}
|
||||
<Tooltip.Root>
|
||||
<Tooltip.Trigger>
|
||||
<Image class={iconSize} />
|
||||
</Tooltip.Trigger>
|
||||
|
||||
<Tooltip.Content>
|
||||
<p>Vision</p>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
{/if}
|
||||
|
||||
{#if modalities?.video}
|
||||
<Tooltip.Root>
|
||||
<Tooltip.Trigger>
|
||||
<Video class={iconSize} />
|
||||
</Tooltip.Trigger>
|
||||
|
||||
<Tooltip.Content>
|
||||
<p>Video</p>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
{/if}
|
||||
|
||||
{#if modalities?.audio}
|
||||
<Tooltip.Root>
|
||||
<Tooltip.Trigger>
|
||||
<Mic class={iconSize} />
|
||||
</Tooltip.Trigger>
|
||||
|
||||
<Tooltip.Content>
|
||||
<p>Audio</p>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
{/if}
|
||||
</span>
|
||||
{/if}
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { Database, Image, Lightbulb, Mic, ScrollText, Video, Wrench } from '@lucide/svelte';
|
||||
import ModelCapabilityIcons from './ModelCapabilityIcons.svelte';
|
||||
import { Database, ScrollText } from '@lucide/svelte';
|
||||
import { TruncatedText } from '$lib/components/app';
|
||||
import * as Tooltip from '$lib/components/ui/tooltip';
|
||||
import { isAuxSidecar, type ModelSidecar } from '$lib/constants';
|
||||
@@ -81,7 +82,6 @@
|
||||
let uniqueAliases = $derived([...new Set(aliases ?? [])]);
|
||||
let uniqueTags = $derived([...new Set([...(parsed.tags ?? []), ...(tags ?? [])])]);
|
||||
let uniqueDraftSidecars = $derived([...new Set(draftSidecars)].filter((s) => !isAuxSidecar(s)));
|
||||
let hasModalityIcons = $derived(modalities?.vision || modalities?.video || modalities?.audio);
|
||||
|
||||
let primaryAlias = $derived(uniqueAliases.length === 1 ? uniqueAliases[0] : null);
|
||||
let displayName = $derived(primaryAlias ?? parsed.modelName ?? modelId);
|
||||
@@ -99,72 +99,6 @@
|
||||
{#if resolvedShowRaw}
|
||||
<TruncatedText class="font-medium {className}" showTooltip={false} text={modelId} {...rest} />
|
||||
{:else}
|
||||
{#snippet capabilityIcons()}
|
||||
{#if supportsToolUse}
|
||||
<Tooltip.Root>
|
||||
<Tooltip.Trigger>
|
||||
<Wrench class="h-3 w-3 text-muted-foreground" />
|
||||
</Tooltip.Trigger>
|
||||
|
||||
<Tooltip.Content>
|
||||
<p>Tool use</p>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
{/if}
|
||||
|
||||
{#if supportsThinking && !hideReasoning}
|
||||
<Tooltip.Root>
|
||||
<Tooltip.Trigger>
|
||||
<Lightbulb class="h-3 w-3 text-muted-foreground" />
|
||||
</Tooltip.Trigger>
|
||||
|
||||
<Tooltip.Content>
|
||||
<p>Reasoning</p>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
{/if}
|
||||
|
||||
{#if hasModalityIcons && !hideModalities}
|
||||
<span class="inline-flex items-center gap-1.25 text-muted-foreground">
|
||||
{#if modalities?.vision}
|
||||
<Tooltip.Root>
|
||||
<Tooltip.Trigger>
|
||||
<Image class="h-3 w-3 text-muted-foreground" />
|
||||
</Tooltip.Trigger>
|
||||
|
||||
<Tooltip.Content>
|
||||
<p>Vision</p>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
{/if}
|
||||
|
||||
{#if modalities?.video}
|
||||
<Tooltip.Root>
|
||||
<Tooltip.Trigger>
|
||||
<Video class="h-3 w-3 text-muted-foreground" />
|
||||
</Tooltip.Trigger>
|
||||
|
||||
<Tooltip.Content>
|
||||
<p>Video</p>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
{/if}
|
||||
|
||||
{#if modalities?.audio}
|
||||
<Tooltip.Root>
|
||||
<Tooltip.Trigger>
|
||||
<Mic class="h-3 w-3 text-muted-foreground" />
|
||||
</Tooltip.Trigger>
|
||||
|
||||
<Tooltip.Content>
|
||||
<p>Audio</p>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
{/if}
|
||||
</span>
|
||||
{/if}
|
||||
{/snippet}
|
||||
|
||||
{#snippet nameAndBadges()}
|
||||
{#if !hideName}
|
||||
<span class="min-w-0 truncate font-medium">
|
||||
@@ -238,12 +172,28 @@
|
||||
{@render nameAndBadges()}
|
||||
{/if}
|
||||
|
||||
{#if !iconsOnNewLine}{@render capabilityIcons()}{/if}
|
||||
{#if !iconsOnNewLine}
|
||||
<ModelCapabilityIcons
|
||||
{hideModalities}
|
||||
{hideReasoning}
|
||||
{modalities}
|
||||
{supportsThinking}
|
||||
{supportsToolUse}
|
||||
/>
|
||||
{/if}
|
||||
</span>
|
||||
|
||||
{#if iconsOnNewLine || contextLength || sizeRange}
|
||||
<span class="inline-flex items-center gap-1.5">
|
||||
{#if iconsOnNewLine}{@render capabilityIcons()}{/if}
|
||||
{#if iconsOnNewLine}
|
||||
<ModelCapabilityIcons
|
||||
{hideModalities}
|
||||
{hideReasoning}
|
||||
{modalities}
|
||||
{supportsThinking}
|
||||
{supportsToolUse}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if contextLength}
|
||||
<span class="inline-flex items-center gap-1 text-muted-foreground">
|
||||
|
||||
+9
-41
@@ -1,8 +1,8 @@
|
||||
<script lang="ts">
|
||||
import ModelCapabilityIcons from '../../ModelCapabilityIcons.svelte';
|
||||
import ModelsDiscoverAvatar from '../ModelsDiscoverAvatar.svelte';
|
||||
import ModelsDiscoverDetailsMetadata from './ModelsDiscoverDetailsMetadata.svelte';
|
||||
import { Download, ExternalLink, Heart, Image, Lightbulb, Wrench } from '@lucide/svelte';
|
||||
import * as Tooltip from '$lib/components/ui/tooltip';
|
||||
import { Download, ExternalLink, Heart } from '@lucide/svelte';
|
||||
import { ICON_CLASS_SM } from '$lib/constants';
|
||||
import { HuggingFaceService } from '$lib/services';
|
||||
import type { HfModelDetailInfo, HfModelGguf } from '$lib/types/huggingface';
|
||||
@@ -44,45 +44,13 @@
|
||||
<div class="flex items-center gap-2">
|
||||
<h1 class="truncate text-lg font-semibold">{details.id ?? modelId}</h1>
|
||||
|
||||
{#if hasVision || hasTools || hasReasoning}
|
||||
<div class="flex shrink-0 items-center gap-2.5 text-muted-foreground">
|
||||
{#if hasVision}
|
||||
<Tooltip.Root>
|
||||
<Tooltip.Trigger>
|
||||
<Image class="h-4 w-4" />
|
||||
</Tooltip.Trigger>
|
||||
|
||||
<Tooltip.Content>
|
||||
<p>Vision</p>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
{/if}
|
||||
|
||||
{#if hasTools}
|
||||
<Tooltip.Root>
|
||||
<Tooltip.Trigger>
|
||||
<Wrench class="h-4 w-4" />
|
||||
</Tooltip.Trigger>
|
||||
|
||||
<Tooltip.Content>
|
||||
<p>Tool use</p>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
{/if}
|
||||
|
||||
{#if hasReasoning}
|
||||
<Tooltip.Root>
|
||||
<Tooltip.Trigger>
|
||||
<Lightbulb class="h-4 w-4" />
|
||||
</Tooltip.Trigger>
|
||||
|
||||
<Tooltip.Content>
|
||||
<p>Reasoning</p>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
<ModelCapabilityIcons
|
||||
gapClass="gap-2"
|
||||
iconSize="h-4 w-4"
|
||||
modalities={{ audio: false, video: false, vision: hasVision }}
|
||||
supportsThinking={hasReasoning}
|
||||
supportsToolUse={hasTools}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{#if baseModels.length}
|
||||
|
||||
@@ -49,4 +49,13 @@ export { default as ModelBadge } from './ModelBadge.svelte';
|
||||
*/
|
||||
export { default as ModelId } from './ModelId.svelte';
|
||||
|
||||
/**
|
||||
* **ModelCapabilityIcons** - Capability and modality icon row
|
||||
*
|
||||
* The shared tool-use / reasoning / vision / video / audio icon cluster with
|
||||
* tooltips, used by ModelId and the discover details header so the order and
|
||||
* styling stay consistent across every model-id surface.
|
||||
*/
|
||||
export { default as ModelCapabilityIcons } from './ModelCapabilityIcons.svelte';
|
||||
|
||||
export * from './ModelsSelector';
|
||||
|
||||
Reference in New Issue
Block a user