Skip to content

Commit a6aebbe

Browse files
saphidclaude
andauthored
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 parent 8e39994 commit a6aebbe

1 file changed

Lines changed: 26 additions & 4 deletions

File tree

‎apps/web/src/components/chat/ModelPickerContent.tsx‎

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ export function adjacentModelPickerProvider(input: {
144144
}
145145

146146
const EMPTY_MODEL_JUMP_LABELS = new Map<string, string>();
147+
const MODEL_LIST_ESTIMATED_ITEM_SIZE = 52;
147148

148149
function 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"

0 commit comments

Comments
 (0)