tui: display 'Free' badge for zero-cost models in model selection dialog

This commit is contained in:
Dax Raad
2025-11-08 16:20:13 -05:00
parent f8a1a0b26f
commit ce9b758d0a
2 changed files with 11 additions and 3 deletions

View File

@@ -4,12 +4,19 @@ import { useSync } from "@tui/context/sync"
import { map, pipe, flatMap, entries, filter, isDeepEqual, sortBy } from "remeda" import { map, pipe, flatMap, entries, filter, isDeepEqual, sortBy } from "remeda"
import { DialogSelect, type DialogSelectRef } from "@tui/ui/dialog-select" import { DialogSelect, type DialogSelectRef } from "@tui/ui/dialog-select"
import { useDialog } from "@tui/ui/dialog" import { useDialog } from "@tui/ui/dialog"
import { useTheme } from "../context/theme"
function Free() {
const { theme } = useTheme()
return <span style={{ fg: theme.secondary }}>Free</span>
}
export function DialogModel() { export function DialogModel() {
const local = useLocal() const local = useLocal()
const sync = useSync() const sync = useSync()
const dialog = useDialog() const dialog = useDialog()
const [ref, setRef] = createSignal<DialogSelectRef<unknown>>() const [ref, setRef] = createSignal<DialogSelectRef<unknown>>()
const { theme } = useTheme()
const options = createMemo(() => { const options = createMemo(() => {
return [ return [
@@ -29,6 +36,7 @@ export function DialogModel() {
title: model.name ?? item.modelID, title: model.name ?? item.modelID,
description: provider.name, description: provider.name,
category: "Recent", category: "Recent",
footer: model.cost.input === 0 && provider.id === "opencode" ? <Free /> : undefined,
}, },
] ]
}) })
@@ -51,6 +59,7 @@ export function DialogModel() {
title: info.name ?? model, title: info.name ?? model,
description: provider.name, description: provider.name,
category: provider.name, category: provider.name,
footer: info.cost.input === 0 && provider.id === "opencode" ? <Free /> : undefined,
})), })),
filter((x) => Boolean(ref()?.filter) || !local.model.recent().find((y) => isDeepEqual(y, x.value))), filter((x) => Boolean(ref()?.filter) || !local.model.recent().find((y) => isDeepEqual(y, x.value))),
), ),

View File

@@ -30,7 +30,7 @@ export interface DialogSelectOption<T = any> {
title: string title: string
value: T value: T
description?: string description?: string
footer?: string footer?: JSX.Element | string
category?: string category?: string
disabled?: boolean disabled?: boolean
bg?: RGBA bg?: RGBA
@@ -172,7 +172,6 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
props.onFilter?.(e) props.onFilter?.(e)
}) })
}} }}
onKeyDown={(e) => {}}
focusedBackgroundColor={theme.backgroundPanel} focusedBackgroundColor={theme.backgroundPanel}
cursorColor={theme.primary} cursorColor={theme.primary}
focusedTextColor={theme.textMuted} focusedTextColor={theme.textMuted}
@@ -256,7 +255,7 @@ function Option(props: {
description?: string description?: string
active?: boolean active?: boolean
current?: boolean current?: boolean
footer?: string footer?: JSX.Element | string
onMouseOver?: () => void onMouseOver?: () => void
}) { }) {
const { theme } = useTheme() const { theme } = useTheme()