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.
This commit is contained in:
parent
8961b03163
commit
a7273bbf42
1 changed files with 17 additions and 12 deletions
29
src/app.ts
29
src/app.ts
|
|
@ -1287,26 +1287,31 @@ export class App extends Protocol<AppRequest, AppNotification, AppResult> {
|
|||
scheduled = true;
|
||||
requestAnimationFrame(() => {
|
||||
scheduled = false;
|
||||
|
||||
// Hosts determine the container size in fullscreen, so size-changed
|
||||
// notifications are not needed.
|
||||
if (this._hostContext?.displayMode === "fullscreen") {
|
||||
return;
|
||||
}
|
||||
|
||||
const html = document.documentElement;
|
||||
|
||||
// Measure actual content size by temporarily overriding html sizing.
|
||||
// Width uses fit-content so content wraps at the host-provided width.
|
||||
// Measure actual content height by temporarily overriding html sizing.
|
||||
// Height uses max-content because fit-content would clamp to the viewport
|
||||
// height when content is taller than the iframe, causing internal scrolling.
|
||||
const originalWidth = html.style.width;
|
||||
//
|
||||
// Width uses window.innerWidth instead of measuring via fit-content.
|
||||
// Setting html.style.width to fit-content forces a synchronous reflow at
|
||||
// 0px width for responsive apps (whose content derives width from the
|
||||
// container rather than having intrinsic width). This causes the browser
|
||||
// to clamp scrollLeft on any horizontal scroll containers to 0, permanently
|
||||
// destroying their scroll positions.
|
||||
const originalHeight = html.style.height;
|
||||
html.style.width = "fit-content";
|
||||
html.style.height = "max-content";
|
||||
const rect = html.getBoundingClientRect();
|
||||
html.style.width = originalWidth;
|
||||
const height = Math.ceil(html.getBoundingClientRect().height);
|
||||
html.style.height = originalHeight;
|
||||
|
||||
// Compensate for scrollbar width on Linux/Windows where scrollbars consume space.
|
||||
// On systems with overlay scrollbars (macOS), this will be 0.
|
||||
const scrollbarWidth = window.innerWidth - html.clientWidth;
|
||||
|
||||
const width = Math.ceil(rect.width + scrollbarWidth);
|
||||
const height = Math.ceil(rect.height);
|
||||
const width = Math.ceil(window.innerWidth);
|
||||
|
||||
// Only send if size actually changed (prevents feedback loops from style changes)
|
||||
if (width !== lastWidth || height !== lastHeight) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue