From e96adc8e05507edf2dd445510761666f63f7796b Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 24 Jun 2026 22:25:29 +0800 Subject: [PATCH 1/4] Track CLOSED PR state in worktrees The batched GraphQL query only fetched OPEN and MERGED PRs, so closed PRs disappeared from worktree rows entirely. Add CLOSED to the query filter and update the UI layers to display it: - GithubCLIClient: add CLOSED to states filter - PullRequestBadgeView: add closedColor and CLOSED badge/helpText cases - PullRequestStatusButton: allow CLOSED in shouldDisplay, add CLOSED init path - WorktreeRow: show "Closed" label in summary text Co-authored-by: Qwen-Coder Signed-off-by: Alex --- supacode/Clients/Github/GithubCLIClient.swift | 2 +- .../Repositories/Views/PullRequestBadgeView.swift | 5 +++++ .../Repositories/Views/PullRequestStatusButton.swift | 8 +++++++- supacode/Features/Repositories/Views/WorktreeRow.swift | 5 +++++ 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/supacode/Clients/Github/GithubCLIClient.swift b/supacode/Clients/Github/GithubCLIClient.swift index f64ed10e..acfb6bc7 100644 --- a/supacode/Clients/Github/GithubCLIClient.swift +++ b/supacode/Clients/Github/GithubCLIClient.swift @@ -1190,7 +1190,7 @@ nonisolated private func makeBatchPullRequestsQuery( let escapedBranch = escapeGraphQLString(branch) let orderBy = "orderBy: {field: UPDATED_AT, direction: DESC}" let selection = """ - \(alias): pullRequests(first: 5, states: [OPEN, MERGED], headRefName: \"\(escapedBranch)\", \(orderBy)) { + \(alias): pullRequests(first: 5, states: [OPEN, MERGED, CLOSED], headRefName: \"\(escapedBranch)\", \(orderBy)) { nodes { number title diff --git a/supacode/Features/Repositories/Views/PullRequestBadgeView.swift b/supacode/Features/Repositories/Views/PullRequestBadgeView.swift index db1821ee..1dcaca7f 100644 --- a/supacode/Features/Repositories/Views/PullRequestBadgeView.swift +++ b/supacode/Features/Repositories/Views/PullRequestBadgeView.swift @@ -3,6 +3,7 @@ import SwiftUI enum PullRequestBadgeStyle { static let mergedColor = Color.purple static let openColor = Color.green + static let closedColor = Color.red static let queuedColor = Color.brown static func style(state: String?, number: Int?, isQueued: Bool = false) -> (text: String, color: Color)? { @@ -14,6 +15,8 @@ enum PullRequestBadgeStyle { return (text: number.map { "#\($0)" } ?? "MERGED", color: mergedColor) case "OPEN": return (text: number.map { "#\($0)" } ?? "OPEN", color: isQueued ? queuedColor : openColor) + case "CLOSED": + return (text: number.map { "#\($0)" } ?? "CLOSED", color: closedColor) default: return nil } @@ -26,6 +29,8 @@ enum PullRequestBadgeStyle { return url == nil ? "Pull request merged" : "Open merged pull request on GitHub" case "OPEN": return url == nil ? "Pull request open" : "Open pull request on GitHub" + case "CLOSED": + return url == nil ? "Pull request closed" : "Open closed pull request on GitHub" default: return url == nil ? "Pull request" : "Open pull request on GitHub" } diff --git a/supacode/Features/Repositories/Views/PullRequestStatusButton.swift b/supacode/Features/Repositories/Views/PullRequestStatusButton.swift index c19cc288..6219541f 100644 --- a/supacode/Features/Repositories/Views/PullRequestStatusButton.swift +++ b/supacode/Features/Repositories/Views/PullRequestStatusButton.swift @@ -76,6 +76,11 @@ struct PullRequestStatusModel: Equatable { self.statusChecks = [] return } + if state == "CLOSED" { + self.detailText = nil + self.statusChecks = [] + return + } let isDraft = pullRequest.isDraft let prefix = isDraft ? "(Drafted) " : "" let mergeReadiness = PullRequestMergeReadiness(pullRequest: pullRequest) @@ -116,6 +121,7 @@ struct PullRequestStatusModel: Equatable { guard number != nil else { return false } - return state?.uppercased() != "CLOSED" + let s = state?.uppercased() + return s != nil && s != "UNKNOWN" } } diff --git a/supacode/Features/Repositories/Views/WorktreeRow.swift b/supacode/Features/Repositories/Views/WorktreeRow.swift index 5de0524e..dee38940 100644 --- a/supacode/Features/Repositories/Views/WorktreeRow.swift +++ b/supacode/Features/Repositories/Views/WorktreeRow.swift @@ -278,6 +278,11 @@ private struct WorktreeRowInfoView: View { var segment = AttributedString("Merged") segment.foregroundColor = PullRequestBadgeStyle.mergedColor result.append(segment) + } else if pullRequestState == "CLOSED" { + appendSeparator() + var segment = AttributedString("Closed") + segment.foregroundColor = PullRequestBadgeStyle.closedColor + result.append(segment) } else if isQueued { // A queued PR is mid-merge, so the queue state takes priority over the // merge-readiness label. -- 2.51.2 From 02ec6b9b20bd1d8dd00d4d26d55faa5c4afe290c Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 24 Jun 2026 22:52:05 +0800 Subject: [PATCH 2/4] Add CLOSED to cross-repo batch PR query makeCrossRepoBatchQuery was still using states: [OPEN, MERGED], missing CLOSED PRs. This caused closed PRs to briefly appear then disappear when the cross-repo refresh path kicked in. Both PR query paths now include CLOSED: - makeBatchPullRequestsQuery (single-repo) - makeCrossRepoBatchQuery (cross-repo, actual code path used) Signed-off-by: Alex --- supacode/Clients/Github/GithubCLIClient.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/supacode/Clients/Github/GithubCLIClient.swift b/supacode/Clients/Github/GithubCLIClient.swift index acfb6bc7..25988a1e 100644 --- a/supacode/Clients/Github/GithubCLIClient.swift +++ b/supacode/Clients/Github/GithubCLIClient.swift @@ -578,7 +578,7 @@ nonisolated private func makeCrossRepoBatchQuery( branchAliasMap[branchAlias] = branch let escapedBranch = escapeGraphQLString(branch) let pullRequestsArgs = - "first: 5, states: [OPEN, MERGED], headRefName: \"\(escapedBranch)\", \(orderBy)" + "first: 5, states: [OPEN, MERGED, CLOSED], headRefName: \"\(escapedBranch)\", \(orderBy)" let selection = """ \(branchAlias): pullRequests(\(pullRequestsArgs)) { nodes { -- 2.51.2 From 7c55fe1d90b6df1a1d9c4c9aba07d6aac62c3dbf Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 24 Jun 2026 22:57:29 +0800 Subject: [PATCH 3/4] Fix SwiftLint identifier_name violation in shouldDisplay Variable name 's' was too short (minimum 3 chars). Rename to 'uppercasedState' to satisfy the lint rule. Signed-off-by: Alex --- .../Features/Repositories/Views/PullRequestStatusButton.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/supacode/Features/Repositories/Views/PullRequestStatusButton.swift b/supacode/Features/Repositories/Views/PullRequestStatusButton.swift index 6219541f..2c0f84b2 100644 --- a/supacode/Features/Repositories/Views/PullRequestStatusButton.swift +++ b/supacode/Features/Repositories/Views/PullRequestStatusButton.swift @@ -121,7 +121,7 @@ struct PullRequestStatusModel: Equatable { guard number != nil else { return false } - let s = state?.uppercased() - return s != nil && s != "UNKNOWN" + let uppercasedState = state?.uppercased() + return uppercasedState != nil && uppercasedState != "UNKNOWN" } } -- 2.51.2 From 79a3243043921e312c9cea45bdbbe246a9eb96f2 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 25 Jun 2026 09:38:47 +0800 Subject: [PATCH 4/4] Change closed PR color from red to orange Red overlaps with error semantics (conflicts, failed checks, blocked). A closed PR is a terminal/informational state, not an error, so orange keeps it visible without competing with failure states. Signed-off-by: Alex --- supacode/Features/Repositories/Views/PullRequestBadgeView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/supacode/Features/Repositories/Views/PullRequestBadgeView.swift b/supacode/Features/Repositories/Views/PullRequestBadgeView.swift index 1dcaca7f..466e239b 100644 --- a/supacode/Features/Repositories/Views/PullRequestBadgeView.swift +++ b/supacode/Features/Repositories/Views/PullRequestBadgeView.swift @@ -3,7 +3,7 @@ import SwiftUI enum PullRequestBadgeStyle { static let mergedColor = Color.purple static let openColor = Color.green - static let closedColor = Color.red + static let closedColor = Color.orange static let queuedColor = Color.brown static func style(state: String?, number: Int?, isQueued: Bool = false) -> (text: String, color: Color)? { -- 2.51.2