refactor: clean up dialog-model.tsx per code review (#12983)

This commit is contained in:
Dax
2026-02-10 10:13:37 -05:00
committed by GitHub
parent 1e03a55acd
commit 27fa9dc843
2 changed files with 78 additions and 137 deletions

View File

@@ -2,7 +2,7 @@ import { createMemo, createSignal } from "solid-js"
import { useLocal } from "@tui/context/local" import { useLocal } from "@tui/context/local"
import { useSync } from "@tui/context/sync" import { useSync } from "@tui/context/sync"
import { map, pipe, flatMap, entries, filter, sortBy, take } from "remeda" import { map, pipe, flatMap, entries, filter, sortBy, take } from "remeda"
import { DialogSelect, type DialogSelectRef } from "@tui/ui/dialog-select" import { DialogSelect } from "@tui/ui/dialog-select"
import { useDialog } from "@tui/ui/dialog" import { useDialog } from "@tui/ui/dialog"
import { createDialogProviderOptions, DialogProvider } from "./dialog-provider" import { createDialogProviderOptions, DialogProvider } from "./dialog-provider"
import { useKeybind } from "../context/keybind" import { useKeybind } from "../context/keybind"
@@ -20,96 +20,51 @@ export function DialogModel(props: { providerID?: string }) {
const sync = useSync() const sync = useSync()
const dialog = useDialog() const dialog = useDialog()
const keybind = useKeybind() const keybind = useKeybind()
const [ref, setRef] = createSignal<DialogSelectRef<unknown>>()
const [query, setQuery] = createSignal("") const [query, setQuery] = createSignal("")
const connected = useConnected() const connected = useConnected()
const providers = createDialogProviderOptions() const providers = createDialogProviderOptions()
const showExtra = createMemo(() => { const showExtra = createMemo(() => connected() && !props.providerID)
if (!connected()) return false
if (props.providerID) return false
return true
})
const options = createMemo(() => { const options = createMemo(() => {
const q = query() const needle = query().trim()
const needle = q.trim()
const showSections = showExtra() && needle.length === 0 const showSections = showExtra() && needle.length === 0
const favorites = connected() ? local.model.favorite() : [] const favorites = connected() ? local.model.favorite() : []
const recents = local.model.recent() const recents = local.model.recent()
const recentList = showSections function toOptions(items: typeof favorites, category: string) {
? recents.filter( if (!showSections) return []
(item) => !favorites.some((fav) => fav.providerID === item.providerID && fav.modelID === item.modelID), return items.flatMap((item) => {
) const provider = sync.data.provider.find((x) => x.id === item.providerID)
: [] if (!provider) return []
const model = provider.models[item.modelID]
const favoriteOptions = showSections if (!model) return []
? favorites.flatMap((item) => { return [
const provider = sync.data.provider.find((x) => x.id === item.providerID) {
if (!provider) return [] key: item,
const model = provider.models[item.modelID] value: { providerID: provider.id, modelID: model.id },
if (!model) return [] title: model.name ?? item.modelID,
return [ description: provider.name,
{ category,
key: item, disabled: provider.id === "opencode" && model.id.includes("-nano"),
value: { footer: model.cost?.input === 0 && provider.id === "opencode" ? "Free" : undefined,
providerID: provider.id, onSelect: () => {
modelID: model.id, dialog.clear()
}, local.model.set({ providerID: provider.id, modelID: model.id }, { recent: true })
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 const favoriteOptions = toOptions(favorites, "Favorites")
? recentList.flatMap((item) => { const recentOptions = toOptions(
const provider = sync.data.provider.find((x) => x.id === item.providerID) recents.filter(
if (!provider) return [] (item) => !favorites.some((fav) => fav.providerID === item.providerID && fav.modelID === item.modelID),
const model = provider.models[item.modelID] ),
if (!model) return [] "Recent",
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( const providerOptions = pipe(
sync.data.provider, sync.data.provider,
@@ -123,45 +78,26 @@ export function DialogModel(props: { providerID?: string }) {
entries(), entries(),
filter(([_, info]) => info.status !== "deprecated"), filter(([_, info]) => info.status !== "deprecated"),
filter(([_, info]) => (props.providerID ? info.providerID === props.providerID : true)), filter(([_, info]) => (props.providerID ? info.providerID === props.providerID : true)),
map(([model, info]) => { map(([model, info]) => ({
const value = { value: { providerID: provider.id, modelID: model },
providerID: provider.id, title: info.name ?? model,
modelID: model, description: favorites.some((item) => item.providerID === provider.id && item.modelID === model)
} ? "(Favorite)"
return { : undefined,
value, category: connected() ? provider.name : undefined,
title: info.name ?? model, disabled: provider.id === "opencode" && model.includes("-nano"),
description: favorites.some( footer: info.cost?.input === 0 && provider.id === "opencode" ? "Free" : undefined,
(item) => item.providerID === value.providerID && item.modelID === value.modelID, onSelect() {
) dialog.clear()
? "(Favorite)" local.model.set({ providerID: provider.id, modelID: model }, { recent: true })
: 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) => { filter((x) => {
if (!showSections) return true if (!showSections) return true
const value = x.value if (favorites.some((item) => item.providerID === x.value.providerID && item.modelID === x.value.modelID))
const inFavorites = favorites.some( return false
(item) => item.providerID === value.providerID && item.modelID === value.modelID, if (recents.some((item) => item.providerID === x.value.providerID && item.modelID === x.value.modelID))
) return false
if (inFavorites) return false
const inRecents = recents.some(
(item) => item.providerID === value.providerID && item.modelID === value.modelID,
)
if (inRecents) return false
return true return true
}), }),
sortBy( sortBy(
@@ -175,21 +111,19 @@ export function DialogModel(props: { providerID?: string }) {
const popularProviders = !connected() const popularProviders = !connected()
? pipe( ? pipe(
providers(), providers(),
map((option) => { map((option) => ({
return { ...option,
...option, category: "Popular providers",
category: "Popular providers", })),
}
}),
take(6), take(6),
) )
: [] : []
// Search shows a single merged list (favorites inline)
if (needle) { if (needle) {
const filteredProviders = fuzzysort.go(needle, providerOptions, { keys: ["title", "category"] }).map((x) => x.obj) return [
const filteredPopular = fuzzysort.go(needle, popularProviders, { keys: ["title"] }).map((x) => x.obj) ...fuzzysort.go(needle, providerOptions, { keys: ["title", "category"] }).map((x) => x.obj),
return [...filteredProviders, ...filteredPopular] ...fuzzysort.go(needle, popularProviders, { keys: ["title"] }).map((x) => x.obj),
]
} }
return [...favoriteOptions, ...recentOptions, ...providerOptions, ...popularProviders] return [...favoriteOptions, ...recentOptions, ...providerOptions, ...popularProviders]
@@ -199,13 +133,11 @@ export function DialogModel(props: { providerID?: string }) {
props.providerID ? sync.data.provider.find((x) => x.id === props.providerID) : null, props.providerID ? sync.data.provider.find((x) => x.id === props.providerID) : null,
) )
const title = createMemo(() => { const title = createMemo(() => provider()?.name ?? "Select model")
if (provider()) return provider()!.name
return "Select model"
})
return ( return (
<DialogSelect <DialogSelect<ReturnType<typeof options>[number]["value"]>
options={options()}
keybind={[ keybind={[
{ {
keybind: keybind.all.model_provider_list?.[0], keybind: keybind.all.model_provider_list?.[0],
@@ -223,12 +155,11 @@ export function DialogModel(props: { providerID?: string }) {
}, },
}, },
]} ]}
ref={setRef}
onFilter={setQuery} onFilter={setQuery}
flat={true}
skipFilter={true} skipFilter={true}
title={title()} title={title()}
current={local.model.current()} current={local.model.current()}
options={options()}
/> />
) )
} }

View File

@@ -15,6 +15,7 @@ export interface DialogSelectProps<T> {
title: string title: string
placeholder?: string placeholder?: string
options: DialogSelectOption<T>[] options: DialogSelectOption<T>[]
flat?: boolean
ref?: (ref: DialogSelectRef<T>) => void ref?: (ref: DialogSelectRef<T>) => void
onMove?: (option: DialogSelectOption<T>) => void onMove?: (option: DialogSelectOption<T>) => void
onFilter?: (query: string) => void onFilter?: (query: string) => void
@@ -100,7 +101,10 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
setStore("input", "keyboard") setStore("input", "keyboard")
}) })
const grouped = createMemo(() => { const flatten = createMemo(() => props.flat && store.filter.length > 0)
const grouped = createMemo<[string, DialogSelectOption<T>[]][]>(() => {
if (flatten()) return [["", filtered()]]
const result = pipe( const result = pipe(
filtered(), filtered(),
groupBy((x) => x.category ?? ""), groupBy((x) => x.category ?? ""),
@@ -117,10 +121,16 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
) )
}) })
const rows = createMemo(() => {
const headers = grouped().reduce((acc, [category], i) => {
if (!category) return acc
return acc + (i > 0 ? 2 : 1)
}, 0)
return flat().length + headers
})
const dimensions = useTerminalDimensions() const dimensions = useTerminalDimensions()
const height = createMemo(() => const height = createMemo(() => Math.min(rows(), Math.floor(dimensions().height / 2) - 6))
Math.min(flat().length + grouped().length * 2 - 1, Math.floor(dimensions().height / 2) - 6),
)
const selected = createMemo(() => flat()[store.selected]) const selected = createMemo(() => flat()[store.selected])
@@ -311,7 +321,7 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
> >
<Option <Option
title={option.title} title={option.title}
footer={option.footer} footer={flatten() ? (option.category ?? option.footer) : option.footer}
description={option.description !== category ? option.description : undefined} description={option.description !== category ? option.description : undefined}
active={active()} active={active()}
current={current()} current={current()}