From 1bd080208c610e7868bf3011a348ef3d3e8beee0 Mon Sep 17 00:00:00 2001 From: Long Yixing Date: Fri, 24 Jul 2026 21:20:57 +0800 Subject: [PATCH] Fix Studio model picker toolbar overflow (#7403) * fix(studio): contain model picker toolbar * fix(studio): preserve model picker tab icons --- .../model-picker/components/model-selector.tsx | 8 +++++--- .../components/model-selector/pickers.tsx | 15 ++++++++++----- .../components/model-selector/pill-tabs.tsx | 7 ++++--- tests/studio/test_model_picker_contracts.py | 18 ++++++++++++++++++ 4 files changed, 37 insertions(+), 11 deletions(-) diff --git a/studio/frontend/src/features/model-picker/components/model-selector.tsx b/studio/frontend/src/features/model-picker/components/model-selector.tsx index cda6e49e24..9ae7e59a1f 100644 --- a/studio/frontend/src/features/model-picker/components/model-selector.tsx +++ b/studio/frontend/src/features/model-picker/components/model-selector.tsx @@ -301,12 +301,12 @@ const HUB_SECTION_TABS: { value: string; label: string; icon?: ReactNode }[] = [ { value: "recommended", label: "Recommended", - icon: , + icon: , }, { value: "downloaded", label: "On Device", - icon: , + icon: , }, ]; @@ -384,7 +384,9 @@ function ModelSelectorContent({ { value: "connected", label: "Connected", - icon: , + icon: ( + + ), }, ] : HUB_SECTION_TABS, diff --git a/studio/frontend/src/features/model-picker/components/model-selector/pickers.tsx b/studio/frontend/src/features/model-picker/components/model-selector/pickers.tsx index 20810b3f11..71b20058f4 100644 --- a/studio/frontend/src/features/model-picker/components/model-selector/pickers.tsx +++ b/studio/frontend/src/features/model-picker/components/model-selector/pickers.tsx @@ -3064,13 +3064,18 @@ export function HubModelPicker({ ) : null} - {/* Section tabs then the format and sort dropdowns, packed left with one - uniform gap between every control. The box is sized so the last - dropdown still lands on Search Hub's edge. Dropdowns hide on Connected. */} -
+ {/* Keep the left-packed controls on one line while they fit, then wrap + whole groups before their intrinsic widths cross the picker edge. + Dropdowns hide on Connected. */} +
{sectionToggle} {showConnected ? null : ( -
+
onValueChange(tab.value)} className={cn( "relative z-10 inline-flex items-center justify-center gap-1.5 rounded-full transition-colors", - fit ? "shrink-0" : "min-w-0 flex-1", + fit ? "min-w-0 shrink" : "min-w-0 flex-1", compact ? "h-7 px-2.5 text-ui-11" : "h-9 px-3 text-ui-12p5", value === tab.value ? "text-foreground" @@ -97,7 +98,7 @@ export function PillTabs({ )} > {tab.icon} - {tab.label} + {tab.label} ))}
diff --git a/tests/studio/test_model_picker_contracts.py b/tests/studio/test_model_picker_contracts.py index 62b1a3cf76..3934b72d83 100644 --- a/tests/studio/test_model_picker_contracts.py +++ b/tests/studio/test_model_picker_contracts.py @@ -217,6 +217,24 @@ def test_local_picker_rows_require_chat_capability(): assert "row.capabilities.canChat" in memo.group(0) +def test_model_picker_toolbar_reflows_before_crossing_picker_edge(): + """The content-sized section tabs and fixed-width dropdowns must reflow, + while an oversized tab group must shrink labels but preserve its icons.""" + picker = _read("features/model-picker/components/model-selector/pickers.tsx") + assert '"flex flex-wrap items-center gap-2"' in picker + assert 'hasConnected ? "-mr-4" : "-mr-2"' in picker + assert '"flex max-w-full min-w-0 flex-wrap items-center gap-2"' in picker + + tabs = _read("features/model-picker/components/model-selector/pill-tabs.tsx") + assert 'fit ? "min-w-0 shrink" : "min-w-0 flex-1"' in tabs + assert '{tab.label}' in tabs + + selector = _read("features/model-picker/components/model-selector.tsx") + assert 'icon={StarIcon} className="size-3.5 shrink-0"' in selector + assert 'icon={Download01Icon} className="size-3.5 shrink-0"' in selector + assert 'icon={CloudIcon} className="size-3.5 shrink-0"' in selector + + def test_native_picked_gguf_template_read_through_lease(): """A native (picked / drag-drop) GGUF's path lives only in its signed lease, and the picker chat-template GET has no lease plumbing, so the default