mirror of
https://github.com/tiennm99/ghglance.git
synced 2026-10-11 03:13:20 +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:
1 parent
eb04446272
commit
0e0e4fc500
5 files changed
+37
-23
No files matched your search
@@ -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 |
|
| 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) |
|
| 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 |
|
| 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>` |
|
| Hover | `<title>HH:00 — N commits</title>` / `<title>Mon — N commits</title>` / `<title>YYYY — N commits</title>` |
|
||||||
|
|
||||||
## Heatmap card (contributions-heatmap)
|
## Heatmap card (contributions-heatmap)
|
||||||
|
|||||||
@@ -204,9 +204,9 @@ func TestFitTitleFontSize(t *testing.T) {
|
|||||||
{"Top Starred Repos", 15},
|
{"Top Starred Repos", 15},
|
||||||
{"Most Commit Language (all time)", 15}, // 31 chars
|
{"Most Commit Language (all time)", 15}, // 31 chars
|
||||||
{"Contributions by Year", 15},
|
{"Contributions by Year", 15},
|
||||||
{"Commits by Hour (last year, UTC+7.00)", 14}, // 37 chars
|
{"Commits by Hour (last year, UTC+7)", 15}, // 34 chars, common integer-zone case
|
||||||
{"Commits by Weekday (last year, UTC+7.00)", 13}, // 40 chars
|
{"Commits by Hour (last year, UTC+5:45)", 14}, // 37 chars, quarter-hour zone (Kathmandu)
|
||||||
{"Commits by Weekday (last year, UTC+12.75)", 12}, // 41 chars
|
{"Commits by Weekday (last year)", 15}, // 30 chars — weekday titles never include UTC
|
||||||
{strings.Repeat("x", 200), 11}, // pathological
|
{strings.Repeat("x", 200), 11}, // pathological
|
||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
@@ -369,7 +369,7 @@ func adversarialProfile() *github.Profile {
|
|||||||
p := &github.Profile{
|
p := &github.Profile{
|
||||||
Login: "user-with-a-very-long-login-name",
|
Login: "user-with-a-very-long-login-name",
|
||||||
Name: "A Very Long Display Name That Keeps Going",
|
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",
|
Company: "A-Company-With-An-Unusually-Long-Name Pty Ltd",
|
||||||
Location: "A Place With A Name That Is Way Too Long To Fit",
|
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) Filename() string { return "productive-weekday.svg" }
|
||||||
|
|
||||||
func (productiveWeekdayCard) SVG(p *github.Profile, t theme.Theme) ([]byte, error) {
|
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{}
|
type productiveWeekdayAllTimeCard struct{}
|
||||||
@@ -21,14 +21,14 @@ type productiveWeekdayAllTimeCard struct{}
|
|||||||
func (productiveWeekdayAllTimeCard) Filename() string { return "productive-weekday-all-time.svg" }
|
func (productiveWeekdayAllTimeCard) Filename() string { return "productive-weekday-all-time.svg" }
|
||||||
|
|
||||||
func (productiveWeekdayAllTimeCard) SVG(p *github.Profile, t theme.Theme) ([]byte, error) {
|
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 {
|
// weekdayTitle skips the UTC offset — the data aggregates into day-of-week
|
||||||
if utcLabel == "" {
|
// buckets, so exact clock precision isn't informative and dropping it keeps
|
||||||
return "Commits by Weekday (" + window + ")"
|
// the title short enough to render at the full 15 px.
|
||||||
}
|
func weekdayTitle(window string) string {
|
||||||
return "Commits by Weekday (" + window + ", " + utcLabel + ")"
|
return "Commits by Weekday (" + window + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Index 0 = Sunday to match time.Weekday (which is what FetchProductive stores).
|
// 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"
|
// utcOffsetLabel formats the location's current offset from UTC compactly:
|
||||||
// (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.
|
// 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 {
|
func utcOffsetLabel(loc *time.Location) string {
|
||||||
_, offsetSec := time.Now().In(loc).Zone()
|
_, offsetSec := time.Now().In(loc).Zone()
|
||||||
hours := float64(offsetSec) / 3600.0
|
sign := "+"
|
||||||
return fmt.Sprintf("UTC%+.2f", hours)
|
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) {
|
func resolveThemes(spec string) ([]theme.Theme, error) {
|
||||||
|
|||||||
+6
-6
@@ -6,17 +6,17 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestUTCOffsetLabel checks that half-hour and quarter-hour zones render
|
// TestUTCOffsetLabel checks the compact format: integer hours drop the
|
||||||
// with a decimal, matching github-profile-summary-cards' "UTC+X.NN" style.
|
// minutes suffix, non-zero offsets render as `UTC±H:MM`.
|
||||||
func TestUTCOffsetLabel(t *testing.T) {
|
func TestUTCOffsetLabel(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
zone string
|
zone string
|
||||||
want string // must appear in the label; exact value varies by DST
|
want string // must appear in the label; exact value varies by DST
|
||||||
}{
|
}{
|
||||||
{"UTC", "UTC+0.00"},
|
{"UTC", "UTC+0"},
|
||||||
{"Asia/Saigon", "UTC+7.00"},
|
{"Asia/Saigon", "UTC+7"},
|
||||||
{"Asia/Kolkata", "UTC+5.50"}, // half-hour zone
|
{"Asia/Kolkata", "UTC+5:30"}, // half-hour zone
|
||||||
{"Asia/Kathmandu", "UTC+5.75"}, // quarter-hour zone
|
{"Asia/Kathmandu", "UTC+5:45"}, // quarter-hour zone
|
||||||
}
|
}
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
loc, err := time.LoadLocation(tc.zone)
|
loc, err := time.LoadLocation(tc.zone)
|
||||||
|
|||||||
Reference in new issue
Block a user