Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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**
Expand Down
9 changes: 9 additions & 0 deletions e2e/features/framing.feature
Original file line number Diff line number Diff line change
Expand Up @@ -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
12 changes: 12 additions & 0 deletions e2e/steps/browser.steps.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion plugins/vstack/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
2 changes: 1 addition & 1 deletion plugins/vstack/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
4 changes: 3 additions & 1 deletion plugins/vstack/skills/review/assets/workspace.html
Original file line number Diff line number Diff line change
Expand Up @@ -3037,14 +3037,16 @@ <h2 id="doneTitle"></h2>
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();
if (natural > vh + 2) {
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;
}
Expand Down
Loading