diff --git a/components/practice-cards.tsx b/components/practice-cards.tsx index 45f6381..cff604c 100644 --- a/components/practice-cards.tsx +++ b/components/practice-cards.tsx @@ -21,6 +21,7 @@ import type { ArtifactPartProps } from "@/src/core"; */ import { useEffect, useRef, useState } from "react"; +import { createPortal } from "react-dom"; import { ThinkingBlock } from "@/components/thinking-block"; import { usePopoverDismiss } from "@/components/popover-dismiss"; @@ -177,7 +178,12 @@ function MetadataPopover({ problemInput: { mode?: unknown; target?: unknown }; }) { const [open, setOpen] = useState(false); + const triggerRef = useRef(null); const rootRef = useRef(null); + // The portal's anchor (owner fix, 2026-10-06): the box is placed from + // the trigger's viewport rect AT OPEN TIME — any scroll closes the + // box, so there is nothing to track. + const [anchor, setAnchor] = useState<{ top: number; left: number } | null>(null); const [names, setNames] = useState | null>(null); const targets = Array.isArray(problemInput.target) ? problemInput.target.filter((t): t is string => typeof t === "string") @@ -216,19 +222,39 @@ function MetadataPopover({ - {open && ( -
+ {open && + anchor && + createPortal( +
Mode
{mode || "—"}
@@ -256,8 +282,9 @@ function MetadataPopover({
Retest
{retest === undefined ? "—" : retest ? "yes" : "no"}
-
- )} +
, + document.body, + )} ); }