perf(panel): cached info and history paint first, lazy markdown up the thread, background prefetch

This commit is contained in:
hh committed 2026-09-02 05:16:13 +02:00
1 parent 2d903c7c5f
commit 5b851c11d4
10 files changed
+192 -16

No files matched your search

+47 -5
View File
@@ -3,8 +3,13 @@
import { usePanelHost } from "./host";
import { linkOf, renderMarkdown } from "./markdown";
let { text, class: className = "" }: { text: string; class?: string } =
$props();
let {
text,
class: className = "",
eager = true,
}: { text: string; class?: string; eager?: boolean } = $props();
const NEAR = "1200px";
const host = usePanelHost();
const html = $derived(host.markdown ? "" : renderMarkdown(text));
@@ -36,15 +41,52 @@
};
}
// The host renders when asked; far up the thread that is when the
// node comes near the viewport, with the plain text holding its place.
function render(node: HTMLElement, value: string) {
let dispose = host.markdown?.(node, value);
let dispose: (() => void) | undefined;
let watcher: IntersectionObserver | null = null;
let pending = value;
const now = (next: string) => {
dispose?.();
node.classList.remove("whitespace-pre-wrap");
dispose = host.markdown?.(node, next);
};
const later = (next: string) => {
pending = next;
node.textContent = next;
node.classList.add("whitespace-pre-wrap");
if (watcher) {
return;
}
watcher = new IntersectionObserver(
(entries) => {
if (entries.some((entry) => entry.isIntersecting)) {
watcher?.disconnect();
watcher = null;
now(pending);
}
},
{ rootMargin: NEAR }
);
watcher.observe(node);
};
if (eager || typeof IntersectionObserver === "undefined") {
now(value);
} else {
later(value);
}
return {
destroy() {
watcher?.disconnect();
dispose?.();
},
update(next: string) {
dispose?.();
dispose = host.markdown?.(node, next);
if (watcher) {
later(next);
} else {
now(next);
}
},
};
}