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