diff --git a/apps/web/src/app.css b/apps/web/src/app.css index 66ae7296a..38feb7cfe 100644 --- a/apps/web/src/app.css +++ b/apps/web/src/app.css @@ -47,6 +47,8 @@ flex: 1; min-width: 0; flex-direction: column; + min-height: 0; + overflow-y: auto; } .shell-main-content { diff --git a/apps/web/src/pages/agents-page.tsx b/apps/web/src/pages/agents-page.tsx index 96efdcb9c..a61f24819 100644 --- a/apps/web/src/pages/agents-page.tsx +++ b/apps/web/src/pages/agents-page.tsx @@ -18,7 +18,7 @@ import { } from "@corbits/react-ui"; import type { BadgeTone, ViewMode } from "@corbits/react-ui"; import { Bot, Copy, Workflow } from "lucide-react"; -import { useEffect, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import type { ReactNode } from "react"; import { useQueryClient } from "@tanstack/react-query"; @@ -59,6 +59,16 @@ const INSTANCE_CAP = 4; * visible text anywhere on this surface. */ function CopyAddressButton({ address }: { readonly address: string }) { const [copied, setCopied] = useState(false); + // The "Copied" confirmation clears itself after a timeout. Track that + // timer so unmounting the button (switching tabs, leaving the page) can + // cancel it — otherwise the callback fires setState on an unmounted + // component, the classic leak. + const resetTimer = useRef | null>(null); + useEffect(() => { + return () => { + if (resetTimer.current !== null) clearTimeout(resetTimer.current); + }; + }, []); return (