Skip to content

Commit f4ce3f3

Browse files
committed
feat: by default we use colors directly from the GitHub API
1 parent 30478dd commit f4ce3f3

File tree

3 files changed

+51
-20
lines changed

3 files changed

+51
-20
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ Here is the list of available options:
134134

135135
- --username (required): GitHub username
136136
- --token (required): Token with access to the user activity
137+
- --default-color: Generate light version with GitHub default colors
137138
- --dark: Generate dark version
138139
- --light: Generate light version
139140
- --no-ghost-bricks: All days are used as bricks

src/cli.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import * as path from "path";
66
interface ParsedArgs {
77
username?: string; // GitHub username
88
token?: string; // GitHub token
9+
defaultColors?: boolean; // Use default colors
910
light?: boolean; // Generate light mode SVG
1011
dark?: boolean; // Generate dark mode SVG
1112
enableGhostBricks?: boolean; // Ghost bricks for days without contribution
@@ -32,6 +33,7 @@ function getInput(name: string): string | undefined {
3233
*/
3334
function parseArgs(argv: string[]): ParsedArgs {
3435
const parsed: ParsedArgs = {
36+
defaultColors: false,
3537
dark: false,
3638
light: false,
3739
};
@@ -41,6 +43,8 @@ function parseArgs(argv: string[]): ParsedArgs {
4143
parsed.username = argv[++i];
4244
} else if (arg === "--token" && i + 1 < argv.length) {
4345
parsed.token = argv[++i];
46+
} else if (arg === "--default-colors") {
47+
parsed.defaultColors = true;
4448
} else if (arg === "--dark") {
4549
parsed.dark = true;
4650
} else if (arg === "--light") {
@@ -82,6 +86,7 @@ const bricksColorsFromInput =
8286
const options = {
8387
username: cliArgs.username || getInput("GITHUB_USERNAME"),
8488
token: cliArgs.token || getInput("GITHUB_TOKEN"),
89+
defaultColors: cliArgs.defaultColors,
8590
dark: cliArgs.dark,
8691
light: cliArgs.light,
8792
enableGhostBricks:
@@ -106,11 +111,12 @@ if (!options.username || !options.token) {
106111
if (
107112
!options.dark &&
108113
!options.light &&
114+
!options.defaultColors &&
109115
!options.bricksColors &&
110116
!options.paddleColor &&
111117
!options.ballColor
112118
) {
113-
options.light = true; // Default to light mode if neither is specified
119+
options.defaultColors = true; // Default colors if neither is specified
114120

115121
// Enable both for GitHub actions by default
116122
if (process.env.GITHUB_ACTIONS === "true") {
@@ -140,6 +146,9 @@ if (options.paddleColor || options.ballColor || options.bricksColors) {
140146
name: "custom",
141147
});
142148
}
149+
if (options.defaultColors) {
150+
variants.push({ bricksColors: undefined, name: "light" });
151+
}
143152
if (options.light) {
144153
variants.push({ bricksColors: "github_light", name: "light" });
145154
}

src/svg.ts

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -62,20 +62,30 @@ type FrameState = {
6262
bricks: BrickStatus[]; // Array of brick statuses (visible or hidden)
6363
};
6464

65+
// Interface for GitHub Contribution Graph
66+
interface GitHubContributionDay {
67+
level: 0 | 1 | 2 | 3 | 4;
68+
contributionCount: number;
69+
}
70+
71+
// Interface for GitHub Contribution Response
72+
interface GithubContributionResponse {
73+
days: (GitHubContributionDay | null)[][];
74+
defaultColorPalette: ColorPalette;
75+
}
76+
6577
/**
6678
* Fetches the GitHub contributions calendar for a user using the GraphQL API.
6779
*
6880
* @param userName - The GitHub username to fetch contributions for.
6981
* @param githubToken - A GitHub personal access token with appropriate permissions.
70-
* @returns A 2D array representing weeks and days, where each element contains the color string or null.
82+
* @returns The default color palette and a 2D array representing weeks and days, where each element contains the color string or null.
7183
* @throws Will throw an error if the API request fails or returns errors.
7284
*/
7385
async function fetchGithubContributionsGraphQL(
7486
userName: string,
7587
githubToken: string,
76-
): Promise<
77-
({ level: 0 | 1 | 2 | 3 | 4; contributionCount: number } | null)[][]
78-
> {
88+
): Promise<GithubContributionResponse> {
7989
const query = `
8090
query($userName:String!) {
8191
user(login: $userName){
@@ -85,6 +95,7 @@ async function fetchGithubContributionsGraphQL(
8595
contributionDays {
8696
contributionLevel
8797
contributionCount
98+
color
8899
}
89100
}
90101
}
@@ -115,26 +126,36 @@ async function fetchGithubContributionsGraphQL(
115126
// Format the contribution days into a 2D array of objects (weeks x days)
116127
const weeks =
117128
json.data.user.contributionsCollection.contributionCalendar.weeks;
118-
const levels: ({
119-
level: 0 | 1 | 2 | 3 | 4;
120-
contributionCount: number;
121-
} | null)[][] = [];
129+
const defaultColorPalette: Record<0 | 1 | 2 | 3 | 4, string> = {
130+
0: "#000",
131+
1: "#000",
132+
2: "#000",
133+
3: "#000",
134+
4: "#000",
135+
};
136+
const levels: (GitHubContributionDay | null)[][] = [];
122137
for (let c = 0; c < weeks.length; c++) {
123138
levels[c] = [];
124139
const days = weeks[c].contributionDays;
125140
for (let r = 0; r < days.length; r++) {
141+
const level =
142+
(days[r].contributionLevel === "FOURTH_QUARTILE" && 4) ||
143+
(days[r].contributionLevel === "THIRD_QUARTILE" && 3) ||
144+
(days[r].contributionLevel === "SECOND_QUARTILE" && 2) ||
145+
(days[r].contributionLevel === "FIRST_QUARTILE" && 1) ||
146+
0;
147+
148+
defaultColorPalette[level] = days[r].color;
126149
levels[c][r] = {
127-
level:
128-
(days[r].contributionLevel === "FOURTH_QUARTILE" && 4) ||
129-
(days[r].contributionLevel === "THIRD_QUARTILE" && 3) ||
130-
(days[r].contributionLevel === "SECOND_QUARTILE" && 2) ||
131-
(days[r].contributionLevel === "FIRST_QUARTILE" && 1) ||
132-
0,
150+
level,
133151
contributionCount: days[r].contributionCount,
134152
};
135153
}
136154
}
137-
return levels;
155+
return {
156+
days: levels,
157+
defaultColorPalette: Object.values(defaultColorPalette) as ColorPalette,
158+
};
138159
}
139160

140161
/**
@@ -334,15 +355,15 @@ export async function generateSVG(
334355
enableGhostBricks = true,
335356
paddleColor = "#1F6FEB",
336357
ballColor = "#1F6FEB",
337-
bricksColors = "github_light",
358+
bricksColors,
338359
} = options;
339360
const colorDays = await fetchGithubContributionsGraphQL(
340361
username,
341362
githubToken,
342363
);
343364

344365
// The number of columns (weeks) is determined by the API response
345-
const brickColumnCount = colorDays.length;
366+
const brickColumnCount = colorDays.days.length;
346367

347368
// Calculate canvasWidth and canvasHeight dynamically
348369
const canvasWidth =
@@ -360,7 +381,7 @@ export async function generateSVG(
360381
const canvasHeight = paddleY + PADDLE_HEIGHT + PADDING;
361382

362383
// Pick palette
363-
let colorPalette: ColorPalette = GITHUB_LIGHT;
384+
let colorPalette: ColorPalette = colorDays.defaultColorPalette;
364385
if (bricksColors === "github_light") {
365386
colorPalette = GITHUB_LIGHT;
366387
} else if (bricksColors === "github_dark") {
@@ -373,7 +394,7 @@ export async function generateSVG(
373394
const bricks: Brick[] = [];
374395
for (let c = 0; c < brickColumnCount; c++) {
375396
for (let r = 0; r < 7; r++) {
376-
const day = (colorDays[c] && colorDays[c][r]) || null;
397+
const day = (colorDays.days[c] && colorDays.days[c][r]) || null;
377398
if (!day) continue; // skip bricks for missing days
378399

379400
bricks.push({

0 commit comments

Comments
 (0)