@@ -165,32 +165,48 @@ export async function initializeMap (divId = 'maplibre-map') {
165165 map . triggerRepaint ( )
166166 } , false )
167167
168- // Visibility watchdog — surfaces map activity in the 3s after resume via
169- // status toast (no console available on mobile PWA), so we can identify what
170- // triggers the delayed freeze.
168+ // Diagnostic state shared with the input watchdog so freeze toasts can carry
169+ // the post-resume map activity summary. Tracked for 10s after each resume.
170+ let postResumeT0 = 0
171+ let postResumeRenderCount = 0
172+ let postResumeMaxGap = 0
173+ let postResumeLastRender = 0
174+ let postResumeCounts = { }
175+ const trackedEvents = [ 'load' , 'idle' , 'dataloading' , 'data' , 'sourcedataloading' , 'sourcedata' ,
176+ 'styledataloading' , 'styledata' , 'error' , 'movestart' , 'moveend' , 'zoomstart' , 'zoomend' ,
177+ 'dragstart' , 'dragend' ]
178+
179+ const diagnosticSummary = ( ) => {
180+ const elapsed = Math . round ( performance . now ( ) - postResumeT0 )
181+ return `t+${ elapsed } ms R:${ postResumeRenderCount } gap:${ Math . round ( postResumeMaxGap ) } ms ` +
182+ Object . entries ( postResumeCounts ) . filter ( ( [ _ , n ] ) => n > 0 )
183+ . map ( ( [ k , n ] ) => `${ k . replace ( 'source' , 'src' ) . replace ( 'style' , 'sty' ) . replace ( 'loading' , 'L' ) } ×${ n } ` ) . join ( ' ' )
184+ }
185+
186+ // Visibility watchdog — silently tracks map activity for 10s after resume so
187+ // the input/render freeze toasts can include the diagnostic summary inline
188+ // (no separate toast that would be clobbered by the freeze toast).
171189 document . addEventListener ( 'visibilitychange' , ( ) => {
172190 if ( document . visibilityState !== 'visible' ) return
173191 status ( 'Visibility: visible' , 'info' , 'medium' , 1000 )
174192
175- const t0 = performance . now ( )
176- const counts = { }
177- const eventNames = [ 'load' , 'idle' , 'dataloading' , 'data' , 'sourcedataloading' , 'sourcedata' ,
178- 'styledataloading' , 'styledata' , 'error' , 'movestart' , 'moveend' , 'zoomstart' , 'zoomend' ,
179- 'dragstart' , 'dragend' ]
193+ postResumeT0 = performance . now ( )
194+ postResumeRenderCount = 0
195+ postResumeMaxGap = 0
196+ postResumeLastRender = postResumeT0
197+ postResumeCounts = { }
198+
180199 const handlers = { }
181- eventNames . forEach ( name => {
182- handlers [ name ] = ( ) => { counts [ name ] = ( counts [ name ] || 0 ) + 1 }
200+ trackedEvents . forEach ( name => {
201+ handlers [ name ] = ( ) => { postResumeCounts [ name ] = ( postResumeCounts [ name ] || 0 ) + 1 }
183202 map . on ( name , handlers [ name ] )
184203 } )
185- let lastRenderInWindow = t0
186- let maxRenderGap = 0
187- let renderCount = 0
188204 handlers . render = ( ) => {
189205 const now = performance . now ( )
190- const gap = now - lastRenderInWindow
191- if ( gap > maxRenderGap ) maxRenderGap = gap
192- lastRenderInWindow = now
193- renderCount ++
206+ const gap = now - postResumeLastRender
207+ if ( gap > postResumeMaxGap ) postResumeMaxGap = gap
208+ postResumeLastRender = now
209+ postResumeRenderCount ++
194210 }
195211 map . on ( 'render' , handlers . render )
196212
@@ -200,17 +216,14 @@ export async function initializeMap (divId = 'maplibre-map') {
200216 const renderedSinceWatchdog = lastRenderAt > beforeRepaint
201217 const glLost = map . painter ?. context ?. gl ?. isContextLost ?. ( )
202218 if ( ! renderedSinceWatchdog || glLost ) {
203- status ( `Map render frozen (gl_lost=${ glLost } )` , 'warning' , 'medium' , 3000 )
219+ status ( `Map render frozen (gl_lost=${ glLost } ) | ${ diagnosticSummary ( ) } ` , 'warning' , 'medium' , 10000 )
204220 }
205221 } , 500 )
206222
207223 setTimeout ( ( ) => {
208- eventNames . forEach ( name => map . off ( name , handlers [ name ] ) )
224+ trackedEvents . forEach ( name => map . off ( name , handlers [ name ] ) )
209225 map . off ( 'render' , handlers . render )
210- const summary = `R:${ renderCount } gap:${ Math . round ( maxRenderGap ) } ms ` +
211- Object . entries ( counts ) . filter ( ( [ _ , n ] ) => n > 0 ) . map ( ( [ k , n ] ) => `${ k . replace ( 'source' , 'src' ) . replace ( 'style' , 'sty' ) . replace ( 'loading' , 'L' ) } ×${ n } ` ) . join ( ' ' )
212- status ( summary , 'info' , 'medium' , 15000 )
213- } , 3000 )
226+ } , 10000 )
214227 } )
215228
216229 // Input watchdog — detects handler-level freezes where render still works but
@@ -238,7 +251,7 @@ export async function initializeMap (divId = 'maplibre-map') {
238251 inputFreezeReported = true
239252 const glLost = map . painter ?. context ?. gl ?. isContextLost ?. ( )
240253 console . warn ( 'Map drag input frozen' , { glLost } )
241- status ( `Map drag frozen (gl_lost=${ glLost } )` , 'warning' , 'medium' , 3000 )
254+ status ( `Map drag frozen (gl_lost=${ glLost } ) | ${ diagnosticSummary ( ) } ` , 'warning' , 'medium' , 10000 )
242255 } )
243256 mapCanvas . addEventListener ( 'pointerup' , ( ) => { pointerDownAt = null } )
244257 mapCanvas . addEventListener ( 'pointercancel' , ( ) => { pointerDownAt = null } )
0 commit comments