From 4ccd25873f53a1761d6cdedba3cc8cf89390a4a9 Mon Sep 17 00:00:00 2001 From: DeyangChan Date: Fri, 6 Nov 2026 12:35:09 +0800 Subject: [PATCH] The canvas stays where the reviewer was when the page changes height Refitting the frame shrinks it first, which clamps the canvas scroll to 0. The scroll is now read before the frame shrinks and restored afterwards, as the modal path already did. A new end-to-end scenario fails without this. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 8 ++++++++ e2e/features/framing.feature | 9 +++++++++ e2e/steps/browser.steps.mjs | 12 ++++++++++++ plugins/vstack/.claude-plugin/plugin.json | 2 +- plugins/vstack/.codex-plugin/plugin.json | 2 +- plugins/vstack/skills/review/assets/workspace.html | 4 +++- 6 files changed, 34 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12599b6..1b161e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,14 @@ The version in `plugins/vstack/.claude-plugin/plugin.json` is what your host compares against to decide an update is available. See the release checklist in [`CONTRIBUTING.md`](CONTRIBUTING.md). +## 6.9.1 — 2026-11-06 + +**Fixed** + +- **The canvas stays where you were when the page changes height.** If you had + scrolled down a long page and it then grew or shrank, the canvas jumped back to + the top. It now keeps your place. + ## 6.9.0 — 2026-11-06 **Added** diff --git a/e2e/features/framing.feature b/e2e/features/framing.feature index 80efe4d..24c81f1 100644 --- a/e2e/features/framing.feature +++ b/e2e/features/framing.feature @@ -32,3 +32,12 @@ Feature: Long pages and overlays on the canvas Then the dialog is where the reviewer is looking When the framed page closes its confirmation dialog Then the canvas fits the whole page again + + @browser + Scenario: F4 — the canvas stays where the reviewer was when the page changes height + Given a long page is under review + And the reviewer opens the workspace + When the reviewer scrolls the canvas to the bottom + And the framed page grows + Then the canvas fits the whole page again + And the canvas has not moved diff --git a/e2e/steps/browser.steps.mjs b/e2e/steps/browser.steps.mjs index 7396e26..2ffa6bc 100644 --- a/e2e/steps/browser.steps.mjs +++ b/e2e/steps/browser.steps.mjs @@ -371,6 +371,18 @@ Then('the canvas fits the whole page again', async function () { }, 'the frame goes back to the height of the page') }) +When('the framed page grows', async function () { + this.canvasScrollWas = await this.browserPage.locator('#viewportBox').evaluate(element => element.scrollTop) + const framed = await framedWindow(this) + await framed.evaluate(() => { document.querySelector('main').style.minHeight = '2800px' }) +}) + +Then('the canvas has not moved', async function () { + const now = await this.browserPage.locator('#viewportBox').evaluate(element => element.scrollTop) + assert.ok(Math.abs(now - this.canvasScrollWas) <= 1, + `the canvas was at ${this.canvasScrollWas} and is now at ${now}`) +}) + When('the reviewer opens Clear all', async function () { await this.browserPage.locator('#btnClear').click() await this.browserPage.locator('#clearDialog[open]').waitFor() diff --git a/plugins/vstack/.claude-plugin/plugin.json b/plugins/vstack/.claude-plugin/plugin.json index 5f1056c..17fffb0 100644 --- a/plugins/vstack/.claude-plugin/plugin.json +++ b/plugins/vstack/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "vstack", "displayName": "Visual Stack", - "version": "6.9.0", + "version": "6.9.1", "description": "Stop prompting. Start pointing. Visual Stack adds a Figma-like feedback layer to Claude Code. Create a new screen from a prompt, screenshot, reference site, or design system, or open an app you already have running. Click any element and leave feedback exactly where the problem is hiding, and Claude publishes the next revision into the same workspace. Compare revisions on a timeline, preview desktop, tablet, and mobile layouts, and keep every comment attached to the element, route, and version it refers to. Wireframes are self-contained HTML and review state stays in your project.", "author": { "name": "Cavalry Collective", diff --git a/plugins/vstack/.codex-plugin/plugin.json b/plugins/vstack/.codex-plugin/plugin.json index f3bedb7..01a1898 100644 --- a/plugins/vstack/.codex-plugin/plugin.json +++ b/plugins/vstack/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "vstack", - "version": "6.9.0", + "version": "6.9.1", "description": "Stop prompting. Start pointing. Visual Stack adds a Figma-like feedback layer to Codex. Create a new screen from a prompt, screenshot, reference site, or design system, or open an app you already have running. Click any element and leave feedback exactly where the problem is hiding, and Codex publishes the next revision into the same workspace. Compare revisions on a timeline, preview desktop, tablet, and mobile layouts, and keep every comment attached to the element, route, and version it refers to. Wireframes are self-contained HTML and review state stays in your project.", "author": { "name": "Cavalry Collective", diff --git a/plugins/vstack/skills/review/assets/workspace.html b/plugins/vstack/skills/review/assets/workspace.html index e677fb2..59dfba3 100644 --- a/plugins/vstack/skills/review/assets/workspace.html +++ b/plugins/vstack/skills/review/assets/workspace.html @@ -3037,6 +3037,8 @@

return vh; } const inner = modalViewport ? (fwin()?.scrollY || 0) : 0; + // Read the outer scroll before apply(vh) shrinks the frame and clamps it to 0. + const outer = modalViewport ? 0 : vpBox.scrollTop; modalViewport = false; apply(vh); const natural = measure(); @@ -3044,7 +3046,7 @@

apply(natural); if (measure() > natural + 2) apply(vh); } - if (inner) vpBox.scrollTop = inner; + if (inner || outer) vpBox.scrollTop = inner || outer; requestAnimationFrame(() => { sizingFrame = false }); return parseFloat(frame.style.height) || 0; }