/** Scroll metrics of the viewport at one point in time. */ export interface ScrollSnapshot { scrollTop: number; scrollHeight: number; clientHeight: number; } /** * Distance from the bottom edge within which the user counts as "at the * bottom": entering it re-sticks, and tiny upward wiggles inside it (trackpad * inertia, macOS bounce) never unstick. */ export declare const RESTICK_PX = 24; export declare function distanceFromBottom(snapshot: ScrollSnapshot): number; /** * Classify one scroll event. `lastScrollTop` is the previous event's position. * Order matters: the bottom zone wins over direction, so bounce-back at the * bottom edge stays stuck. */ export declare function resolveScrollIntent(snapshot: ScrollSnapshot, lastScrollTop: number): 'stick' | 'unstick' | 'none'; /** * How the message list changed between two renders, judged purely from ids — * content growth inside an existing message (streaming) is deliberately NOT a * transition (the follow behaviour reacts to it via ResizeObserver instead). * * - `append` — new messages at the end (feeds the new-messages counter). * - `prepend` — older messages loaded at the top (needs the scroll anchor fix). * - `truncate` — trailing messages removed (stable head, shorter list): NOT an * arrival, so it must never bump the counter (review finding, P2 wave). * - `replace` — both ends changed or the list was reset (jump to bottom). * - `initial` — first non-empty render (open at the bottom). * Simultaneous append+prepend degrades to `replace` — rare (history load * during streaming) and safe: worst case is one jump to the bottom. */ export type ListTransition = 'none' | 'initial' | 'append' | 'prepend' | 'truncate' | 'replace'; export interface ListIdSnapshot { firstId: string | undefined; lastId: string | undefined; length: number; } export declare function classifyTransition(prev: ListIdSnapshot, next: ListIdSnapshot): ListTransition; /** * New messages added by an `append` transition. A same-length append (the tail * message was swapped for a new id) still counts as one new message. */ export declare function appendedCount(prevLength: number, nextLength: number): number;