summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-01-04 06:14:24 -0600
committerAdam <[email protected]>2026-01-04 06:14:24 -0600
commit5053822bd6bc82ee13f4e586e5b88dad15ad9cad (patch)
treebef46b395be5bb292ed84dedb3ac5bdbae233b0a
parent177b01a8531c0918323f4b02984f1def304a74b2 (diff)
downloadopencode-5053822bd6bc82ee13f4e586e5b88dad15ad9cad.tar.gz
opencode-5053822bd6bc82ee13f4e586e5b88dad15ad9cad.zip
fix(app): auto-scroll
-rw-r--r--packages/ui/src/hooks/create-auto-scroll.tsx73
1 files changed, 62 insertions, 11 deletions
diff --git a/packages/ui/src/hooks/create-auto-scroll.tsx b/packages/ui/src/hooks/create-auto-scroll.tsx
index 6dacc2795..b9eae5488 100644
--- a/packages/ui/src/hooks/create-auto-scroll.tsx
+++ b/packages/ui/src/hooks/create-auto-scroll.tsx
@@ -11,6 +11,8 @@ export function createAutoScroll(options: AutoScrollOptions) {
let scroll: HTMLElement | undefined
let settling = false
let settleTimer: ReturnType<typeof setTimeout> | undefined
+ let down = false
+ let cleanup: (() => void) | undefined
const [store, setStore] = createStore({
contentRef: undefined as HTMLElement | undefined,
@@ -45,8 +47,43 @@ export function createAutoScroll(options: AutoScrollOptions) {
scrollToBottomNow(behavior)
}
+ const stop = () => {
+ if (!active()) return
+ if (store.userScrolled) return
+
+ setStore("userScrolled", true)
+ options.onUserInteracted?.()
+ }
+
+ const handleWheel = (e: WheelEvent) => {
+ if (e.deltaY >= 0) return
+ stop()
+ }
+
+ const handlePointerUp = () => {
+ down = false
+ window.removeEventListener("pointerup", handlePointerUp)
+ }
+
+ const handlePointerDown = () => {
+ if (down) return
+ down = true
+ window.addEventListener("pointerup", handlePointerUp)
+ }
+
+ const handleTouchEnd = () => {
+ down = false
+ window.removeEventListener("touchend", handleTouchEnd)
+ }
+
+ const handleTouchStart = () => {
+ if (down) return
+ down = true
+ window.addEventListener("touchend", handleTouchEnd)
+ }
+
const handleScroll = () => {
- if (!options.working()) return
+ if (!active()) return
if (!scroll) return
if (distanceFromBottom() < 10) {
@@ -54,18 +91,11 @@ export function createAutoScroll(options: AutoScrollOptions) {
return
}
- if (store.userScrolled) return
-
- setStore("userScrolled", true)
- options.onUserInteracted?.()
+ if (down) stop()
}
const handleInteraction = () => {
- if (!options.working()) return
- if (store.userScrolled) return
-
- setStore("userScrolled", true)
- options.onUserInteracted?.()
+ stop()
}
createResizeObserver(
@@ -99,12 +129,33 @@ export function createAutoScroll(options: AutoScrollOptions) {
onCleanup(() => {
if (settleTimer) clearTimeout(settleTimer)
+ if (cleanup) cleanup()
})
return {
scrollRef: (el: HTMLElement | undefined) => {
+ if (cleanup) {
+ cleanup()
+ cleanup = undefined
+ }
+
scroll = el
- if (el) el.style.overflowAnchor = "none"
+ down = false
+
+ if (!el) return
+
+ el.style.overflowAnchor = "none"
+ el.addEventListener("wheel", handleWheel, { passive: true })
+ el.addEventListener("pointerdown", handlePointerDown)
+ el.addEventListener("touchstart", handleTouchStart, { passive: true })
+
+ cleanup = () => {
+ el.removeEventListener("wheel", handleWheel)
+ el.removeEventListener("pointerdown", handlePointerDown)
+ el.removeEventListener("touchstart", handleTouchStart)
+ window.removeEventListener("pointerup", handlePointerUp)
+ window.removeEventListener("touchend", handleTouchEnd)
+ }
},
contentRef: (el: HTMLElement | undefined) => setStore("contentRef", el),
handleScroll,