fix(session): disable todo dock auto-scroll (#20840)
parent
2002f08f2e
commit
3deee3a02b
|
|
@ -7,7 +7,7 @@ import { useSpring } from "@opencode-ai/ui/motion-spring"
|
||||||
import { TextReveal } from "@opencode-ai/ui/text-reveal"
|
import { TextReveal } from "@opencode-ai/ui/text-reveal"
|
||||||
import { TextStrikethrough } from "@opencode-ai/ui/text-strikethrough"
|
import { TextStrikethrough } from "@opencode-ai/ui/text-strikethrough"
|
||||||
import { createResizeObserver } from "@solid-primitives/resize-observer"
|
import { createResizeObserver } from "@solid-primitives/resize-observer"
|
||||||
import { Index, createEffect, createMemo, on, onCleanup } from "solid-js"
|
import { Index, createEffect, createMemo, onCleanup } from "solid-js"
|
||||||
import { createStore } from "solid-js/store"
|
import { createStore } from "solid-js/store"
|
||||||
import { composerEnabled, composerProbe } from "@/testing/session-composer"
|
import { composerEnabled, composerProbe } from "@/testing/session-composer"
|
||||||
import { useLanguage } from "@/context/language"
|
import { useLanguage } from "@/context/language"
|
||||||
|
|
@ -210,76 +210,25 @@ export function SessionTodoDock(props: {
|
||||||
opacity: `${Math.max(0, Math.min(1, 1 - hide()))}`,
|
opacity: `${Math.max(0, Math.min(1, 1 - hide()))}`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<TodoList todos={props.todos} open={!store.collapsed} />
|
<TodoList todos={props.todos} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</DockTray>
|
</DockTray>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function TodoList(props: { todos: Todo[]; open: boolean }) {
|
function TodoList(props: { todos: Todo[] }) {
|
||||||
const [store, setStore] = createStore({
|
const [store, setStore] = createStore({
|
||||||
stuck: false,
|
stuck: false,
|
||||||
scrolling: false,
|
|
||||||
})
|
|
||||||
let scrollRef!: HTMLDivElement
|
|
||||||
let timer: number | undefined
|
|
||||||
|
|
||||||
const inProgress = createMemo(() => props.todos.findIndex((todo) => todo.status === "in_progress"))
|
|
||||||
|
|
||||||
const ensure = () => {
|
|
||||||
if (!props.open) return
|
|
||||||
if (store.scrolling) return
|
|
||||||
if (!scrollRef || scrollRef.offsetParent === null) return
|
|
||||||
|
|
||||||
const el = scrollRef.querySelector("[data-in-progress]")
|
|
||||||
if (!(el instanceof HTMLElement)) return
|
|
||||||
|
|
||||||
const topFade = 16
|
|
||||||
const bottomFade = 44
|
|
||||||
const container = scrollRef.getBoundingClientRect()
|
|
||||||
const rect = el.getBoundingClientRect()
|
|
||||||
const top = rect.top - container.top + scrollRef.scrollTop
|
|
||||||
const bottom = rect.bottom - container.top + scrollRef.scrollTop
|
|
||||||
const viewTop = scrollRef.scrollTop + topFade
|
|
||||||
const viewBottom = scrollRef.scrollTop + scrollRef.clientHeight - bottomFade
|
|
||||||
|
|
||||||
if (top < viewTop) {
|
|
||||||
scrollRef.scrollTop = Math.max(0, top - topFade)
|
|
||||||
} else if (bottom > viewBottom) {
|
|
||||||
scrollRef.scrollTop = bottom - (scrollRef.clientHeight - bottomFade)
|
|
||||||
}
|
|
||||||
|
|
||||||
setStore("stuck", scrollRef.scrollTop > 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
createEffect(
|
|
||||||
on([() => props.open, inProgress], () => {
|
|
||||||
if (!props.open || inProgress() < 0) return
|
|
||||||
requestAnimationFrame(ensure)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
onCleanup(() => {
|
|
||||||
if (!timer) return
|
|
||||||
window.clearTimeout(timer)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<div
|
<div
|
||||||
class="px-3 pb-11 flex flex-col gap-1.5 max-h-42 overflow-y-auto no-scrollbar"
|
class="px-3 pb-11 flex flex-col gap-1.5 max-h-42 overflow-y-auto no-scrollbar"
|
||||||
ref={scrollRef}
|
|
||||||
style={{ "overflow-anchor": "none" }}
|
style={{ "overflow-anchor": "none" }}
|
||||||
onScroll={(e) => {
|
onScroll={(e) => {
|
||||||
setStore("stuck", e.currentTarget.scrollTop > 0)
|
setStore("stuck", e.currentTarget.scrollTop > 0)
|
||||||
setStore("scrolling", true)
|
|
||||||
if (timer) window.clearTimeout(timer)
|
|
||||||
timer = window.setTimeout(() => {
|
|
||||||
setStore("scrolling", false)
|
|
||||||
if (inProgress() < 0) return
|
|
||||||
requestAnimationFrame(ensure)
|
|
||||||
}, 250)
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Index each={props.todos}>
|
<Index each={props.todos}>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue