Model Selector
Searchable model picker in a command dialog, with provider logos and grouped lists.
import { ModelSelector, ModelSelectorContent, ModelSelectorEmpty, ModelSelectorGroup, ModelSelectorInput, ModelSelectorItem, ModelSelectorList, ModelSelectorLogo, ModelSelectorName, ModelSelectorShortcut, ModelSelectorTrigger,} from "@/components/ai/model-selector";import { Button } from "@/components/ui/button";import { useState } from "react";
const models = [ { provider: "anthropic", label: "Anthropic", items: ["Claude Opus", "Claude Sonnet"], }, { provider: "openai", label: "OpenAI", items: ["GPT-5"] }, { provider: "google", label: "Google", items: ["Gemini 2.5 Pro"] },];
export default function Demo() { const [selected, setSelected] = useState("Claude Opus"); const [open, setOpen] = useState(false); const current = models.find((g) => g.items.includes(selected)); let n = 0; return ( <ModelSelector open={open} onOpenChange={setOpen}> <ModelSelectorTrigger render={<Button variant="outline" />}> {current && <ModelSelectorLogo provider={current.provider} />} <ModelSelectorName>{selected}</ModelSelectorName> </ModelSelectorTrigger> <ModelSelectorContent> <ModelSelectorInput placeholder="Search models..." /> <ModelSelectorList> <ModelSelectorEmpty>No models found.</ModelSelectorEmpty> {models.map((group) => ( <ModelSelectorGroup key={group.provider} heading={group.label}> {group.items.map((item) => ( <ModelSelectorItem key={item} value={item} onSelect={() => { setSelected(item); setOpen(false); }} > <ModelSelectorLogo provider={group.provider} /> <ModelSelectorName>{item}</ModelSelectorName> <ModelSelectorShortcut>{`⌘${++n}`}</ModelSelectorShortcut> </ModelSelectorItem> ))} </ModelSelectorGroup> ))} </ModelSelectorList> </ModelSelectorContent> </ModelSelector> );}<script setup lang="ts">import { computed, ref } from "vue";import { ModelSelector, ModelSelectorContent, ModelSelectorEmpty, ModelSelectorGroup, ModelSelectorInput, ModelSelectorItem, ModelSelectorList, ModelSelectorLogo, ModelSelectorName, ModelSelectorShortcut, ModelSelectorTrigger,} from "@/components/ai/model-selector";import { Button } from "@/components/ui/button";
const models = [ { provider: "anthropic", label: "Anthropic", items: ["Claude Opus", "Claude Sonnet"] }, { provider: "openai", label: "OpenAI", items: ["GPT-5"] }, { provider: "google", label: "Google", items: ["Gemini 2.5 Pro"] },];
const selected = ref("Claude Opus");const open = ref(false);const current = computed(() => models.find((g) => g.items.includes(selected.value)));
// Running ⌘1..⌘n shortcut index across groups.const shortcut = (provider: string, item: string) => { let n = 0; for (const g of models) { for (const i of g.items) { n++; if (g.provider === provider && i === item) return `⌘${n}`; } } return "";};
function pick(item: string) { selected.value = item; open.value = false;}</script>
<template> <ModelSelector v-model:open="open"> <ModelSelectorTrigger as-child> <Button variant="outline"> <ModelSelectorLogo v-if="current" :provider="current.provider" /> <ModelSelectorName>{{ selected }}</ModelSelectorName> </Button> </ModelSelectorTrigger> <ModelSelectorContent> <ModelSelectorInput placeholder="Search models..." /> <ModelSelectorList> <ModelSelectorEmpty>No models found.</ModelSelectorEmpty> <ModelSelectorGroup v-for="group in models" :key="group.provider" :heading="group.label"> <ModelSelectorItem v-for="item in group.items" :key="item" :value="item" @select="pick(item)" > <ModelSelectorLogo :provider="group.provider" /> <ModelSelectorName>{{ item }}</ModelSelectorName> <ModelSelectorShortcut>{{ shortcut(group.provider, item) }}</ModelSelectorShortcut> </ModelSelectorItem> </ModelSelectorGroup> </ModelSelectorList> </ModelSelectorContent> </ModelSelector></template><script lang="ts"> import { ModelSelector, ModelSelectorContent, ModelSelectorEmpty, ModelSelectorGroup, ModelSelectorInput, ModelSelectorItem, ModelSelectorList, ModelSelectorLogo, ModelSelectorName, ModelSelectorShortcut, ModelSelectorTrigger, } from "$lib/components/ai/model-selector"; import { Button } from "$lib/components/ui/button";
const models = [ { provider: "anthropic", label: "Anthropic", items: ["Claude Opus", "Claude Sonnet"] }, { provider: "openai", label: "OpenAI", items: ["GPT-5"] }, { provider: "google", label: "Google", items: ["Gemini 2.5 Pro"] }, ];
let selected = $state("Claude Opus"); let open = $state(false); const current = $derived(models.find((g) => g.items.includes(selected)));
// Running ⌘1..⌘n shortcut index across groups. const shortcut = (provider: string, item: string) => { let n = 0; for (const g of models) { for (const i of g.items) { n++; if (g.provider === provider && i === item) return `⌘${n}`; } } return ""; };
function pick(item: string) { selected = item; open = false; }</script>
<ModelSelector bind:open> <ModelSelectorTrigger> {#snippet child({ props })} <Button variant="outline" {...props}> {#if current}<ModelSelectorLogo provider={current.provider} />{/if} <ModelSelectorName>{selected}</ModelSelectorName> </Button> {/snippet} </ModelSelectorTrigger> <ModelSelectorContent> <ModelSelectorInput placeholder="Search models..." /> <ModelSelectorList> <ModelSelectorEmpty>No models found.</ModelSelectorEmpty> {#each models as group (group.provider)} <ModelSelectorGroup heading={group.label}> {#each group.items as item (item)} <ModelSelectorItem value={item} onSelect={() => pick(item)}> <ModelSelectorLogo provider={group.provider} /> <ModelSelectorName>{item}</ModelSelectorName> <ModelSelectorShortcut>{shortcut(group.provider, item)}</ModelSelectorShortcut> </ModelSelectorItem> {/each} </ModelSelectorGroup> {/each} </ModelSelectorList> </ModelSelectorContent></ModelSelector>Built on command and dialog.
Installation
Section titled “Installation”npx shadcn@latest add @edmi-ui/ai-model-selectorpnpm dlx shadcn@latest add @edmi-ui/ai-model-selectoryarn dlx shadcn@latest add @edmi-ui/ai-model-selectorbunx shadcn@latest add @edmi-ui/ai-model-selectorPick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.
Add the Edmi components this one builds on:
command,dialog.Copy the code from Component source into your project and update the import paths to match your aliases.
npx shadcn-vue@latest add @edmi-ui/ai-model-selectorpnpm dlx shadcn-vue@latest add @edmi-ui/ai-model-selectoryarn dlx shadcn-vue@latest add @edmi-ui/ai-model-selectorbunx shadcn-vue@latest add @edmi-ui/ai-model-selectorPick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.
Install the dependencies:
Terminal window npm install reka-uiTerminal window pnpm add reka-uiTerminal window yarn add reka-uiTerminal window bun add reka-uiAdd the Edmi components this one builds on:
command,dialog.Copy the code from Component source into your project and update the import paths to match your aliases.
npx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/ai-model-selector.jsonpnpm dlx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/ai-model-selector.jsonyarn dlx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/ai-model-selector.jsonbunx --bun shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/ai-model-selector.jsonPick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.
Add the Edmi components this one builds on:
command,dialog.Copy the code from Component source into your project and update the import paths to match your aliases.
import { ModelSelector, ModelSelectorContent, ModelSelectorEmpty, ModelSelectorInput, ModelSelectorList, ModelSelectorName, ModelSelectorTrigger,} from "@/components/ai/model-selector";import { Button } from "@/components/ui/button";
<ModelSelector> <ModelSelectorTrigger render={<Button variant="outline" />}> <ModelSelectorName></ModelSelectorName> </ModelSelectorTrigger> <ModelSelectorContent> <ModelSelectorInput placeholder="Search models..." /> <ModelSelectorList> <ModelSelectorEmpty>No models found.</ModelSelectorEmpty> </ModelSelectorList> </ModelSelectorContent></ModelSelector><script setup lang="ts">import { ModelSelector, ModelSelectorContent, ModelSelectorEmpty, ModelSelectorGroup, ModelSelectorInput, ModelSelectorItem, ModelSelectorList, ModelSelectorLogo, ModelSelectorName, ModelSelectorShortcut, ModelSelectorTrigger,} from "@/components/ai/model-selector";import { Button } from "@/components/ui/button";</script>
<template> <ModelSelector> <ModelSelectorTrigger as-child> <Button variant="outline"> <ModelSelectorLogo /> <ModelSelectorName></ModelSelectorName> </Button> </ModelSelectorTrigger> <ModelSelectorContent> <ModelSelectorInput placeholder="Search models..." /> <ModelSelectorList> <ModelSelectorEmpty>No models found.</ModelSelectorEmpty> <ModelSelectorGroup :key="group.provider" :heading="group.label"> <ModelSelectorItem v-for="item in group.items" :key="item" :value="item" @select="pick(item)" > <ModelSelectorLogo :provider="group.provider" /> <ModelSelectorName>{{ item }}</ModelSelectorName> <ModelSelectorShortcut></ModelSelectorShortcut> </ModelSelectorItem> </ModelSelectorGroup> </ModelSelectorList> </ModelSelectorContent> </ModelSelector></template><script lang="ts"> import { ModelSelector, ModelSelectorLogo, ModelSelectorName, ModelSelectorTrigger, } from "$lib/components/ai/model-selector"; import { Button } from "$lib/components/ui/button";</script>
<ModelSelector bind:open> <ModelSelectorTrigger> {#snippet child({ props })} <Button variant="outline" {...props}> {#if current}<ModelSelectorLogo />{/if} <ModelSelectorName></ModelSelectorName> </Button> {/snippet} </ModelSelectorTrigger> <!-- … --></ModelSelector>Component source
Section titled “Component source”"use client";
import { cn } from "cn";import type { ComponentProps, ReactNode } from "react";import { Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut,} from "@/registry/edmi/ui/command";import { Dialog, DialogContent, DialogTitle, DialogTrigger,} from "@/registry/edmi/ui/dialog";
// Built on ui/command + ui/dialog (DESIGN §5b): the palette body is the Edmi Command, the popup the Edmi Dialog.
export type ModelSelectorProps = ComponentProps<typeof Dialog>;
export const ModelSelector = (props: ModelSelectorProps) => ( <Dialog {...props} />);
export type ModelSelectorTriggerProps = ComponentProps<typeof DialogTrigger>;
/** Pass the visible trigger with `render`, e.g. `render={<Button variant="outline" />}`. */export const ModelSelectorTrigger = (props: ModelSelectorTriggerProps) => ( <DialogTrigger data-slot="ai-model-selector-trigger" {...props} />);
export type ModelSelectorContentProps = ComponentProps<typeof DialogContent> & { title?: ReactNode;};
export const ModelSelectorContent = ({ className, children, title = "Model Selector", ...props}: ModelSelectorContentProps) => ( <DialogContent data-slot="ai-model-selector-content" aria-describedby={undefined} showCloseButton={false} className={cn( "top-1/3 translate-y-0 gap-0 overflow-hidden p-0 sm:max-w-md", className, )} {...props} > <DialogTitle className="sr-only">{title}</DialogTitle> <Command className="rounded-none">{children}</Command> </DialogContent>);
export type ModelSelectorDialogProps = ComponentProps<typeof CommandDialog>;
export const ModelSelectorDialog = (props: ModelSelectorDialogProps) => ( <CommandDialog {...props} />);
export type ModelSelectorInputProps = ComponentProps<typeof CommandInput>;
export const ModelSelectorInput = ({ className, ...props}: ModelSelectorInputProps) => ( <CommandInput className={cn("h-auto py-3.5", className)} {...props} />);
export type ModelSelectorListProps = ComponentProps<typeof CommandList>;
export const ModelSelectorList = (props: ModelSelectorListProps) => ( <CommandList {...props} />);
export type ModelSelectorEmptyProps = ComponentProps<typeof CommandEmpty>;
export const ModelSelectorEmpty = (props: ModelSelectorEmptyProps) => ( <CommandEmpty {...props} />);
export type ModelSelectorGroupProps = ComponentProps<typeof CommandGroup>;
export const ModelSelectorGroup = (props: ModelSelectorGroupProps) => ( <CommandGroup {...props} />);
export type ModelSelectorItemProps = ComponentProps<typeof CommandItem>;
export const ModelSelectorItem = (props: ModelSelectorItemProps) => ( <CommandItem {...props} />);
export type ModelSelectorShortcutProps = ComponentProps<typeof CommandShortcut>;
export const ModelSelectorShortcut = (props: ModelSelectorShortcutProps) => ( <CommandShortcut {...props} />);
export type ModelSelectorSeparatorProps = ComponentProps< typeof CommandSeparator>;
export const ModelSelectorSeparator = (props: ModelSelectorSeparatorProps) => ( <CommandSeparator {...props} />);
export type ModelSelectorLogoProps = Omit< ComponentProps<"img">, "src" | "alt"> & { /** A models.dev provider id (`anthropic`, `openai`, `google`, …); any string works. */ provider: | "anthropic" | "openai" | "google" | "xai" | "mistral" | "groq" | "deepseek" | "perplexity" | "meta" | (string & {});};
export const ModelSelectorLogo = ({ provider, className, ...props}: ModelSelectorLogoProps) => ( <img {...props} alt={`${provider} logo`} className={cn("size-3.5 dark:invert", className)} height={14} src={`https://models.dev/logos/${provider}.svg`} width={14} />);
export type ModelSelectorLogoGroupProps = ComponentProps<"div">;
/** Overlapping logos, each on a solid --card disc with a 1px --border ring. */export const ModelSelectorLogoGroup = ({ className, ...props}: ModelSelectorLogoGroupProps) => ( <div className={cn( "flex shrink-0 items-center -space-x-1 [&>img]:rounded-full [&>img]:border [&>img]:border-border [&>img]:bg-card [&>img]:p-px", className, )} {...props} />);
export type ModelSelectorNameProps = ComponentProps<"span">;
export const ModelSelectorName = ({ className, ...props}: ModelSelectorNameProps) => ( <span className={cn("flex-1 truncate text-left", className)} {...props} />);<script setup lang="ts">import type { DialogRootEmits, DialogRootProps } from "reka-ui"import { useForwardPropsEmits } from "reka-ui"import { Dialog } from "@/registry/edmi/ui/dialog"
// Built on ui/command + ui/dialog (DESIGN §5b): the palette body is the Edmi Command, the popup the Edmi Dialog.const props = defineProps<DialogRootProps>()const emits = defineEmits<DialogRootEmits>()const forwarded = useForwardPropsEmits(props, emits)</script>
<template> <Dialog v-bind="forwarded"> <slot /> </Dialog></template><script setup lang="ts">import type { AcceptableValue } from "reka-ui"import type { HTMLAttributes } from "vue"import { cn } from "@/registry/edmi/lib/utils"import { Command } from "@/registry/edmi/ui/command"import { DialogContent, DialogDescription, DialogTitle } from "@/registry/edmi/ui/dialog"
defineOptions({ inheritAttrs: false })
const props = withDefaults(defineProps<{ title?: string class?: HTMLAttributes["class"]}>(), { title: "Model Selector",})const value = defineModel<AcceptableValue | AcceptableValue[]>()</script>
<template> <DialogContent v-bind="$attrs" data-slot="ai-model-selector-content" :show-close-button="false" :class="cn('top-1/3 translate-y-0 gap-0 overflow-hidden p-0 sm:max-w-md', props.class)" > <DialogTitle class="sr-only"> {{ props.title }} </DialogTitle> <DialogDescription class="sr-only"> Search and pick a model </DialogDescription> <Command v-model="value" class="rounded-none **:data-[slot=command-input-wrapper]:h-auto"> <slot /> </Command> </DialogContent></template><script setup lang="ts">import type { DialogRootEmits, DialogRootProps } from "reka-ui"import { useForwardPropsEmits } from "reka-ui"import { CommandDialog } from "@/registry/edmi/ui/command"
const props = defineProps<DialogRootProps & { title?: string, description?: string }>()const emits = defineEmits<DialogRootEmits>()const forwarded = useForwardPropsEmits(props, emits)</script>
<template> <CommandDialog v-bind="forwarded"> <slot /> </CommandDialog></template><script setup lang="ts">import { CommandEmpty } from "@/registry/edmi/ui/command"
type Props = InstanceType<typeof CommandEmpty>["$props"]interface ModelSelectorProps extends /* @vue-ignore */ Props {}defineProps<ModelSelectorProps>()</script>
<template> <CommandEmpty> <slot /> </CommandEmpty></template><script setup lang="ts">import { CommandGroup } from "@/registry/edmi/ui/command"
type Props = InstanceType<typeof CommandGroup>["$props"]interface ModelSelectorProps extends /* @vue-ignore */ Props {}defineProps<ModelSelectorProps>()</script>
<template> <CommandGroup> <slot /> </CommandGroup></template><script setup lang="ts">import type { HTMLAttributes } from "vue"import { cn } from "@/registry/edmi/lib/utils"import { CommandInput } from "@/registry/edmi/ui/command"
const props = defineProps<{ class?: HTMLAttributes["class"] placeholder?: string}>()</script>
<template> <CommandInput :placeholder="props.placeholder" :class="cn('py-3.5', props.class)" /></template><script setup lang="ts">import { CommandItem } from "@/registry/edmi/ui/command"
type Props = InstanceType<typeof CommandItem>["$props"]interface ModelSelectorProps extends /* @vue-ignore */ Props {}defineOptions({ inheritAttrs: false })defineProps<ModelSelectorProps>()</script>
<template> <CommandItem v-bind="($attrs as any)"> <slot /> </CommandItem></template><script setup lang="ts">import { CommandList } from "@/registry/edmi/ui/command"
type Props = InstanceType<typeof CommandList>["$props"]interface ModelSelectorProps extends /* @vue-ignore */ Props {}defineProps<ModelSelectorProps>()</script>
<template> <CommandList> <slot /> </CommandList></template><script setup lang="ts">import type { HTMLAttributes } from "vue"import { cn } from "@/registry/edmi/lib/utils"
const props = defineProps<{ /** A models.dev provider id (`anthropic`, `openai`, `google`, ...); any string works. */ provider: string class?: HTMLAttributes["class"]}>()</script>
<template> <img :alt="`${props.provider} logo`" :class="cn('size-3.5 dark:invert', props.class)" height="14" :src="`https://models.dev/logos/${props.provider}.svg`" width="14" ></template><script setup lang="ts">import type { HTMLAttributes } from "vue"import { cn } from "@/registry/edmi/lib/utils"
const props = defineProps<{ class?: HTMLAttributes["class"]}>()</script>
<template> <!-- Overlapping logos, each on a solid --card disc with a 1px --border ring. --> <div :class="cn( 'flex shrink-0 items-center -space-x-1 [&>img]:rounded-full [&>img]:border [&>img]:border-border [&>img]:bg-card [&>img]:p-px', props.class, )" > <slot /> </div></template><script setup lang="ts">import type { HTMLAttributes } from "vue"import { cn } from "@/registry/edmi/lib/utils"
const props = defineProps<{ class?: HTMLAttributes["class"]}>()</script>
<template> <span :class="cn('flex-1 truncate text-left', props.class)"> <slot /> </span></template><script setup lang="ts">import { CommandSeparator } from "@/registry/edmi/ui/command"
type Props = InstanceType<typeof CommandSeparator>["$props"]interface ModelSelectorProps extends /* @vue-ignore */ Props {}defineProps<ModelSelectorProps>()</script>
<template> <CommandSeparator> <slot /> </CommandSeparator></template><script setup lang="ts">import { CommandShortcut } from "@/registry/edmi/ui/command"
type Props = InstanceType<typeof CommandShortcut>["$props"]interface ModelSelectorProps extends /* @vue-ignore */ Props {}defineProps<ModelSelectorProps>()</script>
<template> <CommandShortcut> <slot /> </CommandShortcut></template><script setup lang="ts">import type { DialogTriggerProps } from "reka-ui"import { DialogTrigger } from "@/registry/edmi/ui/dialog"
// Pass the visible trigger with `as-child`, e.g. <ModelSelectorTrigger as-child><Button variant="outline" /></ModelSelectorTrigger>.const props = defineProps<DialogTriggerProps>()</script>
<template> <DialogTrigger data-slot="ai-model-selector-trigger" v-bind="props"> <slot /> </DialogTrigger></template>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";import Root from "./model-selector.svelte";import Content from "./model-selector-content.svelte";import Dialog from "./model-selector-dialog.svelte";import Empty from "./model-selector-empty.svelte";import Group from "./model-selector-group.svelte";import Input from "./model-selector-input.svelte";import Item from "./model-selector-item.svelte";import List from "./model-selector-list.svelte";import Logo from "./model-selector-logo.svelte";import LogoGroup from "./model-selector-logo-group.svelte";import Name from "./model-selector-name.svelte";import Separator from "./model-selector-separator.svelte";import Shortcut from "./model-selector-shortcut.svelte";import Trigger from "./model-selector-trigger.svelte";
export { Content, Content as ModelSelectorContent, Dialog, Dialog as ModelSelectorDialog, Empty, Empty as ModelSelectorEmpty, Group, Group as ModelSelectorGroup, Input, Input as ModelSelectorInput, Item, Item as ModelSelectorItem, List, List as ModelSelectorList, Logo, Logo as ModelSelectorLogo, LogoGroup, LogoGroup as ModelSelectorLogoGroup, Name, Name as ModelSelectorName, Root, // Root as ModelSelector, Separator, Separator as ModelSelectorSeparator, Shortcut, Shortcut as ModelSelectorShortcut, Trigger, Trigger as ModelSelectorTrigger,};<script lang="ts"> import { Command } from "$lib/registry/ui/command/index.js"; import { DialogContent, DialogDescription, DialogTitle, } from "$lib/registry/ui/dialog/index.js"; import { cn } from "$lib/utils.js"; import type { ComponentProps, Snippet } from "svelte";
let { class: className, children, title = "Model Selector", value = $bindable(""), ...restProps }: Omit<ComponentProps<typeof DialogContent>, "children" | "showCloseButton"> & { title?: string; value?: string; children?: Snippet; } = $props();</script>
<DialogContent data-slot="ai-model-selector-content" showCloseButton={false} class={cn("top-1/3 translate-y-0 gap-0 overflow-hidden p-0 sm:max-w-md", className)} {...restProps}> <DialogTitle class="sr-only">{title}</DialogTitle> <DialogDescription class="sr-only">Search and pick a model</DialogDescription> <Command bind:value class="rounded-none **:data-[slot=command-input-wrapper]:h-auto"> {@render children?.()} </Command></DialogContent><script lang="ts"> import { CommandDialog } from "$lib/registry/ui/command/index.js"; import type { ComponentProps } from "svelte";
let { open = $bindable(false), value = $bindable(""), ...restProps }: ComponentProps<typeof CommandDialog> = $props();</script>
<CommandDialog bind:open bind:value {...restProps} /><script lang="ts"> import { CommandEmpty } from "$lib/registry/ui/command/index.js"; import type { ComponentProps } from "svelte";
let { ref = $bindable(null), children, ...restProps }: ComponentProps<typeof CommandEmpty> = $props();</script>
<CommandEmpty bind:ref {...restProps}> {@render children?.()}</CommandEmpty><script lang="ts"> import { CommandGroup } from "$lib/registry/ui/command/index.js"; import type { ComponentProps } from "svelte";
let { ref = $bindable(null), children, ...restProps }: ComponentProps<typeof CommandGroup> = $props();</script>
<CommandGroup bind:ref {...restProps}> {@render children?.()}</CommandGroup><script lang="ts"> import { CommandInput } from "$lib/registry/ui/command/index.js"; import { cn } from "$lib/utils.js"; import type { ComponentProps } from "svelte";
let { ref = $bindable(null), value = $bindable(""), class: className, ...restProps }: ComponentProps<typeof CommandInput> = $props();</script>
<CommandInput bind:ref bind:value class={cn("py-3.5", className)} {...restProps} /><script lang="ts"> import { CommandItem } from "$lib/registry/ui/command/index.js"; import type { ComponentProps } from "svelte";
let { ref = $bindable(null), children, ...restProps }: ComponentProps<typeof CommandItem> = $props();</script>
<CommandItem bind:ref {...restProps}> {@render children?.()}</CommandItem><script lang="ts"> import { CommandList } from "$lib/registry/ui/command/index.js"; import type { ComponentProps } from "svelte";
let { ref = $bindable(null), children, ...restProps }: ComponentProps<typeof CommandList> = $props();</script>
<CommandList bind:ref {...restProps}> {@render children?.()}</CommandList><script lang="ts"> import { cn } from "$lib/utils.js"; import type { Snippet } from "svelte"; import type { HTMLAttributes } from "svelte/elements";
let { class: className, children, ...restProps }: HTMLAttributes<HTMLDivElement> & { children?: Snippet } = $props();</script>
<!-- Overlapping logos, each on a solid --card disc with a 1px --border ring. --><div class={cn( "flex shrink-0 items-center -space-x-1 [&>img]:rounded-full [&>img]:border [&>img]:border-border [&>img]:bg-card [&>img]:p-px", className )} {...restProps}> {@render children?.()}</div><script lang="ts"> import { cn } from "$lib/utils.js"; import type { HTMLImgAttributes } from "svelte/elements";
let { provider, class: className, ...restProps }: Omit<HTMLImgAttributes, "src" | "alt"> & { /** A models.dev provider id (`anthropic`, `openai`, `google`, ...); any string works. */ provider: string; } = $props();</script>
<img {...restProps} alt="{provider} logo" class={cn("size-3.5 dark:invert", className)} height="14" src="https://models.dev/logos/{provider}.svg" width="14"/><script lang="ts"> import { cn } from "$lib/utils.js"; import type { Snippet } from "svelte"; import type { HTMLAttributes } from "svelte/elements";
let { class: className, children, ...restProps }: HTMLAttributes<HTMLSpanElement> & { children?: Snippet } = $props();</script>
<span class={cn("flex-1 truncate text-left", className)} {...restProps}> {@render children?.()}</span><script lang="ts"> import { CommandSeparator } from "$lib/registry/ui/command/index.js"; import type { ComponentProps } from "svelte";
let { ref = $bindable(null), children, ...restProps }: ComponentProps<typeof CommandSeparator> = $props();</script>
<CommandSeparator bind:ref {...restProps}> {@render children?.()}</CommandSeparator><script lang="ts"> import { CommandShortcut } from "$lib/registry/ui/command/index.js"; import type { ComponentProps } from "svelte";
let { ref = $bindable(null), children, ...restProps }: ComponentProps<typeof CommandShortcut> = $props();</script>
<CommandShortcut bind:ref {...restProps}> {@render children?.()}</CommandShortcut><script lang="ts"> import { DialogTrigger } from "$lib/registry/ui/dialog/index.js"; import type { ComponentProps } from "svelte";
// Pass the visible trigger with the `child` snippet, e.g. `{#snippet child({ props })}<Button {...props} />{/snippet}`. let { ref = $bindable(null), ...restProps }: ComponentProps<typeof DialogTrigger> = $props();</script>
<DialogTrigger bind:ref data-slot="ai-model-selector-trigger" {...restProps} /><script lang="ts"> import { Dialog } from "$lib/registry/ui/dialog/index.js"; import type { ComponentProps } from "svelte";
// Built on ui/command + ui/dialog (DESIGN §5b): the palette body is the Edmi Command, the popup the Edmi Dialog. let { open = $bindable(false), ...restProps }: ComponentProps<typeof Dialog> = $props();</script>
<Dialog bind:open {...restProps} />| Part | Prop | Type | Default |
|---|---|---|---|
| ModelSelector | Dialog props (open, onOpenChange) |
||
| ModelSelectorTrigger | render |
element, e.g. <Button variant="outline" /> |
|
| ModelSelectorContent | title |
ReactNode (screen reader) |
Model Selector |
| ModelSelectorLogo | provider |
models.dev provider id | |
| ModelSelectorInput/List/Empty/Group/Item/Shortcut/Separator/Name/LogoGroup, ModelSelectorDialog |
Logos load from https://models.dev/logos/<provider>.svg.
Vue and Svelte
Section titled “Vue and Svelte”- Trigger: React
render={<Button />}, Vueas-child, Svelte{#snippet child({ props })}. Item selection: ReactonSelect, Vue@select, SvelteonSelect.
✦ Edmi additions
Section titled “✦ Edmi additions”- The popup is the Edmi dialog with the command palette inside (no close button, search first).