diff --git a/.gitignore b/.gitignore index 1a0fe55d..700c7053 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,7 @@ yarn-error.log* .pnpm-store/ .pnpm-debug.log .idea +*.har # Playwright node_modules/ @@ -54,3 +55,4 @@ CLAUDE.local.md claude.md HANDOVER*.md TASKS.md +*.agents.md diff --git a/src/components/LoadingLogo.tsx b/src/components/LoadingLogo.tsx index 2f14d132..1f503e0c 100644 --- a/src/components/LoadingLogo.tsx +++ b/src/components/LoadingLogo.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from 'react' +import { useCallback, useEffect, useRef, useState } from 'react' import { createPortal } from 'react-dom' import { motion, useAnimationControls } from 'framer-motion' import { EASE_OUT_QUINT_TUPLE, EASE_IN_OUT_QUINT_TUPLE } from '../lib/animations' @@ -24,8 +24,11 @@ interface LoadingLogoProps { export default function LoadingLogo({ text, done, exitMode = 'none', onExitComplete }: LoadingLogoProps) { const reducedMotion = useReducedMotion() const [visible, setVisible] = useState(true) + const [statusRevealed, setStatusRevealed] = useState(false) const showBackground = exitMode !== 'none' const containerRef = useRef(null) + const tapCountRef = useRef(0) + const tapTimerRef = useRef>() const flyControls = useAnimationControls() const flyControlsRef = useRef(flyControls) flyControlsRef.current = flyControls @@ -38,11 +41,29 @@ export default function LoadingLogo({ text, done, exitMode = 'none', onExitCompl reducedMotion, }) + const handleLogoTap = useCallback(() => { + tapCountRef.current += 1 + clearTimeout(tapTimerRef.current) + if (tapCountRef.current >= 3) { + tapCountRef.current = 0 + setStatusRevealed((v) => !v) + } else { + tapTimerRef.current = setTimeout(() => { + tapCountRef.current = 0 + }, 600) + } + }, []) + // When done signal arrives, request the bounce loop to stop useEffect(() => { if (done) requestStop() }, [done, requestStop]) + // Clean up tap timer on unmount + useEffect(() => { + return () => clearTimeout(tapTimerRef.current) + }, []) + // When bounce loop has stopped, play exit animation useEffect(() => { if (!stopped || !done) return @@ -141,23 +162,25 @@ export default function LoadingLogo({ text, done, exitMode = 'none', onExitCompl gap: '1rem', }} > - +
- - + + + + + - - - {text ? ( +
+ {text && statusRevealed ? ( ) : null} - {showBackground ? ( + {showBackground && statusRevealed ? (