From 4240d69a0ae4fc19ecb9e20502a4ba0424457ac7 Mon Sep 17 00:00:00 2001 From: Dustin Hilgaertner Date: Wed, 15 Apr 2026 14:00:47 -0500 Subject: [PATCH] Make TicketCard issue and PR chips clickable Extract LinkChip into its own file so the ticket board rows can reuse the same clickable capsule the session header already uses. Replace the static purple PR badge and the plain issue-number text in TicketCard with gold LinkChips that open the issue and PR URLs in the default browser. Co-Authored-By: Claude Opus 4.6 (1M context) --- Packages/CrowUI/Sources/CrowUI/LinkChip.swift | 34 +++++++++++++++++++ .../Sources/CrowUI/SessionDetailView.swift | 32 ----------------- .../Sources/CrowUI/TicketBoardView.swift | 33 ++++++------------ 3 files changed, 45 insertions(+), 54 deletions(-) create mode 100644 Packages/CrowUI/Sources/CrowUI/LinkChip.swift diff --git a/Packages/CrowUI/Sources/CrowUI/LinkChip.swift b/Packages/CrowUI/Sources/CrowUI/LinkChip.swift new file mode 100644 index 0000000..d9e7ddd --- /dev/null +++ b/Packages/CrowUI/Sources/CrowUI/LinkChip.swift @@ -0,0 +1,34 @@ +import SwiftUI +import CrowCore + +/// Clickable capsule that opens a URL (issue link, PR link, repo link). +struct LinkChip: View { + let label: String + let url: String + let icon: String + + var body: some View { + Button { + if let nsURL = URL(string: url) { + NSWorkspace.shared.open(nsURL) + } + } label: { + HStack(spacing: 4) { + Image(systemName: icon) + .font(.caption) + Text(label) + .font(.caption) + .fontWeight(.medium) + } + .foregroundStyle(CorveilTheme.gold) + .padding(.horizontal, 8) + .padding(.vertical, 4) + .background(CorveilTheme.gold.opacity(0.1)) + .overlay( + Capsule().strokeBorder(CorveilTheme.goldDark.opacity(0.3), lineWidth: 1) + ) + .clipShape(Capsule()) + } + .buttonStyle(.plain) + } +} diff --git a/Packages/CrowUI/Sources/CrowUI/SessionDetailView.swift b/Packages/CrowUI/Sources/CrowUI/SessionDetailView.swift index d53ac2f..2dc17e4 100644 --- a/Packages/CrowUI/Sources/CrowUI/SessionDetailView.swift +++ b/Packages/CrowUI/Sources/CrowUI/SessionDetailView.swift @@ -356,38 +356,6 @@ struct StatusBadge: View { } } -/// Clickable capsule that opens a URL (issue link, PR link, repo link). -struct LinkChip: View { - let label: String - let url: String - let icon: String - - var body: some View { - Button { - if let nsURL = URL(string: url) { - NSWorkspace.shared.open(nsURL) - } - } label: { - HStack(spacing: 4) { - Image(systemName: icon) - .font(.caption) - Text(label) - .font(.caption) - .fontWeight(.medium) - } - .foregroundStyle(CorveilTheme.gold) - .padding(.horizontal, 8) - .padding(.vertical, 4) - .background(CorveilTheme.gold.opacity(0.1)) - .overlay( - Capsule().strokeBorder(CorveilTheme.goldDark.opacity(0.3), lineWidth: 1) - ) - .clipShape(Capsule()) - } - .buttonStyle(.plain) - } -} - // MARK: - Readiness-Aware Terminal Wrapper /// Wraps a TerminalSurfaceView with readiness tracking. diff --git a/Packages/CrowUI/Sources/CrowUI/TicketBoardView.swift b/Packages/CrowUI/Sources/CrowUI/TicketBoardView.swift index 790cba6..c1808bc 100644 --- a/Packages/CrowUI/Sources/CrowUI/TicketBoardView.swift +++ b/Packages/CrowUI/Sources/CrowUI/TicketBoardView.swift @@ -404,13 +404,18 @@ struct TicketCard: View { .font(.caption) .foregroundStyle(isDone ? CorveilTheme.textMuted : CorveilTheme.textSecondary) - Text("#\(String(issue.number))") - .font(.system(size: 12, weight: .medium)) - .monospacedDigit() - .foregroundStyle(isDone ? CorveilTheme.textMuted : CorveilTheme.textPrimary) + LinkChip( + label: "Issue #\(String(issue.number))", + url: issue.url, + icon: "link" + ) - if let prNum = issue.prNumber { - ticketPRBadge(number: prNum, url: issue.prURL) + if let prNum = issue.prNumber, let prURL = issue.prURL { + LinkChip( + label: "PR #\(String(prNum))", + url: prURL, + icon: "arrow.triangle.pull" + ) } Spacer() @@ -493,22 +498,6 @@ struct TicketCard: View { .opacity(isSelectable ? 1.0 : 0.3) } - private func ticketPRBadge(number: Int, url: String?) -> some View { - HStack(spacing: 3) { - Image(systemName: "arrow.triangle.pull") - .font(.caption2) - Text("PR #\(String(number))") - .font(.caption2) - .fontWeight(.medium) - } - .foregroundStyle(.purple) - .padding(.horizontal, 6) - .padding(.vertical, 2) - .background(.purple.opacity(0.1)) - .overlay(Capsule().strokeBorder(Color.purple.opacity(0.3), lineWidth: 0.5)) - .clipShape(Capsule()) - } - private var labelRow: some View { HStack(spacing: 4) { ForEach(issue.labels.prefix(3), id: \.self) { label in