1<script lang="ts">
  2	import { untrack } from 'svelte';
  3	import { PROCESSING_INFO_TIMEOUT } from '$lib/constants/processing-info';
  4	import { useProcessingState } from '$lib/hooks/use-processing-state.svelte';
  5	import { chatStore, isLoading, isChatStreaming } from '$lib/stores/chat.svelte';
  6	import { activeMessages, activeConversation } from '$lib/stores/conversations.svelte';
  7	import { config } from '$lib/stores/settings.svelte';
  8
  9	const processingState = useProcessingState();
 10
 11	let isCurrentConversationLoading = $derived(isLoading());
 12	let isStreaming = $derived(isChatStreaming());
 13	let hasProcessingData = $derived(processingState.processingState !== null);
 14	let processingDetails = $derived(processingState.getProcessingDetails());
 15
 16	let showProcessingInfo = $derived(
 17		isCurrentConversationLoading || isStreaming || config().keepStatsVisible || hasProcessingData
 18	);
 19
 20	$effect(() => {
 21		const conversation = activeConversation();
 22
 23		untrack(() => chatStore.setActiveProcessingConversation(conversation?.id ?? null));
 24	});
 25
 26	$effect(() => {
 27		const keepStatsVisible = config().keepStatsVisible;
 28		const shouldMonitor = keepStatsVisible || isCurrentConversationLoading || isStreaming;
 29
 30		if (shouldMonitor) {
 31			processingState.startMonitoring();
 32		}
 33
 34		if (!isCurrentConversationLoading && !isStreaming && !keepStatsVisible) {
 35			const timeout = setTimeout(() => {
 36				if (!config().keepStatsVisible && !isChatStreaming()) {
 37					processingState.stopMonitoring();
 38				}
 39			}, PROCESSING_INFO_TIMEOUT);
 40
 41			return () => clearTimeout(timeout);
 42		}
 43	});
 44
 45	$effect(() => {
 46		const conversation = activeConversation();
 47		const messages = activeMessages() as DatabaseMessage[];
 48		const keepStatsVisible = config().keepStatsVisible;
 49
 50		if (keepStatsVisible && conversation) {
 51			if (messages.length === 0) {
 52				untrack(() => chatStore.clearProcessingState(conversation.id));
 53				return;
 54			}
 55
 56			if (!isCurrentConversationLoading && !isStreaming) {
 57				untrack(() => chatStore.restoreProcessingStateFromMessages(messages, conversation.id));
 58			}
 59		}
 60	});
 61</script>
 62
 63<div class="chat-processing-info-container pointer-events-none" class:visible={showProcessingInfo}>
 64	<div class="chat-processing-info-content">
 65		{#each processingDetails as detail (detail)}
 66			<span class="chat-processing-info-detail pointer-events-auto">{detail}</span>
 67		{/each}
 68	</div>
 69</div>
 70
 71<style>
 72	.chat-processing-info-container {
 73		position: sticky;
 74		top: 0;
 75		z-index: 10;
 76		padding: 1.5rem 1rem;
 77		opacity: 0;
 78		transform: translateY(50%);
 79		transition:
 80			opacity 300ms ease-out,
 81			transform 300ms ease-out;
 82	}
 83
 84	.chat-processing-info-container.visible {
 85		opacity: 1;
 86		transform: translateY(0);
 87	}
 88
 89	.chat-processing-info-content {
 90		display: flex;
 91		flex-wrap: wrap;
 92		align-items: center;
 93		gap: 1rem;
 94		justify-content: center;
 95		max-width: 48rem;
 96		margin: 0 auto;
 97	}
 98
 99	.chat-processing-info-detail {
100		color: var(--muted-foreground);
101		font-size: 0.75rem;
102		padding: 0.25rem 0.75rem;
103		background: var(--muted);
104		border-radius: 0.375rem;
105		font-family:
106			ui-monospace, SFMono-Regular, 'SF Mono', Consolas, 'Liberation Mono', Menlo, monospace;
107		white-space: nowrap;
108	}
109
110	@media (max-width: 768px) {
111		.chat-processing-info-content {
112			gap: 0.5rem;
113		}
114
115		.chat-processing-info-detail {
116			font-size: 0.7rem;
117			padding: 0.2rem 0.5rem;
118		}
119	}
120</style>