mirror of
https://github.com/tiennm99/ghglance.git
synced 2026-10-11 12:18:55 +00:00
fix(card): auto-shrink card titles that don't fit at 15 px (#10)
productive-weekday's "Commits by Weekday (last year, UTC+7.00)" is 40 chars — at font-size 15 that renders ≈360 px wide, past the 340 px frame. Before the previous stress-test run missed it because the fixture left UTCOffsetLabel blank, so the title was the short "Commits by Weekday (last year)" form that happens to fit. Fix at the render layer instead of rewriting every long title: header() now picks the largest font between 11 and 15 px at which the title still fits in 316 px (card width − 20 left inset − 4 right safety). 40-char titles land at 13 px; ≤ 35-char titles still render at 15 px as before. Readability floor is 11 px — far from the point at which text becomes illegible, but a hard floor nevertheless. Stress-test fixture now populates UTCOffsetLabel with "UTC+12.75" (half-hour / quarter-hour zones make the widest title) so this class of bug surfaces automatically from here on.
This commit is contained in:
1 parent
927926751a
commit
9dcb4505fa
2 files changed
+35
-3
No files matched your search
@@ -290,6 +290,8 @@ 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
|
||||
|
||||
Company: "A-Company-With-An-Unusually-Long-Name Pty Ltd",
|
||||
Location: "A Place With A Name That Is Way Too Long To Fit",
|
||||
Website: "https://example-with-a-very-long-domain.example.com/profile",
|
||||
|
||||
+33
-3
@@ -3,6 +3,7 @@ package card
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// escapeXML replaces the five XML-significant characters so user-controlled
|
||||
@@ -65,14 +66,43 @@ func formatInt(n int) string {
|
||||
return out
|
||||
}
|
||||
|
||||
// header returns the opening <svg> tag + background rect + title text.
|
||||
// header returns the opening <svg> tag + background rect + title text. The
|
||||
// title font-size is auto-shrunk if the string wouldn't fit on one line at
|
||||
// the default 15 px — productive-time and productive-weekday append the
|
||||
// timezone label ("UTC+7.00") which can push the title to ~40 chars, past
|
||||
// the 340 px frame at 15 px. Floor at 11 px so small zones stay readable.
|
||||
func header(width, height int, bg, stroke string, strokeOpacity float64, titleColor, title string) string {
|
||||
fontSize := fitTitleFontSize(title, width)
|
||||
return fmt.Sprintf(`<svg xmlns="http://www.w3.org/2000/svg" width="%d" height="%d" viewBox="0 0 %d %d" font-family="'Segoe UI', Ubuntu, Sans-Serif">
|
||||
<rect x="0.5" y="0.5" width="%d" height="%d" rx="6" fill="%s" stroke="%s" stroke-opacity="%.2f"/>
|
||||
<text x="20" y="30" font-size="15" font-weight="600" fill="%s">%s</text>`,
|
||||
<text x="20" y="30" font-size="%d" font-weight="600" fill="%s">%s</text>`,
|
||||
width, height, width, height,
|
||||
width-1, height-1, bg, stroke, strokeOpacity,
|
||||
titleColor, escapeXML(title))
|
||||
fontSize, titleColor, escapeXML(title))
|
||||
}
|
||||
|
||||
// fitTitleFontSize picks the largest title font (between 11 and 15 px) at
|
||||
// which the title still fits in width − leftInset − rightSafety. Uses the
|
||||
// same 0.6 × font-size char-width estimate as the fit-the-frame test.
|
||||
func fitTitleFontSize(title string, width int) int {
|
||||
const (
|
||||
leftInset = 20
|
||||
rightSafety = 4
|
||||
minFont = 11
|
||||
maxFont = 15
|
||||
avgCharRatio = 0.6
|
||||
)
|
||||
budget := float64(width - leftInset - rightSafety)
|
||||
chars := utf8.RuneCountInString(title)
|
||||
if chars == 0 {
|
||||
return maxFont
|
||||
}
|
||||
for fs := maxFont; fs >= minFont; fs-- {
|
||||
if float64(chars)*float64(fs)*avgCharRatio <= budget {
|
||||
return fs
|
||||
}
|
||||
}
|
||||
return minFont
|
||||
}
|
||||
|
||||
const footer = `
|
||||
|
||||
Reference in new issue
Block a user