/
Alex_Ural
/
opencode
Обзор
Документация
Войти
/
Alex_Ural
/
opencode
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
dev
packages/app/src/components/settings-models.tsx
149 строк
6 KB
Brendan Allan
feat(app): add servers tab to settings dialog (#29675)
03 июн 2026, 11:25
Не верифицирован
03 июн 2026, 11:25
c6f6843
Код
Авторство
О чём код?
import { useFilteredList } from "@opencode-ai/ui/hooks" import { ProviderIcon } from "@opencode-ai/ui/provider-icon" import { Switch } from "@opencode-ai/ui/switch" import { Icon } from "@opencode-ai/ui/icon" import { IconButton } from "@opencode-ai/ui/icon-button" import { TextField } from "@opencode-ai/ui/text-field" import { type Component, For, Show } from "solid-js" import { useLanguage } from "@/context/language" import { useModels } from "@/context/models" import { popularProviders } from "@/hooks/use-providers" import { SettingsList } from "./settings-list" import { SettingsServerPicker, SettingsServerScope } from "./settings-server-picker" type ModelItem = ReturnType<ReturnType<typeof useModels>["list"]>[number] const ListLoadingState: Component<{ label: string }> = (props) => { return ( <div class="flex flex-col items-center justify-center py-12 text-center"> <span class="text-14-regular text-text-weak">{props.label}</span> </div> ) } const ListEmptyState: Component<{ message: string; filter: string }> = (props) => { return ( <div class="flex flex-col items-center justify-center py-12 text-center"> <span class="text-14-regular text-text-weak">{props.message}</span> <Show when={props.filter}> <span class="text-14-regular text-text-strong mt-1">"{props.filter}"</span> </Show> </div> ) } export const SettingsModels: Component = () => { return ( <SettingsServerScope> <SettingsModelsContent /> </SettingsServerScope> ) } const SettingsModelsContent: Component = () => { const language = useLanguage() const models = useModels() const list = useFilteredList<ModelItem>({ items: (_filter) => models.list(), key: (x) => `${x.provider.id}:${x.id}`, filterKeys: ["provider.name", "name", "id"], sortBy: (a, b) => a.name.localeCompare(b.name), groupBy: (x) => x.provider.id, sortGroupsBy: (a, b) => { const aIndex = popularProviders.indexOf(a.category) const bIndex = popularProviders.indexOf(b.category) const aPopular = aIndex >= 0 const bPopular = bIndex >= 0 if (aPopular && !bPopular) return -1 if (!aPopular && bPopular) return 1 if (aPopular && bPopular) return aIndex - bIndex const aName = a.items[0].provider.name const bName = b.items[0].provider.name return aName.localeCompare(bName) }, }) return ( <div class="flex flex-col h-full overflow-y-auto no-scrollbar px-4 pb-10 sm:px-10 sm:pb-10"> <div class="sticky top-0 z-10 bg-[linear-gradient(to_bottom,var(--surface-stronger-non-alpha)_calc(100%_-_24px),transparent)]"> <div class="flex flex-col gap-4 pt-6 pb-6 max-w-[720px]"> <div class="flex items-center justify-between gap-4"> <h2 class="text-16-medium text-text-strong">{language.t("settings.models.title")}</h2> <SettingsServerPicker /> </div> <div class="flex items-center gap-2 px-3 h-9 rounded-lg bg-surface-base"> <Icon name="magnifying-glass" class="text-icon-weak-base flex-shrink-0" /> <TextField variant="ghost" type="text" value={list.filter()} onChange={list.onInput} placeholder={language.t("dialog.model.search.placeholder")} spellcheck={false} autocorrect="off" autocomplete="off" autocapitalize="off" class="flex-1" /> <Show when={list.filter()}> <IconButton icon="circle-x" variant="ghost" onClick={list.clear} /> </Show> </div> </div> </div> <div class="flex flex-col gap-8 max-w-[720px]"> <Show when={!list.grouped.loading} fallback={ <ListLoadingState label={`${language.t("common.loading")}${language.t("common.loading.ellipsis")}`} /> } > <Show when={list.flat().length > 0} fallback={<ListEmptyState message={language.t("dialog.model.empty")} filter={list.filter()} />} > <For each={list.grouped.latest}> {(group) => ( <div class="flex flex-col gap-1"> <div class="flex items-center gap-2 pb-2"> <ProviderIcon id={group.category} class="size-5 shrink-0 icon-strong-base" /> <span class="text-14-medium text-text-strong">{group.items[0].provider.name}</span> </div> <SettingsList> <For each={group.items}> {(item) => { const key = { providerID: item.provider.id, modelID: item.id } return ( <div class="flex flex-wrap items-center justify-between gap-4 py-3 border-b border-border-weak-base last:border-none"> <div class="min-w-0"> <span class="text-14-regular text-text-strong truncate block">{item.name}</span> </div> <div class="flex-shrink-0"> <Switch checked={models.visible(key)} onChange={(checked) => { models.setVisibility(key, checked) }} hideLabel > {item.name} </Switch> </div> </div> ) }} </For> </SettingsList> </div> )} </For> </Show> </Show> </div> </div> ) }