@@ -23,7 +23,7 @@ import {
2323} from "@trigger.dev/core/v3" ;
2424import type { RuntimeEnvironmentType } from "@trigger.dev/database" ;
2525import { motion } from "framer-motion" ;
26- import { useCallback , useEffect , useMemo , useRef , useState } from "react" ;
26+ import { useCallback , useEffect , useRef , useState } from "react" ;
2727import { useHotkeys } from "react-hotkeys-hook" ;
2828import { redirect } from "remix-typedjson" ;
2929import { MoveToTopIcon } from "~/assets/icons/MoveToTopIcon" ;
@@ -140,10 +140,10 @@ const resizableSettings = {
140140type TraceEvent = NonNullable < SerializeFrom < typeof loader > [ "trace" ] > [ "events" ] [ 0 ] ;
141141
142142type RunsListNavigation = {
143- runs : Array < { friendlyId : string } > ;
143+ runs : Array < { friendlyId : string ; spanId : string } > ;
144144 pagination : { next ?: string ; previous ?: string } ;
145- prevPageLastRun ?: { friendlyId : string ; cursor : string } ;
146- nextPageFirstRun ?: { friendlyId : string ; cursor : string } ;
145+ prevPageLastRun ?: { friendlyId : string ; spanId : string ; cursor : string } ;
146+ nextPageFirstRun ?: { friendlyId : string ; spanId : string ; cursor : string } ;
147147} ;
148148
149149async function getRunsListFromTableState ( {
@@ -204,6 +204,7 @@ async function getRunsListFromTableState({
204204 if ( prevPageResult . runs . length > 0 ) {
205205 runsList . prevPageLastRun = {
206206 friendlyId : prevPageResult . runs [ 0 ] . friendlyId ,
207+ spanId : prevPageResult . runs [ 0 ] . spanId ,
207208 cursor : currentPageResult . pagination . previous ,
208209 } ;
209210 }
@@ -222,6 +223,7 @@ async function getRunsListFromTableState({
222223 if ( nextPageResult . runs . length > 0 ) {
223224 runsList . nextPageFirstRun = {
224225 friendlyId : nextPageResult . runs [ 0 ] . friendlyId ,
226+ spanId : nextPageResult . runs [ 0 ] . spanId ,
225227 cursor : currentPageResult . pagination . next ,
226228 } ;
227229 }
@@ -296,7 +298,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
296298type LoaderData = SerializeFrom < typeof loader > ;
297299
298300export default function Page ( ) {
299- const { run, trace, resizable , maximumLiveReloadingSetting, runsList } = useLoaderData < typeof loader > ( ) ;
301+ const { run, trace, maximumLiveReloadingSetting, runsList } = useLoaderData < typeof loader > ( ) ;
300302 const organization = useOrganization ( ) ;
301303 const project = useProject ( ) ;
302304 const environment = useEnvironment ( ) ;
@@ -308,8 +310,10 @@ export default function Page() {
308310 const tableState = decodeURIComponent ( value ( "tableState" ) ?? "" ) ;
309311 const tableStateSearchParams = new URLSearchParams ( tableState ) ;
310312 const filters = getRunFiltersFromSearchParams ( tableStateSearchParams ) ;
313+ const tabParam = value ( "tab" ) ?? undefined ;
314+ const spanParam = value ( "span" ) ?? undefined ;
311315
312- const [ previousRunPath , nextRunPath ] = useAdjacentRunPaths ( { organization, project, environment, tableState, run, runsList} ) ;
316+ const [ previousRunPath , nextRunPath ] = useAdjacentRunPaths ( { organization, project, environment, tableState, run, runsList, tabParam , useSpan : ! ! spanParam } ) ;
313317
314318 return (
315319 < >
@@ -320,7 +324,7 @@ export default function Page() {
320324 text : "Runs" ,
321325 } }
322326 title = { < >
323- < CopyableText value = { run . friendlyId } variant = "text-below" className = "font-mono" />
327+ < CopyableText value = { run . friendlyId } variant = "text-below" className = "font-mono px-0 py-0 pb-[2px] " />
324328 { tableState && ( < div className = "flex" >
325329 < PreviousRunButton to = { previousRunPath } />
326330 < NextRunButton to = { nextRunPath } />
@@ -940,7 +944,6 @@ function TimelineView({
940944 scale,
941945 rootSpanStatus,
942946 rootStartedAt,
943- parentRef,
944947 timelineScrollRef,
945948 virtualizer,
946949 events,
@@ -1127,7 +1130,8 @@ function TimelineView({
11271130 "-ml-[0.5px] h-[0.5625rem] w-px rounded-none" ,
11281131 eventBackgroundClassName ( node . data )
11291132 ) }
1130- layoutId = { `${ node . id } -${ event . name } ` }
1133+ layoutId = { node . data . isPartial ? `${ node . id } -${ event . name } ` : undefined }
1134+ animate = { ! node . data . isPartial ? false : undefined }
11311135 />
11321136 ) }
11331137 </ Timeline . Point >
@@ -1145,7 +1149,8 @@ function TimelineView({
11451149 "-ml-[0.1562rem] size-[0.3125rem] rounded-full border bg-background-bright" ,
11461150 eventBorderClassName ( node . data )
11471151 ) }
1148- layoutId = { `${ node . id } -${ event . name } ` }
1152+ layoutId = { node . data . isPartial ? `${ node . id } -${ event . name } ` : undefined }
1153+ animate = { ! node . data . isPartial ? false : undefined }
11491154 />
11501155 ) }
11511156 </ Timeline . Point >
@@ -1164,7 +1169,8 @@ function TimelineView({
11641169 >
11651170 < motion . div
11661171 className = { cn ( "h-px w-full" , eventBackgroundClassName ( node . data ) ) }
1167- layoutId = { `mark-${ node . id } ` }
1172+ layoutId = { node . data . isPartial ? `mark-${ node . id } ` : undefined }
1173+ animate = { ! node . data . isPartial ? false : undefined }
11681174 />
11691175 </ Timeline . Span >
11701176 ) : null }
@@ -1201,7 +1207,8 @@ function TimelineView({
12011207 "-ml-0.5 size-3 rounded-full border-2 border-background-bright" ,
12021208 eventBackgroundClassName ( node . data )
12031209 ) }
1204- layoutId = { node . id }
1210+ layoutId = { node . data . isPartial ? node . id : undefined }
1211+ animate = { ! node . data . isPartial ? false : undefined }
12051212 />
12061213 ) }
12071214 </ Timeline . Point >
@@ -1444,7 +1451,8 @@ function SpanWithDuration({
14441451 fadeLeft ? "rounded-r-sm bg-gradient-to-r from-black/50 to-transparent" : "rounded-sm"
14451452 ) }
14461453 style = { { backgroundSize : "20px 100%" , backgroundRepeat : "no-repeat" } }
1447- layoutId = { node . id }
1454+ layoutId = { node . data . isPartial ? node . id : undefined }
1455+ animate = { ! node . data . isPartial ? false : undefined }
14481456 >
14491457 { node . data . isPartial && (
14501458 < div
@@ -1457,10 +1465,12 @@ function SpanWithDuration({
14571465 "sticky left-0 z-10 transition-opacity group-hover:opacity-100" ,
14581466 ! showDuration && "opacity-0"
14591467 ) }
1468+ animate = { ! node . data . isPartial ? false : undefined }
14601469 >
14611470 < motion . div
14621471 className = "whitespace-nowrap rounded-sm px-1 py-0.5 text-xxs text-text-bright text-shadow-custom"
1463- layout = "position"
1472+ layout = { node . data . isPartial ? "position" : undefined }
1473+ animate = { ! node . data . isPartial ? false : undefined }
14641474 >
14651475 { formatDurationMilliseconds ( props . durationMs , {
14661476 style : "short" ,
@@ -1543,12 +1553,11 @@ function KeyboardShortcuts({
15431553 expandAllBelowDepth,
15441554 collapseAllBelowDepth,
15451555 toggleExpandLevel,
1546- setShowDurations,
15471556} : {
15481557 expandAllBelowDepth : ( depth : number ) => void ;
15491558 collapseAllBelowDepth : ( depth : number ) => void ;
15501559 toggleExpandLevel : ( depth : number ) => void ;
1551- setShowDurations : ( show : ( show : boolean ) => boolean ) => void ;
1560+ setShowDurations ? : ( show : ( show : boolean ) => boolean ) => void ;
15521561} ) {
15531562 return (
15541563 < >
@@ -1666,63 +1675,77 @@ function useAdjacentRunPaths({
16661675 tableState,
16671676 run,
16681677 runsList,
1678+ tabParam,
1679+ useSpan
16691680} : {
16701681 organization : { slug : string } ;
16711682 project : { slug : string } ;
16721683 environment : { slug : string } ;
16731684 tableState : string ;
1674- run : { friendlyId : string } ;
1685+ run : { friendlyId : string , spanId : string } ;
16751686 runsList : RunsListNavigation | null ;
1687+ tabParam ?: string ;
1688+ useSpan ?: boolean ;
16761689} ) : [ string | null , string | null ] {
1677- return useMemo ( ( ) => {
1678- if ( ! runsList || runsList . runs . length === 0 ) {
1679- return [ null , null ] ;
1680- }
1681-
1682- const currentIndex = runsList . runs . findIndex ( ( r ) => r . friendlyId === run . friendlyId ) ;
1683-
1684- if ( currentIndex === - 1 ) {
1685- return [ null , null ] ;
1686- }
1690+ if ( ! runsList || runsList . runs . length === 0 ) {
1691+ return [ null , null ] ;
1692+ }
16871693
1688- // Determine previous run: use prevPageLastRun if at first position, otherwise use previous run in list
1689- let previousRun : { friendlyId : string } | null = null ;
1690- const previousRunTableState = new URLSearchParams ( tableState ) ;
1691- if ( currentIndex > 0 ) {
1692- previousRun = runsList . runs [ currentIndex - 1 ] ;
1693- } else if ( runsList . prevPageLastRun ) {
1694- previousRun = runsList . prevPageLastRun ;
1695- // Update tableState with the new cursor for the previous page
1696- previousRunTableState . set ( "cursor" , runsList . prevPageLastRun . cursor ) ;
1697- previousRunTableState . set ( "direction" , "backward" ) ;
1698- }
1694+ const currentIndex = runsList . runs . findIndex ( ( r ) => r . friendlyId === run . friendlyId ) ;
1695+
1696+ if ( currentIndex === - 1 ) {
1697+ return [ null , null ] ;
1698+ }
16991699
1700- // Determine next run: use nextPageFirstRun if at last position, otherwise use next run in list
1701- let nextRun : { friendlyId : string } | null = null ;
1702- const nextRunTableState = new URLSearchParams ( tableState ) ;
1703- if ( currentIndex < runsList . runs . length - 1 ) {
1704- nextRun = runsList . runs [ currentIndex + 1 ] ;
1705- } else if ( runsList . nextPageFirstRun ) {
1706- nextRun = runsList . nextPageFirstRun ;
1707- // Update tableState with the new cursor for the next page
1708- nextRunTableState . set ( "cursor" , runsList . nextPageFirstRun . cursor ) ;
1709- nextRunTableState . set ( "direction" , "forward " ) ;
1710- }
1700+ // Determine previous run: use prevPageLastRun if at first position, otherwise use previous run in list
1701+ let previousRun : { friendlyId : string ; spanId : string } | null = null ;
1702+ const previousRunTableState = new URLSearchParams ( tableState ) ;
1703+ if ( currentIndex > 0 ) {
1704+ previousRun = runsList . runs [ currentIndex - 1 ] ;
1705+ } else if ( runsList . prevPageLastRun ) {
1706+ previousRun = runsList . prevPageLastRun ;
1707+ // Update tableState with the new cursor for the previous page
1708+ previousRunTableState . set ( "cursor" , runsList . prevPageLastRun . cursor ) ;
1709+ previousRunTableState . set ( "direction" , "backward " ) ;
1710+ }
17111711
1712- const previousURLSearchParams = new URLSearchParams ( ) ;
1713- previousURLSearchParams . set ( "tableState" , previousRunTableState . toString ( ) ) ;
1714- const previousRunPath = previousRun
1715- ? v3RunPath ( organization , project , environment , previousRun , previousURLSearchParams )
1716- : null ;
1712+ // Determine next run: use nextPageFirstRun if at last position, otherwise use next run in list
1713+ let nextRun : { friendlyId : string ; spanId : string } | null = null ;
1714+ const nextRunTableState = new URLSearchParams ( tableState ) ;
1715+ if ( currentIndex < runsList . runs . length - 1 ) {
1716+ nextRun = runsList . runs [ currentIndex + 1 ] ;
1717+ } else if ( runsList . nextPageFirstRun ) {
1718+ nextRun = runsList . nextPageFirstRun ;
1719+ // Update tableState with the new cursor for the next page
1720+ nextRunTableState . set ( "cursor" , runsList . nextPageFirstRun . cursor ) ;
1721+ nextRunTableState . set ( "direction" , "forward" ) ;
1722+ }
17171723
1718- const nextURLSearchParams = new URLSearchParams ( ) ;
1719- nextURLSearchParams . set ( "tableState" , nextRunTableState . toString ( ) ) ;
1720- const nextRunPath = nextRun
1721- ? v3RunPath ( organization , project , environment , nextRun , nextURLSearchParams )
1722- : null ;
1724+ const previousURLSearchParams = new URLSearchParams ( ) ;
1725+ previousURLSearchParams . set ( "tableState" , previousRunTableState . toString ( ) ) ;
1726+ if ( previousRun && useSpan ) {
1727+ previousURLSearchParams . set ( "span" , previousRun . spanId ) ;
1728+ }
1729+ if ( tabParam && useSpan ) {
1730+ previousURLSearchParams . set ( "tab" , tabParam ) ;
1731+ }
1732+ const previousRunPath = previousRun
1733+ ? v3RunPath ( organization , project , environment , previousRun , previousURLSearchParams )
1734+ : null ;
1735+
1736+ const nextURLSearchParams = new URLSearchParams ( ) ;
1737+ nextURLSearchParams . set ( "tableState" , nextRunTableState . toString ( ) ) ;
1738+ if ( nextRun && useSpan ) {
1739+ nextURLSearchParams . set ( "span" , nextRun . spanId ) ;
1740+ }
1741+ if ( tabParam && useSpan ) {
1742+ nextURLSearchParams . set ( "tab" , tabParam ) ;
1743+ }
1744+ const nextRunPath = nextRun
1745+ ? v3RunPath ( organization , project , environment , nextRun , nextURLSearchParams )
1746+ : null ;
17231747
1724- return [ previousRunPath , nextRunPath ] ;
1725- } , [ organization , project , environment , tableState , run . friendlyId , runsList ] ) ;
1748+ return [ previousRunPath , nextRunPath ] ;
17261749}
17271750
17281751
@@ -1741,6 +1764,7 @@ function PreviousRunButton({ to }: { to: string | null }) {
17411764 shortcut = { { key : "[" } }
17421765 tooltip = "Previous Run"
17431766 disabled = { ! to }
1767+ replace
17441768 />
17451769 </ div >
17461770 ) ;
@@ -1761,6 +1785,7 @@ function NextRunButton({ to }: { to: string | null }) {
17611785 shortcut = { { key : "]" } }
17621786 tooltip = "Next Run"
17631787 disabled = { ! to }
1788+ replace
17641789 />
17651790 </ div >
17661791 ) ;
0 commit comments