From 7d935efa44ef9396258b4f540a8140855d4229aa Mon Sep 17 00:00:00 2001
From: King-of-Infinite-Space
<27502578+King-of-Infinite-Space@users.noreply.github.com>
Date: Fri, 5 Jun 2026 00:06:15 -0700
Subject: [PATCH] feat: improve chat overview with persistent viewport and
pinning
Introduces viewport state persistence and a pin toggle for the chat overview (tree view), along with minor fix and cleanup.
- Added a "Viewport Pinned" toggle to disable auto-focus on active node.
- Persists viewport coordinates (x, y, zoom), layout direction, and pin state to localStorage.
- Debounced the localStorage viewport save (300ms) to prevent blocking during smooth pan/zoom gestures.
- Structured overviewViewports cache as a self-pruning 50-item LRU to prevent unbounded storage bloat.
- Resolved an existing memory leak by properly cleaning up nodesInitialized, width, and height store subscriptions on component destroy.
- Fixed a scroll alignment bug when navigating from the Overview tree. Defers scrollIntoView to the next event loop tick (0ms) so dynamic elements can layout properly before calculating the target scroll coordinate.
- Removed unused imports.
---
src/lib/components/chat/Chat.svelte | 10 +-
src/lib/components/chat/Overview/Flow.svelte | 27 ++--
src/lib/components/chat/Overview/View.svelte | 149 ++++++++++++++-----
3 files changed, 136 insertions(+), 50 deletions(-)
diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte
index 69652dbe41..8f4f7106fb 100644
--- a/src/lib/components/chat/Chat.svelte
+++ b/src/lib/components/chat/Chat.svelte
@@ -454,10 +454,12 @@
await tick();
if (($settings?.scrollOnBranchChange ?? true) && scroll) {
- const messageElement = document.getElementById(`message-${message.id}`);
- if (messageElement) {
- messageElement.scrollIntoView({ behavior: 'smooth', block: 'start' });
- }
+ setTimeout(() => {
+ const messageElement = document.getElementById(`message-${message.id}`);
+ if (messageElement) {
+ messageElement.scrollIntoView({ behavior: 'smooth', block: 'start' });
+ }
+ }, 0);
}
await tick();
diff --git a/src/lib/components/chat/Overview/Flow.svelte b/src/lib/components/chat/Overview/Flow.svelte
index d3963e22ae..707c0b760e 100644
--- a/src/lib/components/chat/Overview/Flow.svelte
+++ b/src/lib/components/chat/Overview/Flow.svelte
@@ -1,7 +1,7 @@
dispatch('nodeclick', e.detail)}
- oninit={() => {
- console.log('Flow initialized');
- }}
+ on:nodeclick={(e) => onNodeClick?.(e.detail)}
+ oninit={() => {}}
>
+ (viewportPinned = !viewportPinned)}
+ title={viewportPinned ? $i18n.t('Viewport Pinned') : $i18n.t('Viewport Unpinned')}
+ >
+ {#if viewportPinned}
+
+ {:else}
+
+ {/if}
+
setLayoutDirection('vertical')} title="Vertical Layout">
diff --git a/src/lib/components/chat/Overview/View.svelte b/src/lib/components/chat/Overview/View.svelte
index d757c69dd2..6a88f12c7a 100644
--- a/src/lib/components/chat/Overview/View.svelte
+++ b/src/lib/components/chat/Overview/View.svelte
@@ -1,25 +1,20 @@