refactor: UI badges

This commit is contained in:
Aleksander Grygier 2025-11-23 23:44:14 +01:00
parent 188d3236e4
commit 16747dee5b
6 changed files with 81 additions and 38 deletions

View File

@ -1,5 +1,6 @@
<script lang="ts"> <script lang="ts">
import { import {
BadgeModelName,
ChatMessageActions, ChatMessageActions,
ChatMessageStatistics, ChatMessageStatistics,
ChatMessageThinkingBlock, ChatMessageThinkingBlock,
@ -260,13 +261,11 @@
disabled={isLoading()} disabled={isLoading()}
/> />
{:else} {:else}
<button <BadgeModelName
class="inline-flex cursor-pointer items-center gap-1 rounded-sm bg-muted-foreground/15 px-1.5 py-0.75" model={displayedModel() || undefined}
onclick={handleCopyModel} onclick={handleCopyModel}
> showCopyIcon={true}
{displayedModel()} />
<CopyToClipboardIcon text={displayedModel() || ''} ariaLabel="Copy model name" />
</button>
{/if} {/if}
{#if currentConfig.showMessageStats && message.timings && message.timings.predicted_n && message.timings.predicted_ms} {#if currentConfig.showMessageStats && message.timings && message.timings.predicted_n && message.timings.predicted_ms}

View File

@ -1,5 +1,6 @@
<script lang="ts"> <script lang="ts">
import { Clock, Gauge, WholeWord } from '@lucide/svelte'; import { Clock, Gauge, WholeWord } from '@lucide/svelte';
import { BadgeChatStatistic } from '$lib/components/app';
interface Props { interface Props {
predictedTokens: number; predictedTokens: number;
@ -12,17 +13,8 @@
let timeInSeconds = $derived((predictedMs / 1000).toFixed(2)); let timeInSeconds = $derived((predictedMs / 1000).toFixed(2));
</script> </script>
<span class="inline-flex items-center gap-1 rounded-sm bg-muted-foreground/15 px-1.5 py-0.75"> <BadgeChatStatistic icon={WholeWord} value="{predictedTokens} tokens" />
<WholeWord class="h-3 w-3" />
{predictedTokens} tokens
</span>
<span class="inline-flex items-center gap-1 rounded-sm bg-muted-foreground/15 px-1.5 py-0.75"> <BadgeChatStatistic icon={Clock} value="{timeInSeconds}s" />
<Clock class="h-3 w-3" />
{timeInSeconds}s
</span>
<span class="inline-flex items-center gap-1 rounded-sm bg-muted-foreground/15 px-1.5 py-0.75"> <BadgeChatStatistic icon={Gauge} value="{tokensPerSecond.toFixed(2)} tokens/s" />
<Gauge class="h-3 w-3" />
{tokensPerSecond.toFixed(2)} tokens/s
</span>

View File

@ -53,6 +53,8 @@ export { default as DialogModelInformation } from './dialogs/DialogModelInformat
export { default as ActionButton } from './misc/ActionButton.svelte'; export { default as ActionButton } from './misc/ActionButton.svelte';
export { default as ActionDropdown } from './misc/ActionDropdown.svelte'; export { default as ActionDropdown } from './misc/ActionDropdown.svelte';
export { default as BadgeChatStatistic } from './misc/BadgeChatStatistic.svelte';
export { default as BadgeInfo } from './misc/BadgeInfo.svelte';
export { default as BadgeModelName } from './misc/BadgeModelName.svelte'; export { default as BadgeModelName } from './misc/BadgeModelName.svelte';
export { default as BadgeModality } from './misc/BadgeModality.svelte'; export { default as BadgeModality } from './misc/BadgeModality.svelte';
export { default as ConversationSelection } from './misc/ConversationSelection.svelte'; export { default as ConversationSelection } from './misc/ConversationSelection.svelte';

View File

@ -0,0 +1,20 @@
<script lang="ts">
import { BadgeInfo } from '$lib/components/app';
import type { Component } from 'svelte';
interface Props {
class?: string;
icon: Component;
value: string | number;
}
let { class: className = '', icon: Icon, value }: Props = $props();
</script>
<BadgeInfo class={className}>
{#snippet icon()}
<Icon class="h-3 w-3" />
{/snippet}
{value}
</BadgeInfo>

View File

@ -0,0 +1,27 @@
<script lang="ts">
import { cn } from '$lib/components/ui/utils';
import type { Snippet } from 'svelte';
interface Props {
children: Snippet;
class?: string;
icon?: Snippet;
onclick?: () => void;
}
let { children, class: className = '', icon, onclick }: Props = $props();
</script>
<button
class={cn(
'inline-flex cursor-pointer items-center gap-1 rounded-sm bg-muted-foreground/15 px-1.5 py-0.75',
className
)}
{onclick}
>
{#if icon}
{@render icon()}
{/if}
{@render children()}
</button>

View File

@ -1,46 +1,49 @@
<script lang="ts"> <script lang="ts">
import { Package } from '@lucide/svelte'; import { Package } from '@lucide/svelte';
import { Badge } from '$lib/components/ui/badge'; import { BadgeInfo, CopyToClipboardIcon } from '$lib/components/app';
import { serverStore } from '$lib/stores/server.svelte'; import { serverStore } from '$lib/stores/server.svelte';
import { cn } from '$lib/components/ui/utils';
import * as Tooltip from '$lib/components/ui/tooltip'; import * as Tooltip from '$lib/components/ui/tooltip';
import { TOOLTIP_DELAY_DURATION } from '$lib/constants/tooltip-config'; import { TOOLTIP_DELAY_DURATION } from '$lib/constants/tooltip-config';
interface Props { interface Props {
class?: string; class?: string;
model?: string;
onclick?: () => void; onclick?: () => void;
showCopyIcon?: boolean;
showTooltip?: boolean; showTooltip?: boolean;
} }
let { class: className = '', onclick, showTooltip = false }: Props = $props(); let {
class: className = '',
model: modelProp,
onclick,
showCopyIcon = false,
showTooltip = false
}: Props = $props();
let model = $derived(serverStore.modelName); let model = $derived(modelProp || serverStore.modelName);
let isModelMode = $derived(serverStore.isModelMode); let isModelMode = $derived(serverStore.isModelMode);
</script> </script>
{#snippet badge()} {#snippet badgeContent()}
<Badge <BadgeInfo class={className} {onclick}>
variant="outline" {#snippet icon()}
class={cn(
'text-xs',
onclick ? 'cursor-pointer transition-colors hover:bg-foreground/20' : '',
className
)}
{onclick}
>
<div class="icons mr-0.5 flex items-center gap-1.5">
<Package class="h-3 w-3" /> <Package class="h-3 w-3" />
</div> {/snippet}
<span class="block truncate">{model}</span> {model}
</Badge>
{#if showCopyIcon}
<CopyToClipboardIcon text={model || ''} ariaLabel="Copy model name" />
{/if}
</BadgeInfo>
{/snippet} {/snippet}
{#if model && isModelMode} {#if model && isModelMode}
{#if showTooltip} {#if showTooltip}
<Tooltip.Root delayDuration={TOOLTIP_DELAY_DURATION}> <Tooltip.Root delayDuration={TOOLTIP_DELAY_DURATION}>
<Tooltip.Trigger> <Tooltip.Trigger>
{@render badge()} {@render badgeContent()}
</Tooltip.Trigger> </Tooltip.Trigger>
<Tooltip.Content> <Tooltip.Content>
@ -48,6 +51,6 @@
</Tooltip.Content> </Tooltip.Content>
</Tooltip.Root> </Tooltip.Root>
{:else} {:else}
{@render badge()} {@render badgeContent()}
{/if} {/if}
{/if} {/if}