11import * as Ariakit from "@ariakit/react" ;
2- import { ArrowPathIcon , ChevronUpDownIcon } from "@heroicons/react/20/solid" ;
2+ import { ArrowPathIcon , ChevronUpDownIcon , EyeIcon } from "@heroicons/react/20/solid" ;
33import { DialogClose } from "@radix-ui/react-dialog" ;
44import { type MetaFunction , useFetcher } from "@remix-run/react" ;
55import {
@@ -10,6 +10,7 @@ import {
1010} from "@remix-run/server-runtime" ;
1111
1212import { AnimatePresence , motion } from "framer-motion" ;
13+ import { ClipboardCheckIcon , ClipboardIcon , GitBranchPlusIcon } from "lucide-react" ;
1314import { useCallback , useEffect , useRef , useState } from "react" ;
1415import { typedjson , useTypedLoaderData } from "remix-typedjson" ;
1516import { CodeBlock } from "~/components/code/CodeBlock" ;
@@ -21,9 +22,6 @@ import { ProvidersFilter } from "~/components/metrics/ProvidersFilter";
2122import { AppliedFilter } from "~/components/primitives/AppliedFilter" ;
2223import { Badge } from "~/components/primitives/Badge" ;
2324import { Button } from "~/components/primitives/Buttons" ;
24- import { CopyButton } from "~/components/primitives/CopyButton" ;
25- import { ClipboardCheckIcon , ClipboardIcon , GitBranchPlusIcon } from "lucide-react" ;
26- import { CopyableText } from "~/components/primitives/CopyableText" ;
2725import { DateTime } from "~/components/primitives/DateTime" ;
2826import { Dialog , DialogContent , DialogHeader } from "~/components/primitives/Dialog" ;
2927import { Header3 } from "~/components/primitives/Headers" ;
@@ -33,9 +31,15 @@ import { InputGroup } from "~/components/primitives/InputGroup";
3331import { Label } from "~/components/primitives/Label" ;
3432import { NavBar , PageAccessories , PageTitle } from "~/components/primitives/PageHeader" ;
3533import { Paragraph } from "~/components/primitives/Paragraph" ;
36- import { RadioButtonCircle } from "~/components/primitives/RadioButton" ;
37- import { Popover , PopoverContent , PopoverTrigger } from "~/components/primitives/Popover" ;
34+ import {
35+ Popover ,
36+ PopoverContent ,
37+ PopoverMenuItem ,
38+ PopoverTrigger ,
39+ PopoverVerticalEllipseTrigger ,
40+ } from "~/components/primitives/Popover" ;
3841import * as Property from "~/components/primitives/PropertyTable" ;
42+ import { RadioButtonCircle } from "~/components/primitives/RadioButton" ;
3943import {
4044 ResizableHandle ,
4145 ResizablePanel ,
@@ -52,7 +56,6 @@ import {
5256import { Spinner } from "~/components/primitives/Spinner" ;
5357import { TabButton , TabContainer } from "~/components/primitives/Tabs" ;
5458import { TextArea } from "~/components/primitives/TextArea" ;
55- import tablerSpritePath from "~/components/primitives/tabler-sprite.svg" ;
5659import { TimeFilter } from "~/components/runs/v3/SharedFilters" ;
5760import { prisma } from "~/db.server" ;
5861import { useEnvironment } from "~/hooks/useEnvironment" ;
@@ -71,14 +74,14 @@ import { PromptService } from "~/v3/services/promptService.server";
7174
7275import { z } from "zod" ;
7376import { AIPromptsIcon } from "~/assets/icons/AIPromptsIcon" ;
77+ import { RunsIcon } from "~/assets/icons/RunsIcon" ;
7478import { InlineCode } from "~/components/code/InlineCode" ;
7579import { InfoPanel } from "~/components/primitives/InfoPanel" ;
76- import { TextLink } from "~/components/primitives/TextLink " ;
80+ import { SimpleTooltip } from "~/components/primitives/Tooltip " ;
7781import { MetricWidget } from "~/routes/resources.metric" ;
7882import { cn } from "~/utils/cn" ;
7983import { EnvironmentParamSchema , v3PromptsPath , v3RunSpanPath } from "~/utils/pathBuilder" ;
8084import { parsePeriodToMs } from "~/utils/periods" ;
81- import { SimpleTooltip } from "~/components/primitives/Tooltip" ;
8285
8386const ParamSchema = EnvironmentParamSchema . extend ( {
8487 promptSlug : z . string ( ) ,
@@ -1172,6 +1175,52 @@ function PreviewTab({
11721175 ) ;
11731176}
11741177
1178+ function GenerationPopoverMenu ( {
1179+ isSelected,
1180+ runPath,
1181+ onViewDetails,
1182+ } : {
1183+ isSelected : boolean ;
1184+ runPath : string ;
1185+ onViewDetails : ( ) => void ;
1186+ } ) {
1187+ const [ open , setOpen ] = useState ( false ) ;
1188+
1189+ return (
1190+ < Popover open = { open } onOpenChange = { setOpen } >
1191+ < PopoverVerticalEllipseTrigger
1192+ onClick = { ( e ) => e . stopPropagation ( ) }
1193+ className = "shrink-0"
1194+ />
1195+ < PopoverContent
1196+ className = "min-w-[10rem] overflow-y-auto p-0 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600"
1197+ align = "end"
1198+ onClick = { ( e ) => e . stopPropagation ( ) }
1199+ >
1200+ < div className = "flex flex-col gap-1 p-1" >
1201+ < PopoverMenuItem
1202+ to = { runPath }
1203+ icon = { RunsIcon }
1204+ leadingIconClassName = "text-runs"
1205+ title = "Jump to run"
1206+ />
1207+ { ! isSelected && (
1208+ < PopoverMenuItem
1209+ icon = { EyeIcon }
1210+ leadingIconClassName = "text-text-dimmed"
1211+ title = "View details"
1212+ onClick = { ( ) => {
1213+ onViewDetails ( ) ;
1214+ setOpen ( false ) ;
1215+ } }
1216+ />
1217+ ) }
1218+ </ div >
1219+ </ PopoverContent >
1220+ </ Popover >
1221+ ) ;
1222+ }
1223+
11751224// ─── Generations Tab ─────────────────────────────────────
11761225
11771226function GenerationsTab ( {
@@ -1463,28 +1512,36 @@ function GenerationsTab({
14631512 key = { `${ gen . run_id } -${ gen . span_id } -${ i } ` }
14641513 data-generation-item
14651514 onClick = { ( ) => onSelectSpan ( { runId : gen . run_id , spanId : gen . span_id } ) }
1466- className = { `cursor-pointer border-b border-grid-dimmed px-3 py-2 text-xs transition last:border-0 ${
1467- isSelected ? "bg-indigo-500/10" : "hover:bg-charcoal-850"
1468- } `}
1515+ className = { cn (
1516+ "group/gen flex cursor-pointer items-center gap-3 border-b border-grid-dimmed px-3 py-3 text-sm transition" ,
1517+ isSelected
1518+ ? "bg-indigo-500/10 hover:bg-indigo-500/[0.07]"
1519+ : "hover:bg-charcoal-750"
1520+ ) }
14691521 >
1470- < div className = "flex items-center justify-between" >
1471- < span className = "font-medium text-text-bright " >
1472- { gen . operation_id || gen . task_identifier }
1473- </ span >
1474- < span className = "text-text-dimmed" > { gen . start_time } </ span >
1475- </ div >
1476- < div className = "mt-0.5 flex items-center justify-between" >
1477- < div className = "flex items-center gap-3 text-text-dimmed" >
1522+ < RadioButtonCircle checked = { isSelected } / >
1523+ < div className = "min-w-0 flex-1 " >
1524+ < div className = "flex items-center" >
1525+ < span className = "font-medium text-text-bright" >
1526+ { gen . operation_id || gen . task_identifier }
1527+ </ span >
1528+ </ div >
1529+ < div className = "mt-0.5 flex items-center gap-3 text-xs text-text-dimmed" >
14781530 < span className = "text-charcoal-400" > v{ gen . prompt_version } </ span >
14791531 < span > { gen . response_model } </ span >
14801532 < span > { gen . input_tokens + gen . output_tokens } tokens</ span >
14811533 < span > { formatCost ( gen . total_cost ) } </ span >
14821534 < span > { Math . round ( gen . duration_ms ) } ms</ span >
14831535 </ div >
1484- < TextLink to = { runPath } className = "text-xs" onClick = { ( e ) => e . stopPropagation ( ) } >
1485- View run
1486- </ TextLink >
14871536 </ div >
1537+ < span className = "shrink-0 text-xs text-text-dimmed" > { gen . start_time } </ span >
1538+ < GenerationPopoverMenu
1539+ isSelected = { isSelected }
1540+ runPath = { runPath }
1541+ onViewDetails = { ( ) =>
1542+ onSelectSpan ( { runId : gen . run_id , spanId : gen . span_id } )
1543+ }
1544+ />
14881545 </ div >
14891546 ) ;
14901547 } ) }
@@ -1502,7 +1559,7 @@ function GenerationsTab({
15021559 < ResizableHandle id = "prompt-gen-handle" />
15031560
15041561 { /* Span inspector */ }
1505- < ResizablePanel id = "prompt-gen-inspector" default = "40% " min = "200px" isStaticAtRest >
1562+ < ResizablePanel id = "prompt-gen-inspector" default = "430px " min = "200px" isStaticAtRest >
15061563 { selectedSpan ? (
15071564 < SpanView runParam = { selectedSpan . runId } spanId = { selectedSpan . spanId } />
15081565 ) : (
0 commit comments