Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions apps/web/src/components/model-selector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -243,14 +244,15 @@ 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;

const selectedModel = useMemo(() => getModelById(models, value), [models, value]);

const uniqueProviders = useMemo(() => {
const providerMap = new Map<string, { id: string; name: string; logoId: string; count: number }>();
const providerMap = new Map<string, { id: string; name: string; modelName: string; logoId: string; count: number }>();
for (const model of models) {
const existing = providerMap.get(model.providerId);
if (existing) {
Expand All @@ -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,
});
Expand Down Expand Up @@ -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}
>
<ProviderLogo providerId={provider.logoId} className="size-4" />
<span className="max-w-[80px] truncate">{provider.name}</span>
<ProviderLogo providerId={provider.logoId} className="size-5" />
</button>
))}
</div>
Expand Down Expand Up @@ -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}
>
<ProviderLogo providerId={provider.logoId} className="size-5" />
</button>
Expand Down
44 changes: 44 additions & 0 deletions apps/web/src/routes/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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);
Expand All @@ -744,6 +747,47 @@ function ModelsSection() {

return (
<div className="space-y-8">
{/* Filter Display */}
<section className="space-y-4">
<h2 className="text-sm font-medium text-muted-foreground uppercase tracking-wide">
Filter Display
</h2>
<div className="rounded-xl border bg-card p-4 space-y-3">
<div className="flex items-center justify-between gap-4">
<div>
<p className="text-sm font-medium">Provider filter labels</p>
<p className="text-sm text-muted-foreground">
Show company names (OpenAI, Meta Llama) or model names (GPT, Llama) in filters.
</p>
</div>
<div className="flex items-center gap-1 rounded-lg bg-muted p-1">
<button
onClick={() => setFilterStyle("model")}
className={cn(
"rounded-md px-3 py-1.5 text-xs font-medium transition-all",
filterStyle === "model"
? "bg-background text-foreground shadow-sm"
: "text-muted-foreground hover:text-foreground",
)}
>
Model
</button>
<button
onClick={() => setFilterStyle("company")}
className={cn(
"rounded-md px-3 py-1.5 text-xs font-medium transition-all",
filterStyle === "company"
? "bg-background text-foreground shadow-sm"
: "text-muted-foreground hover:text-foreground",
)}
>
Company
</button>
</div>
</div>
</div>
</section>

{/* Model Source Info */}
<section className="space-y-4">
<h2 className="text-sm font-medium text-muted-foreground uppercase tracking-wide">
Expand Down
28 changes: 18 additions & 10 deletions apps/web/src/stores/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -57,20 +58,20 @@ export interface Model {
// Provider mapping for display names and logo IDs
// ============================================================================

const PROVIDER_INFO: Record<string, { name: string; logoId: string }> = {
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<string, { name: string; modelName?: string; logoId: string }> = {
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" },
Expand Down Expand Up @@ -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 || ""),
Expand Down Expand Up @@ -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",
Expand All @@ -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",
Expand All @@ -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",
Expand All @@ -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",
Expand All @@ -449,6 +455,7 @@ function getFallbackModels(): Model[] {
id: "deepseek/deepseek-chat",
name: "DeepSeek Chat",
provider: "DeepSeek",
modelName: "DeepSeek",
providerId: "deepseek",
logoId: "deepseek",
family: "DeepSeek",
Expand All @@ -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",
Expand Down
11 changes: 11 additions & 0 deletions apps/web/src/stores/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import { create } from "zustand";
import { persist, devtools } from "zustand/middleware";

export type FilterStyle = "company" | "model";

interface UIState {
// Sidebar
sidebarOpen: boolean;
Expand All @@ -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<UIState>()(
Expand All @@ -28,6 +34,7 @@ export const useUIStore = create<UIState>()(
sidebarOpen: true,
sidebarCollapsed: false,
commandPaletteOpen: false,
filterStyle: "model" as FilterStyle,

toggleSidebar: () =>
set((s) => ({ sidebarOpen: !s.sidebarOpen }), false, "ui/toggleSidebar"),
Expand All @@ -46,11 +53,15 @@ export const useUIStore = create<UIState>()(

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,
}),
},
),
Expand Down