Skip to content

Commit fd54575

Browse files
samejrericallam
authored andcommitted
Improves the title bar copy behaviour
1 parent a0c598a commit fd54575

1 file changed

Lines changed: 118 additions & 10 deletions

File tree

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx

Lines changed: 118 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as Ariakit from "@ariakit/react";
2-
import { ArrowPathIcon } from "@heroicons/react/20/solid";
2+
import { ArrowPathIcon, ChevronUpDownIcon } from "@heroicons/react/20/solid";
33
import { DialogClose } from "@radix-ui/react-dialog";
44
import { type MetaFunction, useFetcher } from "@remix-run/react";
55
import {
@@ -21,6 +21,7 @@ import { AppliedFilter } from "~/components/primitives/AppliedFilter";
2121
import { Badge } from "~/components/primitives/Badge";
2222
import { Button } from "~/components/primitives/Buttons";
2323
import { CopyButton } from "~/components/primitives/CopyButton";
24+
import { ClipboardCheckIcon, ClipboardIcon } from "lucide-react";
2425
import { CopyableText } from "~/components/primitives/CopyableText";
2526
import { DateTime } from "~/components/primitives/DateTime";
2627
import { Dialog, DialogContent, DialogHeader } from "~/components/primitives/Dialog";
@@ -31,6 +32,7 @@ import { InputGroup } from "~/components/primitives/InputGroup";
3132
import { Label } from "~/components/primitives/Label";
3233
import { NavBar, PageAccessories, PageTitle } from "~/components/primitives/PageHeader";
3334
import { Paragraph } from "~/components/primitives/Paragraph";
35+
import { Popover, PopoverContent, PopoverTrigger } from "~/components/primitives/Popover";
3436
import * as Property from "~/components/primitives/PropertyTable";
3537
import {
3638
ResizableHandle,
@@ -71,8 +73,10 @@ import { InlineCode } from "~/components/code/InlineCode";
7173
import { InfoPanel } from "~/components/primitives/InfoPanel";
7274
import { TextLink } from "~/components/primitives/TextLink";
7375
import { MetricWidget } from "~/routes/resources.metric";
76+
import { cn } from "~/utils/cn";
7477
import { EnvironmentParamSchema, v3PromptsPath, v3RunSpanPath } from "~/utils/pathBuilder";
7578
import { parsePeriodToMs } from "~/utils/periods";
79+
import { SimpleTooltip } from "~/components/primitives/Tooltip";
7680

7781
const ParamSchema = EnvironmentParamSchema.extend({
7882
promptSlug: z.string(),
@@ -473,12 +477,11 @@ export default function PromptDetailPage() {
473477
<NavBar>
474478
<PageTitle
475479
title={
476-
<div className="flex items-center gap-2">
477-
<span>{prompt.slug}</span>
478-
<span className="font-mono text-xs text-text-dimmed">
479-
<CopyableText value={prompt.friendlyId} />
480-
</span>
481-
</div>
480+
<PromptCopyPopover
481+
slug={prompt.slug}
482+
friendlyId={prompt.friendlyId}
483+
description={prompt.description}
484+
/>
482485
}
483486
backButton={{ to: v3PromptsPath(organization, project, environment), text: "Prompts" }}
484487
/>
@@ -495,7 +498,7 @@ export default function PromptDetailPage() {
495498
)}
496499
{selectedVersion && !isCurrent && selectedVersion.source === "code" && (
497500
<Button
498-
variant="tertiary/small"
501+
variant="secondary/small"
499502
onClick={() => handlePromote(selectedVersion.id)}
500503
disabled={fetcher.state !== "idle"}
501504
>
@@ -506,7 +509,7 @@ export default function PromptDetailPage() {
506509
selectedVersion.source !== "code" &&
507510
!selectedVersion.labels.includes("override") && (
508511
<Button
509-
variant="tertiary/small"
512+
variant="secondary/small"
510513
onClick={() =>
511514
fetcher.submit(
512515
{ intent: "reactivateOverride", versionId: selectedVersion.id },
@@ -519,7 +522,7 @@ export default function PromptDetailPage() {
519522
</Button>
520523
)}
521524
{!overrideVersion && (
522-
<Button variant="tertiary/small" onClick={() => setOverrideDialogOpen(true)}>
525+
<Button variant="secondary/small" onClick={() => setOverrideDialogOpen(true)}>
523526
Create Override
524527
</Button>
525528
)}
@@ -1950,3 +1953,108 @@ function VersionsTab({
19501953
</div>
19511954
);
19521955
}
1956+
1957+
const MAX_DESCRIPTION_PREVIEW = 80;
1958+
1959+
function PromptCopyPopover({
1960+
slug,
1961+
friendlyId,
1962+
description,
1963+
}: {
1964+
slug: string;
1965+
friendlyId: string;
1966+
description: string | null;
1967+
}) {
1968+
const [open, setOpen] = useState(false);
1969+
1970+
return (
1971+
<Popover open={open} onOpenChange={setOpen}>
1972+
<PopoverTrigger className="-ml-1.5 flex items-center gap-1 rounded py-1.5 pl-2 pr-1.5 font-mono text-xs text-text-dimmed transition focus-custom hover:bg-charcoal-750 hover:text-text-bright">
1973+
{slug}
1974+
<ChevronUpDownIcon className="size-4 text-charcoal-500" />
1975+
</PopoverTrigger>
1976+
<PopoverContent
1977+
align="start"
1978+
className="flex min-w-0 flex-col p-1"
1979+
onOpenAutoFocus={(e) => {
1980+
e.preventDefault();
1981+
const el = e.currentTarget as HTMLElement;
1982+
el.style.pointerEvents = "none";
1983+
requestAnimationFrame(() => {
1984+
el.style.pointerEvents = "";
1985+
});
1986+
}}
1987+
>
1988+
<CopyPopoverItem label="Copy slug" value={slug} onCopied={() => setOpen(false)} />
1989+
<CopyPopoverItem
1990+
label="Copy friendly ID"
1991+
value={friendlyId}
1992+
onCopied={() => setOpen(false)}
1993+
/>
1994+
{description && (
1995+
<CopyPopoverItem
1996+
label="Copy description"
1997+
value={description}
1998+
preview={
1999+
description.length > MAX_DESCRIPTION_PREVIEW
2000+
? description.slice(0, MAX_DESCRIPTION_PREVIEW) + "…"
2001+
: description
2002+
}
2003+
onCopied={() => setOpen(false)}
2004+
/>
2005+
)}
2006+
</PopoverContent>
2007+
</Popover>
2008+
);
2009+
}
2010+
2011+
function CopyPopoverItem({
2012+
label,
2013+
value,
2014+
preview,
2015+
onCopied,
2016+
}: {
2017+
label: string;
2018+
value: string;
2019+
preview?: string;
2020+
onCopied?: () => void;
2021+
}) {
2022+
const [copied, setCopied] = useState(false);
2023+
2024+
const handleCopy = () => {
2025+
navigator.clipboard.writeText(value);
2026+
setCopied(true);
2027+
setTimeout(() => {
2028+
setCopied(false);
2029+
onCopied?.();
2030+
}, 600);
2031+
};
2032+
2033+
return (
2034+
<SimpleTooltip
2035+
button={
2036+
<button
2037+
type="button"
2038+
onClick={handleCopy}
2039+
className={cn(
2040+
"flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-xs transition",
2041+
copied
2042+
? "text-green-500"
2043+
: "text-text-dimmed hover:bg-charcoal-700 hover:text-text-bright"
2044+
)}
2045+
>
2046+
{copied ? (
2047+
<ClipboardCheckIcon className="size-3.5 shrink-0" />
2048+
) : (
2049+
<ClipboardIcon className="size-3.5 shrink-0" />
2050+
)}
2051+
{label}
2052+
</button>
2053+
}
2054+
content={<span className="max-w-64 break-all font-mono text-xs">{preview ?? value}</span>}
2055+
side="right"
2056+
disableHoverableContent
2057+
asChild
2058+
/>
2059+
);
2060+
}

0 commit comments

Comments
 (0)