Files
PaperClipAI/ui/src/lib/issue-chat-scroll.ts
T

74 lines
2.1 KiB
TypeScript

export type IssueChatScrollTarget =
| { type: "element"; element: HTMLElement }
| { type: "window" };
export interface ComposerViewportSnapshot {
composerViewportTop: number;
}
export function resolveIssueChatScrollTarget(
doc: Document = document,
win: Window = window,
): IssueChatScrollTarget {
const mainContent = doc.getElementById("main-content");
if (mainContent instanceof HTMLElement) {
const overflowY = win.getComputedStyle(mainContent).overflowY;
const usesOwnScroll =
(overflowY === "auto" || overflowY === "scroll" || overflowY === "overlay")
&& mainContent.scrollHeight > mainContent.clientHeight + 1;
if (usesOwnScroll) {
return { type: "element", element: mainContent };
}
}
return { type: "window" };
}
export function captureComposerViewportSnapshot(
composerElement: HTMLElement | null,
): ComposerViewportSnapshot | null {
if (!composerElement) return null;
return {
composerViewportTop: composerElement.getBoundingClientRect().top,
};
}
export function shouldPreserveComposerViewport(
composerElement: HTMLElement | null,
doc: Document = document,
) {
if (!composerElement) return false;
const activeElement = doc.activeElement;
if (activeElement instanceof Node && composerElement.contains(activeElement)) {
return true;
}
const viewportHeight = doc.defaultView?.innerHeight ?? window.innerHeight;
const rect = composerElement.getBoundingClientRect();
return rect.bottom > 0 && rect.top < viewportHeight;
}
export function restoreComposerViewportSnapshot(
snapshot: ComposerViewportSnapshot | null,
composerElement: HTMLElement | null,
doc: Document = document,
win: Window = window,
) {
if (!snapshot || !composerElement) return;
const delta = composerElement.getBoundingClientRect().top - snapshot.composerViewportTop;
if (!Number.isFinite(delta) || Math.abs(delta) < 1) return;
const target = resolveIssueChatScrollTarget(doc, win);
if (target.type === "element") {
target.element.scrollTop += delta;
return;
}
win.scrollBy({ top: delta, left: 0, behavior: "auto" });
}