| 1 | // A gesture inside the timeline may belong to a nested tool result or code |
| 2 | // block. Only treat it as timeline navigation if it can chain to the outer list. |
| 3 | export function isTimelineScrollTarget( |
| 4 | target: EventTarget | null, |
| 5 | timeline: HTMLElement, |
| 6 | deltaY: number, |
| 7 | ): boolean { |
| 8 | if (!(target instanceof Element) || !timeline.contains(target) || deltaY === 0) return false; |
| 9 | |
| 10 | for ( |
| 11 | let element: Element | null = target; |
| 12 | element && element !== timeline; |
| 13 | element = element.parentElement |
| 14 | ) { |
| 15 | const style = getComputedStyle(element); |
| 16 | if (style.overflowY !== "auto" && style.overflowY !== "scroll") continue; |
| 17 | |
| 18 | const canScroll = |
| 19 | deltaY < 0 |
| 20 | ? element.scrollTop > 0 |
| 21 | : element.scrollTop < element.scrollHeight - element.clientHeight; |
| 22 | if ( |
| 23 | canScroll || |
| 24 | style.overscrollBehaviorY === "contain" || |
| 25 | style.overscrollBehaviorY === "none" |
| 26 | ) { |
| 27 | return false; |
| 28 | } |
| 29 | } |
| 30 | return true; |
| 31 | } |