31 Commits
Author SHA1 Message Date
tiennm99 537c87c458 feat(web): show cards inline for quick viewing and accept the visitor's own token
The web UI is for a quick look at a profile, not for hosting embeddable
images. Cards are inlined into /u/<user> as data: images, the per-card
route is gone, and the page no longer offers copy links or Markdown
snippets. The page itself stays shareable.

Visitors can again paste their own token, with a button that opens
GitHub's new-token page with the needed scopes ticked. A pasted token
takes precedence over sign-in, follows the same ownership, privacy and
cooldown rules, is never stored or logged, and is not revoked. The server
still uses no token of its own.
2026-10-07 19:15:15 +07:00
tiennm99 2def27f49a feat(web): require GitHub sign-in to generate cards
The web UI now runs every job on an OAuth token from "Sign in with
GitHub". The ticked options decide the requested scopes (read:user, plus
repo for private repos, plus read:org for org repos); a grant wider than
requested is refused. The token lives only on the job and is revoked when
the job ends, on every path including shutdown.

The server no longer holds a GitHub token of its own, and the pasted-token
field and /generate are gone. -serve requires -oauth-client-id,
-oauth-client-secret and -public-url (GHGLANCE_OAUTH_* and
GHGLANCE_PUBLIC_URL), and compose.yml requires them too. The CLI and the
Action keep -token unchanged.
2026-10-07 17:50:33 +07:00
tiennm99 9b3bcb451e chore(compose): rename the server token variable to GHGLANCE_TOKEN 2026-10-07 11:23:13 +07:00
tiennm99 ad775adffa feat(web): add web UI and Coolify compose deployment
`ghglance -serve :8080` runs a web UI from the same binary: submit a
username, a background job renders every card in every theme, and
/u/<user> shows them again with a theme picker and embed URLs.

- Cards are stored on disk with atomic symlink publishing and deleted
  after -retention (default 24h); -cooldown limits token-less regeneration.
- Token-less jobs use a public-only server token with private and org
  scope forced off; a submitter's own token is used for that job only and
  never stored or logged. The form links to GitHub's new-token page with
  the needed scopes pre-ticked.
- The CLI and web share one fetch helper; CLI output is unchanged.
- compose.yml deploys to Coolify with a data volume and /healthz check.
2026-10-07 11:08:07 +07:00
tiennm99 3fc8a59de6 chore: rename the project to ghglance
The Marketplace rejects ghstats as an action name, so the repo, Go module,
binary, and action share the name ghglance instead. The demo workflow keeps
reading the existing GHSTATS_TOKEN repo secret.
2026-10-03 13:38:15 +07:00
tiennm99 9ab9cfd159 chore(action): list on the Marketplace as ghstats 2026-10-03 13:05:54 +07:00
tiennm99 333f1aa12a feat(card): show seven repos on the top-starred card
Retune row spacing to the stats/records rhythm so seven rows fit the
shared 340x200 frame.
2026-08-27 14:39:53 +07:00
tiennm99 7c081ac591 feat: let commits-per-repo 0 mean every commit
The pagination guard broke on seen >= maxPerRepo, so a cap of 0 stopped
before the first page and rendered empty productive-time,
productive-weekday and most-commit-language cards. That inverted the
meaning 0 carries for top-repos, where it already means unlimited.

Treat a cap of 0 or less as no cap. Verified against a real repo: a cap
of 100 still stops at 100, while 500 and 0 both walk the full 382-commit
history.
2026-08-13 14:19:41 +07:00
tiennm99 9e0387f307 docs: show include_org_repos in the runnable examples
The input reached both reference tables but neither example, so anyone
copying the workflow YAML or the CLI invocation would not learn the knob
exists. Add it to the Action example with a note on when to flip it, and
add a second CLI example alongside the existing one.

