Something went wrong. Try again.
ATProto-based AI Assistant Chat interface.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263import type { ModelRef } from '@scn-chat/lexicons'import { Select, type SelectOption } from '../../../shared/ui/Select.tsx'import { type ModelOption, modelRef, sameModel } from '../models.ts'
const keyOf = (m: ModelRef) => `${m.provider}/${m.id}`
/** The choice of no model: an option, or only what the button says while none is chosen. */export interface None { label: string /** Whether "none" can be picked, rather than only shown until something is. */ selectable: boolean}
interface Props { models: ModelOption[] value: ModelRef | null onChange: (model: ModelRef | null) => void none?: None id?: string 'aria-label'?: string /** The button's tooltip, by default the chosen model's name, which the button may cut short. */ title?: string /** How the button looks: a field, or quiet text, as in the line under the composer. */ quiet?: boolean /** Whether the models are on their way, which holds the button with a line in place of a name. */ loading?: boolean className?: string}
/** A choice among the models on offer, or none, by reference. A chosen model that is not on offer shows as unavailable. */export function ModelSelect(props: Props) { const value = props.value const offered = !value || props.models.some((m) => sameModel(m, value)) const options: SelectOption[] = [ ...(props.none?.selectable ? [{ value: '', label: props.none.label }] : []), ...(value && !offered ? [{ value: keyOf(value), label: `Unavailable: ${keyOf(value)}`, disabled: true }] : []), // A search finds a model by its ID or provider too, as when its name says less. ...props.models.map((m) => ({ value: keyOf(m), label: m.name, keywords: keyOf(m) })), ] const key = value ? keyOf(value) : '' const name = options.find((option) => option.value === key)?.label ?? props.none?.label return ( <Select id={props.id} aria-label={props['aria-label']} title={props.title ?? name} quiet={props.quiet} loading={props.loading} className={props.className} options={options} value={key} placeholder={props.none?.label} searchLabel="Search models" onChange={(next) => { const model = props.models.find((m) => keyOf(m) === next) props.onChange(model ? modelRef(model) : null) }} /> )}