mirror of
https://github.com/tiennm99/ghstats.git
synced 2026-09-02 16:20:08 +00:00
fix(card): productive titles render at 15 px for every timezone (#24)
Two cuts together keep the productive-time and productive-weekday
titles at the same 15 px the rest of the gallery uses:
1. utcOffsetLabel is now compact. Integer offsets drop the '.00'
padding ("UTC+7" vs the old "UTC+7.00") and non-integer offsets use
the colon form ("UTC+5:30", "UTC+5:45"). Saves 3 chars for the
common integer case, so "Commits by Hour (last year, UTC+7)" lands
at 34 chars — inside the 15 px budget.
2. weekdayTitle no longer embeds the UTC label. Day-of-week aggregates
by whole days; clock precision isn't informative there, and
dropping it shortens the title to 30 chars so the full 15 px lands
for every timezone.
Quarter-hour-zone users (Kathmandu UTC+5:45) see the hour title drop
to 14 px — 37 chars still exceeds the budget — but that's a rare
case and only 1 px off.
TestUTCOffsetLabel updated to the new format. TestFitTitleFontSize
pinned the new titles.
This commit is contained in:
@@ -70,7 +70,7 @@ When there's **exactly one slice** (one language at 100%), the renderer emits tw
|
||||
| Bar fill | `theme.Accent` for the peak bar; `mixHex(Background, Accent, 0.55)` dim for the rest so the busiest period reads at a glance |
|
||||
| Y-axis ticks | `niceTicks(max, 5)` — 1/2/5 × 10^k ladder. `last = ceil(max/step) × step` so `yMax ≥ dataMax` always (bars can't poke above chartH into the title) |
|
||||
| Axis caption | "hour of day" bottom-center on productive-time; weekday / by-year omit the caption since the x labels are self-describing |
|
||||
| Title format | `Commits by Hour (<window>, UTC±N.NN)` / `Commits by Weekday (<window>, UTC±N.NN)` / `Contributions by Year` |
|
||||
| Title format | `Commits by Hour (<window>, UTC±H[:MM])` / `Commits by Weekday (<window>)` — weekday drops the UTC so the title always fits at 15 px / `Contributions by Year` |
|
||||
| Hover | `<title>HH:00 — N commits</title>` / `<title>Mon — N commits</title>` / `<title>YYYY — N commits</title>` |
|
||||
|
||||
## Heatmap card (contributions-heatmap)
|
||||
|
||||
@@ -204,9 +204,9 @@ func TestFitTitleFontSize(t *testing.T) {
|
||||
{"Top Starred Repos", 15},
|
||||
{"Most Commit Language (all time)", 15}, // 31 chars
|
||||
{"Contributions by Year", 15},
|
||||
{"Commits by Hour (last year, UTC+7.00)", 14}, // 37 chars
|
||||
{"Commits by Weekday (last year, UTC+7.00)", 13}, // 40 chars
|
||||
{"Commits by Weekday (last year, UTC+12.75)", 12}, // 41 chars
|
||||
{"Commits by Hour (last year, UTC+7)", 15}, // 34 chars, common integer-zone case
|
||||
{"Commits by Hour (last year, UTC+5:45)", 14}, // 37 chars, quarter-hour zone (Kathmandu)
|
||||
{"Commits by Weekday (last year)", 15}, // 30 chars — weekday titles never include UTC
|
||||
{strings.Repeat("x", 200), 11}, // pathological
|
||||
}
|
||||
for _, c := range cases {
|
||||
@@ -369,7 +369,7 @@ func adversarialProfile() *github.Profile {
|
||||
p := &github.Profile{
|
||||
Login: "user-with-a-very-long-login-name",
|
||||
Name: "A Very Long Display Name That Keeps Going",
|
||||
UTCOffsetLabel: "UTC+12.75", // half-hour / quarter-hour zones widen the title
|
||||
UTCOffsetLabel: "UTC+12:45", // quarter-hour zone — longest realistic UTC label
|
||||
|
||||
Company: "A-Company-With-An-Unusually-Long-Name Pty Ltd",
|
||||
Location: "A Place With A Name That Is Way Too Long To Fit",
|
||||
|
||||
@@ -13,7 +13,7 @@ type productiveWeekdayCard struct{}
|
||||
func (productiveWeekdayCard) Filename() string { return "productive-weekday.svg" }
|
||||
|
||||
func (productiveWeekdayCard) SVG(p *github.Profile, t theme.Theme) ([]byte, error) {
|
||||
return renderWeekday(weekdayTitle("last year", p.UTCOffsetLabel), p.Weekday, t), nil
|
||||
return renderWeekday(weekdayTitle("last year"), p.Weekday, t), nil
|
||||
}
|
||||
|
||||
type productiveWeekdayAllTimeCard struct{}
|
||||
@@ -21,14 +21,14 @@ type productiveWeekdayAllTimeCard struct{}
|
||||
func (productiveWeekdayAllTimeCard) Filename() string { return "productive-weekday-all-time.svg" }
|
||||
|
||||
func (productiveWeekdayAllTimeCard) SVG(p *github.Profile, t theme.Theme) ([]byte, error) {
|
||||
return renderWeekday(weekdayTitle("all time", p.UTCOffsetLabel), p.WeekdayAllTime, t), nil
|
||||
return renderWeekday(weekdayTitle("all time"), p.WeekdayAllTime, t), nil
|
||||
}
|
||||
|
||||
func weekdayTitle(window, utcLabel string) string {
|
||||
if utcLabel == "" {
|
||||
return "Commits by Weekday (" + window + ")"
|
||||
}
|
||||
return "Commits by Weekday (" + window + ", " + utcLabel + ")"
|
||||
// weekdayTitle skips the UTC offset — the data aggregates into day-of-week
|
||||
// buckets, so exact clock precision isn't informative and dropping it keeps
|
||||
// the title short enough to render at the full 15 px.
|
||||
func weekdayTitle(window string) string {
|
||||
return "Commits by Weekday (" + window + ")"
|
||||
}
|
||||
|
||||
// Index 0 = Sunday to match time.Weekday (which is what FetchProductive stores).
|
||||
|
||||
@@ -113,13 +113,27 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
// utcOffsetLabel formats the location's current offset from UTC as "UTC±N.NN"
|
||||
// (two-decimal hours) so half-hour zones like India (UTC+5.30) or Nepal
|
||||
// (UTC+5.75) render cleanly. Matches github-profile-summary-cards' style.
|
||||
// utcOffsetLabel formats the location's current offset from UTC compactly:
|
||||
//
|
||||
// integer hours → "UTC+7" (no ".00" padding — 3 chars shorter than
|
||||
// the old "UTC+7.00" format, keeps the
|
||||
// productive-time title at 15 px)
|
||||
// half-hour zone → "UTC+5:30" (India)
|
||||
// quarter-hour → "UTC+5:45" (Nepal)
|
||||
// negative zone → "UTC-3" / "UTC-3:30"
|
||||
func utcOffsetLabel(loc *time.Location) string {
|
||||
_, offsetSec := time.Now().In(loc).Zone()
|
||||
hours := float64(offsetSec) / 3600.0
|
||||
return fmt.Sprintf("UTC%+.2f", hours)
|
||||
sign := "+"
|
||||
if offsetSec < 0 {
|
||||
sign = "-"
|
||||
offsetSec = -offsetSec
|
||||
}
|
||||
hours := offsetSec / 3600
|
||||
minutes := (offsetSec % 3600) / 60
|
||||
if minutes == 0 {
|
||||
return fmt.Sprintf("UTC%s%d", sign, hours)
|
||||
}
|
||||
return fmt.Sprintf("UTC%s%d:%02d", sign, hours, minutes)
|
||||
}
|
||||
|
||||
func resolveThemes(spec string) ([]theme.Theme, error) {
|
||||
|
||||
+6
-6
@@ -6,17 +6,17 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// TestUTCOffsetLabel checks that half-hour and quarter-hour zones render
|
||||
// with a decimal, matching github-profile-summary-cards' "UTC+X.NN" style.
|
||||
// TestUTCOffsetLabel checks the compact format: integer hours drop the
|
||||
// minutes suffix, non-zero offsets render as `UTC±H:MM`.
|
||||
func TestUTCOffsetLabel(t *testing.T) {
|
||||
cases := []struct {
|
||||
zone string
|
||||
want string // must appear in the label; exact value varies by DST
|
||||
}{
|
||||
{"UTC", "UTC+0.00"},
|
||||
{"Asia/Saigon", "UTC+7.00"},
|
||||
{"Asia/Kolkata", "UTC+5.50"}, // half-hour zone
|
||||
{"Asia/Kathmandu", "UTC+5.75"}, // quarter-hour zone
|
||||
{"UTC", "UTC+0"},
|
||||
{"Asia/Saigon", "UTC+7"},
|
||||
{"Asia/Kolkata", "UTC+5:30"}, // half-hour zone
|
||||
{"Asia/Kathmandu", "UTC+5:45"}, // quarter-hour zone
|
||||
}
|
||||
for _, tc := range cases {
|
||||
loc, err := time.LoadLocation(tc.zone)
|
||||
|
||||
Reference in New Issue
Block a user