feat(ui): render colored label pills from GitHub label colors#278
Merged
Conversation
Fetch label color data from the GitHub GraphQL API and render per-label colored pills in the session header, sidebar, review board, and ticket board. Labels without color (GitLab) fall back to the existing gold theme. Uses the W3C relative luminance formula for contrast-aware text. 🤖 Generated with Claude Code, orchestrated by Crow Co-Authored-By: Claude <noreply@anthropic.com> Crow-Session: 5B4B439E-1D4E-4303-BB71-50C0E328B1F8
SessionDetailView and SessionListView had explicit [String] type annotations on their sessionLabels computed properties, causing build failures after the labels(forSession:) return type changed to [LabelInfo]. 🤖 Generated with Claude Code, orchestrated by Crow Co-Authored-By: Claude <noreply@anthropic.com> Crow-Session: 5B4B439E-1D4E-4303-BB71-50C0E328B1F8
dhilgaertner
approved these changes
May 15, 2026
Contributor
dhilgaertner
left a comment
There was a problem hiding this comment.
Code & Security Review
Security Review
Strengths:
- GraphQL queries are static templates; the new
colorfield is added safely with no user-controlled interpolation. Color(hexString:)validates length (6) and radix-16 parsing before use; bad input gracefully falls back rather than crashing.- Label color values flow only into rendering — no eval, no attribution, no path/URL/SQL contexts. No injection surface introduced.
Codableshape change forAssignedIssue.labels/ReviewRequest.labelsis safe: both are in-memory caches inAppState(re-populated from the API on each fetch), not persisted to disk viaJSONStore— so no on-disk migration concern.
Concerns:
- None.
Code Quality
Strengths:
LabelInfois a clean, focused model with sensible defaults;Identifiable/Hashable/Sendableconformances are right.contrastingTextColor(for:)correctly implements the W3C relative luminance formula with the standard 0.179 threshold — matches GitHub's renderer.- Test coverage: 3 new
LabelInfotests (Codable round-trip with/without color, equality), and existing lookup tests updated to assert color is preserved. - All call sites in
AppState,IssueTracker, andLabelPillsViewconsistently switched to$0.name.lowercased()— filter and search behavior is preserved.
Nits (non-blocking):
- 🟡 GitLab fallback isn't quite "existing gold theme" as the PR description claims. Old:
gold.opacity(0.08)background,borderSubtle(=gold @ 0.12) border. New (whenlabel.color == nil):gold.opacity(0.15)background,gold.opacity(0.3)border. Slightly more saturated than before. If pixel-perfect parity matters for GitLab labels, consider branching the styling onlabel.color == nilto keep the old values. - 🟢 Inconsistent invalid-input fallbacks in
CorveilTheme.swift:Color(hexString:)falls back toCorveilTheme.gold, whilecontrastingTextColor(for:)falls back totextSecondary. Both unreachable in practice (GitHub always returns 6-char hex), but worth aligning if either is reused later. - 🟢 Duplicated label parsing block in
IssueTracker.swift:1002-1007and1120-1125. Could be a smallparseLabels(from: [String: Any]) -> [LabelInfo]helper, but only worth it if a third call site appears. - 🟢
LabelInfo.id == namemeans SwiftUI'sForEachwould crash on duplicate label names. GitHub guarantees label-name uniqueness per repo, so this isn't a real risk — just noting it.
Summary Table
| Priority | Issue |
|---|---|
| Red | None |
| Yellow | GitLab fallback styling drift (opacity 0.08→0.15, border color change) — verify visually before claiming "no change" |
| Green | Inconsistent fallback colors in CorveilTheme; duplicated label-parsing in IssueTracker.swift; LabelInfo.id collision risk on duplicate names |
Recommendation: Approve. Focused, well-tested change. Build is clean, all 165 tests pass including the 3 new ones. The GitLab fallback styling drift is the only thing worth a second look before merge — and only if you want pixel parity with the previous look.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
LabelInfomodel withnameand optionalcolor(hex string), replacing plain[String]labels throughout the data pipelinecolorfield to the GitHub GraphQL query for issue and PR labelsLabelPillsViewto render per-label colored pills with W3C contrast-aware text colorsCloses #277
Test plan
🤖 Generated with Claude Code