fix(web): stabilize chat scrolling and mobile alignment (#1278)

* fix(web): stabilize chat scrolling and mobile alignment

* fix(web): scope chat scroll containment
This commit is contained in:
Ananovo
2026-08-01 17:10:38 +08:00
committed by GitHub
parent fd20e584d6
commit 01bffa3c8c
2 changed files with 46 additions and 2 deletions
@@ -461,6 +461,28 @@ export function HappyThread(props: {
failureCount: 0,
autoPaused: false
})
useLayoutEffect(() => {
const viewport = viewportRef.current
if (!viewport) return
const updateScrollbarGutter = () => {
const supportsStableBothEdges = typeof CSS !== 'undefined'
&& typeof CSS.supports === 'function'
&& CSS.supports('scrollbar-gutter: stable both-edges')
const gutter = supportsStableBothEdges
? Math.max(0, (viewport.offsetWidth - viewport.clientWidth) / 2)
: 0
viewport.style.setProperty('--chat-scroll-gutter-inline', `${gutter}px`)
}
updateScrollbarGutter()
if (typeof ResizeObserver === 'undefined') return
const observer = new ResizeObserver(updateScrollbarGutter)
observer.observe(viewport)
return () => observer.disconnect()
}, [])
const requestOlderRef = useRef<(source: HistoryLoadSource) => Promise<OlderHistoryLoadResult>>(
() => Promise.resolve('transient-stop')
)
@@ -1414,10 +1436,10 @@ export function HappyThread(props: {
>
<div
ref={viewportRef}
className="app-scroll-y min-h-0 flex-1 overflow-x-hidden"
className="app-scroll-y chat-scroll-y min-h-0 flex-1 overflow-x-hidden"
tabIndex={0}
>
<div ref={contentRef} className="mx-auto w-full max-w-content min-w-0 p-3">
<div ref={contentRef} className="chat-scroll-content mx-auto w-full max-w-content min-w-0 p-3">
<div ref={topSentinelRef} className="h-px w-full" aria-hidden="true" />
{showSkeleton ? (
<MessageSkeleton />
+22
View File
@@ -310,6 +310,14 @@ body {
height: 100%;
}
/* Chat viewports own vertical scrolling; keep transiently oversized messages
* from creating a second document scrollbar without locking non-chat routes. */
html:has(.chat-scroll-y),
html:has(.chat-scroll-y) body,
html:has(.chat-scroll-y) #root {
overflow: hidden;
}
.app-scroll-y {
overflow-y: auto;
overscroll-behavior-y: contain;
@@ -317,6 +325,20 @@ body {
touch-action: pan-y;
}
/* On touch-first devices, keep the chat scrollbar from shifting centered content. */
@media (hover: none) and (pointer: coarse) {
.chat-scroll-y {
scrollbar-gutter: stable both-edges;
}
.chat-scroll-content {
padding-inline: max(
0.125rem,
calc(0.75rem - var(--chat-scroll-gutter-inline, 0px))
);
}
}
@media (hover: hover) and (pointer: fine) {
.session-list-item:hover,
.session-list-new-button:hover {