Просмотр исходного кода

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.
Dax Raad 3 месяцев назад
Родитель
Сommit
d4cb47eadc

+ 22 - 0
packages/desktop/src/context/local.tsx

@@ -162,10 +162,32 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
 
       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 {
         current: currentModel,
         recent,
         list,
+        cycle,
         set(model: ModelKey | undefined, options?: { recent?: boolean }) {
           batch(() => {
             setStore("model", agent.current().name, model ?? fallbackModel())

+ 18 - 0
packages/opencode/src/cli/cmd/tui/app.tsx

@@ -172,6 +172,24 @@ function App() {
         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",
       value: "agent.list",

+ 21 - 10
packages/opencode/src/cli/cmd/tui/context/local.tsx

@@ -147,15 +147,6 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
           setModelStore("ready", true)
         })
 
-      createEffect(() => {
-        Bun.write(
-          file,
-          JSON.stringify({
-            recent: modelStore.recent,
-          }),
-        )
-      })
-
       const fallbackModel = createMemo(() => {
         if (sync.data.config.model) {
           const [providerID, modelID] = sync.data.config.model.split("/")
@@ -206,6 +197,21 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
             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 }) {
           batch(() => {
             if (!isModelValid(model)) {
@@ -216,12 +222,17 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
               })
               return
             }
-
             setModelStore("model", agent.current().name, model)
             if (options?.recent) {
               const uniq = uniqueBy([model, ...modelStore.recent], (x) => x.providerID + x.modelID)
               if (uniq.length > 5) uniq.pop()
               setModelStore("recent", uniq)
+              Bun.write(
+                file,
+                JSON.stringify({
+                  recent: modelStore.recent,
+                }),
+              )
             }
           })
         },

+ 6 - 0
packages/opencode/src/config/config.ts

@@ -453,6 +453,12 @@ export namespace Config {
         .default("<leader>h")
         .describe("Toggle code block concealment in messages"),
       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"),
       agent_list: z.string().optional().default("<leader>a").describe("List agents"),
       agent_cycle: z.string().optional().default("tab").describe("Next agent"),

+ 8 - 0
packages/sdk/js/src/gen/types.gen.ts

@@ -114,6 +114,14 @@ export type KeybindsConfig = {
    * List available models
    */
   model_list?: string
+  /**
+   * Next recently used model
+   */
+  model_cycle_recent?: string
+  /**
+   * Previous recently used model
+   */
+  model_cycle_recent_reverse?: string
   /**
    * List available commands
    */