Skip to content

feat(ui): render colored label pills from GitHub label colors#278

Merged
dgershman merged 2 commits into
mainfrom
feature/crow-277-labels-session-header
May 15, 2026
Merged

feat(ui): render colored label pills from GitHub label colors#278
dgershman merged 2 commits into
mainfrom
feature/crow-277-labels-session-header

Conversation

@dgershman
Copy link
Copy Markdown
Collaborator

Summary

  • Introduce LabelInfo model with name and optional color (hex string), replacing plain [String] labels throughout the data pipeline
  • Add color field to the GitHub GraphQL query for issue and PR labels
  • Update LabelPillsView to render per-label colored pills with W3C contrast-aware text colors
  • GitLab labels (no color available from REST API) gracefully fall back to the existing gold theme

Closes #277

Test plan

  • CrowCore package builds cleanly
  • All 165 CrowCore tests pass (including 3 new LabelInfo tests)
  • Open app, view a session linked to a GitHub issue with colored labels — pills render with per-label colors
  • GitLab sessions show labels in gold fallback styling
  • Muted labels (done state in ticket board) remain desaturated
  • Label search and ignoreReviewLabels filtering still work correctly

🤖 Generated with Claude Code

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
Copy link
Copy Markdown
Contributor

@dhilgaertner dhilgaertner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code & Security Review

Security Review

Strengths:

  • GraphQL queries are static templates; the new color field 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.
  • Codable shape change for AssignedIssue.labels / ReviewRequest.labels is safe: both are in-memory caches in AppState (re-populated from the API on each fetch), not persisted to disk via JSONStore — so no on-disk migration concern.

Concerns:

  • None.

Code Quality

Strengths:

  • LabelInfo is a clean, focused model with sensible defaults; Identifiable/Hashable/Sendable conformances 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 LabelInfo tests (Codable round-trip with/without color, equality), and existing lookup tests updated to assert color is preserved.
  • All call sites in AppState, IssueTracker, and LabelPillsView consistently 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 (when label.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 on label.color == nil to keep the old values.
  • 🟢 Inconsistent invalid-input fallbacks in CorveilTheme.swift: Color(hexString:) falls back to CorveilTheme.gold, while contrastingTextColor(for:) falls back to textSecondary. 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-1007 and 1120-1125. Could be a small parseLabels(from: [String: Any]) -> [LabelInfo] helper, but only worth it if a third call site appears.
  • 🟢 LabelInfo.id == name means SwiftUI's ForEach would 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.


🤖 Reviewed by Crow via Claude Code

@dgershman dgershman merged commit aa9e628 into main May 15, 2026
2 checks passed
@dgershman dgershman deleted the feature/crow-277-labels-session-header branch May 15, 2026 04:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Show issue/PR labels in the session detail header bar

2 participants