feat: more in-depth token details dropdown

This commit is contained in:
Zoe
2026-03-03 10:22:56 -06:00
parent 26230abc53
commit ea17ee2313
+92 -69
View File
@@ -16,24 +16,35 @@ const { floatingStyles } = useFloating(triggerElement, tokenDetailsDropdownRef,
});
const outputPercentage = computed(() => {
if (!generation.value?.tokens?.output || !generation.value.tokens.thinking) return 0;
return (generation.value.tokens.thinking / generation.value.tokens.output) * 100;
const total = generation.value?.tokens?.output ?? 0;
const thinking = generation.value?.tokens?.thinking ?? 0;
if (total === 0) return 0;
return (thinking / total) * 100;
});
const cacheReadPercentage = computed(() => {
const cache = generation.value?.tokens?.cache;
if (!cache || !cache.read || !cache.write) return 0;
return (cache.read / (cache.read + cache.write)) * 100;
const inputStats = computed(() => {
const totalInput = generation.value?.tokens?.input ?? 0;
const cachedInput = generation.value?.tokens?.cache?.read ?? 0;
const uncachedInput = Math.max(0, totalInput - cachedInput);
const output = generation.value?.tokens?.output ?? 0;
const combinedTotal = totalInput + output;
if (combinedTotal === 0) return { uncached: 0, cached: 0, output: 0, rawUncached: 0, rawCached: 0, rawOutput: 0 };
return {
uncached: (uncachedInput / combinedTotal) * 100,
cached: (cachedInput / combinedTotal) * 100,
output: (output / combinedTotal) * 100,
rawUncached: uncachedInput,
rawCached: cachedInput,
rawOutput: output
};
});
const hasCache = computed(() => {
const cache = generation.value?.tokens?.cache;
return cache && ((cache.read ?? 0) > 0 || (cache.write ?? 0) > 0);
});
const formatNumber = (num?: number) => {
const formatNumber = (num: number | null | undefined) => {
if (num === undefined || num === null) return '0';
return num.toLocaleString();
return num.toLocaleString(undefined, { maximumFractionDigits: 1 });
};
</script>
@@ -47,81 +58,93 @@ const formatNumber = (num?: number) => {
<div v-if="isOpen && generation && generation.tokens" v-click-outside="close" ref="tokenDetailsDropdownRef"
:style="floatingStyles"
class="absolute z-50 flex flex-col min-w-64 bg-[var(--bg-surface)] rounded-lg border border-[var(--color-border)] shadow-xl">
<div class="p-2 border-b border-[var(--color-border)]">
<span class="font-medium">Token Details</span>
</div>
class="absolute z-50 flex flex-col min-w-[280px] bg-[var(--bg-surface)] rounded-md border border-[var(--color-border)] shadow-2xl text-[var(--text-secondary)] font-sans">
<div class="p-2">
<div class="flex items-center justify-between mb-2">
<span
class="text-xs font-medium text-[var(--text-secondary)] uppercase tracking-wide">Output</span>
<span class="text-sm font-medium">{{ formatNumber(generation.tokens.output ?? 0) }}
tokens</span>
<!-- Output Details Section -->
<div class="px-3 pt-3 pb-4">
<div class="text-[12px] mb-2 px-0.5 font-medium">Output Details</div>
<div class="relative w-full h-[5px] mb-4 rounded-full overflow-hidden flex">
<div class="h-full bg-pink-500" :style="{ width: `${outputPercentage}%` }"></div>
<div class="h-full bg-emerald-400" :style="{ width: `${100 - outputPercentage}%` }"></div>
</div>
<div class="relative w-full h-2 mb-3 bg-[var(--bg-hover)] rounded-full overflow-hidden">
<div class="absolute left-0 top-0 bottom-0 bg-pink-400"
:style="{ width: `${outputPercentage}%` }">
<div class="space-y-1.5 px-0.5">
<div v-if="generation.tokens.thinking ?? 0 > 0" class="flex items-center justify-between">
<div class="flex items-center gap-2">
<span class="w-2 h-2 bg-pink-500 rounded-full"></span>
<span class="text-[14px]">Deep Thinking</span>
</div>
<span class="text-[14px] font-medium text-[var(--text-primary)]">
{{ formatNumber(generation.tokens.thinking ?? 0) }}
</span>
</div>
<div class="absolute top-0 bottom-0 bg-[var(--text-tertiary)]"
:style="{ left: `${outputPercentage}%`, width: `${100 - outputPercentage}%` }">
</div>
</div>
<div class="flex gap-6 text-sm">
<div class="flex items-center gap-2">
<span class="w-2 h-2 bg-pink-400 rounded-full"></span>
<span class="text-[var(--text-secondary)]">Reasoning</span>
<span class="font-medium">{{ formatNumber(generation.tokens.thinking ?? 0) }}</span>
</div>
<div class="flex items-center gap-2">
<span class="w-2 h-2 bg-[var(--text-tertiary)] rounded-full"></span>
<span class="text-[var(--text-secondary)]">Output</span>
<span class="font-medium">
<div class="flex items-center justify-between">
<div class="flex items-center gap-2">
<span class="w-2 h-2 bg-emerald-400 rounded-full"></span>
<span class="text-[14px]">Text Output</span>
</div>
<span class="text-[14px] font-medium text-[var(--text-primary)]">
{{ formatNumber((generation.tokens.output ?? 0) - (generation.tokens.thinking ?? 0)) }}
</span>
</div>
</div>
</div>
<div v-if="hasCache" class="px-2 pb-2">
<div class="flex items-center justify-between mb-2">
<span
class="text-xs font-medium text-[var(--text-secondary)] uppercase tracking-wide">Cache</span>
<span class="text-sm font-medium">{{ formatNumber((generation.tokens.cache?.read || 0) +
(generation.tokens.cache?.write || 0)) }} tokens</span>
<!-- Input/Output Progress Bar -->
<div class="px-3 pb-4">
<div class="relative w-full h-[5px] mb-4 bg-[var(--text-dim)] rounded-full overflow-hidden flex">
<div class="h-full bg-[var(--text-dim)]" :style="{ width: `${inputStats.uncached}%` }"></div>
<div class="h-full bg-orange-400" :style="{ width: `${inputStats.cached}%` }"></div>
<div class="h-full bg-lime-300" :style="{ width: `${inputStats.output}%` }"></div>
</div>
<div class="relative w-full h-2 mb-3 bg-[var(--bg-hover)] rounded-full overflow-hidden">
<div class="absolute left-0 top-0 bottom-0 bg-sky-400"
:style="{ width: `${cacheReadPercentage}%` }">
<div class="space-y-1.5 px-0.5">
<div v-if="inputStats.rawUncached > 0" class="flex items-center justify-between">
<div class="flex items-center gap-2">
<span class="w-2 h-2 bg-[var(--text-dim)] rounded-full"></span>
<span class="text-[14px]">Uncached Input</span>
</div>
<span class="text-[14px] font-medium text-[var(--text-primary)]">
{{ formatNumber(inputStats.rawUncached) }}
</span>
</div>
<div class="absolute top-0 bottom-0 bg-emerald-400"
:style="{ left: `${cacheReadPercentage}%`, width: `${100 - cacheReadPercentage}%` }">
<div v-if="inputStats.cached > 0" class="flex items-center justify-between">
<div class="flex items-center gap-2">
<span class="w-2 h-2 bg-orange-400 rounded-full"></span>
<span class="text-[14px]">Cached Input</span>
</div>
<span class="text-[14px] font-medium text-[var(--text-primary)]">
{{ formatNumber(inputStats.rawCached) }}
</span>
</div>
</div>
<div class="flex gap-6 text-sm">
<div class="flex items-center gap-2">
<span class="w-2 h-2 bg-sky-400 rounded-full"></span>
<span class="text-[var(--text-secondary)]">Read</span>
<span class="font-medium">{{ formatNumber(generation.tokens.cache?.read ?? 0) }}</span>
</div>
<div class="flex items-center gap-2">
<span class="w-2 h-2 bg-emerald-400 rounded-full"></span>
<span class="text-[var(--text-secondary)]">Write</span>
<span class="font-medium">{{ formatNumber(generation.tokens.cache?.write ?? 0) }}</span>
<div class="flex items-center justify-between">
<div class="flex items-center gap-2">
<span class="w-2 h-2 bg-lime-300 rounded-full"></span>
<span class="text-[14px]">Output</span>
</div>
<span class="text-[14px] font-medium text-[var(--text-primary)]">
{{ formatNumber(inputStats.rawOutput) }}
</span>
</div>
</div>
</div>
<div class="flex border-t border-[var(--color-border)]">
<div class="flex-1 p-2 text-center border-r border-[var(--color-border)]">
<div class="text-xs font-medium text-[var(--text-secondary)] uppercase tracking-wide mb-1">TTFT
<!-- Footer Stats -->
<div class="flex border-t border-[var(--color-border)] bg-[var(--bg-base)] rounded-b-md px-1 py-1">
<div class="flex-1 py-1.5 text-center border-r border-[var(--color-border)]">
<div class="text-[9px] font-bold text-[var(--text-tertiary)] uppercase tracking-wider mb-0.5">
TTFT</div>
<div class="text-[13px] font-semibold text-[var(--text-primary)]">
{{ formatNumber(generation.tokens.ttft) }}ms
</div>
<div class="text-base font-medium">{{ generation.tokens.ttft?.toFixed(1) || '0' }}ms</div>
</div>
<div class="flex-1 p-2 text-center">
<div class="text-xs font-medium text-[var(--text-secondary)] uppercase tracking-wide mb-1">Speed
<div class="flex-1 py-1.5 text-center">
<div class="text-[9px] font-bold text-[var(--text-tertiary)] uppercase tracking-wider mb-0.5">
Speed</div>
<div class="text-[13px] font-semibold text-[var(--text-primary)]">
{{ formatNumber(generation.tokens.tps) }} tps
</div>
<div class="text-base font-medium">{{ generation.tokens.tps?.toFixed(1) || '0' }} tps</div>
</div>
</div>
</div>