diff --git a/app/components/demo/ModelPickerDemo.vue b/app/components/demo/ModelPickerDemo.vue new file mode 100644 index 0000000..f18e691 --- /dev/null +++ b/app/components/demo/ModelPickerDemo.vue @@ -0,0 +1,62 @@ + + + + + + + + + diff --git a/app/registry/blocks/model-picker/ModelPicker.vue b/app/registry/blocks/model-picker/ModelPicker.vue new file mode 100644 index 0000000..ecc6bad --- /dev/null +++ b/app/registry/blocks/model-picker/ModelPicker.vue @@ -0,0 +1,199 @@ + + + + + + + + + {{ selectedModel?.label ?? placeholder }} + + + + + + + + + + + + + + + + + {{ model.label }} + {{ model.description ?? model.provider }} + + + + + + + + + {{ emptyLabel }} + + + + + diff --git a/app/registry/blocks/model-picker/index.ts b/app/registry/blocks/model-picker/index.ts new file mode 100644 index 0000000..3394aa2 --- /dev/null +++ b/app/registry/blocks/model-picker/index.ts @@ -0,0 +1 @@ +export { default as ModelPicker, type ModelPickerModel, type ModelPickerProps } from "./ModelPicker.vue"; diff --git a/content/docs/2.components/17.model-picker.md b/content/docs/2.components/17.model-picker.md new file mode 100644 index 0000000..177b90e --- /dev/null +++ b/content/docs/2.components/17.model-picker.md @@ -0,0 +1,95 @@ +--- +title: ModelPicker +description: Render a controlled model selector from app-owned model/provider data. +category: chat +--- + +::component-preview +--- +name: ModelPickerDemo +--- +:: + +## Installation + +```bash +npx shadcn-vue@latest add "https://ui.stackhacker.io/r/model-picker.json" +``` + +## Usage + +```vue + + + + + +``` + +## App-Owned Model State + +`ModelPicker` is controlled UI. Your app owns model availability, provider metadata, persistence, cookies or local storage, pricing, and runtime selection behavior. + +Pass model options through the `models` prop and handle `update:modelValue` through `v-model`. Use scoped slots for provider icons or custom option rendering instead of Nuxt Icon strings or hardcoded provider lists. + +## Examples + +### Controlled picker + +::component-preview +--- +name: ModelPickerDemo +--- +:: + +## API Reference + +### Props + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `modelValue` | `string` | — | Controlled selected model value. | +| `models` | `ModelPickerModel[] \| null` | `[]` | Models supplied by your app. | +| `placeholder` | `string` | `'Select model'` | Trigger placeholder when selected value is unknown. | +| `searchPlaceholder` | `string` | `'Search models...'` | Search input placeholder. | +| `emptyLabel` | `string` | `'No models found.'` | Empty state text. | +| `disabled` | `boolean` | `false` | Disable the picker. | +| `align` | `'start' \| 'end'` | `'start'` | Align the menu against the trigger. | +| `class` | `string` | — | Additional CSS classes for the root element. | + +### Types + +```ts +interface ModelPickerModel { + value: string + label: string + description?: string + provider?: string + disabled?: boolean +} +``` + +### Events + +| Event | Payload | Description | +|------|---------|-------------| +| `update:modelValue` | `[value: string]` | Emitted when a selectable model is chosen. | + +### Slots + +| Slot | Props | Description | +|------|-------|-------------| +| `icon` | `{ model?: ModelPickerModel }` | Optional icon for the selected model and option rows. | +| `selected` | `{ model?: ModelPickerModel }` | Replace trigger selected-model content. | +| `option` | `{ model: ModelPickerModel, selected: boolean }` | Replace option row content. | +| `empty` | `{ query: string }` | Replace empty state content. | diff --git a/scripts/registry-verify.ts b/scripts/registry-verify.ts index fe5b213..09a0359 100644 --- a/scripts/registry-verify.ts +++ b/scripts/registry-verify.ts @@ -151,6 +151,10 @@ const expectedItems: Record = { dependencies: [], registryDependencies: [], }, + "model-picker": { + dependencies: ["@lucide/vue"], + registryDependencies: [], + }, }; if (registryIndex) {