1<script lang="ts">
  2	import { Clock, Gauge, WholeWord, BookOpenText, Sparkles } from '@lucide/svelte';
  3	import { BadgeChatStatistic } from '$lib/components/app';
  4	import * as Tooltip from '$lib/components/ui/tooltip';
  5	import { ChatMessageStatsView } from '$lib/enums';
  6
  7	interface Props {
  8		predictedTokens?: number;
  9		predictedMs?: number;
 10		promptTokens?: number;
 11		promptMs?: number;
 12		// Live mode: when true, shows stats during streaming
 13		isLive?: boolean;
 14		// Whether prompt processing is still in progress
 15		isProcessingPrompt?: boolean;
 16		// Initial view to show (defaults to READING in live mode)
 17		initialView?: ChatMessageStatsView;
 18	}
 19
 20	let {
 21		predictedTokens,
 22		predictedMs,
 23		promptTokens,
 24		promptMs,
 25		isLive = false,
 26		isProcessingPrompt = false,
 27		initialView = ChatMessageStatsView.GENERATION
 28	}: Props = $props();
 29
 30	let activeView: ChatMessageStatsView = $state(initialView);
 31	let hasAutoSwitchedToGeneration = $state(false);
 32
 33	// In live mode: auto-switch to GENERATION tab when prompt processing completes
 34	$effect(() => {
 35		if (isLive) {
 36			// Auto-switch to generation tab only when prompt processing is done (once)
 37			if (
 38				!hasAutoSwitchedToGeneration &&
 39				!isProcessingPrompt &&
 40				predictedTokens &&
 41				predictedTokens > 0
 42			) {
 43				activeView = ChatMessageStatsView.GENERATION;
 44				hasAutoSwitchedToGeneration = true;
 45			} else if (!hasAutoSwitchedToGeneration) {
 46				// Stay on READING while prompt is still being processed
 47				activeView = ChatMessageStatsView.READING;
 48			}
 49		}
 50	});
 51
 52	let hasGenerationStats = $derived(
 53		predictedTokens !== undefined &&
 54			predictedTokens > 0 &&
 55			predictedMs !== undefined &&
 56			predictedMs > 0
 57	);
 58
 59	let tokensPerSecond = $derived(hasGenerationStats ? (predictedTokens! / predictedMs!) * 1000 : 0);
 60	let timeInSeconds = $derived(
 61		predictedMs !== undefined ? (predictedMs / 1000).toFixed(2) : '0.00'
 62	);
 63
 64	let promptTokensPerSecond = $derived(
 65		promptTokens !== undefined && promptMs !== undefined && promptMs > 0
 66			? (promptTokens / promptMs) * 1000
 67			: undefined
 68	);
 69
 70	let promptTimeInSeconds = $derived(
 71		promptMs !== undefined ? (promptMs / 1000).toFixed(2) : undefined
 72	);
 73
 74	let hasPromptStats = $derived(
 75		promptTokens !== undefined &&
 76			promptMs !== undefined &&
 77			promptTokensPerSecond !== undefined &&
 78			promptTimeInSeconds !== undefined
 79	);
 80
 81	// In live mode, generation tab is disabled until we have generation stats
 82	let isGenerationDisabled = $derived(isLive && !hasGenerationStats);
 83</script>
 84
 85<div class="inline-flex items-center text-xs text-muted-foreground">
 86	<div class="inline-flex items-center rounded-sm bg-muted-foreground/15 p-0.5">
 87		{#if hasPromptStats || isLive}
 88			<Tooltip.Root>
 89				<Tooltip.Trigger>
 90					<button
 91						type="button"
 92						class="inline-flex h-5 w-5 items-center justify-center rounded-sm transition-colors {activeView ===
 93						ChatMessageStatsView.READING
 94							? 'bg-background text-foreground shadow-sm'
 95							: 'hover:text-foreground'}"
 96						onclick={() => (activeView = ChatMessageStatsView.READING)}
 97					>
 98						<BookOpenText class="h-3 w-3" />
 99						<span class="sr-only">Reading</span>
100					</button>
101				</Tooltip.Trigger>
102				<Tooltip.Content>
103					<p>Reading (prompt processing)</p>
104				</Tooltip.Content>
105			</Tooltip.Root>
106		{/if}
107		<Tooltip.Root>
108			<Tooltip.Trigger>
109				<button
110					type="button"
111					class="inline-flex h-5 w-5 items-center justify-center rounded-sm transition-colors {activeView ===
112					ChatMessageStatsView.GENERATION
113						? 'bg-background text-foreground shadow-sm'
114						: isGenerationDisabled
115							? 'cursor-not-allowed opacity-40'
116							: 'hover:text-foreground'}"
117					onclick={() => !isGenerationDisabled && (activeView = ChatMessageStatsView.GENERATION)}
118					disabled={isGenerationDisabled}
119				>
120					<Sparkles class="h-3 w-3" />
121					<span class="sr-only">Generation</span>
122				</button>
123			</Tooltip.Trigger>
124			<Tooltip.Content>
125				<p>
126					{isGenerationDisabled
127						? 'Generation (waiting for tokens...)'
128						: 'Generation (token output)'}
129				</p>
130			</Tooltip.Content>
131		</Tooltip.Root>
132	</div>
133
134	<div class="flex items-center gap-1 px-2">
135		{#if activeView === ChatMessageStatsView.GENERATION && hasGenerationStats}
136			<BadgeChatStatistic
137				class="bg-transparent"
138				icon={WholeWord}
139				value="{predictedTokens?.toLocaleString()} tokens"
140				tooltipLabel="Generated tokens"
141			/>
142			<BadgeChatStatistic
143				class="bg-transparent"
144				icon={Clock}
145				value="{timeInSeconds}s"
146				tooltipLabel="Generation time"
147			/>
148			<BadgeChatStatistic
149				class="bg-transparent"
150				icon={Gauge}
151				value="{tokensPerSecond.toFixed(2)} tokens/s"
152				tooltipLabel="Generation speed"
153			/>
154		{:else if hasPromptStats}
155			<BadgeChatStatistic
156				class="bg-transparent"
157				icon={WholeWord}
158				value="{promptTokens} tokens"
159				tooltipLabel="Prompt tokens"
160			/>
161			<BadgeChatStatistic
162				class="bg-transparent"
163				icon={Clock}
164				value="{promptTimeInSeconds}s"
165				tooltipLabel="Prompt processing time"
166			/>
167			<BadgeChatStatistic
168				class="bg-transparent"
169				icon={Gauge}
170				value="{promptTokensPerSecond!.toFixed(2)} tokens/s"
171				tooltipLabel="Prompt processing speed"
172			/>
173		{/if}
174	</div>
175</div>