authorgravatar for saphid@gmail.comAlex Southwell <saphid@gmail.com> 2026-10-04 11:59:24+11:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-03 21:59:24-03:00
loga6aebbe2e06e6f2fb247a291272dcfbedb8ed63f
tree5143c0dc2cd2b90b087a1426ffeaa8b2a22052f8
parent8e399944393ee48054c0ca4aece0b6c9befd0998
signature Signed by PGP key B5690EEEBB952194

fix(web): size the model picker to its content (#15152)

Co-authored-by: saphid <saphid@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

1 files changed, 26 insertions(+), 4 deletions(-)

apps/web/src/components/chat/ModelPickerContent.tsx+26-4
......@@ -144,6 +144,7 @@ export function adjacentModelPickerProvider(input: {
144144}
145145
146146const EMPTY_MODEL_JUMP_LABELS = new Map<string, string>();
147const MODEL_LIST_ESTIMATED_ITEM_SIZE = 52;
147148
148149function ModelListSeparator() {
149150 return <div className="h-0.5" />;
......@@ -197,6 +198,7 @@ export const ModelPickerContent = memo(function ModelPickerContent(props: {
197198 const [showBottomScrollFade, setShowBottomScrollFade] = useState(false);
198199 const searchInputRef = useRef<HTMLInputElement>(null);
199200 const modelListRef = useRef<LegendListRef | null>(null);
201 const pickerContentRef = useRef<HTMLDivElement>(null);
200202 const highlightedModelKeyRef = useRef<string | null>(null);
201203 const favorites = useClientSettings((s) => s.favorites ?? []);
202204 const activeEntry = props.instanceEntries.find(
......@@ -699,6 +701,17 @@ export const ModelPickerContent = memo(function ModelPickerContent(props: {
699701 ),
700702 [visibleModels],
701703 );
704 const [modelListContentSize, setModelListContentSize] = useState(
705 () => filteredItemKeys.length * MODEL_LIST_ESTIMATED_ITEM_SIZE,
706 );
707 const [searchHeight, setSearchHeight] = useState(0);
708 useLayoutEffect(
709 () => modelListRef.current?.getState().listen("totalSize", setModelListContentSize),
710 [],
711 );
712 // Fit the list to its rows plus the combobox list `py-1` and LegendList `py-1.5`.
713 const modelListHeight =
714 filteredItemKeys.length === 0 ? 0 : `calc(${modelListContentSize}px + var(--spacing) * 5)`;
702715 const updateModelListScrollFades = useCallback(() => {
703716 const scrollElement = modelListRef.current?.getScrollableNode();
704717 if (!(scrollElement instanceof HTMLElement)) {
......@@ -815,7 +828,10 @@ export const ModelPickerContent = memo(function ModelPickerContent(props: {
815828 return (
816829 <TooltipProvider delay={0}>
817830 <div
818 className="relative flex h-screen max-h-86.5 w-screen max-w-90 flex-row overflow-hidden"
831 ref={pickerContentRef}
832 className="relative flex max-h-86.5 w-screen max-w-90 flex-row overflow-hidden"
833 // Hold the height from when the search started; results scroll instead of resizing.
834 style={isSearching ? { height: searchHeight } : undefined}
819835 data-model-picker-content="true"
820836 >
821837 {/* Sidebar */}
......@@ -890,7 +906,10 @@ export const ModelPickerContent = memo(function ModelPickerContent(props: {
890906 ref={searchInputRef}
891907 placeholder="Search models..."
892908 value={searchQuery}
893 onChange={(e) => setSearchQuery(e.target.value)}
909 onChange={(e) => {
910 if (!isSearching) setSearchHeight(pickerContentRef.current?.offsetHeight ?? 0);
911 setSearchQuery(e.target.value);
912 }}
894913 onKeyDown={(e) => {
895914 if (
896915 showSidebar &&
......@@ -944,7 +963,10 @@ export const ModelPickerContent = memo(function ModelPickerContent(props: {
944963 />
945964
946965 {/* Model list */}
947 <div className="relative min-h-0 flex-1 overflow-hidden pr-px">
966 <div
967 className="relative min-h-0 overflow-hidden pr-px"
968 style={{ height: modelListHeight }}
969 >
948970 <ComboboxListVirtualized>
949971 <LegendList<string>
950972 ref={modelListRef}
......@@ -1013,7 +1035,7 @@ export const ModelPickerContent = memo(function ModelPickerContent(props: {
10131035 />
10141036 );
10151037 }}
1016 estimatedItemSize={52}
1038 estimatedItemSize={MODEL_LIST_ESTIMATED_ITEM_SIZE}
10171039 drawDistance={480}
10181040 recycleItems
10191041 contentContainerClassName="pl-2 pr-px"