@stats-organization/github-readme-stats-core 2.1.5 → 2.2.0

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.
Files changed (169) hide show
  1. package/build/api/gist.d.ts +2 -7
  2. package/build/api/gist.d.ts.map +1 -1
  3. package/build/api/gist.js +9 -41
  4. package/build/api/index.d.ts +3 -8
  5. package/build/api/index.d.ts.map +1 -1
  6. package/build/api/index.js +23 -45
  7. package/build/api/pin.d.ts +2 -7
  8. package/build/api/pin.d.ts.map +1 -1
  9. package/build/api/pin.js +9 -41
  10. package/build/api/top-langs.d.ts +2 -7
  11. package/build/api/top-langs.d.ts.map +1 -1
  12. package/build/api/top-langs.js +11 -55
  13. package/build/api/wakatime.d.ts +2 -7
  14. package/build/api/wakatime.d.ts.map +1 -1
  15. package/build/api/wakatime.js +9 -41
  16. package/build/cards/common-options.d.ts +13 -0
  17. package/build/cards/common-options.d.ts.map +1 -0
  18. package/build/cards/common-options.js +1 -0
  19. package/build/cards/gist.d.ts +11 -16
  20. package/build/cards/gist.d.ts.map +1 -1
  21. package/build/cards/gist.js +26 -31
  22. package/build/cards/repo.d.ts +19 -16
  23. package/build/cards/repo.d.ts.map +1 -1
  24. package/build/cards/repo.js +58 -60
  25. package/build/cards/stats.d.ts +29 -46
  26. package/build/cards/stats.d.ts.map +1 -1
  27. package/build/cards/stats.js +113 -181
  28. package/build/cards/top-languages.d.ts +81 -77
  29. package/build/cards/top-languages.d.ts.map +1 -1
  30. package/build/cards/top-languages.js +174 -200
  31. package/build/cards/wakatime.d.ts +21 -11
  32. package/build/cards/wakatime.d.ts.map +1 -1
  33. package/build/cards/wakatime.js +73 -111
  34. package/build/common/Card.d.ts +34 -13
  35. package/build/common/Card.d.ts.map +1 -1
  36. package/build/common/Card.js +73 -40
  37. package/build/common/I18n.d.ts +4 -2
  38. package/build/common/I18n.d.ts.map +1 -1
  39. package/build/common/color.d.ts +60 -18
  40. package/build/common/color.d.ts.map +1 -1
  41. package/build/common/color.js +102 -5
  42. package/build/common/config.d.ts +2 -1
  43. package/build/common/config.d.ts.map +1 -1
  44. package/build/common/config.js +16 -26
  45. package/build/common/constants.d.ts +2 -1
  46. package/build/common/constants.d.ts.map +1 -1
  47. package/build/common/date.d.ts +37 -0
  48. package/build/common/date.d.ts.map +1 -0
  49. package/build/common/date.js +31 -0
  50. package/build/common/http.d.ts +25 -2
  51. package/build/common/http.d.ts.map +1 -1
  52. package/build/common/http.js +26 -3
  53. package/build/common/icons.d.ts +1 -0
  54. package/build/common/icons.d.ts.map +1 -1
  55. package/build/common/icons.js +1 -0
  56. package/build/common/languageColors.d.ts +11 -0
  57. package/build/common/languageColors.d.ts.map +1 -0
  58. package/build/common/languageColors.js +15 -0
  59. package/build/common/languageColors.json +1 -0
  60. package/build/common/ops.d.ts +3 -10
  61. package/build/common/ops.d.ts.map +1 -1
  62. package/build/common/ops.js +3 -15
  63. package/build/common/render.d.ts +43 -6
  64. package/build/common/render.d.ts.map +1 -1
  65. package/build/common/render.js +77 -11
  66. package/build/common/retryer.d.ts +3 -2
  67. package/build/common/retryer.d.ts.map +1 -1
  68. package/build/common/retryer.js +1 -0
  69. package/build/fetchers/gist.d.ts.map +1 -1
  70. package/build/fetchers/gist.js +8 -39
  71. package/build/fetchers/repo.d.ts +12 -11
  72. package/build/fetchers/repo.d.ts.map +1 -1
  73. package/build/fetchers/repo.js +26 -67
  74. package/build/fetchers/stats.d.ts +24 -17
  75. package/build/fetchers/stats.d.ts.map +1 -1
  76. package/build/fetchers/stats.js +302 -183
  77. package/build/fetchers/top-languages.d.ts +10 -15
  78. package/build/fetchers/top-languages.d.ts.map +1 -1
  79. package/build/fetchers/top-languages.js +52 -103
  80. package/build/fetchers/types.d.ts +24 -20
  81. package/build/fetchers/types.d.ts.map +1 -1
  82. package/build/fetchers/wakatime.d.ts +9 -5
  83. package/build/fetchers/wakatime.d.ts.map +1 -1
  84. package/build/fetchers/wakatime.js +8 -4
  85. package/build/graphql/contributionsDocument.d.ts +18 -0
  86. package/build/graphql/contributionsDocument.d.ts.map +1 -0
  87. package/build/graphql/contributionsDocument.js +30 -0
  88. package/build/graphql/generated/common.d.ts +42 -0
  89. package/build/graphql/generated/common.d.ts.map +1 -0
  90. package/build/graphql/generated/common.js +2 -0
  91. package/build/graphql/generated/gist.d.ts +44 -0
  92. package/build/graphql/generated/gist.d.ts.map +1 -0
  93. package/build/graphql/generated/gist.js +27 -0
  94. package/build/graphql/generated/repo.d.ts +67 -0
  95. package/build/graphql/generated/repo.d.ts.map +1 -0
  96. package/build/graphql/generated/repo.js +30 -0
  97. package/build/graphql/generated/stats.d.ts +150 -0
  98. package/build/graphql/generated/stats.d.ts.map +1 -0
  99. package/build/graphql/generated/stats.js +95 -0
  100. package/build/graphql/generated/top-languages.d.ts +54 -0
  101. package/build/graphql/generated/top-languages.d.ts.map +1 -0
  102. package/build/graphql/generated/top-languages.js +27 -0
  103. package/build/graphql/graphqlDocument.d.ts +22 -0
  104. package/build/graphql/graphqlDocument.d.ts.map +1 -0
  105. package/build/graphql/graphqlDocument.js +8 -0
  106. package/build/graphql/reposContributedToDocument.d.ts +27 -0
  107. package/build/graphql/reposContributedToDocument.d.ts.map +1 -0
  108. package/build/graphql/reposContributedToDocument.js +59 -0
  109. package/build/index.d.ts +3 -1
  110. package/build/index.d.ts.map +1 -1
  111. package/build/index.js +2 -1
  112. package/build/themes/index.d.ts +32 -0
  113. package/build/themes/index.d.ts.map +1 -1
  114. package/build/themes/index.js +28 -0
  115. package/build/translations.d.ts +17 -0
  116. package/build/translations.d.ts.map +1 -1
  117. package/build/translations.js +17 -0
  118. package/package.json +13 -7
  119. package/src/_github-username-regex.d.ts +4 -0
  120. package/src/api/gist.js +10 -46
  121. package/src/api/index.js +30 -51
  122. package/src/api/pin.js +10 -46
  123. package/src/api/top-langs.js +13 -60
  124. package/src/api/wakatime.js +10 -46
  125. package/src/cards/common-options.ts +13 -0
  126. package/src/cards/{gist.js → gist.ts} +37 -37
  127. package/src/cards/{repo.js → repo.ts} +86 -68
  128. package/src/cards/{stats.js → stats.ts} +168 -219
  129. package/src/cards/{top-languages.js → top-languages.ts} +309 -265
  130. package/src/cards/{wakatime.js → wakatime.ts} +136 -130
  131. package/src/common/Card.ts +94 -49
  132. package/src/common/I18n.ts +6 -2
  133. package/src/common/color.ts +163 -14
  134. package/src/common/config.ts +22 -29
  135. package/src/common/constants.ts +3 -1
  136. package/src/common/date.ts +45 -0
  137. package/src/common/http.ts +43 -3
  138. package/src/common/icons.ts +1 -0
  139. package/src/common/languageColors.json +1 -0
  140. package/src/common/languageColors.ts +19 -0
  141. package/src/common/ops.ts +8 -18
  142. package/src/common/render.ts +114 -14
  143. package/src/common/retryer.ts +6 -5
  144. package/src/fetchers/gist.ts +15 -72
  145. package/src/fetchers/repo.ts +114 -0
  146. package/src/fetchers/stats.ts +743 -0
  147. package/src/fetchers/top-languages.ts +135 -0
  148. package/src/fetchers/types.ts +28 -20
  149. package/src/fetchers/{wakatime.js → wakatime.ts} +19 -5
  150. package/src/graphql/contributionsDocument.ts +44 -0
  151. package/src/graphql/generated/common.ts +26 -0
  152. package/src/graphql/generated/gist.ts +59 -0
  153. package/src/graphql/generated/repo.ts +90 -0
  154. package/src/graphql/generated/stats.ts +194 -0
  155. package/src/graphql/generated/top-languages.ts +75 -0
  156. package/src/graphql/graphqlDocument.ts +25 -0
  157. package/src/graphql/queries/gist.graphql +25 -0
  158. package/src/graphql/queries/repo.graphql +28 -0
  159. package/src/graphql/queries/stats.graphql +120 -0
  160. package/src/graphql/queries/top-languages.graphql +34 -0
  161. package/src/graphql/reposContributedToDocument.ts +86 -0
  162. package/src/index.ts +3 -1
  163. package/src/themes/index.ts +33 -0
  164. package/src/translations.ts +17 -0
  165. package/src/cards/types.d.ts +0 -78
  166. package/src/fetchers/repo.js +0 -161
  167. package/src/fetchers/stats.js +0 -473
  168. package/src/fetchers/top-languages.js +0 -175
  169. package/src/themes/README.md +0 -229
