import type { ConversationView, InboxState, LiveState } from "@earendil-works/pi-durable"; import type { Scope } from "./ui.ts"; /** Acknowledge the settled messages actually drawn in a visible conversation pane. */ export function readWhenVisible( scope: Scope, acknowledge: (entryId: number) => Promise, ): (view: ConversationView) => void { let current = 0; let readThrough = 0; let sending = false; let disposed = false; const read = async () => { if (disposed || document.visibilityState !== "visible" || sending || current <= readThrough) { return; } const entryId = current; sending = true; try { await acknowledge(entryId); readThrough = Math.max(readThrough, entryId); } catch (error) { console.error("[berry] read receipt:", error); } finally { sending = false; if (current !== entryId) void read(); } }; const onVisibility = () => void read(); document.addEventListener("visibilitychange", onVisibility); scope.add(() => { disposed = true; document.removeEventListener("visibilitychange", onVisibility); }); return ({ entries, docs }) => { const live = docs["pi.live"] as LiveState | undefined; const inbox = docs["pi.inbox"] as InboxState | undefined; current = live?.run || inbox?.items.length ? 0 : entries.findLast((entry) => entry.kind === "pi.assistant" || entry.kind === "pi.user") ?.id ?? 0; void read(); }; }