|
| 1 | +--- |
| 2 | +title: Model Selector |
| 3 | +description: A searchable command palette for selecting AI models in your chat interface. |
| 4 | +icon: lucide:square-mouse-pointer |
| 5 | +--- |
| 6 | + |
| 7 | +The `Model Selector` 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 | +:::ComponentLoader{label="Preview" componentName="ModelSelector"} |
| 10 | +::: |
| 11 | + |
| 12 | +## Install using CLI |
| 13 | + |
| 14 | +::tabs{variant="card"} |
| 15 | + ::div{label="ai-elements-vue"} |
| 16 | + ```sh |
| 17 | + npx ai-elements-vue@latest add model-selector |
| 18 | + ``` |
| 19 | + :: |
| 20 | + ::div{label="shadcn-vue"} |
| 21 | + |
| 22 | + ```sh |
| 23 | + npx shadcn-vue@latest add https://registry.ai-elements-vue.com/model-selector.json |
| 24 | + ``` |
| 25 | + :: |
| 26 | +:: |
| 27 | + |
| 28 | +## Install Manually |
| 29 | + |
| 30 | +Copy and paste the following code in the same folder. |
| 31 | + |
| 32 | +:::code-group |
| 33 | +```vue [ModelSelector.vue] height=260 collapse |
| 34 | +<script setup lang="ts"> |
| 35 | +import { Dialog } from '@repo/shadcn-vue/components/ui/dialog' |
| 36 | +</script> |
| 37 | +
|
| 38 | +<template> |
| 39 | + <Dialog v-bind="$attrs"> |
| 40 | + <slot /> |
| 41 | + </Dialog> |
| 42 | +</template> |
| 43 | +``` |
| 44 | + |
| 45 | +```vue [ModelSelectorTrigger.vue] height=260 collapse |
| 46 | +<script setup lang="ts"> |
| 47 | +import { DialogTrigger } from '@repo/shadcn-vue/components/ui/dialog' |
| 48 | +</script> |
| 49 | +
|
| 50 | +<template> |
| 51 | + <DialogTrigger v-bind="$attrs"> |
| 52 | + <slot /> |
| 53 | + </DialogTrigger> |
| 54 | +</template> |
| 55 | +``` |
| 56 | + |
| 57 | +```vue [ModelSelectorContent.vue] height=260 collapse |
| 58 | +<script setup lang="ts"> |
| 59 | +import type { HTMLAttributes } from 'vue' |
| 60 | +import { Command } from '@repo/shadcn-vue/components/ui/command' |
| 61 | +import { DialogContent, DialogTitle } from '@repo/shadcn-vue/components/ui/dialog' |
| 62 | +import { cn } from '@repo/shadcn-vue/lib/utils' |
| 63 | +
|
| 64 | +interface Props { |
| 65 | + title?: string |
| 66 | + class?: HTMLAttributes['class'] |
| 67 | +} |
| 68 | +
|
| 69 | +const props = withDefaults(defineProps<Props>(), { |
| 70 | + title: 'Model Selector', |
| 71 | +}) |
| 72 | +</script> |
| 73 | +
|
| 74 | +<template> |
| 75 | + <DialogContent |
| 76 | + :class="cn('p-0', props.class)" |
| 77 | + v-bind="$attrs" |
| 78 | + > |
| 79 | + <DialogTitle class="sr-only"> |
| 80 | + {{ title }} |
| 81 | + </DialogTitle> |
| 82 | + <Command class="**:data-[slot=command-input-wrapper]:h-auto"> |
| 83 | + <slot /> |
| 84 | + </Command> |
| 85 | + </DialogContent> |
| 86 | +</template> |
| 87 | +``` |
| 88 | + |
| 89 | +```vue [ModelSelectorDialog.vue] height=260 collapse |
| 90 | +<script setup lang="ts"> |
| 91 | +import { CommandDialog } from '@repo/shadcn-vue/components/ui/command' |
| 92 | +</script> |
| 93 | +
|
| 94 | +<template> |
| 95 | + <CommandDialog v-bind="$attrs"> |
| 96 | + <slot /> |
| 97 | + </CommandDialog> |
| 98 | +</template> |
| 99 | +``` |
| 100 | + |
| 101 | +```vue [ModelSelectorInput.vue] height=260 collapse |
| 102 | +<script setup lang="ts"> |
| 103 | +import type { HTMLAttributes } from 'vue' |
| 104 | +import { CommandInput } from '@repo/shadcn-vue/components/ui/command' |
| 105 | +import { cn } from '@repo/shadcn-vue/lib/utils' |
| 106 | +
|
| 107 | +interface Props { |
| 108 | + class?: HTMLAttributes['class'] |
| 109 | +} |
| 110 | +
|
| 111 | +const props = defineProps<Props>() |
| 112 | +</script> |
| 113 | +
|
| 114 | +<template> |
| 115 | + <CommandInput |
| 116 | + :class="cn('h-auto py-3.5', props.class)" |
| 117 | + v-bind="$attrs" |
| 118 | + /> |
| 119 | +</template> |
| 120 | +``` |
| 121 | + |
| 122 | +```vue [ModelSelectorList.vue] height=260 collapse |
| 123 | +<script setup lang="ts"> |
| 124 | +import { CommandList } from '@repo/shadcn-vue/components/ui/command' |
| 125 | +</script> |
| 126 | +
|
| 127 | +<template> |
| 128 | + <CommandList v-bind="$attrs"> |
| 129 | + <slot /> |
| 130 | + </CommandList> |
| 131 | +</template> |
| 132 | +``` |
| 133 | + |
| 134 | +```vue [ModelSelectorEmpty.vue] height=260 collapse |
| 135 | +<script setup lang="ts"> |
| 136 | +import { CommandEmpty } from '@repo/shadcn-vue/components/ui/command' |
| 137 | +</script> |
| 138 | +
|
| 139 | +<template> |
| 140 | + <CommandEmpty v-bind="$attrs"> |
| 141 | + <slot /> |
| 142 | + </CommandEmpty> |
| 143 | +</template> |
| 144 | +``` |
| 145 | + |
| 146 | +```vue [ModelSelectorGroup.vue] height=260 collapse |
| 147 | +<script setup lang="ts"> |
| 148 | +import { CommandGroup } from '@repo/shadcn-vue/components/ui/command' |
| 149 | +</script> |
| 150 | +
|
| 151 | +<template> |
| 152 | + <CommandGroup v-bind="$attrs"> |
| 153 | + <slot /> |
| 154 | + </CommandGroup> |
| 155 | +</template> |
| 156 | +``` |
| 157 | + |
| 158 | +```vue [ModelSelectorItem.vue] height=260 collapse |
| 159 | +<script setup lang="ts"> |
| 160 | +import { CommandItem } from '@repo/shadcn-vue/components/ui/command' |
| 161 | +</script> |
| 162 | +
|
| 163 | +<template> |
| 164 | + <CommandItem v-bind="$attrs"> |
| 165 | + <slot /> |
| 166 | + </CommandItem> |
| 167 | +</template> |
| 168 | +``` |
| 169 | + |
| 170 | +```vue [ModelSelectorShortcut.vue] height=260 collapse |
| 171 | +<script setup lang="ts"> |
| 172 | +import { CommandShortcut } from '@repo/shadcn-vue/components/ui/command' |
| 173 | +</script> |
| 174 | +
|
| 175 | +<template> |
| 176 | + <CommandShortcut v-bind="$attrs"> |
| 177 | + <slot /> |
| 178 | + </CommandShortcut> |
| 179 | +</template> |
| 180 | +``` |
| 181 | + |
| 182 | +```vue [ModelSelectorSeparator.vue] height=260 collapse |
| 183 | +<script setup lang="ts"> |
| 184 | +import { CommandSeparator } from '@repo/shadcn-vue/components/ui/command' |
| 185 | +</script> |
| 186 | +
|
| 187 | +<template> |
| 188 | + <CommandSeparator v-bind="$attrs" /> |
| 189 | +</template> |
| 190 | +``` |
| 191 | + |
| 192 | +```vue [ModelSelectorLogo.vue] height=260 collapse |
| 193 | +<script setup lang="ts"> |
| 194 | +import type { HTMLAttributes } from 'vue' |
| 195 | +import { cn } from '@repo/shadcn-vue/lib/utils' |
| 196 | +
|
| 197 | +interface Props { |
| 198 | + provider: string |
| 199 | + class?: HTMLAttributes['class'] |
| 200 | +} |
| 201 | +
|
| 202 | +const props = defineProps<Props>() |
| 203 | +</script> |
| 204 | +
|
| 205 | +<template> |
| 206 | + <img |
| 207 | + v-bind="$attrs" |
| 208 | + :alt="`${props.provider} logo`" |
| 209 | + :class="cn('size-3', props.class)" |
| 210 | + height="12" |
| 211 | + :src="`https://models.dev/logos/${props.provider}.svg`" |
| 212 | + width="12" |
| 213 | + > |
| 214 | +</template> |
| 215 | +``` |
| 216 | + |
| 217 | +```vue [ModelSelectorLogoGroup.vue] height=260 collapse |
| 218 | +<script setup lang="ts"> |
| 219 | +import type { HTMLAttributes } from 'vue' |
| 220 | +import { cn } from '@repo/shadcn-vue/lib/utils' |
| 221 | +
|
| 222 | +interface Props { |
| 223 | + class?: HTMLAttributes['class'] |
| 224 | +} |
| 225 | +
|
| 226 | +const props = defineProps<Props>() |
| 227 | +</script> |
| 228 | +
|
| 229 | +<template> |
| 230 | + <div |
| 231 | + :class=" |
| 232 | + cn( |
| 233 | + '-space-x-1 flex shrink-0 items-center [&>img]:rounded-full [&>img]:bg-background [&>img]:p-px [&>img]:ring-1 [&>img]:ring-border', |
| 234 | + props.class, |
| 235 | + ) |
| 236 | + " |
| 237 | + v-bind="$attrs" |
| 238 | + > |
| 239 | + <slot /> |
| 240 | + </div> |
| 241 | +</template> |
| 242 | +``` |
| 243 | + |
| 244 | +```vue [ModelSelectorName.vue] height=260 collapse |
| 245 | +<script setup lang="ts"> |
| 246 | +import type { HTMLAttributes } from 'vue' |
| 247 | +import { cn } from '@repo/shadcn-vue/lib/utils' |
| 248 | +
|
| 249 | +interface Props { |
| 250 | + class?: HTMLAttributes['class'] |
| 251 | +} |
| 252 | +
|
| 253 | +const props = defineProps<Props>() |
| 254 | +</script> |
| 255 | +
|
| 256 | +<template> |
| 257 | + <span |
| 258 | + :class="cn('flex-1 truncate text-left', props.class)" |
| 259 | + v-bind="$attrs" |
| 260 | + > |
| 261 | + <slot /> |
| 262 | + </span> |
| 263 | +</template> |
| 264 | +``` |
| 265 | + |
| 266 | +```ts [index.ts] height=260 collapse |
| 267 | +export { default as ModelSelector } from './ModelSelector.vue' |
| 268 | +export { default as ModelSelectorContent } from './ModelSelectorContent.vue' |
| 269 | +export { default as ModelSelectorDialog } from './ModelSelectorDialog.vue' |
| 270 | +export { default as ModelSelectorEmpty } from './ModelSelectorEmpty.vue' |
| 271 | +export { default as ModelSelectorGroup } from './ModelSelectorGroup.vue' |
| 272 | +export { default as ModelSelectorInput } from './ModelSelectorInput.vue' |
| 273 | +export { default as ModelSelectorItem } from './ModelSelectorItem.vue' |
| 274 | +export { default as ModelSelectorList } from './ModelSelectorList.vue' |
| 275 | +export { default as ModelSelectorLogo } from './ModelSelectorLogo.vue' |
| 276 | +export { default as ModelSelectorLogoGroup } from './ModelSelectorLogoGroup.vue' |
| 277 | +export { default as ModelSelectorName } from './ModelSelectorName.vue' |
| 278 | +export { default as ModelSelectorSeparator } from './ModelSelectorSeparator.vue' |
| 279 | +export { default as ModelSelectorShortcut } from './ModelSelectorShortcut.vue' |
| 280 | +export { default as ModelSelectorTrigger } from './ModelSelectorTrigger.vue' |
| 281 | +``` |
| 282 | +::: |
| 283 | + |
| 284 | +## Features |
| 285 | + |
| 286 | +- Searchable interface with keyboard navigation |
| 287 | +- Fuzzy search filtering across model names |
| 288 | +- Grouped model organization by provider |
| 289 | +- Keyboard shortcuts support |
| 290 | +- Empty state handling |
| 291 | +- Customizable styling with Tailwind CSS |
| 292 | +- Built on cmdk for excellent accessibility |
| 293 | +- Support for both inline and dialog modes |
| 294 | +- TypeScript support with proper type definitions |
| 295 | + |
| 296 | +## Props |
| 297 | + |
| 298 | +### `<ModelSelectorContent />` |
| 299 | + |
| 300 | +:::field-group |
| 301 | + ::field{name="title" type="string" defaultValue="'Model Selector'"} |
| 302 | + Title of the model selector. |
| 303 | + :: |
| 304 | + |
| 305 | + ::field{name="class" type="string"} |
| 306 | + Additional classes applied to the component. |
| 307 | + :: |
| 308 | +::: |
| 309 | + |
| 310 | +### `<ModelSelectorInput />` |
| 311 | + |
| 312 | +:::field-group |
| 313 | + ::field{name="class" type="string"} |
| 314 | + Additional classes applied to the component. |
| 315 | + :: |
| 316 | +::: |
| 317 | + |
| 318 | +### `<ModelSelectorLogo />` |
| 319 | + |
| 320 | +:::field-group |
| 321 | + ::field{name="provider" type="string"} |
| 322 | + The AI provider name. Supports major providers like "openai", "anthropic", "google", "mistral", etc. |
| 323 | + :: |
| 324 | + |
| 325 | + ::field{name="class" type="string"} |
| 326 | + Additional classes applied to the component. |
| 327 | + :: |
| 328 | +::: |
| 329 | + |
| 330 | +### `<ModelSelectorLogoGroup />` |
| 331 | + |
| 332 | +:::field-group |
| 333 | + ::field{name="class" type="string"} |
| 334 | + Additional classes applied to the component. |
| 335 | + :: |
| 336 | +::: |
| 337 | + |
| 338 | +### `<ModelSelectorName />` |
| 339 | + |
| 340 | +:::field-group |
| 341 | + ::field{name="class" type="string"} |
| 342 | + Additional classes applied to the component. |
| 343 | + :: |
| 344 | +::: |
0 commit comments