@@ -71,23 +71,28 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
7171 run . realtimeStreamsVersion
7272 ) ;
7373
74- return realtimeStream . streamResponse (
75- request ,
76- run . friendlyId ,
77- streamKey ,
78- request . signal ,
79- lastEventId
80- ) ;
74+ return realtimeStream . streamResponse ( request , run . friendlyId , streamKey , request . signal , {
75+ lastEventId,
76+ } ) ;
8177} ;
8278
83- export function RealtimeStreamViewer ( { runId, streamKey } : { runId : string ; streamKey : string } ) {
79+ export function RealtimeStreamViewer ( {
80+ runId,
81+ streamKey,
82+ metadata,
83+ } : {
84+ runId : string ;
85+ streamKey : string ;
86+ metadata : Record < string , unknown > | undefined ;
87+ } ) {
8488 const organization = useOrganization ( ) ;
8589 const project = useProject ( ) ;
8690 const environment = useEnvironment ( ) ;
8791
8892 const resourcePath = `/resources/orgs/${ organization . slug } /projects/${ project . slug } /env/${ environment . slug } /runs/${ runId } /streams/${ streamKey } ` ;
8993
90- const { chunks, error, isConnected } = useRealtimeStream ( resourcePath ) ;
94+ const startIndex = typeof metadata ?. startIndex === "number" ? metadata . startIndex : undefined ;
95+ const { chunks, error, isConnected } = useRealtimeStream ( resourcePath , startIndex ) ;
9196 const scrollRef = useRef < HTMLDivElement > ( null ) ;
9297 const bottomRef = useRef < HTMLDivElement > ( null ) ;
9398 const [ isAtBottom , setIsAtBottom ] = useState ( true ) ;
@@ -124,7 +129,10 @@ export function RealtimeStreamViewer({ runId, streamKey }: { runId: string; stre
124129 }
125130 } , [ chunks , isAtBottom ] ) ;
126131
127- const maxLineNumberWidth = chunks . length . toString ( ) . length ;
132+ const firstLineNumber = startIndex ?? 0 ;
133+ const lastLineNumber = firstLineNumber + chunks . length - 1 ;
134+ const maxLineNumberWidth = ( chunks . length > 0 ? lastLineNumber : firstLineNumber ) . toString ( )
135+ . length ;
128136
129137 return (
130138 < div className = "flex h-full flex-col overflow-hidden border-t border-grid-bright" >
@@ -178,7 +186,7 @@ export function RealtimeStreamViewer({ runId, streamKey }: { runId: string; stre
178186 < StreamChunkLine
179187 key = { index }
180188 chunk = { chunk }
181- lineNumber = { index + 1 }
189+ lineNumber = { firstLineNumber + index }
182190 maxLineNumberWidth = { maxLineNumberWidth }
183191 />
184192 ) ) }
@@ -246,7 +254,7 @@ function StreamChunkLine({
246254 ) ;
247255}
248256
249- function useRealtimeStream ( resourcePath : string ) {
257+ function useRealtimeStream ( resourcePath : string , startIndex ?: number ) {
250258 const [ chunks , setChunks ] = useState < StreamChunk [ ] > ( [ ] ) ;
251259 const [ error , setError ] = useState < Error | null > ( null ) ;
252260 const [ isConnected , setIsConnected ] = useState ( false ) ;
@@ -259,6 +267,8 @@ function useRealtimeStream(resourcePath: string) {
259267 try {
260268 const sseSubscription = new SSEStreamSubscription ( resourcePath , {
261269 signal : abortController . signal ,
270+ lastEventId : startIndex ? ( startIndex - 1 ) . toString ( ) : undefined ,
271+ timeoutInSeconds : 30 ,
262272 } ) ;
263273
264274 const stream = await sseSubscription . subscribe ( ) ;
@@ -300,7 +310,7 @@ function useRealtimeStream(resourcePath: string) {
300310 abortController . abort ( ) ;
301311 reader ?. cancel ( ) ;
302312 } ;
303- } , [ resourcePath ] ) ;
313+ } , [ resourcePath , startIndex ] ) ;
304314
305315 return { chunks, error, isConnected } ;
306316}
0 commit comments