From a6aebbe2e06e6f2fb247a291272dcfbedb8ed63f Mon Sep 17 00:00:00 2001 From: Alex Southwell Date: Sun, 4 Oct 2026 11:59:24 +1100 Subject: [PATCH] fix(web): size the model picker to its content (#15152) Co-authored-by: saphid Co-authored-by: Claude Opus 5.5 (1M context) --- .../components/chat/ModelPickerContent.tsx | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/apps/web/src/components/chat/ModelPickerContent.tsx b/apps/web/src/components/chat/ModelPickerContent.tsx index 70181947bc4a7352f3b64fe53f47f2c85f585511..53a2e9c45bacf7da154c077388b5af7d2ad6239d 100644 --- a/apps/web/src/components/chat/ModelPickerContent.tsx +++ b/apps/web/src/components/chat/ModelPickerContent.tsx @@ -144,6 +144,7 @@ export function adjacentModelPickerProvider(input: { } const EMPTY_MODEL_JUMP_LABELS = new Map(); +const MODEL_LIST_ESTIMATED_ITEM_SIZE = 52; function ModelListSeparator() { return
; @@ -197,6 +198,7 @@ export const ModelPickerContent = memo(function ModelPickerContent(props: { const [showBottomScrollFade, setShowBottomScrollFade] = useState(false); const searchInputRef = useRef(null); const modelListRef = useRef(null); + const pickerContentRef = useRef(null); const highlightedModelKeyRef = useRef(null); const favorites = useClientSettings((s) => s.favorites ?? []); const activeEntry = props.instanceEntries.find( @@ -699,6 +701,17 @@ export const ModelPickerContent = memo(function ModelPickerContent(props: { ), [visibleModels], ); + const [modelListContentSize, setModelListContentSize] = useState( + () => filteredItemKeys.length * MODEL_LIST_ESTIMATED_ITEM_SIZE, + ); + const [searchHeight, setSearchHeight] = useState(0); + useLayoutEffect( + () => modelListRef.current?.getState().listen("totalSize", setModelListContentSize), + [], + ); + // Fit the list to its rows plus the combobox list `py-1` and LegendList `py-1.5`. + const modelListHeight = + filteredItemKeys.length === 0 ? 0 : `calc(${modelListContentSize}px + var(--spacing) * 5)`; const updateModelListScrollFades = useCallback(() => { const scrollElement = modelListRef.current?.getScrollableNode(); if (!(scrollElement instanceof HTMLElement)) { @@ -815,7 +828,10 @@ export const ModelPickerContent = memo(function ModelPickerContent(props: { return (
{/* Sidebar */} @@ -890,7 +906,10 @@ export const ModelPickerContent = memo(function ModelPickerContent(props: { ref={searchInputRef} placeholder="Search models..." value={searchQuery} - onChange={(e) => setSearchQuery(e.target.value)} + onChange={(e) => { + if (!isSearching) setSearchHeight(pickerContentRef.current?.offsetHeight ?? 0); + setSearchQuery(e.target.value); + }} onKeyDown={(e) => { if ( showSidebar && @@ -944,7 +963,10 @@ export const ModelPickerContent = memo(function ModelPickerContent(props: { /> {/* Model list */} -
+
ref={modelListRef} @@ -1013,7 +1035,7 @@ export const ModelPickerContent = memo(function ModelPickerContent(props: { /> ); }} - estimatedItemSize={52} + estimatedItemSize={MODEL_LIST_ESTIMATED_ITEM_SIZE} drawDistance={480} recycleItems contentContainerClassName="pl-2 pr-px" -- 2.54.0