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