Add CLAUDE.md recording the five surfaces a config knob has to reach —
flag, action input, entrypoint translation, README tables *and*
examples, architecture doc — since the examples are the step that got
missed here. Also note the demo gallery's hardcoded card list and the
GitHub API ceilings the fetch pipeline works around.
2026-08-13 13:52:38 +07:00
tiennm99 2c3667d28c fix(github): split a saturated quarter by month to recover dropped repos
Quarterly windows were not enough for the busiest quarter: a live run
warned that 2026-04-01..2026-06-30 still came back at the 100-repo
ceiling, so its seed list was truncated and those repos never got
probed for commit history.

Re-ask a saturated quarter month by month and merge only the repo
lists. The quarter has already contributed its calendar days and commit
totals, so the month passes skip that fold; verified by forcing the
ceiling down to 20 and confirming all six calendar-derived cards stay
byte-identical. Warn only when a month itself saturates, since there is
no finer window left to ask for.
2026-08-13 13:34:08 +07:00
tiennm99 30ba70d8ea feat: count org repos and fix contribution-window truncation
Split each contribution year into quarterly windows.
commitContributionsByRepository caps at 100 repos per query and drops
the remainder without signalling it, which a prolific year hits: 2026
returned exactly 100 where the quarterly union returns 108. Warn when a
window still comes back at the ceiling instead of reporting a complete
list.

Add -include-org-repos / include_org_repos so org-owned repos the user
administers count toward stars, repo count, repos-per-language and
top-starred. Off by default, since enabling it moves those totals for
every existing caller. Commit-driven cards already covered org repos
through the seed list. Org repos carrying only read or write access are
dropped via viewerPermission.

Rename Profile.PublicRepos to RepoCount and drop "public" from the card
label: the count tracks the fetch filters and includes private repos
whenever the token can see them.
2026-08-13 13:20:41 +07:00
tiennm99 01c812158f feat(card): add records card with six lifetime personal-bests
New records.svg surfaces extremes the existing aggregate cards don't:
peak day (count + date), peak month, first contribution, lifetime
active days, account age in fractional years, and distinct languages
used. All values derive from data already fetched — no new GraphQL
queries. Three new octicon paths (calendar, history, globe) added to
icons.go; existing icons reused where they fit.
2026-05-09 09:47:38 +07:00
tiennm99 d39f41e901 chore(readme): mirror profile layout in preview, demo uses Monday start
- Project README preview section collapses the two side-by-side tables into
  one centred table matching tiennm99/tiennm99's stats table (row order
  identical to the per-theme demo pages).
- demo.yml now renders with -start-of-week monday so the gallery showcases
  the new flag and stays visually consistent with the author profile.
