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