From 9a5deae45184d0539d5a96ece401fac78384e209 Mon Sep 17 00:00:00 2001 From: Amadeus Demarzi Date: Tue, 4 Aug 2026 15:43:32 -0700 Subject: [PATCH] diffs: harden CodeView resize handling In order to properly scroll fix issues triggered by ResizeObservers, we need to synchronously trigger a render from them. Additionally, we also need were not always triggering synchronous renders in certain cases, which would cause a jump jitter while scrolling sometimes. --- packages/diffs/src/components/CodeView.ts | 25 +++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/packages/diffs/src/components/CodeView.ts b/packages/diffs/src/components/CodeView.ts index 2d3a89d65..db35c8083 100644 --- a/packages/diffs/src/components/CodeView.ts +++ b/packages/diffs/src/components/CodeView.ts @@ -583,6 +583,7 @@ export interface CodeViewOptions } const DEFAULT_SCROLL_INTERACTION_RESTORE_DELAY_MS = 120; +const SUB_PIXEL_TOLERANCE = 1; const SCROLLING_CODE_OVERFLOW_FIX_VARIABLE = '--diffs-overflow-override'; const SCROLL_REBASE_CONTAINER_HEIGHT = 12_000_000; const SCROLL_REBASE_TRIGGER_TOP = 1_000_000; @@ -898,9 +899,7 @@ export class CodeView { } const actualHeight = this.stickyContainer.getBoundingClientRect().height; - // Tolerate sub-pixel rounding from summing many flex children; real - // discrepancies are whole rows (or larger) tall. - if (Math.abs(actualHeight - stickyHeight) < 1) { + if (Math.abs(actualHeight - stickyHeight) < SUB_PIXEL_TOLERANCE) { return; } @@ -3548,14 +3547,16 @@ export class CodeView { }; private handleResize = (entries: ResizeObserverEntry[]) => { + let shouldRender = false; for (const entry of entries) { // If the sticky container resizes (could be from a render, which it will // probably ignore) or if an annotation or line wrap triggers a resize if (entry.target === this.stickyContainer) { const blockSize = entry.borderBoxSize[0].blockSize; - // If the height of the sticky container was already known, there's - // nothing for us to do - if (blockSize !== this.renderState.stickyHeight) { + if ( + Math.abs(blockSize - this.renderState.stickyHeight) >= + SUB_PIXEL_TOLERANCE + ) { // If content resizes above the viewport, we want to be sure that it // doesn't cause things to jump within the viewport const currentScrollTop = this.getScrollTop(); @@ -3588,6 +3589,7 @@ export class CodeView { this.pendingScrollTarget = undefined; this.scrollAnimation = undefined; } + shouldRender = true; } } // A header/footer host resized after mount (async content, fonts, a React @@ -3631,16 +3633,23 @@ export class CodeView { this.pendingScrollTarget = undefined; this.scrollAnimation = undefined; } - this.render(); + shouldRender = true; } } // Root element resize (element-mode only) else { this.scrollDirty = true; this.heightDirty = true; - this.render(); + shouldRender = true; } } + + // If the DOM changed in an unexpected way, we should kick + // off a synchronous render immediately because it will + // ensure no visual jitter if we need to scroll fix + if (shouldRender) { + this.render(true); + } }; /**