tiennm99 dedce3da73 fix(card): make heatmap cells 3× taller — width was tight, height had room (#21)
Cells were 4 × 4 with 1 px gap: the grid used 35 of 140 available
vertical pixels (25 %) and ~80 px of dead space below. Widening the
cells isn't an option — 53 weeks already consume every horizontal
pixel past the weekday-label gutter. So stretch vertically instead:

  cellW = 4 (unchanged)  cellH = 12  cellGap = 1

Grid footprint is now 265 × 91 px inside a 340 × 200 frame, with a 32
px gap below the grid for the legend. Each weekday reads as a distinct
horizontal band instead of a cramped postage-stamp row.

Legend keeps 8 × 8 SQUARE swatches so the "Less ▢▢▢▢▢ More" row is
still recognisable as an intensity legend rather than a stretched echo
of the data cells. Label baseline offset switches from `cellSize - 1`
to `cellH - 3` so "Mon"/"Wed"/"Fri" sit visually centred in the taller
rows.
2026-04-19 11:14:42 +07:00
2026-04-19 04:01:53 +00:00
2026-04-18 18:18:23 +07:00

ghstats

Generate SVG cards summarizing a GitHub user's profile — written in Go.

Marketplace Release License

ghstats is a single-binary CLI (and a GitHub Action wrapping it) that fetches data for a GitHub user and writes a themed set of SVGs you can embed in your profile README.

Marketplace listing: ghstats-cards · Source: tiennm99/ghstats

Cards rendered:

# Card What it shows
0 Profile details Login (Name) title + Octicon-labelled rows for company, location, link, join date (with age), followers/following, public repos
1 Repos per language Donut + legend: how many owned non-fork repos use each language as primary
2 Most commit language (last year) Donut + legend: last-year commits byte-weighted across each repo's language breakdown
3 Stats Star, commit (lifetime + last-year), PR, issue, PR-review, contributed-to totals
4 Productive time (last year) 24-hour bar chart with axes, title includes UTC±N.NN
5 Productive weekday (last year) 7-bar day-of-week chart, peak day highlighted
6 Contributions (last year) Smooth monthly area chart, Y-axis mirrored both sides, mm/yy labels
7 Contributions heatmap Classic 7×53 calendar grid with theme-derived intensity ramp and legend
8 Top starred repos Top 5 owned non-fork repos by , language dot + proportional bar
9 Streak Current streak, longest streak, active days / total days with date ranges
10 Most commit language (all time) Same as #2 but over lifetime commits
11 Productive time (all time) Same as #4 but over lifetime commits
12 Productive weekday (all time) Same as #5 but over lifetime commits
13 Contributions (all time) Area chart across every active year, auto-thinned x-axis labels
14 Contributions by year One bar per active year, peak year highlighted

Preview — dracula theme

Live render against the author's profile, committed by .github/workflows/demo.yml on every push to main. Other 64 themes in the demo gallery.

profile details stats
streak top starred repos
repos per language contributions heatmap
contributions by year

Last year vs all time

Last yearAll time
most commit language last year most commit language all time
productive time last year productive time all time
productive weekday last year productive weekday all time
contributions last year contributions all time

In the wild

  • tiennm99/tiennm99 — author's profile README, refreshed daily via tiennm99/ghstats@v1. Two-per-row layout, dracula theme.

Drop this in .github/workflows/ghstats.yml in your profile repo (the one named after your username):

name: ghstats

on:
  schedule:
    - cron: "0 0 * * *" # daily
  workflow_dispatch:

permissions:
  contents: write

jobs:
  cards:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: tiennm99/ghstats@v1
        with:
          user: ${{ github.repository_owner }}
          token: ${{ secrets.GHSTATS_TOKEN }}   # classic PAT with read:user + repo
          themes: dracula,github_dark,tokyonight
          tz: Asia/Saigon
          include_forks: "true"
          include_private: "true"
          commit_changes: "true"

Then embed the cards in your README.md:

![profile](./output/dracula/profile-details.svg)
![repos-per-language](./output/dracula/repos-per-language.svg)
![most-commit-language](./output/dracula/most-commit-language.svg)
![stats](./output/dracula/stats.svg)
![productive-time](./output/dracula/productive-time.svg)
![productive-weekday](./output/dracula/productive-weekday.svg)
![contributions](./output/dracula/contributions.svg)
![contributions-heatmap](./output/dracula/contributions-heatmap.svg)
![top-starred-repos](./output/dracula/top-starred-repos.svg)
![streak](./output/dracula/streak.svg)
![most-commit-language-all-time](./output/dracula/most-commit-language-all-time.svg)
![productive-time-all-time](./output/dracula/productive-time-all-time.svg)
![productive-weekday-all-time](./output/dracula/productive-weekday-all-time.svg)
![contributions-all-time](./output/dracula/contributions-all-time.svg)
![contributions-by-year](./output/dracula/contributions-by-year.svg)

Action inputs

Input Default Description
user GitHub username (required)
token ${{ github.token }} PAT with read:user + repo for private repo stats
out output Output directory
themes dracula Comma-separated theme ids, or all
tz UTC IANA tz for the productive-time card (e.g. Asia/Saigon)
top_repos 0 Optional cap on seed repos probed for commit history (0 = unlimited)
commits_per_repo 500 Max commits sampled per repo (covers last-year and all-time aggregates)
include_forks true Include forked repos in stats and commit probing
include_private true Include private repos (requires PAT with repo scope; silently no-op otherwise)
commit_changes false Commit generated cards back to the repo
commit_message chore: update ghstats cards Commit message
commit_branch (current ref) Target branch for auto-commit
author_name github-actions[bot] Commit author
author_email …@users.noreply.github.com Commit email

Use as a CLI

go install github.com/tiennm99/ghstats@latest

Or build from source:

git clone https://github.com/tiennm99/ghstats
cd ghstats
go build -o ghstats .

Then:

export GITHUB_TOKEN=ghp_xxx
ghstats -user tiennm99 -themes dracula,github_dark -tz Asia/Saigon -out output
Flag Default Description
-user (required) GitHub username
-token $GITHUB_TOKEN Personal access token
-out output Output directory (<out>/<theme>/…svg)
-themes dracula Comma-separated theme ids, or all
-tz Local IANA timezone for productive-time cards
-top-repos 0 Optional cap on seed repos probed (0 = unlimited)
-commits-per-repo 500 Max commits sampled per repo
-include-forks true Include forked repos in the stats
-include-private true Include private repos (requires repo PAT scope; silently no-op otherwise)
-list-themes Print available theme ids and exit

How attribution works

Repo sampling uses a seed list built from contributionsCollection.commitContributionsByRepository, unioned across every active contribution year. This catches every repo you've committed in — not just your top-starred ones.

Commit-to-language is byte-weighted: each commit credits every language in the repo, proportional to linguist's byte share. A commit to a 60% Go / 40% Python repo adds 0.6 to Go and 0.4 to Python, regardless of which file was touched. Caveats:

  • Linguist excludes prose (Markdown, AsciiDoc, reST) from byte counts, so heavily-Markdown repos skew toward whatever small code fraction linguist did detect.
  • For per-file accuracy, a future -accurate-languages mode is planned (per-commit REST + go-enry).

Cost per run (current defaults, typical user):

  • ~1 profile query + ~1 query per active year + ~50 commit-history pages ≈ 50-70 GraphQL calls.
  • Zero REST calls. Well under the 5000 points/hr budget.

Themes

Run ghstats -list-themes for the full list (65 themes ported from github-profile-summary-cards). Built-ins include default, dark, dracula, github, github_dark, tokyonight, onedark, nord_dark, nord_bright, gruvbox, radical, synthwave, monokai, solarized, solarized_dark, transparent, and more. Preview every one against real profile data in the demo gallery.

Output

output/
  dracula/
    profile-details.svg
    repos-per-language.svg
    most-commit-language.svg
    stats.svg
    productive-time.svg
    productive-weekday.svg
    contributions.svg
    contributions-heatmap.svg
    top-starred-repos.svg
    streak.svg
    most-commit-language-all-time.svg
    productive-time-all-time.svg
    productive-weekday-all-time.svg
    contributions-all-time.svg
    contributions-by-year.svg

output/ is entirely gitignored — it's regenerated on each run. For a reference render of every card in every theme, see the CI-built demo/ gallery instead.

Tokens & permissions

The default ${{ github.token }} can read public user data but will not see your private-repo commits. For accurate stats, create a classic personal access token with read:user and repo, save it as a repo secret (e.g. GHSTATS_TOKEN), and pass it via the token input. include_private defaults to true so those commits are counted automatically once the token has repo scope; pass include_private: "false" if you want to keep private work out of the rendered cards even when the token can see it.

Credits & inspiration

License

Apache-2.0 — see LICENSE.

S
Description
Generate SVG cards summarizing a GitHub user's profile — written in Go. Inspired by github-profile-summary-cards and profile-summary-for-github.
Readme Apache-2.0 7.1 MiB
Languages
Go 98.3%
Shell 1.3%
Dockerfile 0.4%