@@ -201,6 +201,11 @@ export class RemoteBrowser {
201201 private networkRequestTimeout : NodeJS . Timeout | null = null ;
202202 private pendingNetworkRequests : string [ ] = [ ] ;
203203 private readonly NETWORK_QUIET_PERIOD = 8000 ;
204+ private readonly INITIAL_LOAD_QUIET_PERIOD = 3000 ;
205+ private networkWaitStartTime : number = 0 ;
206+ private progressInterval : NodeJS . Timeout | null = null ;
207+ private hasShownInitialLoader : boolean = false ;
208+ private isInitialLoadInProgress : boolean = false ;
204209
205210 /**
206211 * Initializes a new instances of the {@link Generator} and {@link WorkflowInterpreter} classes and
@@ -432,42 +437,74 @@ export class RemoteBrowser {
432437 if ( ! this . currentPage ) return ;
433438
434439 this . currentPage . on ( "domcontentloaded" , async ( ) => {
435- logger . info ( "DOM content loaded - triggering snapshot" ) ;
436- await this . makeAndEmitDOMSnapshot ( ) ;
440+ if ( ! this . isInitialLoadInProgress ) {
441+ logger . info ( "DOM content loaded - triggering snapshot" ) ;
442+ await this . makeAndEmitDOMSnapshot ( ) ;
443+ }
437444 } ) ;
438445
439446 this . currentPage . on ( "response" , async ( response ) => {
440447 const url = response . url ( ) ;
441- if (
442- response . request ( ) . resourceType ( ) === "document" ||
443- url . includes ( "api/" ) ||
444- url . includes ( "ajax" )
445- ) {
448+ const isDocumentRequest = response . request ( ) . resourceType ( ) === "document" ;
449+
450+ if ( ! this . hasShownInitialLoader && isDocumentRequest && ! url . includes ( "about:blank" ) ) {
451+ this . hasShownInitialLoader = true ;
452+ this . isInitialLoadInProgress = true ;
446453 this . pendingNetworkRequests . push ( url ) ;
447454
448455 if ( this . networkRequestTimeout ) {
449456 clearTimeout ( this . networkRequestTimeout ) ;
450457 this . networkRequestTimeout = null ;
451458 }
452459
460+ if ( this . progressInterval ) {
461+ clearInterval ( this . progressInterval ) ;
462+ this . progressInterval = null ;
463+ }
464+
465+ this . networkWaitStartTime = Date . now ( ) ;
466+ this . progressInterval = setInterval ( ( ) => {
467+ const elapsed = Date . now ( ) - this . networkWaitStartTime ;
468+ const navigationProgress = Math . min ( ( elapsed / this . INITIAL_LOAD_QUIET_PERIOD ) * 40 , 35 ) ;
469+ const totalProgress = 60 + navigationProgress ;
470+ this . emitLoadingProgress ( totalProgress , this . pendingNetworkRequests . length ) ;
471+ } , 500 ) ;
472+
453473 logger . debug (
454- `Network request received: ${ url } . Total pending: ${ this . pendingNetworkRequests . length } `
474+ `Initial load network request received: ${ url } . Using ${ this . INITIAL_LOAD_QUIET_PERIOD } ms quiet period `
455475 ) ;
456476
457477 this . networkRequestTimeout = setTimeout ( async ( ) => {
458478 logger . info (
459- `Network quiet period reached. Processing ${ this . pendingNetworkRequests . length } requests `
479+ `Initial load network quiet period reached ( ${ this . INITIAL_LOAD_QUIET_PERIOD } ms) `
460480 ) ;
461481
482+ if ( this . progressInterval ) {
483+ clearInterval ( this . progressInterval ) ;
484+ this . progressInterval = null ;
485+ }
486+
487+ this . emitLoadingProgress ( 100 , this . pendingNetworkRequests . length ) ;
488+
462489 this . pendingNetworkRequests = [ ] ;
463490 this . networkRequestTimeout = null ;
491+ this . isInitialLoadInProgress = false ;
464492
465493 await this . makeAndEmitDOMSnapshot ( ) ;
466- } , this . NETWORK_QUIET_PERIOD ) ;
494+ } , this . INITIAL_LOAD_QUIET_PERIOD ) ;
467495 }
468496 } ) ;
469497 }
470498
499+ private emitLoadingProgress ( progress : number , pendingRequests : number ) : void {
500+ this . socket . emit ( "domLoadingProgress" , {
501+ progress : Math . round ( progress ) ,
502+ pendingRequests,
503+ userId : this . userId ,
504+ timestamp : Date . now ( ) ,
505+ } ) ;
506+ }
507+
471508 private async setupPageEventListeners ( page : Page ) {
472509 page . on ( 'framenavigated' , async ( frame ) => {
473510 if ( frame === page . mainFrame ( ) ) {
@@ -521,7 +558,13 @@ export class RemoteBrowser {
521558 const MAX_RETRIES = 3 ;
522559 let retryCount = 0 ;
523560 let success = false ;
524-
561+
562+ this . socket . emit ( "dom-snapshot-loading" , {
563+ userId : this . userId ,
564+ timestamp : Date . now ( ) ,
565+ } ) ;
566+ this . emitLoadingProgress ( 0 , 0 ) ;
567+
525568 while ( ! success && retryCount < MAX_RETRIES ) {
526569 try {
527570 this . browser = < Browser > ( await chromium . launch ( {
@@ -545,7 +588,9 @@ export class RemoteBrowser {
545588 if ( ! this . browser || this . browser . isConnected ( ) === false ) {
546589 throw new Error ( 'Browser failed to launch or is not connected' ) ;
547590 }
548-
591+
592+ this . emitLoadingProgress ( 20 , 0 ) ;
593+
549594 const proxyConfig = await getDecryptedProxyConfig ( userId ) ;
550595 let proxyOptions : { server : string , username ?: string , password ?: string } = { server : '' } ;
551596
@@ -623,6 +668,8 @@ export class RemoteBrowser {
623668
624669 this . currentPage = await this . context . newPage ( ) ;
625670
671+ this . emitLoadingProgress ( 40 , 0 ) ;
672+
626673 await this . setupPageEventListeners ( this . currentPage ) ;
627674
628675 const viewportSize = await this . currentPage . viewportSize ( ) ;
@@ -645,7 +692,9 @@ export class RemoteBrowser {
645692 // Still need to set up the CDP session even if blocker fails
646693 this . client = await this . currentPage . context ( ) . newCDPSession ( this . currentPage ) ;
647694 }
648-
695+
696+ this . emitLoadingProgress ( 60 , 0 ) ;
697+
649698 success = true ;
650699 logger . log ( 'debug' , `Browser initialized successfully for user ${ userId } ` ) ;
651700 } catch ( error : any ) {
@@ -1521,9 +1570,6 @@ export class RemoteBrowser {
15211570 this . isDOMStreamingActive = true ;
15221571 logger . info ( "DOM streaming started successfully" ) ;
15231572
1524- // Initial DOM snapshot
1525- await this . makeAndEmitDOMSnapshot ( ) ;
1526-
15271573 this . setupScrollEventListener ( ) ;
15281574 this . setupPageChangeListeners ( ) ;
15291575 } catch ( error ) {
0 commit comments