Skip to content

Commit 5e94c54

Browse files
authored
Merge branch 'main' into refactor/streak-alive-helper
2 parents 166ab57 + d138b96 commit 5e94c54

54 files changed

Lines changed: 2198 additions & 779 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

ARCHITECTURE.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Architecture
2+
3+
This document explains how a request flows through the SVG rendering pipeline.
4+
5+
## Request → Response Flow
6+
7+
```mermaid
8+
flowchart TD
9+
A[Client Request] --> B[app/api/streak/route.ts]
10+
B --> C[lib/github.ts]
11+
C --> D[lib/calculate.ts]
12+
D --> E[lib/svg/generator.ts]
13+
E --> F[SVG Response]
14+
```
15+
16+
## Layer Responsibilities
17+
18+
### API Layer
19+
20+
**File:** `app/api/streak/route.ts`
21+
22+
Receives incoming requests and handles request validation. It acts as the entry point for the SVG generation pipeline.
23+
24+
### GitHub Data Layer
25+
26+
**File:** `lib/github.ts`
27+
28+
Fetches contribution and activity data from GitHub APIs and prepares the required information.
29+
30+
### Calculation Layer
31+
32+
**File:** `lib/calculate.ts`
33+
34+
Processes fetched data and calculates statistics such as contribution streaks and related values.
35+
36+
### SVG Generation Layer
37+
38+
**File:** `lib/svg/generator.ts`
39+
40+
Converts processed data into SVG elements and generates the final visual output.
41+
42+
## Caching Strategy
43+
44+
Caching reduces repeated API requests and improves performance.
45+
46+
The system can cache GitHub responses and calculated results to:
47+
48+
- Reduce unnecessary API calls
49+
- Improve response speed
50+
- Reduce server load
51+
- Prevent repeated processing for frequently requested data
52+
53+
## Summary
54+
55+
Pipeline flow:
56+
57+
1. Client sends request
58+
2. API route receives request
59+
3. GitHub data is fetched
60+
4. Data calculations are performed
61+
5. SVG is generated
62+
6. Response is returned

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ URL Parameter > Theme Default > System Fallback
155155
| `hide_background` | `boolean` | No | `false` | Remove the background rect, letting the monolith float on the page |
156156
| `hide_stats` | `boolean` | No | `false` | Hides the bottom row displaying Current Streak, Annual Sync Total, and Peak Streak stats when set to `true` or `1`. |
157157
| `tz` | `string` | No | Omitted = UTC | IANA timezone (e.g. `Asia/Kolkata`, `America/New_York`) — aligns "today" with the user local midnight. Note: `?tz=UTC` is valid but cached separately from omitting `tz`. |
158-
| `lang` | `string` | No | `en` | Language code for labels (`en`, `es`, `hi`, `fr`) |
158+
| `lang` | `string` | No | `en` | Language code for labels (`en`, `es`, `hi`, `fr`, `pt`, `ko`) |
159159
| `view` | `string` | No | `default` | Rendering mode: `default` (3D Monolith) or `monthly` (Compact monthly stats) |
160160
| `delta_format` | `string` | No | `percent` | Format for month-over-month delta in monthly view: `percent` (e.g. +12%), `absolute` (e.g. +15 commits), or `both` |
161161
| `width` | `number` | No | `300` | Custom width for the SVG canvas (currently only applies to `view=monthly`) |

THEMES.md

Lines changed: 53 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,105 +1,91 @@
1-
# 🎨 CommitPulse Theme Gallery
1+
# 🎨 CommitPulse Themes
22

3-
This gallery showcases all available themes for the CommitPulse streak card.
3+
All 13 available themes for your CommitPulse badge. Use the `?theme=<slug>` query parameter to apply a
4+
theme.
45

