From c65157f63a3f3f8b6b182daf64175117385d3647 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Fri, 29 May 2026 15:00:04 +0200 Subject: [PATCH 1/2] Fix hardcoded 'behind main' label to use configured baseBranch Thread baseBranch parameter from RepoModel through renderCard, worktreeCard, compactWorktreeCard, mainBehindWithSync, syncButton, and mainBehindIndicator so the UI displays the actual configured base branch name instead of always showing 'main'. --- src/Client/App.fs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) 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 ] From 80d0dc4e124ebe0ad787c7b1124fb6e74500a8d4 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Fri, 29 May 2026 15:18:44 +0200 Subject: [PATCH 2/2] Fix E2E tests to not hardcode 'behind main' in assertions Tests now assert on ' behind ' pattern instead of 'behind main', matching the new dynamic baseBranch label. --- src/Tests/DashboardTests.fs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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)")