mirror of
https://github.com/tiennm99/ghglance.git
synced 2026-10-11 03:13:20 +00:00
4.0 KiB
4.0 KiB
phase, title, status, priority, effort, dependencies
| phase | title | status | priority | effort | dependencies | |
|---|---|---|---|---|---|---|
| 2 | SVG card rendering | completed | P1 | 1.5h |
|
Phase 2: SVG card rendering
Overview
Render the 6 records as a records.svg card mirroring stats.svg's row layout (icon + label + right-aligned accent value). Register the card in allCards so RenderAll picks it up.
Requirements
Functional
- File output:
records.svg(340×200, matches all other cards) - Title:
Records (all time) - 6 rows in this order:
- ⚡ Best day —
{count} on YYYY-MM-DD - 🔥 Best month —
{count} in MMM YYYY(e.g.612 in Mar 2026) - 🌱 First contribution —
YYYY-MM-DD - 📆 Active days —
{formatInt(count)} - ⏳ On GitHub —
{years.1f} years - 🌐 Languages used —
{count}
- ⚡ Best day —
- Empty-data fallback: render the card with rows showing
—(em-dash) for missing values, never panic
Non-functional
- Reuse
header()/footerhelpers frominternal/card/svg.go - Reuse
escapeXML()andformatInt()from existing helpers - Octicon paths live in
icons.go(1-3 new icons may be needed)
Architecture
internal/card/records.go
├── recordsCard struct{}
├── (recordsCard) Filename() string → "records.svg"
├── (recordsCard) SVG(p, t) → builds 6 statRow-style entries, emits SVG
└── helpers from Phase 1
Register in internal/card/card.go:
var allCards = []Card{
...
contributionsByYearCard{},
recordsCard{}, // new — appended last
}
Icon strategy (minimize new octicon paths):
- Best day →
iconCommit(existing, fits "activity peak") - Best month →
iconStar(existing, fits "highlight") - First contribution → new
iconCalendarocticon - Active days →
iconRepos(existing, decent fallback) OR newiconHistory - On GitHub →
iconClock(existing) - Languages used → new
iconGlobeocticon
Net: 2-3 new octicon paths added to icons.go from primer/octicons. Final icon set decided during implementation — fewer-new is fine if reuse looks clean.
Related Code Files
- Create:
internal/card/records.go - Modify:
internal/card/card.go(registerrecordsCard{}inallCards) - Modify:
internal/card/icons.go(add 2-3 octicon paths) - Read for context:
internal/card/stats.go,internal/card/svg.go
Implementation Steps
- Add octicon paths to
icons.go(copy fromprimer/octicons16×16 viewBox). - Create
recordsCardstruct +Filename(). - Implement
SVG():- Compute 6 records via Phase-1 helpers (
time.Now()foraccountAgeYears). - Build
[]statRow(or local equivalent) with formatted strings. - Empty-data branch: if
len(p.DailyContributionsAllTime) == 0, label/value rows still render but values are—. - Emit header → 6 rows (same coords as stats card:
rowX=20, rowY0=55, rowDY=20, iconSize=12, valueX=320) → footer.
- Compute 6 records via Phase-1 helpers (
- Register in
allCards. go build ./...— confirm clean compile.- Run end-to-end render against the demo fixture if available, else inspect generated SVG manually.
Success Criteria
go build ./...clean- Card filename
records.svgregistered and produced byRenderAll - SVG validates (no XML parse errors) for all 65 themes
- Empty-data fixture produces a legible card (no panic, no missing values causing layout shift)
Risk Assessment
- Risk: Row spacing collides at 6 rows × 20px DY + 55px Y0 = 175px; fits within 200px frame. Mitigation: verified against stats card (which has 7 rows at the same cadence). Safe.
- Risk: Long values (e.g. "612 in Mar 2026") overrun the value column at narrow themes. Mitigation: value text uses
text-anchor="end"atvalueX=320— overflows go left, never clip. If labels collide with values on a row, shorten label phrasing (e.g. "Best month" not "Best month ever"). - Risk: New octicon paths copied incorrectly (broken SVG). Mitigation: copy from
primer/octiconssource; smoke-test render in browser.