summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorspoons-and-mirrors <[email protected]>2025-08-11 14:00:32 +0200
committerGitHub <[email protected]>2025-08-11 07:00:32 -0500
commit73a8356b10d537f7d2b31a9fb777192a8522ab54 (patch)
tree3205439f152f1c4d23b9703dadca338a30d85725
parent8db75266d0da48bfd1257d528373549dc0814584 (diff)
downloadopencode-73a8356b10d537f7d2b31a9fb777192a8522ab54.tar.gz
opencode-73a8356b10d537f7d2b31a9fb777192a8522ab54.zip
Feat: Add F2 Keybind to Cycle Through the 5 Most Recent Models (#1778)
-rw-r--r--packages/tui/internal/app/app.go33
-rw-r--r--packages/tui/internal/commands/command.go6
-rw-r--r--packages/tui/internal/tui/tui.go5
3 files changed, 44 insertions, 0 deletions
diff --git a/packages/tui/internal/app/app.go b/packages/tui/internal/app/app.go
index 517cafd23..48f4684fe 100644
--- a/packages/tui/internal/app/app.go
+++ b/packages/tui/internal/app/app.go
@@ -280,6 +280,39 @@ func (a *App) SwitchAgentReverse() (*App, tea.Cmd) {
return a.cycleMode(false)
}
+func (a *App) CycleRecentModel() (*App, tea.Cmd) {
+ recentModels := a.State.RecentlyUsedModels
+ if len(recentModels) > 5 {
+ recentModels = recentModels[:5]
+ }
+ if len(recentModels) < 2 {
+ return a, toast.NewInfoToast("Need at least 2 recent models to cycle")
+ }
+ nextIndex := 0
+ for i, recentModel := range recentModels {
+ if a.Provider != nil && a.Model != nil && recentModel.ProviderID == a.Provider.ID && recentModel.ModelID == a.Model.ID {
+ nextIndex = (i + 1) % len(recentModels)
+ break
+ }
+ }
+ for range recentModels {
+ currentRecentModel := recentModels[nextIndex%len(recentModels)]
+ provider, model := findModelByProviderAndModelID(a.Providers, currentRecentModel.ProviderID, currentRecentModel.ModelID)
+ if provider != nil && model != nil {
+ a.Provider, a.Model = provider, model
+ a.State.AgentModel[a.Agent().Name] = AgentModel{ProviderID: provider.ID, ModelID: model.ID}
+ return a, tea.Sequence(a.SaveState(), toast.NewSuccessToast(fmt.Sprintf("Switched to %s (%s)", model.Name, provider.Name)))
+ }
+ recentModels = append(recentModels[:nextIndex%len(recentModels)], recentModels[nextIndex%len(recentModels)+1:]...)
+ if len(recentModels) < 2 {
+ a.State.RecentlyUsedModels = recentModels
+ return a, tea.Sequence(a.SaveState(), toast.NewInfoToast("Not enough valid recent models to cycle"))
+ }
+ }
+ a.State.RecentlyUsedModels = recentModels
+ return a, toast.NewErrorToast("Recent model not found")
+}
+
// findModelByFullID finds a model by its full ID in the format "provider/model"
func findModelByFullID(
providers []opencode.Provider,
diff --git a/packages/tui/internal/commands/command.go b/packages/tui/internal/commands/command.go
index 55f118aaa..516caab79 100644
--- a/packages/tui/internal/commands/command.go
+++ b/packages/tui/internal/commands/command.go
@@ -121,6 +121,7 @@ const (
ToolDetailsCommand CommandName = "tool_details"
ModelListCommand CommandName = "model_list"
AgentListCommand CommandName = "agent_list"
+ ModelCycleRecentCommand CommandName = "model_cycle_recent"
ThemeListCommand CommandName = "theme_list"
FileListCommand CommandName = "file_list"
FileCloseCommand CommandName = "file_close"
@@ -257,6 +258,11 @@ func LoadFromConfig(config *opencode.Config) CommandRegistry {
Trigger: []string{"agents"},
},
{
+ Name: ModelCycleRecentCommand,
+ Description: "cycle recent models",
+ Keybindings: parseBindings("f2"),
+ },
+ {
Name: ThemeListCommand,
Description: "list themes",
Keybindings: parseBindings("<leader>t"),
diff --git a/packages/tui/internal/tui/tui.go b/packages/tui/internal/tui/tui.go
index ea9f0560b..499d67c65 100644
--- a/packages/tui/internal/tui/tui.go
+++ b/packages/tui/internal/tui/tui.go
@@ -1148,6 +1148,11 @@ func (a Model) executeCommand(command commands.Command) (tea.Model, tea.Cmd) {
case commands.AgentListCommand:
agentDialog := dialog.NewAgentDialog(a.app)
a.modal = agentDialog
+ case commands.ModelCycleRecentCommand:
+ slog.Debug("ModelCycleRecentCommand triggered")
+ updated, cmd := a.app.CycleRecentModel()
+ a.app = updated
+ cmds = append(cmds, cmd)
case commands.ThemeListCommand:
themeDialog := dialog.NewThemeDialog()
a.modal = themeDialog