From eed0b49f8ceee2d23afd1dd3db0ff6ac295cb571 Mon Sep 17 00:00:00 2001 From: Ayman Bagabas Date: Thu, 18 Dec 2025 16:55:17 -0500 Subject: [PATCH] refactor(ui): dialog: unify session and model switching dialogs --- internal/ui/dialog/commands.go | 4 +-- internal/ui/dialog/messages.go | 7 ++-- internal/ui/model/ui.go | 60 ++++++++++++++++------------------ 3 files changed, 36 insertions(+), 35 deletions(-) diff --git a/internal/ui/dialog/commands.go b/internal/ui/dialog/commands.go index a2c0948f26b75ab7b9d597ca5da867b76c936867..f97ea7374209b26e3702722079660af0619ba229 100644 --- a/internal/ui/dialog/commands.go +++ b/internal/ui/dialog/commands.go @@ -324,7 +324,7 @@ func (c *Commands) defaultCommands() []uicmd.Command { Description: "Switch to a different session", Shortcut: "ctrl+s", Handler: func(cmd uicmd.Command) tea.Cmd { - return uiutil.CmdHandler(SwitchSessionsMsg{}) + return uiutil.CmdHandler(OpenDialogMsg{SessionsID}) }, }, { @@ -333,7 +333,7 @@ func (c *Commands) defaultCommands() []uicmd.Command { Description: "Switch to a different model", Shortcut: "ctrl+l", Handler: func(cmd uicmd.Command) tea.Cmd { - return uiutil.CmdHandler(SwitchModelMsg{}) + return uiutil.CmdHandler(OpenDialogMsg{ModelsID}) }, }, } diff --git a/internal/ui/dialog/messages.go b/internal/ui/dialog/messages.go index ca44d1118a57bd1506d5f35bb47d5f56bbe9fa6f..2d69e4c9b841fcc9d8776e8e6fb5cf04e3d1d0f0 100644 --- a/internal/ui/dialog/messages.go +++ b/internal/ui/dialog/messages.go @@ -12,6 +12,11 @@ type CloseMsg struct{} // QuitMsg is a message to quit the application. type QuitMsg = tea.QuitMsg +// OpenDialogMsg is a message to open a dialog. +type OpenDialogMsg struct { + DialogID string +} + // SessionSelectedMsg is a message indicating a session has been selected. type SessionSelectedMsg struct { Session session.Session @@ -25,9 +30,7 @@ type ModelSelectedMsg struct { // Messages for commands type ( - SwitchSessionsMsg struct{} NewSessionsMsg struct{} - SwitchModelMsg struct{} OpenFilePickerMsg struct{} ToggleHelpMsg struct{} ToggleCompactModeMsg struct{} diff --git a/internal/ui/model/ui.go b/internal/ui/model/ui.go index a8f80fe1f03f947ee11d05853f62e69927897afa..ea656d5c8cfe42a97c58fa163b1689b11ac228af 100644 --- a/internal/ui/model/ui.go +++ b/internal/ui/model/ui.go @@ -64,11 +64,6 @@ type openEditorMsg struct { Text string } -// listSessionsMsg is a message to list available sessions. -type listSessionsMsg struct { - sessions []session.Session -} - // UI represents the main user interface model. type UI struct { com *common.Common @@ -190,10 +185,6 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if !m.sendProgressBar { m.sendProgressBar = slices.Contains(msg, "WT_SESSION") } - case listSessionsMsg: - if cmd := m.openSessionsDialog(msg.sessions); cmd != nil { - cmds = append(cmds, cmd) - } case loadSessionMsg: m.state = uiChat m.session = msg.session @@ -499,11 +490,8 @@ func (m *UI) handleKeyPressMsg(msg tea.KeyPressMsg) tea.Cmd { } return true case key.Matches(msg, m.keyMap.Sessions): - if m.dialog.ContainsDialog(dialog.SessionsID) { - // Bring to front - m.dialog.BringToFront(dialog.SessionsID) - } else { - cmds = append(cmds, m.listSessions) + if cmd := m.openSessionsDialog(); cmd != nil { + cmds = append(cmds, cmd) } return true } @@ -536,15 +524,30 @@ func (m *UI) handleKeyPressMsg(msg tea.KeyPressMsg) tea.Cmd { m.dialog.CloseDialog(dialog.SessionsID) cmds = append(cmds, m.loadSession(msg.Session.ID)) + // Open dialog message + case dialog.OpenDialogMsg: + switch msg.DialogID { + case dialog.SessionsID: + if cmd := m.openSessionsDialog(); cmd != nil { + cmds = append(cmds, cmd) + } + case dialog.ModelsID: + if cmd := m.openModelsDialog(); cmd != nil { + cmds = append(cmds, cmd) + } + default: + // Unknown dialog + break + } + + m.dialog.CloseDialog(msg.DialogID) + // Command dialog messages case dialog.ToggleYoloModeMsg: yolo := !m.com.App.Permissions.SkipRequests() m.com.App.Permissions.SetSkipRequests(yolo) m.setEditorPrompt(yolo) m.dialog.CloseDialog(dialog.CommandsID) - case dialog.SwitchSessionsMsg: - cmds = append(cmds, m.listSessions) - m.dialog.CloseDialog(dialog.CommandsID) case dialog.NewSessionsMsg: if m.com.App.AgentCoordinator != nil && m.com.App.AgentCoordinator.IsBusy() { cmds = append(cmds, uiutil.ReportWarn("Agent is busy, please wait before starting a new session...")) @@ -562,11 +565,6 @@ func (m *UI) handleKeyPressMsg(msg tea.KeyPressMsg) tea.Cmd { m.dialog.CloseDialog(dialog.CommandsID) case dialog.QuitMsg: cmds = append(cmds, tea.Quit) - case dialog.SwitchModelMsg: - m.dialog.CloseDialog(dialog.CommandsID) - if cmd := m.openModelsDialog(); cmd != nil { - cmds = append(cmds, cmd) - } case dialog.ModelSelectedMsg: // TODO: Handle model switching } @@ -1428,14 +1426,21 @@ func (m *UI) openCommandsDialog() tea.Cmd { return nil } -// openSessionsDialog opens the sessions dialog with the given sessions. -func (m *UI) openSessionsDialog(sessions []session.Session) tea.Cmd { +// openSessionsDialog opens the sessions dialog. If the dialog is already open, +// it brings it to the front. Otherwise, it will list all the sessions and open +// the dialog. +func (m *UI) openSessionsDialog() tea.Cmd { if m.dialog.ContainsDialog(dialog.SessionsID) { // Bring to front m.dialog.BringToFront(dialog.SessionsID) return nil } + sessions, err := m.com.App.Sessions.List(context.TODO()) + if err != nil { + return uiutil.ReportError(err) + } + dialog := dialog.NewSessions(m.com, sessions...) // TODO: Get. Rid. Of. Magic numbers! dialog.SetSize(min(120, m.width-8), 30) @@ -1444,13 +1449,6 @@ func (m *UI) openSessionsDialog(sessions []session.Session) tea.Cmd { return nil } -// listSessions is a [tea.Cmd] that lists all sessions and returns them in a -// [listSessionsMsg]. -func (m *UI) listSessions() tea.Msg { - allSessions, _ := m.com.App.Sessions.List(context.TODO()) - return listSessionsMsg{sessions: allSessions} -} - // newSession clears the current session state and prepares for a new session. // The actual session creation happens when the user sends their first message. func (m *UI) newSession() {