From 15b989cfc7bc9eb4ecdd4eaf80def2b69f798388 Mon Sep 17 00:00:00 2001 From: Christian Rocha Date: Tue, 26 Aug 2025 16:41:36 -0400 Subject: [PATCH] fix(tui): further guard against type accessor and map panics (#783) --- internal/tui/tui.go | 82 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 65 insertions(+), 17 deletions(-) diff --git a/internal/tui/tui.go b/internal/tui/tui.go index 2f54e179b43bf288a1e7345ade3f5e68cfbfafe3..1c987722b58b883862b71c582ed3800afc946de7 100644 --- a/internal/tui/tui.go +++ b/internal/tui/tui.go @@ -76,8 +76,13 @@ type appModel struct { // Init initializes the application model and returns initial commands. func (a appModel) Init() tea.Cmd { + item, ok := a.pages[a.currentPage] + if !ok { + return nil + } + var cmds []tea.Cmd - cmd := a.pages[a.currentPage].Init() + cmd := item.Init() cmds = append(cmds, cmd) a.loadedPages[a.currentPage] = true @@ -99,7 +104,10 @@ func (a *appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case tea.KeyboardEnhancementsMsg: for id, page := range a.pages { m, pageCmd := page.Update(msg) - a.pages[id] = m.(util.Model) + if model, ok := m.(util.Model); ok { + a.pages[id] = model + } + if pageCmd != nil { cmds = append(cmds, pageCmd) } @@ -114,7 +122,10 @@ func (a *appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case completions.OpenCompletionsMsg, completions.FilterCompletionsMsg, completions.CloseCompletionsMsg, completions.RepositionCompletionsMsg: u, completionCmd := a.completions.Update(msg) - a.completions = u.(completions.Completions) + if model, ok := u.(completions.Completions); ok { + a.completions = model + } + return a, completionCmd // Dialog messages @@ -214,9 +225,12 @@ func (a *appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return a, nil } - // forward to page + // Forward to view. updated, itemCmd := item.Update(msg) - a.pages[a.currentPage] = updated.(util.Model) + if model, ok := updated.(util.Model); ok { + a.pages[a.currentPage] = model + } + return a, itemCmd case pubsub.Event[permission.PermissionRequest]: return a, util.CmdHandler(dialogs.OpenDialogMsg{ @@ -241,7 +255,10 @@ func (a *appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // Forward agent events to dialogs if a.dialog.HasDialogs() && a.dialog.ActiveDialogID() == compact.CompactDialogID { u, dialogCmd := a.dialog.Update(payload) - a.dialog = u.(dialogs.DialogCmp) + if model, ok := u.(dialogs.DialogCmp); ok { + a.dialog = model + } + cmds = append(cmds, dialogCmd) } @@ -270,7 +287,10 @@ func (a *appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { a.isConfigured = config.HasInitialDataConfig() updated, pageCmd := item.Update(msg) - a.pages[a.currentPage] = updated.(util.Model) + if model, ok := updated.(util.Model); ok { + a.pages[a.currentPage] = model + } + cmds = append(cmds, pageCmd) return a, tea.Batch(cmds...) @@ -289,14 +309,20 @@ func (a *appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } updated, pageCmd := item.Update(msg) - a.pages[a.currentPage] = updated.(util.Model) + if model, ok := updated.(util.Model); ok { + a.pages[a.currentPage] = model + } + cmds = append(cmds, pageCmd) } return a, tea.Batch(cmds...) case tea.PasteMsg: if a.dialog.HasDialogs() { u, dialogCmd := a.dialog.Update(msg) - a.dialog = u.(dialogs.DialogCmp) + if model, ok := u.(dialogs.DialogCmp); ok { + a.dialog = model + } + cmds = append(cmds, dialogCmd) } else { item, ok := a.pages[a.currentPage] @@ -305,7 +331,10 @@ func (a *appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } updated, pageCmd := item.Update(msg) - a.pages[a.currentPage] = updated.(util.Model) + if model, ok := updated.(util.Model); ok { + a.pages[a.currentPage] = model + } + cmds = append(cmds, pageCmd) } return a, tea.Batch(cmds...) @@ -317,12 +346,18 @@ func (a *appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if !ok { return a, nil } + updated, cmd := item.Update(msg) - a.pages[a.currentPage] = updated.(util.Model) + if model, ok := updated.(util.Model); ok { + a.pages[a.currentPage] = model + } if a.dialog.HasDialogs() { u, dialogCmd := a.dialog.Update(msg) - a.dialog = u.(dialogs.DialogCmp) + if model, ok := u.(dialogs.DialogCmp); ok { + a.dialog = model + } + cmds = append(cmds, dialogCmd) } cmds = append(cmds, cmd) @@ -332,27 +367,38 @@ func (a *appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // handleWindowResize processes window resize events and updates all components. func (a *appModel) handleWindowResize(width, height int) tea.Cmd { var cmds []tea.Cmd + + // TODO: clean up these magic numbers. if a.showingFullHelp { height -= 5 } else { height -= 2 } + a.width, a.height = width, height // Update status bar s, cmd := a.status.Update(tea.WindowSizeMsg{Width: width, Height: height}) - a.status = s.(status.StatusCmp) + if model, ok := s.(status.StatusCmp); ok { + a.status = model + } cmds = append(cmds, cmd) - // Update the current page + // Update the current view. for p, page := range a.pages { updated, pageCmd := page.Update(tea.WindowSizeMsg{Width: width, Height: height}) - a.pages[p] = updated.(util.Model) + if model, ok := updated.(util.Model); ok { + a.pages[p] = model + } + cmds = append(cmds, pageCmd) } // Update the dialogs dialog, cmd := a.dialog.Update(tea.WindowSizeMsg{Width: width, Height: height}) - a.dialog = dialog.(dialogs.DialogCmp) + if model, ok := dialog.(dialogs.DialogCmp); ok { + a.dialog = model + } + cmds = append(cmds, cmd) return tea.Batch(cmds...) @@ -443,7 +489,9 @@ func (a *appModel) handleKeyPressMsg(msg tea.KeyPressMsg) tea.Cmd { } updated, cmd := item.Update(msg) - a.pages[a.currentPage] = updated.(util.Model) + if model, ok := updated.(util.Model); ok { + a.pages[a.currentPage] = model + } return cmd } }