diff --git a/src/lib/components/chat/ModelSelector/Selector.svelte b/src/lib/components/chat/ModelSelector/Selector.svelte index 88f79c983d..e6de34a563 100644 --- a/src/lib/components/chat/ModelSelector/Selector.svelte +++ b/src/lib/components/chat/ModelSelector/Selector.svelte @@ -74,6 +74,7 @@ let show = false; let triggerElement: HTMLElement | null = null; let contentElement: HTMLElement | null = null; + let panelElement: HTMLElement | null = null; let dropdownPosition = { top: 0, left: 0, maxHeight: undefined as number | undefined }; let positionFrame: number | undefined; let settleTimers: number[] = []; @@ -87,6 +88,19 @@ }; }; + const measureContent = () => { + if (!contentElement) return { width: 0, height: 0 }; + + const previousMaxHeight = panelElement?.style.maxHeight; + if (panelElement) panelElement.style.maxHeight = ''; + const rect = contentElement.getBoundingClientRect(); + if (panelElement && previousMaxHeight !== undefined) { + panelElement.style.maxHeight = previousMaxHeight; + } + + return { width: rect.width, height: rect.height }; + }; + const visualViewportRect = () => { const viewport = window.visualViewport; return { @@ -100,9 +114,7 @@ const updatePosition = () => { if (!show || !triggerElement) return; const rect = triggerElement.getBoundingClientRect(); - const contentRect = contentElement?.getBoundingClientRect(); - const contentWidth = contentRect?.width ?? 0; - const contentHeight = contentRect?.height ?? 0; + const { width: contentWidth, height: contentHeight } = measureContent(); const viewport = visualViewportRect(); const viewportRight = viewport.left + viewport.width; const viewportBottom = viewport.top + viewport.height; @@ -155,6 +167,11 @@ } }; + const handleScroll = (event: Event) => { + if (event.target instanceof Node && contentElement?.contains(event.target)) return; + schedulePositionUpdate(); + }; + const toggleOpen = async () => { show = !show; if (show) { @@ -550,14 +567,14 @@ tags = Array.from(new Set(tags)).sort((a, b) => a.localeCompare(b)); } - window.addEventListener('scroll', schedulePositionUpdate, true); + window.addEventListener('scroll', handleScroll, true); window.visualViewport?.addEventListener('resize', scheduleSettledPositionUpdates); window.visualViewport?.addEventListener('scroll', schedulePositionUpdate); return () => { if (positionFrame != null) cancelAnimationFrame(positionFrame); for (const timer of settleTimers) window.clearTimeout(timer); - window.removeEventListener('scroll', schedulePositionUpdate, true); + window.removeEventListener('scroll', handleScroll, true); window.visualViewport?.removeEventListener('resize', scheduleSettledPositionUpdates); window.visualViewport?.removeEventListener('scroll', schedulePositionUpdate); }; @@ -642,7 +659,28 @@ let listScrollTop = 0; let listContainer; - $: listViewportHeight = Math.min(288, Math.max(96, (dropdownPosition.maxHeight ?? 352) - 64)); + let listViewportHeight = 288; + + const trackListViewport = (node: HTMLElement) => { + const updateHeight = () => { + listViewportHeight = node.clientHeight || 288; + }; + + updateHeight(); + + if (!('ResizeObserver' in window)) { + return { destroy() {} }; + } + + const observer = new ResizeObserver(updateHeight); + observer.observe(node); + + return { + destroy() { + observer.disconnect(); + } + }; + }; $: visibleStart = Math.max(0, Math.floor(listScrollTop / ITEM_HEIGHT) - OVERSCAN); $: visibleEnd = Math.min( @@ -707,17 +745,17 @@
{#if searchEnabled} -
+
{/if} -
+
{#if filteredItems.length === 0} {#if items.length === 0 && $user?.role === 'admin'}
@@ -818,11 +856,12 @@ {:else}
{ listScrollTop = listContainer.scrollTop; }} @@ -936,7 +975,7 @@
{#if showSetDefault} -
+
{:else} -
+
{/if}