@dytsou/github-readme-stats 1.0.1
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/LICENSE +21 -0
- package/api/gist.js +98 -0
- package/api/index.js +146 -0
- package/api/pin.js +114 -0
- package/api/status/pat-info.js +193 -0
- package/api/status/up.js +129 -0
- package/api/top-langs.js +163 -0
- package/api/wakatime.js +129 -0
- package/package.json +102 -0
- package/readme.md +412 -0
- package/src/calculateRank.js +87 -0
- package/src/cards/gist.js +151 -0
- package/src/cards/index.js +4 -0
- package/src/cards/repo.js +199 -0
- package/src/cards/stats.js +607 -0
- package/src/cards/top-languages.js +968 -0
- package/src/cards/types.d.ts +67 -0
- package/src/cards/wakatime.js +482 -0
- package/src/common/Card.js +294 -0
- package/src/common/I18n.js +41 -0
- package/src/common/access.js +69 -0
- package/src/common/api-utils.js +221 -0
- package/src/common/blacklist.js +10 -0
- package/src/common/cache.js +153 -0
- package/src/common/color.js +194 -0
- package/src/common/envs.js +15 -0
- package/src/common/error.js +84 -0
- package/src/common/fmt.js +90 -0
- package/src/common/html.js +43 -0
- package/src/common/http.js +24 -0
- package/src/common/icons.js +63 -0
- package/src/common/index.js +13 -0
- package/src/common/languageColors.json +651 -0
- package/src/common/log.js +14 -0
- package/src/common/ops.js +124 -0
- package/src/common/render.js +261 -0
- package/src/common/retryer.js +97 -0
- package/src/common/worker-adapter.js +148 -0
- package/src/common/worker-env.js +48 -0
- package/src/fetchers/gist.js +114 -0
- package/src/fetchers/repo.js +118 -0
- package/src/fetchers/stats.js +350 -0
- package/src/fetchers/top-languages.js +192 -0
- package/src/fetchers/types.d.ts +118 -0
- package/src/fetchers/wakatime.js +109 -0
- package/src/index.js +2 -0
- package/src/translations.js +1105 -0
- package/src/worker.ts +116 -0
- package/themes/README.md +229 -0
- package/themes/index.js +467 -0
package/readme.md
ADDED
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
# GitHub Readme Stats
|
|
2
|
+
|
|
3
|
+
Dynamically generated GitHub stats cards for your README.
|
|
4
|
+
|
|
5
|
+
<div>
|
|
6
|
+
<h1>GitHub Readme Stats</h1>
|
|
7
|
+
<p>Get dynamically generated GitHub stats on your READMEs!</p>
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<details>
|
|
11
|
+
<summary>Table of contents</summary>
|
|
12
|
+
|
|
13
|
+
- [Quick Start](#quick-start)
|
|
14
|
+
- [GitHub Stats Card](#github-stats-card)
|
|
15
|
+
- [Top Languages Card](#top-languages-card)
|
|
16
|
+
- [Repository Card](#repository-card)
|
|
17
|
+
- [Gist Card](#gist-card)
|
|
18
|
+
- [WakaTime Card](#wakatime-card)
|
|
19
|
+
- [Deployment](#deployment)
|
|
20
|
+
- [Configuration](#configuration)
|
|
21
|
+
- [Support](#support)
|
|
22
|
+
|
|
23
|
+
</details>
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
Add this to your README (replace `YOUR_USERNAME` with your GitHub username):
|
|
28
|
+
|
|
29
|
+
```markdown
|
|
30
|
+

|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
> **Note:** Replace `YOUR-INSTANCE.WORKERS.DEV` with your deployed Cloudflare Workers instance URL and `YOUR_USERNAME` with your GitHub username.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Important Notes
|
|
38
|
+
|
|
39
|
+
> [!IMPORTANT]
|
|
40
|
+
> The GitHub API allows 5k requests per hour per account. Public instances may hit rate limits. We use caching to prevent this. For better control, [deploy your own instance](#deployment).
|
|
41
|
+
|
|
42
|
+
> [!WARNING]
|
|
43
|
+
> By default, cards only show statistics from public repositories. To include private repository statistics, deploy your own instance with a GitHub Personal Access Token.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## GitHub Stats Card
|
|
48
|
+
|
|
49
|
+
Display your GitHub statistics including stars, commits, pull requests, and more.
|
|
50
|
+
|
|
51
|
+
### Basic Usage
|
|
52
|
+
|
|
53
|
+
```markdown
|
|
54
|
+

|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Common Options
|
|
58
|
+
|
|
59
|
+
| Parameter | Description | Example |
|
|
60
|
+
|-----------|-------------|---------|
|
|
61
|
+
| `username` | GitHub username (required) | `anuraghazra` |
|
|
62
|
+
| `theme` | Theme name | `dark`, `radical`, `merko`, etc. |
|
|
63
|
+
| `hide` | Hide specific stats | `stars,commits,prs` |
|
|
64
|
+
| `show` | Show additional stats | `reviews,discussions_started` |
|
|
65
|
+
| `show_icons` | Display icons | `true` |
|
|
66
|
+
| `hide_border` | Hide card border | `true` |
|
|
67
|
+
| `bg_color` | Background color (hex) | `1a1b27` |
|
|
68
|
+
| `title_color` | Title color (hex) | `fff` |
|
|
69
|
+
| `text_color` | Text color (hex) | `9f9f9f` |
|
|
70
|
+
| `border_color` | Border color (hex) | `e4e2e2` |
|
|
71
|
+
| `locale` | Language | `en`, `es`, `fr`, etc. |
|
|
72
|
+
| `cache_seconds` | Cache duration (21600-86400) | `21600` |
|
|
73
|
+
|
|
74
|
+
### Stats Card Specific Options
|
|
75
|
+
|
|
76
|
+
| Parameter | Description | Default |
|
|
77
|
+
|-----------|-------------|---------|
|
|
78
|
+
| `hide_title` | Hide card title | `false` |
|
|
79
|
+
| `hide_rank` | Hide rank badge | `false` |
|
|
80
|
+
| `rank_icon` | Rank icon style | `default`, `github`, `percentile` |
|
|
81
|
+
| `card_width` | Card width in pixels | `500` |
|
|
82
|
+
| `include_all_commits` | Count all commits (not just current year) | `false` |
|
|
83
|
+
| `commits_year` | Filter commits by year | Current year |
|
|
84
|
+
| `exclude_repo` | Exclude repositories | Comma-separated list |
|
|
85
|
+
| `custom_title` | Custom card title | `<username> GitHub Stats` |
|
|
86
|
+
| `number_format` | Number format | `short` (e.g., `6.6k`) or `long` (e.g., `6626`) |
|
|
87
|
+
|
|
88
|
+
### Examples
|
|
89
|
+
|
|
90
|
+
**Hide specific stats:**
|
|
91
|
+
```markdown
|
|
92
|
+

|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Show icons:**
|
|
96
|
+
```markdown
|
|
97
|
+

|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Use a theme:**
|
|
101
|
+
```markdown
|
|
102
|
+

|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Custom colors:**
|
|
106
|
+
```markdown
|
|
107
|
+

|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Available Themes
|
|
111
|
+
|
|
112
|
+
Popular themes include: `dark`, `radical`, `merko`, `gruvbox`, `tokyonight`, `onedark`, `cobalt`, `synthwave`, `highcontrast`, `dracula`, `transparent`, and more.
|
|
113
|
+
|
|
114
|
+
See [all available themes](themes/README.md) for the complete list.
|
|
115
|
+
|
|
116
|
+
### Responsive Themes
|
|
117
|
+
|
|
118
|
+
Use GitHub's theme context tags for automatic dark/light mode:
|
|
119
|
+
|
|
120
|
+
```markdown
|
|
121
|
+
[](https://github.com/YOUR_USERNAME)
|
|
122
|
+
[](https://github.com/YOUR_USERNAME)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Top Languages Card
|
|
128
|
+
|
|
129
|
+
Display your most frequently used programming languages.
|
|
130
|
+
|
|
131
|
+
### Basic Usage
|
|
132
|
+
|
|
133
|
+
```markdown
|
|
134
|
+

|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Options
|
|
138
|
+
|
|
139
|
+
| Parameter | Description | Default |
|
|
140
|
+
|-----------|-------------|---------|
|
|
141
|
+
| `layout` | Layout style | `normal`, `compact`, `donut`, `donut-vertical`, `pie` |
|
|
142
|
+
| `langs_count` | Number of languages to show (1-20) | `5` |
|
|
143
|
+
| `hide` | Hide specific languages | Comma-separated list |
|
|
144
|
+
| `exclude_repo` | Exclude repositories | Comma-separated list |
|
|
145
|
+
| `size_weight` | Weight for byte count in ranking | `1` |
|
|
146
|
+
| `count_weight` | Weight for repo count in ranking | `0` |
|
|
147
|
+
| `stats_format` | Display format | `percentages` or `bytes` |
|
|
148
|
+
| `hide_progress` | Hide progress bars | `false` |
|
|
149
|
+
|
|
150
|
+
### Examples
|
|
151
|
+
|
|
152
|
+
**Compact layout:**
|
|
153
|
+
```markdown
|
|
154
|
+

|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Donut chart:**
|
|
158
|
+
```markdown
|
|
159
|
+

|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Show more languages:**
|
|
163
|
+
```markdown
|
|
164
|
+

|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Hide specific languages:**
|
|
168
|
+
```markdown
|
|
169
|
+

|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Language Ranking Algorithm
|
|
173
|
+
|
|
174
|
+
Languages are ranked using:
|
|
175
|
+
```
|
|
176
|
+
ranking_index = (byte_count ^ size_weight) * (repo_count ^ count_weight)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
- Default (`size_weight=1, count_weight=0`): Orders by byte count
|
|
180
|
+
- Recommended (`size_weight=0.5, count_weight=0.5`): Uses both byte and repo count
|
|
181
|
+
- Repo-based (`size_weight=0, count_weight=1`): Orders by repo count
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Repository Card
|
|
186
|
+
|
|
187
|
+
Pin additional repositories beyond GitHub's 6-repo limit.
|
|
188
|
+
|
|
189
|
+
### Basic Usage
|
|
190
|
+
|
|
191
|
+
```markdown
|
|
192
|
+

|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Options
|
|
196
|
+
|
|
197
|
+
| Parameter | Description | Default |
|
|
198
|
+
|-----------|-------------|---------|
|
|
199
|
+
| `show_owner` | Show repository owner | `false` |
|
|
200
|
+
| `description_lines_count` | Number of description lines (1-3) | Auto |
|
|
201
|
+
|
|
202
|
+
### Example
|
|
203
|
+
|
|
204
|
+
```markdown
|
|
205
|
+

|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Gist Card
|
|
211
|
+
|
|
212
|
+
Display GitHub Gists in your README.
|
|
213
|
+
|
|
214
|
+
### Basic Usage
|
|
215
|
+
|
|
216
|
+
```markdown
|
|
217
|
+

|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Options
|
|
221
|
+
|
|
222
|
+
| Parameter | Description | Default |
|
|
223
|
+
|-----------|-------------|---------|
|
|
224
|
+
| `show_owner` | Show gist owner | `false` |
|
|
225
|
+
|
|
226
|
+
### Example
|
|
227
|
+
|
|
228
|
+
```markdown
|
|
229
|
+

|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## WakaTime Card
|
|
235
|
+
|
|
236
|
+
Display your WakaTime coding statistics.
|
|
237
|
+
|
|
238
|
+
> [!WARNING]
|
|
239
|
+
> Your WakaTime profile must be public. Enable both "Display code time publicly" and "Display languages, editors, os, categories publicly" in your WakaTime settings.
|
|
240
|
+
|
|
241
|
+
### Basic Usage
|
|
242
|
+
|
|
243
|
+
```markdown
|
|
244
|
+

|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Options
|
|
248
|
+
|
|
249
|
+
| Parameter | Description | Default |
|
|
250
|
+
|-----------|-------------|---------|
|
|
251
|
+
| `layout` | Layout style | `default`, `compact` |
|
|
252
|
+
| `langs_count` | Limit number of languages | All languages |
|
|
253
|
+
| `hide_progress` | Hide progress bars | `false` |
|
|
254
|
+
| `display_format` | Display format | `time` or `percent` |
|
|
255
|
+
| `api_domain` | Custom API domain | `wakatime.com` |
|
|
256
|
+
|
|
257
|
+
### Example
|
|
258
|
+
|
|
259
|
+
```markdown
|
|
260
|
+

|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Deployment
|
|
266
|
+
|
|
267
|
+
### Prerequisites
|
|
268
|
+
|
|
269
|
+
1. **GitHub Personal Access Token (PAT)**
|
|
270
|
+
- Go to [GitHub Settings > Developer Settings > Personal Access Tokens](https://github.com/settings/tokens)
|
|
271
|
+
- Create a new token (classic) with `repo` and `read:user` scopes
|
|
272
|
+
- Copy the token
|
|
273
|
+
|
|
274
|
+
### Deploy to Cloudflare Workers
|
|
275
|
+
|
|
276
|
+
1. **Fork this repository**
|
|
277
|
+
|
|
278
|
+
2. **Install dependencies:**
|
|
279
|
+
```bash
|
|
280
|
+
pnpm install
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
> **Note:** This project uses [pnpm](https://pnpm.io/) as the package manager. If you don't have pnpm installed, you can install it with `npm install -g pnpm`.
|
|
284
|
+
|
|
285
|
+
3. **Configure `wrangler.toml`:**
|
|
286
|
+
```toml
|
|
287
|
+
name = "github-readme-stats"
|
|
288
|
+
main = "src/worker.ts"
|
|
289
|
+
compatibility_date = "2025-12-04"
|
|
290
|
+
compatibility_flags = ["nodejs_compat"]
|
|
291
|
+
|
|
292
|
+
[observability]
|
|
293
|
+
enabled = true
|
|
294
|
+
head_sampling_rate = 1
|
|
295
|
+
|
|
296
|
+
[observability.logs]
|
|
297
|
+
enabled = true
|
|
298
|
+
head_sampling_rate = 1
|
|
299
|
+
persist = true
|
|
300
|
+
invocation_logs = true
|
|
301
|
+
|
|
302
|
+
[observability.traces]
|
|
303
|
+
enabled = true
|
|
304
|
+
persist = true
|
|
305
|
+
head_sampling_rate = 1
|
|
306
|
+
|
|
307
|
+
[vars]
|
|
308
|
+
PAT_1 = "your_pat_token_here"
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
> [!WARNING]
|
|
312
|
+
> For production, use Cloudflare secrets instead of `[vars]`:
|
|
313
|
+
> ```bash
|
|
314
|
+
> wrangler secret put PAT_1
|
|
315
|
+
> ```
|
|
316
|
+
|
|
317
|
+
4. **Deploy:**
|
|
318
|
+
|
|
319
|
+
**Option A: Manual deployment**
|
|
320
|
+
```bash
|
|
321
|
+
npx wrangler deploy
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
**Option B: GitHub Actions (Recommended)**
|
|
325
|
+
|
|
326
|
+
Set up the following secrets in your GitHub repository:
|
|
327
|
+
- `CLOUDFLARE_API_TOKEN`: Your Cloudflare API token (create at [Cloudflare Dashboard > My Profile > API Tokens](https://dash.cloudflare.com/profile/api-tokens))
|
|
328
|
+
- `CLOUDFLARE_ACCOUNT_ID`: Your Cloudflare Account ID (found in the right sidebar of your Cloudflare dashboard)
|
|
329
|
+
- `PAT_1`: Your GitHub Personal Access Token (optional, if not set in `wrangler.toml`)
|
|
330
|
+
|
|
331
|
+
The workflow will automatically deploy on every push to `main`/`master` branch, or you can trigger it manually from the Actions tab.
|
|
332
|
+
|
|
333
|
+
5. **Your instance will be available at:**
|
|
334
|
+
```
|
|
335
|
+
https://your-worker-name.your-subdomain.workers.dev
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
6. **Update your README URLs** to use your new domain!
|
|
339
|
+
|
|
340
|
+
### Environment Variables
|
|
341
|
+
|
|
342
|
+
Configure your instance using these environment variables:
|
|
343
|
+
|
|
344
|
+
| Variable | Description | Example |
|
|
345
|
+
|----------|-------------|---------|
|
|
346
|
+
| `PAT_1`, `PAT_2`, etc. | GitHub Personal Access Tokens | `ghp_...` |
|
|
347
|
+
| `CACHE_SECONDS` | Cache duration in seconds (0 to disable) | `21600` |
|
|
348
|
+
| `WHITELIST` | Comma-separated allowed usernames | `user1,user2` |
|
|
349
|
+
| `GIST_WHITELIST` | Comma-separated allowed Gist IDs | `id1,id2` |
|
|
350
|
+
| `EXCLUDE_REPO` | Comma-separated repositories to exclude | `repo1,repo2` |
|
|
351
|
+
| `FETCH_MULTI_PAGE_STARS` | Fetch all starred repos | `true` or `false` |
|
|
352
|
+
|
|
353
|
+
> [!WARNING]
|
|
354
|
+
> Redeploy your instance after changing environment variables for changes to take effect.
|
|
355
|
+
|
|
356
|
+
### Keep Your Fork Updated
|
|
357
|
+
|
|
358
|
+
Use GitHub's [Sync Fork](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork) feature or the [pull](https://github.com/wei/pull) package to keep your fork up to date.
|
|
359
|
+
|
|
360
|
+
---
|
|
361
|
+
|
|
362
|
+
## Configuration
|
|
363
|
+
|
|
364
|
+
### Available Locales
|
|
365
|
+
|
|
366
|
+
The cards support multiple languages. Set the `locale` parameter to use:
|
|
367
|
+
|
|
368
|
+
`ar`, `az`, `bn`, `bg`, `my`, `ca`, `cn`, `zh-tw`, `cs`, `nl`, `en`, `fil`, `fi`, `fr`, `de`, `el`, `he`, `hi`, `hu`, `id`, `it`, `ja`, `kr`, `ml`, `np`, `no`, `fa`, `pl`, `pt-br`, `pt-pt`, `ro`, `ru`, `sa`, `sr`, `sr-latn`, `sk`, `es`, `sw`, `se`, `ta`, `th`, `tr`, `uk-ua`, `ur`, `uz`, `vi`
|
|
369
|
+
|
|
370
|
+
Example:
|
|
371
|
+
```markdown
|
|
372
|
+

|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### Caching
|
|
376
|
+
|
|
377
|
+
Default cache durations:
|
|
378
|
+
- Stats card: 24 hours
|
|
379
|
+
- Top languages card: 144 hours (6 days)
|
|
380
|
+
- Repository card: 240 hours (10 days)
|
|
381
|
+
- Gist card: 48 hours (2 days)
|
|
382
|
+
- WakaTime card: 24 hours
|
|
383
|
+
|
|
384
|
+
Override with `cache_seconds` parameter (min: 21600, max: 86400) or set `CACHE_SECONDS` environment variable.
|
|
385
|
+
|
|
386
|
+
### Aligning Cards Side by Side
|
|
387
|
+
|
|
388
|
+
Use HTML with `align` attribute:
|
|
389
|
+
|
|
390
|
+
```html
|
|
391
|
+
<a href="https://github.com/YOUR_USERNAME">
|
|
392
|
+
<img height=200 align="center" src="https://YOUR-INSTANCE.WORKERS.DEV/api?username=YOUR_USERNAME" />
|
|
393
|
+
</a>
|
|
394
|
+
<a href="https://github.com/YOUR_USERNAME">
|
|
395
|
+
<img height=200 align="center" src="https://YOUR-INSTANCE.WORKERS.DEV/api/top-langs?username=YOUR_USERNAME&layout=compact" />
|
|
396
|
+
</a>
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
## Support
|
|
402
|
+
|
|
403
|
+
Contributions are welcome! If you find this project useful:
|
|
404
|
+
|
|
405
|
+
- ⭐ Star the repository
|
|
406
|
+
- 🐛 Report bugs
|
|
407
|
+
- 💡 Suggest features
|
|
408
|
+
- 📖 Improve documentation
|
|
409
|
+
|
|
410
|
+
Made with ❤️ and JavaScript.
|
|
411
|
+
|
|
412
|
+
[](https://workers.cloudflare.com/)
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Calculates the exponential cdf.
|
|
3
|
+
*
|
|
4
|
+
* @param {number} x The value.
|
|
5
|
+
* @returns {number} The exponential cdf.
|
|
6
|
+
*/
|
|
7
|
+
function exponential_cdf(x) {
|
|
8
|
+
return 1 - 2 ** -x;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Calculates the log normal cdf.
|
|
13
|
+
*
|
|
14
|
+
* @param {number} x The value.
|
|
15
|
+
* @returns {number} The log normal cdf.
|
|
16
|
+
*/
|
|
17
|
+
function log_normal_cdf(x) {
|
|
18
|
+
// approximation
|
|
19
|
+
return x / (1 + x);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Calculates the users rank.
|
|
24
|
+
*
|
|
25
|
+
* @param {object} params Parameters on which the user's rank depends.
|
|
26
|
+
* @param {boolean} params.all_commits Whether `include_all_commits` was used.
|
|
27
|
+
* @param {number} params.commits Number of commits.
|
|
28
|
+
* @param {number} params.prs The number of pull requests.
|
|
29
|
+
* @param {number} params.issues The number of issues.
|
|
30
|
+
* @param {number} params.reviews The number of reviews.
|
|
31
|
+
* @param {number} params.repos Total number of repos.
|
|
32
|
+
* @param {number} params.stars The number of stars.
|
|
33
|
+
* @param {number} params.followers The number of followers.
|
|
34
|
+
* @returns {{ level: string, percentile: number }} The users rank.
|
|
35
|
+
*/
|
|
36
|
+
function calculateRank({
|
|
37
|
+
all_commits,
|
|
38
|
+
commits,
|
|
39
|
+
prs,
|
|
40
|
+
issues,
|
|
41
|
+
reviews,
|
|
42
|
+
// eslint-disable-next-line no-unused-vars
|
|
43
|
+
repos, // unused
|
|
44
|
+
stars,
|
|
45
|
+
followers,
|
|
46
|
+
}) {
|
|
47
|
+
const COMMITS_MEDIAN = all_commits ? 1000 : 250,
|
|
48
|
+
COMMITS_WEIGHT = 2;
|
|
49
|
+
const PRS_MEDIAN = 50,
|
|
50
|
+
PRS_WEIGHT = 3;
|
|
51
|
+
const ISSUES_MEDIAN = 25,
|
|
52
|
+
ISSUES_WEIGHT = 1;
|
|
53
|
+
const REVIEWS_MEDIAN = 2,
|
|
54
|
+
REVIEWS_WEIGHT = 1;
|
|
55
|
+
const STARS_MEDIAN = 50,
|
|
56
|
+
STARS_WEIGHT = 4;
|
|
57
|
+
const FOLLOWERS_MEDIAN = 10,
|
|
58
|
+
FOLLOWERS_WEIGHT = 1;
|
|
59
|
+
|
|
60
|
+
const TOTAL_WEIGHT =
|
|
61
|
+
COMMITS_WEIGHT +
|
|
62
|
+
PRS_WEIGHT +
|
|
63
|
+
ISSUES_WEIGHT +
|
|
64
|
+
REVIEWS_WEIGHT +
|
|
65
|
+
STARS_WEIGHT +
|
|
66
|
+
FOLLOWERS_WEIGHT;
|
|
67
|
+
|
|
68
|
+
const THRESHOLDS = [1, 12.5, 25, 37.5, 50, 62.5, 75, 87.5, 100];
|
|
69
|
+
const LEVELS = ["S", "A+", "A", "A-", "B+", "B", "B-", "C+", "C"];
|
|
70
|
+
|
|
71
|
+
const rank =
|
|
72
|
+
1 -
|
|
73
|
+
(COMMITS_WEIGHT * exponential_cdf(commits / COMMITS_MEDIAN) +
|
|
74
|
+
PRS_WEIGHT * exponential_cdf(prs / PRS_MEDIAN) +
|
|
75
|
+
ISSUES_WEIGHT * exponential_cdf(issues / ISSUES_MEDIAN) +
|
|
76
|
+
REVIEWS_WEIGHT * exponential_cdf(reviews / REVIEWS_MEDIAN) +
|
|
77
|
+
STARS_WEIGHT * log_normal_cdf(stars / STARS_MEDIAN) +
|
|
78
|
+
FOLLOWERS_WEIGHT * log_normal_cdf(followers / FOLLOWERS_MEDIAN)) /
|
|
79
|
+
TOTAL_WEIGHT;
|
|
80
|
+
|
|
81
|
+
const level = LEVELS[THRESHOLDS.findIndex((t) => rank * 100 <= t)];
|
|
82
|
+
|
|
83
|
+
return { level, percentile: rank * 100 };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export { calculateRank };
|
|
87
|
+
export default calculateRank;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
measureText,
|
|
5
|
+
flexLayout,
|
|
6
|
+
iconWithLabel,
|
|
7
|
+
createLanguageNode,
|
|
8
|
+
} from "../common/render.js";
|
|
9
|
+
import Card from "../common/Card.js";
|
|
10
|
+
import { getCardColors } from "../common/color.js";
|
|
11
|
+
import { kFormatter, wrapTextMultiline } from "../common/fmt.js";
|
|
12
|
+
import { encodeHTML, escapeCSSValue } from "../common/html.js";
|
|
13
|
+
import { icons } from "../common/icons.js";
|
|
14
|
+
import { parseEmojis } from "../common/ops.js";
|
|
15
|
+
|
|
16
|
+
/** Import language colors.
|
|
17
|
+
*
|
|
18
|
+
* @description Using ES module JSON import which works in modern Node.js and Cloudflare Workers.
|
|
19
|
+
*/
|
|
20
|
+
// @ts-ignore - JSON import
|
|
21
|
+
import languageColors from "../common/languageColors.json";
|
|
22
|
+
|
|
23
|
+
const ICON_SIZE = 16;
|
|
24
|
+
const CARD_DEFAULT_WIDTH = 400;
|
|
25
|
+
const HEADER_MAX_LENGTH = 35;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @typedef {import('./types').GistCardOptions} GistCardOptions Gist card options.
|
|
29
|
+
* @typedef {import('../fetchers/types').GistData} GistData Gist data.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Render gist card.
|
|
34
|
+
*
|
|
35
|
+
* @param {GistData} gistData Gist data.
|
|
36
|
+
* @param {Partial<GistCardOptions>} options Gist card options.
|
|
37
|
+
* @returns {string} Gist card.
|
|
38
|
+
*/
|
|
39
|
+
const renderGistCard = (gistData, options = {}) => {
|
|
40
|
+
const { name, nameWithOwner, description, language, starsCount, forksCount } =
|
|
41
|
+
gistData;
|
|
42
|
+
const {
|
|
43
|
+
title_color,
|
|
44
|
+
icon_color,
|
|
45
|
+
text_color,
|
|
46
|
+
bg_color,
|
|
47
|
+
theme,
|
|
48
|
+
border_radius,
|
|
49
|
+
border_color,
|
|
50
|
+
show_owner = false,
|
|
51
|
+
hide_border = false,
|
|
52
|
+
} = options;
|
|
53
|
+
|
|
54
|
+
// returns theme based colors with proper overrides and defaults
|
|
55
|
+
const { titleColor, textColor, iconColor, bgColor, borderColor } =
|
|
56
|
+
getCardColors({
|
|
57
|
+
title_color,
|
|
58
|
+
icon_color,
|
|
59
|
+
text_color,
|
|
60
|
+
bg_color,
|
|
61
|
+
border_color,
|
|
62
|
+
theme,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const lineWidth = 59;
|
|
66
|
+
const linesLimit = 10;
|
|
67
|
+
const desc = parseEmojis(description || "No description provided");
|
|
68
|
+
const multiLineDescription = wrapTextMultiline(desc, lineWidth, linesLimit);
|
|
69
|
+
const descriptionLines = multiLineDescription.length;
|
|
70
|
+
const descriptionSvg = multiLineDescription
|
|
71
|
+
.map((line) => `<tspan dy="1.2em" x="25">${encodeHTML(line)}</tspan>`)
|
|
72
|
+
.join("");
|
|
73
|
+
|
|
74
|
+
const lineHeight = descriptionLines > 3 ? 12 : 10;
|
|
75
|
+
const height =
|
|
76
|
+
(descriptionLines > 1 ? 120 : 110) + descriptionLines * lineHeight;
|
|
77
|
+
|
|
78
|
+
const totalStars = kFormatter(starsCount);
|
|
79
|
+
const totalForks = kFormatter(forksCount);
|
|
80
|
+
const svgStars = iconWithLabel(
|
|
81
|
+
icons.star,
|
|
82
|
+
totalStars,
|
|
83
|
+
"starsCount",
|
|
84
|
+
ICON_SIZE,
|
|
85
|
+
);
|
|
86
|
+
const svgForks = iconWithLabel(
|
|
87
|
+
icons.fork,
|
|
88
|
+
totalForks,
|
|
89
|
+
"forksCount",
|
|
90
|
+
ICON_SIZE,
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const languageName = language || "Unspecified";
|
|
94
|
+
// @ts-ignore
|
|
95
|
+
const languageColor = languageColors[languageName] || "#858585";
|
|
96
|
+
|
|
97
|
+
const svgLanguage = createLanguageNode(languageName, languageColor);
|
|
98
|
+
|
|
99
|
+
const starAndForkCount = flexLayout({
|
|
100
|
+
items: [svgLanguage, svgStars, svgForks],
|
|
101
|
+
sizes: [
|
|
102
|
+
measureText(languageName, 12),
|
|
103
|
+
ICON_SIZE + measureText(`${totalStars}`, 12),
|
|
104
|
+
ICON_SIZE + measureText(`${totalForks}`, 12),
|
|
105
|
+
],
|
|
106
|
+
gap: 25,
|
|
107
|
+
}).join("");
|
|
108
|
+
|
|
109
|
+
const header = show_owner ? nameWithOwner : name;
|
|
110
|
+
|
|
111
|
+
const card = new Card({
|
|
112
|
+
defaultTitle:
|
|
113
|
+
header.length > HEADER_MAX_LENGTH
|
|
114
|
+
? `${header.slice(0, HEADER_MAX_LENGTH)}...`
|
|
115
|
+
: header,
|
|
116
|
+
titlePrefixIcon: icons.gist,
|
|
117
|
+
width: CARD_DEFAULT_WIDTH,
|
|
118
|
+
height,
|
|
119
|
+
border_radius,
|
|
120
|
+
colors: {
|
|
121
|
+
titleColor,
|
|
122
|
+
textColor,
|
|
123
|
+
iconColor,
|
|
124
|
+
bgColor,
|
|
125
|
+
borderColor,
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Sanitize color values to prevent XSS
|
|
130
|
+
const safeTextColor = escapeCSSValue(textColor);
|
|
131
|
+
const safeIconColor = escapeCSSValue(iconColor);
|
|
132
|
+
card.setCSS(`
|
|
133
|
+
.description { font: 400 13px 'Segoe UI', Ubuntu, Sans-Serif; fill: ${safeTextColor} }
|
|
134
|
+
.gray { font: 400 12px 'Segoe UI', Ubuntu, Sans-Serif; fill: ${safeTextColor} }
|
|
135
|
+
.icon { fill: ${safeIconColor} }
|
|
136
|
+
`);
|
|
137
|
+
card.setHideBorder(hide_border);
|
|
138
|
+
|
|
139
|
+
return card.render(`
|
|
140
|
+
<text class="description" x="25" y="-5">
|
|
141
|
+
${descriptionSvg}
|
|
142
|
+
</text>
|
|
143
|
+
|
|
144
|
+
<g transform="translate(30, ${height - 75})">
|
|
145
|
+
${starAndForkCount}
|
|
146
|
+
</g>
|
|
147
|
+
`);
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export { renderGistCard, HEADER_MAX_LENGTH };
|
|
151
|
+
export default renderGistCard;
|