diff --git a/apps/web/src/components/model-selector.tsx b/apps/web/src/components/model-selector.tsx index 792df0db..9356c067 100644 --- a/apps/web/src/components/model-selector.tsx +++ b/apps/web/src/components/model-selector.tsx @@ -4,6 +4,7 @@ import type { Model } from "@/stores/model"; import { cn } from "@/lib/utils"; import { getModelById, useModels, useModelStore } from "@/stores/model"; import { useFavoriteModels } from "@/hooks/use-favorite-models"; +import { useUIStore } from "@/stores/ui"; import { CheckIcon, ChevronDownIcon, SearchIcon } from "@/components/icons"; function useIsMobile() { @@ -243,6 +244,7 @@ export function ModelSelector({ const { models, isLoading } = useModels(); const { favorites, toggleFavorite, isFavorite, addDefaults, missingDefaultsCount } = useFavoriteModels(); + const filterStyle = useUIStore((s) => s.filterStyle); const deferredQuery = useDeferredValue(query); const isSearching = deferredQuery.trim().length > 0; @@ -250,7 +252,7 @@ export function ModelSelector({ const selectedModel = useMemo(() => getModelById(models, value), [models, value]); const uniqueProviders = useMemo(() => { - const providerMap = new Map(); + const providerMap = new Map(); for (const model of models) { const existing = providerMap.get(model.providerId); if (existing) { @@ -259,6 +261,7 @@ export function ModelSelector({ providerMap.set(model.providerId, { id: model.providerId, name: model.provider, + modelName: model.modelName, logoId: model.logoId, count: 1, }); @@ -567,14 +570,14 @@ export function ModelSelector({ } }} className={cn( - "flex h-9 shrink-0 items-center gap-2 rounded-full px-3.5 text-sm font-medium transition-all duration-200", + "flex size-9 shrink-0 items-center justify-center rounded-full transition-all duration-200", selectedProvider === provider.id ? "bg-accent text-foreground" : "bg-muted/50 text-muted-foreground active:bg-accent active:text-foreground", )} + title={filterStyle === "company" ? provider.name : provider.modelName} > - - {provider.name} + ))} @@ -713,7 +716,7 @@ export function ModelSelector({ ? "bg-accent text-foreground shadow-sm" : "text-muted-foreground hover:bg-accent/60 hover:text-foreground hover:scale-105", )} - title={provider.name} + title={filterStyle === "company" ? provider.name : provider.modelName} > diff --git a/apps/web/src/routes/settings.tsx b/apps/web/src/routes/settings.tsx index 2f58362b..10fed105 100644 --- a/apps/web/src/routes/settings.tsx +++ b/apps/web/src/routes/settings.tsx @@ -14,6 +14,7 @@ import { useOpenRouterKey } from "@/stores/openrouter"; import { useProviderStore, DAILY_LIMIT_CENTS } from "@/stores/provider"; import { useModels, getCacheStatus } from "@/stores/model"; import { useChatTitleStore, type ChatTitleLength } from "@/stores/chat-title"; +import { useUIStore } from "@/stores/ui"; import { OpenRouterConnectModal } from "@/components/openrouter-connect-modal"; import { DeleteAccountModal } from "@/components/delete-account-modal"; import { Switch } from "@/components/ui/switch"; @@ -721,6 +722,8 @@ function ModelsSection() { const { models, isLoading, reload, totalCount, error } = useModels(); const cacheStatus = getCacheStatus(); const [isReloading, setIsReloading] = useState(false); + const filterStyle = useUIStore((s) => s.filterStyle); + const setFilterStyle = useUIStore((s) => s.setFilterStyle); const handleReload = async () => { setIsReloading(true); @@ -744,6 +747,47 @@ function ModelsSection() { return (
+ {/* Filter Display */} +
+

+ Filter Display +

+
+
+
+

Provider filter labels

+

+ Show company names (OpenAI, Meta Llama) or model names (GPT, Llama) in filters. +

+
+
+ + +
+
+
+
+ {/* Model Source Info */}

diff --git a/apps/web/src/stores/model.ts b/apps/web/src/stores/model.ts index 1f86fae0..6263c6c5 100644 --- a/apps/web/src/stores/model.ts +++ b/apps/web/src/stores/model.ts @@ -38,7 +38,8 @@ interface OpenRouterModel { export interface Model { id: string; name: string; - provider: string; // Provider display name (e.g., "Anthropic") + provider: string; // Provider/company name (e.g., "Anthropic", "Meta Llama") + modelName: string; // Model/product name for filters (e.g., "Claude", "Llama") providerId: string; // Provider slug from model ID (e.g., "meta-llama", "qwen") logoId: string; // Logo slug for models.dev (e.g., "llama", "alibaba") family?: string; // Model family (e.g., "claude-3.5") @@ -57,20 +58,20 @@ export interface Model { // Provider mapping for display names and logo IDs // ============================================================================ -const PROVIDER_INFO: Record = { - openai: { name: "OpenAI", logoId: "openai" }, - anthropic: { name: "Anthropic", logoId: "anthropic" }, - google: { name: "Google", logoId: "google" }, - "meta-llama": { name: "Llama", logoId: "llama" }, +const PROVIDER_INFO: Record = { + openai: { name: "OpenAI", modelName: "GPT", logoId: "openai" }, + anthropic: { name: "Anthropic", modelName: "Claude", logoId: "anthropic" }, + google: { name: "Google", modelName: "Gemini", logoId: "google" }, + "meta-llama": { name: "Meta Llama", modelName: "Llama", logoId: "llama" }, mistralai: { name: "Mistral", logoId: "mistral" }, deepseek: { name: "DeepSeek", logoId: "deepseek" }, - "x-ai": { name: "xAI", logoId: "xai" }, + "x-ai": { name: "xAI", modelName: "Grok", logoId: "xai" }, cohere: { name: "Cohere", logoId: "cohere" }, perplexity: { name: "Perplexity", logoId: "perplexity" }, qwen: { name: "Qwen", logoId: "alibaba" }, nvidia: { name: "NVIDIA", logoId: "nvidia" }, - microsoft: { name: "Microsoft", logoId: "azure" }, - amazon: { name: "Amazon", logoId: "amazon-bedrock" }, + microsoft: { name: "Microsoft", modelName: "Phi", logoId: "azure" }, + amazon: { name: "Amazon", modelName: "Nova", logoId: "amazon-bedrock" }, ai21: { name: "AI21", logoId: "ai21" }, together: { name: "Together", logoId: "togetherai" }, "fireworks-ai": { name: "Fireworks", logoId: "fireworks-ai" }, @@ -292,6 +293,7 @@ function transformModel(raw: OpenRouterModel): Model { id, name: raw.name || id, provider: info.name, + modelName: info.modelName || info.name, providerId: providerSlug, logoId: info.logoId, family: extractFamily(id, raw.name || ""), @@ -413,6 +415,7 @@ function getFallbackModels(): Model[] { id: "anthropic/claude-3.5-sonnet", name: "Claude 3.5 Sonnet", provider: "Anthropic", + modelName: "Claude", providerId: "anthropic", logoId: "anthropic", family: "Claude 3.5", @@ -422,6 +425,7 @@ function getFallbackModels(): Model[] { id: "openai/gpt-4o", name: "GPT-4o", provider: "OpenAI", + modelName: "GPT", providerId: "openai", logoId: "openai", family: "GPT-4o", @@ -431,6 +435,7 @@ function getFallbackModels(): Model[] { id: "openai/gpt-4o-mini", name: "GPT-4o Mini", provider: "OpenAI", + modelName: "GPT", providerId: "openai", logoId: "openai", family: "GPT-4o", @@ -440,6 +445,7 @@ function getFallbackModels(): Model[] { id: "google/gemini-2.5-flash", name: "Gemini 2.5 Flash", provider: "Google", + modelName: "Gemini", providerId: "google", logoId: "google", family: "Gemini 2.5", @@ -449,6 +455,7 @@ function getFallbackModels(): Model[] { id: "deepseek/deepseek-chat", name: "DeepSeek Chat", provider: "DeepSeek", + modelName: "DeepSeek", providerId: "deepseek", logoId: "deepseek", family: "DeepSeek", @@ -457,7 +464,8 @@ function getFallbackModels(): Model[] { { id: "meta-llama/llama-3.3-70b-instruct", name: "Llama 3.3 70B", - provider: "Llama", + provider: "Meta Llama", + modelName: "Llama", providerId: "meta-llama", logoId: "llama", family: "Llama 3.3", diff --git a/apps/web/src/stores/ui.ts b/apps/web/src/stores/ui.ts index 62e80836..946eedc4 100644 --- a/apps/web/src/stores/ui.ts +++ b/apps/web/src/stores/ui.ts @@ -5,6 +5,8 @@ import { create } from "zustand"; import { persist, devtools } from "zustand/middleware"; +export type FilterStyle = "company" | "model"; + interface UIState { // Sidebar sidebarOpen: boolean; @@ -13,12 +15,16 @@ interface UIState { // Command palette commandPaletteOpen: boolean; + // Model selector filter display + filterStyle: FilterStyle; + // Actions toggleSidebar: () => void; setSidebarOpen: (open: boolean) => void; setSidebarCollapsed: (collapsed: boolean) => void; toggleCommandPalette: () => void; setCommandPaletteOpen: (open: boolean) => void; + setFilterStyle: (style: FilterStyle) => void; } export const useUIStore = create()( @@ -28,6 +34,7 @@ export const useUIStore = create()( sidebarOpen: true, sidebarCollapsed: false, commandPaletteOpen: false, + filterStyle: "model" as FilterStyle, toggleSidebar: () => set((s) => ({ sidebarOpen: !s.sidebarOpen }), false, "ui/toggleSidebar"), @@ -46,11 +53,15 @@ export const useUIStore = create()( setCommandPaletteOpen: (open) => set({ commandPaletteOpen: open }, false, "ui/setCommandPaletteOpen"), + + setFilterStyle: (style) => + set({ filterStyle: style }, false, "ui/setFilterStyle"), }), { name: "ui-store", partialize: (state) => ({ sidebarCollapsed: state.sidebarCollapsed, + filterStyle: state.filterStyle, }), }, ),