File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -43,17 +43,19 @@ export function SectionTabs({
4343 /** Timeout path: drop the click lock and re-read the visible section. */
4444 const expireScrollLock = useCallback ( ( ) => {
4545 clearScrollLock ( ) ;
46- // If the user interrupted smooth scroll, the target may never intersect — without a
47- // resync `active` would stay on the clicked tab while another section is on screen.
46+ // If the user interrupted smooth scroll, the target may never cross the observer threshold —
47+ // without a resync `active` would stay on the clicked tab while another section is on screen.
48+ // Prefer the heading nearest the sticky-strip reading line (not only those with top <= 120),
49+ // so a destination that stopped mid-viewport still wins over an off-screen prior heading.
4850 let bestId : string | null = null ;
49- let bestTop = Number . NEGATIVE_INFINITY ;
51+ let bestDistance = Number . POSITIVE_INFINITY ;
52+ const readingLine = 72 ;
5053 for ( const item of items ) {
5154 const node = document . getElementById ( sectionAnchorId ( scope , item . id ) ) ;
5255 if ( ! node ) continue ;
53- const top = node . getBoundingClientRect ( ) . top ;
54- // Match the observer bias: prefer a heading near the top of the viewport.
55- if ( top <= 120 && top > bestTop ) {
56- bestTop = top ;
56+ const distance = Math . abs ( node . getBoundingClientRect ( ) . top - readingLine ) ;
57+ if ( distance < bestDistance ) {
58+ bestDistance = distance ;
5759 bestId = item . id ;
5860 }
5961 }
Original file line number Diff line number Diff line change @@ -468,7 +468,10 @@ export default function Logs({ apiBase }: { apiBase: string }) {
468468 } else if ( settledFailure && failureStreak . error !== logsState . error ) {
469469 setFailureStreak ( previous => ( { error : logsState . error , count : previous . count + 1 } ) ) ;
470470 }
471- const pollFailing = failureStreak . count >= STALE_POLL_FAILURE_LIMIT ;
471+ // Auto-refresh off: one settled failure is enough — there is no next poll to recover quietly.
472+ const pollFailing =
473+ failureStreak . count >= STALE_POLL_FAILURE_LIMIT
474+ || ( ! autoRefresh && settledFailure ) ;
472475
473476 const detailInfo = detail ? statusCodeInfo ( detail . status , locale ) : null ;
474477 const conversationQuery = conversationFilter . trim ( ) ;
@@ -635,8 +638,8 @@ export default function Logs({ apiBase }: { apiBase: string }) {
635638 </ button >
636639 </ Notice >
637640 ) }
638- { /* A run of failed polls is no longer transient: say the rows below are stale rather than
639- letting them read as current . Cleared by the first successful poll . */ }
641+ { /* Stale rows after a sustained poll outage, or any settled failure while auto-refresh is
642+ off (no next tick will recover) . Cleared by the first successful fetch . */ }
640643 { pollFailing && logs . length > 0 && (
641644 < Notice tone = "err" >
642645 { t ( "logs.loadError" ) } { " " }
You can’t perform that action at this time.
0 commit comments