From d56d74b3877192af37961ea6c78cbcf0ec70f343 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Thu, 23 Apr 2026 19:39:06 +0900 Subject: [PATCH] refac --- src/lib/components/chat/Messages.svelte | 117 +----------------- .../components/chat/Messages/Message.svelte | 13 +- 2 files changed, 13 insertions(+), 117 deletions(-) diff --git a/src/lib/components/chat/Messages.svelte b/src/lib/components/chat/Messages.svelte index 151f89fb2a..51a75f1242 100644 --- a/src/lib/components/chat/Messages.svelte +++ b/src/lib/components/chat/Messages.svelte @@ -60,102 +60,7 @@ export let messagesCount: number | null = 8; let messagesLoading = false; - // Off-screen message unloading. Heights are measured on scroll so spacers - // always match real sizes — no scroll jumps, no feedback loops needed. - const OVERSCAN = 3; - const DEFAULT_HEIGHT = 150; - let visibleStart = 0; - let visibleEnd = 0; - let messageHeights = new Map(); - let topSpacerHeight = 0; - let bottomSpacerHeight = 0; - let pendingCull = null; - - // Helper: get height for a message (cached or default) - const heightOf = (id) => messageHeights.get(id) ?? DEFAULT_HEIGHT; - - /** Measure all currently rendered message elements and cache their heights */ - const measureMessageHeights = () => { - const elements = document - .getElementById('messages-container') - ?.querySelectorAll('[role="listitem"]'); - if (!elements) return; - - messageHeights = new Map([ - ...messageHeights, - ...Array.from(elements) - .map((el, i) => [messages[visibleStart + i]?.id, el.getBoundingClientRect().height]) - .filter(([id]) => id != null) - ]); - }; - - /** Compute visible range from current scroll position and apply */ - const updateVisibleRange = () => { - const container = document.getElementById('messages-container'); - if (!container || messages.length === 0) return; - - const st = container.scrollTop; - const ch = container.clientHeight; - - // Build prefix sums from measured heights - const prefixSums = messages.reduce( - (acc, m) => [...acc, acc[acc.length - 1] + heightOf(m.id)], - [0] - ); - - const firstVisible = Math.max(0, prefixSums.findIndex((h) => h > st) - 1); - const lastVisible = prefixSums.findIndex((h) => h > st + ch); - - // Only cull messages that have been measured (so spacer height is accurate) - // findIndex returns -1 when all are measured → no limit on culling - const firstUnmeasured = messages.findIndex((m) => !messageHeights.has(m.id)); - const cullLimit = firstUnmeasured === -1 ? messages.length : firstUnmeasured; - - visibleStart = Math.max(0, Math.min(firstVisible - OVERSCAN, cullLimit)); - visibleEnd = Math.min( - messages.length, - (lastVisible === -1 ? messages.length : lastVisible) + OVERSCAN - ); - topSpacerHeight = prefixSums[visibleStart] ?? 0; - bottomSpacerHeight = (prefixSums[messages.length] ?? 0) - (prefixSums[visibleEnd] ?? 0); - }; - - /** Scroll handler: measure every frame, cull via rAF (same throttle as pendingRebuild) */ - const handleContainerScroll = () => { - measureMessageHeights(); - - // Don't cull during progressive loading - if (messagesLoading) return; - - if (!pendingCull) { - pendingCull = requestAnimationFrame(() => { - pendingCull = null; - updateVisibleRange(); - }); - } - }; - - let scrollListenerAttached = false; - - const attachScrollListener = () => { - if (scrollListenerAttached) return; - const container = document.getElementById('messages-container'); - if (!container) return; - - container.addEventListener('scroll', handleContainerScroll, { passive: true }); - scrollListenerAttached = true; - }; - - onMount(() => { - attachScrollListener(); - }); - onDestroy(() => { - const container = document.getElementById('messages-container'); - if (container && scrollListenerAttached) { - container.removeEventListener('scroll', handleContainerScroll); - } - cancelAnimationFrame(pendingCull); cancelAnimationFrame(pendingRebuild); }); @@ -169,12 +74,6 @@ buildMessages(); - // Show all messages during progressive loading (no culling) - visibleStart = 0; - visibleEnd = messages.length; - topSpacerHeight = 0; - bottomSpacerHeight = 0; - await tick(); messagesLoading = false; @@ -201,7 +100,6 @@ } messages = _messages.reverse(); - visibleEnd = messages.length; }; // Throttle message list rebuilds to once per animation frame during streaming. @@ -220,8 +118,6 @@ cancelAnimationFrame(pendingRebuild); pendingRebuild = null; buildMessages(); - // No explicit culling needed — scrollToBottom will fire a scroll event, - // which triggers handleContainerScroll → rAF → updateVisibleRange } else if (_messages) { // Content update (streaming) — throttle to once per frame if (!pendingRebuild) { @@ -570,13 +466,7 @@ {/if}
diff --git a/src/lib/components/chat/Messages/Message.svelte b/src/lib/components/chat/Messages/Message.svelte index d9ca32492a..b161aa8556 100644 --- a/src/lib/components/chat/Messages/Message.svelte +++ b/src/lib/components/chat/Messages/Message.svelte @@ -49,7 +49,7 @@ role="listitem" class="flex flex-col justify-between px-5 mb-3 w-full {($settings?.widescreenMode ?? null) ? 'max-w-full' - : 'max-w-5xl'} mx-auto rounded-lg group" + : 'max-w-5xl'} mx-auto rounded-lg group message-listitem" > {#if history.messages[messageId]} {#if history.messages[messageId].role === 'user'} @@ -128,3 +128,14 @@ {/if} {/if}
+ + +