| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | | import { memo, useState, useCallback } from "react"; |
| 1 | import { memo, useState, useCallback, useRef, useEffect } from "react"; |
| 2 | 2 | import { Badge } from "./ui/badge"; |
| 3 | 3 | import { Button } from "./ui/button"; |
| 4 | 4 | import { ScrollArea } from "./ui/scroll-area"; |
| ... | ... | @@ -66,6 +66,7 @@ const PlanSidebar = memo(function PlanSidebar({ |
| 66 | 66 | const [proposedPlanExpanded, setProposedPlanExpanded] = useState(false); |
| 67 | 67 | const [isSavingToWorkspace, setIsSavingToWorkspace] = useState(false); |
| 68 | 68 | const [copied, setCopied] = useState(false); |
| 69 | const copiedTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null); |
| 69 | 70 | |
| 70 | 71 | const planMarkdown = activeProposedPlan?.planMarkdown ?? null; |
| 71 | 72 | const displayedPlanMarkdown = planMarkdown ? stripDisplayedPlanMarkdown(planMarkdown) : null; |
| ... | ... | @@ -74,8 +75,15 @@ const PlanSidebar = memo(function PlanSidebar({ |
| 74 | 75 | const handleCopyPlan = useCallback(() => { |
| 75 | 76 | if (!planMarkdown) return; |
| 76 | 77 | void navigator.clipboard.writeText(planMarkdown); |
| 78 | if (copiedTimerRef.current != null) { |
| 79 | clearTimeout(copiedTimerRef.current); |
| 80 | } |
| 81 | |
| 77 | 82 | setCopied(true); |
| 78 | | setTimeout(() => setCopied(false), 2000); |
| 83 | copiedTimerRef.current = setTimeout(() => { |
| 84 | setCopied(false); |
| 85 | copiedTimerRef.current = null; |
| 86 | }, 2000); |
| 79 | 87 | }, [planMarkdown]); |
| 80 | 88 | |
| 81 | 89 | const handleDownload = useCallback(() => { |
| ... | ... | @@ -120,6 +128,14 @@ const PlanSidebar = memo(function PlanSidebar({ |
| 120 | 128 | {/* Header */} |
| 121 | 129 | <div className="flex h-12 shrink-0 items-center justify-between border-b border-border/60 px-3"> |
| 122 | 130 | <div className="flex items-center gap-2"> |
| 131 | // Cleanup timeout on unmount |
| 132 | useEffect(() => { |
| 133 | return () => { |
| 134 | if (copiedTimerRef.current \!= null) { |
| 135 | clearTimeout(copiedTimerRef.current); |
| 136 | } |
| 137 | }; |
| 138 | }, []); |
| 123 | 139 | <Badge |
| 124 | 140 | variant="secondary" |
| 125 | 141 | className="rounded-md bg-blue-500/10 px-1.5 py-0 text-[10px] font-semibold tracking-wide text-blue-400 uppercase" |