diff --git a/internal/tui/handlers.go b/internal/tui/handlers.go index 23c979d..e6a0659 100644 --- a/internal/tui/handlers.go +++ b/internal/tui/handlers.go @@ -311,8 +311,7 @@ func (m *Model) handleMergedPRStatus(msg mergedPRStatusMsg) (Model, tea.Cmd) { } // If all visible merged PRs are already reviewed and nothing is scoring, // we may need to transition out of startup. - m.tryStartupTransition() - return *m, nil + return *m, m.tryStartupTransition() } // markPostMergeReacted marks a post-merge card as reacted and navigates away if hidden. @@ -357,10 +356,11 @@ func (m *Model) handlePRDetails(msg prDetailsFetchedMsg) (Model, tea.Cmd) { ctx := msg.ctx // Dedup: another list handler may have already created this card. if m.findCard(ctx.Repo, pr.Number) != nil { + var clearCmd tea.Cmd if m.fetching == 0 { - m.tryStartupTransition() + clearCmd = m.tryStartupTransition() } - return *m, nil + return *m, clearCmd } if !m.startupDone { fetched := m.total - m.fetching @@ -388,7 +388,9 @@ func (m *Model) handlePRDetails(msg prDetailsFetchedMsg) (Model, tea.Cmd) { cmds := []tea.Cmd{parseDiffCmd(ctx.Repo, pr), fetchMergedPRStatusCmd(ctx.Repo, pr.Number, m.app.CurrentUser)} cmds = append(cmds, m.fetchBodyImages(pr, ctx.Repo)...) if m.fetching == 0 { - m.tryStartupTransition() + if cmd := m.tryStartupTransition(); cmd != nil { + cmds = append(cmds, cmd) + } } return *m, tea.Batch(cmds...) } @@ -404,7 +406,9 @@ func (m *Model) handlePRDetails(msg prDetailsFetchedMsg) (Model, tea.Cmd) { // When all details are fetched, check if we can transition — earlier // cached scores may have been blocked waiting for fetching to finish. if m.fetching == 0 { - m.tryStartupTransition() + if cmd := m.tryStartupTransition(); cmd != nil { + cmds = append(cmds, cmd) + } } return *m, tea.Batch(cmds...) @@ -421,10 +425,11 @@ func (m *Model) handleDiffParsed(msg prDiffParsedMsg) (Model, tea.Cmd) { if card := m.currentCard(); card != nil && card.Ctx.Repo == msg.repo && card.PR.Number == msg.prNumber { m.loadCurrentDiff() } + var clearCmd tea.Cmd if !m.startupDone && m.parsing == 0 { - m.tryStartupTransition() + clearCmd = m.tryStartupTransition() } - return *m, nil + return *m, clearCmd } func (m *Model) handleScoringToolCall(msg scoringToolCallMsg) (Model, tea.Cmd) { @@ -477,6 +482,7 @@ func (m *Model) handlePRScored(msg prScoredMsg) (Model, tea.Cmd) { // Transition from startup screen once the card list is stable (all details // fetched) and at least one visible PR is scored. Waiting for fetching==0 // ensures no more card inserts will shift m.current and cause flashing. + var clearCmd tea.Cmd if !m.startupDone && scoredCard != nil && m.fetching == 0 && m.parsing == 0 { // Count truly visible cards: post-merge cards must have their status checked. settled := true @@ -506,6 +512,7 @@ func (m *Model) handlePRScored(msg prScoredMsg) (Model, tea.Cmd) { m.logDone() m.startupDone = true m.resizeLayout() + clearCmd = tea.ClearScreen if visibleSettled == 0 { logger.Info("startup: no visible cards, entering bulk approve") m.tryEnterBulkApprove() @@ -525,7 +532,7 @@ func (m *Model) handlePRScored(msg prScoredMsg) (Model, tea.Cmd) { m.tryPreWarm(card) } m.refreshBulkApproveIfActive() - return *m, nil + return *m, clearCmd } func (m *Model) handlePRRefreshed(msg prRefreshedMsg) (Model, tea.Cmd) { diff --git a/internal/tui/model_helpers.go b/internal/tui/model_helpers.go index dbc83ee..a378345 100644 --- a/internal/tui/model_helpers.go +++ b/internal/tui/model_helpers.go @@ -41,10 +41,10 @@ func (m Model) currentCard() *PRCard { } // tryStartupTransition checks if we can exit the startup screen. -// Called when a merged PR status arrives and no scoring was triggered. -func (m *Model) tryStartupTransition() { +// Returns tea.ClearScreen when transitioning to flush residual startup content. +func (m *Model) tryStartupTransition() tea.Cmd { if m.startupDone || m.fetching > 0 || m.parsing > 0 { - return + return nil } // Check if any visible card exists (scored or not — unscored merged PRs are still viewable). hasVisible := false @@ -60,7 +60,7 @@ func (m *Model) tryStartupTransition() { // trigger the bulk/fireworks scene before slower repos' cards arrive. nRepos := len(m.app.Repos) if m.openListsDone < nRepos || m.mergedListsDone < nRepos || m.trackedListsDone < nRepos { - return + return nil } if len(m.cards) > 0 { // Cards exist but all are reviewed — show bulk approve (fireworks). @@ -69,10 +69,10 @@ func (m *Model) tryStartupTransition() { m.startupDone = true m.resizeLayout() m.tryEnterBulkApprove() - return + return tea.ClearScreen } m.checkNoPRs() - return + return nil } // If there's at least one visible card and nothing is being fetched, transition. // Scoring may still be in progress but the user can start browsing. @@ -82,6 +82,7 @@ func (m *Model) tryStartupTransition() { m.skipToVisibleCard() m.loadCurrentDiff() m.resizeLayout() + return tea.ClearScreen } // checkNoPRs sets noPRs=true only when all repo lists have returned and there are no cards or fetches pending.