Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 16 additions & 9 deletions internal/tui/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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...)
}
Expand All @@ -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...)
Expand All @@ -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) {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand All @@ -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) {
Expand Down
13 changes: 7 additions & 6 deletions internal/tui/model_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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).
Expand All @@ -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.
Expand All @@ -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.
Expand Down