mirror of
https://github.com/tiennm99/ghstats.git
synced 2026-08-05 18:24:24 +00:00
refactor(card): shrink cards from 500x220 to 340x200
Two ghstats cards side-by-side at 500px overflow GitHub's README column
width (~816px). Matching github-profile-summary-cards' 340x200 lets
consumers place two cards per row cleanly.
- Shared header() rect corner radius 8→6, title font 18→15, title anchor
(25,35)→(20,30).
- All card layouts reworked:
* profile + stats: row padding 70→55 / 24→20, icon 14→12, value
font 13→12, stats right-anchor x=475→320.
* donut_chart: centre (380,120)→(250,110), outerR 70→55, innerR 38→30,
legend y0 70→55, swatch 12→10, topN 6→5.
* productive-time: leftAxis 50→35, topPad 60→45, barGap 2→1.
* contributions: leftPad/rightPad 35→28, topPad 60→45, chartH 120→105.
Visual parity with github-profile-summary-cards; two-per-row layouts in
profile READMEs now fit.
Also: document the one-time GitHub Marketplace publishing step in
deployment-guide (no CLI flag exists — UI checkbox only).
This commit is contained in:
@@ -110,7 +110,12 @@ runs:
|
||||
3. On green, GHCR push + cross-platform binary artifacts happen automatically.
|
||||
4. Docker base images and third-party actions are SHA-pinned (with version
|
||||
comments) so mutable-tag changes upstream can't rewrite a released image.
|
||||
5. Update any public Actions marketplace metadata if the major version changed.
|
||||
5. **Marketplace publishing (one-time per repo):** GitHub only exposes the
|
||||
"Publish this Action to the GitHub Marketplace" toggle on the Release
|
||||
web UI — there is no CLI flag. Open the newly created release at
|
||||
`https://github.com/tiennm99/ghstats/releases/tag/vX.Y.Z/edit`, tick the
|
||||
marketplace checkbox, accept the terms, and re-publish. Subsequent
|
||||
releases inherit marketplace visibility automatically.
|
||||
|
||||
## Rollback
|
||||
|
||||
|
||||
@@ -37,12 +37,12 @@ type monthBucket struct {
|
||||
// contribution cards; only title + data differ.
|
||||
func renderContributions(title string, days []github.DailyContribution, t theme.Theme) []byte {
|
||||
const (
|
||||
width = 500
|
||||
height = 220
|
||||
leftPad = 35
|
||||
rightPad = 35
|
||||
topPad = 60
|
||||
chartH = 120
|
||||
width = 340
|
||||
height = 200
|
||||
leftPad = 28
|
||||
rightPad = 28
|
||||
topPad = 45
|
||||
chartH = 105
|
||||
)
|
||||
chartW := width - leftPad - rightPad
|
||||
|
||||
|
||||
@@ -14,17 +14,17 @@ import (
|
||||
// shown; smaller slices are grouped into "Other".
|
||||
func renderDonutCard(title string, stats []github.LangStat, t theme.Theme) []byte {
|
||||
const (
|
||||
width = 500
|
||||
height = 220
|
||||
topN = 6
|
||||
cx = 380 // donut centre x
|
||||
cy = 120 // donut centre y
|
||||
outerR = 70.0 // donut outer radius
|
||||
innerR = 38.0 // donut hole
|
||||
legendX = 30
|
||||
legendY0 = 70
|
||||
legendDY = 22
|
||||
swatchSz = 12
|
||||
width = 340
|
||||
height = 200
|
||||
topN = 5
|
||||
cx = 250 // donut centre x
|
||||
cy = 110 // donut centre y
|
||||
outerR = 55.0 // donut outer radius
|
||||
innerR = 30.0 // donut hole
|
||||
legendX = 20
|
||||
legendY0 = 55
|
||||
legendDY = 20
|
||||
swatchSz = 10
|
||||
)
|
||||
|
||||
stats = collapseOther(stats, topN)
|
||||
@@ -50,10 +50,10 @@ func renderDonutCard(title string, stats []github.LangStat, t theme.Theme) []byt
|
||||
y := legendY0 + i*legendDY
|
||||
fmt.Fprintf(&b, `
|
||||
<rect x="%d" y="%d" width="%d" height="%d" fill="%s" stroke="%s" stroke-width="1"/>
|
||||
<text x="%d" y="%d" font-size="13" fill="%s">%s %.2f%%</text>`,
|
||||
<text x="%d" y="%d" font-size="11" fill="%s">%s %.2f%%</text>`,
|
||||
legendX, y-swatchSz+2, swatchSz, swatchSz,
|
||||
colorOrAccent(s.Color, t.Accent), t.Background,
|
||||
legendX+swatchSz+8, y, t.Text,
|
||||
legendX+swatchSz+6, y, t.Text,
|
||||
escapeXML(s.Name), pct)
|
||||
}
|
||||
|
||||
|
||||
@@ -40,13 +40,13 @@ var xTickHours = [...]int{0, 6, 12, 18, 23}
|
||||
// hover titles. Shared by the last-year and all-time productive-time cards.
|
||||
func renderProductiveTime(title string, data [24]int, t theme.Theme) []byte {
|
||||
const (
|
||||
width = 500
|
||||
height = 220
|
||||
leftAxis = 50
|
||||
rightPad = 25
|
||||
topPad = 60
|
||||
width = 340
|
||||
height = 200
|
||||
leftAxis = 35
|
||||
rightPad = 15
|
||||
topPad = 45
|
||||
chartH = 110
|
||||
barGap = 2
|
||||
barGap = 1
|
||||
)
|
||||
chartW := width - leftAxis - rightPad
|
||||
barW := float64(chartW-barGap*23) / 24.0
|
||||
|
||||
@@ -21,12 +21,12 @@ type profileRow struct {
|
||||
|
||||
func (profileCard) SVG(p *github.Profile, t theme.Theme) ([]byte, error) {
|
||||
const (
|
||||
width = 500
|
||||
height = 220
|
||||
rowX = 25
|
||||
rowY0 = 70
|
||||
rowDY = 24
|
||||
iconSize = 14
|
||||
width = 340
|
||||
height = 200
|
||||
rowX = 20
|
||||
rowY0 = 60
|
||||
rowDY = 20
|
||||
iconSize = 12
|
||||
maxRows = 7
|
||||
)
|
||||
|
||||
@@ -46,9 +46,9 @@ func (profileCard) SVG(p *github.Profile, t theme.Theme) ([]byte, error) {
|
||||
// icon glyph: translate to row position, scale down, fill with muted.
|
||||
fmt.Fprintf(&b, `
|
||||
<g transform="translate(%d,%.2f) scale(%.3f)" fill="%s">%s</g>
|
||||
<text x="%d" y="%d" font-size="13" fill="%s">%s</text>`,
|
||||
<text x="%d" y="%d" font-size="12" fill="%s">%s</text>`,
|
||||
rowX, float64(y-iconSize+2), scale, t.Muted, r.icon,
|
||||
rowX+iconSize+10, y, t.Text, escapeXML(r.value))
|
||||
rowX+iconSize+8, y, t.Text, escapeXML(r.value))
|
||||
}
|
||||
|
||||
b.WriteString(footer)
|
||||
|
||||
+10
-10
@@ -21,13 +21,13 @@ type statRow struct {
|
||||
|
||||
func (statsCard) SVG(p *github.Profile, t theme.Theme) ([]byte, error) {
|
||||
const (
|
||||
width = 500
|
||||
height = 220
|
||||
rowX = 25
|
||||
rowY0 = 70
|
||||
rowDY = 24
|
||||
iconSize = 14
|
||||
valueX = 475 // right-aligned x anchor
|
||||
width = 340
|
||||
height = 200
|
||||
rowX = 20
|
||||
rowY0 = 55
|
||||
rowDY = 20
|
||||
iconSize = 12
|
||||
valueX = 320 // right-aligned x anchor
|
||||
)
|
||||
|
||||
rows := []statRow{
|
||||
@@ -48,10 +48,10 @@ func (statsCard) SVG(p *github.Profile, t theme.Theme) ([]byte, error) {
|
||||
y := rowY0 + i*rowDY
|
||||
fmt.Fprintf(&b, `
|
||||
<g transform="translate(%d,%.2f) scale(%.3f)" fill="%s">%s</g>
|
||||
<text x="%d" y="%d" font-size="13" fill="%s">%s</text>
|
||||
<text x="%d" y="%d" font-size="13" font-weight="600" fill="%s" text-anchor="end">%s</text>`,
|
||||
<text x="%d" y="%d" font-size="12" fill="%s">%s</text>
|
||||
<text x="%d" y="%d" font-size="12" font-weight="600" fill="%s" text-anchor="end">%s</text>`,
|
||||
rowX, float64(y-iconSize+2), scale, t.Muted, r.icon,
|
||||
rowX+iconSize+10, y, t.Text, escapeXML(r.label),
|
||||
rowX+iconSize+8, y, t.Text, escapeXML(r.label),
|
||||
valueX, y, t.Accent, escapeXML(r.value))
|
||||
}
|
||||
|
||||
|
||||
@@ -55,8 +55,8 @@ func formatInt(n int) string {
|
||||
// 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>`,
|
||||
<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>`,
|
||||
width, height, width, height,
|
||||
width-1, height-1, bg, stroke, strokeOpacity,
|
||||
titleColor, escapeXML(title))
|
||||
|
||||
Reference in New Issue
Block a user