5-
---
6-
7-
## 🌑 Neon
8-
9-
![Neon](assets/themes/neon.png)
10-
11-
Usage: `/api/streak?user=yourusername&theme=neon`
6+
```
7+
https://commitpulse.vercel.app/api/badge?username=YOUR_USERNAME&theme=<slug>
8+
```
129

1310
---
1411

15-
## 🧛 Dracula
16-
17-
![Dracula](assets/themes/dracula.png)
18-
19-
Usage: `/api/streak?user=yourusername&theme=dracula`
12+
## Theme Gallery
13+
14+
| Theme | Background | Text | Accent |
15+
| ------------ | ---------- | --------- | --------- |
16+
| dark | `#0d1117` | `#c9d1d9` | `#58a6ff` |
17+
| light | `#ffffff` | `#24292f` | `#0969da` |
18+
| neon | `#000000` | `#00ffcc` | `#ff00ff` |
19+
| github | `#0d1117` | `#ffffff` | `#238636` |
20+
| dracula | `#282a36` | `#f8f8f2` | `#bd93f9` |
21+
| ocean | `#0a192f` | `#ccd6f6` | `#64ffda` |
22+
| sunset | `#1a0a0a` | `#ffd6c0` | `#ff6b35` |
23+
| forest | `#0d1f0d` | `#c8f0c8` | `#39d353` |
24+
| rose | `#1f0d14` | `#f0c8d4` | `#ff6b9d` |
25+
| nord | `#2e3440` | `#d8dee9` | `#88c0d0` |
26+
| synthwave | `#0d0221` | `#f8f8f2` | `#ff2d78` |
27+
| gruvbox | `#282828` | `#ebdbb2` | `#fe8019` |
28+
| highcontrast | `#0a0a0a` | `#888888` | `#ff4500` |
2029

2130
---
2231

23-
## 🌑 Dark
32+
## Preview URLs
2433

25-
![Dark](assets/themes/dark.png)
34+
Use these URLs to preview each theme in your README:
2635

27-
Usage: `/api/streak?user=yourusername&theme=dark`
36+
**dark**
2837

