From 1ebfc42db2ffdfa2cdc5949cebef364465deae49 Mon Sep 17 00:00:00 2001 From: Anthony Stirling <77850077+Frooodle@users.noreply.github.com> Date: Sun, 30 Aug 2026 10:44:09 +0100 Subject: [PATCH] Extend touch-scroll rule to the global pointer layer and harden the pan config canary --- .../core/components/viewer/RedactionAPIBridge.tsx | 2 ++ .../components/viewer/ViewerPointerProviders.tsx | 2 ++ .../core/components/viewer/viewerPanConfig.test.ts | 12 ++++++------ .../src/core/components/viewer/viewerPanConfig.ts | 2 ++ frontend/editor/src/core/styles/theme.css | 2 ++ .../stubbed/viewer-touch-device-selection.spec.ts | 10 ++++++++++ 6 files changed, 24 insertions(+), 6 deletions(-) diff --git a/frontend/editor/src/core/components/viewer/RedactionAPIBridge.tsx b/frontend/editor/src/core/components/viewer/RedactionAPIBridge.tsx index 49c350f4ee..5f86253aa2 100644 --- a/frontend/editor/src/core/components/viewer/RedactionAPIBridge.tsx +++ b/frontend/editor/src/core/components/viewer/RedactionAPIBridge.tsx @@ -43,6 +43,8 @@ function RedactionAPIBridgeInner({ documentId }: { documentId: string }) { }; }, [setBridgeReady]); + // The interaction mode is viewer-global, so a stranded redaction mode would block + // selection for the next document. useEffect(() => { return () => { try { diff --git a/frontend/editor/src/core/components/viewer/ViewerPointerProviders.tsx b/frontend/editor/src/core/components/viewer/ViewerPointerProviders.tsx index 38a10125c0..14a3cf8551 100644 --- a/frontend/editor/src/core/components/viewer/ViewerPointerProviders.tsx +++ b/frontend/editor/src/core/components/viewer/ViewerPointerProviders.tsx @@ -33,6 +33,7 @@ export function ViewerGlobalPointerProvider({ return ( {children} @@ -53,6 +54,7 @@ export function ViewerPagePointerProvider({ {children} diff --git a/frontend/editor/src/core/components/viewer/viewerPanConfig.test.ts b/frontend/editor/src/core/components/viewer/viewerPanConfig.test.ts index 03412137b7..f239cbca32 100644 --- a/frontend/editor/src/core/components/viewer/viewerPanConfig.test.ts +++ b/frontend/editor/src/core/components/viewer/viewerPanConfig.test.ts @@ -3,12 +3,12 @@ import { PanPluginPackage } from "@embedpdf/plugin-pan"; import { VIEWER_PAN_CONFIG } from "@app/components/viewer/viewerPanConfig"; describe("viewer pan plugin config", () => { - it("resolves defaultMode to never so pan never becomes the interaction default", () => { - const resolved = { - ...PanPluginPackage.manifest.defaultConfig, - ...VIEWER_PAN_CONFIG, - }; + it("overrides the plugin default so pan never becomes the interaction default", () => { + expect(VIEWER_PAN_CONFIG.defaultMode).toBe("never"); + }); - expect(resolved.defaultMode).toBe("never"); + // Canary: if upstream ever ships a safe default, this override can go. + it("still needs the override because the plugin ships defaultMode mobile", () => { + expect(PanPluginPackage.manifest.defaultConfig.defaultMode).toBe("mobile"); }); }); diff --git a/frontend/editor/src/core/components/viewer/viewerPanConfig.ts b/frontend/editor/src/core/components/viewer/viewerPanConfig.ts index 101284594c..7024378b79 100644 --- a/frontend/editor/src/core/components/viewer/viewerPanConfig.ts +++ b/frontend/editor/src/core/components/viewer/viewerPanConfig.ts @@ -1,5 +1,7 @@ import type { PanPluginConfig } from "@embedpdf/plugin-pan"; +// The plugin's own defaultConfig is "mobile", which makes pan the global default +// mode on any touch-capable device and locks activateDefaultMode into pan (#5175). export const VIEWER_PAN_CONFIG: PanPluginConfig = { defaultMode: "never", }; diff --git a/frontend/editor/src/core/styles/theme.css b/frontend/editor/src/core/styles/theme.css index cd2c5013cc..fe29e9ce27 100644 --- a/frontend/editor/src/core/styles/theme.css +++ b/frontend/editor/src/core/styles/theme.css @@ -602,6 +602,8 @@ background-color: var(--p-gray-500) !important; } +/* !important beats the inline touch-action:none the interaction manager writes for pointerMode. */ +[data-viewer-touch-scroll="on"], [data-viewer-touch-scroll="on"] .pdf-page-pointer-layer { touch-action: pan-y pinch-zoom !important; } diff --git a/frontend/editor/src/core/tests/stubbed/viewer-touch-device-selection.spec.ts b/frontend/editor/src/core/tests/stubbed/viewer-touch-device-selection.spec.ts index 5029f391e6..9fa9e12947 100644 --- a/frontend/editor/src/core/tests/stubbed/viewer-touch-device-selection.spec.ts +++ b/frontend/editor/src/core/tests/stubbed/viewer-touch-device-selection.spec.ts @@ -249,6 +249,16 @@ test.describe("touch-primary device", () => { }), ).toBe("pan-y pinch-zoom"); + // The global provider is an ancestor of the scroller, so its inline none would veto pinch-zoom. + expect( + await page.evaluate(() => { + const el = document.querySelector( + '[data-viewer-touch-scroll="on"]', + ); + return el ? getComputedStyle(el).touchAction : null; + }), + ).toBe("pan-y pinch-zoom"); + const cdp = await page.context().newCDPSession(page); const touchDrag = async (x: number, y: number, dx: number, dy: number) => { await cdp.send("Input.dispatchTouchEvent", {