source dump of claude code
at main 31 lines 1.2 kB view raw
1import { useMemo } from 'react' 2import { stringWidth } from '../../ink/stringWidth.js' 3import { type DOMElement, useAnimationFrame } from '../../ink.js' 4import type { SpinnerMode } from './types.js' 5 6export function useShimmerAnimation( 7 mode: SpinnerMode, 8 message: string, 9 isStalled: boolean, 10): [ref: (element: DOMElement | null) => void, glimmerIndex: number] { 11 const glimmerSpeed = mode === 'requesting' ? 50 : 200 12 // Pass null when stalled to unsubscribe from the clock — otherwise the 13 // setInterval keeps firing at 20fps even when the shimmer isn't visible. 14 // Notably, if the caller never attaches `ref` (e.g. conditional JSX), 15 // useTerminalViewport stays at its initial isVisible:true and the 16 // viewport-pause never kicks in, so this is the only stop mechanism. 17 const [ref, time] = useAnimationFrame(isStalled ? null : glimmerSpeed) 18 const messageWidth = useMemo(() => stringWidth(message), [message]) 19 20 if (isStalled) { 21 return [ref, -100] 22 } 23 24 const cyclePosition = Math.floor(time / glimmerSpeed) 25 const cycleLength = messageWidth + 20 26 27 if (mode === 'requesting') { 28 return [ref, (cyclePosition % cycleLength) - 10] 29 } 30 return [ref, messageWidth + 10 - (cyclePosition % cycleLength)] 31}