From a2c90e12a62231e34f4956dccd19303fca95317b Mon Sep 17 00:00:00 2001 From: Yilin Jing Date: Wed, 9 Sep 2026 15:06:49 -0400 Subject: [PATCH] canvas: a two-finger pan over a board no longer goes back a page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A wheel event whose target is inside an iframe never reaches the parent document. tldraw stops this gesture by calling preventDefault on the wheel that reaches its container (useGestureEvents, bound passive:false), so over a board that call never happens and the browser takes the horizontal component as a back navigation. The page's own overscroll-behavior: none cannot help: overscroll chains one frame at a time, and a board's document declares nothing. be13507 fixed the case it could see by putting the board iframe behind its container, which also lets the pan reach tldraw. That line stays — it does the other half of the job — but it only applies to a board that is not being edited, and it is not available to the other two iframes: the inspector's preview is meant to take input, and the link card's cover sits on an opaque background, so a negative z-index there would paint it out of existence. What all three share is the HTML, so the fix goes there: every board is loaded with a style tag that stops overscroll in its own document. Appended rather than spliced, because 37 of the 180 boards emit no and a tag before the doctype would mean quirks mode. --- RELEASE-NOTES.md | 4 ++++ canvas/src/canvasLibrary.test.ts | 8 ++++++++ canvas/src/canvasLibrary.ts | 22 +++++++++++++++++++--- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index a502fc9..6b4eadc 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -21,6 +21,10 @@ toolkit carry the same version; `sp-canvas start` says so when they drift. Everything below is on `main` and reaches no install until a version is cut. +- **A two-finger pan over a board pans the canvas.** It used to send the browser + back a page instead, because a wheel event inside a board's iframe never + reaches the canvas and so never gets stopped. + ## v1.1.0 2026-09-09. The canvas became something a review can point at, and the plugin diff --git a/canvas/src/canvasLibrary.test.ts b/canvas/src/canvasLibrary.test.ts index a77744b..91cc180 100644 --- a/canvas/src/canvasLibrary.test.ts +++ b/canvas/src/canvasLibrary.test.ts @@ -26,4 +26,12 @@ describe('loadCanvasFileHtml', () => { expect(await loadCanvasFileHtml(missing)).toBeUndefined() expect(canvasFileHtml.has(missing)).toBe(false) }) + + it('ends every board with the tag that stops the browser back gesture', async () => { + // A wheel inside an iframe never reaches tldraw, so a board that does not stop overscroll + // in its own document turns a two-finger pan over it into a back navigation. + const path = readCanvasLibrary()[1][0].path + const html = await loadCanvasFileHtml(path) + expect(html).toMatch(/"; + +/** path -> the HTML every board iframe renders, for every file fetched so far. */ export const canvasFileHtml = new Map(); const canvasFileLoads = new Map>(); @@ -237,8 +252,9 @@ export function loadCanvasFileHtml(path: string): Promise { if (!load) { load = loader() .then((html) => { - canvasFileHtml.set(path, html); - return html; + const board = html + NO_OVERSCROLL; + canvasFileHtml.set(path, board); + return board; }) // A chunk can fail to arrive — a board deleted between discovery and first render, a // dev-server restart mid-flight. Without this the rejected promise stays in the map and