@shiinasaku/github-card 4.1.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +182 -98
- package/dist/card.d.ts +2 -15
- package/dist/lib.d.ts +1 -1
- package/dist/lib.js +3951 -380
- package/dist/types.d.ts +8 -3
- package/dist/utils/font.d.ts +3 -1
- package/dist/utils/format.d.ts +7 -0
- package/dist/utils/icons.d.ts +3 -1
- package/dist/utils/index.d.ts +5 -6
- package/dist/utils/languages.d.ts +0 -1
- package/package.json +16 -19
- package/src/card.ts +392 -467
- package/src/gallery.ts +37 -0
- package/src/github.ts +180 -78
- package/src/index.ts +91 -167
- package/src/lib.ts +1 -1
- package/src/types.ts +8 -3
- package/src/utils/font.ts +4 -2
- package/src/utils/format.ts +62 -22
- package/src/utils/github-client.ts +43 -133
- package/src/utils/icons.ts +31 -10
- package/src/utils/index.ts +5 -6
- package/src/utils/languages.ts +1 -1
- package/fonts/Roboto-subset.woff2 +0 -0
- package/src/env.d.ts +0 -4
- package/src/input.css +0 -1
- package/src/tailwind.css +0 -3837
package/README.md
CHANGED
|
@@ -1,46 +1,111 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-
<img src="assets/logo.svg" alt="GitHub Card" width="
|
|
3
|
+
<img src="assets/logo.svg" alt="GitHub Card" width="520" />
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
### Beautiful, blazing-fast GitHub profile cards
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
Drop one line in your README — get a live, auto-updating stats card.
|
|
7
|
+
Live SVG cards for your README — stars, commits, languages, all auto-updating. One line, zero config, edge-cached.
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
<br/>
|
|
10
|
+
|
|
11
|
+
[](https://www.npmjs.com/package/@shiinasaku/github-card)
|
|
12
|
+
[](https://bun.sh)
|
|
13
|
+
[](https://next.elysiajs.com)
|
|
12
14
|
[](https://www.typescriptlang.org)
|
|
13
15
|
[](LICENSE)
|
|
14
16
|
[](https://vercel.com/new/clone?repository-url=https://github.com/ShiinaSaku/Github-Card)
|
|
15
17
|
|
|
16
|
-
<
|
|
18
|
+
<a href="https://card.shiina.xyz/card/shiinasaku?theme=shiina">
|
|
19
|
+
<img src="https://card.shiina.xyz/card/shiinasaku?theme=shiina" alt="Live demo — shiinasaku" width="520" />
|
|
20
|
+
</a>
|
|
21
|
+
|
|
22
|
+
<sub>Live instance · <code>shiinasaku</code> · theme <code>shiina</code> — updates itself as you code</sub>
|
|
17
23
|
|
|
18
24
|
</div>
|
|
19
25
|
|
|
20
26
|
---
|
|
21
27
|
|
|
28
|
+
### ✨ What's new in 4.2
|
|
29
|
+
|
|
30
|
+
> System-font only · no shipped `.woff2` · crisper text (`geometricPrecision`, tabular numbers) · new 24 px icons (MingCute / Huge / Tabler) · airy stat stack with 7 px gaps, no boxes · default `affiliations=owner` — org stars excluded unless `?affiliations=affiliated` · fully typed `github.ts` & `github-client`, cache v9.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
22
34
|
## Quickstart
|
|
23
35
|
|
|
24
|
-
Paste
|
|
36
|
+
Paste into your profile README — replace `shiinasaku` with your username:
|
|
25
37
|
|
|
26
38
|
```md
|
|
27
39
|
[](https://github.com/shiinasaku)
|
|
28
40
|
```
|
|
29
41
|
|
|
30
|
-
That's it. No sign-up, no build
|
|
42
|
+
That's it. No sign-up, no build — the card refreshes automatically as cached data expires.
|
|
43
|
+
|
|
44
|
+
<details>
|
|
45
|
+
<summary><b>Self-host / local dev</b></summary>
|
|
46
|
+
|
|
47
|
+
Requires Bun 1.4.4 or newer. The API uses Elysia 2 RC with pinned framework and plugin versions.
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
bun install
|
|
51
|
+
cp .env.example .env # add GITHUB_TOKEN
|
|
52
|
+
bun dev # http://localhost:3000
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
| Command | Action |
|
|
56
|
+
| ------------------- | -------------------- |
|
|
57
|
+
| `bun test` | Run tests |
|
|
58
|
+
| `bun run typecheck` | Native `bun check` |
|
|
59
|
+
| `bun run fmt` | Bun native formatter |
|
|
60
|
+
| `bun run fmt:check` | Check formatting |
|
|
61
|
+
|
|
62
|
+
Formatting uses Bun's built-in `bun fmt`; no separate formatter or linter is installed.
|
|
63
|
+
Bun 1.4.4 does not provide a built-in `bun lint` command. `bun check` enforces strict typing and
|
|
64
|
+
unused-symbol checks. TypeScript is retained only to emit the library’s `.d.ts` files during builds.
|
|
65
|
+
|
|
66
|
+
`src/index.ts` serves as the entrypoint for Vercel and local development, starting a listener
|
|
67
|
+
only when run directly. The rendering library is unchanged.
|
|
68
|
+
|
|
69
|
+
</details>
|
|
70
|
+
|
|
71
|
+
---
|
|
31
72
|
|
|
32
73
|
## Why GitHub Card?
|
|
33
74
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
-
|
|
75
|
+
<table>
|
|
76
|
+
<tr>
|
|
77
|
+
<td width="50%">
|
|
78
|
+
|
|
79
|
+
**One line, zero config**<br/>
|
|
80
|
+
An `<img>` tag is the entire integration. No OAuth, no action.
|
|
81
|
+
|
|
82
|
+
**Edge-fast**<br/>
|
|
83
|
+
L1 memory + L2 Redis SWR, `s-maxage=1800`, `stale-while-revalidate`, `ETag`/`304`. Warm hits render in microseconds.
|
|
84
|
+
|
|
85
|
+
**Retina avatars**<br/>
|
|
86
|
+
Profile images inlined as Base64 at 2×, clipped + ringed in pure SVG.
|
|
87
|
+
|
|
88
|
+
</td>
|
|
89
|
+
<td width="50%">
|
|
90
|
+
|
|
91
|
+
**Adaptive layout**<br/>
|
|
92
|
+
Width, wrapping, and legends measured with per-glyph metrics — nothing ever overlaps or clips.
|
|
93
|
+
|
|
94
|
+
**Users _and_ orgs**<br/>
|
|
95
|
+
`GET /card/:login` works for users and organizations.
|
|
96
|
+
|
|
97
|
+
**Glassmorphic depth**<br/>
|
|
98
|
+
Radial + linear ambient gradients track your accent for volumetric light at zero runtime cost.
|
|
99
|
+
|
|
100
|
+
</td>
|
|
101
|
+
</tr>
|
|
102
|
+
</table>
|
|
103
|
+
|
|
104
|
+
OpenAPI docs live at [`/openapi`](https://card.shiina.xyz/openapi). Pure ESM lib, zero server dep for `renderCard`.
|
|
105
|
+
|
|
106
|
+
---
|
|
42
107
|
|
|
43
|
-
## Themes
|
|
108
|
+
## Themes — 21 hand-tuned
|
|
44
109
|
|
|
45
110
|
Preview **every** theme with your own data:
|
|
46
111
|
|
|
@@ -54,31 +119,51 @@ Preview **every** theme with your own data:
|
|
|
54
119
|
| `github_dark` · `dark` · `nord` · `dracula` | `forest` · `rose` · `cobalt` |
|
|
55
120
|
| `tokyonight` · `synthwave` · `radical` · `monokai` | `gruvbox` · `merko` · `onedark` · `highcontrast` |
|
|
56
121
|
|
|
122
|
+
Add `?theme=tokyonight` or override any slot with hex or Tailwind tokens: `pink-400`, `#ff79c6`, `ff79c6`.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
57
126
|
## Customization
|
|
58
127
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
|
62
|
-
|
|
|
63
|
-
| `
|
|
64
|
-
| `
|
|
65
|
-
| `
|
|
66
|
-
| `
|
|
67
|
-
| `
|
|
68
|
-
| `
|
|
69
|
-
| `
|
|
70
|
-
| `
|
|
71
|
-
| `
|
|
72
|
-
| `
|
|
73
|
-
| `
|
|
74
|
-
| `
|
|
75
|
-
| `
|
|
76
|
-
| `
|
|
77
|
-
| `
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
128
|
+
| Parameter | Type | Description |
|
|
129
|
+
| -------------- | :--------: | ----------------------------------------------------------------- |
|
|
130
|
+
| `theme` | `string` | Built-in theme (table above) |
|
|
131
|
+
| `bg_color` | `string` | Background fill |
|
|
132
|
+
| `title_color` | `string` | Name / title |
|
|
133
|
+
| `text_color` | `string` | Stats, labels, body |
|
|
134
|
+
| `icon_color` | `string` | Icons + ambient glow |
|
|
135
|
+
| `border_color` | `string` | Card outline |
|
|
136
|
+
| `hide_border` | `boolean` | Remove outline |
|
|
137
|
+
| `compact` | `boolean` | Slim — hides bio/pronouns/Twitter/legend |
|
|
138
|
+
| `animate` | `boolean` | Fade-in stats + animated language bar |
|
|
139
|
+
| `hide` | `string[]` | Hide stats: `stars`, `commits`, `issues`, `repos`, `prs` |
|
|
140
|
+
| `hide_langs` | `string[]` | Exclude `hide_langs=html,css` |
|
|
141
|
+
| `show_langs` | `string[]` | Only `show_langs=typescript,rust` |
|
|
142
|
+
| `lang_count` | `1–10` | Top languages (default `5`) |
|
|
143
|
+
| `scope` | `string` | `personal` (default), `org`, `all` |
|
|
144
|
+
| `orgs` | `string[]` | Restrict org stats to `orgs=vercel,elysiajs` |
|
|
145
|
+
| `affiliations` | `string` | `owner` (default) or `affiliated` — org stars excluded by default |
|
|
146
|
+
| `fields` | `string[]` | Fetch only `fields=stats` for speed |
|
|
147
|
+
|
|
148
|
+
#### What the numbers mean
|
|
149
|
+
|
|
150
|
+
- Stars, repositories, and languages come from non-fork repositories in the requested scope.
|
|
151
|
+
`affiliations=owner` is the default for both the HTTP API and `getProfileData`.
|
|
152
|
+
- User commits are contributions credited by GitHub from January 1 (UTC) to now, not lifetime
|
|
153
|
+
commit counts. Default user PR/issue totals are lifetime authored totals. With `scope=org`,
|
|
154
|
+
commits, PRs, and issues are this year's contributions to the selected organizations.
|
|
155
|
+
- `scope=all` combines personal and contributed organization repositories without counting a repo
|
|
156
|
+
twice; its activity totals remain the user's totals. `orgs` filters the organization portion.
|
|
157
|
+
- Organization accounts have repository/star/language data, but no computed activity totals.
|
|
158
|
+
Those metrics display `—` (and are `null` in `UserStats`), not a misleading zero.
|
|
159
|
+
- Language filters run before `lang_count`. Percentages describe the displayed languages by code
|
|
160
|
+
size, not time spent coding. The bar and legend use the same “Other” group.
|
|
161
|
+
- Results depend on what the GitHub token can access. GitHub's query limits still apply:
|
|
162
|
+
the top 10 languages per repository and up to 100 repositories in each org contribution
|
|
163
|
+
breakdown. Repository pagination supports up to 10,000 repos per connection; reaching that
|
|
164
|
+
limit with more pages remaining returns an error rather than caching incomplete totals.
|
|
165
|
+
|
|
166
|
+
#### Examples
|
|
82
167
|
|
|
83
168
|
```md
|
|
84
169
|
<!-- Dracula, no border, animated -->
|
|
@@ -89,107 +174,106 @@ Every visual slot is overridable. Colors accept hex (`ff79c6` or `#ff79c6`) and
|
|
|
89
174
|
|
|
90
175
|

|
|
91
176
|
|
|
92
|
-
<!-- Compact,
|
|
177
|
+
<!-- Compact, no issues/prs -->
|
|
93
178
|
|
|
94
179
|

|
|
95
180
|
|
|
96
|
-
<!--
|
|
181
|
+
<!-- Include org stars explicitly -->
|
|
182
|
+
|
|
183
|
+

|
|
184
|
+
|
|
185
|
+
<!-- Organization -->
|
|
97
186
|
|
|
98
187
|

|
|
99
188
|
```
|
|
100
189
|
|
|
190
|
+
---
|
|
191
|
+
|
|
101
192
|
## Deploy your own
|
|
102
193
|
|
|
103
194
|
[](https://vercel.com/new/clone?repository-url=https://github.com/ShiinaSaku/Github-Card)
|
|
104
195
|
|
|
105
|
-
1. Click the button
|
|
106
|
-
2. Add
|
|
196
|
+
1. Click the button (or fork + import).
|
|
197
|
+
2. Add env vars:
|
|
107
198
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
199
|
+
| Variable | Required | Purpose |
|
|
200
|
+
| :------------: | :------: | ----------------------------------- |
|
|
201
|
+
| `GITHUB_TOKEN` | ✅ | GitHub PAT — read-only is enough |
|
|
202
|
+
| `REDIS_URL` | ➖ | Redis / Upstash for shared L2 cache |
|
|
112
203
|
|
|
113
|
-
3. Done
|
|
204
|
+
3. Done — serves `/card/:username` on Vercel CDN `s-maxage=1800, stale-while-revalidate=1800`.
|
|
114
205
|
|
|
115
|
-
> Redis
|
|
206
|
+
> Without Redis, bounded in-memory cache (1000 entries, SWR 30 min) still keeps warm instances fast.
|
|
207
|
+
|
|
208
|
+
---
|
|
116
209
|
|
|
117
210
|
## API
|
|
118
211
|
|
|
119
212
|
| Endpoint | Description |
|
|
120
213
|
| ----------------------- | ------------------------------------------- |
|
|
121
|
-
| `GET /card/:username` | SVG card
|
|
122
|
-
| `GET /:username/themes` | One SVG
|
|
214
|
+
| `GET /card/:username` | SVG card — all params above |
|
|
215
|
+
| `GET /:username/themes` | One SVG with **all themes** for a user |
|
|
123
216
|
| `GET /meta` | Service metadata + theme list |
|
|
124
217
|
| `GET /health` | Uptime, cache telemetry, Redis reachability |
|
|
125
218
|
| `GET /openapi` | Interactive OpenAPI docs |
|
|
126
219
|
|
|
127
|
-
Responses
|
|
220
|
+
Responses: `image/svg+xml`, `ETag` + `CDN-Cache-Control` + CORS `*`. Conditional `If-None-Match` → `304`.
|
|
221
|
+
|
|
222
|
+
---
|
|
128
223
|
|
|
129
224
|
## Use as a library
|
|
130
225
|
|
|
131
226
|
```bash
|
|
132
227
|
bun add @shiinasaku/github-card # Bun
|
|
133
|
-
npm install @shiinasaku/github-card # Node / Next
|
|
228
|
+
npm install @shiinasaku/github-card # Node / Next / Vite
|
|
134
229
|
```
|
|
135
230
|
|
|
136
231
|
```ts
|
|
137
232
|
import { renderCard, themes } from "@shiinasaku/github-card";
|
|
138
233
|
|
|
139
234
|
const svg = renderCard(user, stats, languages, { theme: "tokyonight" });
|
|
235
|
+
// → SVG string, no network or server required
|
|
140
236
|
```
|
|
141
237
|
|
|
142
|
-
|
|
238
|
+
Prebuilt `dist/lib.js` + `.d.ts`; Bun resolves raw TS via `bun` export condition. Pure renderer — no network, no server.
|
|
143
239
|
|
|
144
|
-
|
|
240
|
+
---
|
|
145
241
|
|
|
146
|
-
|
|
242
|
+
## Architecture
|
|
147
243
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
244
|
+
```mermaid
|
|
245
|
+
flowchart LR
|
|
246
|
+
A[GET /card/:username] --> B[Elysia<br/>validation · CORS · OpenAPI · timing]
|
|
247
|
+
B --> C{L1 memory<br/>SWR 30m}
|
|
248
|
+
C -- hit --> G[render SVG<br/>ETag/304 → CDN]
|
|
249
|
+
C -- miss --> D{L2 Redis<br/>SWR 30m}
|
|
250
|
+
D -- hit --> G
|
|
251
|
+
D -- miss --> E[GitHub GraphQL<br/>100/page, up to 100 pages, 6s/request]
|
|
252
|
+
E --> F[typed fetch<br/>+ avatar 150px]
|
|
253
|
+
F --> G
|
|
254
|
+
G --> H[Pure SVG<br/>system fonts · measured layout · glass]
|
|
255
|
+
```
|
|
151
256
|
|
|
152
|
-
|
|
257
|
+
- **Bun + Elysia** — one of the fastest HTTP stacks.
|
|
258
|
+
- **Two-tier SWR** — stale served instantly + background refresh, request coalescing via `inFlight`.
|
|
259
|
+
- **Typed upstream** — `RepoNode`/`ContributionEntry` generics, `T=unknown`, `TimeoutError`/`RateLimitError` mapping.
|
|
153
260
|
|
|
154
|
-
|
|
155
|
-
bun install
|
|
156
|
-
cp .env.example .env # add your GITHUB_TOKEN
|
|
157
|
-
bun dev # http://localhost:3000
|
|
158
|
-
```
|
|
261
|
+
---
|
|
159
262
|
|
|
160
|
-
|
|
161
|
-
| ------------------- | --------------------- |
|
|
162
|
-
| `bun test` | Run the test suite |
|
|
163
|
-
| `bun run typecheck` | Strict `tsc --noEmit` |
|
|
164
|
-
| `bun run lint` | oxlint |
|
|
165
|
-
| `bun run fmt` | oxfmt |
|
|
263
|
+
## Releasing
|
|
166
264
|
|
|
167
|
-
|
|
265
|
+
Fully automated:
|
|
168
266
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
▼
|
|
173
|
-
Elysia (validation, CORS, OpenAPI, server-timing)
|
|
174
|
-
│
|
|
175
|
-
▼
|
|
176
|
-
L1 in-memory cache ──hit──► render SVG ──► ETag/304 ──► CDN
|
|
177
|
-
│miss ▲
|
|
178
|
-
▼ │ stale-while-revalidate
|
|
179
|
-
L2 Redis (SWR) ──hit──► serve stale + background refresh
|
|
180
|
-
│miss
|
|
181
|
-
▼
|
|
182
|
-
GitHub GraphQL (batched, paginated 100/page, 6s timeout)
|
|
183
|
-
│
|
|
184
|
-
▼
|
|
185
|
-
Pure SVG renderer (zero-dependency, dynamic layout engine)
|
|
186
|
-
```
|
|
267
|
+
1. **Actions → Bump** → `patch` / `minor` / `major`.
|
|
268
|
+
2. Workflow bumps `package.json`, commits, tags `vX.Y.Z`.
|
|
269
|
+
3. Tag triggers **Release**: `bun fmt --check` · `bun check` · `tests` · `dist` build · tag↔version guard · `npm pack` verify → publish with [provenance](https://docs.npmjs.com/generating-provenance-statements) + GitHub Release.
|
|
187
270
|
|
|
188
|
-
|
|
189
|
-
- **Two-tier SWR cache** — users never wait on GitHub's API.
|
|
190
|
-
- **Request coalescing** — concurrent misses share a single upstream fetch.
|
|
191
|
-
- **Defensive upstream** — timeouts, size caps, and typed errors (`401/404/429/502`).
|
|
271
|
+
---
|
|
192
272
|
|
|
193
|
-
|
|
273
|
+
<div align="center">
|
|
194
274
|
|
|
195
|
-
[MIT](LICENSE) © [saku shiina](https://www.shiina.xyz)
|
|
275
|
+
**[MIT](LICENSE) © [saku shiina](https://www.shiina.xyz)** — built with Bun, Elysia, and system fonts.
|
|
276
|
+
|
|
277
|
+
<sub>Stars default to <code>owner</code> only in 4.2 — add <code>?affiliations=affiliated</code> to include orgs.</sub>
|
|
278
|
+
|
|
279
|
+
</div>
|
package/dist/card.d.ts
CHANGED
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
import type { UserProfile, UserStats, LanguageStat } from "./types";
|
|
2
|
-
export type CardOpts =
|
|
3
|
-
theme?: string;
|
|
4
|
-
title_color?: string;
|
|
5
|
-
text_color?: string;
|
|
6
|
-
icon_color?: string;
|
|
7
|
-
bg_color?: string;
|
|
8
|
-
border_color?: string;
|
|
9
|
-
hide_border?: boolean;
|
|
10
|
-
compact?: boolean;
|
|
11
|
-
hide?: string[];
|
|
12
|
-
hide_langs?: string[];
|
|
13
|
-
show_langs?: string[];
|
|
14
|
-
animate?: boolean;
|
|
15
|
-
};
|
|
1
|
+
import type { UserProfile, UserStats, LanguageStat, CardOptions } from "./types";
|
|
2
|
+
export type CardOpts = CardOptions;
|
|
16
3
|
export declare function renderCard(user: UserProfile, stats: UserStats, langs: LanguageStat[], opts?: CardOpts): string;
|
package/dist/lib.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
export { renderCard, type CardOpts } from "./card";
|
|
8
8
|
export type { UserProfile, UserStats, LanguageStat, ProfileData, CardOptions } from "./types";
|
|
9
9
|
export { themes, resolveColors, type Theme } from "./utils/themes";
|
|
10
|
-
export { kFormat, escapeXml, wrapText } from "./utils/format";
|
|
10
|
+
export { kFormat, escapeXml, wrapText, measureText } from "./utils/format";
|
|
11
11
|
export { icons, icon } from "./utils/icons";
|
|
12
12
|
export { getLangColor } from "./utils/languages";
|
|
13
13
|
export { FONT_FACE, FONT_FAMILY } from "./utils/font";
|