From b512b3ffadfc492ceaaa8a3df45da3fe35b7233d Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Wed, 22 Jul 2026 10:57:16 +0100 Subject: [PATCH 1/8] fix(webapp): restore admin debug tooltip on Tasks and Runs, make its IDs copyable The admin-only debug tooltip (the shield icon in the page header) was removed from the Tasks page in #3941 and had never been present on the Runs list. Re-add `` to both pages' accessories so it matches the other dashboard pages. Also wrap the tooltip's identifier values (User ID, Org ID, Project ID, Project ref, Environment ID) in `CopyableText` so admins can copy them on hover, reusing the same copy-on-hover control used in table cells. --- .../app/components/admin/debugTooltip.tsx | 21 ++++++++++++++----- .../route.tsx | 2 ++ .../route.tsx | 2 ++ 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/apps/webapp/app/components/admin/debugTooltip.tsx b/apps/webapp/app/components/admin/debugTooltip.tsx index b4ccb74f88d..cbb74caf8a1 100644 --- a/apps/webapp/app/components/admin/debugTooltip.tsx +++ b/apps/webapp/app/components/admin/debugTooltip.tsx @@ -1,4 +1,5 @@ import { ShieldCheckIcon } from "@heroicons/react/20/solid"; +import { CopyableText } from "~/components/primitives/CopyableText"; import * as Property from "~/components/primitives/PropertyTable"; import { Tooltip, @@ -44,23 +45,31 @@ function Content({ children }: { children: React.ReactNode }) { User ID - {user.id} + + + {organization && ( Org ID - {organization.id} + + + )} {project && ( <> Project ID - {project.id} + + + Project ref - {project.externalRef} + + + )} @@ -68,7 +77,9 @@ function Content({ children }: { children: React.ReactNode }) { <> Environment ID - {environment.id} + + + Environment type diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam._index/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam._index/route.tsx index c16e96b8f51..99c73f33849 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam._index/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam._index/route.tsx @@ -16,6 +16,7 @@ import { PlusIcon } from "~/assets/icons/PlusIcon"; import { QuestionMarkIcon } from "~/assets/icons/QuestionMarkIcon"; import { RunsIcon } from "~/assets/icons/RunsIcon"; import { TaskIcon } from "~/assets/icons/TaskIcon"; +import { AdminDebugTooltip } from "~/components/admin/debugTooltip"; import { CodeBlock } from "~/components/code/CodeBlock"; import { InlineCode } from "~/components/code/InlineCode"; import { HasNoTasksDeployed, HasNoTasksDev } from "~/components/BlankStatePanels"; @@ -261,6 +262,7 @@ export default function Page() { } /> + )} + Date: Wed, 22 Jul 2026 11:09:39 +0100 Subject: [PATCH 2/8] fix(webapp): pad the debug tooltip so the copy button isn't clipped The copy button is absolutely positioned to the right of each value (`-right-6`), which pushed it past the popover's right edge where `overflow-y-auto` clipped it. Add right padding so it always shows. --- apps/webapp/app/components/admin/debugTooltip.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/webapp/app/components/admin/debugTooltip.tsx b/apps/webapp/app/components/admin/debugTooltip.tsx index cbb74caf8a1..ede3ed521cc 100644 --- a/apps/webapp/app/components/admin/debugTooltip.tsx +++ b/apps/webapp/app/components/admin/debugTooltip.tsx @@ -26,7 +26,7 @@ export function AdminDebugTooltip({ children }: { children?: React.ReactNode }) - + {children} From 4993929aa47596de241e8fd31752505d227989cf Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Wed, 22 Jul 2026 11:35:56 +0100 Subject: [PATCH 3/8] fix(webapp): make the admin debug panel a hover popover so copy buttons work MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Radix tooltips enforce one-open-at-a-time globally (opening any tooltip dispatches a document event that closes the others), so hovering a copy button — which opens its own "Copy" tooltip — dismissed the debug panel. Render the panel as a hover-triggered Popover instead. A popover holds interactive content without closing, so copy-on-hover now works and the panel stays open while you interact with it. --- .../app/components/admin/debugTooltip.tsx | 61 ++++++++++++++----- 1 file changed, 45 insertions(+), 16 deletions(-) diff --git a/apps/webapp/app/components/admin/debugTooltip.tsx b/apps/webapp/app/components/admin/debugTooltip.tsx index ede3ed521cc..65bc62ead0d 100644 --- a/apps/webapp/app/components/admin/debugTooltip.tsx +++ b/apps/webapp/app/components/admin/debugTooltip.tsx @@ -1,12 +1,8 @@ import { ShieldCheckIcon } from "@heroicons/react/20/solid"; +import { useEffect, useRef, useState } from "react"; import { CopyableText } from "~/components/primitives/CopyableText"; +import { Popover, PopoverContent, PopoverTrigger } from "~/components/primitives/Popover"; import * as Property from "~/components/primitives/PropertyTable"; -import { - Tooltip, - TooltipContent, - TooltipProvider, - TooltipTrigger, -} from "~/components/primitives/Tooltip"; import { useOptionalEnvironment } from "~/hooks/useEnvironment"; import { useIsImpersonating, useOptionalOrganization } from "~/hooks/useOrganizations"; import { useOptionalProject } from "~/hooks/useProject"; @@ -15,22 +11,55 @@ import { useHasAdminAccess, useUser } from "~/hooks/useUser"; export function AdminDebugTooltip({ children }: { children?: React.ReactNode }) { const hasAdminAccess = useHasAdminAccess(); const isImpersonating = useIsImpersonating(); + const [open, setOpen] = useState(false); + const closeTimeout = useRef>(); + + // Clean up any pending close timer on unmount. + useEffect(() => () => clearTimeout(closeTimeout.current), []); if (!hasAdminAccess && !isImpersonating) { return null; } + const openNow = () => { + clearTimeout(closeTimeout.current); + setOpen(true); + }; + + // Delay closing so moving the pointer across the gap into the popover — or onto a + // copy button that opens its own tooltip — doesn't dismiss it. This is a Popover + // rather than a Tooltip on purpose: Radix tooltips only allow one open at a time, so + // a copy button's tooltip would otherwise close this panel. + const closeSoon = () => { + clearTimeout(closeTimeout.current); + closeTimeout.current = setTimeout(() => setOpen(false), 150); + }; + return ( - - - - - - - {children} - - - + + e.preventDefault()} + onMouseEnter={openNow} + onMouseLeave={closeSoon} + onFocus={openNow} + onBlur={closeSoon} + > + + + e.preventDefault()} + onCloseAutoFocus={(e) => e.preventDefault()} + onMouseEnter={openNow} + onMouseLeave={closeSoon} + > + {children} + + ); } From 2ed4a555ac609dc699086f0dfb1b4abecdccd544 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Wed, 22 Jul 2026 11:50:28 +0100 Subject: [PATCH 4/8] fix(webapp): make the per-page debug panel ID rows copyable too The custom rows some pages add to the admin debug panel (Run/Deployment IDs, trace id, env/org ids, image reference, region/branch ids, etc.) were still plain text. Wrap the opaque identifiers in CopyableText across those pages so they match the shared rows and can be copied on hover. Enum/boolean/human-readable rows stay plain (Environment type/paused, deployment Platform, plan name) and the Build Server link is left as-is. Also drop a duplicated project ID that rendered twice in the env settings panel. --- .../route.tsx | 5 ++++- .../route.tsx | 4 +++- .../route.tsx | 5 ++++- .../route.tsx | 17 +++++++++++++---- .../route.tsx | 4 +++- .../route.tsx | 5 ++++- .../route.tsx | 4 +++- .../route.tsx | 16 ++++++++++++---- .../route.tsx | 13 +++++++------ .../route.tsx | 5 ++++- 10 files changed, 57 insertions(+), 21 deletions(-) diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.apikeys/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.apikeys/route.tsx index 839b66f70b3..0b0aa4416eb 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.apikeys/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.apikeys/route.tsx @@ -2,6 +2,7 @@ import { BookOpenIcon } from "@heroicons/react/20/solid"; import { type MetaFunction } from "@remix-run/react"; import { typedjson, useTypedLoaderData } from "remix-typedjson"; import { AdminDebugTooltip } from "~/components/admin/debugTooltip"; +import { CopyableText } from "~/components/primitives/CopyableText"; import { CodeBlock } from "~/components/code/CodeBlock"; import { InlineCode } from "~/components/code/InlineCode"; import { @@ -113,7 +114,9 @@ export default function Page() { {environment.slug} - {environment.id} + + + diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx index 8c1de33c6f6..2b89f405e83 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx @@ -242,7 +242,9 @@ export default function Page() { {branches.map((branch) => ( {branch.branchName} - {branch.id} + + + ))} diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.concurrency/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.concurrency/route.tsx index 1bc359af598..50edeccf402 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.concurrency/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.concurrency/route.tsx @@ -21,6 +21,7 @@ import { typedjson, useTypedLoaderData } from "remix-typedjson"; import simplur from "simplur"; import { z } from "zod"; import { AdminDebugTooltip } from "~/components/admin/debugTooltip"; +import { CopyableText } from "~/components/primitives/CopyableText"; import { EnvironmentCombo } from "~/components/environments/EnvironmentLabel"; import { Feedback } from "~/components/Feedback"; import { @@ -270,7 +271,9 @@ export default function Page() { {environment.type}{" "} {environment.branchName ? ` (${environment.branchName})` : ""} - {environment.id} + + + ))} diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx index 20dd91d34b4..ac7726e7177 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx @@ -18,6 +18,7 @@ import { GitMetadata } from "~/components/GitMetadata"; import { VercelLink } from "~/components/integrations/VercelLink"; import { RuntimeIcon } from "~/components/RuntimeIcon"; import { AdminDebugTooltip } from "~/components/admin/debugTooltip"; +import { CopyableText } from "~/components/primitives/CopyableText"; import { EnvironmentCombo } from "~/components/environments/EnvironmentLabel"; import { Badge } from "~/components/primitives/Badge"; import { LinkButton } from "~/components/primitives/Buttons"; @@ -283,20 +284,28 @@ export default function Page() { ID - {deployment.id} + + + Project ID - {deployment.projectId} + + + Org ID - {deployment.organizationId} + + + {deployment.imageReference && ( Image - {deployment.imageReference} + + + )} diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dev-branches/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dev-branches/route.tsx index 9f704a54f9b..510aac991ce 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dev-branches/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dev-branches/route.tsx @@ -95,7 +95,9 @@ export default function Page() { {branches.map((branch) => ( {branch.branchName} - {branch.id} + + + ))} diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.limits/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.limits/route.tsx index e27b11b7545..c036ee2b0c4 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.limits/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.limits/route.tsx @@ -7,6 +7,7 @@ import { Gauge } from "lucide-react"; import { typedjson, useTypedLoaderData } from "remix-typedjson"; import { ConcurrencyIcon } from "~/assets/icons/ConcurrencyIcon"; import { AdminDebugTooltip } from "~/components/admin/debugTooltip"; +import { CopyableText } from "~/components/primitives/CopyableText"; import { Feedback } from "~/components/Feedback"; import { PageBody, PageContainer } from "~/components/layout/AppLayout"; import { EnvironmentSelector } from "~/components/navigation/EnvironmentSelector"; @@ -125,7 +126,9 @@ export default function Page() { Organization ID - {data.organizationId} + + + diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.regions/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.regions/route.tsx index 7992f28cc92..44d1ee54411 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.regions/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.regions/route.tsx @@ -163,7 +163,9 @@ export default function Page() { {regions.map((region) => ( {region.name} - {region.id} + + + ))} diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx index dc5e8e591c1..2f443b14206 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx @@ -489,19 +489,27 @@ export default function Page() { ID - {run.id} + + + Trace ID - {run.traceId} + + + Env ID - {run.environment.id} + + + Org ID - {run.environment.organizationId} + + + diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx index 1777d50e197..694963a9e7c 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx @@ -2,9 +2,9 @@ import { Outlet, type MetaFunction } from "@remix-run/react"; import { type LoaderFunctionArgs, redirect } from "@remix-run/server-runtime"; import { PageBody, PageContainer } from "~/components/layout/AppLayout"; import { NavBar, PageAccessories, PageTitle } from "~/components/primitives/PageHeader"; -import { Paragraph } from "~/components/primitives/Paragraph"; import * as Property from "~/components/primitives/PropertyTable"; import { AdminDebugTooltip } from "~/components/admin/debugTooltip"; +import { CopyableText } from "~/components/primitives/CopyableText"; import { useProject } from "~/hooks/useProject"; import { requireUserId } from "~/services/session.server"; import { @@ -55,14 +55,15 @@ export default function SettingsLayout() { ID - {project.id} -
- {project.id} -
+ + +
Org ID - {project.organizationId} + + +
diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx index 32920a678ff..33877f6d43f 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx @@ -18,6 +18,7 @@ import { z } from "zod"; import { Feedback } from "~/components/Feedback"; import { UserAvatar } from "~/components/UserProfilePhoto"; import { AdminDebugTooltip } from "~/components/admin/debugTooltip"; +import { CopyableText } from "~/components/primitives/CopyableText"; import { PageBody, PageContainer } from "~/components/layout/AppLayout"; import { Alert, @@ -372,7 +373,9 @@ export default function Page() { Org ID - {organization.id} + + + {members.map((member) => ( From 283135ccca740cbe246f8fcbb590b04a86b8e1ac Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Wed, 22 Jul 2026 12:17:02 +0100 Subject: [PATCH 5/8] chore(webapp): add server-changes note for the admin debug panel --- .server-changes/admin-debug-panel-copyable-ids.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .server-changes/admin-debug-panel-copyable-ids.md diff --git a/.server-changes/admin-debug-panel-copyable-ids.md b/.server-changes/admin-debug-panel-copyable-ids.md new file mode 100644 index 00000000000..3f498ff397d --- /dev/null +++ b/.server-changes/admin-debug-panel-copyable-ids.md @@ -0,0 +1,6 @@ +--- +area: webapp +type: fix +--- + +Make IDs in the admin debug panel copyable, and restore it on the Tasks and Runs pages. From 1b9b15876b512562f5b3fa8d3ac934f9fa6f324a Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Wed, 22 Jul 2026 12:24:26 +0100 Subject: [PATCH 6/8] chore(webapp): drop the server-changes note (admin-only, not user-facing) --- .server-changes/admin-debug-panel-copyable-ids.md | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 .server-changes/admin-debug-panel-copyable-ids.md diff --git a/.server-changes/admin-debug-panel-copyable-ids.md b/.server-changes/admin-debug-panel-copyable-ids.md deleted file mode 100644 index 3f498ff397d..00000000000 --- a/.server-changes/admin-debug-panel-copyable-ids.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -area: webapp -type: fix ---- - -Make IDs in the admin debug panel copyable, and restore it on the Tasks and Runs pages. From 5af5b8c150630313028c6ead34e20ee195e61433 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Wed, 22 Jul 2026 12:29:48 +0100 Subject: [PATCH 7/8] fix(webapp): hide the debug panel's extra-rows divider when there are no children --- apps/webapp/app/components/admin/debugTooltip.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/webapp/app/components/admin/debugTooltip.tsx b/apps/webapp/app/components/admin/debugTooltip.tsx index 65bc62ead0d..cd185c1e7ed 100644 --- a/apps/webapp/app/components/admin/debugTooltip.tsx +++ b/apps/webapp/app/components/admin/debugTooltip.tsx @@ -121,7 +121,7 @@ function Content({ children }: { children: React.ReactNode }) { )} -
{children}
+ {children &&
{children}
} ); } From 1e9d3d314edcf57ffb72f40ffadb4f0f141b45bd Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Wed, 22 Jul 2026 12:56:47 +0100 Subject: [PATCH 8/8] refactor(webapp): suppress copy-button tooltips via hideTooltip instead of a Popover Add a hideTooltip prop to CopyableText that renders the copy control without its own tooltip. The admin debug panel passes it on every copy control, which removes the nested tooltip that was firing Radix's global 'one tooltip open at a time' close. With the root cause gone the panel reverts from a Popover back to a plain Tooltip (no manual open/close timer or effect needed). --- .../app/components/admin/debugTooltip.tsx | 74 ++++++------------- .../components/primitives/CopyableText.tsx | 59 +++++++++------ .../route.tsx | 2 +- .../route.tsx | 2 +- .../route.tsx | 2 +- .../route.tsx | 8 +- .../route.tsx | 2 +- .../route.tsx | 2 +- .../route.tsx | 2 +- .../route.tsx | 8 +- .../route.tsx | 4 +- .../route.tsx | 2 +- 12 files changed, 77 insertions(+), 90 deletions(-) diff --git a/apps/webapp/app/components/admin/debugTooltip.tsx b/apps/webapp/app/components/admin/debugTooltip.tsx index cd185c1e7ed..4157f898163 100644 --- a/apps/webapp/app/components/admin/debugTooltip.tsx +++ b/apps/webapp/app/components/admin/debugTooltip.tsx @@ -1,8 +1,12 @@ import { ShieldCheckIcon } from "@heroicons/react/20/solid"; -import { useEffect, useRef, useState } from "react"; import { CopyableText } from "~/components/primitives/CopyableText"; -import { Popover, PopoverContent, PopoverTrigger } from "~/components/primitives/Popover"; import * as Property from "~/components/primitives/PropertyTable"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "~/components/primitives/Tooltip"; import { useOptionalEnvironment } from "~/hooks/useEnvironment"; import { useIsImpersonating, useOptionalOrganization } from "~/hooks/useOrganizations"; import { useOptionalProject } from "~/hooks/useProject"; @@ -11,55 +15,25 @@ import { useHasAdminAccess, useUser } from "~/hooks/useUser"; export function AdminDebugTooltip({ children }: { children?: React.ReactNode }) { const hasAdminAccess = useHasAdminAccess(); const isImpersonating = useIsImpersonating(); - const [open, setOpen] = useState(false); - const closeTimeout = useRef>(); - - // Clean up any pending close timer on unmount. - useEffect(() => () => clearTimeout(closeTimeout.current), []); if (!hasAdminAccess && !isImpersonating) { return null; } - const openNow = () => { - clearTimeout(closeTimeout.current); - setOpen(true); - }; - - // Delay closing so moving the pointer across the gap into the popover — or onto a - // copy button that opens its own tooltip — doesn't dismiss it. This is a Popover - // rather than a Tooltip on purpose: Radix tooltips only allow one open at a time, so - // a copy button's tooltip would otherwise close this panel. - const closeSoon = () => { - clearTimeout(closeTimeout.current); - closeTimeout.current = setTimeout(() => setOpen(false), 150); - }; - return ( - - e.preventDefault()} - onMouseEnter={openNow} - onMouseLeave={closeSoon} - onFocus={openNow} - onBlur={closeSoon} - > - - - e.preventDefault()} - onCloseAutoFocus={(e) => e.preventDefault()} - onMouseEnter={openNow} - onMouseLeave={closeSoon} - > - {children} - - + + + + + + {/* The copy controls below pass `hideTooltip` so their own tooltips don't fire + Radix's global close and dismiss this panel. `pr-8` leaves room for the + copy button, which is absolutely positioned to the right of each value. */} + + {children} + + + ); } @@ -75,14 +49,14 @@ function Content({ children }: { children: React.ReactNode }) { User ID - + {organization && ( Org ID - + )} @@ -91,13 +65,13 @@ function Content({ children }: { children: React.ReactNode }) { Project ID - + Project ref - + @@ -107,7 +81,7 @@ function Content({ children }: { children: React.ReactNode }) { Environment ID - + diff --git a/apps/webapp/app/components/primitives/CopyableText.tsx b/apps/webapp/app/components/primitives/CopyableText.tsx index f8e19402aed..9634ad1ac01 100644 --- a/apps/webapp/app/components/primitives/CopyableText.tsx +++ b/apps/webapp/app/components/primitives/CopyableText.tsx @@ -11,12 +11,19 @@ export function CopyableText({ 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); @@ -24,6 +31,24 @@ export function CopyableText({ const resolvedVariant = variant ?? "icon-right"; if (resolvedVariant === "icon-right") { + const iconButton = ( + + {copied ? ( + + ) : ( + + )} + + ); + return ( - - {copied ? ( - - ) : ( - - )} - - } - content={copied ? "Copied!" : "Copy"} - className="font-sans" - disableHoverableContent - asChild={asChild} - /> + {hideTooltip ? ( + iconButton + ) : ( + + )} ); diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.apikeys/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.apikeys/route.tsx index 0b0aa4416eb..1d6b12bd2d0 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.apikeys/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.apikeys/route.tsx @@ -115,7 +115,7 @@ export default function Page() { {environment.slug} - + diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx index 2b89f405e83..909a8bb6381 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx @@ -243,7 +243,7 @@ export default function Page() { {branch.branchName} - + ))} diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.concurrency/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.concurrency/route.tsx index 50edeccf402..e9542b5c8f5 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.concurrency/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.concurrency/route.tsx @@ -272,7 +272,7 @@ export default function Page() { {environment.branchName ? ` (${environment.branchName})` : ""} - + ))} diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx index ac7726e7177..c4950277288 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx @@ -285,26 +285,26 @@ export default function Page() { ID - + Project ID - + Org ID - + {deployment.imageReference && ( Image - + )} diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dev-branches/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dev-branches/route.tsx index 510aac991ce..dd2679085e4 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dev-branches/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dev-branches/route.tsx @@ -96,7 +96,7 @@ export default function Page() { {branch.branchName} - + ))} diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.limits/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.limits/route.tsx index c036ee2b0c4..7ace6919a55 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.limits/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.limits/route.tsx @@ -127,7 +127,7 @@ export default function Page() { Organization ID - + diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.regions/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.regions/route.tsx index 44d1ee54411..588647f22a7 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.regions/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.regions/route.tsx @@ -164,7 +164,7 @@ export default function Page() { {region.name} - + ))} diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx index 2f443b14206..95dbe5f7926 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx @@ -490,25 +490,25 @@ export default function Page() { ID - + Trace ID - + Env ID - + Org ID - + diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx index 694963a9e7c..f99a14f336c 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx @@ -56,13 +56,13 @@ export default function SettingsLayout() { ID - + Org ID - + diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx index 33877f6d43f..3e11636aeec 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx @@ -374,7 +374,7 @@ export default function Page() { Org ID - +