forked from mirrored/github-readme-stats
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
533fe040a6 |
@@ -11,8 +11,6 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: Describe the bug
|
label: Describe the bug
|
||||||
description: A clear and concise description of what the bug is.
|
description: A clear and concise description of what the bug is.
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Expected behaviour
|
label: Expected behaviour
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ body:
|
|||||||
description:
|
description:
|
||||||
A clear and concise description of what the problem is. Ex. I'm always
|
A clear and concise description of what the problem is. Ex. I'm always
|
||||||
frustrated when [...]
|
frustrated when [...]
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Describe the solution you'd like
|
label: Describe the solution you'd like
|
||||||
|
|||||||
@@ -26,5 +26,5 @@ jobs:
|
|||||||
|
|
||||||
- run: npm run close-stale-theme-prs
|
- run: npm run close-stale-theme-prs
|
||||||
env:
|
env:
|
||||||
STALE_DAYS: 20
|
STALE_DAYS: 15
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
+5
-9
@@ -1,3 +1,4 @@
|
|||||||
|
import * as dotenv from "dotenv";
|
||||||
import { renderStatsCard } from "../src/cards/stats-card.js";
|
import { renderStatsCard } from "../src/cards/stats-card.js";
|
||||||
import { blacklist } from "../src/common/blacklist.js";
|
import { blacklist } from "../src/common/blacklist.js";
|
||||||
import {
|
import {
|
||||||
@@ -10,6 +11,8 @@ import {
|
|||||||
import { fetchStats } from "../src/fetchers/stats-fetcher.js";
|
import { fetchStats } from "../src/fetchers/stats-fetcher.js";
|
||||||
import { isLocaleAvailable } from "../src/translations.js";
|
import { isLocaleAvailable } from "../src/translations.js";
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
export default async (req, res) => {
|
export default async (req, res) => {
|
||||||
const {
|
const {
|
||||||
username,
|
username,
|
||||||
@@ -23,7 +26,6 @@ export default async (req, res) => {
|
|||||||
include_all_commits,
|
include_all_commits,
|
||||||
line_height,
|
line_height,
|
||||||
title_color,
|
title_color,
|
||||||
ring_color,
|
|
||||||
icon_color,
|
icon_color,
|
||||||
text_color,
|
text_color,
|
||||||
text_bold,
|
text_bold,
|
||||||
@@ -61,12 +63,7 @@ export default async (req, res) => {
|
|||||||
CONSTANTS.ONE_DAY,
|
CONSTANTS.ONE_DAY,
|
||||||
);
|
);
|
||||||
|
|
||||||
res.setHeader(
|
res.setHeader("Cache-Control", `public, max-age=${cacheSeconds}`);
|
||||||
"Cache-Control",
|
|
||||||
`max-age=${
|
|
||||||
cacheSeconds / 2
|
|
||||||
}, s-maxage=${cacheSeconds}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
return res.send(
|
return res.send(
|
||||||
renderStatsCard(stats, {
|
renderStatsCard(stats, {
|
||||||
@@ -79,7 +76,6 @@ export default async (req, res) => {
|
|||||||
include_all_commits: parseBoolean(include_all_commits),
|
include_all_commits: parseBoolean(include_all_commits),
|
||||||
line_height,
|
line_height,
|
||||||
title_color,
|
title_color,
|
||||||
ring_color,
|
|
||||||
icon_color,
|
icon_color,
|
||||||
text_color,
|
text_color,
|
||||||
text_bold: parseBoolean(text_bold),
|
text_bold: parseBoolean(text_bold),
|
||||||
@@ -93,7 +89,7 @@ export default async (req, res) => {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
res.setHeader("Cache-Control", `no-cache, no-store, must-revalidate`); // Don't cache error responses.
|
res.setHeader("Cache-Control", `no-store`); // Don't cache error responses.
|
||||||
return res.send(renderError(err.message, err.secondaryMessage));
|
return res.send(renderError(err.message, err.secondaryMessage));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+2
-7
@@ -58,12 +58,7 @@ export default async (req, res) => {
|
|||||||
cacheSeconds = CONSTANTS.FOUR_HOURS;
|
cacheSeconds = CONSTANTS.FOUR_HOURS;
|
||||||
}
|
}
|
||||||
|
|
||||||
res.setHeader(
|
res.setHeader("Cache-Control", `public, max-age=${cacheSeconds}`);
|
||||||
"Cache-Control",
|
|
||||||
`max-age=${
|
|
||||||
cacheSeconds / 2
|
|
||||||
}, s-maxage=${cacheSeconds}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
return res.send(
|
return res.send(
|
||||||
renderRepoCard(repoData, {
|
renderRepoCard(repoData, {
|
||||||
@@ -80,7 +75,7 @@ export default async (req, res) => {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
res.setHeader("Cache-Control", `no-cache, no-store, must-revalidate`); // Don't cache error responses.
|
res.setHeader("Cache-Control", `no-store`); // Don't cache error responses.
|
||||||
return res.send(renderError(err.message, err.secondaryMessage));
|
return res.send(renderError(err.message, err.secondaryMessage));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+5
-7
@@ -1,3 +1,4 @@
|
|||||||
|
import * as dotenv from "dotenv";
|
||||||
import { renderTopLanguages } from "../src/cards/top-languages-card.js";
|
import { renderTopLanguages } from "../src/cards/top-languages-card.js";
|
||||||
import { blacklist } from "../src/common/blacklist.js";
|
import { blacklist } from "../src/common/blacklist.js";
|
||||||
import {
|
import {
|
||||||
@@ -10,6 +11,8 @@ import {
|
|||||||
import { fetchTopLanguages } from "../src/fetchers/top-languages-fetcher.js";
|
import { fetchTopLanguages } from "../src/fetchers/top-languages-fetcher.js";
|
||||||
import { isLocaleAvailable } from "../src/translations.js";
|
import { isLocaleAvailable } from "../src/translations.js";
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
export default async (req, res) => {
|
export default async (req, res) => {
|
||||||
const {
|
const {
|
||||||
username,
|
username,
|
||||||
@@ -52,12 +55,7 @@ export default async (req, res) => {
|
|||||||
CONSTANTS.ONE_DAY,
|
CONSTANTS.ONE_DAY,
|
||||||
);
|
);
|
||||||
|
|
||||||
res.setHeader(
|
res.setHeader("Cache-Control", `public, max-age=${cacheSeconds}`);
|
||||||
"Cache-Control",
|
|
||||||
`max-age=${
|
|
||||||
cacheSeconds / 2
|
|
||||||
}, s-maxage=${cacheSeconds}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
return res.send(
|
return res.send(
|
||||||
renderTopLanguages(topLangs, {
|
renderTopLanguages(topLangs, {
|
||||||
@@ -78,7 +76,7 @@ export default async (req, res) => {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
res.setHeader("Cache-Control", `no-cache, no-store, must-revalidate`); // Don't cache error responses.
|
res.setHeader("Cache-Control", `no-store`); // Don't cache error responses.
|
||||||
return res.send(renderError(err.message, err.secondaryMessage));
|
return res.send(renderError(err.message, err.secondaryMessage));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+5
-7
@@ -1,3 +1,4 @@
|
|||||||
|
import * as dotenv from "dotenv";
|
||||||
import { renderWakatimeCard } from "../src/cards/wakatime-card.js";
|
import { renderWakatimeCard } from "../src/cards/wakatime-card.js";
|
||||||
import {
|
import {
|
||||||
clampValue,
|
clampValue,
|
||||||
@@ -9,6 +10,8 @@ import {
|
|||||||
import { fetchWakatimeStats } from "../src/fetchers/wakatime-fetcher.js";
|
import { fetchWakatimeStats } from "../src/fetchers/wakatime-fetcher.js";
|
||||||
import { isLocaleAvailable } from "../src/translations.js";
|
import { isLocaleAvailable } from "../src/translations.js";
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
export default async (req, res) => {
|
export default async (req, res) => {
|
||||||
const {
|
const {
|
||||||
username,
|
username,
|
||||||
@@ -52,12 +55,7 @@ export default async (req, res) => {
|
|||||||
cacheSeconds = CONSTANTS.FOUR_HOURS;
|
cacheSeconds = CONSTANTS.FOUR_HOURS;
|
||||||
}
|
}
|
||||||
|
|
||||||
res.setHeader(
|
res.setHeader("Cache-Control", `public, max-age=${cacheSeconds}`);
|
||||||
"Cache-Control",
|
|
||||||
`max-age=${
|
|
||||||
cacheSeconds / 2
|
|
||||||
}, s-maxage=${cacheSeconds}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
return res.send(
|
return res.send(
|
||||||
renderWakatimeCard(stats, {
|
renderWakatimeCard(stats, {
|
||||||
@@ -80,7 +78,7 @@ export default async (req, res) => {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
res.setHeader("Cache-Control", `no-cache, no-store, must-revalidate`); // Don't cache error responses.
|
res.setHeader("Cache-Control", `no-store`); // Don't cache error responses.
|
||||||
return res.send(renderError(err.message, err.secondaryMessage));
|
return res.send(renderError(err.message, err.secondaryMessage));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+3
-3
@@ -1,7 +1,7 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img width="100px" src="https://res.cloudinary.com/anuraghazra/image/upload/v1594908242/logo_ccswme.svg" align="center" alt="GitHub Readme Stats" />
|
<img width="100px" src="https://res.cloudinary.com/anuraghazra/image/upload/v1594908242/logo_ccswme.svg" align="center" alt="GitHub Readme Stats" />
|
||||||
<h2 align="center">GitHub Readme Stats</h2>
|
<h2 align="center">GitHub Readme Stats</h2>
|
||||||
<p align="center">동적으로 생성되는 GitHub 사용량 통계를 여러분의 README 에 추가해보세요!</p>
|
<p align="center">동적으로 생성되는 Github 사용량 통계를 여러분의 README 에 추가해보세요!</p>
|
||||||
</p>
|
</p>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://github.com/anuraghazra/github-readme-stats/actions">
|
<a href="https://github.com/anuraghazra/github-readme-stats/actions">
|
||||||
@@ -72,7 +72,7 @@
|
|||||||
|
|
||||||
아래 코드를 복사해서 마크다운 파일에 붙여넣으면 끝이에요, 아주 간단해요!
|
아래 코드를 복사해서 마크다운 파일에 붙여넣으면 끝이에요, 아주 간단해요!
|
||||||
|
|
||||||
`?username=` 속성의 값을 GitHub 계정의 사용자 명(닉네임)으로 바꿔주세요.
|
`?username=` 속성의 값을 Github 계정의 사용자 명(닉네임)으로 바꿔주세요.
|
||||||
|
|
||||||
```md
|
```md
|
||||||
[](https://github.com/anuraghazra/github-readme-stats)
|
[](https://github.com/anuraghazra/github-readme-stats)
|
||||||
@@ -235,7 +235,7 @@ GitHub 저장소 여분 핀을 이용하면, 6개 이상의 저장소 핀을 여
|
|||||||
|
|
||||||
# 언어 사용량 통계
|
# 언어 사용량 통계
|
||||||
|
|
||||||
언어 사용량 통계 카드는 GitHub 사용자가 가장 많이 사용한 언어가 표시됩니다.
|
언어 사용량 통계 카드는 Github 사용자가 가장 많이 사용한 언어가 표시됩니다.
|
||||||
|
|
||||||
_참고:
|
_참고:
|
||||||
언어 사용량 통계는 GitHub 에서 가장 많이 사용된 언어의 표기일 뿐입니다.
|
언어 사용량 통계는 GitHub 에서 가장 많이 사용된 언어의 표기일 뿐입니다.
|
||||||
|
|||||||
+2
-2
@@ -219,7 +219,7 @@ Use [show_owner](#customization) variable to include the repo's owner username
|
|||||||
|
|
||||||
टोप भाषाकार्डले github परयोग गर्नेहरुको प्रोग्रम्मिंग भाषाहरु देखाऊने गर्दछ |.
|
टोप भाषाकार्डले github परयोग गर्नेहरुको प्रोग्रम्मिंग भाषाहरु देखाऊने गर्दछ |.
|
||||||
|
|
||||||
_NOTE: टोप भाषाहरुले आफ्नो सिपलाए संकेत गरेको होईन | योचै GitHub Metricबाट धेरै कुन भाषा परयोग भाकोलाए संकेत गरेको हो |
|
_NOTE: टोप भाषाहरुले आफ्नो सिपलाए संकेत गरेको होईन | योचै Github Metricबाट धेरै कुन भाषा परयोग भाकोलाए संकेत गरेको हो |
|
||||||
### प्रयोग
|
### प्रयोग
|
||||||
|
|
||||||
कोदलाए कपी- पेसेत readme मा गर्नु होला र लिंक परिवतन गर्नु होला |
|
कोदलाए कपी- पेसेत readme मा गर्नु होला र लिंक परिवतन गर्नु होला |
|
||||||
@@ -389,7 +389,7 @@ NOTE: Since [#58](https://github.com/anuraghazra/github-readme-stats/pull/58) we
|
|||||||
येदि तपाइले यो प्रोजेक्ट चलाउनु बाकोक्ष बने र मलाई अझै प्रसंसा गर्ने हो बने तपाइले थुप्रै तरिका ले गर्नु सक्नु हुने छ :-
|
येदि तपाइले यो प्रोजेक्ट चलाउनु बाकोक्ष बने र मलाई अझै प्रसंसा गर्ने हो बने तपाइले थुप्रै तरिका ले गर्नु सक्नु हुने छ :-
|
||||||
|
|
||||||
- यो प्रोजेक्टमा तपाइले प्रहयोग गर्दा मलाई क्रेडिट दिन सक्नु हुनेक्ष ।
|
- यो प्रोजेक्टमा तपाइले प्रहयोग गर्दा मलाई क्रेडिट दिन सक्नु हुनेक्ष ।
|
||||||
- तपाइले GitHub ReadMe Stats स्तार्रेड गर्न सक्नु हुनेक्ष :rocket:
|
- तपाइले Github ReadMe Stats स्तार्रेड गर्न सक्नु हुनेक्ष :rocket:
|
||||||
- [](https://www.paypal.me/anuraghazra) - तपाइले पेपाल बाट पनि सहयोग (डक्क्षिन) गर्न सक्नु हुनेक्ष | म ~~कोफी ~~ चिया . :tea: किन्न सक्क्षु ।
|
- [](https://www.paypal.me/anuraghazra) - तपाइले पेपाल बाट पनि सहयोग (डक्क्षिन) गर्न सक्नु हुनेक्ष | म ~~कोफी ~~ चिया . :tea: किन्न सक्क्षु ।
|
||||||
|
|
||||||
धन्याबाद! :heart:
|
धन्याबाद! :heart:
|
||||||
|
|||||||
@@ -53,8 +53,6 @@
|
|||||||
<a href="/docs/readme_nl.md">Nederlands</a>
|
<a href="/docs/readme_nl.md">Nederlands</a>
|
||||||
.
|
.
|
||||||
<a href="/docs/readme_np.md">नेपाली</a>
|
<a href="/docs/readme_np.md">नेपाली</a>
|
||||||
.
|
|
||||||
<a href="/docs/readme_tr.md">Türkçe</a>
|
|
||||||
</p>
|
</p>
|
||||||
</p>
|
</p>
|
||||||
<p align="center">Gostou do projeto? Por favor considere <a href="https://www.paypal.me/anuraghazra">fazer uma doação</a> para ajudar a melhorá-lo!
|
<p align="center">Gostou do projeto? Por favor considere <a href="https://www.paypal.me/anuraghazra">fazer uma doação</a> para ajudar a melhorá-lo!
|
||||||
@@ -144,7 +142,7 @@ Personalize a aparência do seu `Stats Card` ou `Repo Card` da maneira que desej
|
|||||||
- `cache_seconds` - Defina o cabeçalho do cache manualmente _(min: 1800, max: 86400)_
|
- `cache_seconds` - Defina o cabeçalho do cache manualmente _(min: 1800, max: 86400)_
|
||||||
- `locale` - defina o idioma no cartão _(por exemplo. cn, de, es, etc.)_
|
- `locale` - defina o idioma no cartão _(por exemplo. cn, de, es, etc.)_
|
||||||
|
|
||||||
> Nota sobre o cache: Cartões de repositório tem um cache padrão de 30 minutos (1800 segundos), se o número a contagem de forks e contagem de estrelas é menor que 1 mil o padrão é 2 horas (7200 segundos). Note também que o cache é limitado a um mínimo de 30 minutos e um máximo de 24 horas.
|
> Nota sobre o cache: Cartões de repositório tem um cache padrão de 30 minutos (1800 segundos), se o número a contagem de forks e contagem de estrelas é menor que 1 mil o padrão é 2 horas (7200). Note também que o cache é limitado a um mínimo de 30 minutos e um máximo de 24 horas.
|
||||||
|
|
||||||
#### Opções exclusivas do cartão de estatísticas:
|
#### Opções exclusivas do cartão de estatísticas:
|
||||||
|
|
||||||
@@ -169,7 +167,7 @@ Personalize a aparência do seu `Stats Card` ou `Repo Card` da maneira que desej
|
|||||||
|
|
||||||
> :warning: **Importante:**
|
> :warning: **Importante:**
|
||||||
> Nomes de linguagens devem ser uma sequência escapada de URI, como específicado em [Codificação por cento](https://pt.wikipedia.org/wiki/Codificação_por_cento)
|
> Nomes de linguagens devem ser uma sequência escapada de URI, como específicado em [Codificação por cento](https://pt.wikipedia.org/wiki/Codificação_por_cento)
|
||||||
> (Ou seja: `c++` deve se tornar `c%2B%2B`, `jupyter notebook` deve se tornar `jupyter%20notebook`, etc.)
|
> (Isso é: `c++` deve se tornar `c%2B%2B`, `jupyter notebook` deve se tornar `jupyter%20notebook`, etc.)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+7
-7
@@ -73,10 +73,10 @@
|
|||||||
|
|
||||||
Alt kısımdaki kodu Kopyalayın ve yapıştırın. İşte bu kadar. Çok basit!
|
Alt kısımdaki kodu Kopyalayın ve yapıştırın. İşte bu kadar. Çok basit!
|
||||||
|
|
||||||
`?username=` değerini kendi GitHub kullanıcı adınız ile değiştirin.
|
`?username=` değerini kendi Github kullanıcı adınız ile değiştirin.
|
||||||
|
|
||||||
```md
|
```md
|
||||||
[](https://github.com/anuraghazra/github-readme-stats)
|
[](https://github.com/anuraghazra/github-readme-stats)
|
||||||
```
|
```
|
||||||
_Not: Şu sıralamalar mevcut: S+ (en üst 1%), S (en üst 25%), A++ (en üst 45%), A+ (en üst 60%), and B+ (herkes).
|
_Not: Şu sıralamalar mevcut: S+ (en üst 1%), S (en üst 25%), A++ (en üst 45%), A+ (en üst 60%), and B+ (herkes).
|
||||||
Buradaki değerler [cumulative distribution function](https://en.wikipedia.org/wiki/Cumulative_distribution_function) ile hesaplanırken; commitler, katkılar, hatalar, yıldızlar, çekme istekleri, takipçiler ve sahip olunan depolar (repository) göz önünde bulundurulamaktadır.
|
Buradaki değerler [cumulative distribution function](https://en.wikipedia.org/wiki/Cumulative_distribution_function) ile hesaplanırken; commitler, katkılar, hatalar, yıldızlar, çekme istekleri, takipçiler ve sahip olunan depolar (repository) göz önünde bulundurulamaktadır.
|
||||||
@@ -199,7 +199,7 @@ bg_color içerisinde birden fazla rengi gradient olarak göstermek için virgül
|
|||||||
|
|
||||||
# GitHub Ekstra Pinler
|
# GitHub Ekstra Pinler
|
||||||
|
|
||||||
GitHub ekstra pinler profilinize 6'dan fazla repoyu / depoyu profilinizde pinleyebilirsiniz.
|
Github ekstra pinler profilinize 6'dan fazla repoyu / depoyu profilinizde pinleyebilirsiniz.
|
||||||
|
|
||||||
Hey! Artık 6 pin ile kısıtlı kalmayacaksınız!
|
Hey! Artık 6 pin ile kısıtlı kalmayacaksınız!
|
||||||
|
|
||||||
@@ -225,7 +225,7 @@ Endpoint: `api/pin?username=mustafacagri&repo=github-readme-stats`
|
|||||||
|
|
||||||
En çok kullanılan diller kartı kullanıcının en çok kullandığı dilleri gösterir.
|
En çok kullanılan diller kartı kullanıcının en çok kullandığı dilleri gösterir.
|
||||||
|
|
||||||
_NOTE: En çok kullanılan dillerde yer alan bilgiler sizin yeteneğinizi ve benzeri şeyleri göstermek. Bu, kodlarınızda en çok kullandığınız dilleri gösteren bir GitHub metriğidir. Ayrıca, github-readme-stats'ın yeni özelliğidir.
|
_NOTE: En çok kullanılan dillerde yer alan bilgiler sizin yeteneğinizi ve benzeri şeyleri göstermek. Bu, kodlarınızda en çok kullandığınız dilleri gösteren bir Github metriğidir. Ayrıca, github-readme-stats'ın yeni özelliğidir.
|
||||||
|
|
||||||
### Kullanım
|
### Kullanım
|
||||||
|
|
||||||
@@ -365,7 +365,7 @@ Genellikle resimleri yan yana düzenleyemezsiniz. Bunu yapmak için şu yaklaş
|
|||||||
|
|
||||||
#### [@codeSTACKr'ın Yayınladığı Video Eğitimine Göz Atın](https://youtu.be/n6d4KHSKqGk?t=107)
|
#### [@codeSTACKr'ın Yayınladığı Video Eğitimine Göz Atın](https://youtu.be/n6d4KHSKqGk?t=107)
|
||||||
|
|
||||||
GitHub API saatte sadece 5.000 isteğe izin verdiği için `https://github-readme-stats.vercel.app/api` adresindeki API'm bu limite muhtemelen takılmış olabilir. Eğer projeyi kendi Vercel sunucunuzda yayınlarsanız, böyle bir sorun yaşamayabilirsiniz. Deploy butonuna tıkla ve deploy başlasın!
|
Github API saatte sadece 5.000 isteğe izin verdiği için `https://github-readme-stats.vercel.app/api` adresindeki API'm bu limite muhtemelen takılmış olabilir. Eğer projeyi kendi Vercel sunucunuzda yayınlarsanız, böyle bir sorun yaşamayabilirsiniz. Deploy butonuna tıkla ve deploy başlasın!
|
||||||
|
|
||||||
|
|
||||||
NOT: [#58](https://github.com/anuraghazra/github-readme-stats/pull/58) geliştirmesi sonrasında anlamadığımız bir şekilde 5.000 istek limitine takılmıyoruz :)
|
NOT: [#58](https://github.com/anuraghazra/github-readme-stats/pull/58) geliştirmesi sonrasında anlamadığımız bir şekilde 5.000 istek limitine takılmıyoruz :)
|
||||||
@@ -378,9 +378,9 @@ NOT: [#58](https://github.com/anuraghazra/github-readme-stats/pull/58) geliştir
|
|||||||
1. [vercel.com](https://vercel.com/) adresine gidin
|
1. [vercel.com](https://vercel.com/) adresine gidin
|
||||||
1. `Log in`'e tıklayın
|
1. `Log in`'e tıklayın
|
||||||

|

|
||||||
1. `Continue with GitHub`'e basarak GitHub ile giriş yapın
|
1. `Continue with GitHub`'e basarak Github ile giriş yapın
|
||||||

|

|
||||||
1. GitHub'a giriş yapın ve eğer çıkarsa tüm repolara izin verin.
|
1. Github'a giriş yapın ve eğer çıkarsa tüm repolara izin verin.
|
||||||
1. Bu repoyu fork'layın
|
1. Bu repoyu fork'layın
|
||||||
1. [Vercel dashboard](https://vercel.com/dashboard)'unuza geri dönün.
|
1. [Vercel dashboard](https://vercel.com/dashboard)'unuza geri dönün.
|
||||||
1. `Import Project`'i seçin.
|
1. `Import Project`'i seçin.
|
||||||
|
|||||||
@@ -85,7 +85,6 @@ Visit <https://indiafightscorona.giveindia.org> and make a small donation to hel
|
|||||||
- [Top Languages Card](#top-languages-card)
|
- [Top Languages Card](#top-languages-card)
|
||||||
- [Wakatime Week Stats](#wakatime-week-stats)
|
- [Wakatime Week Stats](#wakatime-week-stats)
|
||||||
- [Themes](#themes)
|
- [Themes](#themes)
|
||||||
- [Responsive Card Theme](#responsive-card-theme)
|
|
||||||
- [Customization](#customization)
|
- [Customization](#customization)
|
||||||
- [Common Options](#common-options)
|
- [Common Options](#common-options)
|
||||||
- [Stats Card Exclusive Options](#stats-card-exclusive-options)
|
- [Stats Card Exclusive Options](#stats-card-exclusive-options)
|
||||||
@@ -151,101 +150,12 @@ Use `&theme=THEME_NAME` parameter like so :
|
|||||||
|
|
||||||
#### All inbuilt themes
|
#### All inbuilt themes
|
||||||
|
|
||||||
GitHub readme stats comes with several built-in themes (e.g. `dark`, `radical`, `merko`, `gruvbox`, `tokyonight`, `onedark`, `cobalt`, `synthwave`, `highcontrast`, `dracula`).
|
Github readme stats comes with several built-in themes (e.g. `dark`, `radical`, `merko`, `gruvbox`, `tokyonight`, `onedark`, `cobalt`, `synthwave`, `highcontrast`, `dracula`).
|
||||||
|
|
||||||
<img src="https://res.cloudinary.com/anuraghazra/image/upload/v1595174536/grs-themes_l4ynja.png" alt="GitHub Readme Stats Themes" width="600px"/>
|
<img src="https://res.cloudinary.com/anuraghazra/image/upload/v1595174536/grs-themes_l4ynja.png" alt="GitHub Readme Stats Themes" width="600px"/>
|
||||||
|
|
||||||
You can look at a preview for [all available themes](./themes/README.md) or checkout the [theme config file](./themes/index.js) & **you can also contribute new themes** if you like :D
|
You can look at a preview for [all available themes](./themes/README.md) or checkout the [theme config file](./themes/index.js) & **you can also contribute new themes** if you like :D
|
||||||
|
|
||||||
#### Responsive Card Theme
|
|
||||||
|
|
||||||
[](https://github.com/anuraghazra/github-readme-stats#gh-dark-mode-only)
|
|
||||||
[](https://github.com/anuraghazra/github-readme-stats#gh-light-mode-only)
|
|
||||||
|
|
||||||
Since GitHub will re-upload the cards and serve them from their [CDN](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/about-anonymized-urls), we can not infer the browser/GitHub theme on the server side. There are, however, four methods you can use to create dynamics themes on the client side.
|
|
||||||
|
|
||||||
##### Use the transparent theme
|
|
||||||
|
|
||||||
We have included a `transparent` theme that has a transparent background. This theme is optimized to look good on GitHub's dark and light default themes. You can enable this theme using the `&theme=transparent` parameter like so:
|
|
||||||
|
|
||||||
```md
|
|
||||||

|
|
||||||
```
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>:eyes: Show example</summary>
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
##### Add transparent alpha channel to a themes bg_color
|
|
||||||
|
|
||||||
You can use the `bg_color` parameter to make any of [the available themes](./themes/README.md) transparent. This is done by setting the `bg_color` to a colour with a transparent alpha channel (i.e. `bg_color=00000000`):
|
|
||||||
|
|
||||||
```md
|
|
||||||

|
|
||||||
```
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>:eyes: Show example</summary>
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
##### 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/) tags to switch the theme based on the user GitHub theme automatically. This is done by appending `#gh-dark-mode-only` or `#gh-light-mode-only` to the end of an image URL. This tag will define whether the image specified in the markdown is only shown to viewers using a light or a dark GitHub theme:
|
|
||||||
|
|
||||||
```md
|
|
||||||
[](https://github.com/anuraghazra/github-readme-stats#gh-dark-mode-only)
|
|
||||||
[](https://github.com/anuraghazra/github-readme-stats#gh-light-mode-only)
|
|
||||||
```
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>:eyes: Show example</summary>
|
|
||||||
|
|
||||||
[](https://github.com/anuraghazra/github-readme-stats#gh-dark-mode-only)
|
|
||||||
[](https://github.com/anuraghazra/github-readme-stats#gh-light-mode-only)
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
##### Use GitHub's new media feature
|
|
||||||
|
|
||||||
You can use [GitHub's new media feature](https://github.blog/changelog/2022-05-19-specify-theme-context-for-images-in-markdown-beta/) in HTML to specify whether to display images for light or dark themes. This is done using the HTML `<picture>` element in combination with the `prefers-color-scheme` media feature.
|
|
||||||
|
|
||||||
```html
|
|
||||||
<picture>
|
|
||||||
<source
|
|
||||||
srcset="https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&theme=dark"
|
|
||||||
media="(prefers-color-scheme: dark)"
|
|
||||||
/>
|
|
||||||
<source
|
|
||||||
srcset="https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true"
|
|
||||||
media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)"
|
|
||||||
/>
|
|
||||||
<img src="https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true" />
|
|
||||||
</picture>
|
|
||||||
```
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>:eyes: Show example</summary>
|
|
||||||
|
|
||||||
<picture>
|
|
||||||
<source
|
|
||||||
srcset="https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&theme=dark"
|
|
||||||
media="(prefers-color-scheme: dark)"
|
|
||||||
/>
|
|
||||||
<source
|
|
||||||
srcset="https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true"
|
|
||||||
media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)"
|
|
||||||
/>
|
|
||||||
<img src="https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true" />
|
|
||||||
</picture>
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
### Customization
|
### Customization
|
||||||
|
|
||||||
You can customize the appearance of your `Stats Card` or `Repo Card` however you wish with URL parameters.
|
You can customize the appearance of your `Stats Card` or `Repo Card` however you wish with URL parameters.
|
||||||
@@ -283,10 +193,9 @@ You can provide multiple comma-separated values in the bg_color option to render
|
|||||||
- `count_private` - Count private commits _(boolean)_. Default: `false`.
|
- `count_private` - Count private commits _(boolean)_. Default: `false`.
|
||||||
- `line_height` - Sets the line height between text _(number)_. Default: `25`.
|
- `line_height` - Sets the line height between text _(number)_. Default: `25`.
|
||||||
- `exclude_repo` - Exclude stars from specified repositories _(Comma-separated values)_. Default: `[] (blank array)`.
|
- `exclude_repo` - Exclude stars from specified repositories _(Comma-separated values)_. Default: `[] (blank array)`.
|
||||||
- `custom_title` - Sets a custom title for the card. Default: `<username> GitHub Stats`.
|
- `custom_title` - Sets a custom title for the card. Default: `<username> Github Stats`.
|
||||||
- `text_bold` - Use bold text _(boolean)_. Default: `true`.
|
- `text_bold` - Use bold text _(boolean)_. Default: `true`.
|
||||||
- `disable_animations` - Disables all animations in the card _(boolean)_. Default: `false`.
|
- `disable_animations` - Disables all animations in the card _(boolean)_. Default: `false`.
|
||||||
- `ring_color` - Color of the rank circle _(hex color)_. Defaults to the theme ring color if it exists and otherwise the title color.
|
|
||||||
|
|
||||||
> **Note**
|
> **Note**
|
||||||
> When hide_rank=`true`, the minimum card width is 270 px + the title length and padding.
|
> When hide_rank=`true`, the minimum card width is 270 px + the title length and padding.
|
||||||
|
|||||||
@@ -9,28 +9,19 @@ import github from "@actions/github";
|
|||||||
import { RequestError } from "@octokit/request-error";
|
import { RequestError } from "@octokit/request-error";
|
||||||
import { getGithubToken, getRepoInfo } from "./helpers.js";
|
import { getGithubToken, getRepoInfo } from "./helpers.js";
|
||||||
|
|
||||||
|
// Script parameters
|
||||||
const CLOSING_COMMENT = `
|
const CLOSING_COMMENT = `
|
||||||
\rThis theme PR has been automatically closed due to inactivity. Please reopen it if you want to continue working on it.\
|
\rThis PR has been automatically closed due to inactivity. Please feel free to reopen it if you need to continue working on it.\
|
||||||
\rThank you for your contributions.
|
\rThank you for your contributions.
|
||||||
`;
|
`;
|
||||||
const REVIEWER = "github-actions[bot]";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the review user.
|
|
||||||
* @returns {string} review user.
|
|
||||||
*/
|
|
||||||
const getReviewer = () => {
|
|
||||||
return process.env.REVIEWER ? process.env.REVIEWER : REVIEWER;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch open PRs from a given repository.
|
* Fetch open PRs from a given repository.
|
||||||
* @param user The user name of the repository owner.
|
* @param user The user name of the repository owner.
|
||||||
* @param repo The name of the repository.
|
* @param repo The name of the repository.
|
||||||
* @param reviewer The reviewer to filter by.
|
|
||||||
* @returns The open PRs.
|
* @returns The open PRs.
|
||||||
*/
|
*/
|
||||||
export const fetchOpenPRs = async (octokit, user, repo, reviewer) => {
|
export const fetchOpenPRs = async (octokit, user, repo) => {
|
||||||
const openPRs = [];
|
const openPRs = [];
|
||||||
let hasNextPage = true;
|
let hasNextPage = true;
|
||||||
let endCursor;
|
let endCursor;
|
||||||
@@ -58,9 +49,9 @@ export const fetchOpenPRs = async (octokit, user, repo, reviewer) => {
|
|||||||
name
|
name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reviews(first: 100, states: CHANGES_REQUESTED, author: "${reviewer}") {
|
reviews(first: 1, states: CHANGES_REQUESTED, author: "github-actions[bot]") {
|
||||||
nodes {
|
nodes {
|
||||||
submittedAt
|
updatedAt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,13 +99,11 @@ const isStale = (pullRequest, staleDays) => {
|
|||||||
pullRequest.commits.nodes[0].commit.pushedDate,
|
pullRequest.commits.nodes[0].commit.pushedDate,
|
||||||
);
|
);
|
||||||
if (pullRequest.reviews.nodes[0]) {
|
if (pullRequest.reviews.nodes[0]) {
|
||||||
const lastReviewDate = new Date(
|
const lastReviewDate = new Date(pullRequest.reviews.nodes[0].updatedAt);
|
||||||
pullRequest.reviews.nodes.sort((a, b) => (a < b ? 1 : -1))[0].submittedAt,
|
|
||||||
);
|
|
||||||
const lastUpdateDate =
|
const lastUpdateDate =
|
||||||
lastCommitDate >= lastReviewDate ? lastCommitDate : lastReviewDate;
|
lastCommitDate >= lastReviewDate ? lastCommitDate : lastReviewDate;
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
return (now - lastUpdateDate) / (1000 * 60 * 60 * 24) >= staleDays;
|
return now - lastUpdateDate > 1000 * 60 * 60 * 24 * staleDays;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -127,15 +116,14 @@ const run = async () => {
|
|||||||
try {
|
try {
|
||||||
// Create octokit client.
|
// Create octokit client.
|
||||||
const dryRun = process.env.DRY_RUN === "true" || false;
|
const dryRun = process.env.DRY_RUN === "true" || false;
|
||||||
const staleDays = process.env.STALE_DAYS || 20;
|
const staleDays = process.env.STALE_DAYS || 15;
|
||||||
debug("Creating octokit client...");
|
debug("Creating octokit client...");
|
||||||
const octokit = github.getOctokit(getGithubToken());
|
const octokit = github.getOctokit(getGithubToken());
|
||||||
const { owner, repo } = getRepoInfo(github.context);
|
const { owner, repo } = getRepoInfo(github.context);
|
||||||
const reviewer = getReviewer();
|
|
||||||
|
|
||||||
// Retrieve all theme pull requests.
|
// Retrieve all theme pull requests.
|
||||||
debug("Retrieving all theme pull requests...");
|
debug("Retrieving all theme pull requests...");
|
||||||
const prs = await fetchOpenPRs(octokit, owner, repo, reviewer);
|
const prs = await fetchOpenPRs(octokit, owner, repo);
|
||||||
const themePRs = pullsWithLabel(prs, "themes");
|
const themePRs = pullsWithLabel(prs, "themes");
|
||||||
const invalidThemePRs = pullsWithLabel(themePRs, "invalid");
|
const invalidThemePRs = pullsWithLabel(themePRs, "invalid");
|
||||||
debug("Retrieving stale theme PRs...");
|
debug("Retrieving stale theme PRs...");
|
||||||
|
|||||||
+2
-1
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
import { getInput } from "@actions/core";
|
import { getInput } from "@actions/core";
|
||||||
|
|
||||||
|
// Script variables.
|
||||||
const OWNER = "anuraghazra";
|
const OWNER = "anuraghazra";
|
||||||
const REPO = "github-readme-stats";
|
const REPO = "github-readme-stats";
|
||||||
|
|
||||||
@@ -30,7 +31,7 @@ export const getRepoInfo = (ctx) => {
|
|||||||
/**
|
/**
|
||||||
* Retrieve github token and throw error if it is not found.
|
* Retrieve github token and throw error if it is not found.
|
||||||
*
|
*
|
||||||
* @returns {string} GitHub token.
|
* @returns {string} Github token.
|
||||||
*/
|
*/
|
||||||
export const getGithubToken = () => {
|
export const getGithubToken = () => {
|
||||||
const token = getInput("github_token") || process.env.GITHUB_TOKEN;
|
const token = getInput("github_token") || process.env.GITHUB_TOKEN;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { isValidHexColor } from "../src/common/utils.js";
|
|||||||
import { themes } from "../themes/index.js";
|
import { themes } from "../themes/index.js";
|
||||||
import { getGithubToken, getRepoInfo } from "./helpers.js";
|
import { getGithubToken, getRepoInfo } from "./helpers.js";
|
||||||
|
|
||||||
|
// Script variables.
|
||||||
const COMMENTER = "github-actions[bot]";
|
const COMMENTER = "github-actions[bot]";
|
||||||
|
|
||||||
const COMMENT_TITLE = "Automated Theme Preview";
|
const COMMENT_TITLE = "Automated Theme Preview";
|
||||||
@@ -24,7 +25,7 @@ const THEME_PR_SUCCESS_TEXT =
|
|||||||
":heavy_check_mark: Theme PR does adhere to our guidelines.";
|
":heavy_check_mark: Theme PR does adhere to our guidelines.";
|
||||||
const FAIL_TEXT = `
|
const FAIL_TEXT = `
|
||||||
\rUnfortunately, your theme PR contains an error or does not adhere to our [theme guidelines](https://github.com/anuraghazra/github-readme-stats/blob/master/CONTRIBUTING.md#themes-contribution). Please fix the issues below, and we will review your\
|
\rUnfortunately, your theme PR contains an error or does not adhere to our [theme guidelines](https://github.com/anuraghazra/github-readme-stats/blob/master/CONTRIBUTING.md#themes-contribution). Please fix the issues below, and we will review your\
|
||||||
\r PR again. This pull request will **automatically close in 20 days** if no changes are made. After this time, you must re-open the PR for it to be reviewed.
|
\r PR again. This pull request will **automatically close in 15 days** if no changes are made. After this time, you must re-open the PR for it to be reviewed.
|
||||||
`;
|
`;
|
||||||
const THEME_CONTRIB_GUIDELINESS = `
|
const THEME_CONTRIB_GUIDELINESS = `
|
||||||
\rHi, thanks for the theme contribution. Please read our theme [contribution guidelines](https://github.com/anuraghazra/github-readme-stats/blob/master/CONTRIBUTING.md#themes-contribution).
|
\rHi, thanks for the theme contribution. Please read our theme [contribution guidelines](https://github.com/anuraghazra/github-readme-stats/blob/master/CONTRIBUTING.md#themes-contribution).
|
||||||
@@ -92,7 +93,7 @@ const isPreviewComment = (inputs, comment) => {
|
|||||||
* @param {number} issueNumber Issue number.
|
* @param {number} issueNumber Issue number.
|
||||||
* @param {string} repo Repository name.
|
* @param {string} repo Repository name.
|
||||||
* @param {string} owner Owner of the repository.
|
* @param {string} owner Owner of the repository.
|
||||||
* @returns {Object} The GitHub comment object.
|
* @returns {Object} The Github comment object.
|
||||||
*/
|
*/
|
||||||
const findComment = async (octokit, issueNumber, owner, repo, commenter) => {
|
const findComment = async (octokit, issueNumber, owner, repo, commenter) => {
|
||||||
const parameters = {
|
const parameters = {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ set -e
|
|||||||
export BRANCH_NAME=updated-theme-readme
|
export BRANCH_NAME=updated-theme-readme
|
||||||
git --version
|
git --version
|
||||||
git config --global user.email "no-reply@githubreadmestats.com"
|
git config --global user.email "no-reply@githubreadmestats.com"
|
||||||
git config --global user.name "GitHub Readme Stats Bot"
|
git config --global user.name "Github Readme Stats Bot"
|
||||||
git branch -d $BRANCH_NAME || true
|
git branch -d $BRANCH_NAME || true
|
||||||
git checkout -b $BRANCH_NAME
|
git checkout -b $BRANCH_NAME
|
||||||
git add --all
|
git add --all
|
||||||
|
|||||||
+6
-10
@@ -9,7 +9,7 @@
|
|||||||
* @param {number} to The value to calculate the probability for.
|
* @param {number} to The value to calculate the probability for.
|
||||||
* @returns {number} Probability.
|
* @returns {number} Probability.
|
||||||
*/
|
*/
|
||||||
const normalcdf = (mean, sigma, to) => {
|
function normalcdf(mean, sigma, to) {
|
||||||
var z = (to - mean) / Math.sqrt(2 * sigma * sigma);
|
var z = (to - mean) / Math.sqrt(2 * sigma * sigma);
|
||||||
var t = 1 / (1 + 0.3275911 * Math.abs(z));
|
var t = 1 / (1 + 0.3275911 * Math.abs(z));
|
||||||
var a1 = 0.254829592;
|
var a1 = 0.254829592;
|
||||||
@@ -24,7 +24,7 @@ const normalcdf = (mean, sigma, to) => {
|
|||||||
sign = -1;
|
sign = -1;
|
||||||
}
|
}
|
||||||
return (1 / 2) * (1 + sign * erf);
|
return (1 / 2) * (1 + sign * erf);
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the users rank.
|
* Calculates the users rank.
|
||||||
@@ -38,7 +38,7 @@ const normalcdf = (mean, sigma, to) => {
|
|||||||
* @param {number} stargazers The number of stars.
|
* @param {number} stargazers The number of stars.
|
||||||
* @returns {{level: string, score: number}}} The users rank.
|
* @returns {{level: string, score: number}}} The users rank.
|
||||||
*/
|
*/
|
||||||
const calculateRank = ({
|
function calculateRank({
|
||||||
totalRepos,
|
totalRepos,
|
||||||
totalCommits,
|
totalCommits,
|
||||||
contributions,
|
contributions,
|
||||||
@@ -46,7 +46,7 @@ const calculateRank = ({
|
|||||||
prs,
|
prs,
|
||||||
issues,
|
issues,
|
||||||
stargazers,
|
stargazers,
|
||||||
}) => {
|
}) {
|
||||||
const COMMITS_OFFSET = 1.65;
|
const COMMITS_OFFSET = 1.65;
|
||||||
const CONTRIBS_OFFSET = 1.65;
|
const CONTRIBS_OFFSET = 1.65;
|
||||||
const ISSUES_OFFSET = 1;
|
const ISSUES_OFFSET = 1;
|
||||||
@@ -70,11 +70,7 @@ const calculateRank = ({
|
|||||||
const RANK_B_VALUE = 100;
|
const RANK_B_VALUE = 100;
|
||||||
|
|
||||||
const TOTAL_VALUES =
|
const TOTAL_VALUES =
|
||||||
RANK_S_VALUE +
|
RANK_S_VALUE + RANK_A2_VALUE + RANK_A3_VALUE + RANK_B_VALUE;
|
||||||
RANK_DOUBLE_A_VALUE +
|
|
||||||
RANK_A2_VALUE +
|
|
||||||
RANK_A3_VALUE +
|
|
||||||
RANK_B_VALUE;
|
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
const score = (
|
const score = (
|
||||||
@@ -98,7 +94,7 @@ const calculateRank = ({
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
return { level, score: normalizedScore };
|
return { level, score: normalizedScore };
|
||||||
};
|
}
|
||||||
|
|
||||||
export { calculateRank };
|
export { calculateRank };
|
||||||
export default calculateRank;
|
export default calculateRank;
|
||||||
|
|||||||
@@ -89,7 +89,6 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => {
|
|||||||
include_all_commits = false,
|
include_all_commits = false,
|
||||||
line_height = 25,
|
line_height = 25,
|
||||||
title_color,
|
title_color,
|
||||||
ring_color,
|
|
||||||
icon_color,
|
icon_color,
|
||||||
text_color,
|
text_color,
|
||||||
text_bold = true,
|
text_bold = true,
|
||||||
@@ -105,14 +104,13 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => {
|
|||||||
const lheight = parseInt(String(line_height), 10);
|
const lheight = parseInt(String(line_height), 10);
|
||||||
|
|
||||||
// returns theme based colors with proper overrides and defaults
|
// returns theme based colors with proper overrides and defaults
|
||||||
const { titleColor, iconColor, textColor, bgColor, borderColor, ringColor } =
|
const { titleColor, textColor, iconColor, bgColor, borderColor } =
|
||||||
getCardColors({
|
getCardColors({
|
||||||
title_color,
|
title_color,
|
||||||
text_color,
|
|
||||||
icon_color,
|
icon_color,
|
||||||
|
text_color,
|
||||||
bg_color,
|
bg_color,
|
||||||
border_color,
|
border_color,
|
||||||
ring_color,
|
|
||||||
theme,
|
theme,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -154,7 +152,7 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => {
|
|||||||
},
|
},
|
||||||
contribs: {
|
contribs: {
|
||||||
icon: icons.contribs,
|
icon: icons.contribs,
|
||||||
label: i18n.t("statcard.contribs") + " (last year)",
|
label: i18n.t("statcard.contribs"),
|
||||||
value: contributedTo,
|
value: contributedTo,
|
||||||
id: "contribs",
|
id: "contribs",
|
||||||
},
|
},
|
||||||
@@ -172,7 +170,6 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => {
|
|||||||
"pl",
|
"pl",
|
||||||
"de",
|
"de",
|
||||||
"nl",
|
"nl",
|
||||||
"zh-tw",
|
|
||||||
];
|
];
|
||||||
const isLongLocale = longLocales.includes(locale) === true;
|
const isLongLocale = longLocales.includes(locale) === true;
|
||||||
|
|
||||||
@@ -185,7 +182,8 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => {
|
|||||||
...STATS[key],
|
...STATS[key],
|
||||||
index,
|
index,
|
||||||
showIcons: show_icons,
|
showIcons: show_icons,
|
||||||
shiftValuePos: 79.01 + (isLongLocale ? 50 : 0),
|
shiftValuePos:
|
||||||
|
(!include_all_commits ? 50 : 35) + (isLongLocale ? 50 : 0),
|
||||||
bold: text_bold,
|
bold: text_bold,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -202,7 +200,6 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => {
|
|||||||
const progress = 100 - rank.score;
|
const progress = 100 - rank.score;
|
||||||
const cssStyles = getStyles({
|
const cssStyles = getStyles({
|
||||||
titleColor,
|
titleColor,
|
||||||
ringColor,
|
|
||||||
textColor,
|
textColor,
|
||||||
iconColor,
|
iconColor,
|
||||||
show_icons,
|
show_icons,
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ const recalculatePercentages = (languages) => {
|
|||||||
* @returns {string} WakaTime card SVG.
|
* @returns {string} WakaTime card SVG.
|
||||||
*/
|
*/
|
||||||
const renderWakatimeCard = (stats = {}, options = { hide: [] }) => {
|
const renderWakatimeCard = (stats = {}, options = { hide: [] }) => {
|
||||||
let { languages = [] } = stats;
|
let { languages } = stats;
|
||||||
const {
|
const {
|
||||||
hide_title = false,
|
hide_title = false,
|
||||||
hide_border = false,
|
hide_border = false,
|
||||||
@@ -174,24 +174,20 @@ const renderWakatimeCard = (stats = {}, options = { hide: [] }) => {
|
|||||||
custom_title,
|
custom_title,
|
||||||
locale,
|
locale,
|
||||||
layout,
|
layout,
|
||||||
langs_count = languages.length,
|
langs_count = languages ? languages.length : 0,
|
||||||
border_radius,
|
border_radius,
|
||||||
border_color,
|
border_color,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
const shouldHideLangs = Array.isArray(hide) && hide.length > 0;
|
const shouldHideLangs = Array.isArray(hide) && hide.length > 0;
|
||||||
if (shouldHideLangs) {
|
if (shouldHideLangs && languages !== undefined) {
|
||||||
const languagesToHide = new Set(hide.map((lang) => lowercaseTrim(lang)));
|
const languagesToHide = new Set(hide.map((lang) => lowercaseTrim(lang)));
|
||||||
languages = languages.filter(
|
languages = languages.filter(
|
||||||
(lang) => !languagesToHide.has(lowercaseTrim(lang.name)),
|
(lang) => !languagesToHide.has(lowercaseTrim(lang.name)),
|
||||||
);
|
);
|
||||||
|
recalculatePercentages(languages);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since the percentages are sorted in descending order, we can just
|
|
||||||
// slice from the beginning without sorting.
|
|
||||||
languages = languages.slice(0, langs_count);
|
|
||||||
recalculatePercentages(languages);
|
|
||||||
|
|
||||||
const i18n = new I18n({
|
const i18n = new I18n({
|
||||||
locale,
|
locale,
|
||||||
translations: wakatimeCardLocales,
|
translations: wakatimeCardLocales,
|
||||||
@@ -213,8 +209,10 @@ const renderWakatimeCard = (stats = {}, options = { hide: [] }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const filteredLanguages = languages
|
const filteredLanguages = languages
|
||||||
.filter((language) => language.hours || language.minutes)
|
? languages
|
||||||
.slice(0, langsCount);
|
.filter((language) => language.hours || language.minutes)
|
||||||
|
.slice(0, langsCount)
|
||||||
|
: [];
|
||||||
|
|
||||||
// Calculate the card height depending on how many items there are
|
// Calculate the card height depending on how many items there are
|
||||||
// but if rank circle is visible clamp the minimum height to `150`
|
// but if rank circle is visible clamp the minimum height to `150`
|
||||||
|
|||||||
@@ -1,11 +1,5 @@
|
|||||||
import { CustomError, logger } from "./utils.js";
|
import { CustomError, logger } from "./utils.js";
|
||||||
|
|
||||||
// Script variables.
|
|
||||||
const PATs = Object.keys(process.env).filter((key) =>
|
|
||||||
/PAT_\d*$/.exec(key),
|
|
||||||
).length;
|
|
||||||
const RETRIES = PATs ? PATs : 7;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to execute the fetcher function until it succeeds or the max number of retries is reached.
|
* Try to execute the fetcher function until it succeeds or the max number of retries is reached.
|
||||||
*
|
*
|
||||||
@@ -16,7 +10,7 @@ const RETRIES = PATs ? PATs : 7;
|
|||||||
* @returns Promise<retryer>
|
* @returns Promise<retryer>
|
||||||
*/
|
*/
|
||||||
const retryer = async (fetcher, variables, retries = 0) => {
|
const retryer = async (fetcher, variables, retries = 0) => {
|
||||||
if (retries > RETRIES) {
|
if (retries > 7) {
|
||||||
throw new CustomError("Maximum retries exceeded", CustomError.MAX_RETRY);
|
throw new CustomError("Maximum retries exceeded", CustomError.MAX_RETRY);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
|||||||
+49
-57
@@ -42,13 +42,13 @@ const renderError = (message, secondaryMessage = "") => {
|
|||||||
* @param {string} str String to encode.
|
* @param {string} str String to encode.
|
||||||
* @returns {string} Encoded string.
|
* @returns {string} Encoded string.
|
||||||
*/
|
*/
|
||||||
const encodeHTML = (str) => {
|
function encodeHTML(str) {
|
||||||
return str
|
return str
|
||||||
.replace(/[\u00A0-\u9999<>&](?!#)/gim, (i) => {
|
.replace(/[\u00A0-\u9999<>&](?!#)/gim, (i) => {
|
||||||
return "&#" + i.charCodeAt(0) + ";";
|
return "&#" + i.charCodeAt(0) + ";";
|
||||||
})
|
})
|
||||||
.replace(/\u0008/gim, "");
|
.replace(/\u0008/gim, "");
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves num with suffix k(thousands) precise to 1 decimal if greater than 999.
|
* Retrieves num with suffix k(thousands) precise to 1 decimal if greater than 999.
|
||||||
@@ -56,42 +56,41 @@ const encodeHTML = (str) => {
|
|||||||
* @param {number} num The number to format.
|
* @param {number} num The number to format.
|
||||||
* @returns {string|number} The formatted number.
|
* @returns {string|number} The formatted number.
|
||||||
*/
|
*/
|
||||||
const kFormatter = (num) => {
|
function kFormatter(num) {
|
||||||
return Math.abs(num) > 999
|
return Math.abs(num) > 999
|
||||||
? Math.sign(num) * parseFloat((Math.abs(num) / 1000).toFixed(1)) + "k"
|
? Math.sign(num) * parseFloat((Math.abs(num) / 1000).toFixed(1)) + "k"
|
||||||
: Math.sign(num) * Math.abs(num);
|
: Math.sign(num) * Math.abs(num);
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if a string is a valid hex color.
|
* Checks if a string is a valid hex color.
|
||||||
*
|
*
|
||||||
* @param {string} hexColor String to check.
|
* @param {string} hexColor String to check.
|
||||||
* @returns {boolean} True if the given string is a valid hex color.
|
* @returns {boolean} True if the given string is a valid hex color.
|
||||||
|
*
|
||||||
|
* @description: Tests color codes without the hash (#) prefix.
|
||||||
*/
|
*/
|
||||||
const isValidHexColor = (hexColor) => {
|
function isValidHexColor(hexColor) {
|
||||||
return new RegExp(
|
return new RegExp(
|
||||||
/^([A-Fa-f0-9]{8}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}|[A-Fa-f0-9]{4})$/,
|
/^([A-Fa-f0-9]{8}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}|[A-Fa-f0-9]{4})$/,
|
||||||
).test(hexColor);
|
).test(hexColor);
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns boolean if value is either "true" or "false" else the value as it is.
|
* Returns boolean if value is either "true" or "false" else the value as it is.
|
||||||
*
|
*
|
||||||
* @param {string | boolean} value The value to parse.
|
* @param {string} value The value to parse.
|
||||||
* @returns {boolean | undefined } The parsed value.
|
* @returns {boolean | string} The parsed value.
|
||||||
*/
|
*/
|
||||||
const parseBoolean = (value) => {
|
function parseBoolean(value) {
|
||||||
if (typeof value === "boolean") return value;
|
if (value === "true") {
|
||||||
|
return true;
|
||||||
if (typeof value === "string") {
|
} else if (value === "false") {
|
||||||
if (value.toLowerCase() === "true") {
|
return false;
|
||||||
return true;
|
} else {
|
||||||
} else if (value.toLowerCase() === "false") {
|
return value;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return undefined;
|
}
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse string to array of strings.
|
* Parse string to array of strings.
|
||||||
@@ -99,10 +98,10 @@ const parseBoolean = (value) => {
|
|||||||
* @param {string} str The string to parse.
|
* @param {string} str The string to parse.
|
||||||
* @returns {string[]} The array of strings.
|
* @returns {string[]} The array of strings.
|
||||||
*/
|
*/
|
||||||
const parseArray = (str) => {
|
function parseArray(str) {
|
||||||
if (!str) return [];
|
if (!str) return [];
|
||||||
return str.split(",");
|
return str.split(",");
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clamp the given number between the given range.
|
* Clamp the given number between the given range.
|
||||||
@@ -112,11 +111,11 @@ const parseArray = (str) => {
|
|||||||
* @param {number} max The maximum value.
|
* @param {number} max The maximum value.
|
||||||
* returns {number} The clamped number.
|
* returns {number} The clamped number.
|
||||||
*/
|
*/
|
||||||
const clampValue = (number, min, max) => {
|
function clampValue(number, min, max) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
if (Number.isNaN(parseInt(number))) return min;
|
if (Number.isNaN(parseInt(number))) return min;
|
||||||
return Math.max(min, Math.min(number, max));
|
return Math.max(min, Math.min(number, max));
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the given string is a valid gradient.
|
* Check if the given string is a valid gradient.
|
||||||
@@ -124,9 +123,10 @@ const clampValue = (number, min, max) => {
|
|||||||
* @param {string[]} colors Array of colors.
|
* @param {string[]} colors Array of colors.
|
||||||
* @returns {boolean} True if the given string is a valid gradient.
|
* @returns {boolean} True if the given string is a valid gradient.
|
||||||
*/
|
*/
|
||||||
const isValidGradient = (colors) => {
|
function isValidGradient(colors) {
|
||||||
return isValidHexColor(colors[1]) && isValidHexColor(colors[2]);
|
if (isNaN(colors[0])) return false;
|
||||||
};
|
return colors.slice(1).every((color) => isValidHexColor(color));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a gradient if color has more than one valid hex codes else a single color.
|
* Retrieves a gradient if color has more than one valid hex codes else a single color.
|
||||||
@@ -135,10 +135,10 @@ const isValidGradient = (colors) => {
|
|||||||
* @param {string} fallbackColor The fallback color.
|
* @param {string} fallbackColor The fallback color.
|
||||||
* @returns {string | string[]} The gradient or color.
|
* @returns {string | string[]} The gradient or color.
|
||||||
*/
|
*/
|
||||||
const fallbackColor = (color, fallbackColor) => {
|
function fallbackColor(color, fallbackColor) {
|
||||||
|
let colors = color.split(",");
|
||||||
let gradient = null;
|
let gradient = null;
|
||||||
|
|
||||||
let colors = color ? color.split(",") : [];
|
|
||||||
if (colors.length > 1 && isValidGradient(colors)) {
|
if (colors.length > 1 && isValidGradient(colors)) {
|
||||||
gradient = colors;
|
gradient = colors;
|
||||||
}
|
}
|
||||||
@@ -147,7 +147,7 @@ const fallbackColor = (color, fallbackColor) => {
|
|||||||
(gradient ? gradient : isValidHexColor(color) && `#${color}`) ||
|
(gradient ? gradient : isValidHexColor(color) && `#${color}`) ||
|
||||||
fallbackColor
|
fallbackColor
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send GraphQL request to GitHub API.
|
* Send GraphQL request to GitHub API.
|
||||||
@@ -156,7 +156,7 @@ const fallbackColor = (color, fallbackColor) => {
|
|||||||
* @param {import('axios').AxiosRequestConfig['headers']} headers Request headers.
|
* @param {import('axios').AxiosRequestConfig['headers']} headers Request headers.
|
||||||
* @returns {Promise<any>} Request response.
|
* @returns {Promise<any>} Request response.
|
||||||
*/
|
*/
|
||||||
const request = (data, headers) => {
|
function request(data, headers) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return axios({
|
return axios({
|
||||||
url: "https://api.github.com/graphql",
|
url: "https://api.github.com/graphql",
|
||||||
@@ -164,7 +164,7 @@ const request = (data, headers) => {
|
|||||||
headers,
|
headers,
|
||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Auto layout utility, allows us to layout things vertically or horizontally with
|
* Auto layout utility, allows us to layout things vertically or horizontally with
|
||||||
@@ -173,11 +173,11 @@ const request = (data, headers) => {
|
|||||||
* @param {object} props Function properties.
|
* @param {object} props Function properties.
|
||||||
* @param {string[]} props.items Array of items to layout.
|
* @param {string[]} props.items Array of items to layout.
|
||||||
* @param {number} props.gap Gap between items.
|
* @param {number} props.gap Gap between items.
|
||||||
* @param {number[]?=} props.sizes Array of sizes for each item.
|
|
||||||
* @param {"column" | "row"?=} props.direction Direction to layout items.
|
* @param {"column" | "row"?=} props.direction Direction to layout items.
|
||||||
|
* @param {number[]?=} props.sizes Array of sizes for each item.
|
||||||
* @returns {string[]} Array of items with proper layout.
|
* @returns {string[]} Array of items with proper layout.
|
||||||
*/
|
*/
|
||||||
const flexLayout = ({ items, gap, direction, sizes = [] }) => {
|
function flexLayout({ items, gap, direction, sizes = [] }) {
|
||||||
let lastSize = 0;
|
let lastSize = 0;
|
||||||
// filter() for filtering out empty strings
|
// filter() for filtering out empty strings
|
||||||
return items.filter(Boolean).map((item, i) => {
|
return items.filter(Boolean).map((item, i) => {
|
||||||
@@ -189,7 +189,7 @@ const flexLayout = ({ items, gap, direction, sizes = [] }) => {
|
|||||||
lastSize += size + gap;
|
lastSize += size + gap;
|
||||||
return `<g transform="${transform}">${item}</g>`;
|
return `<g transform="${transform}">${item}</g>`;
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns theme based colors with proper overrides and defaults.
|
* Returns theme based colors with proper overrides and defaults.
|
||||||
@@ -204,16 +204,15 @@ const flexLayout = ({ items, gap, direction, sizes = [] }) => {
|
|||||||
* @param {string} args.fallbackTheme Fallback theme.
|
* @param {string} args.fallbackTheme Fallback theme.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
const getCardColors = ({
|
function getCardColors({
|
||||||
title_color,
|
title_color,
|
||||||
text_color,
|
text_color,
|
||||||
icon_color,
|
icon_color,
|
||||||
bg_color,
|
bg_color,
|
||||||
border_color,
|
border_color,
|
||||||
ring_color,
|
|
||||||
theme,
|
theme,
|
||||||
fallbackTheme = "default",
|
fallbackTheme = "default",
|
||||||
}) => {
|
}) {
|
||||||
const defaultTheme = themes[fallbackTheme];
|
const defaultTheme = themes[fallbackTheme];
|
||||||
const selectedTheme = themes[theme] || defaultTheme;
|
const selectedTheme = themes[theme] || defaultTheme;
|
||||||
const defaultBorderColor =
|
const defaultBorderColor =
|
||||||
@@ -225,13 +224,6 @@ const getCardColors = ({
|
|||||||
title_color || selectedTheme.title_color,
|
title_color || selectedTheme.title_color,
|
||||||
"#" + defaultTheme.title_color,
|
"#" + defaultTheme.title_color,
|
||||||
);
|
);
|
||||||
|
|
||||||
// get the color provided by the user else the theme color
|
|
||||||
// finally if both colors are invalid we use the titleColor
|
|
||||||
const ringColor = fallbackColor(
|
|
||||||
ring_color || selectedTheme.ring_color,
|
|
||||||
titleColor,
|
|
||||||
);
|
|
||||||
const iconColor = fallbackColor(
|
const iconColor = fallbackColor(
|
||||||
icon_color || selectedTheme.icon_color,
|
icon_color || selectedTheme.icon_color,
|
||||||
"#" + defaultTheme.icon_color,
|
"#" + defaultTheme.icon_color,
|
||||||
@@ -250,8 +242,8 @@ const getCardColors = ({
|
|||||||
"#" + defaultBorderColor,
|
"#" + defaultBorderColor,
|
||||||
);
|
);
|
||||||
|
|
||||||
return { titleColor, iconColor, textColor, bgColor, borderColor, ringColor };
|
return { titleColor, iconColor, textColor, bgColor, borderColor };
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Split text over multiple lines based on the card width.
|
* Split text over multiple lines based on the card width.
|
||||||
@@ -261,7 +253,7 @@ const getCardColors = ({
|
|||||||
* @param {number} maxLines Maximum number of lines.
|
* @param {number} maxLines Maximum number of lines.
|
||||||
* @returns {string[]} Array of lines.
|
* @returns {string[]} Array of lines.
|
||||||
*/
|
*/
|
||||||
const wrapTextMultiline = (text, width = 59, maxLines = 3) => {
|
function wrapTextMultiline(text, width = 59, maxLines = 3) {
|
||||||
const fullWidthComma = ",";
|
const fullWidthComma = ",";
|
||||||
const encoded = encodeHTML(text);
|
const encoded = encodeHTML(text);
|
||||||
const isChinese = encoded.includes(fullWidthComma);
|
const isChinese = encoded.includes(fullWidthComma);
|
||||||
@@ -286,7 +278,7 @@ const wrapTextMultiline = (text, width = 59, maxLines = 3) => {
|
|||||||
// Remove empty lines if text fits in less than maxLines lines
|
// Remove empty lines if text fits in less than maxLines lines
|
||||||
const multiLineText = lines.filter(Boolean);
|
const multiLineText = lines.filter(Boolean);
|
||||||
return multiLineText;
|
return multiLineText;
|
||||||
};
|
}
|
||||||
|
|
||||||
const noop = () => {};
|
const noop = () => {};
|
||||||
// return console instance based on the environment
|
// return console instance based on the environment
|
||||||
@@ -352,7 +344,7 @@ class MissingParamError extends Error {
|
|||||||
* @param {number} fontSize Font size.
|
* @param {number} fontSize Font size.
|
||||||
* @returns {number} Text length.
|
* @returns {number} Text length.
|
||||||
*/
|
*/
|
||||||
const measureText = (str, fontSize = 10) => {
|
function measureText(str, fontSize = 10) {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
const widths = [
|
const widths = [
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
@@ -384,20 +376,20 @@ const measureText = (str, fontSize = 10) => {
|
|||||||
)
|
)
|
||||||
.reduce((cur, acc) => acc + cur) * fontSize
|
.reduce((cur, acc) => acc + cur) * fontSize
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
/** @param {string} name */
|
/** @param {string} name */
|
||||||
const lowercaseTrim = (name) => name.toLowerCase().trim();
|
const lowercaseTrim = (name) => name.toLowerCase().trim();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Split array of languages in two columns.
|
* Split array of languages in columns.
|
||||||
*
|
*
|
||||||
* @template T Langauge object.
|
* @template T Langauge object.
|
||||||
* @param {Array<T>} arr Array of languages.
|
* @param {Array<T>} arr Array of languages.
|
||||||
* @param {number} perChunk Number of languages per column.
|
* @param {number} perChunk Number of languages per column.
|
||||||
* @returns {Array<T>} Array of languages split in two columns.
|
* @returns {Array<T>} Array of languages split in columns.
|
||||||
*/
|
*/
|
||||||
const chunkArray = (arr, perChunk) => {
|
function chunkArray(arr, perChunk) {
|
||||||
return arr.reduce((resultArray, item, index) => {
|
return arr.reduce((resultArray, item, index) => {
|
||||||
const chunkIndex = Math.floor(index / perChunk);
|
const chunkIndex = Math.floor(index / perChunk);
|
||||||
|
|
||||||
@@ -409,7 +401,7 @@ const chunkArray = (arr, perChunk) => {
|
|||||||
|
|
||||||
return resultArray;
|
return resultArray;
|
||||||
}, []);
|
}, []);
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse emoji from string.
|
* Parse emoji from string.
|
||||||
@@ -417,12 +409,12 @@ const chunkArray = (arr, perChunk) => {
|
|||||||
* @param {string} str String to parse emoji from.
|
* @param {string} str String to parse emoji from.
|
||||||
* @returns {string} String with emoji parsed.
|
* @returns {string} String with emoji parsed.
|
||||||
*/
|
*/
|
||||||
const parseEmojis = (str) => {
|
function parseEmojis(str) {
|
||||||
if (!str) throw new Error("[parseEmoji]: str argument not provided");
|
if (!str) throw new Error("[parseEmoji]: str argument not provided");
|
||||||
return str.replace(/:\w+:/gm, (emoji) => {
|
return str.replace(/:[\w+-]+:/gm, (emoji) => {
|
||||||
return toEmoji.get(emoji) || "";
|
return toEmoji.get(emoji) || "";
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
ERROR_CARD_LENGTH,
|
ERROR_CARD_LENGTH,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { MissingParamError, request } from "../common/utils.js";
|
|||||||
* Repo data fetcher.
|
* Repo data fetcher.
|
||||||
*
|
*
|
||||||
* @param {import('Axios').AxiosRequestHeaders} variables Fetcher variables.
|
* @param {import('Axios').AxiosRequestHeaders} variables Fetcher variables.
|
||||||
* @param {string} token GitHub token.
|
* @param {string} token Github token.
|
||||||
* @returns {Promise<import('Axios').AxiosResponse>} The response.
|
* @returns {Promise<import('Axios').AxiosResponse>} The response.
|
||||||
*/
|
*/
|
||||||
const fetcher = (variables, token) => {
|
const fetcher = (variables, token) => {
|
||||||
@@ -56,11 +56,11 @@ const urlExample = "/api/pin?username=USERNAME&repo=REPO_NAME";
|
|||||||
/**
|
/**
|
||||||
* Fetch repository data.
|
* Fetch repository data.
|
||||||
*
|
*
|
||||||
* @param {string} username GitHub username.
|
* @param {string} username Github username.
|
||||||
* @param {string} reponame GitHub repository name.
|
* @param {string} reponame Github repository name.
|
||||||
* @returns {Promise<import("./types").RepositoryData>} Repository data.
|
* @returns {Promise<import("./types").RepositoryData>} Repository data.
|
||||||
*/
|
*/
|
||||||
const fetchRepo = async (username, reponame) => {
|
async function fetchRepo(username, reponame) {
|
||||||
if (!username && !reponame) {
|
if (!username && !reponame) {
|
||||||
throw new MissingParamError(["username", "repo"], urlExample);
|
throw new MissingParamError(["username", "repo"], urlExample);
|
||||||
}
|
}
|
||||||
@@ -100,7 +100,7 @@ const fetchRepo = async (username, reponame) => {
|
|||||||
starCount: data.organization.repository.stargazers.totalCount,
|
starCount: data.organization.repository.stargazers.totalCount,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
export { fetchRepo };
|
export { fetchRepo };
|
||||||
export default fetchRepo;
|
export default fetchRepo;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import * as dotenv from "dotenv";
|
||||||
import githubUsernameRegex from "github-username-regex";
|
import githubUsernameRegex from "github-username-regex";
|
||||||
import { calculateRank } from "../calculateRank.js";
|
import { calculateRank } from "../calculateRank.js";
|
||||||
import { retryer } from "../common/retryer.js";
|
import { retryer } from "../common/retryer.js";
|
||||||
@@ -11,11 +12,13 @@ import {
|
|||||||
wrapTextMultiline,
|
wrapTextMultiline,
|
||||||
} from "../common/utils.js";
|
} from "../common/utils.js";
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stats fetcher object.
|
* Stats fetcher object.
|
||||||
*
|
*
|
||||||
* @param {import('axios').AxiosRequestHeaders} variables Fetcher variables.
|
* @param {import('axios').AxiosRequestHeaders} variables Fetcher variables.
|
||||||
* @param {string} token GitHub token.
|
* @param {string} token Github token.
|
||||||
* @returns {Promise<import('../common/types').StatsFetcherResponse>} Stats fetcher response.
|
* @returns {Promise<import('../common/types').StatsFetcherResponse>} Stats fetcher response.
|
||||||
*/
|
*/
|
||||||
const fetcher = (variables, token) => {
|
const fetcher = (variables, token) => {
|
||||||
@@ -63,7 +66,7 @@ const fetcher = (variables, token) => {
|
|||||||
* Fetch first 100 repositories for a given username.
|
* Fetch first 100 repositories for a given username.
|
||||||
*
|
*
|
||||||
* @param {import('axios').AxiosRequestHeaders} variables Fetcher variables.
|
* @param {import('axios').AxiosRequestHeaders} variables Fetcher variables.
|
||||||
* @param {string} token GitHub token.
|
* @param {string} token Github token.
|
||||||
* @returns {Promise<import('../common/types').StatsFetcherResponse>} Repositories fetcher response.
|
* @returns {Promise<import('../common/types').StatsFetcherResponse>} Repositories fetcher response.
|
||||||
*/
|
*/
|
||||||
const repositoriesFetcher = (variables, token) => {
|
const repositoriesFetcher = (variables, token) => {
|
||||||
@@ -98,10 +101,10 @@ const repositoriesFetcher = (variables, token) => {
|
|||||||
/**
|
/**
|
||||||
* Fetch all the commits for all the repositories of a given username.
|
* Fetch all the commits for all the repositories of a given username.
|
||||||
*
|
*
|
||||||
* @param {*} username GitHub username.
|
* @param {*} username Github username.
|
||||||
* @returns {Promise<number>} Total commits.
|
* @returns {Promise<number>} Total commits.
|
||||||
*
|
*
|
||||||
* @description Done like this because the GitHub API does not provide a way to fetch all the commits. See
|
* @description Done like this because the Github API does not provide a way to fetch all the commits. See
|
||||||
* #92#issuecomment-661026467 and #211 for more information.
|
* #92#issuecomment-661026467 and #211 for more information.
|
||||||
*/
|
*/
|
||||||
const totalCommitsFetcher = async (username) => {
|
const totalCommitsFetcher = async (username) => {
|
||||||
@@ -140,7 +143,7 @@ const totalCommitsFetcher = async (username) => {
|
|||||||
/**
|
/**
|
||||||
* Fetch all the stars for all the repositories of a given username.
|
* Fetch all the stars for all the repositories of a given username.
|
||||||
*
|
*
|
||||||
* @param {string} username GitHub username.
|
* @param {string} username Github username.
|
||||||
* @param {array} repoToHide Repositories to hide.
|
* @param {array} repoToHide Repositories to hide.
|
||||||
* @returns {Promise<number>} Total stars.
|
* @returns {Promise<number>} Total stars.
|
||||||
*/
|
*/
|
||||||
@@ -180,17 +183,17 @@ const totalStarsFetcher = async (username, repoToHide) => {
|
|||||||
/**
|
/**
|
||||||
* Fetch stats for a given username.
|
* Fetch stats for a given username.
|
||||||
*
|
*
|
||||||
* @param {string} username GitHub username.
|
* @param {string} username Github username.
|
||||||
* @param {boolean} count_private Include private contributions.
|
* @param {boolean} count_private Include private contributions.
|
||||||
* @param {boolean} include_all_commits Include all commits.
|
* @param {boolean} include_all_commits Include all commits.
|
||||||
* @returns {Promise<import("./types").StatsData>} Stats data.
|
* @returns {Promise<import("./types").StatsData>} Stats data.
|
||||||
*/
|
*/
|
||||||
const fetchStats = async (
|
async function fetchStats(
|
||||||
username,
|
username,
|
||||||
count_private = false,
|
count_private = false,
|
||||||
include_all_commits = false,
|
include_all_commits = false,
|
||||||
exclude_repo = [],
|
exclude_repo = [],
|
||||||
) => {
|
) {
|
||||||
if (!username) throw new MissingParamError(["username"]);
|
if (!username) throw new MissingParamError(["username"]);
|
||||||
|
|
||||||
const stats = {
|
const stats = {
|
||||||
@@ -272,7 +275,7 @@ const fetchStats = async (
|
|||||||
});
|
});
|
||||||
|
|
||||||
return stats;
|
return stats;
|
||||||
};
|
}
|
||||||
|
|
||||||
export { fetchStats };
|
export { fetchStats };
|
||||||
export default fetchStats;
|
export default fetchStats;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
|
import * as dotenv from "dotenv";
|
||||||
import { retryer } from "../common/retryer.js";
|
import { retryer } from "../common/retryer.js";
|
||||||
import {
|
import {
|
||||||
CustomError,
|
CustomError,
|
||||||
@@ -8,11 +9,13 @@ import {
|
|||||||
wrapTextMultiline,
|
wrapTextMultiline,
|
||||||
} from "../common/utils.js";
|
} from "../common/utils.js";
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Top languages fetcher object.
|
* Top languages fetcher object.
|
||||||
*
|
*
|
||||||
* @param {import('Axios').AxiosRequestHeaders} variables Fetcher variables.
|
* @param {import('Axios').AxiosRequestHeaders} variables Fetcher variables.
|
||||||
* @param {string} token GitHub token.
|
* @param {string} token Github token.
|
||||||
* @returns {Promise<import('../common/types').StatsFetcherResponse>} Languages fetcher response.
|
* @returns {Promise<import('../common/types').StatsFetcherResponse>} Languages fetcher response.
|
||||||
*/
|
*/
|
||||||
const fetcher = (variables, token) => {
|
const fetcher = (variables, token) => {
|
||||||
@@ -50,11 +53,11 @@ const fetcher = (variables, token) => {
|
|||||||
/**
|
/**
|
||||||
* Fetch top languages for a given username.
|
* Fetch top languages for a given username.
|
||||||
*
|
*
|
||||||
* @param {string} username GitHub username.
|
* @param {string} username Github username.
|
||||||
* @param {string[]} exclude_repo List of repositories to exclude.
|
* @param {string[]} exclude_repo List of repositories to exclude.
|
||||||
* @returns {Promise<import("./types").TopLangData>} Top languages data.
|
* @returns {Promise<import("./types").TopLangData>} Top languages data.
|
||||||
*/
|
*/
|
||||||
const fetchTopLanguages = async (username, exclude_repo = []) => {
|
async function fetchTopLanguages(username, exclude_repo = []) {
|
||||||
if (!username) throw new MissingParamError(["username"]);
|
if (!username) throw new MissingParamError(["username"]);
|
||||||
|
|
||||||
const res = await retryer(fetcher, { login: username });
|
const res = await retryer(fetcher, { login: username });
|
||||||
@@ -133,7 +136,7 @@ const fetchTopLanguages = async (username, exclude_repo = []) => {
|
|||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
return topLangs;
|
return topLangs;
|
||||||
};
|
}
|
||||||
|
|
||||||
export { fetchTopLanguages };
|
export { fetchTopLanguages };
|
||||||
export default fetchTopLanguages;
|
export default fetchTopLanguages;
|
||||||
|
|||||||
+2
-3
@@ -77,7 +77,6 @@ const getStyles = ({
|
|||||||
titleColor,
|
titleColor,
|
||||||
textColor,
|
textColor,
|
||||||
iconColor,
|
iconColor,
|
||||||
ringColor,
|
|
||||||
show_icons,
|
show_icons,
|
||||||
progress,
|
progress,
|
||||||
}) => {
|
}) => {
|
||||||
@@ -106,13 +105,13 @@ const getStyles = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
.rank-circle-rim {
|
.rank-circle-rim {
|
||||||
stroke: ${ringColor};
|
stroke: ${titleColor};
|
||||||
fill: none;
|
fill: none;
|
||||||
stroke-width: 6;
|
stroke-width: 6;
|
||||||
opacity: 0.2;
|
opacity: 0.2;
|
||||||
}
|
}
|
||||||
.rank-circle {
|
.rank-circle {
|
||||||
stroke: ${ringColor};
|
stroke: ${titleColor};
|
||||||
stroke-dasharray: 250;
|
stroke-dasharray: 250;
|
||||||
fill: none;
|
fill: none;
|
||||||
stroke-width: 6;
|
stroke-width: 6;
|
||||||
|
|||||||
+6
-6
@@ -37,7 +37,7 @@ const statCardLocales = ({ name, apostrophe }) => {
|
|||||||
sk: `GitHub štatistiky používateľa ${encodedName}`,
|
sk: `GitHub štatistiky používateľa ${encodedName}`,
|
||||||
tr: `${encodedName} Hesabının GitHub Yıldızları`,
|
tr: `${encodedName} Hesabının GitHub Yıldızları`,
|
||||||
pl: `Statystyki GitHub użytkownika ${encodedName}`,
|
pl: `Statystyki GitHub użytkownika ${encodedName}`,
|
||||||
uz: `${encodedName}ning GitHub'dagi statistikasi`,
|
uz: `${encodedName}ning Github'dagi statistikasi`,
|
||||||
vi: `Thống Kê GitHub ${encodedName}`,
|
vi: `Thống Kê GitHub ${encodedName}`,
|
||||||
se: `GitHubstatistik för ${encodedName}`,
|
se: `GitHubstatistik för ${encodedName}`,
|
||||||
},
|
},
|
||||||
@@ -67,7 +67,7 @@ const statCardLocales = ({ name, apostrophe }) => {
|
|||||||
my: "Jumlah Bintang",
|
my: "Jumlah Bintang",
|
||||||
sk: "Hviezdy",
|
sk: "Hviezdy",
|
||||||
tr: "Toplam Yıldız",
|
tr: "Toplam Yıldız",
|
||||||
pl: "Liczba otrzymanych gwiazdek",
|
pl: "Liczba Gwiazdek dostanych",
|
||||||
uz: "Yulduzchalar",
|
uz: "Yulduzchalar",
|
||||||
vi: "Tổng Số Sao",
|
vi: "Tổng Số Sao",
|
||||||
se: "Antal intjänade stjärnor",
|
se: "Antal intjänade stjärnor",
|
||||||
@@ -160,7 +160,7 @@ const statCardLocales = ({ name, apostrophe }) => {
|
|||||||
my: "Jumlah Isu Dilaporkan",
|
my: "Jumlah Isu Dilaporkan",
|
||||||
sk: "Všetky problémy",
|
sk: "Všetky problémy",
|
||||||
tr: "Toplam Hata",
|
tr: "Toplam Hata",
|
||||||
pl: "Wszystkie problemy",
|
pl: "Wszystkie Issues",
|
||||||
uz: "'Issue'lar",
|
uz: "'Issue'lar",
|
||||||
vi: "Tổng Số Vấn Đề",
|
vi: "Tổng Số Vấn Đề",
|
||||||
se: "Total antal issues",
|
se: "Total antal issues",
|
||||||
@@ -322,7 +322,7 @@ const wakatimeCardLocales = {
|
|||||||
my: "Statistik Wakatime",
|
my: "Statistik Wakatime",
|
||||||
sk: "Wakatime štatistika",
|
sk: "Wakatime štatistika",
|
||||||
tr: "Waketime İstatistikler",
|
tr: "Waketime İstatistikler",
|
||||||
pl: "Statystyki Wakatime",
|
pl: "statystyki Wakatime",
|
||||||
vi: "Thống Kê Wakatime",
|
vi: "Thống Kê Wakatime",
|
||||||
se: "Wakatime statistik",
|
se: "Wakatime statistik",
|
||||||
},
|
},
|
||||||
@@ -367,9 +367,9 @@ const availableLocales = Object.keys(repoCardLocales["repocard.archived"]);
|
|||||||
* @param {string} locale The locale to check.
|
* @param {string} locale The locale to check.
|
||||||
* @returns {boolean} Boolean specifying whether the locale is available or not.
|
* @returns {boolean} Boolean specifying whether the locale is available or not.
|
||||||
*/
|
*/
|
||||||
const isLocaleAvailable = (locale) => {
|
function isLocaleAvailable(locale) {
|
||||||
return availableLocales.includes(locale.toLowerCase());
|
return availableLocales.includes(locale.toLowerCase());
|
||||||
};
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
isLocaleAvailable,
|
isLocaleAvailable,
|
||||||
|
|||||||
@@ -51,13 +51,13 @@ exports[`Test Render Wakatime Card should render correctly with compact layout 1
|
|||||||
}
|
}
|
||||||
|
|
||||||
.rank-circle-rim {
|
.rank-circle-rim {
|
||||||
stroke: undefined;
|
stroke: #2f80ed;
|
||||||
fill: none;
|
fill: none;
|
||||||
stroke-width: 6;
|
stroke-width: 6;
|
||||||
opacity: 0.2;
|
opacity: 0.2;
|
||||||
}
|
}
|
||||||
.rank-circle {
|
.rank-circle {
|
||||||
stroke: undefined;
|
stroke: #2f80ed;
|
||||||
stroke-dasharray: 250;
|
stroke-dasharray: 250;
|
||||||
fill: none;
|
fill: none;
|
||||||
stroke-width: 6;
|
stroke-width: 6;
|
||||||
@@ -122,7 +122,7 @@ exports[`Test Render Wakatime Card should render correctly with compact layout 1
|
|||||||
data-testid="lang-progress"
|
data-testid="lang-progress"
|
||||||
x="0"
|
x="0"
|
||||||
y="0"
|
y="0"
|
||||||
width="415.61699999999996"
|
width="6.6495"
|
||||||
height="8"
|
height="8"
|
||||||
fill="#858585"
|
fill="#858585"
|
||||||
/>
|
/>
|
||||||
@@ -130,167 +130,9 @@ exports[`Test Render Wakatime Card should render correctly with compact layout 1
|
|||||||
<rect
|
<rect
|
||||||
mask="url(#rect-mask)"
|
mask="url(#rect-mask)"
|
||||||
data-testid="lang-progress"
|
data-testid="lang-progress"
|
||||||
x="415.61699999999996"
|
x="6.6495"
|
||||||
y="0"
|
y="0"
|
||||||
width="29.0625"
|
width="0.465"
|
||||||
height="8"
|
|
||||||
fill="#3178c6"
|
|
||||||
/>
|
|
||||||
|
|
||||||
|
|
||||||
<g transform="translate(25, 25)">
|
|
||||||
<circle cx="5" cy="6" r="5" fill="#858585" />
|
|
||||||
<text data-testid="lang-name" x="15" y="10" class='lang-name'>
|
|
||||||
Other - 19 mins
|
|
||||||
</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g transform="translate(230, 25)">
|
|
||||||
<circle cx="5" cy="6" r="5" fill="#3178c6" />
|
|
||||||
<text data-testid="lang-name" x="15" y="10" class='lang-name'>
|
|
||||||
TypeScript - 1 min
|
|
||||||
</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Test Render Wakatime Card should render correctly with compact layout when langs_count is set 1`] = `
|
|
||||||
"
|
|
||||||
<svg
|
|
||||||
width="495"
|
|
||||||
height="115"
|
|
||||||
viewBox="0 0 495 115"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
role="img"
|
|
||||||
aria-labelledby="descId"
|
|
||||||
>
|
|
||||||
<title id="titleId"></title>
|
|
||||||
<desc id="descId"></desc>
|
|
||||||
<style>
|
|
||||||
.header {
|
|
||||||
font: 600 18px 'Segoe UI', Ubuntu, Sans-Serif;
|
|
||||||
fill: #2f80ed;
|
|
||||||
animation: fadeInAnimation 0.8s ease-in-out forwards;
|
|
||||||
}
|
|
||||||
@supports(-moz-appearance: auto) {
|
|
||||||
/* Selector detects Firefox */
|
|
||||||
.header { font-size: 15.5px; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.stat {
|
|
||||||
font: 600 14px 'Segoe UI', Ubuntu, "Helvetica Neue", Sans-Serif; fill: #434d58;
|
|
||||||
}
|
|
||||||
@supports(-moz-appearance: auto) {
|
|
||||||
/* Selector detects Firefox */
|
|
||||||
.stat { font-size:12px; }
|
|
||||||
}
|
|
||||||
.stagger {
|
|
||||||
opacity: 0;
|
|
||||||
animation: fadeInAnimation 0.3s ease-in-out forwards;
|
|
||||||
}
|
|
||||||
.rank-text {
|
|
||||||
font: 800 24px 'Segoe UI', Ubuntu, Sans-Serif; fill: #434d58;
|
|
||||||
animation: scaleInAnimation 0.3s ease-in-out forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
.not_bold { font-weight: 400 }
|
|
||||||
.bold { font-weight: 700 }
|
|
||||||
.icon {
|
|
||||||
fill: #4c71f2;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rank-circle-rim {
|
|
||||||
stroke: undefined;
|
|
||||||
fill: none;
|
|
||||||
stroke-width: 6;
|
|
||||||
opacity: 0.2;
|
|
||||||
}
|
|
||||||
.rank-circle {
|
|
||||||
stroke: undefined;
|
|
||||||
stroke-dasharray: 250;
|
|
||||||
fill: none;
|
|
||||||
stroke-width: 6;
|
|
||||||
stroke-linecap: round;
|
|
||||||
opacity: 0.8;
|
|
||||||
transform-origin: -10px 8px;
|
|
||||||
transform: rotate(-90deg);
|
|
||||||
animation: rankAnimation 1s forwards ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.lang-name { font: 400 11px 'Segoe UI', Ubuntu, Sans-Serif; fill: #434d58 }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<rect
|
|
||||||
data-testid="card-bg"
|
|
||||||
x="0.5"
|
|
||||||
y="0.5"
|
|
||||||
rx="4.5"
|
|
||||||
height="99%"
|
|
||||||
stroke="#e4e2e2"
|
|
||||||
width="494"
|
|
||||||
fill="#fffefe"
|
|
||||||
stroke-opacity="1"
|
|
||||||
/>
|
|
||||||
|
|
||||||
|
|
||||||
<g
|
|
||||||
data-testid="card-title"
|
|
||||||
transform="translate(25, 35)"
|
|
||||||
>
|
|
||||||
<g transform="translate(0, 0)">
|
|
||||||
<text
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
class="header"
|
|
||||||
data-testid="header"
|
|
||||||
>Wakatime Stats</text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
|
|
||||||
<g
|
|
||||||
data-testid="main-card-body"
|
|
||||||
transform="translate(0, 55)"
|
|
||||||
>
|
|
||||||
|
|
||||||
<svg x="0" y="0" width="100%">
|
|
||||||
|
|
||||||
<mask id="rect-mask">
|
|
||||||
<rect x="25" y="0" width="440" height="8" fill="white" rx="5" />
|
|
||||||
</mask>
|
|
||||||
|
|
||||||
<rect
|
|
||||||
mask="url(#rect-mask)"
|
|
||||||
data-testid="lang-progress"
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="436.4025"
|
|
||||||
height="8"
|
|
||||||
fill="#858585"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<rect
|
|
||||||
mask="url(#rect-mask)"
|
|
||||||
data-testid="lang-progress"
|
|
||||||
x="436.4025"
|
|
||||||
y="0"
|
|
||||||
width="30.503999999999998"
|
|
||||||
height="8"
|
height="8"
|
||||||
fill="#3178c6"
|
fill="#3178c6"
|
||||||
/>
|
/>
|
||||||
|
|||||||
+6
-66
@@ -160,12 +160,7 @@ describe("Test /api/", () => {
|
|||||||
|
|
||||||
expect(res.setHeader.mock.calls).toEqual([
|
expect(res.setHeader.mock.calls).toEqual([
|
||||||
["Content-Type", "image/svg+xml"],
|
["Content-Type", "image/svg+xml"],
|
||||||
[
|
["Cache-Control", `public, max-age=${CONSTANTS.FOUR_HOURS}`],
|
||||||
"Cache-Control",
|
|
||||||
`max-age=${CONSTANTS.FOUR_HOURS / 2}, s-maxage=${
|
|
||||||
CONSTANTS.FOUR_HOURS
|
|
||||||
}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
|
|
||||||
],
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -175,12 +170,7 @@ describe("Test /api/", () => {
|
|||||||
|
|
||||||
expect(res.setHeader.mock.calls).toEqual([
|
expect(res.setHeader.mock.calls).toEqual([
|
||||||
["Content-Type", "image/svg+xml"],
|
["Content-Type", "image/svg+xml"],
|
||||||
[
|
["Cache-Control", `public, max-age=${15000}`],
|
||||||
"Cache-Control",
|
|
||||||
`max-age=7500, s-maxage=${15000}, stale-while-revalidate=${
|
|
||||||
CONSTANTS.ONE_DAY
|
|
||||||
}`,
|
|
||||||
],
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -190,7 +180,7 @@ describe("Test /api/", () => {
|
|||||||
|
|
||||||
expect(res.setHeader.mock.calls).toEqual([
|
expect(res.setHeader.mock.calls).toEqual([
|
||||||
["Content-Type", "image/svg+xml"],
|
["Content-Type", "image/svg+xml"],
|
||||||
["Cache-Control", `no-cache, no-store, must-revalidate`],
|
["Cache-Control", `no-store`],
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -201,12 +191,7 @@ describe("Test /api/", () => {
|
|||||||
|
|
||||||
expect(res.setHeader.mock.calls).toEqual([
|
expect(res.setHeader.mock.calls).toEqual([
|
||||||
["Content-Type", "image/svg+xml"],
|
["Content-Type", "image/svg+xml"],
|
||||||
[
|
["Cache-Control", `public, max-age=${CONSTANTS.ONE_DAY}`],
|
||||||
"Cache-Control",
|
|
||||||
`max-age=${CONSTANTS.ONE_DAY / 2}, s-maxage=${
|
|
||||||
CONSTANTS.ONE_DAY
|
|
||||||
}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
|
|
||||||
],
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,12 +202,7 @@ describe("Test /api/", () => {
|
|||||||
|
|
||||||
expect(res.setHeader.mock.calls).toEqual([
|
expect(res.setHeader.mock.calls).toEqual([
|
||||||
["Content-Type", "image/svg+xml"],
|
["Content-Type", "image/svg+xml"],
|
||||||
[
|
["Cache-Control", `public, max-age=${CONSTANTS.FOUR_HOURS}`],
|
||||||
"Cache-Control",
|
|
||||||
`max-age=${CONSTANTS.FOUR_HOURS / 2}, s-maxage=${
|
|
||||||
CONSTANTS.FOUR_HOURS
|
|
||||||
}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
|
|
||||||
],
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,12 +212,7 @@ describe("Test /api/", () => {
|
|||||||
|
|
||||||
expect(res.setHeader.mock.calls).toEqual([
|
expect(res.setHeader.mock.calls).toEqual([
|
||||||
["Content-Type", "image/svg+xml"],
|
["Content-Type", "image/svg+xml"],
|
||||||
[
|
["Cache-Control", `public, max-age=${CONSTANTS.FOUR_HOURS}`],
|
||||||
"Cache-Control",
|
|
||||||
`max-age=${CONSTANTS.FOUR_HOURS / 2}, s-maxage=${
|
|
||||||
CONSTANTS.FOUR_HOURS
|
|
||||||
}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
|
|
||||||
],
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -273,39 +248,4 @@ describe("Test /api/", () => {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should allow changing ring_color", async () => {
|
|
||||||
const { req, res } = faker(
|
|
||||||
{
|
|
||||||
username: "anuraghazra",
|
|
||||||
hide: "issues,prs,contribs",
|
|
||||||
show_icons: true,
|
|
||||||
hide_border: true,
|
|
||||||
line_height: 100,
|
|
||||||
title_color: "fff",
|
|
||||||
ring_color: "0000ff",
|
|
||||||
icon_color: "fff",
|
|
||||||
text_color: "fff",
|
|
||||||
bg_color: "fff",
|
|
||||||
},
|
|
||||||
data,
|
|
||||||
);
|
|
||||||
|
|
||||||
await api(req, res);
|
|
||||||
|
|
||||||
expect(res.setHeader).toBeCalledWith("Content-Type", "image/svg+xml");
|
|
||||||
expect(res.send).toBeCalledWith(
|
|
||||||
renderStatsCard(stats, {
|
|
||||||
hide: ["issues", "prs", "contribs"],
|
|
||||||
show_icons: true,
|
|
||||||
hide_border: true,
|
|
||||||
line_height: 100,
|
|
||||||
title_color: "fff",
|
|
||||||
ring_color: "0000ff",
|
|
||||||
icon_color: "fff",
|
|
||||||
text_color: "fff",
|
|
||||||
bg_color: "fff",
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,6 +13,6 @@ describe("Test calculateRank", () => {
|
|||||||
prs: 300,
|
prs: 300,
|
||||||
issues: 200,
|
issues: 200,
|
||||||
}),
|
}),
|
||||||
).toStrictEqual({ level: "A+", score: 49.25629684876535 });
|
).toStrictEqual({ level: "A+", score: 49.16605417270399 });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -145,6 +145,7 @@ describe("Card", () => {
|
|||||||
"#fff",
|
"#fff",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should render gradient backgrounds", () => {
|
it("should render gradient backgrounds", () => {
|
||||||
const { titleColor, textColor, iconColor, bgColor } = getCardColors({
|
const { titleColor, textColor, iconColor, bgColor } = getCardColors({
|
||||||
title_color: "f00",
|
title_color: "f00",
|
||||||
|
|||||||
+25
-20
@@ -11,22 +11,27 @@ import { renderStatsCard } from "../../src/cards/stats-card.js";
|
|||||||
import { renderTopLanguages } from "../../src/cards/top-languages-card.js";
|
import { renderTopLanguages } from "../../src/cards/top-languages-card.js";
|
||||||
import { renderWakatimeCard } from "../../src/cards/wakatime-card.js";
|
import { renderWakatimeCard } from "../../src/cards/wakatime-card.js";
|
||||||
|
|
||||||
const REPO = "curly-fiesta";
|
const REPO = "dummy-cra";
|
||||||
const USER = "catelinemnemosyne";
|
const USER = "grsdummy";
|
||||||
const STATS_DATA = {
|
const STATS_DATA = {
|
||||||
name: "Cateline Mnemosyne",
|
name: "grsdummy",
|
||||||
totalPRs: 1,
|
totalPRs: 2,
|
||||||
totalCommits: 7,
|
totalCommits: 2,
|
||||||
totalIssues: 1,
|
totalIssues: 1,
|
||||||
totalStars: 1,
|
totalStars: 1,
|
||||||
contributedTo: 1,
|
contributedTo: 2,
|
||||||
rank: {
|
rank: {
|
||||||
level: "A+",
|
level: "A+",
|
||||||
score: 50.893750297869225,
|
score: 51.01013099671447,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const LANGS_DATA = {
|
const LANGS_DATA = {
|
||||||
|
TypeScript: {
|
||||||
|
color: "#3178c6",
|
||||||
|
name: "TypeScript",
|
||||||
|
size: 2049,
|
||||||
|
},
|
||||||
HTML: {
|
HTML: {
|
||||||
color: "#e34c26",
|
color: "#e34c26",
|
||||||
name: "HTML",
|
name: "HTML",
|
||||||
@@ -37,19 +42,19 @@ const LANGS_DATA = {
|
|||||||
name: "CSS",
|
name: "CSS",
|
||||||
size: 930,
|
size: 930,
|
||||||
},
|
},
|
||||||
JavaScript: {
|
Python: {
|
||||||
color: "#f1e05a",
|
color: "#3572A5",
|
||||||
name: "JavaScript",
|
name: "Python",
|
||||||
size: 1912,
|
size: 671,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const WAKATIME_DATA = {
|
const WAKATIME_DATA = {
|
||||||
human_readable_range: "last week",
|
human_readable_range: "last week",
|
||||||
is_already_updating: false,
|
is_already_updating: false,
|
||||||
is_coding_activity_visible: true,
|
is_coding_activity_visible: false,
|
||||||
is_including_today: false,
|
is_including_today: false,
|
||||||
is_other_usage_visible: true,
|
is_other_usage_visible: false,
|
||||||
is_stuck: false,
|
is_stuck: false,
|
||||||
is_up_to_date: false,
|
is_up_to_date: false,
|
||||||
is_up_to_date_pending_future: false,
|
is_up_to_date_pending_future: false,
|
||||||
@@ -57,24 +62,24 @@ const WAKATIME_DATA = {
|
|||||||
range: "last_7_days",
|
range: "last_7_days",
|
||||||
status: "pending_update",
|
status: "pending_update",
|
||||||
timeout: 15,
|
timeout: 15,
|
||||||
username: USER,
|
username: "grsdummy",
|
||||||
writes_only: false,
|
writes_only: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const REPOSITORY_DATA = {
|
const REPOSITORY_DATA = {
|
||||||
name: "cra-test",
|
name: "dummy-cra",
|
||||||
nameWithOwner: `${USER}/cra-test`,
|
nameWithOwner: "grsdummy/dummy-cra",
|
||||||
isPrivate: false,
|
isPrivate: false,
|
||||||
isArchived: false,
|
isArchived: false,
|
||||||
isTemplate: false,
|
isTemplate: false,
|
||||||
stargazers: {
|
stargazers: {
|
||||||
totalCount: 1,
|
totalCount: 1,
|
||||||
},
|
},
|
||||||
description: "Simple cra test repo.",
|
description: "Dummy create react app.",
|
||||||
primaryLanguage: {
|
primaryLanguage: {
|
||||||
color: "#f1e05a",
|
color: "#3178c6",
|
||||||
id: "MDg6TGFuZ3VhZ2UxNDA=",
|
id: "MDg6TGFuZ3VhZ2UyODc=",
|
||||||
name: "JavaScript",
|
name: "TypeScript",
|
||||||
},
|
},
|
||||||
forkCount: 0,
|
forkCount: 0,
|
||||||
starCount: 1,
|
starCount: 1,
|
||||||
|
|||||||
@@ -239,39 +239,6 @@ describe("Test renderStatsCard", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should render custom ring_color properly", () => {
|
|
||||||
const customColors = {
|
|
||||||
title_color: "5a0",
|
|
||||||
ring_color: "0000ff",
|
|
||||||
icon_color: "1b998b",
|
|
||||||
text_color: "9991",
|
|
||||||
bg_color: "252525",
|
|
||||||
};
|
|
||||||
|
|
||||||
document.body.innerHTML = renderStatsCard(stats, { ...customColors });
|
|
||||||
|
|
||||||
const styleTag = document.querySelector("style");
|
|
||||||
const stylesObject = cssToObject(styleTag.innerHTML);
|
|
||||||
|
|
||||||
const headerClassStyles = stylesObject[":host"][".header "];
|
|
||||||
const statClassStyles = stylesObject[":host"][".stat "];
|
|
||||||
const iconClassStyles = stylesObject[":host"][".icon "];
|
|
||||||
const rankCircleStyles = stylesObject[":host"][".rank-circle "];
|
|
||||||
const rankCircleRimStyles = stylesObject[":host"][".rank-circle-rim "];
|
|
||||||
|
|
||||||
expect(headerClassStyles.fill.trim()).toBe(`#${customColors.title_color}`);
|
|
||||||
expect(statClassStyles.fill.trim()).toBe(`#${customColors.text_color}`);
|
|
||||||
expect(iconClassStyles.fill.trim()).toBe(`#${customColors.icon_color}`);
|
|
||||||
expect(rankCircleStyles.stroke.trim()).toBe(`#${customColors.ring_color}`);
|
|
||||||
expect(rankCircleRimStyles.stroke.trim()).toBe(
|
|
||||||
`#${customColors.ring_color}`,
|
|
||||||
);
|
|
||||||
expect(queryByTestId(document.body, "card-bg")).toHaveAttribute(
|
|
||||||
"fill",
|
|
||||||
"#252525",
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should render icons correctly", () => {
|
it("should render icons correctly", () => {
|
||||||
document.body.innerHTML = renderStatsCard(stats, {
|
document.body.innerHTML = renderStatsCard(stats, {
|
||||||
show_icons: true,
|
show_icons: true,
|
||||||
@@ -329,7 +296,7 @@ describe("Test renderStatsCard", () => {
|
|||||||
document.querySelector(
|
document.querySelector(
|
||||||
'g[transform="translate(0, 25)"]>.stagger>.stat.bold',
|
'g[transform="translate(0, 25)"]>.stagger>.stat.bold',
|
||||||
).textContent,
|
).textContent,
|
||||||
).toMatchInlineSnapshot(`"累计提交数(commit) (2023):"`);
|
).toMatchInlineSnapshot(`"累计提交数(commit) (2022):"`);
|
||||||
expect(
|
expect(
|
||||||
document.querySelector(
|
document.querySelector(
|
||||||
'g[transform="translate(0, 50)"]>.stagger>.stat.bold',
|
'g[transform="translate(0, 50)"]>.stagger>.stat.bold',
|
||||||
@@ -344,7 +311,7 @@ describe("Test renderStatsCard", () => {
|
|||||||
document.querySelector(
|
document.querySelector(
|
||||||
'g[transform="translate(0, 100)"]>.stagger>.stat.bold',
|
'g[transform="translate(0, 100)"]>.stagger>.stat.bold',
|
||||||
).textContent,
|
).textContent,
|
||||||
).toMatchInlineSnapshot(`"参与项目数 (last year):"`);
|
).toMatchInlineSnapshot(`"参与项目数:"`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should render without rounding", () => {
|
it("should render without rounding", () => {
|
||||||
|
|||||||
@@ -16,15 +16,6 @@ describe("Test Render Wakatime Card", () => {
|
|||||||
expect(card).toMatchSnapshot();
|
expect(card).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should render correctly with compact layout when langs_count is set", () => {
|
|
||||||
const card = renderWakatimeCard(wakaTimeData.data, {
|
|
||||||
layout: "compact",
|
|
||||||
langs_count: 2,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(card).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should hide languages when hide is passed", () => {
|
it("should hide languages when hide is passed", () => {
|
||||||
document.body.innerHTML = renderWakatimeCard(wakaTimeData.data, {
|
document.body.innerHTML = renderWakatimeCard(wakaTimeData.data, {
|
||||||
hide: ["YAML", "Other"],
|
hide: ["YAML", "Other"],
|
||||||
|
|||||||
+151
-62
@@ -1,48 +1,25 @@
|
|||||||
import { queryByTestId } from "@testing-library/dom";
|
import { queryByTestId } from "@testing-library/dom";
|
||||||
import "@testing-library/jest-dom";
|
import "@testing-library/jest-dom";
|
||||||
import {
|
import {
|
||||||
|
chunkArray,
|
||||||
|
clampValue,
|
||||||
encodeHTML,
|
encodeHTML,
|
||||||
|
fallbackColor,
|
||||||
|
flexLayout,
|
||||||
getCardColors,
|
getCardColors,
|
||||||
|
isValidGradient,
|
||||||
|
isValidHexColor,
|
||||||
kFormatter,
|
kFormatter,
|
||||||
|
lowercaseTrim,
|
||||||
|
measureText,
|
||||||
|
parseArray,
|
||||||
parseBoolean,
|
parseBoolean,
|
||||||
|
parseEmojis,
|
||||||
renderError,
|
renderError,
|
||||||
wrapTextMultiline,
|
wrapTextMultiline,
|
||||||
} from "../src/common/utils.js";
|
} from "../src/common/utils.js";
|
||||||
|
|
||||||
describe("Test utils.js", () => {
|
describe("Test utils.js", () => {
|
||||||
it("should test kFormatter", () => {
|
|
||||||
expect(kFormatter(1)).toBe(1);
|
|
||||||
expect(kFormatter(-1)).toBe(-1);
|
|
||||||
expect(kFormatter(500)).toBe(500);
|
|
||||||
expect(kFormatter(1000)).toBe("1k");
|
|
||||||
expect(kFormatter(10000)).toBe("10k");
|
|
||||||
expect(kFormatter(12345)).toBe("12.3k");
|
|
||||||
expect(kFormatter(9900000)).toBe("9900k");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should test parseBoolean", () => {
|
|
||||||
expect(parseBoolean(true)).toBe(true);
|
|
||||||
expect(parseBoolean(false)).toBe(false);
|
|
||||||
|
|
||||||
expect(parseBoolean("true")).toBe(true);
|
|
||||||
expect(parseBoolean("false")).toBe(false);
|
|
||||||
expect(parseBoolean("True")).toBe(true);
|
|
||||||
expect(parseBoolean("False")).toBe(false);
|
|
||||||
expect(parseBoolean("TRUE")).toBe(true);
|
|
||||||
expect(parseBoolean("FALSE")).toBe(false);
|
|
||||||
|
|
||||||
expect(parseBoolean("1")).toBe(undefined);
|
|
||||||
expect(parseBoolean("0")).toBe(undefined);
|
|
||||||
expect(parseBoolean("")).toBe(undefined);
|
|
||||||
expect(parseBoolean(undefined)).toBe(undefined);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should test encodeHTML", () => {
|
|
||||||
expect(encodeHTML(`<html>hello world<,.#4^&^@%!))`)).toBe(
|
|
||||||
"<html>hello world<,.#4^&^@%!))",
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should test renderError", () => {
|
it("should test renderError", () => {
|
||||||
document.body.innerHTML = renderError("Something went wrong");
|
document.body.innerHTML = renderError("Something went wrong");
|
||||||
expect(
|
expect(
|
||||||
@@ -62,11 +39,115 @@ describe("Test utils.js", () => {
|
|||||||
).toHaveTextContent(/Secondary Message/gim);
|
).toHaveTextContent(/Secondary Message/gim);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should test encodeHTML", () => {
|
||||||
|
expect(encodeHTML(`<html>hello world<,.#4^&^@%!))`)).toBe(
|
||||||
|
"<html>hello world<,.#4^&^@%!))",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should test kFormatter", () => {
|
||||||
|
expect(kFormatter(1)).toBe(1);
|
||||||
|
expect(kFormatter(-1)).toBe(-1);
|
||||||
|
expect(kFormatter(500)).toBe(500);
|
||||||
|
expect(kFormatter(1000)).toBe("1k");
|
||||||
|
expect(kFormatter(10000)).toBe("10k");
|
||||||
|
expect(kFormatter(12345)).toBe("12.3k");
|
||||||
|
expect(kFormatter(9900000)).toBe("9900k");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should test isValidHexColor", () => {
|
||||||
|
expect(isValidHexColor("00f000")).toBe(true);
|
||||||
|
expect(isValidHexColor("00f0003f")).toBe(true);
|
||||||
|
expect(isValidHexColor("0f03")).toBe(true);
|
||||||
|
expect(isValidHexColor("0f0")).toBe(true);
|
||||||
|
|
||||||
|
expect(isValidHexColor("00f00X")).toBe(false);
|
||||||
|
expect(isValidHexColor("#")).toBe(false);
|
||||||
|
expect(isValidHexColor("")).toBe(false);
|
||||||
|
expect(isValidHexColor("0")).toBe(false);
|
||||||
|
expect(isValidHexColor("00")).toBe(false);
|
||||||
|
expect(isValidHexColor("00f00")).toBe(false);
|
||||||
|
expect(isValidHexColor("00f00")).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should test parseBoolean", () => {
|
||||||
|
expect(parseBoolean(true)).toBe(true);
|
||||||
|
expect(parseBoolean(false)).toBe(false);
|
||||||
|
|
||||||
|
expect(parseBoolean("true")).toBe(true);
|
||||||
|
expect(parseBoolean("false")).toBe(false);
|
||||||
|
|
||||||
|
expect(parseBoolean(undefined)).toBe(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should test parseArray", () => {
|
||||||
|
expect(parseArray("1")).toEqual(["1"]);
|
||||||
|
expect(parseArray("1,2,3")).toEqual(["1", "2", "3"]);
|
||||||
|
expect(parseArray("test")).toEqual(["test"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should test clampValue", () => {
|
||||||
|
expect(clampValue(1, 0, 2)).toBe(1);
|
||||||
|
expect(clampValue(1, 2, 3)).toBe(2);
|
||||||
|
expect(clampValue(1, 0, 0)).toBe(0);
|
||||||
|
expect(clampValue(1, 0, -1)).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should test isValidGradient", () => {
|
||||||
|
expect(isValidGradient(["90", "fff", "000"])).toBe(true);
|
||||||
|
expect(isValidGradient(["90", "fff", "000", "fff"])).toBe(true);
|
||||||
|
|
||||||
|
expect(isValidGradient(["fff", "fff", "fxf"])).toBe(false);
|
||||||
|
expect(isValidGradient(["90", "fff", "fxf"])).toBe(false);
|
||||||
|
expect(isValidGradient(["90", "fxf", ""])).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should test fallbackColor", () => {
|
||||||
|
expect(fallbackColor("fff", "000")).toBe("#fff");
|
||||||
|
expect(fallbackColor("fxf", "#000")).toBe("#000");
|
||||||
|
expect(fallbackColor("90,fff,000", "#000")).toStrictEqual([
|
||||||
|
"90",
|
||||||
|
"fff",
|
||||||
|
"000",
|
||||||
|
]);
|
||||||
|
expect(fallbackColor("90,fff,fxf", "#000")).toBe("#000");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should test flexLayout", () => {
|
||||||
|
const items = Array(2).fill(`<g></g>`);
|
||||||
|
|
||||||
|
const columnLayout = flexLayout({
|
||||||
|
items,
|
||||||
|
gap: 25,
|
||||||
|
direction: "column",
|
||||||
|
sizes: [],
|
||||||
|
});
|
||||||
|
expect(columnLayout[0]).toContain("translate(0, 0)");
|
||||||
|
expect(columnLayout[1]).toContain("translate(0, 25)");
|
||||||
|
|
||||||
|
const rowLayout = flexLayout({
|
||||||
|
items,
|
||||||
|
gap: 25,
|
||||||
|
direction: "row",
|
||||||
|
sizes: [],
|
||||||
|
});
|
||||||
|
expect(rowLayout[0]).toContain("translate(0, 0)");
|
||||||
|
expect(rowLayout[1]).toContain("translate(25, 0)");
|
||||||
|
|
||||||
|
const sizedLayout = flexLayout({
|
||||||
|
items,
|
||||||
|
gap: 25,
|
||||||
|
direction: "row",
|
||||||
|
sizes: [25, 0],
|
||||||
|
});
|
||||||
|
expect(sizedLayout[0]).toContain("translate(0, 0)");
|
||||||
|
expect(sizedLayout[1]).toContain("translate(50, 0)");
|
||||||
|
});
|
||||||
|
|
||||||
it("getCardColors: should return expected values", () => {
|
it("getCardColors: should return expected values", () => {
|
||||||
let colors = getCardColors({
|
let colors = getCardColors({
|
||||||
title_color: "f00",
|
title_color: "f00",
|
||||||
text_color: "0f0",
|
text_color: "0f0",
|
||||||
ring_color: "0000ff",
|
|
||||||
icon_color: "00f",
|
icon_color: "00f",
|
||||||
bg_color: "fff",
|
bg_color: "fff",
|
||||||
border_color: "fff",
|
border_color: "fff",
|
||||||
@@ -76,7 +157,6 @@ describe("Test utils.js", () => {
|
|||||||
titleColor: "#f00",
|
titleColor: "#f00",
|
||||||
textColor: "#0f0",
|
textColor: "#0f0",
|
||||||
iconColor: "#00f",
|
iconColor: "#00f",
|
||||||
ringColor: "#0000ff",
|
|
||||||
bgColor: "#fff",
|
bgColor: "#fff",
|
||||||
borderColor: "#fff",
|
borderColor: "#fff",
|
||||||
});
|
});
|
||||||
@@ -95,7 +175,6 @@ describe("Test utils.js", () => {
|
|||||||
titleColor: "#2f80ed",
|
titleColor: "#2f80ed",
|
||||||
textColor: "#0f0",
|
textColor: "#0f0",
|
||||||
iconColor: "#00f",
|
iconColor: "#00f",
|
||||||
ringColor: "#2f80ed",
|
|
||||||
bgColor: "#fff",
|
bgColor: "#fff",
|
||||||
borderColor: "#e4e2e2",
|
borderColor: "#e4e2e2",
|
||||||
});
|
});
|
||||||
@@ -108,41 +187,20 @@ describe("Test utils.js", () => {
|
|||||||
expect(colors).toStrictEqual({
|
expect(colors).toStrictEqual({
|
||||||
titleColor: "#fff",
|
titleColor: "#fff",
|
||||||
textColor: "#9f9f9f",
|
textColor: "#9f9f9f",
|
||||||
ringColor: "#fff",
|
|
||||||
iconColor: "#79ff97",
|
iconColor: "#79ff97",
|
||||||
bgColor: "#151515",
|
bgColor: "#151515",
|
||||||
borderColor: "#e4e2e2",
|
borderColor: "#e4e2e2",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("getCardColors: should return ring color equal to title color if not ring color is defined", () => {
|
it("wrapTextMultiline: should not wrap small texts", () => {
|
||||||
let colors = getCardColors({
|
|
||||||
title_color: "f00",
|
|
||||||
text_color: "0f0",
|
|
||||||
icon_color: "00f",
|
|
||||||
bg_color: "fff",
|
|
||||||
border_color: "fff",
|
|
||||||
theme: "dark",
|
|
||||||
});
|
|
||||||
expect(colors).toStrictEqual({
|
|
||||||
titleColor: "#f00",
|
|
||||||
textColor: "#0f0",
|
|
||||||
iconColor: "#00f",
|
|
||||||
ringColor: "#f00",
|
|
||||||
bgColor: "#fff",
|
|
||||||
borderColor: "#fff",
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("wrapTextMultiline", () => {
|
|
||||||
it("should not wrap small texts", () => {
|
|
||||||
{
|
{
|
||||||
let multiLineText = wrapTextMultiline("Small text should not wrap");
|
let multiLineText = wrapTextMultiline("Small text should not wrap");
|
||||||
expect(multiLineText).toEqual(["Small text should not wrap"]);
|
expect(multiLineText).toEqual(["Small text should not wrap"]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
it("should wrap large texts", () => {
|
|
||||||
|
it("wrapTextMultiline: should wrap large texts", () => {
|
||||||
let multiLineText = wrapTextMultiline(
|
let multiLineText = wrapTextMultiline(
|
||||||
"Hello world long long long text",
|
"Hello world long long long text",
|
||||||
20,
|
20,
|
||||||
@@ -150,7 +208,8 @@ describe("wrapTextMultiline", () => {
|
|||||||
);
|
);
|
||||||
expect(multiLineText).toEqual(["Hello world long", "long long text"]);
|
expect(multiLineText).toEqual(["Hello world long", "long long text"]);
|
||||||
});
|
});
|
||||||
it("should wrap large texts and limit max lines", () => {
|
|
||||||
|
it("wrapTextMultiline: should wrap large texts and limit max lines", () => {
|
||||||
let multiLineText = wrapTextMultiline(
|
let multiLineText = wrapTextMultiline(
|
||||||
"Hello world long long long text",
|
"Hello world long long long text",
|
||||||
10,
|
10,
|
||||||
@@ -158,11 +217,41 @@ describe("wrapTextMultiline", () => {
|
|||||||
);
|
);
|
||||||
expect(multiLineText).toEqual(["Hello", "world long..."]);
|
expect(multiLineText).toEqual(["Hello", "world long..."]);
|
||||||
});
|
});
|
||||||
it("should wrap chinese by punctuation", () => {
|
|
||||||
|
it("wrapTextMultiline: should wrap chinese by punctuation", () => {
|
||||||
let multiLineText = wrapTextMultiline(
|
let multiLineText = wrapTextMultiline(
|
||||||
"专门为刚开始刷题的同学准备的算法基地,没有最细只有更细,立志用动画将晦涩难懂的算法说的通俗易懂!",
|
"专门为刚开始刷题的同学准备的算法基地,没有最细只有更细,立志用动画将晦涩难懂的算法说的通俗易懂!",
|
||||||
);
|
);
|
||||||
expect(multiLineText.length).toEqual(3);
|
expect(multiLineText.length).toEqual(3);
|
||||||
expect(multiLineText[0].length).toEqual(18 * 8); // &#xxxxx; x 8
|
expect(multiLineText[0].length).toEqual(18 * 8); // &#xxxxx; x 8
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should test measureText", () => {
|
||||||
|
expect(measureText("test")).toEqual(16.078125);
|
||||||
|
|
||||||
|
expect(measureText("test", 12)).toEqual(19.293750000000003);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should test lowercaseTrim", () => {
|
||||||
|
expect(lowercaseTrim("test")).toEqual("test");
|
||||||
|
expect(lowercaseTrim(" test ")).toEqual("test");
|
||||||
|
expect(lowercaseTrim(" Test ")).toEqual("test");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should test chunkArray", () => {
|
||||||
|
expect(chunkArray([1, 2, 3], 1)).toEqual([[1], [2], [3]]);
|
||||||
|
expect(chunkArray([1, 2, 3, 4, 5], 2)).toEqual([[1, 2], [3, 4], [5]]);
|
||||||
|
expect(chunkArray([1, 2, 3, 4, 5, 6, 7], 3)).toEqual([
|
||||||
|
[1, 2, 3],
|
||||||
|
[4, 5, 6],
|
||||||
|
[7],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should test parseEmojis", () => {
|
||||||
|
expect(parseEmojis("test")).toEqual("test");
|
||||||
|
expect(parseEmojis("test :smile:")).toEqual("test 😄");
|
||||||
|
expect(parseEmojis("test :smile: :+1:")).toEqual("test 😄 👍");
|
||||||
|
expect(parseEmojis("test :smile: :+1: :smile:")).toEqual("test 😄 👍 😄");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -367,13 +367,6 @@ export const themes = {
|
|||||||
text_color: "e0def4",
|
text_color: "e0def4",
|
||||||
bg_color: "191724",
|
bg_color: "191724",
|
||||||
},
|
},
|
||||||
date_night: {
|
|
||||||
title_color: "DA7885",
|
|
||||||
text_color: "E1B2A2",
|
|
||||||
icon_color: "BB8470",
|
|
||||||
border_color: "170F0C",
|
|
||||||
bg_color: "170F0C",
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default themes;
|
export default themes;
|
||||||
|
|||||||
Reference in New Issue
Block a user