Unified · Split
| @@ -86,6 +86,15 @@ const PlanSidebar = memo(function PlanSidebar({ · 85 unmodified lines | ||
| 86 | 86 | }, 2000); |
| 87 | 87 | }, [planMarkdown]); |
| 88 | 88 | |
| 89 | + // Cleanup timeout on unmount | |
| 90 | + useEffect(() => { | |
| 91 | + return () => { | |
| 92 | + if (copiedTimerRef.current != null) { | |
| 93 | + clearTimeout(copiedTimerRef.current); | |
| 94 | + } | |
| 95 | + }; | |
| 96 | + }, []); | |
| 97 | + | |
| 89 | 98 | const handleDownload = useCallback(() => { |
| 90 | 99 | if (!planMarkdown) return; |
| 91 | 100 | const filename = buildProposedPlanMarkdownFilename(planMarkdown); |
| @@ -128,14 +137,6 @@ const PlanSidebar = memo(function PlanSidebar({ · 36 unmodified lines | ||
| 128 | 137 | {/* Header */} |
| 129 | 138 | <div className="flex h-12 shrink-0 items-center justify-between border-b border-border/60 px-3"> |
| 130 | 139 | <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 | − }, []); | |
| 139 | 140 | <Badge |
| 140 | 141 | variant="secondary" |
| 141 | 142 | className="rounded-md bg-blue-500/10 px-1.5 py-0 text-[10px] font-semibold tracking-wide text-blue-400 uppercase" |
| @@ -190,6 +190,7 @@ export class WsTransport { · 189 unmodified lines | ||
| 190 | 190 | // Log WebSocket errors for debugging (close event will follow) |
| 191 | 191 | console.warn("WebSocket connection error", { type: event.type, url: this.url }); |
| 192 | 192 | }); |
| 193 | + } | |
| 193 | 194 | |
| 194 | 195 | private handleMessage(raw: unknown) { |
| 195 | 196 | const result = decodeWsResponse(raw); |