fix(ui): chat keeps the bottom pinned while async markdown lands

This commit is contained in:
hh
2026-08-29 19:26:45 +02:00
parent 60dc7db925
commit 2d12391176
+25 -3
View File
@@ -35,6 +35,7 @@
let showResults = $state(false);
let loadedAt = $state(new Date(0).toISOString());
let scroller = $state<HTMLDivElement | null>(null);
let body = $state<HTMLDivElement | null>(null);
let now = $state(Date.now());
let pinned = true;
@@ -90,6 +91,21 @@
}
});
// Markdown lands asynchronously (Obsidian renders after the DOM exists),
// so follow the content's height, not just the message count.
$effect(() => {
if (!(scroller && body)) {
return;
}
const observer = new ResizeObserver(() => {
if (pinned && scroller) {
scroller.scrollTop = scroller.scrollHeight;
}
});
observer.observe(body);
return () => observer.disconnect();
});
$effect(() => {
const timer = setInterval(() => {
if (model.running) {
@@ -124,13 +140,16 @@
</script>
<div
class="flex min-h-0 flex-1 flex-col gap-3 overflow-y-auto px-3 py-3 @md:px-6"
class="min-h-0 flex-1 overflow-y-auto px-3 py-3 @md:px-6"
onscroll={() => {
pinned = nearBottom();
}}
bind:this={scroller}
>
<span class="flex items-center gap-2 self-end text-muted-foreground text-xs">
<div class="flex flex-col gap-3" bind:this={body}>
<span
class="flex items-center gap-2 self-end text-muted-foreground text-xs"
>
<Switch aria-label="Show tool results" bind:checked={showResults} />
show tool results
</span>
@@ -169,7 +188,9 @@
/>
{:else if block.type === "tool_use"}
<p class="flex items-baseline gap-2 px-1 text-xs">
<span class="font-medium">{toolLabel(block.name ?? "?")}</span>
<span class="font-medium"
>{toolLabel(block.name ?? "?")}</span
>
<span class="truncate text-muted-foreground">
{clip(summarizeInput(block.name ?? "", block.input), 160)}
</span>
@@ -198,3 +219,4 @@
<TurnCard {now} {turn} />
{/each}
</div>
</div>