Files
James Ritchie 11d8a05fa6 fix(webapp): restore admin debug tooltip on Tasks and Runs, make its IDs copyable (#4332)
Restores the debug panel on the **Tasks** and **Runs** pages, and makes
the data it shows copyable.

Admin/impersonation only — no change for regular users, so there's no
`.server-changes`

<img width="909" height="1420" alt="CleanShot 2026-07-22 at 12 05 27@2x"
src="https://github.com/user-attachments/assets/ce2da167-dc23-422f-83f3-4f4aee9ed32c"
/>
2026-07-22 13:10:31 +01:00

112 lines
3.1 KiB
TypeScript

import { ClipboardCheckIcon, ClipboardIcon } from "lucide-react";
import { useState } from "react";
import { SimpleTooltip } from "~/components/primitives/Tooltip";
import { useCopy } from "~/hooks/useCopy";
import { cn } from "~/utils/cn";
import { Button } from "./Buttons";
export function CopyableText({
value,
copyValue,
className,
asChild,
variant,
hideTooltip,
}: {
value: string;
copyValue?: string;
className?: string;
asChild?: boolean;
variant?: "icon-right" | "text-below";
/**
* Hide the "Copy"/"Copied" hint tooltip. Use when this is rendered inside another
* Radix tooltip (e.g. the admin debug panel): the nested tooltip would otherwise
* fire Radix's global "one tooltip open at a time" close and dismiss the parent.
*/
hideTooltip?: boolean;
}) {
const [isHovered, setIsHovered] = useState(false);
const { copy, copied } = useCopy(copyValue ?? value);
const resolvedVariant = variant ?? "icon-right";
if (resolvedVariant === "icon-right") {
const iconButton = (
<span
className={cn(
"ml-1 flex size-6 items-center justify-center rounded border border-border-bright bg-background-hover",
asChild && "p-1",
copied
? "text-green-500"
: "text-text-dimmed hover:border-border-bright hover:bg-background-raised hover:text-text-bright"
)}
>
{copied ? (
<ClipboardCheckIcon className="size-3.5" />
) : (
<ClipboardIcon className="size-3.5" />
)}
</span>
);
return (
<span
className={cn("group relative inline-flex h-6 items-center", className)}
onMouseLeave={() => setIsHovered(false)}
>
<span onMouseEnter={() => setIsHovered(true)}>{value}</span>
<span
onClick={copy}
onMouseDown={(e) => e.stopPropagation()}
className={cn(
"absolute -right-6 top-0 z-10 size-6 font-sans",
isHovered ? "flex" : "hidden"
)}
>
{hideTooltip ? (
iconButton
) : (
<SimpleTooltip
button={iconButton}
content={copied ? "Copied!" : "Copy"}
className="font-sans"
disableHoverableContent
asChild={asChild}
/>
)}
</span>
</span>
);
}
if (resolvedVariant === "text-below") {
return (
<SimpleTooltip
button={
<Button
variant="minimal/small"
onClick={(e) => {
e.stopPropagation();
copy();
}}
className={cn(
"cursor-pointer bg-transparent px-1 py-0 text-left text-text-dimmed transition-colors hover:bg-transparent",
className
)}
>
<span className="transition-colors group-hover/button:text-text-bright">{value}</span>
</Button>
}
content={copied ? "Copied" : "Copy"}
className="px-2 py-1 font-sans"
disableHoverableContent
open={isHovered || copied}
onOpenChange={setIsHovered}
asChild
/>
);
}
return null;
}