Commit Graph
113 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
github-actions[bot] 006ff6e34c chore(demo): regenerate gallery 2026-10-07 10:53:05 +00: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
github-actions[bot] 3b157e4215 chore(demo): regenerate gallery 2026-10-07 04:28:35 +00:00
tiennm99 a9f7451526 fix(compose): expose port 8080 so Coolify's proxy can route the web UI 2026-10-07 11:26:33 +07:00
github-actions[bot] cd309f7904 chore(demo): regenerate gallery 2026-10-07 04:25:53 +00:00
tiennm99 9b3bcb451e chore(compose): rename the server token variable to GHGLANCE_TOKEN 2026-10-07 11:23:13 +07:00
tiennm99 ee709597df ci(demo): read the token from the GHGLANCE_TOKEN secret 2026-10-07 11:21:20 +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
github-actions[bot] af420f0db7 chore(demo): regenerate gallery 2026-10-03 06:40:30 +00: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.
v1.8.0 v1
2026-10-03 13:38:15 +07:00
github-actions[bot] 8c1f139990 chore(demo): regenerate gallery 2026-10-03 06:07:59 +00:00
tiennm99 9ab9cfd159 chore(action): list on the Marketplace as ghstats v1.7.1 2026-10-03 13:05:54 +07:00
github-actions[bot] bb360e10aa chore(demo): regenerate gallery 2026-08-27 07:42:28 +00: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.
v1.7.0
2026-08-27 14:39:53 +07:00
github-actions[bot] f8ecc8e98d chore(demo): regenerate gallery 2026-08-13 07:22:21 +00: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.
v1.6.2
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.
v1.6.1
2026-08-13 13:52:38 +07:00
github-actions[bot] 1ce59424b5 chore(demo): regenerate gallery v1.6.0 2026-08-13 06:36:22 +00: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
github-actions[bot] 7832842c5d chore(demo): regenerate gallery 2026-08-13 06:25:25 +00:00
tiennm99 6be18b42b1 chore(demo): render records card and count org repos
The per-theme gallery pages embedded 15 SVGs and skipped records.svg,
which the render step has produced since the card landed, while the
index claimed every card was shown. Pair records with streak to match
the profile README layout at tiennm99/tiennm99.

Pass -include-org-repos so the gallery reflects repos under the author's
orgs.
2026-08-13 13:20:48 +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
github-actions[bot] 4d7de90656 chore(demo): regenerate gallery v1.5.0 2026-05-09 02:49:52 +00:00
tiennm99 b5cea5cf33 docs: plan for records card (260509-0913) 2026-05-09 09:47:38 +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
github-actions[bot] ed15d29d1f chore(demo): regenerate gallery 2026-05-09 02:15:41 +00:00
tiennm99 0cd6ccaa17 claude: allow git push in auto mode 2026-05-09 09:13:32 +07:00
tiennm99 85461bd8c8 fix(card): heatmap month labels skip leading zero-pad cell
The first cell of week 0 can be a zero-time padding slot when
start_of_week differs from the data's first weekday. Zero time
(0001-01-01) reports Day=1, Month=January, leaking a bogus "Jan"
label that overlaps the next real month label.
2026-05-09 09:02:16 +07:00
github-actions[bot] d21e65d401 chore(demo): regenerate gallery v1.4.1 2026-05-02 02:15:05 +00:00
tiennm99 f0c4a87132 fix(contributions): trim last-year series to clean 13-month span
GitHub's contributionCalendar is week-aligned, so a "last year" query on
2026-05-02 returns days back to ~2025-04-27. That extra partial month
inflated aggregateByMonth to 14 buckets and the x-axis stride logic placed
04/26 and 05/26 next to each other (x=290 / x=312), overlapping.

