import { useEffect, useState } from 'react' import { RefreshCw } from 'lucide-react' import { useChatStore } from '../../stores/chatStore' import { useTabStore } from '../../stores/tabStore' import { useTranslation, type TranslationKey } from '../../i18n' function formatElapsed(seconds: number): string { if (seconds < 60) return `${seconds}s` const m = Math.floor(seconds / 60) const s = seconds % 60 return `${m}m ${s}s` } function translateServerVerb( t: (key: TranslationKey) => string, verb: string, ): string { const key = `serverVerb.${verb}` as TranslationKey const translated = t(key) return translated === key ? verb : translated } function formatRetrySeconds(ms: number): number { return Math.max(0, Math.ceil(ms / 1000)) } function formatErrorType(errorType: string | undefined): string | null { if (!errorType) return null return errorType .replace(/[_-]+/g, ' ') .replace(/\s+/g, ' ') .trim() } export function StreamingIndicator() { const t = useTranslation() const [now, setNow] = useState(() => Date.now()) const activeTabId = useTabStore((s) => s.activeTabId) const sessionState = useChatStore((s) => activeTabId ? s.sessions[activeTabId] : undefined) const chatState = sessionState?.chatState ?? 'idle' const statusVerb = sessionState?.statusVerb ?? '' const apiRetry = sessionState?.apiRetry ?? null const elapsedSeconds = sessionState?.elapsedSeconds ?? 0 const tokenUsage = sessionState?.tokenUsage ?? { input_tokens: 0, output_tokens: 0 } useEffect(() => { if (!apiRetry) return undefined setNow(Date.now()) const timer = window.setInterval(() => setNow(Date.now()), 1000) return () => window.clearInterval(timer) }, [apiRetry?.receivedAt, apiRetry?.retryDelayMs]) if (apiRetry) { const remainingMs = Math.max(0, apiRetry.retryDelayMs - (now - apiRetry.receivedAt)) const statusText = apiRetry.errorStatus !== null ? t('chat.retry.httpStatus', { status: apiRetry.errorStatus }) : formatErrorType(apiRetry.errorType) ?? t('chat.retry.networkError') const detailText = apiRetry.errorMessage?.trim() return (
) } let verb: string if (statusVerb) { verb = translateServerVerb(t, statusVerb) } else { verb = chatState === 'thinking' ? t('serverVerb.Thinking') : chatState === 'compacting' ? t('serverVerb.Compacting conversation') : chatState === 'tool_executing' ? t('serverVerb.Running') : t('serverVerb.Working') } return (
{verb}... {elapsedSeconds > 0 && ( {formatElapsed(elapsedSeconds)} )} {tokenUsage.output_tokens > 0 && ( · ↓ {tokenUsage.output_tokens} )}
) }