29-
---
38+
![dark](https://commitpulse.vercel.app/api/badge?username=demo&theme=dark)
3039

31-
## 🤍 Light
40+
**light**
3241

33-
![Light](assets/themes/light.png)
42+
![light](https://commitpulse.vercel.app/api/badge?username=demo&theme=light)
3443

35-
Usage: `/api/streak?user=yourusername&theme=light`
44+
**neon**
3645

37-
---
46+
![neon](https://commitpulse.vercel.app/api/badge?username=demo&theme=neon)
3847

39-
## 🐙 GitHub
48+
**github**
4049

41-
![GitHub](assets/themes/github.png)
50+
![github](https://commitpulse.vercel.app/api/badge?username=demo&theme=github)
4251

43-
Usage: `/api/streak?user=yourusername&theme=github`
52+
**dracula**
4453

45-
---
54+
![dracula](https://commitpulse.vercel.app/api/badge?username=demo&theme=dracula)
4655

47-
## 🟠 Gruvbox
56+
**ocean**
4857

49-
![Gruvbox](assets/themes/gruvbox.png)
58+
![ocean](https://commitpulse.vercel.app/api/badge?username=demo&theme=ocean)
5059

51-
Usage: `/api/streak?user=yourusername&theme=gruvbox`
60+
**sunset**
5261

53-
---
62+
![sunset](https://commitpulse.vercel.app/api/badge?username=demo&theme=sunset)
5463

55-
## 🌊 Ocean
64+
**forest**
5665

57-
![Ocean](assets/themes/ocean.png)
66+
![forest](https://commitpulse.vercel.app/api/badge?username=demo&theme=forest)
5867

59-
Usage: `/api/streak?user=yourusername&theme=ocean`
68+
**rose**
6069

61-
---
70+
![rose](https://commitpulse.vercel.app/api/badge?username=demo&theme=rose)
6271

63-
## 🌅 Sunset
72+
**nord**
6473

65-
![Sunset](assets/themes/sunset.png)
74+
![nord](https://commitpulse.vercel.app/api/badge?username=demo&theme=nord)
6675

67-
Usage: `/api/streak?user=yourusername&theme=sunset`
76+
**synthwave**
6877

69-
---
78+
![synthwave](https://commitpulse.vercel.app/api/badge?username=demo&theme=synthwave)
7079

71-
## 🌲 Forest
80+
**gruvbox**
7281

73-
![Forest](assets/themes/forest.png)
82+
![gruvbox](https://commitpulse.vercel.app/api/badge?username=demo&theme=gruvbox)
7483

75-
Usage: `/api/streak?user=yourusername&theme=forest`
84+
**highcontrast**
7685

77-
---
78-
79-
## 🌹 Rose
80-
81-
![Rose](assets/themes/rose.png)
82-
83-
Usage: `/api/streak?user=yourusername&theme=rose`
86+
![highcontrast](https://commitpulse.vercel.app/api/badge?username=demo&theme=highcontrast)
8487

8588
---
8689

87-
## ❄️ Nord
88-
89-
![Nord](assets/themes/nord.png)
90-
91-
Usage: `/api/streak?user=yourusername&theme=nord`
92-
93-
## 🌆 Synthwave
94-
95-
![Synthwave](assets/themes/synthwave.png)
96-
97-
Retro 80s-inspired palette — deep indigo background, neon pink glow, crisp white text.
98-
99-
Usage: `/api/streak?user=yourusername&theme=synthwave`
100-
101-
## 🔥 High Contrast
102-
103-
Near-black background with vivid red-orange accent and medium-grey text — maximum legibility on both solid and transparent backgrounds for low-vision accessibility.
104-
105-
Usage: `/api/streak?user=yourusername&theme=highcontrast`
90+
> 💡 **Tip:** Use `?theme=auto` to automatically switch between `light` and `dark` based on the
91+
> viewer's OS setting.

app/api/og/route.test.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { describe, it, expect, vi, beforeEach } from 'vitest';
2+
import { GET } from './route';
3+
import { NextRequest } from 'next/server';
4+
vi.mock('../../../lib/github', () => ({
5+
fetchGitHubContributions: vi.fn(),
6+
}));
7+
8+
vi.mock('../../../lib/calculate', () => ({
9+
calculateStreak: vi.fn(),
10+
}));
11+
12+
import { fetchGitHubContributions } from '../../../lib/github';
13+
import { calculateStreak } from '../../../lib/calculate';
14+
15+
describe('OG Route', () => {
16+
beforeEach(() => {
17+
vi.clearAllMocks();
18+
});
19+
20+
it('returns 200 successfully', async () => {
21+
vi.mocked(fetchGitHubContributions).mockResolvedValue({} as never);
22+
23+
vi.mocked(calculateStreak).mockReturnValue({
24+
totalContributions: 120,
25+
longestStreak: 20,
26+
currentStreak: 5,
27+
todayDate: '2026-05-27',
28+
});
29+
30+
const req = new NextRequest('http://localhost:3000/api/og?user=testuser');
31+
32+
const res = await GET(req);
33+
34+
expect(res).toBeDefined();
35+
expect(res.status).toBe(200);
36+
});
37+
38+
it('falls back to zeros when github fetch fails', async () => {
39+
vi.mocked(fetchGitHubContributions).mockRejectedValue(new Error('GitHub API failed'));
40+
41+
const req = new NextRequest('http://localhost:3000/api/og?user=testuser');
42+
43+
const res = await GET(req as never);
44+
45+
expect(res.status).toBe(200);
46+
});
47+
48+
it('handles missing user query param', async () => {
49+
const req = new NextRequest('http://localhost:3000/api/og');
50+
51+
const res = await GET(req as never);
52+
53+
expect(res.status).toBe(200);
54+
});
55+
});

0 commit comments

Comments
 (0)