import { IconArrowRight } from "@tabler/icons-react" import { useEffect, useRef, useState } from "react" import { useTranslation } from "react-i18next" import type { ContextUsage } from "@/store/chat" interface ContextUsageRingProps { usage: ContextUsage onDetailClick?: () => void } function formatTokens(n: number): string { if (n >= 1000) return `${(n / 1000).toFixed(1)}k` return String(n) } export function ContextUsageRing({ usage, onDetailClick, }: ContextUsageRingProps) { const { t } = useTranslation() const [intent, setIntent] = useState(false) // user wants open const [visible, setVisible] = useState(false) // DOM mounted const [animated, setAnimated] = useState(false) // CSS target state const [cooldown, setCooldown] = useState(false) const containerRef = useRef(null) const timerRef = useRef>(null) const hoverIntent = useRef>(null) const closeTimer = useRef>(null) useEffect(() => { if (intent) { // Mount first, animate in on next frame if (closeTimer.current) clearTimeout(closeTimer.current) setVisible(true) requestAnimationFrame(() => { requestAnimationFrame(() => setAnimated(true)) }) } else if (visible) { // Animate out, then unmount setAnimated(false) closeTimer.current = setTimeout(() => setVisible(false), 150) } }, [intent, visible]) useEffect(() => { return () => { if (timerRef.current) clearTimeout(timerRef.current) if (hoverIntent.current) clearTimeout(hoverIntent.current) if (closeTimer.current) clearTimeout(closeTimer.current) } }, []) const percent = Math.min(usage.used_percent, 100) const radius = 8 const circumference = 2 * Math.PI * radius const offset = circumference - (percent / 100) * circumference const barPercent = Math.min(percent, 100) const handleDetail = () => { if (cooldown || !onDetailClick) return setCooldown(true) onDetailClick() setIntent(false) timerRef.current = setTimeout(() => setCooldown(false), 1000) } // Desktop: hover to open, mouse leave to close (with small delay) const handleMouseEnter = () => { if (hoverIntent.current) clearTimeout(hoverIntent.current) setIntent(true) } const handleMouseLeave = () => { hoverIntent.current = setTimeout(() => setIntent(false), 150) } // Mobile: tap to toggle (preventDefault suppresses synthetic mouseenter) const handleTouchStart = (e: React.TouchEvent) => { e.preventDefault() setIntent((v) => !v) } return (
{visible && (
{t("chat.contextTitle")} {formatTokens(usage.used_tokens)} /{" "} {formatTokens(usage.compress_at_tokens)}
)}
) }