Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | import { cn } from "@heroui/react" import React, { useEffect, useMemo, useRef, useState } from "react" export const AutoCondenseMarker: React.FC<{ threshold: number usage: number isContextWindowHoverOpen?: boolean shouldAnimate?: boolean }> = ({ threshold, usage, isContextWindowHoverOpen, shouldAnimate = false }) => { const [isAnimating, setIsAnimating] = useState(false) const [animatedPosition, setAnimatedPosition] = useState(0) const [showPercentageAfterAnimation, setShowPercentageAfterAnimation] = useState(false) const [isFadingOut, setIsFadingOut] = useState(false) // Refs to store animation frame and timeout IDs for cleanup const animationFrameRef = useRef<number | null>(null) const fadeOutTimeoutRef = useRef<NodeJS.Timeout | null>(null) const hideTimeoutRef = useRef<NodeJS.Timeout | null>(null) // Animation effect when shouldAnimate prop changes (initial load) useEffect(() => { // Cleanup function to cancel any pending animations or timeouts const cleanup = () => { if (animationFrameRef.current !== null) { cancelAnimationFrame(animationFrameRef.current) animationFrameRef.current = null } if (fadeOutTimeoutRef.current !== null) { clearTimeout(fadeOutTimeoutRef.current) fadeOutTimeoutRef.current = null } if (hideTimeoutRef.current !== null) { clearTimeout(hideTimeoutRef.current) hideTimeoutRef.current = null } } if (shouldAnimate && threshold > 0) { // Clean up any existing animations before starting new one cleanup() setIsAnimating(true) const targetPosition = threshold * 100 const duration = 1200 // ms - slowed down from 800ms const startTime = Date.now() const animate = () => { const elapsed = Date.now() - startTime const progress = Math.min(elapsed / duration, 1) // Ease-out animation curve const easeOut = 1 - (1 - progress) ** 3 const currentPosition = easeOut * targetPosition setAnimatedPosition(currentPosition) if (progress < 1) { animationFrameRef.current = requestAnimationFrame(animate) } else { animationFrameRef.current = null setIsAnimating(false) setShowPercentageAfterAnimation(true) // Start fade out after 1 second fadeOutTimeoutRef.current = setTimeout(() => { setIsFadingOut(true) // Completely hide after fade transition hideTimeoutRef.current = setTimeout(() => { setShowPercentageAfterAnimation(false) setIsFadingOut(false) hideTimeoutRef.current = null setAnimatedPosition(threshold * 100) // Ensure it ends exactly at threshold }, 300) // 300ms fade duration fadeOutTimeoutRef.current = null }, 1000) } } animationFrameRef.current = requestAnimationFrame(animate) } // Cleanup on unmount or when dependencies change return cleanup }, [shouldAnimate, threshold]) // The marker position is calculated based on the threshold percentage // It goes over the progress bar to indicate where the auto-condense will trigger // and it should highlight from what the current percentage (usage) is // to the threshold percentage const marker = useMemo(() => { const _threshold = threshold * 100 // Always use the current threshold for position and label - animation only affects visual movement const position = _threshold const startingPosition = isAnimating ? animatedPosition : position return { start: startingPosition + "%", label: startingPosition.toFixed(0), end: usage > startingPosition ? usage - startingPosition + "%" : 0, } }, [threshold, usage, isAnimating, animatedPosition]) if (!threshold) { return null } return ( <div className="flex-1" id="auto-condense-threshold-marker"> <div className={cn( "absolute top-0 bottom-0 h-full cursor-pointer pointer-events-none z-10 bg-button-background shadow-lg w-1", { "transition-all duration-75": !isAnimating, }, )} style={{ left: marker.start, transform: isAnimating ? `translateX(${animatedPosition - threshold * 100}%)` : "translateX(0)", }}> {(isContextWindowHoverOpen || isAnimating || showPercentageAfterAnimation) && ( <div className={cn("absolute -top-4 -left-1 text-button-background font-mono text-xs", { "opacity-0": isFadingOut, })}> {marker.label}% </div> )} </div> </div> ) } AutoCondenseMarker.displayName = "AutoCondenseMarker" |