From 4b171ee2074c2fdc3f75f93756915582ca6b8f9c Mon Sep 17 00:00:00 2001 From: vhqtvn Date: Mon, 27 Apr 2026 16:14:09 +0700 Subject: [PATCH] Prevent page overscroll in chat layout (#1034) Co-authored-by: vhqtvn <8930337+vhqtvn@users.noreply.github.com> --- .../ui/src/components/chat/ChatContainer.tsx | 8 ++++++-- .../ui/src/components/layout/MainLayout.tsx | 19 +++++++++++-------- packages/ui/src/styles/design-system.css | 8 ++++---- packages/ui/src/styles/mobile.css | 5 +++++ 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/packages/ui/src/components/chat/ChatContainer.tsx b/packages/ui/src/components/chat/ChatContainer.tsx index 70d893e5..ff3401ae 100644 --- a/packages/ui/src/components/chat/ChatContainer.tsx +++ b/packages/ui/src/components/chat/ChatContainer.tsx @@ -48,7 +48,11 @@ const EMPTY_QUESTIONS: QuestionRequest[] = []; const IDLE_SESSION_STATUS = { type: 'idle' as const }; const SESSION_RESELECTED_EVENT = 'openchamber:session-reselected'; const DEFAULT_RETRY_MESSAGE = 'Quota limit reached. Retrying automatically.'; -const CHAT_SCROLL_STYLE = { overflowAnchor: 'none' } as const; +const CHAT_SCROLL_STYLE = { + overflowAnchor: 'none', + overscrollBehavior: 'contain', + overscrollBehaviorY: 'contain', +} as const; const CHAT_NAVIGATION_IGNORED_TARGET_SELECTOR = [ 'a[href]', 'button', @@ -813,7 +817,7 @@ export const ChatContainer: React.FC = () => { )} aria-hidden={isDesktopExpandedInput} > -
+
{HYDRATING_SKELETON_ITEMS.map((item) => (
diff --git a/packages/ui/src/components/layout/MainLayout.tsx b/packages/ui/src/components/layout/MainLayout.tsx index 8e08e4c0..a36aa7ad 100644 --- a/packages/ui/src/components/layout/MainLayout.tsx +++ b/packages/ui/src/components/layout/MainLayout.tsx @@ -383,6 +383,7 @@ export const MainLayout: React.FC = () => { return (
{ aria-hidden={!mobileLeftDrawerOpen} >
-
+
@@ -541,7 +543,7 @@ export const MainLayout: React.FC = () => { )} aria-hidden={!isRightSidebarOpen} > -
+
@@ -550,12 +552,13 @@ export const MainLayout: React.FC = () => { {/* Main content area (fixed) */}
-
+
@@ -599,7 +602,7 @@ export const MainLayout: React.FC = () => {
+ )} data-page-scroll-lock="true"> {isSidebarOpen ? ( <>
{ 'bg-sidebar', isSidebarOpen && 'border-l border-border/50 rounded-tl-[10px] rounded-bl-[10px]', isRightSidebarOpen && 'border-r border-border/50 rounded-tr-[10px] rounded-br-[10px]' - )}> + )} data-page-scroll-lock="true">
-
-
+ )} data-page-scroll-lock="true"> +
+
diff --git a/packages/ui/src/styles/design-system.css b/packages/ui/src/styles/design-system.css index b2b76a8d..51732fe4 100644 --- a/packages/ui/src/styles/design-system.css +++ b/packages/ui/src/styles/design-system.css @@ -125,11 +125,11 @@ font-weight: var(--ui-regular-font-weight, 400); } - /* Desktop shell: prevent rubber-band scrolling of the page itself. + /* Prevent rubber-band scrolling of the page itself. App scroll should live inside dedicated scroll containers. */ - :root.desktop-runtime, - :root.desktop-runtime body, - :root.desktop-runtime #root { + :root, + :root body, + :root #root { height: 100%; overflow: hidden; overscroll-behavior: none; diff --git a/packages/ui/src/styles/mobile.css b/packages/ui/src/styles/mobile.css index 381c0484..6cdf52c6 100644 --- a/packages/ui/src/styles/mobile.css +++ b/packages/ui/src/styles/mobile.css @@ -150,6 +150,11 @@ -webkit-overflow-scrolling: touch; } + :root.mobile-pointer:not(.desktop-runtime) [data-page-scroll-lock="true"] { + overflow: hidden !important; + overscroll-behavior: none; + } + /* Keep markdown code blocks horizontally scrollable on mobile. */ :root.mobile-pointer:not(.desktop-runtime) .markdown-content [data-markdown="code-block-body"] { overflow-x: auto !important;