@@ -1,23 +1,22 @@
1
1
  import { Card } from "../common/Card.js";
2
2
  import { I18n } from "../common/I18n.js";
3
- import {
4
- fallbackColor,
5
- getCardColors,
6
- isPrefixedHexColor,
7
- } from "../common/color.js";
3
+ import { getLightDarkColors, isPrefixedHexColor } from "../common/color.js";
8
4
  import { formatBytes } from "../common/fmt.js";
9
5
  import { encodeHTML } from "../common/html.js";
6
+ import { DEFAULT_LANG_COLOR } from "../common/languageColors.js";
10
7
  import { chunkArray, clampValue, lowercaseTrim } from "../common/ops.js";
11
8
  import {
12
9
  createProgressNode,
13
10
  flexLayout,
14
11
  measureText,
15
12
  } from "../common/render.js";
13
+ import type { Lang, TopLangData } from "../fetchers/types.js";
16
14
  import { langCardLocales } from "../translations.js";
17
15
 
16
+ import type { CommonOptions } from "./common-options.js";
17
+
18
18
  const DEFAULT_CARD_WIDTH = 300;
19
19
  const MIN_CARD_WIDTH = 280;
20
- const DEFAULT_LANG_COLOR = "#858585";
21
20
  const CARD_PADDING = 25;
22
21
  const COMPACT_LAYOUT_BASE_HEIGHT = 90;
23
22
  const MAXIMUM_LANGS_COUNT = 20;
@@ -28,18 +27,32 @@ const DONUT_LAYOUT_DEFAULT_LANGS_COUNT = 5;
28
27
  const PIE_LAYOUT_DEFAULT_LANGS_COUNT = 6;
29
28
  const DONUT_VERTICAL_LAYOUT_DEFAULT_LANGS_COUNT = 6;
30
29
 
31
- /**
32
- * @typedef {import("../fetchers/types").Lang} Lang
33
- */
30
+ type TopLangLayout = "compact" | "normal" | "donut" | "donut-vertical" | "pie";
31
+
32
+ interface TopLangOptions extends CommonOptions {
33
+ hide_title: boolean;
34
+ card_width: number;
35
+ hide: Array<string>;
36
+ layout: TopLangLayout;
37
+ custom_title: string;
38
+ langs_count: number;
39
+ disable_animations: boolean;
40
+ hide_progress: boolean;
41
+ hide_values: boolean;
42
+ prog_bar_bg_color: string;
43
+ stats_format: "percentages" | "bytes";
44
+ }
34
45
 
35
46
  /**
36
47
  * Retrieves the programming language whose name is the longest.
37
48
  *
38
- * @param {Lang[]} arr Array of programming languages.
39
- * @returns {{ name: string, size: number, color: string }} Longest programming language object.
49
+ * @param arr Array of programming languages.
50
+ * @returns Longest programming language object.
40
51
  */
