From 822722960daf5f17abbb8cfb14a3ad043a87dcf8 Mon Sep 17 00:00:00 2001 From: "posthog[bot]" <206114724+posthog[bot]@users.noreply.github.com> Date: Thu, 27 Aug 2026 09:32:19 +0000 Subject: [PATCH] fix(viewer): prevent ResizeObserver feedback loop Generated-By: PostHog Desktop Task-Id: b9264aea-99c2-4268-882d-f261b39fd5c9 --- .changeset/light-mangos-add.md | 5 +++++ e2e/generator.spec.ts | 13 +++++++++++++ src/components/Viewer.tsx | 11 ++++++++++- 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 .changeset/light-mangos-add.md diff --git a/.changeset/light-mangos-add.md b/.changeset/light-mangos-add.md new file mode 100644 index 0000000..99c7c58 --- /dev/null +++ b/.changeset/light-mangos-add.md @@ -0,0 +1,5 @@ +--- +'basekit': patch +--- + +Keep the 3D viewer responsive without browser resize warnings. diff --git a/e2e/generator.spec.ts b/e2e/generator.spec.ts index 8b35fd1..193be7c 100644 --- a/e2e/generator.spec.ts +++ b/e2e/generator.spec.ts @@ -66,6 +66,19 @@ test('builds the default base on load', { tag: '@ci' }, async ({ page }) => { expect(await triangles(page)).toBeGreaterThan(0) }) +test('keeps the 3D canvas out of the observed viewer layout', { tag: '@ci' }, async ({ page }) => { + const resizeErrors: string[] = [] + page.on('pageerror', (error) => { + if (error.message.includes('ResizeObserver')) resizeErrors.push(error.message) + }) + const canvas = page.locator('main canvas') + await expect(canvas).toHaveCSS('position', 'absolute') + await page.setViewportSize({ width: 390, height: 844 }) + await expect(canvas).toHaveCSS('position', 'absolute') + await expect.poll(() => triangles(page)).toBeGreaterThan(0) + expect(resizeErrors).toEqual([]) +}) + test('links to the source repository', async ({ page }) => { await expect(page.getByRole('link', { name: 'GitHub' })).toHaveAttribute('href', 'https://github.com/richardsolomou/basekit') }) diff --git a/src/components/Viewer.tsx b/src/components/Viewer.tsx index 630eb4e..233d219 100644 --- a/src/components/Viewer.tsx +++ b/src/components/Viewer.tsx @@ -94,6 +94,10 @@ export function Viewer({ mesh, width, length, height, round, fitToPart = false } // once per geometry swap instead. renderer.shadowMap.autoUpdate = false shadowsDirty.current = renderer + // Keep the canvas out of the observed container's layout. setSize() writes + // CSS dimensions as well as buffer dimensions, so an in-flow canvas can + // otherwise resize its own ResizeObserver ancestor. + Object.assign(renderer.domElement.style, { position: 'absolute', inset: '0' }) container.append(renderer.domElement) const world = new THREE.Scene() @@ -160,8 +164,13 @@ export function Viewer({ mesh, width, length, height, round, fitToPart = false } held.current = true }) + let lastWidth = -1 + let lastHeight = -1 const resize = () => { const { clientWidth: w, clientHeight: h } = container + if (w === lastWidth && h === lastHeight) return + lastWidth = w + lastHeight = h renderer.setSize(w, h) camera.aspect = w / Math.max(h, 1) camera.updateProjectionMatrix() @@ -302,7 +311,7 @@ export function Viewer({ mesh, width, length, height, round, fitToPart = false } return (
-
+
{/* Registration marks rather than a frame: the sheet is trimmed to size. */}
{CORNERS.map((corner) => (