@dytsou/github-readme-stats 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/api/gist.js +98 -0
- package/api/index.js +146 -0
- package/api/pin.js +114 -0
- package/api/status/pat-info.js +193 -0
- package/api/status/up.js +129 -0
- package/api/top-langs.js +163 -0
- package/api/wakatime.js +129 -0
- package/package.json +102 -0
- package/readme.md +412 -0
- package/src/calculateRank.js +87 -0
- package/src/cards/gist.js +151 -0
- package/src/cards/index.js +4 -0
- package/src/cards/repo.js +199 -0
- package/src/cards/stats.js +607 -0
- package/src/cards/top-languages.js +968 -0
- package/src/cards/types.d.ts +67 -0
- package/src/cards/wakatime.js +482 -0
- package/src/common/Card.js +294 -0
- package/src/common/I18n.js +41 -0
- package/src/common/access.js +69 -0
- package/src/common/api-utils.js +221 -0
- package/src/common/blacklist.js +10 -0
- package/src/common/cache.js +153 -0
- package/src/common/color.js +194 -0
- package/src/common/envs.js +15 -0
- package/src/common/error.js +84 -0
- package/src/common/fmt.js +90 -0
- package/src/common/html.js +43 -0
- package/src/common/http.js +24 -0
- package/src/common/icons.js +63 -0
- package/src/common/index.js +13 -0
- package/src/common/languageColors.json +651 -0
- package/src/common/log.js +14 -0
- package/src/common/ops.js +124 -0
- package/src/common/render.js +261 -0
- package/src/common/retryer.js +97 -0
- package/src/common/worker-adapter.js +148 -0
- package/src/common/worker-env.js +48 -0
- package/src/fetchers/gist.js +114 -0
- package/src/fetchers/repo.js +118 -0
- package/src/fetchers/stats.js +350 -0
- package/src/fetchers/top-languages.js +192 -0
- package/src/fetchers/types.d.ts +118 -0
- package/src/fetchers/wakatime.js +109 -0
- package/src/index.js +2 -0
- package/src/translations.js +1105 -0
- package/src/worker.ts +116 -0
- package/themes/README.md +229 -0
- package/themes/index.js +467 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
type ThemeNames = keyof typeof import("../../themes/index.js");
|
|
2
|
+
type RankIcon = "default" | "github" | "percentile";
|
|
3
|
+
|
|
4
|
+
export type CommonOptions = {
|
|
5
|
+
title_color: string;
|
|
6
|
+
icon_color: string;
|
|
7
|
+
text_color: string;
|
|
8
|
+
bg_color: string;
|
|
9
|
+
theme: ThemeNames;
|
|
10
|
+
border_radius: number;
|
|
11
|
+
border_color: string;
|
|
12
|
+
locale: string;
|
|
13
|
+
hide_border: boolean;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type StatCardOptions = CommonOptions & {
|
|
17
|
+
hide: string[];
|
|
18
|
+
show_icons: boolean;
|
|
19
|
+
hide_title: boolean;
|
|
20
|
+
card_width: number;
|
|
21
|
+
hide_rank: boolean;
|
|
22
|
+
include_all_commits: boolean;
|
|
23
|
+
commits_year: number;
|
|
24
|
+
line_height: number | string;
|
|
25
|
+
custom_title: string;
|
|
26
|
+
disable_animations: boolean;
|
|
27
|
+
number_format: string;
|
|
28
|
+
number_precision: number;
|
|
29
|
+
ring_color: string;
|
|
30
|
+
text_bold: boolean;
|
|
31
|
+
rank_icon: RankIcon;
|
|
32
|
+
show: string[];
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type RepoCardOptions = CommonOptions & {
|
|
36
|
+
show_owner: boolean;
|
|
37
|
+
description_lines_count: number;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export type TopLangOptions = CommonOptions & {
|
|
41
|
+
hide_title: boolean;
|
|
42
|
+
card_width: number;
|
|
43
|
+
hide: string[];
|
|
44
|
+
layout: "compact" | "normal" | "donut" | "donut-vertical" | "pie";
|
|
45
|
+
custom_title: string;
|
|
46
|
+
langs_count: number;
|
|
47
|
+
disable_animations: boolean;
|
|
48
|
+
hide_progress: boolean;
|
|
49
|
+
stats_format: "percentages" | "bytes";
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export type WakaTimeOptions = CommonOptions & {
|
|
53
|
+
hide_title: boolean;
|
|
54
|
+
hide: string[];
|
|
55
|
+
card_width: number;
|
|
56
|
+
line_height: string;
|
|
57
|
+
hide_progress: boolean;
|
|
58
|
+
custom_title: string;
|
|
59
|
+
layout: "compact" | "normal";
|
|
60
|
+
langs_count: number;
|
|
61
|
+
display_format: "time" | "percent";
|
|
62
|
+
disable_animations: boolean;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export type GistCardOptions = CommonOptions & {
|
|
66
|
+
show_owner: boolean;
|
|
67
|
+
};
|
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import { Card } from "../common/Card.js";
|
|
4
|
+
import { getCardColors } from "../common/color.js";
|
|
5
|
+
import { escapeCSSValue } from "../common/html.js";
|
|
6
|
+
import { I18n } from "../common/I18n.js";
|
|
7
|
+
import { clampValue, lowercaseTrim } from "../common/ops.js";
|
|
8
|
+
import { createProgressNode, flexLayout } from "../common/render.js";
|
|
9
|
+
import { wakatimeCardLocales } from "../translations.js";
|
|
10
|
+
|
|
11
|
+
/** Import language colors.
|
|
12
|
+
*
|
|
13
|
+
* @description Using ES module JSON import which works in modern Node.js and Cloudflare Workers.
|
|
14
|
+
*/
|
|
15
|
+
// @ts-ignore - JSON import
|
|
16
|
+
import languageColors from "../common/languageColors.json";
|
|
17
|
+
|
|
18
|
+
const DEFAULT_CARD_WIDTH = 495;
|
|
19
|
+
const MIN_CARD_WIDTH = 250;
|
|
20
|
+
const COMPACT_LAYOUT_MIN_WIDTH = 400;
|
|
21
|
+
const DEFAULT_LINE_HEIGHT = 25;
|
|
22
|
+
const PROGRESSBAR_PADDING = 130;
|
|
23
|
+
const HIDDEN_PROGRESSBAR_PADDING = 170;
|
|
24
|
+
const COMPACT_LAYOUT_PROGRESSBAR_PADDING = 25;
|
|
25
|
+
const TOTAL_TEXT_WIDTH = 275;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Creates the no coding activity SVG node.
|
|
29
|
+
*
|
|
30
|
+
* @param {object} props The function properties.
|
|
31
|
+
* @param {string} props.color No coding activity text color.
|
|
32
|
+
* @param {string} props.text No coding activity translated text.
|
|
33
|
+
* @returns {string} No coding activity SVG node string.
|
|
34
|
+
*/
|
|
35
|
+
const noCodingActivityNode = ({ color, text }) => {
|
|
36
|
+
return `
|
|
37
|
+
<text x="25" y="11" class="stat bold" fill="${color}">${text}</text>
|
|
38
|
+
`;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @typedef {import('../fetchers/types').WakaTimeLang} WakaTimeLang
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Format language value.
|
|
47
|
+
*
|
|
48
|
+
* @param {Object} args The function arguments.
|
|
49
|
+
* @param {WakaTimeLang} args.lang The language object.
|
|
50
|
+
* @param {"time" | "percent"} args.display_format The display format of the language node.
|
|
51
|
+
* @returns {string} The formatted language value.
|
|
52
|
+
*/
|
|
53
|
+
const formatLanguageValue = ({ display_format, lang }) => {
|
|
54
|
+
return display_format === "percent"
|
|
55
|
+
? `${lang.percent.toFixed(2).toString()} %`
|
|
56
|
+
: lang.text;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Create compact WakaTime layout.
|
|
61
|
+
*
|
|
62
|
+
* @param {Object} args The function arguments.
|
|
63
|
+
* @param {WakaTimeLang} args.lang The languages array.
|
|
64
|
+
* @param {number} args.x The x position of the language node.
|
|
65
|
+
* @param {number} args.y The y position of the language node.
|
|
66
|
+
* @param {"time" | "percent"} args.display_format The display format of the language node.
|
|
67
|
+
* @returns {string} The compact layout language SVG node.
|
|
68
|
+
*/
|
|
69
|
+
const createCompactLangNode = ({ lang, x, y, display_format }) => {
|
|
70
|
+
// @ts-ignore
|
|
71
|
+
const color = languageColors[lang.name] || "#858585";
|
|
72
|
+
const value = formatLanguageValue({ display_format, lang });
|
|
73
|
+
|
|
74
|
+
return `
|
|
75
|
+
<g transform="translate(${x}, ${y})">
|
|
76
|
+
<circle cx="5" cy="6" r="5" fill="${color}" />
|
|
77
|
+
<text data-testid="lang-name" x="15" y="10" class='lang-name'>
|
|
78
|
+
${lang.name} - ${value}
|
|
79
|
+
</text>
|
|
80
|
+
</g>
|
|
81
|
+
`;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Create WakaTime language text node item.
|
|
86
|
+
*
|
|
87
|
+
* @param {Object} args The function arguments.
|
|
88
|
+
* @param {WakaTimeLang[]} args.langs The language objects.
|
|
89
|
+
* @param {number} args.y The y position of the language node.
|
|
90
|
+
* @param {"time" | "percent"} args.display_format The display format of the language node.
|
|
91
|
+
* @param {number} args.card_width Width in px of the card.
|
|
92
|
+
* @returns {string[]} The language text node items.
|
|
93
|
+
*/
|
|
94
|
+
const createLanguageTextNode = ({ langs, y, display_format, card_width }) => {
|
|
95
|
+
const LEFT_X = 25;
|
|
96
|
+
const RIGHT_X_BASE = 230;
|
|
97
|
+
const rightOffset = (card_width - DEFAULT_CARD_WIDTH) / 2;
|
|
98
|
+
const RIGHT_X = RIGHT_X_BASE + rightOffset;
|
|
99
|
+
|
|
100
|
+
return langs.map((lang, index) => {
|
|
101
|
+
const isLeft = index % 2 === 0;
|
|
102
|
+
return createCompactLangNode({
|
|
103
|
+
lang,
|
|
104
|
+
x: isLeft ? LEFT_X : RIGHT_X,
|
|
105
|
+
y: y + DEFAULT_LINE_HEIGHT * Math.floor(index / 2),
|
|
106
|
+
display_format,
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Create WakaTime text item.
|
|
113
|
+
*
|
|
114
|
+
* @param {Object} args The function arguments.
|
|
115
|
+
* @param {string} args.id The id of the text node item.
|
|
116
|
+
* @param {string} args.label The label of the text node item.
|
|
117
|
+
* @param {string} args.value The value of the text node item.
|
|
118
|
+
* @param {number} args.index The index of the text node item.
|
|
119
|
+
* @param {number} args.percent Percentage of the text node item.
|
|
120
|
+
* @param {boolean=} args.hideProgress Whether to hide the progress bar.
|
|
121
|
+
* @param {string} args.progressBarColor The color of the progress bar.
|
|
122
|
+
* @param {string} args.progressBarBackgroundColor The color of the progress bar background.
|
|
123
|
+
* @param {number} args.progressBarWidth The width of the progress bar.
|
|
124
|
+
* @returns {string} The text SVG node.
|
|
125
|
+
*/
|
|
126
|
+
const createTextNode = ({
|
|
127
|
+
id,
|
|
128
|
+
label,
|
|
129
|
+
value,
|
|
130
|
+
index,
|
|
131
|
+
percent,
|
|
132
|
+
hideProgress,
|
|
133
|
+
progressBarColor,
|
|
134
|
+
progressBarBackgroundColor,
|
|
135
|
+
progressBarWidth,
|
|
136
|
+
}) => {
|
|
137
|
+
const staggerDelay = (index + 3) * 150;
|
|
138
|
+
const cardProgress = hideProgress
|
|
139
|
+
? null
|
|
140
|
+
: createProgressNode({
|
|
141
|
+
x: 110,
|
|
142
|
+
y: 4,
|
|
143
|
+
progress: percent,
|
|
144
|
+
color: progressBarColor,
|
|
145
|
+
width: progressBarWidth,
|
|
146
|
+
// @ts-ignore
|
|
147
|
+
name: label,
|
|
148
|
+
progressBarBackgroundColor,
|
|
149
|
+
delay: staggerDelay + 300,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
return `
|
|
153
|
+
<g class="stagger" style="animation-delay: ${staggerDelay}ms" transform="translate(25, 0)">
|
|
154
|
+
<text class="stat bold" y="12.5" data-testid="${id}">${label}:</text>
|
|
155
|
+
<text
|
|
156
|
+
class="stat"
|
|
157
|
+
x="${hideProgress ? HIDDEN_PROGRESSBAR_PADDING : PROGRESSBAR_PADDING + progressBarWidth}"
|
|
158
|
+
y="12.5"
|
|
159
|
+
>${value}</text>
|
|
160
|
+
${cardProgress}
|
|
161
|
+
</g>
|
|
162
|
+
`;
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Recalculating percentages so that, compact layout's progress bar does not break when
|
|
167
|
+
* hiding languages.
|
|
168
|
+
*
|
|
169
|
+
* @param {WakaTimeLang[]} languages The languages array.
|
|
170
|
+
* @returns {void} The recalculated languages array.
|
|
171
|
+
*/
|
|
172
|
+
const recalculatePercentages = (languages) => {
|
|
173
|
+
const totalSum = languages.reduce(
|
|
174
|
+
(totalSum, language) => totalSum + language.percent,
|
|
175
|
+
0,
|
|
176
|
+
);
|
|
177
|
+
const weight = +(100 / totalSum).toFixed(2);
|
|
178
|
+
languages.forEach((language) => {
|
|
179
|
+
language.percent = +(language.percent * weight).toFixed(2);
|
|
180
|
+
});
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Retrieves CSS styles for a card.
|
|
185
|
+
*
|
|
186
|
+
* @param {Object} colors The colors to use for the card.
|
|
187
|
+
* @param {string} colors.titleColor The title color.
|
|
188
|
+
* @param {string} colors.textColor The text color.
|
|
189
|
+
* @returns {string} Card CSS styles.
|
|
190
|
+
*/
|
|
191
|
+
const getStyles = ({
|
|
192
|
+
// eslint-disable-next-line no-unused-vars
|
|
193
|
+
titleColor,
|
|
194
|
+
textColor,
|
|
195
|
+
}) => {
|
|
196
|
+
const safeTextColor = escapeCSSValue(textColor);
|
|
197
|
+
return `
|
|
198
|
+
.stat {
|
|
199
|
+
font: 600 14px 'Segoe UI', Ubuntu, "Helvetica Neue", Sans-Serif; fill: ${safeTextColor};
|
|
200
|
+
}
|
|
201
|
+
@supports(-moz-appearance: auto) {
|
|
202
|
+
/* Selector detects Firefox */
|
|
203
|
+
.stat { font-size:12px; }
|
|
204
|
+
}
|
|
205
|
+
.stagger {
|
|
206
|
+
opacity: 0;
|
|
207
|
+
animation: fadeInAnimation 0.3s ease-in-out forwards;
|
|
208
|
+
}
|
|
209
|
+
.not_bold { font-weight: 400 }
|
|
210
|
+
.bold { font-weight: 700 }
|
|
211
|
+
`;
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Normalize incoming width (string or number) and clamp to minimum.
|
|
216
|
+
*
|
|
217
|
+
* @param {Object} args The function arguments.
|
|
218
|
+
* @param {WakaTimeOptions["layout"] | undefined} args.layout The incoming layout value.
|
|
219
|
+
* @param {number|undefined} args.value The incoming width value.
|
|
220
|
+
* @returns {number} The normalized width value.
|
|
221
|
+
*/
|
|
222
|
+
const normalizeCardWidth = ({ value, layout }) => {
|
|
223
|
+
if (value === undefined || value === null || isNaN(value)) {
|
|
224
|
+
return DEFAULT_CARD_WIDTH;
|
|
225
|
+
}
|
|
226
|
+
return Math.max(
|
|
227
|
+
layout === "compact" ? COMPACT_LAYOUT_MIN_WIDTH : MIN_CARD_WIDTH,
|
|
228
|
+
value,
|
|
229
|
+
);
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* @typedef {import('../fetchers/types').WakaTimeData} WakaTimeData
|
|
234
|
+
* @typedef {import('./types').WakaTimeOptions} WakaTimeOptions
|
|
235
|
+
*/
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Renders WakaTime card.
|
|
239
|
+
*
|
|
240
|
+
* @param {Partial<WakaTimeData>} stats WakaTime stats.
|
|
241
|
+
* @param {Partial<WakaTimeOptions>} options Card options.
|
|
242
|
+
* @returns {string} WakaTime card SVG.
|
|
243
|
+
*/
|
|
244
|
+
const renderWakatimeCard = (stats = {}, options = { hide: [] }) => {
|
|
245
|
+
let { languages = [] } = stats;
|
|
246
|
+
const {
|
|
247
|
+
hide_title = false,
|
|
248
|
+
hide_border = false,
|
|
249
|
+
card_width,
|
|
250
|
+
hide,
|
|
251
|
+
line_height = DEFAULT_LINE_HEIGHT,
|
|
252
|
+
title_color,
|
|
253
|
+
icon_color,
|
|
254
|
+
text_color,
|
|
255
|
+
bg_color,
|
|
256
|
+
theme = "default",
|
|
257
|
+
hide_progress,
|
|
258
|
+
custom_title,
|
|
259
|
+
locale,
|
|
260
|
+
layout,
|
|
261
|
+
langs_count = languages.length,
|
|
262
|
+
border_radius,
|
|
263
|
+
border_color,
|
|
264
|
+
display_format = "time",
|
|
265
|
+
disable_animations,
|
|
266
|
+
} = options;
|
|
267
|
+
|
|
268
|
+
const normalizedWidth = normalizeCardWidth({ value: card_width, layout });
|
|
269
|
+
|
|
270
|
+
const shouldHideLangs = Array.isArray(hide) && hide.length > 0;
|
|
271
|
+
if (shouldHideLangs) {
|
|
272
|
+
const languagesToHide = new Set(hide.map((lang) => lowercaseTrim(lang)));
|
|
273
|
+
languages = languages.filter(
|
|
274
|
+
(lang) => !languagesToHide.has(lowercaseTrim(lang.name)),
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Since the percentages are sorted in descending order, we can just
|
|
279
|
+
// slice from the beginning without sorting.
|
|
280
|
+
languages = languages.slice(0, langs_count);
|
|
281
|
+
recalculatePercentages(languages);
|
|
282
|
+
|
|
283
|
+
const i18n = new I18n({
|
|
284
|
+
locale,
|
|
285
|
+
translations: wakatimeCardLocales,
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
const lheight = parseInt(String(line_height), 10);
|
|
289
|
+
|
|
290
|
+
const langsCount = clampValue(langs_count, 1, langs_count);
|
|
291
|
+
|
|
292
|
+
// returns theme based colors with proper overrides and defaults
|
|
293
|
+
const { titleColor, textColor, iconColor, bgColor, borderColor } =
|
|
294
|
+
getCardColors({
|
|
295
|
+
title_color,
|
|
296
|
+
icon_color,
|
|
297
|
+
text_color,
|
|
298
|
+
bg_color,
|
|
299
|
+
border_color,
|
|
300
|
+
theme,
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
const filteredLanguages = languages
|
|
304
|
+
.filter((language) => language.hours || language.minutes)
|
|
305
|
+
.slice(0, langsCount);
|
|
306
|
+
|
|
307
|
+
// Calculate the card height depending on how many items there are
|
|
308
|
+
// but if rank circle is visible clamp the minimum height to `150`
|
|
309
|
+
let height = Math.max(45 + (filteredLanguages.length + 1) * lheight, 150);
|
|
310
|
+
|
|
311
|
+
const cssStyles = getStyles({
|
|
312
|
+
titleColor,
|
|
313
|
+
textColor,
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
let finalLayout = "";
|
|
317
|
+
|
|
318
|
+
// RENDER COMPACT LAYOUT
|
|
319
|
+
if (layout === "compact") {
|
|
320
|
+
const width = normalizedWidth - 5;
|
|
321
|
+
height =
|
|
322
|
+
90 + Math.round(filteredLanguages.length / 2) * DEFAULT_LINE_HEIGHT;
|
|
323
|
+
|
|
324
|
+
// progressOffset holds the previous language's width and used to offset the next language
|
|
325
|
+
// so that we can stack them one after another, like this: [--][----][---]
|
|
326
|
+
let progressOffset = 0;
|
|
327
|
+
const compactProgressBar = filteredLanguages
|
|
328
|
+
.map((language) => {
|
|
329
|
+
const progress =
|
|
330
|
+
((width - COMPACT_LAYOUT_PROGRESSBAR_PADDING) * language.percent) /
|
|
331
|
+
100;
|
|
332
|
+
|
|
333
|
+
// @ts-ignore
|
|
334
|
+
const languageColor = languageColors[language.name] || "#858585";
|
|
335
|
+
const safeLanguageColor = escapeCSSValue(languageColor);
|
|
336
|
+
|
|
337
|
+
const output = `
|
|
338
|
+
<rect
|
|
339
|
+
mask="url(#rect-mask)"
|
|
340
|
+
data-testid="lang-progress"
|
|
341
|
+
x="${progressOffset}"
|
|
342
|
+
y="0"
|
|
343
|
+
width="${progress}"
|
|
344
|
+
height="8"
|
|
345
|
+
fill="${safeLanguageColor}"
|
|
346
|
+
/>
|
|
347
|
+
`;
|
|
348
|
+
progressOffset += progress;
|
|
349
|
+
return output;
|
|
350
|
+
})
|
|
351
|
+
.join("");
|
|
352
|
+
|
|
353
|
+
finalLayout = `
|
|
354
|
+
<mask id="rect-mask">
|
|
355
|
+
<rect x="${COMPACT_LAYOUT_PROGRESSBAR_PADDING}" y="0" width="${width - 2 * COMPACT_LAYOUT_PROGRESSBAR_PADDING}" height="8" fill="white" rx="5" />
|
|
356
|
+
</mask>
|
|
357
|
+
${compactProgressBar}
|
|
358
|
+
${
|
|
359
|
+
filteredLanguages.length
|
|
360
|
+
? createLanguageTextNode({
|
|
361
|
+
y: 25,
|
|
362
|
+
langs: filteredLanguages,
|
|
363
|
+
display_format,
|
|
364
|
+
card_width: normalizedWidth,
|
|
365
|
+
}).join("")
|
|
366
|
+
: noCodingActivityNode({
|
|
367
|
+
// @ts-ignore
|
|
368
|
+
color: textColor,
|
|
369
|
+
text: stats.is_coding_activity_visible
|
|
370
|
+
? stats.is_other_usage_visible
|
|
371
|
+
? i18n.t("wakatimecard.nocodingactivity")
|
|
372
|
+
: i18n.t("wakatimecard.nocodedetails")
|
|
373
|
+
: i18n.t("wakatimecard.notpublic"),
|
|
374
|
+
})
|
|
375
|
+
}
|
|
376
|
+
`;
|
|
377
|
+
} else {
|
|
378
|
+
finalLayout = flexLayout({
|
|
379
|
+
items: filteredLanguages.length
|
|
380
|
+
? filteredLanguages.map((language, index) => {
|
|
381
|
+
return createTextNode({
|
|
382
|
+
id: language.name,
|
|
383
|
+
label: language.name,
|
|
384
|
+
value: formatLanguageValue({ display_format, lang: language }),
|
|
385
|
+
index,
|
|
386
|
+
percent: language.percent,
|
|
387
|
+
// @ts-ignore
|
|
388
|
+
progressBarColor: titleColor,
|
|
389
|
+
// @ts-ignore
|
|
390
|
+
progressBarBackgroundColor: textColor,
|
|
391
|
+
hideProgress: hide_progress,
|
|
392
|
+
progressBarWidth: normalizedWidth - TOTAL_TEXT_WIDTH,
|
|
393
|
+
});
|
|
394
|
+
})
|
|
395
|
+
: [
|
|
396
|
+
noCodingActivityNode({
|
|
397
|
+
// @ts-ignore
|
|
398
|
+
color: textColor,
|
|
399
|
+
text: stats.is_coding_activity_visible
|
|
400
|
+
? stats.is_other_usage_visible
|
|
401
|
+
? i18n.t("wakatimecard.nocodingactivity")
|
|
402
|
+
: i18n.t("wakatimecard.nocodedetails")
|
|
403
|
+
: i18n.t("wakatimecard.notpublic"),
|
|
404
|
+
}),
|
|
405
|
+
],
|
|
406
|
+
gap: lheight,
|
|
407
|
+
direction: "column",
|
|
408
|
+
}).join("");
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Get title range text
|
|
412
|
+
let titleText = i18n.t("wakatimecard.title");
|
|
413
|
+
switch (stats.range) {
|
|
414
|
+
case "last_7_days":
|
|
415
|
+
titleText += ` (${i18n.t("wakatimecard.last7days")})`;
|
|
416
|
+
break;
|
|
417
|
+
case "last_year":
|
|
418
|
+
titleText += ` (${i18n.t("wakatimecard.lastyear")})`;
|
|
419
|
+
break;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
const card = new Card({
|
|
423
|
+
customTitle: custom_title,
|
|
424
|
+
defaultTitle: titleText,
|
|
425
|
+
width: normalizedWidth,
|
|
426
|
+
height,
|
|
427
|
+
border_radius,
|
|
428
|
+
colors: {
|
|
429
|
+
titleColor,
|
|
430
|
+
textColor,
|
|
431
|
+
iconColor,
|
|
432
|
+
bgColor,
|
|
433
|
+
borderColor,
|
|
434
|
+
},
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
if (disable_animations) {
|
|
438
|
+
card.disableAnimations();
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
card.setHideBorder(hide_border);
|
|
442
|
+
card.setHideTitle(hide_title);
|
|
443
|
+
// Sanitize color values to prevent XSS
|
|
444
|
+
const safeTextColor = escapeCSSValue(textColor);
|
|
445
|
+
card.setCSS(
|
|
446
|
+
`
|
|
447
|
+
${cssStyles}
|
|
448
|
+
@keyframes slideInAnimation {
|
|
449
|
+
from {
|
|
450
|
+
width: 0;
|
|
451
|
+
}
|
|
452
|
+
to {
|
|
453
|
+
width: calc(100%-100px);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
@keyframes growWidthAnimation {
|
|
457
|
+
from {
|
|
458
|
+
width: 0;
|
|
459
|
+
}
|
|
460
|
+
to {
|
|
461
|
+
width: 100%;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
.lang-name { font: 400 11px 'Segoe UI', Ubuntu, Sans-Serif; fill: ${safeTextColor} }
|
|
465
|
+
#rect-mask rect{
|
|
466
|
+
animation: slideInAnimation 1s ease-in-out forwards;
|
|
467
|
+
}
|
|
468
|
+
.lang-progress{
|
|
469
|
+
animation: growWidthAnimation 0.6s ease-in-out forwards;
|
|
470
|
+
}
|
|
471
|
+
`,
|
|
472
|
+
);
|
|
473
|
+
|
|
474
|
+
return card.render(`
|
|
475
|
+
<svg x="0" y="0" width="100%">
|
|
476
|
+
${finalLayout}
|
|
477
|
+
</svg>
|
|
478
|
+
`);
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
export { renderWakatimeCard };
|
|
482
|
+
export default renderWakatimeCard;
|