41
- const getLongestLang = (arr) =>
42
- arr.reduce(
52
+ const getLongestLang = (
53
+ arr: Array<Lang>,
54
+ ): Pick<Lang, "name" | "size" | "color"> =>
55
+ arr.reduce<Pick<Lang, "name" | "size" | "color">>(
43
56
  (savedLang, lang) =>
44
57
  lang.name.length > savedLang.name.length ? lang : savedLang,
45
58
  { name: "", size: 0, color: "" },
@@ -48,29 +61,36 @@ const getLongestLang = (arr) =>
48
61
  /**
49
62
  * Convert degrees to radians.
50
63
  *
51
- * @param {number} angleInDegrees Angle in degrees.
52
- * @returns {number} Angle in radians.
64
+ * @param angleInDegrees Angle in degrees.
65
+ * @returns Angle in radians.
53
66
  */
54
- const degreesToRadians = (angleInDegrees) => angleInDegrees * (Math.PI / 180.0);
67
+ const degreesToRadians = (angleInDegrees: number): number =>
68
+ angleInDegrees * (Math.PI / 180.0);
55
69
 
56
70
  /**
57
71
  * Convert radians to degrees.
58
72
  *
59
- * @param {number} angleInRadians Angle in radians.
60
- * @returns {number} Angle in degrees.
73
+ * @param angleInRadians Angle in radians.
74
+ * @returns Angle in degrees.
61
75
  */
62
- const radiansToDegrees = (angleInRadians) => angleInRadians / (Math.PI / 180.0);
76
+ const radiansToDegrees = (angleInRadians: number): number =>
77
+ angleInRadians / (Math.PI / 180.0);
63
78
 
64
79
  /**
65
80
  * Convert polar coordinates to cartesian coordinates.
66
81
  *
67
- * @param {number} centerX Center x coordinate.
68
- * @param {number} centerY Center y coordinate.
69
- * @param {number} radius Radius of the circle.
70
- * @param {number} angleInDegrees Angle in degrees.
71
- * @returns {{x: number, y: number}} Cartesian coordinates.
82
+ * @param centerX Center x coordinate.
83
+ * @param centerY Center y coordinate.
84
+ * @param radius Radius of the circle.
85
+ * @param angleInDegrees Angle in degrees.
86
+ * @returns Cartesian coordinates.
72
87
  */
73
- const polarToCartesian = (centerX, centerY, radius, angleInDegrees) => {
88
+ const polarToCartesian = (
89
+ centerX: number,
90
+ centerY: number,
91
+ radius: number,
92
+ angleInDegrees: number,
93
+ ): { x: number; y: number } => {
74
94
  const rads = degreesToRadians(angleInDegrees);
75
95
  return {
76
96
  x: centerX + radius * Math.cos(rads),
@@ -81,13 +101,18 @@ const polarToCartesian = (centerX, centerY, radius, angleInDegrees) => {
81
101
  /**
82
102
  * Convert cartesian coordinates to polar coordinates.
83
103
  *
84
- * @param {number} centerX Center x coordinate.
85
- * @param {number} centerY Center y coordinate.
86
- * @param {number} x Point x coordinate.
87
- * @param {number} y Point y coordinate.
88
- * @returns {{radius: number, angleInDegrees: number}} Polar coordinates.
104
+ * @param centerX Center x coordinate.
105
+ * @param centerY Center y coordinate.
106
+ * @param x Point x coordinate.
107
+ * @param y Point y coordinate.
108
+ * @returns Polar coordinates.
89
109
  */
90
- const cartesianToPolar = (centerX, centerY, x, y) => {
110
+ const cartesianToPolar = (
111
+ centerX: number,
112
+ centerY: number,
113
+ x: number,
114
+ y: number,
115
+ ): { radius: number; angleInDegrees: number } => {
91
116
  const radius = Math.sqrt(Math.pow(x - centerX, 2) + Math.pow(y - centerY, 2));
92
117
  let angleInDegrees = radiansToDegrees(Math.atan2(y - centerY, x - centerX));
93
118
  if (angleInDegrees < 0) {
@@ -99,90 +124,93 @@ const cartesianToPolar = (centerX, centerY, x, y) => {
99
124
  /**
100
125
  * Calculates length of circle.
101
126
  *
102
- * @param {number} radius Radius of the circle.
103
- * @returns {number} The length of the circle.
127
+ * @param radius Radius of the circle.
128
+ * @returns The length of the circle.
104
129
  */
105
- const getCircleLength = (radius) => {
130
+ const getCircleLength = (radius: number): number => {
106
131
  return 2 * Math.PI * radius;
107
132
  };
108
133
 
109
134
  /**
110
135
  * Calculates height for the compact layout.
111
136
  *
112
- * @param {number} totalLangs Total number of languages.
113
- * @returns {number} Card height.
137
+ * @param totalLangs Total number of languages.
138
+ * @returns Card height.
114
139
  */
115
- const calculateCompactLayoutHeight = (totalLangs) => {
140
+ const calculateCompactLayoutHeight = (totalLangs: number): number => {
116
141
  return COMPACT_LAYOUT_BASE_HEIGHT + Math.round(totalLangs / 2) * 25;
117
142
  };
118
143
 
119
144
  /**
120
145
  * Calculates height for the normal layout.
121
146
  *
122
- * @param {number} totalLangs Total number of languages.
123
- * @returns {number} Card height.
147
+ * @param totalLangs Total number of languages.
148
+ * @returns Card height.
124
149
  */
125
- const calculateNormalLayoutHeight = (totalLangs) => {
150
+ const calculateNormalLayoutHeight = (totalLangs: number): number => {
126
151
  return 45 + (totalLangs + 1) * 40;
127
152
  };
128
153
 
129
154
  /**
130
155
  * Calculates height for the donut layout.
131
156
  *
132
- * @param {number} totalLangs Total number of languages.
133
- * @returns {number} Card height.
157
+ * @param totalLangs Total number of languages.
158
+ * @returns Card height.
134
159
  */
135
- const calculateDonutLayoutHeight = (totalLangs) => {
160
+ const calculateDonutLayoutHeight = (totalLangs: number): number => {
136
161
  return 215 + Math.max(totalLangs - 5, 0) * 32;
137
162
  };
138
163
 
139
164
  /**
140
165
  * Calculates height for the donut vertical layout.
141
166
  *
142
- * @param {number} totalLangs Total number of languages.
143
- * @returns {number} Card height.
167
+ * @param totalLangs Total number of languages.
168
+ * @returns Card height.
144
169
  */
145
- const calculateDonutVerticalLayoutHeight = (totalLangs) => {
170
+ const calculateDonutVerticalLayoutHeight = (totalLangs: number): number => {
146
171
  return 300 + Math.round(totalLangs / 2) * 25;
147
172
  };
148
173
 
149
174
  /**
150
175
  * Calculates height for the pie layout.
151
176
  *
152
- * @param {number} totalLangs Total number of languages.
153
- * @returns {number} Card height.
177
+ * @param totalLangs Total number of languages.
178
+ * @returns Card height.
154
179
  */
155
- const calculatePieLayoutHeight = (totalLangs) => {
180
+ const calculatePieLayoutHeight = (totalLangs: number): number => {
156
181
  return 300 + Math.round(totalLangs / 2) * 25;
157
182
  };
158
183
 
159
184
  /**
160
185
  * Calculates the center translation needed to keep the donut chart centred.
161
- * @param {number} totalLangs Total number of languages.
162
- * @returns {number} Donut center translation.
186
+ *
187
+ * @param totalLangs Total number of languages.
188
+ * @returns Donut center translation.
163
189
  */
164
- const donutCenterTranslation = (totalLangs) => {
190
+ const donutCenterTranslation = (totalLangs: number): number => {
165
191
  return -45 + Math.max(totalLangs - 5, 0) * 16;
166
192
  };
167
193
 
168
194
  /**
169
195
  * Trim top languages to lang_count while also hiding certain languages.
170
196
  *
171
- * @param {Record<string, Lang>} topLangs Top languages.
172
- * @param {number} langs_count Number of languages to show.
173
- * @param {string[]=} hide Languages to hide.
174
- * @returns {{ langs: Lang[], totalLanguageSize: number }} Trimmed top languages and total size.
197
+ * @param topLangs Top languages.
198
+ * @param langs_count Number of languages to show.
199
+ * @param hide Languages to hide.
200
+ * @returns Trimmed top languages and total size.
175
201
  */
176
- const trimTopLanguages = (topLangs, langs_count, hide) => {
202
+ const trimTopLanguages = (
203
+ topLangs: TopLangData,
204
+ langs_count: number,
205
+ hide?: Array<string>,
206
+ ): { langs: Array<Lang>; totalLanguageSize: number } => {
177
207
  let langs = Object.values(topLangs);
178
- let langsToHide = {};
179
- let langsCount = clampValue(langs_count, 1, MAXIMUM_LANGS_COUNT);
208
+ const langsToHide: Record<string, boolean> = {};
209
+ const langsCount = clampValue(langs_count, 1, MAXIMUM_LANGS_COUNT);
180
210
 
181
- // populate langsToHide map for quick lookup
182
- // while filtering out
211
+ // populate langsToHide map for quick lookup while filtering out
183
212
  if (hide) {
184
213
  hide.forEach((langName) => {
185
- // @ts-ignore
186
214
  langsToHide[lowercaseTrim(langName)] = true;
187
215
  });
188
216
  }
@@ -190,10 +218,7 @@ const trimTopLanguages = (topLangs, langs_count, hide) => {
190
218
  // filter out languages to be hidden
191
219
  langs = langs
192
220
  .sort((a, b) => b.size - a.size)
193
- .filter((lang) => {
194
- // @ts-ignore
195
- return !langsToHide[lowercaseTrim(lang.name)];
196
- })
221
+ .filter((lang) => !langsToHide[lowercaseTrim(lang.name)])
197
222
  .slice(0, langsCount);
198
223
 
199
224
  const totalLanguageSize = langs.reduce((acc, curr) => acc + curr.size, 0);
@@ -204,41 +229,67 @@ const trimTopLanguages = (topLangs, langs_count, hide) => {
204
229
  /**
205
230
  * Get display value corresponding to the format.
206
231
  *
207
- * @param {number} size Bytes size.
208
- * @param {number} percentages Percentage value.
209
- * @param {string} format Format of the stats.
210
- * @returns {string} Display value.
232
+ * @param size Bytes size.
233
+ * @param percentages Percentage value.
234
+ * @param format Format of the stats.
235
+ * @returns Display value.
211
236
  */
212
- const getDisplayValue = (size, percentages, format) => {
237
+ const getDisplayValue = (
238
+ size: number,
239
+ percentages: number,
240
+ format: string,
241
+ ): string => {
213
242
  return format === "bytes" ? formatBytes(size) : `${percentages.toFixed(2)}%`;
214
243
  };
215
244
 
245
+ /**
246
+ * Resolves a language's display color, falling back to the default, and
247
+ * validates it is a prefixed hex color.
248
+ *
249
+ * @param lang Programming language object.
250
+ * @returns Validated language color.
251
+ */
252
+ const resolveLangColor = (lang: Lang): string => {
253
+ const color = lang.color || DEFAULT_LANG_COLOR;
254
+ if (!isPrefixedHexColor(color)) {
255
+ throw new Error(`Invalid language color: "${color}"`);
256
+ }
257
+ return color;
258
+ };
259
+
216
260
  /**
217
261
  * Create progress bar text item for a programming language.
218
262
  *
219
- * @param {object} props Function properties.
220
- * @param {number} props.width The card width
221
- * @param {string} props.color Color of the programming language.
222
- * @param {string} props.progBarBgColor Color of the background of progress bar.
223
- * @param {string} props.name Name of the programming language.
224
- * @param {number} props.size Size of the programming language.
225
- * @param {number} props.totalSize Total size of all languages.
226
- * @param {string} props.statsFormat Stats format.
227
- * @param {boolean=} props.hideValues Whether to hide stats values.
228
- * @param {number} props.index Index of the programming language.
229
- * @returns {string} Programming language SVG node.
263
+ * @param props Function properties.
264
+ * @param props.width The card width
265
+ * @param props.color Color of the programming language.
266
+ * @param props.name Name of the programming language.
267
+ * @param props.size Size of the programming language.
268
+ * @param props.totalSize Total size of all languages.
269
+ * @param props.statsFormat Stats format.
270
+ * @param props.hideValues Whether to hide stats values.
271
+ * @param props.index Index of the programming language.
272
+ * @returns Programming language SVG node.
230
273
  */
231
274
  const createProgressTextNode = ({
232
275
  width,
233
276
  color,
234
- progBarBgColor,
235
277
  name,
236
278
  size,
237
279
  totalSize,
238
280
  statsFormat,
239
281
  hideValues,
240
282
  index,
241
- }) => {
283
+ }: {
284
+ width: number;
285
+ color: string;
286
+ name: string;
287
+ size: number;
288
+ totalSize: number;
289
+ statsFormat: string;
290
+ hideValues?: boolean | undefined;
291
+ index: number;
292
+ }): string => {
242
293
  const staggerDelay = (index + 3) * 150;
243
294
  const paddingRight = hideValues ? CARD_PADDING * 2 : 95;
244
295
  const progressTextX = width - paddingRight + 10;
@@ -257,7 +308,6 @@ const createProgressTextNode = ({
257
308
  color,
258
309
  width: progressWidth,
259
310
  progress,
260
- progressBarBackgroundColor: progBarBgColor,
261
311
  delay: staggerDelay + 300,
262
312
  })}
263
313
  </g>
@@ -267,14 +317,14 @@ const createProgressTextNode = ({
267
317
  /**
268
318
  * Creates compact text item for a programming language.
269
319
  *
270
- * @param {object} props Function properties.
271
- * @param {Lang} props.lang Programming language object.
272
- * @param {number} props.totalSize Total size of all languages.
273
- * @param {boolean=} props.hideProgress Whether to hide percentage.
274
- * @param {boolean=} props.hideValues Whether to hide stats values (percentages/bytes).
275
- * @param {string=} props.statsFormat Stats format
276
- * @param {number} props.index Index of the programming language.
277
- * @returns {string} Compact layout programming language SVG node.
320
+ * @param props Function properties.
321
+ * @param props.lang Programming language object.
322
+ * @param props.totalSize Total size of all languages.
323
+ * @param props.hideProgress Whether to hide percentage.
324
+ * @param props.hideValues Whether to hide stats values (percentages/bytes).
325
+ * @param props.statsFormat Stats format
326
+ * @param props.index Index of the programming language.
327
+ * @returns Compact layout programming language SVG node.
278
328
  */
279
329
  const createCompactLangNode = ({
280
330
  lang,
@@ -283,16 +333,19 @@ const createCompactLangNode = ({
283
333
  hideValues,
284
334
  statsFormat = "percentages",
285
335
  index,
286
- }) => {
336
+ }: {
337
+ lang: Lang;
338
+ totalSize: number;
339
+ hideProgress?: boolean | undefined;
340
+ hideValues?: boolean | undefined;
341
+ statsFormat?: string | undefined;
342
+ index: number;
343
+ }): string => {
287
344
  const percentages = (lang.size / totalSize) * 100;
288
345
  const displayValue = getDisplayValue(lang.size, percentages, statsFormat);
289
346
 
290
347
  const staggerDelay = (index + 3) * 150;
291
- const color = lang.color || "#858585";
292
-
293
- if (!isPrefixedHexColor(color)) {
294
- throw new Error(`Invalid language color: "${color}"`);
295
- }
348
+ const color = resolveLangColor(lang);
296
349
 
297
350
  return `
298
351
  <g class="stagger" style="animation-delay: ${staggerDelay}ms">
@@ -307,13 +360,13 @@ const createCompactLangNode = ({
307
360
  /**
308
361
  * Create compact languages text items for all programming languages.
309
362
  *
310
- * @param {object} props Function properties.
311
- * @param {Lang[]} props.langs Array of programming languages.
312
- * @param {number} props.totalSize Total size of all languages.
313
- * @param {boolean=} props.hideProgress Whether to hide percentage.
314
- * @param {boolean=} props.hideValues Whether to hide stats values.
315
- * @param {string=} props.statsFormat Stats format
316
- * @returns {string} Programming languages SVG node.
363
+ * @param props Function properties.
364
+ * @param props.langs Array of programming languages.
365
+ * @param props.totalSize Total size of all languages.
366
+ * @param props.hideProgress Whether to hide percentage.
367
+ * @param props.hideValues Whether to hide stats values.
368
+ * @param props.statsFormat Stats format
369
+ * @returns Programming languages SVG node.
317
370
  */
318
371
  const createLanguageTextNode = ({
319
372
  langs,
@@ -321,11 +374,16 @@ const createLanguageTextNode = ({
321
374
  hideProgress,
322
375
  hideValues,
323
376
  statsFormat,
324
- }) => {
377
+ }: {
378
+ langs: Array<Lang>;
379
+ totalSize: number;
380
+ hideProgress?: boolean | undefined;
381
+ hideValues?: boolean | undefined;
382
+ statsFormat?: string | undefined;
383
+ }): string => {
325
384
  const longestLang = getLongestLang(langs);
326
385
  const chunked = chunkArray(langs, langs.length / 2);
327
386
  const layouts = chunked.map((array) => {
328
- // @ts-ignore
329
387
  const items = array.map((lang, index) =>
330
388
  createCompactLangNode({
331
389
  lang,
@@ -355,19 +413,24 @@ const createLanguageTextNode = ({
355
413
  /**
356
414
  * Create donut languages text items for all programming languages.
357
415
  *
358
- * @param {object} props Function properties.
359
- * @param {Lang[]} props.langs Array of programming languages.
360
- * @param {number} props.totalSize Total size of all languages.
361
- * @param {boolean=} props.hideValues Whether to hide stats values.
362
- * @param {string} props.statsFormat Stats format
363
- * @returns {string} Donut layout programming language SVG node.
416
+ * @param props Function properties.
417
+ * @param props.langs Array of programming languages.
418
+ * @param props.totalSize Total size of all languages.
419
+ * @param props.hideValues Whether to hide stats values.
420
+ * @param props.statsFormat Stats format
421
+ * @returns Donut layout programming language SVG node.
364
422
  */
365
423
  const createDonutLanguagesNode = ({
366
424
  langs,
367
425
  totalSize,
368
426
  hideValues,
369
427
  statsFormat,
370
- }) => {
428
+ }: {
429
+ langs: Array<Lang>;
430
+ totalSize: number;
431
+ hideValues?: boolean | undefined;
432
+ statsFormat?: string | undefined;
433
+ }): string => {
371
434
  return flexLayout({
372
435
  items: langs.map((lang, index) => {
373
436
  return createCompactLangNode({
@@ -387,22 +450,20 @@ const createDonutLanguagesNode = ({
387
450
  /**
388
451
  * Renders the default language card layout.
389
452
  *
390
- * @param {Lang[]} langs Array of programming languages.
391
- * @param {number} width Card width.
392
- * @param {number} totalLanguageSize Total size of all languages.
393
- * @param progBarBgColor Color of the background of progress bar.
394
- * @param {string} statsFormat Stats format.
395
- * @param {boolean=} hideValues Whether to hide stats values.
396
- * @returns {string} Normal layout card SVG object.
453
+ * @param langs Array of programming languages.
454
+ * @param width Card width.
455
+ * @param totalLanguageSize Total size of all languages.
456
+ * @param statsFormat Stats format.
457
+ * @param hideValues Whether to hide stats values.
458
+ * @returns Normal layout card SVG object.
397
459
  */
398
460
  const renderNormalLayout = (
399
- langs,
400
- width,
401
- totalLanguageSize,
402
- progBarBgColor,
403
- statsFormat,
404
- hideValues,
405
- ) => {
461
+ langs: Array<Lang>,
462
+ width: number,
463
+ totalLanguageSize: number,
464
+ statsFormat: string,
465
+ hideValues?: boolean,
466
+ ): string => {
406
467
  return flexLayout({
407
468
  items: langs.map((lang, index) => {
408
469
  return createProgressTextNode({
@@ -414,7 +475,6 @@ const renderNormalLayout = (
414
475
  statsFormat,
415
476
  hideValues,
416
477
  index,
417
- progBarBgColor,
418
478
  });
419
479
  }),
420
480
  gap: 40,
@@ -425,22 +485,22 @@ const renderNormalLayout = (
425
485
  /**
426
486
  * Renders the compact language card layout.
427
487
  *
428
- * @param {Lang[]} langs Array of programming languages.
429
- * @param {number} width Card width.
430
- * @param {number} totalLanguageSize Total size of all languages.
431
- * @param {boolean=} hideProgress Whether to hide progress bar.
432
- * @param {string} statsFormat Stats format.
433
- * @param {boolean=} hideValues Whether to hide stats values.
434
- * @returns {string} Compact layout card SVG object.
488
+ * @param langs Array of programming languages.
489
+ * @param width Card width.
490
+ * @param totalLanguageSize Total size of all languages.
491
+ * @param hideProgress Whether to hide progress bar.
492
+ * @param statsFormat Stats format.
493
+ * @param hideValues Whether to hide stats values.
494
+ * @returns Compact layout card SVG object.
435
495
  */
436
496
  const renderCompactLayout = (
437
- langs,
438
- width,
439
- totalLanguageSize,
440
- hideProgress,
497
+ langs: Array<Lang>,
498
+ width: number,
499
+ totalLanguageSize: number,
500
+ hideProgress?: boolean,
441
501
  statsFormat = "percentages",
442
- hideValues,
443
- ) => {
502
+ hideValues?: boolean,
503
+ ): string => {
444
504
  const paddingRight = 50;
445
505
  const offsetWidth = width - paddingRight;
446
506
  // progressOffset holds the previous language's width and used to offset the next language
@@ -448,10 +508,7 @@ const renderCompactLayout = (
448
508
  let progressOffset = 0;
449
509
  const compactProgressBar = langs
450
510
  .map((lang) => {
451
- const langColor = lang.color || DEFAULT_LANG_COLOR;
452
- if (!isPrefixedHexColor(langColor)) {
453
- throw new Error(`Invalid language color: "${langColor}"`);
454
- }
511
+ const langColor = resolveLangColor(lang);
455
512
 
456
513
  const percentage = parseFloat(
457
514
  ((lang.size / totalLanguageSize) * offsetWidth).toFixed(2),
@@ -501,24 +558,24 @@ const renderCompactLayout = (
501
558
  /**
502
559
  * Renders donut vertical layout to display user's most frequently used programming languages.
503
560
  *
504
- * @param {Lang[]} langs Array of programming languages.
505
- * @param {number} totalLanguageSize Total size of all languages.
506
- * @param {string} statsFormat Stats format.
507
- * @param {boolean=} hideValues Whether to hide stats values.
508
- * @returns {string} Compact layout card SVG object.
561
+ * @param langs Array of programming languages.
562
+ * @param totalLanguageSize Total size of all languages.
563
+ * @param statsFormat Stats format.
564
+ * @param hideValues Whether to hide stats values.
565
+ * @returns Compact layout card SVG object.
509
566
  */
510
567
  const renderDonutVerticalLayout = (
511
- langs,
512
- totalLanguageSize,
513
- statsFormat,
514
- hideValues,
515
- ) => {
568
+ langs: Array<Lang>,
569
+ totalLanguageSize: number,
570
+ statsFormat: string,
571
+ hideValues?: boolean,
572
+ ): string => {
516
573
  // Donut vertical chart radius and total length
517
574
  const radius = 80;
518
575
  const totalCircleLength = getCircleLength(radius);
519
576
 
520
577
  // SVG circles
521
- let circles = [];
578
+ const circles = [];
522
579
 
523
580
  // Start indent for donut vertical chart parts
524
581
  let indent = 0;
@@ -528,10 +585,7 @@ const renderDonutVerticalLayout = (
528
585
 
529
586
  // Generate each donut vertical chart part
530
587
  for (const lang of langs) {
531
- const langColor = lang.color || DEFAULT_LANG_COLOR;
532
- if (!isPrefixedHexColor(langColor)) {
533
- throw new Error(`Invalid language color: "${langColor}"`);
534
- }
588
+ const langColor = resolveLangColor(lang);
535
589
 
536
590
  const percentage = (lang.size / totalLanguageSize) * 100;
537
591
  const circleLength = totalCircleLength * (percentage / 100);
@@ -585,13 +639,18 @@ const renderDonutVerticalLayout = (
585
639
  /**
586
640
  * Renders pie layout to display user's most frequently used programming languages.
587
641
  *
588
- * @param {Lang[]} langs Array of programming languages.
589
- * @param {number} totalLanguageSize Total size of all languages.
590
- * @param {string} statsFormat Stats format.
591
- * @param {boolean=} hideValues Whether to hide stats values.
592
- * @returns {string} Compact layout card SVG object.
642
+ * @param langs Array of programming languages.
643
+ * @param totalLanguageSize Total size of all languages.
644
+ * @param statsFormat Stats format.
645
+ * @param hideValues Whether to hide stats values.
646
+ * @returns Compact layout card SVG object.
593
647
  */
594
- const renderPieLayout = (langs, totalLanguageSize, statsFormat, hideValues) => {
648
+ const renderPieLayout = (
649
+ langs: Array<Lang>,
650
+ totalLanguageSize: number,
651
+ statsFormat: string,
652
+ hideValues?: boolean,
653
+ ): string => {
595
654
  // Pie chart radius and center coordinates
596
655
  const radius = 90;
597
656
  const centerX = 150;
@@ -608,10 +667,7 @@ const renderPieLayout = (langs, totalLanguageSize, statsFormat, hideValues) => {
608
667
 
609
668
  // Generate each pie chart part
610
669
  for (const lang of langs) {
611
- const langColor = lang.color || DEFAULT_LANG_COLOR;
612
- if (!isPrefixedHexColor(langColor)) {
613
- throw new Error(`Invalid language color: "${langColor}"`);
614
- }
670
+ const langColor = resolveLangColor(lang);
615
671
 
616
672
  if (langs.length === 1) {
617
673
  paths.push(`
@@ -689,34 +745,34 @@ const renderPieLayout = (langs, totalLanguageSize, statsFormat, hideValues) => {
689
745
  /**
690
746
  * Creates the SVG paths for the language donut chart.
691
747
  *
692
- * @param {number} cx Donut center x-position.
693
- * @param {number} cy Donut center y-position.
694
- * @param {number} radius Donut arc Radius.
695
- * @param {number[]} percentages Array with donut section percentages.
696
- * @returns {{d: string, percent: number}[]} Array of svg path elements
748
+ * @param cx Donut center x-position.
749
+ * @param cy Donut center y-position.
750
+ * @param radius Donut arc Radius.
751
+ * @param percentages Array with donut section percentages.
752
+ * @returns Array of svg path elements
697
753
  */
698
- const createDonutPaths = (cx, cy, radius, percentages) => {
699
- const paths = [];
754
+ const createDonutPaths = (
755
+ cx: number,
756
+ cy: number,
757
+ radius: number,
758
+ percentages: Array<number>,
759
+ ): Array<{ d: string; percent: number }> => {
760
+ const paths: Array<{ d: string; percent: number }> = [];
700
761
  let startAngle = 0;
701
- let endAngle;
702
762
 
703
763
  const totalPercent = percentages.reduce((acc, curr) => acc + curr, 0);
704
- for (let i = 0; i < percentages.length; i++) {
705
- const tmpPath = {};
764
+ for (const rawPercent of percentages) {
765
+ const percent = parseFloat(((rawPercent / totalPercent) * 100).toFixed(2));
706
766
 
707
- let percent = parseFloat(
708
- ((percentages[i] / totalPercent) * 100).toFixed(2),
709
- );
710
-
711
- endAngle = 3.6 * percent + startAngle;
767
+ const endAngle = 3.6 * percent + startAngle;
712
768
  const startPoint = polarToCartesian(cx, cy, radius, endAngle - 90); // rotate donut 90 degrees counter-clockwise.
713
769
  const endPoint = polarToCartesian(cx, cy, radius, startAngle - 90); // rotate donut 90 degrees counter-clockwise.
714
770
  const largeArc = endAngle - startAngle <= 180 ? 0 : 1;
715
771
 
716
- tmpPath.percent = percent;
717
- tmpPath.d = `M ${startPoint.x} ${startPoint.y} A ${radius} ${radius} 0 ${largeArc} 0 ${endPoint.x} ${endPoint.y}`;
718
-
719
- paths.push(tmpPath);
772
+ paths.push({
773
+ percent,
774
+ d: `M ${startPoint.x} ${startPoint.y} A ${radius} ${radius} 0 ${largeArc} 0 ${endPoint.x} ${endPoint.y}`,
775
+ });
720
776
  startAngle = endAngle;
721
777
  }
722
778
 
@@ -726,20 +782,20 @@ const createDonutPaths = (cx, cy, radius, percentages) => {
726
782
  /**
727
783
  * Renders the donut language card layout.
728
784
  *
729
- * @param {Lang[]} langs Array of programming languages.
730
- * @param {number} width Card width.
731
- * @param {number} totalLanguageSize Total size of all languages.
732
- * @param {string} statsFormat Stats format.
733
- * @param {boolean=} hideValues Whether to hide stats values.
734
- * @returns {string} Donut layout card SVG object.
785
+ * @param langs Array of programming languages.
786
+ * @param width Card width.
787
+ * @param totalLanguageSize Total size of all languages.
788
+ * @param statsFormat Stats format.
789
+ * @param hideValues Whether to hide stats values.
790
+ * @returns Donut layout card SVG object.
735
791
  */
736
792
  const renderDonutLayout = (
737
- langs,
738
- width,
739
- totalLanguageSize,
740
- statsFormat,
741
- hideValues,
742
- ) => {
793
+ langs: Array<Lang>,
794
+ width: number,
795
+ totalLanguageSize: number,
796
+ statsFormat: string,
797
+ hideValues?: boolean,
798
+ ): string => {
743
799
  if (!Number.isFinite(width)) {
744
800
  throw new Error(`Invalid width: "${width}"`);
745
801
  }
@@ -749,13 +805,7 @@ const renderDonutLayout = (
749
805
  const radius = centerX - 60;
750
806
  const strokeWidth = 12;
751
807
 
752
- const colors = langs.map((lang) => {
753
- const langColor = lang.color || DEFAULT_LANG_COLOR;
754
- if (!isPrefixedHexColor(langColor)) {
755
- throw new Error(`Invalid language color: "${langColor}"`);
756
- }
757
- return langColor;
758
- });
808
+ const colors = langs.map(resolveLangColor);
759
809
  const langsPercents = langs.map((lang) =>
760
810
  parseFloat(((lang.size / totalLanguageSize) * 100).toFixed(2)),
761
811
  );
@@ -764,7 +814,7 @@ const renderDonutLayout = (
764
814
 
765
815
  const donutPaths =
766
816
  langs.length === 1
767
- ? `<circle cx="${centerX}" cy="${centerY}" r="${radius}" stroke="${colors[0]}" fill="none" stroke-width="${strokeWidth}" data-testid="lang-donut" size="100"/>`
817
+ ? `<circle cx="${centerX}" cy="${centerY}" r="${radius}" stroke="${colors[0] ?? DEFAULT_LANG_COLOR}" fill="none" stroke-width="${strokeWidth}" data-testid="lang-donut" size="100"/>`
768
818
  : langPaths
769
819
  .map((section, index) => {
770
820
  const staggerDelay = (index + 3) * 100;
@@ -776,7 +826,7 @@ const renderDonutLayout = (
776
826
  data-testid="lang-donut"
777
827
  size="${section.percent}"
778
828
  d="${section.d}"
779
- stroke="${colors[index]}"
829
+ stroke="${colors[index] ?? DEFAULT_LANG_COLOR}"
780
830
  fill="none"
781
831
  stroke-width="${strokeWidth}">
782
832
  </path>
@@ -802,41 +852,43 @@ const renderDonutLayout = (
802
852
  `;
803
853
  };
804
854
 
805
- /**
806
- * @typedef {import("./types").TopLangOptions} TopLangOptions
807
- * @typedef {TopLangOptions["layout"]} Layout
808
- */
809
-
810
855
  /**
811
856
  * Creates the no languages data SVG node.
812
857
  *
813
- * @param {object} props Object with function properties.
814
- * @param {string} props.color No languages data text color.
815
- * @param {string} props.text No languages data translated text.
816
- * @param {Layout | undefined} props.layout Card layout.
817
- * @returns {string} No languages data SVG node string.
858
+ * @param props Object with function properties.
859
+ * @param props.text No languages data translated text.
860
+ * @param props.layout Card layout.
861
+ * @returns No languages data SVG node string.
818
862
  */
819
- const noLanguagesDataNode = ({ color, text, layout }) => {
820
- if (!isPrefixedHexColor(color)) {
821
- throw new Error(`Invalid text color: "${color}"`);
822
- }
823
-
863
+ const noLanguagesDataNode = ({
864
+ text,
865
+ layout,
866
+ }: {
867
+ text: string;
868
+ layout: TopLangLayout | undefined;
869
+ }): string => {
824
870
  return `
825
871
  <text x="${
826
872
  layout === "pie" || layout === "donut-vertical" ? CARD_PADDING : 0
827
- }" y="11" class="stat bold" fill="${color}">${encodeHTML(text)}</text>
873
+ }" y="11" class="stat bold">${encodeHTML(text)}</text>
828
874
  `;
829
875
  };
830
876
 
831
877
  /**
832
878
  * Get default languages count for provided card layout.
833
879
  *
834
- * @param {object} props Function properties.
835
- * @param {Layout=} props.layout Input layout string.
836
- * @param {boolean=} props.hide_progress Input hide_progress parameter value.
837
- * @returns {number} Default languages count for input layout.
880
+ * @param props Function properties.
881
+ * @param props.layout Input layout string.
882
+ * @param props.hide_progress Input hide_progress parameter value.
883
+ * @returns Default languages count for input layout.
838
884
  */
839
- const getDefaultLanguagesCountByLayout = ({ layout, hide_progress }) => {
885
+ const getDefaultLanguagesCountByLayout = ({
886
+ layout,
887
+ hide_progress,
888
+ }: {
889
+ layout?: TopLangLayout | undefined;
890
+ hide_progress?: boolean | undefined;
891
+ }): number => {
840
892
  if (layout === "compact" || hide_progress === true) {
841
893
  return COMPACT_LAYOUT_DEFAULT_LANGS_COUNT;
842
894
  } else if (layout === "donut") {
@@ -850,36 +902,29 @@ const getDefaultLanguagesCountByLayout = ({ layout, hide_progress }) => {
850
902
  }
851
903
  };
852
904
 
853
- /**
854
- * @typedef {import('../fetchers/types').TopLangData} TopLangData
855
- */
856
-
857
905
  /**
858
906
  * Renders card that display user's most frequently used programming languages.
859
907
  *
860
- * @param {TopLangData} topLangs User's most frequently used programming languages.
861
- * @param {Partial<TopLangOptions>} options Card options.
862
- * @returns {string} Language card SVG object.
908
+ * @param topLangs User's most frequently used programming languages.
909
+ * @param options Card options.
910
+ * @returns Language card SVG object.
863
911
  */
864
- const renderTopLanguages = (topLangs, options = {}) => {
912
+ const renderTopLanguages = (
913
+ topLangs: TopLangData,
914
+ options: Partial<TopLangOptions> = {},
915
+ ): string => {
865
916
  const {
866
917
  hide_title = false,
867
918
  hide_border = false,
868
919
  card_width,
869
- title_color,
870
- text_color,
871
- bg_color,
872
- prog_bar_bg_color,
873
920
  hide,
874
921
  hide_progress,
875
922
  hide_values,
876
- theme,
877
923
  layout,
878
924
  custom_title,
879
925
  locale,
880
926
  langs_count = getDefaultLanguagesCountByLayout({ layout, hide_progress }),
881
927
  border_radius,
882
- border_color,
883
928
  disable_animations,
884
929
  stats_format = "percentages",
885
930
  } = options;
@@ -904,20 +949,12 @@ const renderTopLanguages = (topLangs, options = {}) => {
904
949
  : DEFAULT_CARD_WIDTH;
905
950
  let height = calculateNormalLayoutHeight(langs.length);
906
951
 
907
- // returns theme based colors with proper overrides and defaults
908
- const colors = getCardColors({
909
- title_color,
910
- text_color,
911
- bg_color,
912
- border_color,
913
- theme,
914
- });
952
+ const { lightColors, darkColors } = getLightDarkColors(options);
915
953
 
916
- let finalLayout;
954
+ let finalLayout: string;
917
955
  if (langs.length === 0) {
918
956
  height = COMPACT_LAYOUT_BASE_HEIGHT;
919
957
  finalLayout = noLanguagesDataNode({
920
- color: colors.textColor,
921
958
  text: i18n.t("langcard.nodata"),
922
959
  layout,
923
960
  });
@@ -937,7 +974,7 @@ const renderTopLanguages = (topLangs, options = {}) => {
937
974
  stats_format,
938
975
  hide_values,
939
976
  );
940
- } else if (layout === "compact" || hide_progress == true) {
977
+ } else if (layout === "compact" || hide_progress === true) {
941
978
  height =
942
979
  calculateCompactLayoutHeight(langs.length) + (hide_progress ? -25 : 0);
943
980
 
@@ -964,7 +1001,6 @@ const renderTopLanguages = (topLangs, options = {}) => {
964
1001
  langs,
965
1002
  width,
966
1003
  totalLanguageSize,
967
- fallbackColor(prog_bar_bg_color, "#ddd"),
968
1004
  stats_format,
969
1005
  hide_values,
970
1006
  );
@@ -976,7 +1012,7 @@ const renderTopLanguages = (topLangs, options = {}) => {
976
1012
  width,
977
1013
  height,
978
1014
  border_radius,
979
- colors,
1015
+ colors: { light: lightColors, dark: darkColors },
980
1016
  });
981
1017
 
982
1018
  if (disable_animations) {
@@ -985,8 +1021,8 @@ const renderTopLanguages = (topLangs, options = {}) => {
985
1021
 
986
1022
  card.setHideBorder(hide_border);
987
1023
  card.setHideTitle(hide_title);
988
- card.setCSS(
989
- `
1024
+ card.setCSS({
1025
+ light: `
990
1026
  @keyframes slideInAnimation {
991
1027
  from {
992
1028
  width: 0;
@@ -1004,7 +1040,7 @@ const renderTopLanguages = (topLangs, options = {}) => {
1004
1040
  }
1005
1041
  }
1006
1042
  .stat {
1007
- font: 600 14px 'Segoe UI', Ubuntu, "Helvetica Neue", Sans-Serif; fill: ${colors.textColor};
1043
+ font: 600 14px 'Segoe UI', Ubuntu, "Helvetica Neue", Sans-Serif; fill: ${lightColors.textColor};
1008
1044
  }
1009
1045
  @supports(-moz-appearance: auto) {
1010
1046
  /* Selector detects Firefox */
@@ -1013,7 +1049,7 @@ const renderTopLanguages = (topLangs, options = {}) => {
1013
1049
  .bold { font-weight: 700 }
1014
1050
  .lang-name {
1015
1051
  font: 400 11px "Segoe UI", Ubuntu, Sans-Serif;
1016
- fill: ${colors.textColor};
1052
+ fill: ${lightColors.textColor};
1017
1053
  }
1018
1054
  .stagger {
1019
1055
  opacity: 0;
@@ -1025,8 +1061,16 @@ const renderTopLanguages = (topLangs, options = {}) => {
1025
1061
  .lang-progress{
1026
1062
  animation: growWidthAnimation 0.6s ease-in-out forwards;
1027
1063
  }
1064
+ .progress-background { fill: ${lightColors.progBarBgColor}; }
1028
1065
  `,
1029
- );
1066
+ dark: darkColors
1067
+ ? `
1068
+ .stat { fill: ${darkColors.textColor}; }
1069
+ .lang-name { fill: ${darkColors.textColor}; }
1070
+ .progress-background { fill: ${darkColors.progBarBgColor}; }
1071
+ `
1072
+ : null,
1073
+ });
1030
1074
 
1031
1075
  if (layout === "pie" || layout === "donut-vertical") {
1032
1076
  return card.render(finalLayout);