| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233 |
- import { createMemo, createSignal } from "solid-js"
- import { useLocal } from "@tui/context/local"
- import { useSync } from "@tui/context/sync"
- import { map, pipe, flatMap, entries, filter, sortBy, take } from "remeda"
- import { DialogSelect, type DialogSelectRef } from "@tui/ui/dialog-select"
- import { useDialog } from "@tui/ui/dialog"
- import { createDialogProviderOptions, DialogProvider } from "./dialog-provider"
- import { Keybind } from "@/util/keybind"
- import * as fuzzysort from "fuzzysort"
- export function useConnected() {
- const sync = useSync()
- return createMemo(() =>
- sync.data.provider.some((x) => x.id !== "opencode" || Object.values(x.models).some((y) => y.cost?.input !== 0)),
- )
- }
- export function DialogModel(props: { providerID?: string }) {
- const local = useLocal()
- const sync = useSync()
- const dialog = useDialog()
- const [ref, setRef] = createSignal<DialogSelectRef<unknown>>()
- const [query, setQuery] = createSignal("")
- const connected = useConnected()
- const providers = createDialogProviderOptions()
- const showExtra = createMemo(() => {
- if (!connected()) return false
- if (props.providerID) return false
- return true
- })
- const options = createMemo(() => {
- const q = query()
- const needle = q.trim()
- const showSections = showExtra() && needle.length === 0
- const favorites = connected() ? local.model.favorite() : []
- const recents = local.model.recent()
- const recentList = showSections
- ? recents.filter(
- (item) => !favorites.some((fav) => fav.providerID === item.providerID && fav.modelID === item.modelID),
- )
- : []
- const favoriteOptions = showSections
- ? favorites.flatMap((item) => {
- const provider = sync.data.provider.find((x) => x.id === item.providerID)
- if (!provider) return []
- const model = provider.models[item.modelID]
- if (!model) return []
- return [
- {
- key: item,
- value: {
- providerID: provider.id,
- modelID: model.id,
- },
- title: model.name ?? item.modelID,
- description: provider.name,
- category: "Favorites",
- disabled: provider.id === "opencode" && model.id.includes("-nano"),
- footer: model.cost?.input === 0 && provider.id === "opencode" ? "Free" : undefined,
- onSelect: () => {
- dialog.clear()
- local.model.set(
- {
- providerID: provider.id,
- modelID: model.id,
- },
- { recent: true },
- )
- },
- },
- ]
- })
- : []
- const recentOptions = showSections
- ? recentList.flatMap((item) => {
- const provider = sync.data.provider.find((x) => x.id === item.providerID)
- if (!provider) return []
- const model = provider.models[item.modelID]
- if (!model) return []
- return [
- {
- key: item,
- value: {
- providerID: provider.id,
- modelID: model.id,
- },
- title: model.name ?? item.modelID,
- description: provider.name,
- category: "Recent",
- disabled: provider.id === "opencode" && model.id.includes("-nano"),
- footer: model.cost?.input === 0 && provider.id === "opencode" ? "Free" : undefined,
- onSelect: () => {
- dialog.clear()
- local.model.set(
- {
- providerID: provider.id,
- modelID: model.id,
- },
- { recent: true },
- )
- },
- },
- ]
- })
- : []
- const providerOptions = pipe(
- sync.data.provider,
- sortBy(
- (provider) => provider.id !== "opencode",
- (provider) => provider.name,
- ),
- flatMap((provider) =>
- pipe(
- provider.models,
- entries(),
- filter(([_, info]) => info.status !== "deprecated"),
- filter(([_, info]) => (props.providerID ? info.providerID === props.providerID : true)),
- map(([model, info]) => {
- const value = {
- providerID: provider.id,
- modelID: model,
- }
- return {
- value,
- title: info.name ?? model,
- description: favorites.some(
- (item) => item.providerID === value.providerID && item.modelID === value.modelID,
- )
- ? "(Favorite)"
- : undefined,
- category: connected() ? provider.name : undefined,
- disabled: provider.id === "opencode" && model.includes("-nano"),
- footer: info.cost?.input === 0 && provider.id === "opencode" ? "Free" : undefined,
- onSelect() {
- dialog.clear()
- local.model.set(
- {
- providerID: provider.id,
- modelID: model,
- },
- { recent: true },
- )
- },
- }
- }),
- filter((x) => {
- if (!showSections) return true
- const value = x.value
- const inFavorites = favorites.some(
- (item) => item.providerID === value.providerID && item.modelID === value.modelID,
- )
- if (inFavorites) return false
- const inRecents = recentList.some(
- (item) => item.providerID === value.providerID && item.modelID === value.modelID,
- )
- if (inRecents) return false
- return true
- }),
- sortBy(
- (x) => x.footer !== "Free",
- (x) => x.title,
- ),
- ),
- ),
- )
- const popularProviders = !connected()
- ? pipe(
- providers(),
- map((option) => {
- return {
- ...option,
- category: "Popular providers",
- }
- }),
- take(6),
- )
- : []
- // Search shows a single merged list (favorites inline)
- if (needle) {
- const filteredProviders = fuzzysort.go(needle, providerOptions, { keys: ["title", "category"] }).map((x) => x.obj)
- const filteredPopular = fuzzysort.go(needle, popularProviders, { keys: ["title"] }).map((x) => x.obj)
- return [...filteredProviders, ...filteredPopular]
- }
- return [...favoriteOptions, ...recentOptions, ...providerOptions, ...popularProviders]
- })
- const provider = createMemo(() =>
- props.providerID ? sync.data.provider.find((x) => x.id === props.providerID) : null,
- )
- const title = createMemo(() => {
- if (provider()) return provider()!.name
- return "Select model"
- })
- return (
- <DialogSelect
- keybind={[
- {
- keybind: Keybind.parse("ctrl+a")[0],
- title: connected() ? "Connect provider" : "View all providers",
- onTrigger() {
- dialog.replace(() => <DialogProvider />)
- },
- },
- {
- keybind: Keybind.parse("ctrl+f")[0],
- title: "Favorite",
- disabled: !connected(),
- onTrigger: (option) => {
- local.model.toggleFavorite(option.value as { providerID: string; modelID: string })
- },
- },
- ]}
- ref={setRef}
- onFilter={setQuery}
- skipFilter={true}
- title={title()}
- current={local.model.current()}
- options={options()}
- />
- )
- }
|