Skip to content

Commit a7273bb

Browse files
Fix auto-resize destroying horizontal scroll positions (#567)
* Fix auto-resize destroying horizontal scroll positions 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. * 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 frame.
1 parent 8961b03 commit a7273bb

File tree

1 file changed

+17
-12
lines changed

1 file changed

+17
-12
lines changed

src/app.ts

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1287,26 +1287,31 @@ export class App extends Protocol<AppRequest, AppNotification, AppResult> {
12871287
scheduled = true;
12881288
requestAnimationFrame(() => {
12891289
scheduled = false;
1290+
1291+
// Hosts determine the container size in fullscreen, so size-changed
1292+
// notifications are not needed.
1293+
if (this._hostContext?.displayMode === "fullscreen") {
1294+
return;
1295+
}
1296+
12901297
const html = document.documentElement;
12911298

1292-
// Measure actual content size by temporarily overriding html sizing.
1293-
// Width uses fit-content so content wraps at the host-provided width.
1299+
// Measure actual content height by temporarily overriding html sizing.
12941300
// Height uses max-content because fit-content would clamp to the viewport
12951301
// height when content is taller than the iframe, causing internal scrolling.
1296-
const originalWidth = html.style.width;
1302+
//
1303+
// Width uses window.innerWidth instead of measuring via fit-content.
1304+
// Setting html.style.width to fit-content forces a synchronous reflow at
1305+
// 0px width for responsive apps (whose content derives width from the
1306+
// container rather than having intrinsic width). This causes the browser
1307+
// to clamp scrollLeft on any horizontal scroll containers to 0, permanently
1308+
// destroying their scroll positions.
12971309
const originalHeight = html.style.height;
1298-
html.style.width = "fit-content";
12991310
html.style.height = "max-content";
1300-
const rect = html.getBoundingClientRect();
1301-
html.style.width = originalWidth;
1311+
const height = Math.ceil(html.getBoundingClientRect().height);
13021312
html.style.height = originalHeight;
13031313

1304-
// Compensate for scrollbar width on Linux/Windows where scrollbars consume space.
1305-
// On systems with overlay scrollbars (macOS), this will be 0.
1306-
const scrollbarWidth = window.innerWidth - html.clientWidth;
1307-
1308-
const width = Math.ceil(rect.width + scrollbarWidth);
1309-
const height = Math.ceil(rect.height);
1314+
const width = Math.ceil(window.innerWidth);
13101315

13111316
// Only send if size actually changed (prevents feedback loops from style changes)
13121317
if (width !== lastWidth || height !== lastHeight) {

0 commit comments

Comments
 (0)