mirror of
https://github.com/aljazceru/opencode.git
synced 2025-12-21 01:34:22 +01:00
tui: add keyboard shortcuts to cycle through recently used models
Users can now press F2 to cycle forward and Shift+F2 to cycle backward through their recently used models, making it faster to switch between commonly used AI models without opening the model selection dialog.
This commit is contained in:
@@ -162,10 +162,32 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
|||||||
|
|
||||||
const recent = createMemo(() => store.recent.map(find).filter(Boolean))
|
const recent = createMemo(() => store.recent.map(find).filter(Boolean))
|
||||||
|
|
||||||
|
const cycle = (direction: 1 | -1) => {
|
||||||
|
const recentList = recent()
|
||||||
|
const current = currentModel()
|
||||||
|
if (!current) return
|
||||||
|
|
||||||
|
const index = recentList.findIndex((x) => x?.provider.id === current.provider.id && x?.id === current.id)
|
||||||
|
if (index === -1) return
|
||||||
|
|
||||||
|
let next = index + direction
|
||||||
|
if (next < 0) next = recentList.length - 1
|
||||||
|
if (next >= recentList.length) next = 0
|
||||||
|
|
||||||
|
const val = recentList[next]
|
||||||
|
if (!val) return
|
||||||
|
|
||||||
|
model.set({
|
||||||
|
providerID: val.provider.id,
|
||||||
|
modelID: val.id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
current: currentModel,
|
current: currentModel,
|
||||||
recent,
|
recent,
|
||||||
list,
|
list,
|
||||||
|
cycle,
|
||||||
set(model: ModelKey | undefined, options?: { recent?: boolean }) {
|
set(model: ModelKey | undefined, options?: { recent?: boolean }) {
|
||||||
batch(() => {
|
batch(() => {
|
||||||
setStore("model", agent.current().name, model ?? fallbackModel())
|
setStore("model", agent.current().name, model ?? fallbackModel())
|
||||||
|
|||||||
@@ -172,6 +172,24 @@ function App() {
|
|||||||
dialog.replace(() => <DialogModel />)
|
dialog.replace(() => <DialogModel />)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "Model cycle",
|
||||||
|
value: "model.cycle_recent",
|
||||||
|
keybind: "model_cycle_recent",
|
||||||
|
category: "Agent",
|
||||||
|
onSelect: () => {
|
||||||
|
local.model.cycle(1)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Model cycle reverse",
|
||||||
|
value: "model.cycle_recent_reverse",
|
||||||
|
keybind: "model_cycle_recent_reverse",
|
||||||
|
category: "Agent",
|
||||||
|
onSelect: () => {
|
||||||
|
local.model.cycle(-1)
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: "Switch agent",
|
title: "Switch agent",
|
||||||
value: "agent.list",
|
value: "agent.list",
|
||||||
|
|||||||
@@ -147,15 +147,6 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
|||||||
setModelStore("ready", true)
|
setModelStore("ready", true)
|
||||||
})
|
})
|
||||||
|
|
||||||
createEffect(() => {
|
|
||||||
Bun.write(
|
|
||||||
file,
|
|
||||||
JSON.stringify({
|
|
||||||
recent: modelStore.recent,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
const fallbackModel = createMemo(() => {
|
const fallbackModel = createMemo(() => {
|
||||||
if (sync.data.config.model) {
|
if (sync.data.config.model) {
|
||||||
const [providerID, modelID] = sync.data.config.model.split("/")
|
const [providerID, modelID] = sync.data.config.model.split("/")
|
||||||
@@ -206,6 +197,21 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
|||||||
model: model.name ?? value.modelID,
|
model: model.name ?? value.modelID,
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
cycle(direction: 1 | -1) {
|
||||||
|
const current = currentModel()
|
||||||
|
if (!current) return
|
||||||
|
const recent = modelStore.recent
|
||||||
|
const index = recent.findIndex(
|
||||||
|
(x) => x.providerID === current.providerID && x.modelID === current.modelID,
|
||||||
|
)
|
||||||
|
if (index === -1) return
|
||||||
|
let next = index + direction
|
||||||
|
if (next < 0) next = recent.length - 1
|
||||||
|
if (next >= recent.length) next = 0
|
||||||
|
const val = recent[next]
|
||||||
|
if (!val) return
|
||||||
|
setModelStore("model", agent.current().name, { ...val })
|
||||||
|
},
|
||||||
set(model: { providerID: string; modelID: string }, options?: { recent?: boolean }) {
|
set(model: { providerID: string; modelID: string }, options?: { recent?: boolean }) {
|
||||||
batch(() => {
|
batch(() => {
|
||||||
if (!isModelValid(model)) {
|
if (!isModelValid(model)) {
|
||||||
@@ -216,12 +222,17 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
setModelStore("model", agent.current().name, model)
|
setModelStore("model", agent.current().name, model)
|
||||||
if (options?.recent) {
|
if (options?.recent) {
|
||||||
const uniq = uniqueBy([model, ...modelStore.recent], (x) => x.providerID + x.modelID)
|
const uniq = uniqueBy([model, ...modelStore.recent], (x) => x.providerID + x.modelID)
|
||||||
if (uniq.length > 5) uniq.pop()
|
if (uniq.length > 5) uniq.pop()
|
||||||
setModelStore("recent", uniq)
|
setModelStore("recent", uniq)
|
||||||
|
Bun.write(
|
||||||
|
file,
|
||||||
|
JSON.stringify({
|
||||||
|
recent: modelStore.recent,
|
||||||
|
}),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -453,6 +453,12 @@ export namespace Config {
|
|||||||
.default("<leader>h")
|
.default("<leader>h")
|
||||||
.describe("Toggle code block concealment in messages"),
|
.describe("Toggle code block concealment in messages"),
|
||||||
model_list: z.string().optional().default("<leader>m").describe("List available models"),
|
model_list: z.string().optional().default("<leader>m").describe("List available models"),
|
||||||
|
model_cycle_recent: z.string().optional().default("f2").describe("Next recently used model"),
|
||||||
|
model_cycle_recent_reverse: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.default("shift+f2")
|
||||||
|
.describe("Previous recently used model"),
|
||||||
command_list: z.string().optional().default("ctrl+p").describe("List available commands"),
|
command_list: z.string().optional().default("ctrl+p").describe("List available commands"),
|
||||||
agent_list: z.string().optional().default("<leader>a").describe("List agents"),
|
agent_list: z.string().optional().default("<leader>a").describe("List agents"),
|
||||||
agent_cycle: z.string().optional().default("tab").describe("Next agent"),
|
agent_cycle: z.string().optional().default("tab").describe("Next agent"),
|
||||||
|
|||||||
@@ -114,6 +114,14 @@ export type KeybindsConfig = {
|
|||||||
* List available models
|
* List available models
|
||||||
*/
|
*/
|
||||||
model_list?: string
|
model_list?: string
|
||||||
|
/**
|
||||||
|
* Next recently used model
|
||||||
|
*/
|
||||||
|
model_cycle_recent?: string
|
||||||
|
/**
|
||||||
|
* Previous recently used model
|
||||||
|
*/
|
||||||
|
model_cycle_recent_reverse?: string
|
||||||
/**
|
/**
|
||||||
* List available commands
|
* List available commands
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user