Skip to content

Commit 29c2d43

Browse files
ModelSelector component (#217)
* Draft model selector * Add support for logos * Misc fixes * Update model-selector.mdx * Update model-selector.tsx * Rename PromptInputModelSelect, replace with ModelSelector * Add more providers, improve logos * Create model-selector.test.tsx * Create jolly-pots-wave.md
1 parent 5198279 commit 29c2d43

File tree

16 files changed

+2064
-277
lines changed

16 files changed

+2064
-277
lines changed

.changeset/jolly-pots-wave.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"ai-elements": minor
3+
---
4+
5+
Add ModelSelector component
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
---
2+
title: Model Selector
3+
description: A searchable command palette for selecting AI models in your chat interface.
4+
path: elements/components/model-selector
5+
---
6+
7+
The `ModelSelector` component provides a searchable command palette interface for selecting AI models. It's built on top of the cmdk library and provides a keyboard-navigable interface with search functionality.
8+
9+
<Preview path="model-selector" />
10+
11+
## Installation
12+
13+
<ElementsInstaller path="model-selector" />
14+
15+
## Usage
16+
17+
```tsx
18+
import {
19+
ModelSelector,
20+
ModelSelectorDialog,
21+
ModelSelectorEmpty,
22+
ModelSelectorGroup,
23+
ModelSelectorInput,
24+
ModelSelectorItem,
25+
ModelSelectorList,
26+
ModelSelectorSeparator,
27+
ModelSelectorShortcut,
28+
} from '@/components/ai-elements/model-selector';
29+
```
30+
31+
```tsx
32+
<ModelSelector>
33+
<ModelSelectorInput placeholder="Search models..." />
34+
<ModelSelectorList>
35+
<ModelSelectorEmpty>No models found.</ModelSelectorEmpty>
36+
<ModelSelectorGroup heading="OpenAI">
37+
<ModelSelectorItem value="gpt-4o">GPT-4o</ModelSelectorItem>
38+
<ModelSelectorItem value="gpt-4o-mini">GPT-4o Mini</ModelSelectorItem>
39+
</ModelSelectorGroup>
40+
<ModelSelectorSeparator />
41+
<ModelSelectorGroup heading="Anthropic">
42+
<ModelSelectorItem value="claude-opus-4-20250514">
43+
Claude 4 Opus
44+
</ModelSelectorItem>
45+
<ModelSelectorItem value="claude-sonnet-4-20250514">
46+
Claude 4 Sonnet
47+
</ModelSelectorItem>
48+
</ModelSelectorGroup>
49+
</ModelSelectorList>
50+
</ModelSelector>
51+
```
52+
53+
## Features
54+
55+
- Searchable interface with keyboard navigation
56+
- Fuzzy search filtering across model names
57+
- Grouped model organization by provider
58+
- Keyboard shortcuts support
59+
- Empty state handling
60+
- Customizable styling with Tailwind CSS
61+
- Built on cmdk for excellent accessibility
62+
- Support for both inline and dialog modes
63+
- TypeScript support with proper type definitions
64+
65+
## Props
66+
67+
### `<ModelSelector />`
68+
69+
<TypeTable
70+
type={{
71+
'...props': {
72+
description: 'Any other props are spread to the underlying Dialog component.',
73+
type: 'React.ComponentProps<typeof Dialog>',
74+
},
75+
}}
76+
/>
77+
78+
### `<ModelSelectorTrigger />`
79+
80+
<TypeTable
81+
type={{
82+
'...props': {
83+
description: 'Any other props are spread to the underlying DialogTrigger component.',
84+
type: 'React.ComponentProps<typeof DialogTrigger>',
85+
},
86+
}}
87+
/>
88+
89+
### `<ModelSelectorContent />`
90+
91+
<TypeTable
92+
type={{
93+
'...props': {
94+
description: 'Any other props are spread to the underlying DialogContent component.',
95+
type: 'React.ComponentProps<typeof DialogContent>',
96+
},
97+
}}
98+
/>
99+
100+
### `<ModelSelectorDialog />`
101+
102+
<TypeTable
103+
type={{
104+
'...props': {
105+
description: 'Any other props are spread to the underlying CommandDialog component.',
106+
type: 'React.ComponentProps<typeof CommandDialog>',
107+
},
108+
}}
109+
/>
110+
111+
### `<ModelSelectorInput />`
112+
113+
<TypeTable
114+
type={{
115+
'...props': {
116+
description: 'Any other props are spread to the underlying CommandInput component.',
117+
type: 'React.ComponentProps<typeof CommandInput>',
118+
},
119+
}}
120+
/>
121+
122+
### `<ModelSelectorList />`
123+
124+
<TypeTable
125+
type={{
126+
'...props': {
127+
description: 'Any other props are spread to the underlying CommandList component.',
128+
type: 'React.ComponentProps<typeof CommandList>',
129+
},
130+
}}
131+
/>
132+
133+
### `<ModelSelectorEmpty />`
134+
135+
<TypeTable
136+
type={{
137+
'...props': {
138+
description: 'Any other props are spread to the underlying CommandEmpty component.',
139+
type: 'React.ComponentProps<typeof CommandEmpty>',
140+
},
141+
}}
142+
/>
143+
144+
### `<ModelSelectorGroup />`
145+
146+
<TypeTable
147+
type={{
148+
'...props': {
149+
description: 'Any other props are spread to the underlying CommandGroup component.',
150+
type: 'React.ComponentProps<typeof CommandGroup>',
151+
},
152+
}}
153+
/>
154+
155+
### `<ModelSelectorItem />`
156+
157+
<TypeTable
158+
type={{
159+
'...props': {
160+
description: 'Any other props are spread to the underlying CommandItem component.',
161+
type: 'React.ComponentProps<typeof CommandItem>',
162+
},
163+
}}
164+
/>
165+
166+
### `<ModelSelectorShortcut />`
167+
168+
<TypeTable
169+
type={{
170+
'...props': {
171+
description: 'Any other props are spread to the underlying CommandShortcut component.',
172+
type: 'React.ComponentProps<typeof CommandShortcut>',
173+
},
174+
}}
175+
/>
176+
177+
### `<ModelSelectorSeparator />`
178+
179+
<TypeTable
180+
type={{
181+
'...props': {
182+
description: 'Any other props are spread to the underlying CommandSeparator component.',
183+
type: 'React.ComponentProps<typeof CommandSeparator>',
184+
},
185+
}}
186+
/>
187+
188+
### `<ModelSelectorLogo />`
189+
190+
<TypeTable
191+
type={{
192+
provider: {
193+
description: 'The AI provider name. Supports major providers like "openai", "anthropic", "google", "mistral", etc.',
194+
type: 'string',
195+
required: true,
196+
},
197+
'...props': {
198+
description: 'Any other props are spread to the underlying img element (except src and alt which are generated).',
199+
type: 'Omit<React.ComponentProps<"img">, "src" | "alt">',
200+
},
201+
}}
202+
/>
203+
204+
### `<ModelSelectorLogoGroup />`
205+
206+
<TypeTable
207+
type={{
208+
'...props': {
209+
description: 'Any other props are spread to the underlying div element.',
210+
type: 'React.ComponentProps<"div">',
211+
},
212+
}}
213+
/>
214+
215+
### `<ModelSelectorName />`
216+
217+
<TypeTable
218+
type={{
219+
'...props': {
220+
description: 'Any other props are spread to the underlying span element.',
221+
type: 'React.ComponentProps<"span">',
222+
},
223+
}}
224+
/>

apps/docs/content/docs/components/(chatbot)/prompt-input.mdx

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -63,19 +63,19 @@ import { GlobeIcon } from 'lucide-react';
6363
<GlobeIcon size={16} />
6464
<span>Search</span>
6565
</PromptInputButton>
66-
<PromptInputModelSelect onValueChange={(value) => {}} value="gpt-4o">
67-
<PromptInputModelSelectTrigger>
68-
<PromptInputModelSelectValue />
69-
</PromptInputModelSelectTrigger>
70-
<PromptInputModelSelectContent>
71-
<PromptInputModelSelectItem value="gpt-4o">
66+
<PromptInputSelect onValueChange={(value) => {}} value="gpt-4o">
67+
<PromptInputSelectTrigger>
68+
<PromptInputSelectValue />
69+
</PromptInputSelectTrigger>
70+
<PromptInputSelectContent>
71+
<PromptInputSelectItem value="gpt-4o">
7272
GPT-4o
73-
</PromptInputModelSelectItem>
74-
<PromptInputModelSelectItem value="claude-opus-4-20250514">
73+
</PromptInputSelectItem>
74+
<PromptInputSelectItem value="claude-opus-4-20250514">
7575
Claude 4 Opus
76-
</PromptInputModelSelectItem>
77-
</PromptInputModelSelectContent>
78-
</PromptInputModelSelect>
76+
</PromptInputSelectItem>
77+
</PromptInputSelectContent>
78+
</PromptInputSelect>
7979
</PromptInputTools>
8080
<PromptInputSubmit
8181
disabled={false}
@@ -105,11 +105,11 @@ import {
105105
PromptInputBody,
106106
PromptInputButton,
107107
type PromptInputMessage,
108-
PromptInputModelSelect,
109-
PromptInputModelSelectContent,
110-
PromptInputModelSelectItem,
111-
PromptInputModelSelectTrigger,
112-
PromptInputModelSelectValue,
108+
PromptInputSelect,
109+
PromptInputSelectContent,
110+
PromptInputSelectItem,
111+
PromptInputSelectTrigger,
112+
PromptInputSelectValue,
113113
PromptInputSpeechButton,
114114
PromptInputSubmit,
115115
PromptInputTextarea,
@@ -223,23 +223,23 @@ const InputDemo = () => {
223223
<GlobeIcon size={16} />
224224
<span>Search</span>
225225
</PromptInputButton>
226-
<PromptInputModelSelect
226+
<PromptInputSelect
227227
onValueChange={(value) => {
228228
setModel(value);
229229
}}
230230
value={model}
231231
>
232-
<PromptInputModelSelectTrigger>
233-
<PromptInputModelSelectValue />
234-
</PromptInputModelSelectTrigger>
235-
<PromptInputModelSelectContent>
232+
<PromptInputSelectTrigger>
233+
<PromptInputSelectValue />
234+
</PromptInputSelectTrigger>
235+
<PromptInputSelectContent>
236236
{models.map((model) => (
237-
<PromptInputModelSelectItem key={model.id} value={model.id}>
237+
<PromptInputSelectItem key={model.id} value={model.id}>
238238
{model.name}
239-
</PromptInputModelSelectItem>
239+
</PromptInputSelectItem>
240240
))}
241-
</PromptInputModelSelectContent>
242-
</PromptInputModelSelect>
241+
</PromptInputSelectContent>
242+
</PromptInputSelect>
243243
</PromptInputTools>
244244
<PromptInputSubmit disabled={!text && !status} status={status} />
245245
</PromptInputFooter>
@@ -410,7 +410,7 @@ export async function POST(req: Request) {
410410
}}
411411
/>
412412

413-
### `<PromptInputModelSelect />`
413+
### `<PromptInputSelect />`
414414

415415
<TypeTable
416416
type={{
@@ -421,7 +421,7 @@ export async function POST(req: Request) {
421421
}}
422422
/>
423423

424-
### `<PromptInputModelSelectTrigger />`
424+
### `<PromptInputSelectTrigger />`
425425

426426
<TypeTable
427427
type={{
@@ -432,7 +432,7 @@ export async function POST(req: Request) {
432432
}}
433433
/>
434434

435-
### `<PromptInputModelSelectContent />`
435+
### `<PromptInputSelectContent />`
436436

437437
<TypeTable
438438
type={{
@@ -443,7 +443,7 @@ export async function POST(req: Request) {
443443
}}
444444
/>
445445

446-
### `<PromptInputModelSelectItem />`
446+
### `<PromptInputSelectItem />`
447447

448448
<TypeTable
449449
type={{
@@ -454,7 +454,7 @@ export async function POST(req: Request) {
454454
}}
455455
/>
456456

457-
### `<PromptInputModelSelectValue />`
457+
### `<PromptInputSelectValue />`
458458

459459
<TypeTable
460460
type={{

0 commit comments

Comments
 (0)