diff --git a/apps/web/src/components/PlanSidebar.tsx b/apps/web/src/components/PlanSidebar.tsx index c465f3989..2d898b009 100644 --- a/apps/web/src/components/PlanSidebar.tsx +++ b/apps/web/src/components/PlanSidebar.tsx @@ -1,4 +1,4 @@ -import { memo, useState, useCallback } from "react"; +import { memo, useState, useCallback, useRef, useEffect } from "react"; import { Badge } from "./ui/badge"; import { Button } from "./ui/button"; import { ScrollArea } from "./ui/scroll-area"; @@ -66,6 +66,7 @@ const PlanSidebar = memo(function PlanSidebar({ const [proposedPlanExpanded, setProposedPlanExpanded] = useState(false); const [isSavingToWorkspace, setIsSavingToWorkspace] = useState(false); const [copied, setCopied] = useState(false); + const copiedTimerRef = useRef | null>(null); const planMarkdown = activeProposedPlan?.planMarkdown ?? null; const displayedPlanMarkdown = planMarkdown ? stripDisplayedPlanMarkdown(planMarkdown) : null; @@ -74,8 +75,15 @@ const PlanSidebar = memo(function PlanSidebar({ const handleCopyPlan = useCallback(() => { if (!planMarkdown) return; void navigator.clipboard.writeText(planMarkdown); + if (copiedTimerRef.current != null) { + clearTimeout(copiedTimerRef.current); + } + setCopied(true); - setTimeout(() => setCopied(false), 2000); + copiedTimerRef.current = setTimeout(() => { + setCopied(false); + copiedTimerRef.current = null; + }, 2000); }, [planMarkdown]); const handleDownload = useCallback(() => { @@ -120,6 +128,14 @@ const PlanSidebar = memo(function PlanSidebar({ {/* Header */}
+ // Cleanup timeout on unmount + useEffect(() => { + return () => { + if (copiedTimerRef.current \!= null) { + clearTimeout(copiedTimerRef.current); + } + }; + }, []);