diff --git a/src/Client/App.fs b/src/Client/App.fs index 34fa676..9bccd8a 100644 --- a/src/Client/App.fs +++ b/src/Client/App.fs @@ -601,7 +601,7 @@ let beadsProgressBar (b: BeadsSummary) = ] ] -let mainBehindIndicator (count: int) = +let mainBehindIndicator (baseBranch: string) (count: int) = if count = 0 then Html.span [ prop.className "main-behind up-to-date" @@ -610,7 +610,7 @@ let mainBehindIndicator (count: int) = else Html.span [ prop.className (if count > 20 then "main-behind behind-warning" else "main-behind") - prop.text ($"{count} behind main") + prop.text ($"{count} behind {baseBranch}") ] let isBranchSyncing (events: CardEvent list) = @@ -622,7 +622,7 @@ let private providerDisplayName (provider: CodingToolProvider option) = | Some Copilot -> "Copilot" | None -> "Coding tool" -let syncButton dispatch (wt: WorktreeStatus) (branchEvents: CardEvent list) (isPending: bool) (scopedKey: string) = +let syncButton dispatch (baseBranch: string) (wt: WorktreeStatus) (branchEvents: CardEvent list) (isPending: bool) (scopedKey: string) = if isPending then Html.button [ prop.className "sync-starting-btn" @@ -647,22 +647,22 @@ let syncButton dispatch (wt: WorktreeStatus) (branchEvents: CardEvent list) (isP prop.disabled disabled yield! noFocusProps prop.onClick (fun e -> e.stopPropagation(); dispatch (StartSync (wt.Path, scopedKey))) - prop.title (if codingToolBusy then $"{providerDisplayName wt.CodingToolProvider} is active" else "Sync with main (S)") + prop.title (if codingToolBusy then $"{providerDisplayName wt.CodingToolProvider} is active" else $"Sync with {baseBranch} (S)") prop.text "Sync" ] -let mainBehindWithSync dispatch (wt: WorktreeStatus) (branchEvents: CardEvent list) (isPending: bool) (scopedKey: string) = +let mainBehindWithSync dispatch (baseBranch: string) (wt: WorktreeStatus) (branchEvents: CardEvent list) (isPending: bool) (scopedKey: string) = Html.div [ prop.className "main-behind-row" prop.children [ - mainBehindIndicator wt.MainBehindCount + mainBehindIndicator baseBranch wt.MainBehindCount if wt.MainBehindCount > 0 then if wt.IsDirty then Html.span [ prop.className "dirty-warning" prop.text "uncommitted changes" ] - else syncButton dispatch wt branchEvents isPending scopedKey + else syncButton dispatch baseBranch wt branchEvents isPending scopedKey Html.span [ prop.className "git-commit-msg" prop.children [ @@ -1110,7 +1110,7 @@ let workMetricsView = Components.workMetricsView let workMetricsItems = Components.workMetricsItems let FitOrHide = Components.FitOrHide -let compactWorktreeCard dispatch editorName (repoName: string) (cooldowns: Set) (scopedKey: string) (isFocused: bool) (wt: WorktreeStatus) = +let compactWorktreeCard dispatch editorName (repoName: string) (baseBranch: string) (cooldowns: Set) (scopedKey: string) (isFocused: bool) (wt: WorktreeStatus) = let baseClass = cardClassName wt + " compact" let className = if isFocused then baseClass + " focused" else baseClass Html.div [ @@ -1142,14 +1142,14 @@ let compactWorktreeCard dispatch editorName (repoName: string) (cooldowns: Set 0 then beadsCounts "beads-inline" wt.Beads - mainBehindIndicator wt.MainBehindCount + mainBehindIndicator baseBranch wt.MainBehindCount prSection dispatch cooldowns wt repoName ] ] ] ] -let worktreeCard dispatch editorName (repoName: string) (cooldowns: Set) (branchEvents: CardEvent list) (isPending: bool) (scopedKey: string) (isFocused: bool) (wt: WorktreeStatus) = +let worktreeCard dispatch editorName (repoName: string) (baseBranch: string) (cooldowns: Set) (branchEvents: CardEvent list) (isPending: bool) (scopedKey: string) (isFocused: bool) (wt: WorktreeStatus) = let baseClass = cardClassName wt let className = if isFocused then baseClass + " focused" else baseClass let hasContent = wt.LastUserMessage.IsSome || (not (List.isEmpty branchEvents)) @@ -1191,7 +1191,7 @@ let worktreeCard dispatch editorName (repoName: string) (cooldowns: Set) (syncPending: Set) (cooldowns: Set) (wt: WorktreeStatus) = +let renderCard dispatch editorName isCompact (focusedElement: FocusTarget option) repoId repoName baseBranch (branchEvents: Map) (syncPending: Set) (cooldowns: Set) (wt: WorktreeStatus) = let scopedKey = $"{repoId}/{wt.Branch}" let events = branchEvents |> Map.tryFind scopedKey |> Option.defaultValue [] let isPending = syncPending |> Set.contains scopedKey let isFocused = focusedElement = Some (Card scopedKey) - if isCompact then compactWorktreeCard dispatch editorName repoName cooldowns scopedKey isFocused wt - else worktreeCard dispatch editorName repoName cooldowns events isPending scopedKey isFocused wt + if isCompact then compactWorktreeCard dispatch editorName repoName baseBranch cooldowns scopedKey isFocused wt + else worktreeCard dispatch editorName repoName baseBranch cooldowns events isPending scopedKey isFocused wt let archiveSection dispatch = ArchiveViews.archiveSection (ArchiveMsg >> dispatch) @@ -1490,7 +1490,7 @@ let repoSection dispatch editorName isCompact (focusedElement: FocusTarget optio else Html.div [ prop.className "card-grid" - prop.children (repo.Worktrees |> List.map (renderCard dispatch editorName isCompact focusedElement (RepoId.value repo.RepoId) repo.Name branchEvents syncPending cooldowns)) + prop.children (repo.Worktrees |> List.map (renderCard dispatch editorName isCompact focusedElement (RepoId.value repo.RepoId) repo.Name repo.BaseBranch branchEvents syncPending cooldowns)) ] archiveSection dispatch repo.ArchivedWorktrees ] diff --git a/src/Tests/DashboardTests.fs b/src/Tests/DashboardTests.fs index ecc17ad..f50d90f 100644 --- a/src/Tests/DashboardTests.fs +++ b/src/Tests/DashboardTests.fs @@ -458,8 +458,8 @@ type DashboardTests() = let! text = mainBehindElements.First.TextContentAsync() Assert.That( text, - Does.Contain("behind main").Or.EqualTo("up to date"), - "Main-behind indicator should show 'N behind main' or 'up to date'" + Does.Contain(" behind ").Or.EqualTo("up to date"), + "Main-behind indicator should show 'N behind {baseBranch}' or 'up to date'" ) } @@ -487,7 +487,7 @@ type DashboardTests() = Assert.That(count, Is.GreaterThanOrEqualTo(1), "Fixture has worktrees with high MainBehindCount; behind-warning indicators should be present") let! text = behindWarning.First.TextContentAsync() - Assert.That(text, Does.Contain("behind main")) + Assert.That(text, Does.Contain(" behind ")) let! color = behindWarning.First |> computedStyle "color" Assert.That(color, Is.EqualTo("rgb(243, 139, 168)"), "behind-warning should be red (#f38ba8)")