Files
PaperClipAI/ui/src/components/task-chat/scroll-anchor.ts
T
DottaandPaperclip cd4c4ed205 fix(ui): stabilize task loading and live feeds (#13095)
Coordinate initial conversation reveal, preserve message identity and reading anchors during live updates, and bound transcript reads with recoverable retries. Cover desktop/mobile navigation and rich task loading with actual-route browser tests.

Verified all Linux CI gates, 5,575 local UI tests, eight layout browser scenarios, and recorded native Codex walkthroughs. Greptile: 5/5; all review findings resolved.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-09 10:00:37 -05:00

27 lines
1.0 KiB
TypeScript

/** Keep a reading position by logical row identity, not total scroll height. */
export interface ThreadScrollAnchor {
id: string;
top: number;
}
export function readThreadScrollAnchor(root: Element, viewportTop: number, viewportBottom: number): ThreadScrollAnchor | null {
for (const row of root.querySelectorAll<HTMLElement>("[data-thread-anchor]")) {
const rect = row.getBoundingClientRect();
if (rect.height > 0 && rect.bottom > viewportTop && rect.top < viewportBottom) {
return { id: row.dataset.threadAnchor!, top: rect.top - viewportTop };
}
}
return null;
}
export function threadScrollAnchorDelta(root: Element, anchor: ThreadScrollAnchor | null, viewportTop: number): number {
if (!anchor) return 0;
// IDs are opaque and need not be safe CSS selectors.
for (const row of root.querySelectorAll<HTMLElement>("[data-thread-anchor]")) {
if (row.dataset.threadAnchor === anchor.id) {
return row.getBoundingClientRect().top - viewportTop - anchor.top;
}
}
return 0;
}