diff --git a/README.md b/README.md index f0fdcc0..d06fb1d 100644 --- a/README.md +++ b/README.md @@ -2,38 +2,130 @@ Get dynamically generated StackOverflow stats on your readmes. -```markdown +Use the HTML `img` tag: + +```html ``` +Or use the Markdown image syntax: + +```md +![StackOverflow Card](https://stackoverflow-card-tan.vercel.app/?userID=23095094) +``` + + + +You can optionally provide a site parameter to retrieve data from a specific site in the StackExchange ecosystem.
For example: + +```html +``` + +```md +![StackOverflow Card](https://stackoverflow-card-tan.vercel.app/?userID=536223&site=ru.stackoverflow) +``` + You have to provide a valid `userID`. Apart from that, StackOverflow Card supports several options (with their default values): -``` +```js showLogo: true theme: [stackoverflow-dark, stackoverflow-light, dracula, ...] showBorder: true showIcons: true showAnimations: true +site: stackoverflow +``` +### Themes +With built-in themes, you can customize the look of the card. + +Use `&theme=THEME_NAME` parameter like so: +```md +![StackOverflow Card](https://stackoverflow-card-tan.vercel.app/?userID=536223&site=ru.stackoverflow&theme=dracula) +``` +
+Show example + +![StackOverflow Card](https://stackoverflow-card-tan.vercel.app/?userID=536223&site=ru.stackoverflow&theme=dracula) + +
+ +#### All built-in themes +StackOverflow Card comes with several built-in themes (E.g. `dracula`, `stackoverflowdark`, `stackoverflowdark`, `gruvboxdark`, `gruvboxlight`, `solarizeddark`, `solarizedlight`, `tomorrownight`, `tomorrow`). + +See [here](src/themes.js) for all available themes. + +Some card examples can be found [here](themes.md). + +#### Use GitHub's theme context tag + +You can use [GitHub's theme context](https://github.blog/changelog/2021-11-24-specify-theme-context-for-images-in-markdown/) to make the card match the user's GitHub theme automatically. Just add `#gh-dark-mode-only` or `#gh-light-mode-only` at the end of an image link. This tells GitHub how to show the card to users with a light or dark theme preference. + +```md +[![StackOverflow Card Light](https://stackoverflow-card-tan.vercel.app/?userID=536223&site=ru.stackoverflow&theme=tomorrow#gh-light-mode-only)](https://stackoverflow-card-tan.vercel.app/?userID=536223&site=ru.stackoverflow&theme=tomorrow#gh-light-mode-only) +[![StackOverflow Card Dark](https://stackoverflow-card-tan.vercel.app/?userID=536223&site=ru.stackoverflow&theme=dracula#gh-dark-mode-only)](https://stackoverflow-card-tan.vercel.app/?userID=536223&site=ru.stackoverflow&theme=dracula#gh-dark-mode-only) ``` -See [here](https://github.com/nschloe/stackoverflow-card/blob/main/src/themes.js) for -all available themes. Some examples -[here](https://github.com/nschloe/stackoverflow-card/blob/main/themes.md). +
+Show example + +[![StackOverflow Card Light](https://stackoverflow-card-tan.vercel.app/?userID=536223&site=ru.stackoverflow&theme=tomorrow#gh-light-mode-only)](https://stackoverflow-card-tan.vercel.app/?userID=536223&site=ru.stackoverflow&theme=tomorrow#gh-light-mode-only) +[![StackOverflow Card Dark](https://stackoverflow-card-tan.vercel.app/?userID=536223&site=ru.stackoverflow&theme=dracula#gh-dark-mode-only)](https://stackoverflow-card-tan.vercel.app/?userID=536223&site=ru.stackoverflow&theme=dracula#gh-dark-mode-only) + +
+ +#### Use GitHub's new media feature + +You can also use [GitHub's new media feature](https://github.blog/changelog/2022-05-19-specify-theme-context-for-images-in-markdown-beta/) in HTML to control how cards appear in light or dark themes. This is done using the `picture` element in combination with the `prefers-color-scheme` media feature. + +```html + + + + + +``` + +
+Show example + + + + + + + +
### Development Start -``` -nodemon index.js +```sh +nodemon index.js # or npm run start ``` and point a browser to -``` -http://localhost:3000/?userID=353337&theme=stackoverflow-dark -``` + +http://localhost:3000/?userID=536223&site=ru.stackoverflow&theme=stackoverflow-dark + with the desired options. diff --git a/index.js b/index.js index d1c9e89..24d766c 100644 --- a/index.js +++ b/index.js @@ -44,6 +44,15 @@ http } const userID = searchParams.get("userID"); + // Get the site parameter and remove .com if it exists + let site = searchParams.has("site") + ? searchParams.get("site") + : "stackoverflow"; + + if (site.endsWith('.com')) { + site = site.substring(0, site.length - 4); + } + const showLogo = searchParams.has("showLogo") ? stringToBoolean(searchParams.get("showLogo")) : true; @@ -64,8 +73,9 @@ http ? stringToBoolean(searchParams.get("showAnimations")) : true; + // Using site in API request const responseArticles = await fetch( - `https://api.stackexchange.com/2.3/users/${userID}?site=stackoverflow` + `https://api.stackexchange.com/2.3/users/${userID}?site=${site}` ); const json = await responseArticles.json(); @@ -75,10 +85,10 @@ http return; } + // Using site to generate URL ratings const res2 = await fetch( - `https://stackoverflow.com/users/rank\?userId\=${userID}` + `https://${site}.com/users/rank?userId=${userID}` ); - // get text, trim, and remove tags const ratingText = (await res2.text()).trim().replace(/(<([^>]+)>)/gi, ""); const result = await StackOverflowCard( @@ -98,10 +108,9 @@ http // res.setHeader("Expires", "-1"); // res.setHeader("Pragma", "no-cache"); res.writeHead(200, { "Content-Type": "image/svg+xml" }); - res.write(result); res.end(); }) .listen(process.env.PORT || 3000, function () { console.log("server start at port 3000"); - }); + }); \ No newline at end of file diff --git a/src/artwork.js b/src/artwork.js index d199035..5bfc8b2 100644 --- a/src/artwork.js +++ b/src/artwork.js @@ -14,6 +14,13 @@ export const achievementsSm = (size) => export const medal = (size) => ``; +export const source = (size) => + ``; + export function logo(color, height) { const cols = color === "default" diff --git a/src/stackoverflow-card.js b/src/stackoverflow-card.js index 07a238b..30a4f06 100644 --- a/src/stackoverflow-card.js +++ b/src/stackoverflow-card.js @@ -3,6 +3,7 @@ import { reputation, achievementsSm, medal, + source, logo, } from "./artwork.js"; import { themes } from "./themes.js"; @@ -43,6 +44,16 @@ const statLine = (icon, iconColor, label, value) => { `; }; +function getRootUrl(url) { + try { + const parsedUrl = new URL(url.startsWith('http') ? url : `https://${url}`); + return parsedUrl.hostname; + } catch (error) { + console.error('Invalid URL:', error); + return null; + } +} + export const StackOverflowCard = async ( data, ratingText, @@ -64,7 +75,7 @@ export const StackOverflowCard = async ( } const width = 325; - const height = showLogo ? 150 : 105; + let height = showLogo ? 170 : 125; let logoSvg; if (showLogo) { @@ -81,6 +92,13 @@ export const StackOverflowCard = async ( const iconSize = 16; + const lineSrc = statLine( + showIcons ? source(iconSize) : null, + colors.icon, + "Source", + getRootUrl(data.link) + ); + const lineRep = statLine( showIcons ? coinsMono(iconSize) : null, colors.icon, @@ -119,7 +137,14 @@ export const StackOverflowCard = async ( 40 ); - const lines = [lineRep, lineRepYear, lineRating, lineBadges]; + let lines + if(getRootUrl(data.link) == "stackoverflow.com"){ + lines = [lineRep, lineRepYear, lineRating, lineBadges]; + height = showLogo ? 150 : 125; + } else { + lines = [lineSrc, lineRep, lineRepYear, lineRating, lineBadges]; + } + let linesStr = ``; const yOffset = showLogo ? 55 : 0; for (let i = 0; i < lines.length; i++) {