Skip to content

Fix auto-resize destroying horizontal scroll positions#567

Draft
mel-anthropic wants to merge 2 commits intomodelcontextprotocol:mainfrom
mel-anthropic:mel/fix-auto-resize-scroll-clamping
Draft

Fix auto-resize destroying horizontal scroll positions#567
mel-anthropic wants to merge 2 commits intomodelcontextprotocol:mainfrom
mel-anthropic:mel/fix-auto-resize-scroll-clamping

Conversation

@mel-anthropic
Copy link
Contributor

@mel-anthropic mel-anthropic commented Mar 25, 2026

Summary

Fix two issues in the SDK's auto-resize measurement that cause visual glitches during viewport transitions (e.g. inline ↔ fullscreen).

Changes

Fix horizontal scroll position destruction

setupSizeChangedNotifications() temporarily sets document.documentElement.style.width = 'fit-content' on every ResizeObserver callback to measure intrinsic content width. For responsive apps that derive their width from the container (width: 100%, flex: 1, etc.), fit-content resolves to 0px.

The getBoundingClientRect() call forces a synchronous reflow at 0px width. During this reflow, the browser clamps scrollLeft on all horizontal scroll containers to 0 (since scrollLeft cannot exceed scrollWidth - clientWidth, which is 0). When the style is restored, scrollLeft stays clamped — the browser does not un-clamp it.

This manifests as horizontal scroll views losing their position whenever the viewport resizes, which is most visible during inline ↔ fullscreen transitions.

Fix: Replace the fit-content width measurement with window.innerWidth. The height measurement (max-content) is preserved since hosts rely on it. Neither the iOS nor web host uses the width value from size-changed notifications — both only consume height — so this change has no functional impact on hosts.

Skip size-changed measurement during fullscreen

Hosts determine the container size in fullscreen mode, so size-changed notifications are not needed. The temporary max-content height override used for measurement causes visible vertical jitter during animated fullscreen transitions by briefly reflowing the document on every animation frame.

Fix: Skip measurement entirely when hostContext.displayMode === "fullscreen". Verified that all three hosts (iOS, Android, Web) ignore the size-changed height value for layout during fullscreen.

Testing

  • Confirmed scroll position fix on iOS: scrollLeft dropped from 6406 to 772 when entering fullscreen before the fix; stays stable after
  • Applied equivalent CSS fix in Safari Web Inspector and verified scroll position preserved
  • Confirmed fullscreen skip eliminates vertical jitter during animated transitions
  • All existing tests pass (88/88)

The setupSizeChangedNotifications method temporarily sets
html.style.width to 'fit-content' to measure intrinsic content width.
For responsive apps that derive width from their container (width: 100%,
flex: 1, etc.), fit-content resolves to 0px. The synchronous reflow at
0px causes browsers to clamp scrollLeft on all horizontal scroll
containers to 0, permanently destroying their scroll positions.

Replace the fit-content width measurement with window.innerWidth. The
height measurement (max-content) is preserved since hosts rely on it.
Neither the iOS nor web host uses the width value from size-changed
notifications, so this change has no functional impact on hosts while
fixing scroll position clamping for all MCP apps with horizontal scroll
views.
@pkg-pr-new
Copy link

pkg-pr-new bot commented Mar 25, 2026

Open in StackBlitz

@modelcontextprotocol/ext-apps

npm i https://pkg.pr.new/@modelcontextprotocol/ext-apps@567

@modelcontextprotocol/server-basic-preact

npm i https://pkg.pr.new/@modelcontextprotocol/server-basic-preact@567

@modelcontextprotocol/server-basic-react

npm i https://pkg.pr.new/@modelcontextprotocol/server-basic-react@567

@modelcontextprotocol/server-basic-solid

npm i https://pkg.pr.new/@modelcontextprotocol/server-basic-solid@567

@modelcontextprotocol/server-basic-svelte

npm i https://pkg.pr.new/@modelcontextprotocol/server-basic-svelte@567

@modelcontextprotocol/server-basic-vanillajs

npm i https://pkg.pr.new/@modelcontextprotocol/server-basic-vanillajs@567

@modelcontextprotocol/server-basic-vue

npm i https://pkg.pr.new/@modelcontextprotocol/server-basic-vue@567

@modelcontextprotocol/server-budget-allocator

npm i https://pkg.pr.new/@modelcontextprotocol/server-budget-allocator@567

@modelcontextprotocol/server-cohort-heatmap

npm i https://pkg.pr.new/@modelcontextprotocol/server-cohort-heatmap@567

@modelcontextprotocol/server-customer-segmentation

npm i https://pkg.pr.new/@modelcontextprotocol/server-customer-segmentation@567

@modelcontextprotocol/server-debug

npm i https://pkg.pr.new/@modelcontextprotocol/server-debug@567

@modelcontextprotocol/server-map

npm i https://pkg.pr.new/@modelcontextprotocol/server-map@567

@modelcontextprotocol/server-pdf

npm i https://pkg.pr.new/@modelcontextprotocol/server-pdf@567

@modelcontextprotocol/server-scenario-modeler

npm i https://pkg.pr.new/@modelcontextprotocol/server-scenario-modeler@567

@modelcontextprotocol/server-shadertoy

npm i https://pkg.pr.new/@modelcontextprotocol/server-shadertoy@567

@modelcontextprotocol/server-sheet-music

npm i https://pkg.pr.new/@modelcontextprotocol/server-sheet-music@567

@modelcontextprotocol/server-system-monitor

npm i https://pkg.pr.new/@modelcontextprotocol/server-system-monitor@567

@modelcontextprotocol/server-threejs

npm i https://pkg.pr.new/@modelcontextprotocol/server-threejs@567

@modelcontextprotocol/server-transcript

npm i https://pkg.pr.new/@modelcontextprotocol/server-transcript@567

@modelcontextprotocol/server-video-resource

npm i https://pkg.pr.new/@modelcontextprotocol/server-video-resource@567

@modelcontextprotocol/server-wiki-explorer

npm i https://pkg.pr.new/@modelcontextprotocol/server-wiki-explorer@567

commit: 0d1e17c

Hosts determine the container size in fullscreen mode, so size-changed
notifications are not needed. The temporary max-content height override
used for measurement causes visible vertical jitter during animated
fullscreen transitions by briefly reflowing the document on every frame.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant