Compare commits

...
10 changed files with 88 additions and 0 deletions
+7
View File
@@ -3,6 +3,7 @@ import { blacklist } from "../src/common/blacklist.js";
import { import {
clampValue, clampValue,
CONSTANTS, CONSTANTS,
getBase64URIFromImage,
parseBoolean, parseBoolean,
renderError, renderError,
} from "../src/common/utils.js"; } from "../src/common/utils.js";
@@ -24,6 +25,7 @@ export default async (req, res) => {
locale, locale,
border_radius, border_radius,
border_color, border_color,
show_image,
} = req.query; } = req.query;
res.setHeader("Content-Type", "image/svg+xml"); res.setHeader("Content-Type", "image/svg+xml");
@@ -68,6 +70,10 @@ export default async (req, res) => {
}, s-maxage=${cacheSeconds}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`, }, s-maxage=${cacheSeconds}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
); );
repoData.stringifiedRepoImage = await getBase64URIFromImage(
repoData.openGraphImageUrl,
);
return res.send( return res.send(
renderRepoCard(repoData, { renderRepoCard(repoData, {
hide_border: parseBoolean(hide_border), hide_border: parseBoolean(hide_border),
@@ -80,6 +86,7 @@ export default async (req, res) => {
border_color, border_color,
show_owner: parseBoolean(show_owner), show_owner: parseBoolean(show_owner),
locale: locale ? locale.toLowerCase() : null, locale: locale ? locale.toLowerCase() : null,
show_image: parseBoolean(show_image),
}), }),
); );
} catch (err) { } catch (err) {
+6
View File
@@ -386,6 +386,7 @@ If we don't support your language, please consider contributing! You can find mo
#### Repo Card Exclusive Options #### Repo Card Exclusive Options
* `show_owner` - Shows the repo's owner name *(boolean)*. Default: `false`. * `show_owner` - Shows the repo's owner name *(boolean)*. Default: `false`.
* `show_image` - Shows the repo's social preview image *(boolean)*. Default: `false`.
#### Gist Card Exclusive Options #### Gist Card Exclusive Options
@@ -447,6 +448,11 @@ Use [show\_owner](#repo-card-exclusive-options) query option to include the repo
![Readme Card](https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra\&repo=github-readme-stats\&show_owner=true) ![Readme Card](https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra\&repo=github-readme-stats\&show_owner=true)
Use [show\_image](#repo-card-exclusive-options) query option to include the repo's social preview image header
![Readme Card](https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra\&repo=github-readme-stats\&show_image=true)
# GitHub Gist Pins # GitHub Gist Pins
GitHub gist pins allow you to pin gists in your GitHub profile using a GitHub readme profile. GitHub gist pins allow you to pin gists in your GitHub profile using a GitHub readme profile.
+3
View File
@@ -61,6 +61,7 @@ const renderRepoCard = (repo, options = {}) => {
isTemplate, isTemplate,
starCount, starCount,
forkCount, forkCount,
stringifiedRepoImage,
} = repo; } = repo;
const { const {
hide_border = false, hide_border = false,
@@ -73,6 +74,7 @@ const renderRepoCard = (repo, options = {}) => {
border_radius, border_radius,
border_color, border_color,
locale, locale,
show_image = false,
} = options; } = options;
const lineHeight = 10; const lineHeight = 10;
@@ -141,6 +143,7 @@ const renderRepoCard = (repo, options = {}) => {
height, height,
border_radius, border_radius,
colors, colors,
stringifiedRepoImage: show_image ? stringifiedRepoImage : "",
}); });
card.disableAnimations(); card.disableAnimations();
+1
View File
@@ -32,6 +32,7 @@ export type StatCardOptions = CommonOptions & {
export type RepoCardOptions = CommonOptions & { export type RepoCardOptions = CommonOptions & {
show_owner: boolean; show_owner: boolean;
show_image: boolean;
}; };
export type TopLangOptions = CommonOptions & { export type TopLangOptions = CommonOptions & {
+27
View File
@@ -11,6 +11,8 @@ class Card {
* @param {string?=} args.customTitle Card custom title. * @param {string?=} args.customTitle Card custom title.
* @param {string?=} args.defaultTitle Card default title. * @param {string?=} args.defaultTitle Card default title.
* @param {string?=} args.titlePrefixIcon Card title prefix icon. * @param {string?=} args.titlePrefixIcon Card title prefix icon.
* @param {string?=} args.stringifiedRepoImage Card preview image.
* @param {number?=} args.imageHeight Card preview image.
* @param {object?=} args.colors Card colors arguments. * @param {object?=} args.colors Card colors arguments.
* @param {string} args.colors.titleColor Card title color. * @param {string} args.colors.titleColor Card title color.
* @param {string} args.colors.textColor Card text color. * @param {string} args.colors.textColor Card text color.
@@ -27,6 +29,8 @@ class Card {
customTitle, customTitle,
defaultTitle = "", defaultTitle = "",
titlePrefixIcon, titlePrefixIcon,
stringifiedRepoImage = "",
imageHeight = 200,
}) { }) {
this.width = width; this.width = width;
this.height = height; this.height = height;
@@ -36,6 +40,9 @@ class Card {
this.border_radius = border_radius; this.border_radius = border_radius;
this.imageHeight = imageHeight;
this.stringifiedRepoImage = stringifiedRepoImage;
// returns theme based colors with proper overrides and defaults // returns theme based colors with proper overrides and defaults
this.colors = colors; this.colors = colors;
this.title = this.title =
@@ -51,6 +58,10 @@ class Card {
this.animations = true; this.animations = true;
this.a11yTitle = ""; this.a11yTitle = "";
this.a11yDesc = ""; this.a11yDesc = "";
if (this.stringifiedRepoImage) {
this.height += this.imageHeight;
this.paddingY += this.imageHeight;
}
} }
/** /**
@@ -199,6 +210,21 @@ class Card {
`; `;
}; };
/**
* @returns {string} Renders social preview image
*/
renderImage = () => {
if (!this.stringifiedRepoImage) {
return "";
}
return `
<g data-testid="card-image" transform="translate(0, 0)">
<image x="0" y="0" width="${this.width}" height="${this.imageHeight}"
href="${this.stringifiedRepoImage}">
</image>
</g>`;
};
/** /**
* @param {string} body The inner body of the card. * @param {string} body The inner body of the card.
* @returns {string} The rendered card. * @returns {string} The rendered card.
@@ -264,6 +290,7 @@ class Card {
> >
${body} ${body}
</g> </g>
${this.renderImage()}
</svg> </svg>
`; `;
} }
+22
View File
@@ -569,6 +569,27 @@ const dateDiff = (d1, d2) => {
return Math.round(diff / (1000 * 60)); return Math.round(diff / (1000 * 60));
}; };
const getBase64URIFromImage = async (imageURL) => {
try {
const response = await axios.get(imageURL, {
responseType: "arraybuffer",
});
if (response.status === 200) {
const base64Image = Buffer.from(response.data, "binary").toString(
"base64",
);
const mimeType = response.headers["content-type"];
return `data:${mimeType};base64,${base64Image}`;
} else {
throw new Error("Failed to fetch the image.");
}
} catch (error) {
console.error("Error:", error);
return null;
}
};
export { export {
ERROR_CARD_LENGTH, ERROR_CARD_LENGTH,
renderError, renderError,
@@ -595,4 +616,5 @@ export {
chunkArray, chunkArray,
parseEmojis, parseEmojis,
dateDiff, dateDiff,
getBase64URIFromImage,
}; };
+1
View File
@@ -34,6 +34,7 @@ const fetcher = (variables, token) => {
name name
} }
forkCount forkCount
openGraphImageUrl
} }
query getRepo($login: String!, $repo: String!) { query getRepo($login: String!, $repo: String!) {
user(login: $login) { user(login: $login) {
+1
View File
@@ -22,6 +22,7 @@ export type RepositoryData = {
}; };
forkCount: number; forkCount: number;
starCount: number; starCount: number;
stringifiedRepoImage: string;
}; };
export type StatsData = { export type StatsData = {
@@ -150,6 +150,7 @@ exports[`Test Render WakaTime Card should render correctly with compact layout 1
</svg> </svg>
</g> </g>
</svg> </svg>
" "
`; `;
@@ -302,6 +303,7 @@ exports[`Test Render WakaTime Card should render correctly with compact layout w
</svg> </svg>
</g> </g>
</svg> </svg>
" "
`; `;
+18
View File
@@ -18,6 +18,7 @@ const data_repo = {
}, },
starCount: 38000, starCount: 38000,
forkCount: 100, forkCount: 100,
stringifiedRepoImage: "data:image/png;base64,base64/image/string",
}, },
}; };
@@ -339,4 +340,21 @@ describe("Test renderRepoCard", () => {
"No description provided", "No description provided",
); );
}); });
it("should render repo's social preview image, when show_image is true", () => {
document.body.innerHTML = renderRepoCard(data_repo.repository, {
show_image: true,
});
expect(queryByTestId(document.body, "card-image")).toBeInTheDocument();
expect(
queryByTestId(document.body, "card-image").children[0],
).toHaveAttribute("href", data_repo.repository.stringifiedRepoImage);
});
it("should not render repo's social preview image by default", () => {
document.body.innerHTML = renderRepoCard(data_repo.repository);
expect(queryByTestId(document.body, "card-image")).not.toBeInTheDocument();
});
}); });