diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx index c30b8d12a933..00684e2dfd63 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx @@ -2,7 +2,7 @@ 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 } from "@tui/ui/dialog-select" +import { DialogSelect, type DialogSelectOption } from "@tui/ui/dialog-select" import { useDialog } from "@tui/ui/dialog" import { createDialogProviderOptions, DialogProvider } from "./dialog-provider" import { useKeybind } from "../context/keybind" @@ -21,6 +21,7 @@ export function DialogModel(props: { providerID?: string }) { const dialog = useDialog() const keybind = useKeybind() const [query, setQuery] = createSignal("") + const [selected, setSelected] = createSignal>() const connected = useConnected() const providers = createDialogProviderOptions() @@ -154,8 +155,17 @@ export function DialogModel(props: { providerID?: string }) { local.model.toggleFavorite(option.value as { providerID: string; modelID: string }) }, }, + { + keybind: keybind.all.model_recent_remove?.[0], + title: "Remove", + disabled: selected()?.category !== "Recent", + onTrigger: (option) => { + local.model.removeRecent(option.value as { providerID: string; modelID: string }) + }, + }, ]} onFilter={setQuery} + onMove={setSelected} flat={true} skipFilter={true} title={title()} diff --git a/packages/opencode/src/cli/cmd/tui/context/local.tsx b/packages/opencode/src/cli/cmd/tui/context/local.tsx index 72c72dc5bb3c..767c5d5f5ed2 100644 --- a/packages/opencode/src/cli/cmd/tui/context/local.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/local.tsx @@ -323,6 +323,27 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ save() }) }, + removeRecent(model: { providerID: string; modelID: string }) { + const current = currentModel() + const index = modelStore.recent.findIndex( + (x) => x.providerID === model.providerID && x.modelID === model.modelID, + ) + const next = modelStore.recent.filter( + (x) => x.providerID !== model.providerID || x.modelID !== model.modelID, + ) + setModelStore("recent", next) + + // If removed current model, switch to next valid recent model + const isCurrent = current?.providerID === model.providerID && current?.modelID === model.modelID + if (isCurrent && next.length > 0) { + const start = index >= 0 ? Math.min(index, next.length - 1) : 0 + const ordered = [...next.slice(start), ...next.slice(0, start)] + const candidate = ordered.find((item) => isModelValid(item)) + if (candidate) setModelStore("model", agent.current().name, { ...candidate }) + } + + save() + }, variant: { current() { const m = currentModel() diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 261731b8b0a4..4c37295ce2d9 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -780,6 +780,7 @@ export namespace Config { stash_delete: z.string().optional().default("ctrl+d").describe("Delete stash entry"), model_provider_list: z.string().optional().default("ctrl+a").describe("Open provider list from model dialog"), model_favorite_toggle: z.string().optional().default("ctrl+f").describe("Toggle model favorite status"), + model_recent_remove: z.string().optional().default("delete").describe("Remove model from recent list"), session_share: z.string().optional().default("none").describe("Share current session"), session_unshare: z.string().optional().default("none").describe("Unshare current session"), session_interrupt: z.string().optional().default("escape").describe("Interrupt current session"),