Trim to start-of-(last.Month - 1y) so the chart spans a clean 13 months
ending at the current month. Heatmap path is untouched — it needs the
full week-aligned series.
2026-05-02 09:13:34 +07:00
github-actions[bot] e396c221ff chore(demo): regenerate gallery 2026-05-02 01:50:39 +00:00
github-actions[bot] 0154623ca9 chore(demo): regenerate gallery v1.4.0 2026-04-21 14:21:13 +00: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 762a76fe76 chore(demo): mirror author profile README layout in per-theme pages
Reshape the generator in demo.yml so each theme's README renders one centred
table matching tiennm99/tiennm99's stats table: singletons two-per-row at the
top, streak spans full width, then four last-year/all-time pair rows under a
shared header. dracula updated manually so reviewers see the result before the
demo workflow regenerates the other 64 themes on merge.
2026-04-21 21:18:39 +07:00
github-actions[bot] 583a140c3b chore(demo): regenerate gallery 2026-04-21 13:59:40 +00: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
github-actions[bot] e8d9cc5900 chore(demo): regenerate gallery 2026-04-19 04:51:06 +00:00
tiennm99 0e0e4fc500 fix(card): productive titles render at 15 px for every timezone (#24)
Two cuts together keep the productive-time and productive-weekday
titles at the same 15 px the rest of the gallery uses:

1. utcOffsetLabel is now compact. Integer offsets drop the '.00'
   padding ("UTC+7" vs the old "UTC+7.00") and non-integer offsets use
   the colon form ("UTC+5:30", "UTC+5:45"). Saves 3 chars for the
   common integer case, so "Commits by Hour (last year, UTC+7)" lands
   at 34 chars — inside the 15 px budget.

2. weekdayTitle no longer embeds the UTC label. Day-of-week aggregates
   by whole days; clock precision isn't informative there, and
   dropping it shortens the title to 30 chars so the full 15 px lands
   for every timezone.

Quarter-hour-zone users (Kathmandu UTC+5:45) see the hour title drop
to 14 px — 37 chars still exceeds the budget — but that's a rare
case and only 1 px off.

TestUTCOffsetLabel updated to the new format. TestFitTitleFontSize
pinned the new titles.
v1.3.1
2026-04-19 11:49:40 +07:00
github-actions[bot] eb04446272 chore(demo): regenerate gallery 2026-04-19 04:41:32 +00:00
tiennm99 70bcbd43f2 feat(card): stack heatmap into two halves; unify font-size vocabulary (#23)
## Heatmap — two halves, 8x8 cells

The single-row 53-week layout could never hold square cells larger than
4x4 inside a 340 px card — cramped. Split the year in half (ceil(weeks/2)
on top, floor on bottom) and each half is ~27 weeks wide, freeing the
cells to be 8x8 (4x the area) while keeping comfortable left (30 px)
and right (~67 px) gutters. Grid:

  topPadA = 45, half = 7*9 - 1 = 62 tall
  halfGap = 13
  topPadB = 120, same 62 tall
  grid bottom at y = 182, 18 px frame margin

Year still reads top-to-bottom left-to-right, just with one extra line
break at the midpoint. Dropped the separate Less/More legend — at 8x8
the intensity gradient is self-explanatory, and the removal buys the
vertical space the new layout needs.

Refactored into a helper `renderHeatmapHalf` so the two halves share a
single code path (labels + month markers + cells).

## Font-size vocabulary

Four named constants in svg.go: fontBody=12, fontLabel=11, fontAxis=10,
fontBigNum=28. Every card's existing literals already sit on this
ladder except the heatmap, which was using 9 for weekday/month labels.
Heatmap now routes through fontAxis so the whole gallery shares one
size scale.

Other cards' literals weren't rewritten to reference the constants
(pure churn for no behavior change); the constants give future cards
the vocabulary and the design-guidelines the schema.
v1.3.0
2026-04-19 11:39:50 +07:00
github-actions[bot] 672bfe9d8d chore(demo): regenerate gallery v1.2.4 2026-04-19 04:25:37 +00:00
tiennm99 d59ae71116 fix(card): heatmap cells back to 4x4 squares with side gutters (#22)
The 4x12 rectangular stretch from v1.2.3 read as "weird". Requirement
is square cells + comfortable side padding. Given the 53-week hard
constraint, those two requirements together pick the cell size:

  53 * (size + gap) + leftPad + rightPad = 340

Candidates audited:
  6 x 6, gap 1 → 371 wide, overflows the frame
  5 x 5, gap 1 → 318 wide, only 11 px of total side padding ("very close")
  5 x 5, gap 0 → 265 wide, cells touch (need a stroke to fake a gap)
  4 x 4, gap 1 → 265 wide, 30 + 45 px gutters, real 1 px gaps ✓
  3 x 3, gap 2 → 265 wide, cells become pinhead-sized

4 x 4 with a 1 px gap is the largest square that keeps breathing room
on both sides without any rendering trickery. Grid is 35 px tall;
there's leftover vertical space on the card but short beats "stretched
horizontal bands" visually. Bump topPad from 62 → 70 to offset the
grid slightly down from the title and reduce that apparent emptiness.

Legend swatches also revert to the same cellSize so the Less/More
bar matches the grid visually again.
2026-04-19 11:23:48 +07:00
github-actions[bot] 67dab42ac8 chore(demo): regenerate gallery 2026-04-19 04:16:16 +00:00
tiennm99 7d586eb461 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.
v1.2.3
2026-04-19 11:14:42 +07:00
github-actions[bot] 8497c0de76 chore(demo): regenerate gallery 2026-04-19 04:01:53 +00:00
tiennm99 1e58d4c13a fix(card): donut legend caps at 7 rows (Other inclusive); add heatmap gutter (#20)
Two follow-ups:

1. "Top 7 languages" counts rows the reader sees, not "7 named + Other".
   Revert collapseOther to the original semantic: when the input has more
   than n entries, keep in[:n-1] named and fold the tail into a single
   "Other" row — 7 rows total with topN=7. Input with ≤7 real languages
   still passes through unchanged. Test and adversarial-profile updated
   to pin the cap (6 named + Other, Shell/Kotlin/Java collapse).

2. Contributions heatmap grid was touching the card right edge
   (leftPad 22 + 53*6 = 340 exactly). Shrink cells from 5×5 to 4×4 and
   bump leftPad to 30, giving 45 px of right gutter. Grid no longer
   reads as bleeding into the frame border; weekday label column also
   gets a few more pixels of breathing room on the left.
v1.2.2
2026-04-19 11:00:02 +07:00
github-actions[bot] 083be8d01c chore(demo): regenerate gallery 2026-04-19 03:47:40 +00:00
tiennm99 3dd5e85806 fix(card): show 7 *named* languages + Other, not 6 + Other (#19)
With topN=7 the previous collapseOther kept only the first 6 entries
and added "Other" as the 7th row. A user expecting to see 7 actual
languages in the legend saw six named languages plus "Other" — the
exact complaint just raised about the profile repo's donut.

Flip the semantic: the "top N" slots are reserved for real languages,
and "Other" is an extra row when (and only when) there's a non-zero
tail past the Nth entry. Topologically that means up to 8 legend
rows — still fits the card frame (row 8 text baseline at y=195, card
height 200).

- TestDonutTopSevenPlusOther pins the new contract with a 9-language
  input.
- adversarialProfile in TestCardsFitFrame bumped to 9 languages so
  the stress test exercises the 8-row legend geometry.
- design-guidelines: the donut row re-reads "Up to 7 named languages,
  plus an 'Other' row when the tail is non-zero (8 rows max)".
v1.2.1
2026-04-19 10:46:10 +07:00
tiennm99 059c8b11ad docs: focus each file on users or coworkers, drop unrelated content (#18)
project-roadmap.md went from a 146-line phase-by-phase history to a
48-line focused view: what's planned, what's out of scope. Completed
work is already in git log + GitHub Releases — the doc re-telling it
was the thing most likely to rot and least likely to be read.

project-overview-pdr.md: "Open questions" section dropped its stale
bullet list and now just points at project-roadmap.md (single source of
truth for planned work).

code-standards.md: drop the ".claude/ directory" commit rule — that's
a per-user workflow detail, not a project-level standard. Docs are for
users of the CLI/Action and coworkers of this repo, nothing else.
2026-04-19 10:34:02 +07:00