fix(app): open sole provider model picker directly

Running chats expose one provider, so an intermediate provider screen and back action are redundant. Restore the direct model-list entry behavior while preserving the multi-provider picker.
This commit is contained in:
Mohamed Boudra
2026-08-13 20:34:21 +02:00
parent 6a1cc98fdf
commit a732cc5756
4 changed files with 16 additions and 30 deletions
@@ -1,17 +1,14 @@
import { test } from "../support/fixtures"; import { expect, test } from "../support/fixtures";
import { import {
applyProfileFromPicker, applyProfileFromPicker,
closeModelPicker, closeModelPicker,
drillIntoProvider,
expectComposerDoesNotName, expectComposerDoesNotName,
expectAgentProfilesEmptyPrompt, expectAgentProfilesEmptyPrompt,
expectProfileEditTooltip, expectProfileEditTooltip,
expectComposerMode, expectComposerMode,
expectComposerModel, expectComposerModel,
expectModelRowSelected, expectModelRowSelected,
expectNothingSelectedInPickerRoot,
expectProfileEditIsPencilOnly, expectProfileEditIsPencilOnly,
expectProfilePinnedAboveProviders,
expectProfileVisibleForProvider, expectProfileVisibleForProvider,
openModelPicker, openModelPicker,
openAgentProfilesFromEmptyPrompt, openAgentProfilesFromEmptyPrompt,
@@ -74,9 +71,12 @@ test.describe("Agent profiles in the model picker", () => {
await expectComposerMode(page, "Load test"); await expectComposerMode(page, "Load test");
}); });
await test.step("the profile is pinned above the provider list", async () => { await test.step("the sole provider opens directly", async () => {
await openModelPicker(page); await openModelPicker(page);
await expectProfilePinnedAboveProviders(page, { await expect(page.getByTestId("model-search-input").first()).toBeVisible();
await expect(page.getByTestId("sheet-header-back")).toHaveCount(0);
await expect(page.locator('[data-testid^="model-provider-"]')).toHaveCount(0);
await expectProfileVisibleForProvider(page, {
name: PROFILE.name, name: PROFILE.name,
summary: PROFILE_SUMMARY, summary: PROFILE_SUMMARY,
}); });
@@ -100,17 +100,9 @@ test.describe("Agent profiles in the model picker", () => {
await expectComposerDoesNotName(page, PROFILE.name); await expectComposerDoesNotName(page, PROFILE.name);
}); });
await test.step("reopening the picker shows no profile marked as selected", async () => { await test.step("reopening returns directly to the provider models", async () => {
await openModelPicker(page); await openModelPicker(page);
await expectProfilePinnedAboveProviders(page, { await expect(page.getByTestId("model-search-input").first()).toBeVisible();
name: PROFILE.name,
summary: PROFILE_SUMMARY,
});
await expectNothingSelectedInPickerRoot(page);
});
await test.step("the model it materialized is the one marked selected", async () => {
await drillIntoProvider(page, "mock");
await expectProfileVisibleForProvider(page, { await expectProfileVisibleForProvider(page, {
name: PROFILE.name, name: PROFILE.name,
summary: PROFILE_SUMMARY, summary: PROFILE_SUMMARY,
@@ -70,7 +70,7 @@ describe("model browser initial view", () => {
).toEqual({ kind: "all" }); ).toEqual({ kind: "all" });
}); });
it("keeps the root even when a single provider would otherwise be skipped", () => { it("opens a sole provider directly regardless of root content", () => {
expect( expect(
resolveInitialModelBrowserView({ resolveInitialModelBrowserView({
providers: [pi], providers: [pi],
@@ -78,7 +78,7 @@ describe("model browser initial view", () => {
selectedModel: "pi-pro", selectedModel: "pi-pro",
hasProfiles: true, hasProfiles: true,
}), }),
).toEqual({ kind: "all" }); ).toEqual({ kind: "provider", providerId: "pi", providerLabel: "Pi" });
}); });
it("falls back to the root when the selected provider is gone", () => { it("falls back to the root when the selected provider is gone", () => {
@@ -32,11 +32,7 @@ export function resolveModelBrowserAllView({
return { kind: "searchResults", rows }; return { kind: "searchResults", rows };
} }
/** /** Where the picker lands when it opens. A sole provider skips the redundant root view. */
* Where the picker lands when it opens. Agent profiles live on the root view, so
* a host that has any profile always opens there — including the single-provider
* case, which would otherwise skip the root entirely and hide them.
*/
export function resolveInitialModelBrowserView({ export function resolveInitialModelBrowserView({
providers, providers,
selectedProvider, selectedProvider,
@@ -48,10 +44,6 @@ export function resolveInitialModelBrowserView({
selectedModel: string; selectedModel: string;
hasProfiles: boolean; hasProfiles: boolean;
}): ModelBrowserView { }): ModelBrowserView {
if (hasProfiles) {
return { kind: "all" };
}
const singleProvider = providers.length === 1 ? providers[0] : undefined; const singleProvider = providers.length === 1 ? providers[0] : undefined;
if (singleProvider) { if (singleProvider) {
return { return {
@@ -61,6 +53,10 @@ export function resolveInitialModelBrowserView({
}; };
} }
if (hasProfiles) {
return { kind: "all" };
}
if (selectedProvider.length > 0 && selectedModel.length > 0) { if (selectedProvider.length > 0 && selectedModel.length > 0) {
const provider = providers.find((entry) => entry.id === selectedProvider); const provider = providers.find((entry) => entry.id === selectedProvider);
if (provider) { if (provider) {
@@ -292,9 +292,7 @@ export function useModelBrowser({
setSearchQuery(value); setSearchQuery(value);
}, []); }, []);
// A pinned profiles section makes the root worth returning to, so the back const singleProviderView = providers.length === 1;
// affordance stays even when the drill-down was the only provider.
const singleProviderView = providers.length === 1 && !hasProfiles;
const header = useMemo<SheetHeader>(() => { const header = useMemo<SheetHeader>(() => {
if (view.kind === "all") { if (view.kind === "all") {
return { return {