diff --git a/apps/www/content/3.components/1.chatbot/model-selector.md b/apps/www/content/3.components/1.chatbot/model-selector.md new file mode 100644 index 0000000..11174c1 --- /dev/null +++ b/apps/www/content/3.components/1.chatbot/model-selector.md @@ -0,0 +1,344 @@ +--- +title: Model Selector +description: A searchable command palette for selecting AI models in your chat interface. +icon: lucide:square-mouse-pointer +--- + +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. + +:::ComponentLoader{label="Preview" componentName="ModelSelector"} +::: + +## Install using CLI + +::tabs{variant="card"} + ::div{label="ai-elements-vue"} + ```sh + npx ai-elements-vue@latest add model-selector + ``` + :: + ::div{label="shadcn-vue"} + + ```sh + npx shadcn-vue@latest add https://registry.ai-elements-vue.com/model-selector.json + ``` + :: +:: + +## Install Manually + +Copy and paste the following code in the same folder. + +:::code-group +```vue [ModelSelector.vue] height=260 collapse + + + +``` + +```vue [ModelSelectorTrigger.vue] height=260 collapse + + + +``` + +```vue [ModelSelectorContent.vue] height=260 collapse + + + +``` + +```vue [ModelSelectorDialog.vue] height=260 collapse + + + +``` + +```vue [ModelSelectorInput.vue] height=260 collapse + + + +``` + +```vue [ModelSelectorList.vue] height=260 collapse + + + +``` + +```vue [ModelSelectorEmpty.vue] height=260 collapse + + + +``` + +```vue [ModelSelectorGroup.vue] height=260 collapse + + + +``` + +```vue [ModelSelectorItem.vue] height=260 collapse + + + +``` + +```vue [ModelSelectorShortcut.vue] height=260 collapse + + + +``` + +```vue [ModelSelectorSeparator.vue] height=260 collapse + + + +``` + +```vue [ModelSelectorLogo.vue] height=260 collapse + + + +``` + +```vue [ModelSelectorLogoGroup.vue] height=260 collapse + + + +``` + +```vue [ModelSelectorName.vue] height=260 collapse + + + +``` + +```ts [index.ts] height=260 collapse +export { default as ModelSelector } from './ModelSelector.vue' +export { default as ModelSelectorContent } from './ModelSelectorContent.vue' +export { default as ModelSelectorDialog } from './ModelSelectorDialog.vue' +export { default as ModelSelectorEmpty } from './ModelSelectorEmpty.vue' +export { default as ModelSelectorGroup } from './ModelSelectorGroup.vue' +export { default as ModelSelectorInput } from './ModelSelectorInput.vue' +export { default as ModelSelectorItem } from './ModelSelectorItem.vue' +export { default as ModelSelectorList } from './ModelSelectorList.vue' +export { default as ModelSelectorLogo } from './ModelSelectorLogo.vue' +export { default as ModelSelectorLogoGroup } from './ModelSelectorLogoGroup.vue' +export { default as ModelSelectorName } from './ModelSelectorName.vue' +export { default as ModelSelectorSeparator } from './ModelSelectorSeparator.vue' +export { default as ModelSelectorShortcut } from './ModelSelectorShortcut.vue' +export { default as ModelSelectorTrigger } from './ModelSelectorTrigger.vue' +``` +::: + +## Features + +- Searchable interface with keyboard navigation +- Fuzzy search filtering across model names +- Grouped model organization by provider +- Keyboard shortcuts support +- Empty state handling +- Customizable styling with Tailwind CSS +- Built on cmdk for excellent accessibility +- Support for both inline and dialog modes +- TypeScript support with proper type definitions + +## Props + +### `` + +:::field-group + ::field{name="title" type="string" defaultValue="'Model Selector'"} + Title of the model selector. + :: + + ::field{name="class" type="string"} + Additional classes applied to the component. + :: +::: + +### `` + +:::field-group + ::field{name="class" type="string"} + Additional classes applied to the component. + :: +::: + +### `` + +:::field-group + ::field{name="provider" type="string"} + The AI provider name. Supports major providers like "openai", "anthropic", "google", "mistral", etc. + :: + + ::field{name="class" type="string"} + Additional classes applied to the component. + :: +::: + +### `` + +:::field-group + ::field{name="class" type="string"} + Additional classes applied to the component. + :: +::: + +### `` + +:::field-group + ::field{name="class" type="string"} + Additional classes applied to the component. + :: +::: diff --git a/apps/www/plugins/ai-elements.ts b/apps/www/plugins/ai-elements.ts index 85a6203..ecc51da 100644 --- a/apps/www/plugins/ai-elements.ts +++ b/apps/www/plugins/ai-elements.ts @@ -20,6 +20,7 @@ import { LoaderSizes, Message, MessageMarkdown, + ModelSelector, OpenInChat, Plan, PromptInput, @@ -79,6 +80,7 @@ export default defineNuxtPlugin((nuxtApp) => { vueApp.component('CodeBlockDark', CodeBlockDark) vueApp.component('Checkpoint', Checkpoint) vueApp.component('Workflow', Workflow) + vueApp.component('ModelSelector', ModelSelector) vueApp.component('Context', Context) vueApp.component('Confirmation', Confirmation) vueApp.component('ConfirmationAccepted', ConfirmationAccepted) diff --git a/packages/elements/src/index.ts b/packages/elements/src/index.ts index 7634bc1..a570bc5 100644 --- a/packages/elements/src/index.ts +++ b/packages/elements/src/index.ts @@ -11,6 +11,7 @@ export * from './image' export * from './inline-citation' export * from './loader' export * from './message' +export * from './model-selector' export * from './open-in-chat' export * from './plan' export * from './prompt-input' diff --git a/packages/elements/src/model-selector/ModelSelector.vue b/packages/elements/src/model-selector/ModelSelector.vue new file mode 100644 index 0000000..772b021 --- /dev/null +++ b/packages/elements/src/model-selector/ModelSelector.vue @@ -0,0 +1,9 @@ + + + diff --git a/packages/elements/src/model-selector/ModelSelectorContent.vue b/packages/elements/src/model-selector/ModelSelectorContent.vue new file mode 100644 index 0000000..62dd3e1 --- /dev/null +++ b/packages/elements/src/model-selector/ModelSelectorContent.vue @@ -0,0 +1,29 @@ + + + diff --git a/packages/elements/src/model-selector/ModelSelectorDialog.vue b/packages/elements/src/model-selector/ModelSelectorDialog.vue new file mode 100644 index 0000000..bb18879 --- /dev/null +++ b/packages/elements/src/model-selector/ModelSelectorDialog.vue @@ -0,0 +1,9 @@ + + + diff --git a/packages/elements/src/model-selector/ModelSelectorEmpty.vue b/packages/elements/src/model-selector/ModelSelectorEmpty.vue new file mode 100644 index 0000000..abcdd7d --- /dev/null +++ b/packages/elements/src/model-selector/ModelSelectorEmpty.vue @@ -0,0 +1,9 @@ + + + diff --git a/packages/elements/src/model-selector/ModelSelectorGroup.vue b/packages/elements/src/model-selector/ModelSelectorGroup.vue new file mode 100644 index 0000000..e894040 --- /dev/null +++ b/packages/elements/src/model-selector/ModelSelectorGroup.vue @@ -0,0 +1,9 @@ + + + diff --git a/packages/elements/src/model-selector/ModelSelectorInput.vue b/packages/elements/src/model-selector/ModelSelectorInput.vue new file mode 100644 index 0000000..352ff7f --- /dev/null +++ b/packages/elements/src/model-selector/ModelSelectorInput.vue @@ -0,0 +1,18 @@ + + + diff --git a/packages/elements/src/model-selector/ModelSelectorItem.vue b/packages/elements/src/model-selector/ModelSelectorItem.vue new file mode 100644 index 0000000..0df3de2 --- /dev/null +++ b/packages/elements/src/model-selector/ModelSelectorItem.vue @@ -0,0 +1,9 @@ + + + diff --git a/packages/elements/src/model-selector/ModelSelectorList.vue b/packages/elements/src/model-selector/ModelSelectorList.vue new file mode 100644 index 0000000..9f61b70 --- /dev/null +++ b/packages/elements/src/model-selector/ModelSelectorList.vue @@ -0,0 +1,9 @@ + + + diff --git a/packages/elements/src/model-selector/ModelSelectorLogo.vue b/packages/elements/src/model-selector/ModelSelectorLogo.vue new file mode 100644 index 0000000..c074f3d --- /dev/null +++ b/packages/elements/src/model-selector/ModelSelectorLogo.vue @@ -0,0 +1,22 @@ + + + diff --git a/packages/elements/src/model-selector/ModelSelectorLogoGroup.vue b/packages/elements/src/model-selector/ModelSelectorLogoGroup.vue new file mode 100644 index 0000000..3f14262 --- /dev/null +++ b/packages/elements/src/model-selector/ModelSelectorLogoGroup.vue @@ -0,0 +1,24 @@ + + + diff --git a/packages/elements/src/model-selector/ModelSelectorName.vue b/packages/elements/src/model-selector/ModelSelectorName.vue new file mode 100644 index 0000000..e94cd25 --- /dev/null +++ b/packages/elements/src/model-selector/ModelSelectorName.vue @@ -0,0 +1,19 @@ + + + diff --git a/packages/elements/src/model-selector/ModelSelectorSeparator.vue b/packages/elements/src/model-selector/ModelSelectorSeparator.vue new file mode 100644 index 0000000..e916066 --- /dev/null +++ b/packages/elements/src/model-selector/ModelSelectorSeparator.vue @@ -0,0 +1,7 @@ + + + diff --git a/packages/elements/src/model-selector/ModelSelectorShortcut.vue b/packages/elements/src/model-selector/ModelSelectorShortcut.vue new file mode 100644 index 0000000..5539093 --- /dev/null +++ b/packages/elements/src/model-selector/ModelSelectorShortcut.vue @@ -0,0 +1,9 @@ + + + diff --git a/packages/elements/src/model-selector/ModelSelectorTrigger.vue b/packages/elements/src/model-selector/ModelSelectorTrigger.vue new file mode 100644 index 0000000..b2de0a2 --- /dev/null +++ b/packages/elements/src/model-selector/ModelSelectorTrigger.vue @@ -0,0 +1,9 @@ + + + diff --git a/packages/elements/src/model-selector/index.ts b/packages/elements/src/model-selector/index.ts new file mode 100644 index 0000000..35233e4 --- /dev/null +++ b/packages/elements/src/model-selector/index.ts @@ -0,0 +1,14 @@ +export { default as ModelSelector } from './ModelSelector.vue' +export { default as ModelSelectorContent } from './ModelSelectorContent.vue' +export { default as ModelSelectorDialog } from './ModelSelectorDialog.vue' +export { default as ModelSelectorEmpty } from './ModelSelectorEmpty.vue' +export { default as ModelSelectorGroup } from './ModelSelectorGroup.vue' +export { default as ModelSelectorInput } from './ModelSelectorInput.vue' +export { default as ModelSelectorItem } from './ModelSelectorItem.vue' +export { default as ModelSelectorList } from './ModelSelectorList.vue' +export { default as ModelSelectorLogo } from './ModelSelectorLogo.vue' +export { default as ModelSelectorLogoGroup } from './ModelSelectorLogoGroup.vue' +export { default as ModelSelectorName } from './ModelSelectorName.vue' +export { default as ModelSelectorSeparator } from './ModelSelectorSeparator.vue' +export { default as ModelSelectorShortcut } from './ModelSelectorShortcut.vue' +export { default as ModelSelectorTrigger } from './ModelSelectorTrigger.vue' diff --git a/packages/examples/src/index.ts b/packages/examples/src/index.ts index e7c0beb..56e8c68 100644 --- a/packages/examples/src/index.ts +++ b/packages/examples/src/index.ts @@ -19,6 +19,7 @@ export { default as LoaderSizes } from './loader-sizes.vue' export { default as Loader } from './loader.vue' export { default as MessageMarkdown } from './message-markdown.vue' export { default as Message } from './message.vue' +export { default as ModelSelector } from './model-selector.vue' export { default as OpenInChat } from './open-in-chat.vue' export { default as Plan } from './plan.vue' export { default as PromptInput } from './prompt-input.vue' diff --git a/packages/examples/src/model-selector.vue b/packages/examples/src/model-selector.vue new file mode 100644 index 0000000..004a5c9 --- /dev/null +++ b/packages/examples/src/model-selector.vue @@ -0,0 +1,345 @@ + + + diff --git a/packages/shadcn-vue/components/ui/command/Command.vue b/packages/shadcn-vue/components/ui/command/Command.vue new file mode 100644 index 0000000..51f1f75 --- /dev/null +++ b/packages/shadcn-vue/components/ui/command/Command.vue @@ -0,0 +1,87 @@ + + + diff --git a/packages/shadcn-vue/components/ui/command/CommandDialog.vue b/packages/shadcn-vue/components/ui/command/CommandDialog.vue new file mode 100644 index 0000000..711b9f9 --- /dev/null +++ b/packages/shadcn-vue/components/ui/command/CommandDialog.vue @@ -0,0 +1,31 @@ + + + diff --git a/packages/shadcn-vue/components/ui/command/CommandEmpty.vue b/packages/shadcn-vue/components/ui/command/CommandEmpty.vue new file mode 100644 index 0000000..a761b7c --- /dev/null +++ b/packages/shadcn-vue/components/ui/command/CommandEmpty.vue @@ -0,0 +1,27 @@ + + + diff --git a/packages/shadcn-vue/components/ui/command/CommandGroup.vue b/packages/shadcn-vue/components/ui/command/CommandGroup.vue new file mode 100644 index 0000000..8b4c1f1 --- /dev/null +++ b/packages/shadcn-vue/components/ui/command/CommandGroup.vue @@ -0,0 +1,45 @@ + + + diff --git a/packages/shadcn-vue/components/ui/command/CommandInput.vue b/packages/shadcn-vue/components/ui/command/CommandInput.vue new file mode 100644 index 0000000..95079f7 --- /dev/null +++ b/packages/shadcn-vue/components/ui/command/CommandInput.vue @@ -0,0 +1,39 @@ + + + diff --git a/packages/shadcn-vue/components/ui/command/CommandItem.vue b/packages/shadcn-vue/components/ui/command/CommandItem.vue new file mode 100644 index 0000000..c69a54e --- /dev/null +++ b/packages/shadcn-vue/components/ui/command/CommandItem.vue @@ -0,0 +1,76 @@ + + + diff --git a/packages/shadcn-vue/components/ui/command/CommandList.vue b/packages/shadcn-vue/components/ui/command/CommandList.vue new file mode 100644 index 0000000..483bc4c --- /dev/null +++ b/packages/shadcn-vue/components/ui/command/CommandList.vue @@ -0,0 +1,25 @@ + + + diff --git a/packages/shadcn-vue/components/ui/command/CommandSeparator.vue b/packages/shadcn-vue/components/ui/command/CommandSeparator.vue new file mode 100644 index 0000000..410bca0 --- /dev/null +++ b/packages/shadcn-vue/components/ui/command/CommandSeparator.vue @@ -0,0 +1,21 @@ + + + diff --git a/packages/shadcn-vue/components/ui/command/CommandShortcut.vue b/packages/shadcn-vue/components/ui/command/CommandShortcut.vue new file mode 100644 index 0000000..fda5ac8 --- /dev/null +++ b/packages/shadcn-vue/components/ui/command/CommandShortcut.vue @@ -0,0 +1,17 @@ + + + diff --git a/packages/shadcn-vue/components/ui/command/index.ts b/packages/shadcn-vue/components/ui/command/index.ts new file mode 100644 index 0000000..cb48e1e --- /dev/null +++ b/packages/shadcn-vue/components/ui/command/index.ts @@ -0,0 +1,25 @@ +import type { Ref } from 'vue' +import { createContext } from 'reka-ui' + +export { default as Command } from './Command.vue' +export { default as CommandDialog } from './CommandDialog.vue' +export { default as CommandEmpty } from './CommandEmpty.vue' +export { default as CommandGroup } from './CommandGroup.vue' +export { default as CommandInput } from './CommandInput.vue' +export { default as CommandItem } from './CommandItem.vue' +export { default as CommandList } from './CommandList.vue' +export { default as CommandSeparator } from './CommandSeparator.vue' +export { default as CommandShortcut } from './CommandShortcut.vue' + +export const [useCommand, provideCommandContext] = createContext<{ + allItems: Ref> + allGroups: Ref>> + filterState: { + search: string + filtered: { count: number, items: Map, groups: Set } + } +}>('Command') + +export const [useCommandGroup, provideCommandGroupContext] = createContext<{ + id?: string +}>('CommandGroup')