mirror of
https://github.com/tiennm99/ghstats.git
synced 2026-05-24 19:34:39 +00:00
ffc76ebc21
- Replace the previous 30-theme list with the complete 61-theme palette from github-profile-summary-cards (snake_case ids, e.g. github_dark, nord_bright, solarized_dark). - Add Stroke and StrokeOpacity fields to Theme; card frame now uses the theme's own stroke instead of a hardcoded white overlay so light themes render a visible border. - Drop the github-readme-stats credit from README; all palette data now comes from one upstream source.
67 lines
1.5 KiB
Go
67 lines
1.5 KiB
Go
package card
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// escapeXML replaces the five XML-significant characters so user-controlled
|
|
// strings (bio, repo names) can't break the SVG document or inject markup.
|
|
func escapeXML(s string) string {
|
|
r := strings.NewReplacer(
|
|
"&", "&",
|
|
"<", "<",
|
|
">", ">",
|
|
`"`, """,
|
|
"'", "'",
|
|
)
|
|
return r.Replace(s)
|
|
}
|
|
|
|
// formatInt renders n with thousands separators (e.g. 12345 → "12,345").
|
|
func formatInt(n int) string {
|
|
neg := n < 0
|
|
if neg {
|
|
n = -n
|
|
}
|
|
s := fmt.Sprintf("%d", n)
|
|
if len(s) <= 3 {
|
|
if neg {
|
|
return "-" + s
|
|
}
|
|
return s
|
|
}
|
|
var b strings.Builder
|
|
pre := len(s) % 3
|
|
if pre > 0 {
|
|
b.WriteString(s[:pre])
|
|
if len(s) > pre {
|
|
b.WriteByte(',')
|
|
}
|
|
}
|
|
for i := pre; i < len(s); i += 3 {
|
|
b.WriteString(s[i : i+3])
|
|
if i+3 < len(s) {
|
|
b.WriteByte(',')
|
|
}
|
|
}
|
|
out := b.String()
|
|
if neg {
|
|
return "-" + out
|
|
}
|
|
return out
|
|
}
|
|
|
|
// header returns the opening <svg> tag + background rect + title text.
|
|
func header(width, height int, bg, stroke string, strokeOpacity float64, titleColor, title string) string {
|
|
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="8" fill="%s" stroke="%s" stroke-opacity="%.2f"/>
|
|
<text x="25" y="35" font-size="18" font-weight="600" fill="%s">%s</text>`,
|
|
width, height, width, height,
|
|
width-1, height-1, bg, stroke, strokeOpacity,
|
|
titleColor, escapeXML(title))
|
|
}
|
|
|
|
const footer = `
|
|
</svg>`
|