From 71755e5ceed3ef6fb091e80a19e6869ecf5a0d29 Mon Sep 17 00:00:00 2001 From: claude Date: Sun, 7 Jun 2026 13:09:18 +0000 Subject: [PATCH] fix(pdf): replace document.startViewTransition with non-blocking canvas fade document.startViewTransition with an async callback places a ::view-transition overlay on top of the entire page, intercepting pointer events for the duration of the callback (up to the 1s safety timeout + 250ms animation). With rapid auto-compiles this created interface freezes and overlapping transitions that could leave the visual lock in a broken state, causing 'stuck on compiling'. Replace with a canvas snapshot overlay + CSS opacity fade-out: - pointer-events:none so the overlay never blocks input - snapshot covers the canvas-clear from setDocument() (no white flash) - on pagerendered: opacity transitions to 0 over 250ms, then overlay removed - gives the same smooth visual crossfade, reliably, in all browsers Chrome 126+ retains the element-level startViewTransition path which is scoped to the PDF container and does not affect the rest of the page. Co-Authored-By: Claude Sonnet 4.6 --- .../pdf-preview/util/pdf-js-wrapper.ts | 61 +++++-------------- 1 file changed, 16 insertions(+), 45 deletions(-) diff --git a/services/web/frontend/js/features/pdf-preview/util/pdf-js-wrapper.ts b/services/web/frontend/js/features/pdf-preview/util/pdf-js-wrapper.ts index 392adcde61..11c78ff5aa 100644 --- a/services/web/frontend/js/features/pdf-preview/util/pdf-js-wrapper.ts +++ b/services/web/frontend/js/features/pdf-preview/util/pdf-js-wrapper.ts @@ -121,22 +121,6 @@ export default class PDFJSWrapper { // element-level View Transitions (Level 2), Chrome 126+ startViewTransition?: (cb: () => void | Promise) => { ready: Promise - finished: Promise - } - } - const doc_ = document as typeof document & { - // document-level View Transitions (Level 1), Chrome 111+ - startViewTransition?: (cb: () => void | Promise) => { - ready: Promise - finished: Promise - } - } - - // Shared error handler for transition.ready rejections. - const onTransitionError = (err: any) => { - // InvalidStateError just means the document was hidden during the transition. - if (err?.name !== 'InvalidStateError') { - captureException(err, { tags: { handler: 'pdf-preview' } }) } } @@ -145,44 +129,31 @@ export default class PDFJSWrapper { document.visibilityState !== 'hidden' ) { // Chrome 126+: element-level View Transition, scoped to this container. - // Async callback holds the "before" snapshot until pagerendered fires. + // The async callback holds the captured "before" state until the first + // new page is rendered, so the crossfade goes old→new, not old→blank. const transition = container.startViewTransition(async () => { setDocument() await firstPageRendered }) - transition.ready.catch(onTransitionError) - } else if ( - typeof doc_.startViewTransition === 'function' && - document.visibilityState !== 'hidden' - ) { - // Chrome 111+ / Edge 111+: document-level View Transition. - // Give the PDF container a unique view-transition-name so only it - // crossfades. We also suppress the auto-generated root-level animation - // (which would otherwise fade the entire page including the editor) by - // injecting a temporary