2026-04-21 21:18:39 +07:00
tiennm99 92387eddf5 feat(card): configurable start of week (#25)
* feat(card): configurable start of week for heatmap + weekday cards

New -start-of-week CLI flag (and start_of_week action input) rotates the
contribution-heatmap rows and the productive-weekday bars so users whose
calendars start on Monday (or any other day) get matching output. Default
stays Sunday to preserve existing renders.

* docs: note start-of-week in design-guidelines, codebase-summary, deployment-guide

- design-guidelines: heatmap row order + productive-weekday bar order now derive from Profile.WeekStart
- codebase-summary: list new weekday_start_test.go cases + TestParseWeekday
- deployment-guide: mention start_of_week as an optional action input in the workflow template
2026-04-21 20:57:21 +07:00
tiennm99 dab7fe0371 docs(readme): embed live dracula preview at the top (#16)
The README previously only LINKED to the demo gallery. Pull the dracula
theme inline so a reader can scan every card type + the LY/AT pairs
without leaving the page, and use the demo gallery for the other 64
themes. SVGs resolve against demo/dracula/ which the demo workflow keeps
fresh on every push to main.
2026-04-19 10:24:34 +07:00
tiennm99 9c21144ac4 chore: drop tracked output/ sample in favor of CI-built demo gallery (#4)
The dracula sample under output/dracula/ was a reference render for the
README. Now that demo/ auto-generates every card × every theme on each
push, the sample is redundant — and keeping it trackers means every
behavioral tweak shows up as a diff in committed SVGs.

- Delete output/dracula/*.svg.
- .gitignore: collapse `output/*` + `!output/dracula/` to a plain `output/`.
- demo.yml: drop `output/**` from paths-ignore (no longer part of any push).
- README / docs: point readers at demo/ instead of output/dracula/.
2026-04-19 09:03:44 +07:00
tiennm99 fd4c70e53e feat(card): add S-tier cards — heatmap, streak, by-year, weekday, top-starred (#3)
Five new cards, all derived from data FetchProductive / FetchProfile already
pull, so zero additional API calls:

- contributions-heatmap: 7×53 calendar grid with a 5-bucket intensity ramp
  mixed from each theme's Background→Accent so palettes with no dedicated
  heat ramp still render sensibly.
- streak: current streak, longest streak with date ranges, active/total days.
- contributions-by-year: one bar per active year, peak year highlighted.
- productive-weekday + -all-time: 7-bar day-of-week mirror of the hour-of-day
  cards; FetchProductive now also fills Weekday / WeekdayAllTime histograms
  during the same commit-history pass.
- top-starred-repos: top 5 owned non-fork repos by stargazer count; threads
  Stars through RepoInfo.

Card count: 9 → 14. Registered in allCards grouped by recency (last-year
block, then all-time block). Render test extended to cover all new files
and realistic daily-series inputs.
2026-04-19 08:58:59 +07:00
tiennm99 9dffc2234b docs: link demo gallery from readme and correct stale theme count (#2)
- README points to demo/ as an auto-generated theme preview.
- Bump "60+ / 61 themes" references to the actual 65.
- Record Phase 7.5 in roadmap and list demo.yml + demo/ in layout.
2026-04-19 08:45:40 +07:00
tiennm99 f90a5baec5 docs(readme): add tiennm99/tiennm99 as 'In the wild' example 2026-04-19 00:12:59 +07:00
tiennm99 729f1a0611 Revert "chore: rename module + references to tiennm99/ghstats-cards"
This reverts commit accc4b6. Repo stays as tiennm99/ghstats; the
Marketplace display name ("ghstats-cards" in action.yml) is the only
place the new name remains, since that field requires uniqueness on
the Marketplace.

plans/reports/* were added alongside the rename in the same commit
and are preserved by not deleting them in this revert.
2026-04-18 23:51:33 +07:00
tiennm99 accc4b62b2 chore: rename module + references to tiennm99/ghstats-cards
Matches the Marketplace name; repo is being renamed in lockstep.

- go.mod module path: github.com/tiennm99/ghstats →
  github.com/tiennm99/ghstats-cards
- Import paths across every .go file updated.
- README badges, install snippets, and the 'go install' line point
  to the new URL/path.
- docs/deployment-guide.md workflow template, Docker image path, and
  release edit URL updated.

Breaking for consumers pinned to the old URL; they need to swap
tiennm99/ghstats → tiennm99/ghstats-cards in workflows and switch
Docker pulls to ghcr.io/tiennm99/ghstats-cards. GitHub's HTTP
redirect covers git clones but GHCR does NOT redirect — users must
update image URIs manually.
2026-04-18 23:40:29 +07:00
tiennm99 289d00c227 docs(readme): link back to Marketplace listing + add badges
- Marketplace / Release / License badges at the top.
- Explicit Marketplace + source links under the intro so visitors
  arriving from the Marketplace page can jump to the repo and vice
  versa.

Also set repo topics via `gh repo edit`: ghstats, ghstats-cards,
github-action, github-marketplace, profile-readme, profile-stats,
stats-cards, svg-cards, go, github-stats.
2026-04-18 23:36:28 +07:00
tiennm99 d3fc27f33f feat: flip include-forks / include-private defaults to true
Most users reach for ghstats to show their real activity. Making the
two visibility toggles opt-out rather than opt-in gives accurate stats
out of the box.

- -include-forks        false → true
- -include-private      false → true
- include_forks input   false → true
- include_private input false → true

Private-repo data silently no-ops when the token lacks repo scope, so
the default is safe for default github.token runs — it just won't
surface more than read:user already allows. Users who want the old
public-only behavior pass -include-private=false or set the Action
input to "false".
2026-04-18 22:20:46 +07:00
tiennm99 a02d27511a refactor(card): drop numeric prefix from output filenames
Files now land at output/<theme>/profile-details.svg etc., without
leading 0-8 prefixes. README authors embed cards by name, so the
lexicographic-sort rationale for the prefix no longer applies.

- All Filename() methods + the allCards ordering comment updated.
- Tests updated to expect the 9 unnumbered filenames.
- README, deployment-guide, codebase-summary, roadmap references
  refreshed.
- Dracula sample SVGs regenerated under new names.
2026-04-18 22:20:09 +07:00
tiennm99 0168fd1f56 docs(readme): refresh for 9-card layout, seed sampling, visibility flags 2026-04-18 22:10:00 +07:00
tiennm99 9c81926c06 refactor(card): redesign profile details with octicon labels
- Title becomes "login (Name)" (or just "login") instead of
  "Name's Profile Details".
- Row labels replaced with Octicon glyphs (repos, company, location,
  link, clock, people) rendered in the theme's muted color.
- Joined and account-age rows merge into a single "YYYY-MM-DD (N years
  ago)" line; years round down to whole years with month/day fallbacks.
- Drop profile-summary-for-github from README credits — no code from
  that project is used. github-profile-summary-cards is the only direct
  reference.
2026-04-18 19:43:32 +07:00
tiennm99 1c4244722c refactor(theme): port full github-profile-summary-cards palette
- 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.
2026-04-18 19:13:17 +07:00
tiennm99 7eb83be9de refactor: split language card into repos-per-language and most-commit-language
Align card set with github-profile-summary-cards' 5-card layout:

  0-profile-details.svg       (unchanged)
  1-repos-per-language.svg    (new) owned repos grouped by primary language
  2-most-commit-language.svg  (new) last-year commits attributed to each repo's primary language
  3-stats.svg                 (renumbered)
  4-productive-time.svg       (renumbered)

- FetchProductive now fills p.CommitsByLanguage from the same commit history
  it uses for the heatmap, so no extra API calls are introduced.
- TopRepos carries primary language so productive-time can aggregate by lang.
- LangStat.Bytes renamed to Value (repo count or commit count, context-dependent).
- Shared bar+legend renderer extracted to language_bar.go.
- Ignore generated output/ directory.
2026-04-18 18:57:42 +07:00
tiennm99 ff4975fae2 feat: implement profile summary cards with GraphQL fetch and Action wrapper
- Add GraphQL client fetching profile, stats, language aggregation, and
  per-repo commit histograms for the productive-time heatmap.
- Render real SVG cards (profile details, top languages, stats grid,
  weekday×hour heatmap) with XML escaping and thousands-formatted numbers.
- Expand theme palette to 30 built-ins ported from github-readme-stats;
  add -list-themes, multi-theme rendering, and 'all' shortcut.
- Package as Docker-based GitHub Action (action.yml, Dockerfile,
  entrypoint.sh) with optional auto-commit of generated cards.
- Release workflow publishes GHCR image and cross-platform binaries on
  v* tags.
- Unit tests cover rendering, XML escape, number formatting, language sort.
2026-04-18 18:39:51 +07:00
tiennm99 a91d964a1d feat: scaffold ghstats — Go CLI for GitHub profile SVG cards 2026-04-18 18:22:20 +07:00
tiennm99 27b822054a Initial commit 2026-04-18 18:18:23 +07:00