fix(app): don't show scroll-to-bottom unecessarily

This commit is contained in:
Adam
2026-02-04 10:00:55 -06:00
parent a3b281b2f3
commit 61d3f788b8

View File

@@ -279,6 +279,10 @@ export default function Page() {
pendingMessage: undefined as string | undefined,
scrollGesture: 0,
autoCreated: false,
scroll: {
overflow: false,
bottom: true,
},
})
createEffect(
@@ -795,6 +799,7 @@ export default function Page() {
let inputRef!: HTMLDivElement
let promptDock: HTMLDivElement | undefined
let scroller: HTMLDivElement | undefined
let content: HTMLDivElement | undefined
const scrollGestureWindowMs = 250
@@ -1618,10 +1623,40 @@ export default function Page() {
window.history.replaceState(null, "", window.location.href.replace(/#.*$/, ""))
}
let scrollStateFrame: number | undefined
let scrollStateTarget: HTMLDivElement | undefined
const updateScrollState = (el: HTMLDivElement) => {
const max = el.scrollHeight - el.clientHeight
const overflow = max > 1
const bottom = !overflow || el.scrollTop >= max - 2
if (ui.scroll.overflow === overflow && ui.scroll.bottom === bottom) return
setUi("scroll", { overflow, bottom })
}
const scheduleScrollState = (el: HTMLDivElement) => {
scrollStateTarget = el
if (scrollStateFrame !== undefined) return
scrollStateFrame = requestAnimationFrame(() => {
scrollStateFrame = undefined
const target = scrollStateTarget
scrollStateTarget = undefined
if (!target) return
updateScrollState(target)
})
}
const resumeScroll = () => {
setStore("messageId", undefined)
autoScroll.forceScrollToBottom()
clearMessageHash()
const el = scroller
if (el) scheduleScrollState(el)
}
// When the user returns to the bottom, treat the active message as "latest".
@@ -1657,8 +1692,17 @@ export default function Page() {
const setScrollRef = (el: HTMLDivElement | undefined) => {
scroller = el
autoScroll.scrollRef(el)
if (el) scheduleScrollState(el)
}
createResizeObserver(
() => content,
() => {
const el = scroller
if (el) scheduleScrollState(el)
},
)
const turnInit = 20
const turnBatch = 20
let turnHandle: number | undefined
@@ -1759,6 +1803,8 @@ export default function Page() {
el.scrollTo({ top: el.scrollHeight, behavior: "auto" })
})
}
if (el) scheduleScrollState(el)
},
)
@@ -1839,6 +1885,9 @@ export default function Page() {
const hash = window.location.hash.slice(1)
if (!hash) {
autoScroll.forceScrollToBottom()
const el = scroller
if (el) scheduleScrollState(el)
return
}
@@ -1864,6 +1913,9 @@ export default function Page() {
}
autoScroll.forceScrollToBottom()
const el = scroller
if (el) scheduleScrollState(el)
}
const closestMessage = (node: Element | null): HTMLElement | null => {
@@ -2029,6 +2081,7 @@ export default function Page() {
cancelTurnBackfill()
document.removeEventListener("keydown", handleKeyDown)
if (scrollSpyFrame !== undefined) cancelAnimationFrame(scrollSpyFrame)
if (scrollStateFrame !== undefined) cancelAnimationFrame(scrollStateFrame)
})
return (
@@ -2133,8 +2186,9 @@ export default function Page() {
<div
class="absolute left-1/2 -translate-x-1/2 bottom-[calc(var(--prompt-height,8rem)+32px)] z-[60] pointer-events-none transition-all duration-200 ease-out"
classList={{
"opacity-100 translate-y-0 scale-100": autoScroll.userScrolled(),
"opacity-0 translate-y-2 scale-95 pointer-events-none": !autoScroll.userScrolled(),
"opacity-100 translate-y-0 scale-100": ui.scroll.overflow && !ui.scroll.bottom,
"opacity-0 translate-y-2 scale-95 pointer-events-none":
!ui.scroll.overflow || ui.scroll.bottom,
}}
>
<button
@@ -2232,6 +2286,7 @@ export default function Page() {
markScrollGesture(e.currentTarget)
}}
onScroll={(e) => {
scheduleScrollState(e.currentTarget)
if (!hasScrollGesture()) return
autoScroll.handleScroll()
markScrollGesture(e.currentTarget)
@@ -2359,7 +2414,13 @@ export default function Page() {
</Show>
<div
ref={autoScroll.contentRef}
ref={(el) => {
content = el
autoScroll.contentRef(el)
const root = scroller
if (root) scheduleScrollState(root)
}}
role="log"
class="flex flex-col gap-12 items-start justify-start pb-[calc(var(--prompt-height,8rem)+64px)] md:pb-[calc(var(--prompt-height,10rem)+64px)] transition-[margin]"
classList={{