@stats-organization/github-readme-stats-core 2.1.4 → 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 (182) 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 +28 -26
  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 +32 -36
  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 +18 -33
  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 +32 -44
  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 +28 -26
  16. package/build/calculateRank.d.ts +12 -11
  17. package/build/calculateRank.d.ts.map +1 -1
  18. package/build/calculateRank.js +18 -18
  19. package/build/cards/common-options.d.ts +13 -0
  20. package/build/cards/common-options.d.ts.map +1 -0
  21. package/build/cards/common-options.js +1 -0
  22. package/build/cards/gist.d.ts +11 -16
  23. package/build/cards/gist.d.ts.map +1 -1
  24. package/build/cards/gist.js +26 -31
  25. package/build/cards/repo.d.ts +19 -16
  26. package/build/cards/repo.d.ts.map +1 -1
  27. package/build/cards/repo.js +79 -72
  28. package/build/cards/stats.d.ts +29 -44
  29. package/build/cards/stats.d.ts.map +1 -1
  30. package/build/cards/stats.js +118 -173
  31. package/build/cards/top-languages.d.ts +81 -77
  32. package/build/cards/top-languages.d.ts.map +1 -1
  33. package/build/cards/top-languages.js +186 -184
  34. package/build/cards/wakatime.d.ts +21 -11
  35. package/build/cards/wakatime.d.ts.map +1 -1
  36. package/build/cards/wakatime.js +92 -109
  37. package/build/common/Card.d.ts +41 -14
  38. package/build/common/Card.d.ts.map +1 -1
  39. package/build/common/Card.js +92 -31
  40. package/build/common/I18n.d.ts +4 -2
  41. package/build/common/I18n.d.ts.map +1 -1
  42. package/build/common/color.d.ts +91 -15
  43. package/build/common/color.d.ts.map +1 -1
  44. package/build/common/color.js +163 -17
  45. package/build/common/config.d.ts +2 -1
  46. package/build/common/config.d.ts.map +1 -1
  47. package/build/common/config.js +16 -26
  48. package/build/common/constants.d.ts +2 -1
  49. package/build/common/constants.d.ts.map +1 -1
  50. package/build/common/date.d.ts +37 -0
  51. package/build/common/date.d.ts.map +1 -0
  52. package/build/common/date.js +31 -0
  53. package/build/common/html.js +1 -1
  54. package/build/common/http.d.ts +37 -5
  55. package/build/common/http.d.ts.map +1 -1
  56. package/build/common/http.js +29 -6
  57. package/build/common/icons.d.ts +1 -0
  58. package/build/common/icons.d.ts.map +1 -1
  59. package/build/common/icons.js +1 -0
  60. package/build/common/languageColors.d.ts +11 -0
  61. package/build/common/languageColors.d.ts.map +1 -0
  62. package/build/common/languageColors.js +15 -0
  63. package/build/common/languageColors.json +10 -1
  64. package/build/common/ops.d.ts +27 -33
  65. package/build/common/ops.d.ts.map +1 -1
  66. package/build/common/ops.js +31 -48
  67. package/build/common/render.d.ts +154 -113
  68. package/build/common/render.d.ts.map +1 -1
  69. package/build/common/render.js +209 -92
  70. package/build/common/retryer.d.ts +15 -6
  71. package/build/common/retryer.d.ts.map +1 -1
  72. package/build/common/retryer.js +2 -0
  73. package/build/fetchers/gist.d.ts +6 -21
  74. package/build/fetchers/gist.d.ts.map +1 -1
  75. package/build/fetchers/gist.js +28 -64
  76. package/build/fetchers/repo.d.ts +12 -11
  77. package/build/fetchers/repo.d.ts.map +1 -1
  78. package/build/fetchers/repo.js +27 -68
  79. package/build/fetchers/stats.d.ts +24 -17
  80. package/build/fetchers/stats.d.ts.map +1 -1
  81. package/build/fetchers/stats.js +302 -184
  82. package/build/fetchers/top-languages.d.ts +10 -15
  83. package/build/fetchers/top-languages.d.ts.map +1 -1
  84. package/build/fetchers/top-languages.js +52 -103
  85. package/build/fetchers/types.d.ts +130 -0
  86. package/build/fetchers/types.d.ts.map +1 -0
  87. package/build/fetchers/types.js +1 -0
  88. package/build/fetchers/wakatime.d.ts +9 -5
  89. package/build/fetchers/wakatime.d.ts.map +1 -1
  90. package/build/fetchers/wakatime.js +8 -4
  91. package/build/graphql/contributionsDocument.d.ts +18 -0
  92. package/build/graphql/contributionsDocument.d.ts.map +1 -0
  93. package/build/graphql/contributionsDocument.js +30 -0
  94. package/build/graphql/generated/common.d.ts +42 -0
  95. package/build/graphql/generated/common.d.ts.map +1 -0
  96. package/build/graphql/generated/common.js +2 -0
  97. package/build/graphql/generated/gist.d.ts +44 -0
  98. package/build/graphql/generated/gist.d.ts.map +1 -0
  99. package/build/graphql/generated/gist.js +27 -0
  100. package/build/graphql/generated/repo.d.ts +67 -0
  101. package/build/graphql/generated/repo.d.ts.map +1 -0
  102. package/build/graphql/generated/repo.js +30 -0
  103. package/build/graphql/generated/stats.d.ts +150 -0
  104. package/build/graphql/generated/stats.d.ts.map +1 -0
  105. package/build/graphql/generated/stats.js +95 -0
  106. package/build/graphql/generated/top-languages.d.ts +54 -0
  107. package/build/graphql/generated/top-languages.d.ts.map +1 -0
  108. package/build/graphql/generated/top-languages.js +27 -0
  109. package/build/graphql/graphqlDocument.d.ts +22 -0
  110. package/build/graphql/graphqlDocument.d.ts.map +1 -0
  111. package/build/graphql/graphqlDocument.js +8 -0
  112. package/build/graphql/reposContributedToDocument.d.ts +27 -0
  113. package/build/graphql/reposContributedToDocument.d.ts.map +1 -0
  114. package/build/graphql/reposContributedToDocument.js +59 -0
  115. package/build/index.d.ts +3 -1
  116. package/build/index.d.ts.map +1 -1
  117. package/build/index.js +2 -1
  118. package/build/themes/index.d.ts +32 -0
  119. package/build/themes/index.d.ts.map +1 -1
  120. package/build/themes/index.js +28 -0
  121. package/build/translations.d.ts +17 -0
  122. package/build/translations.d.ts.map +1 -1
  123. package/build/translations.js +17 -0
  124. package/package.json +14 -8
  125. package/src/_emoji-name-map.d.ts +10 -0
  126. package/src/_github-username-regex.d.ts +4 -0
  127. package/src/api/gist.js +31 -31
  128. package/src/api/index.js +40 -42
  129. package/src/api/pin.js +20 -38
  130. package/src/api/top-langs.js +36 -49
  131. package/src/api/wakatime.js +31 -31
  132. package/src/{calculateRank.js → calculateRank.ts} +29 -19
  133. package/src/cards/common-options.ts +13 -0
  134. package/src/cards/{gist.js → gist.ts} +37 -37
  135. package/src/cards/{repo.js → repo.ts} +107 -79
  136. package/src/cards/{stats.js → stats.ts} +173 -210
  137. package/src/cards/{top-languages.js → top-languages.ts} +325 -243
  138. package/src/cards/{wakatime.js → wakatime.ts} +158 -127
  139. package/src/common/Card.ts +123 -43
  140. package/src/common/I18n.ts +6 -2
  141. package/src/common/color.ts +240 -28
  142. package/src/common/config.ts +22 -29
  143. package/src/common/constants.ts +3 -1
  144. package/src/common/date.ts +45 -0
  145. package/src/common/html.ts +1 -1
  146. package/src/common/http.ts +71 -0
  147. package/src/common/icons.ts +1 -0
  148. package/src/common/languageColors.json +10 -1
  149. package/src/common/languageColors.ts +19 -0
  150. package/src/common/ops.ts +158 -0
  151. package/src/common/render.ts +652 -0
  152. package/src/common/retryer.ts +21 -12
  153. package/src/fetchers/gist.ts +79 -0
  154. package/src/fetchers/repo.ts +114 -0
  155. package/src/fetchers/stats.ts +743 -0
  156. package/src/fetchers/top-languages.ts +135 -0
  157. package/src/fetchers/{types.d.ts → types.ts} +28 -20
  158. package/src/fetchers/{wakatime.js → wakatime.ts} +19 -5
  159. package/src/graphql/contributionsDocument.ts +44 -0
  160. package/src/graphql/generated/common.ts +26 -0
  161. package/src/graphql/generated/gist.ts +59 -0
  162. package/src/graphql/generated/repo.ts +90 -0
  163. package/src/graphql/generated/stats.ts +194 -0
  164. package/src/graphql/generated/top-languages.ts +75 -0
  165. package/src/graphql/graphqlDocument.ts +25 -0
  166. package/src/graphql/queries/gist.graphql +25 -0
  167. package/src/graphql/queries/repo.graphql +28 -0
  168. package/src/graphql/queries/stats.graphql +120 -0
  169. package/src/graphql/queries/top-languages.graphql +34 -0
  170. package/src/graphql/reposContributedToDocument.ts +86 -0
  171. package/src/index.ts +3 -1
  172. package/src/themes/index.ts +33 -0
  173. package/src/translations.ts +17 -0
  174. package/src/cards/types.d.ts +0 -78
  175. package/src/common/http.js +0 -19
  176. package/src/common/ops.js +0 -170
  177. package/src/common/render.js +0 -440
  178. package/src/fetchers/gist.js +0 -112
  179. package/src/fetchers/repo.js +0 -161
  180. package/src/fetchers/stats.js +0 -474
  181. package/src/fetchers/top-languages.js +0 -175
  182. package/src/themes/README.md +0 -229
@@ -1,13 +1,14 @@
1
1
  import { Card } from "../common/Card.js";
2
2
  import { I18n } from "../common/I18n.js";
3
- import { fallbackColor, getCardColors } from "../common/color.js";
3
+ import { getLightDarkColors, isPrefixedHexColor } from "../common/color.js";
4
4
  import { formatBytes } from "../common/fmt.js";
5
+ import { encodeHTML } from "../common/html.js";
6
+ import { DEFAULT_LANG_COLOR } from "../common/languageColors.js";
5
7
  import { chunkArray, clampValue, lowercaseTrim } from "../common/ops.js";
6
8
  import { createProgressNode, flexLayout, measureText, } from "../common/render.js";
7
9
  import { langCardLocales } from "../translations.js";
8
10
  const DEFAULT_CARD_WIDTH = 300;
9
11
  const MIN_CARD_WIDTH = 280;
10
- const DEFAULT_LANG_COLOR = "#858585";
11
12
  const CARD_PADDING = 25;
12
13
  const COMPACT_LAYOUT_BASE_HEIGHT = 90;
13
14
  const MAXIMUM_LANGS_COUNT = 20;
@@ -16,38 +17,35 @@ const COMPACT_LAYOUT_DEFAULT_LANGS_COUNT = 6;
16
17
  const DONUT_LAYOUT_DEFAULT_LANGS_COUNT = 5;
17
18
  const PIE_LAYOUT_DEFAULT_LANGS_COUNT = 6;
18
19
  const DONUT_VERTICAL_LAYOUT_DEFAULT_LANGS_COUNT = 6;
19
- /**
20
- * @typedef {import("../fetchers/types").Lang} Lang
21
- */
22
20
  /**
23
21
  * Retrieves the programming language whose name is the longest.
24
22
  *
25
- * @param {Lang[]} arr Array of programming languages.
26
- * @returns {{ name: string, size: number, color: string }} Longest programming language object.
23
+ * @param arr Array of programming languages.
24
+ * @returns Longest programming language object.
27
25
  */
28
26
  const getLongestLang = (arr) => arr.reduce((savedLang, lang) => lang.name.length > savedLang.name.length ? lang : savedLang, { name: "", size: 0, color: "" });
29
27
  /**
30
28
  * Convert degrees to radians.
31
29
  *
32
- * @param {number} angleInDegrees Angle in degrees.
33
- * @returns {number} Angle in radians.
30
+ * @param angleInDegrees Angle in degrees.
31
+ * @returns Angle in radians.
34
32
  */
35
33
  const degreesToRadians = (angleInDegrees) => angleInDegrees * (Math.PI / 180.0);
36
34
  /**
37
35
  * Convert radians to degrees.
38
36
  *
39
- * @param {number} angleInRadians Angle in radians.
40
- * @returns {number} Angle in degrees.
37
+ * @param angleInRadians Angle in radians.
38
+ * @returns Angle in degrees.
41
39
  */
42
40
  const radiansToDegrees = (angleInRadians) => angleInRadians / (Math.PI / 180.0);
43
41
  /**
44
42
  * Convert polar coordinates to cartesian coordinates.
45
43
  *
46
- * @param {number} centerX Center x coordinate.
47
- * @param {number} centerY Center y coordinate.
48
- * @param {number} radius Radius of the circle.
49
- * @param {number} angleInDegrees Angle in degrees.
50
- * @returns {{x: number, y: number}} Cartesian coordinates.
44
+ * @param centerX Center x coordinate.
45
+ * @param centerY Center y coordinate.
46
+ * @param radius Radius of the circle.
47
+ * @param angleInDegrees Angle in degrees.
48
+ * @returns Cartesian coordinates.
51
49
  */
52
50
  const polarToCartesian = (centerX, centerY, radius, angleInDegrees) => {
53
51
  const rads = degreesToRadians(angleInDegrees);
@@ -59,11 +57,11 @@ const polarToCartesian = (centerX, centerY, radius, angleInDegrees) => {
59
57
  /**
60
58
  * Convert cartesian coordinates to polar coordinates.
61
59
  *
62
- * @param {number} centerX Center x coordinate.
63
- * @param {number} centerY Center y coordinate.
64
- * @param {number} x Point x coordinate.
65
- * @param {number} y Point y coordinate.
66
- * @returns {{radius: number, angleInDegrees: number}} Polar coordinates.
60
+ * @param centerX Center x coordinate.
61
+ * @param centerY Center y coordinate.
62
+ * @param x Point x coordinate.
63
+ * @param y Point y coordinate.
64
+ * @returns Polar coordinates.
67
65
  */
68
66
  const cartesianToPolar = (centerX, centerY, x, y) => {
69
67
  const radius = Math.sqrt(Math.pow(x - centerX, 2) + Math.pow(y - centerY, 2));
@@ -76,8 +74,8 @@ const cartesianToPolar = (centerX, centerY, x, y) => {
76
74
  /**
77
75
  * Calculates length of circle.
78
76
  *
79
- * @param {number} radius Radius of the circle.
80
- * @returns {number} The length of the circle.
77
+ * @param radius Radius of the circle.
78
+ * @returns The length of the circle.
81
79
  */
82
80
  const getCircleLength = (radius) => {
83
81
  return 2 * Math.PI * radius;
@@ -85,8 +83,8 @@ const getCircleLength = (radius) => {
85
83
  /**
86
84
  * Calculates height for the compact layout.
87
85
  *
88
- * @param {number} totalLangs Total number of languages.
89
- * @returns {number} Card height.
86
+ * @param totalLangs Total number of languages.
87
+ * @returns Card height.
90
88
  */
91
89
  const calculateCompactLayoutHeight = (totalLangs) => {
92
90
  return COMPACT_LAYOUT_BASE_HEIGHT + Math.round(totalLangs / 2) * 25;
@@ -94,8 +92,8 @@ const calculateCompactLayoutHeight = (totalLangs) => {
94
92
  /**
95
93
  * Calculates height for the normal layout.
96
94
  *
97
- * @param {number} totalLangs Total number of languages.
98
- * @returns {number} Card height.
95
+ * @param totalLangs Total number of languages.
96
+ * @returns Card height.
99
97
  */
100
98
  const calculateNormalLayoutHeight = (totalLangs) => {
101
99
  return 45 + (totalLangs + 1) * 40;
@@ -103,8 +101,8 @@ const calculateNormalLayoutHeight = (totalLangs) => {
103
101
  /**
104
102
  * Calculates height for the donut layout.
105
103
  *
106
- * @param {number} totalLangs Total number of languages.
107
- * @returns {number} Card height.
104
+ * @param totalLangs Total number of languages.
105
+ * @returns Card height.
108
106
  */
109
107
  const calculateDonutLayoutHeight = (totalLangs) => {
110
108
  return 215 + Math.max(totalLangs - 5, 0) * 32;
@@ -112,8 +110,8 @@ const calculateDonutLayoutHeight = (totalLangs) => {
112
110
  /**
113
111
  * Calculates height for the donut vertical layout.
114
112
  *
115
- * @param {number} totalLangs Total number of languages.
116
- * @returns {number} Card height.
113
+ * @param totalLangs Total number of languages.
114
+ * @returns Card height.
117
115
  */
118
116
  const calculateDonutVerticalLayoutHeight = (totalLangs) => {
119
117
  return 300 + Math.round(totalLangs / 2) * 25;
@@ -121,16 +119,17 @@ const calculateDonutVerticalLayoutHeight = (totalLangs) => {
121
119
  /**
122
120
  * Calculates height for the pie layout.
123
121
  *
124
- * @param {number} totalLangs Total number of languages.
125
- * @returns {number} Card height.
122
+ * @param totalLangs Total number of languages.
123
+ * @returns Card height.
126
124
  */
127
125
  const calculatePieLayoutHeight = (totalLangs) => {
128
126
  return 300 + Math.round(totalLangs / 2) * 25;
129
127
  };
130
128
  /**
131
129
  * Calculates the center translation needed to keep the donut chart centred.
132
- * @param {number} totalLangs Total number of languages.
133
- * @returns {number} Donut center translation.
130
+ *
131
+ * @param totalLangs Total number of languages.
132
+ * @returns Donut center translation.
134
133
  */
135
134
  const donutCenterTranslation = (totalLangs) => {
136
135
  return -45 + Math.max(totalLangs - 5, 0) * 16;
@@ -138,30 +137,25 @@ const donutCenterTranslation = (totalLangs) => {
138
137
  /**
139
138
  * Trim top languages to lang_count while also hiding certain languages.
140
139
  *
141
- * @param {Record<string, Lang>} topLangs Top languages.
142
- * @param {number} langs_count Number of languages to show.
143
- * @param {string[]=} hide Languages to hide.
144
- * @returns {{ langs: Lang[], totalLanguageSize: number }} Trimmed top languages and total size.
140
+ * @param topLangs Top languages.
141
+ * @param langs_count Number of languages to show.
142
+ * @param hide Languages to hide.
143
+ * @returns Trimmed top languages and total size.
145
144
  */
146
145
  const trimTopLanguages = (topLangs, langs_count, hide) => {
147
146
  let langs = Object.values(topLangs);
148
- let langsToHide = {};
149
- let langsCount = clampValue(langs_count, 1, MAXIMUM_LANGS_COUNT);
150
- // populate langsToHide map for quick lookup
151
- // while filtering out
147
+ const langsToHide = {};
148
+ const langsCount = clampValue(langs_count, 1, MAXIMUM_LANGS_COUNT);
149
+ // populate langsToHide map for quick lookup while filtering out
152
150
  if (hide) {
153
151
  hide.forEach((langName) => {
154
- // @ts-ignore
155
152
  langsToHide[lowercaseTrim(langName)] = true;
156
153
  });
157
154
  }
158
155
  // filter out languages to be hidden
159
156
  langs = langs
160
157
  .sort((a, b) => b.size - a.size)
161
- .filter((lang) => {
162
- // @ts-ignore
163
- return !langsToHide[lowercaseTrim(lang.name)];
164
- })
158
+ .filter((lang) => !langsToHide[lowercaseTrim(lang.name)])
165
159
  .slice(0, langsCount);
166
160
  const totalLanguageSize = langs.reduce((acc, curr) => acc + curr.size, 0);
167
161
  return { langs, totalLanguageSize };
@@ -169,30 +163,43 @@ const trimTopLanguages = (topLangs, langs_count, hide) => {
169
163
  /**
170
164
  * Get display value corresponding to the format.
171
165
  *
172
- * @param {number} size Bytes size.
173
- * @param {number} percentages Percentage value.
174
- * @param {string} format Format of the stats.
175
- * @returns {string} Display value.
166
+ * @param size Bytes size.
167
+ * @param percentages Percentage value.
168
+ * @param format Format of the stats.
169
+ * @returns Display value.
176
170
  */
177
171
  const getDisplayValue = (size, percentages, format) => {
178
172
  return format === "bytes" ? formatBytes(size) : `${percentages.toFixed(2)}%`;
179
173
  };
174
+ /**
175
+ * Resolves a language's display color, falling back to the default, and
176
+ * validates it is a prefixed hex color.
177
+ *
178
+ * @param lang Programming language object.
179
+ * @returns Validated language color.
180
+ */
181
+ const resolveLangColor = (lang) => {
182
+ const color = lang.color || DEFAULT_LANG_COLOR;
183
+ if (!isPrefixedHexColor(color)) {
184
+ throw new Error(`Invalid language color: "${color}"`);
185
+ }
186
+ return color;
187
+ };
180
188
  /**
181
189
  * Create progress bar text item for a programming language.
182
190
  *
183
- * @param {object} props Function properties.
184
- * @param {number} props.width The card width
185
- * @param {string} props.color Color of the programming language.
186
- * @param {string} props.progBarBgColor Color of the background of progress bar.
187
- * @param {string} props.name Name of the programming language.
188
- * @param {number} props.size Size of the programming language.
189
- * @param {number} props.totalSize Total size of all languages.
190
- * @param {string} props.statsFormat Stats format.
191
- * @param {boolean=} props.hideValues Whether to hide stats values.
192
- * @param {number} props.index Index of the programming language.
193
- * @returns {string} Programming language SVG node.
191
+ * @param props Function properties.
192
+ * @param props.width The card width
193
+ * @param props.color Color of the programming language.
194
+ * @param props.name Name of the programming language.
195
+ * @param props.size Size of the programming language.
196
+ * @param props.totalSize Total size of all languages.
197
+ * @param props.statsFormat Stats format.
198
+ * @param props.hideValues Whether to hide stats values.
199
+ * @param props.index Index of the programming language.
200
+ * @returns Programming language SVG node.
194
201
  */
195
- const createProgressTextNode = ({ width, color, progBarBgColor, name, size, totalSize, statsFormat, hideValues, index, }) => {
202
+ const createProgressTextNode = ({ width, color, name, size, totalSize, statsFormat, hideValues, index, }) => {
196
203
  const staggerDelay = (index + 3) * 150;
197
204
  const paddingRight = hideValues ? CARD_PADDING * 2 : 95;
198
205
  const progressTextX = width - paddingRight + 10;
@@ -201,15 +208,14 @@ const createProgressTextNode = ({ width, color, progBarBgColor, name, size, tota
201
208
  const displayValue = getDisplayValue(size, progress, statsFormat);
202
209
  return `
203
210
  <g class="stagger" style="animation-delay: ${staggerDelay}ms">
204
- <text data-testid="lang-name" x="2" y="15" class="lang-name">${name}</text>
205
- ${hideValues ? "" : `<text x="${progressTextX}" y="34" class="lang-name">${displayValue}</text>`}
211
+ <text data-testid="lang-name" x="2" y="15" class="lang-name">${encodeHTML(name)}</text>
212
+ ${hideValues ? "" : `<text x="${progressTextX}" y="34" class="lang-name">${encodeHTML(displayValue)}</text>`}
206
213
  ${createProgressNode({
207
214
  x: 0,
208
215
  y: 25,
209
216
  color,
210
217
  width: progressWidth,
211
218
  progress,
212
- progressBarBackgroundColor: progBarBgColor,
213
219
  delay: staggerDelay + 300,
214
220
  })}
215
221
  </g>
@@ -218,25 +224,25 @@ const createProgressTextNode = ({ width, color, progBarBgColor, name, size, tota
218
224
  /**
219
225
  * Creates compact text item for a programming language.
220
226
  *
221
- * @param {object} props Function properties.
222
- * @param {Lang} props.lang Programming language object.
223
- * @param {number} props.totalSize Total size of all languages.
224
- * @param {boolean=} props.hideProgress Whether to hide percentage.
225
- * @param {boolean=} props.hideValues Whether to hide stats values (percentages/bytes).
226
- * @param {string=} props.statsFormat Stats format
227
- * @param {number} props.index Index of the programming language.
228
- * @returns {string} Compact layout programming language SVG node.
227
+ * @param props Function properties.
228
+ * @param props.lang Programming language object.
229
+ * @param props.totalSize Total size of all languages.
230
+ * @param props.hideProgress Whether to hide percentage.
231
+ * @param props.hideValues Whether to hide stats values (percentages/bytes).
232
+ * @param props.statsFormat Stats format
233
+ * @param props.index Index of the programming language.
234
+ * @returns Compact layout programming language SVG node.
229
235
  */
230
236
  const createCompactLangNode = ({ lang, totalSize, hideProgress, hideValues, statsFormat = "percentages", index, }) => {
231
237
  const percentages = (lang.size / totalSize) * 100;
232
238
  const displayValue = getDisplayValue(lang.size, percentages, statsFormat);
233
239
  const staggerDelay = (index + 3) * 150;
234
- const color = lang.color || "#858585";
240
+ const color = resolveLangColor(lang);
235
241
  return `
236
242
  <g class="stagger" style="animation-delay: ${staggerDelay}ms">
237
243
  <circle cx="5" cy="6" r="5" fill="${color}" />
238
244
  <text data-testid="lang-name" x="15" y="10" class='lang-name'>
239
- ${lang.name} ${hideProgress || hideValues ? "" : displayValue}
245
+ ${encodeHTML(lang.name)} ${hideProgress || hideValues ? "" : encodeHTML(displayValue)}
240
246
  </text>
241
247
  </g>
242
248
  `;
@@ -244,19 +250,18 @@ const createCompactLangNode = ({ lang, totalSize, hideProgress, hideValues, stat
244
250
  /**
245
251
  * Create compact languages text items for all programming languages.
246
252
  *
247
- * @param {object} props Function properties.
248
- * @param {Lang[]} props.langs Array of programming languages.
249
- * @param {number} props.totalSize Total size of all languages.
250
- * @param {boolean=} props.hideProgress Whether to hide percentage.
251
- * @param {boolean=} props.hideValues Whether to hide stats values.
252
- * @param {string=} props.statsFormat Stats format
253
- * @returns {string} Programming languages SVG node.
253
+ * @param props Function properties.
254
+ * @param props.langs Array of programming languages.
255
+ * @param props.totalSize Total size of all languages.
256
+ * @param props.hideProgress Whether to hide percentage.
257
+ * @param props.hideValues Whether to hide stats values.
258
+ * @param props.statsFormat Stats format
259
+ * @returns Programming languages SVG node.
254
260
  */
255
261
  const createLanguageTextNode = ({ langs, totalSize, hideProgress, hideValues, statsFormat, }) => {
256
262
  const longestLang = getLongestLang(langs);
257
263
  const chunked = chunkArray(langs, langs.length / 2);
258
264
  const layouts = chunked.map((array) => {
259
- // @ts-ignore
260
265
  const items = array.map((lang, index) => createCompactLangNode({
261
266
  lang,
262
267
  totalSize,
@@ -282,12 +287,12 @@ const createLanguageTextNode = ({ langs, totalSize, hideProgress, hideValues, st
282
287
  /**
283
288
  * Create donut languages text items for all programming languages.
284
289
  *
285
- * @param {object} props Function properties.
286
- * @param {Lang[]} props.langs Array of programming languages.
287
- * @param {number} props.totalSize Total size of all languages.
288
- * @param {boolean=} props.hideValues Whether to hide stats values.
289
- * @param {string} props.statsFormat Stats format
290
- * @returns {string} Donut layout programming language SVG node.
290
+ * @param props Function properties.
291
+ * @param props.langs Array of programming languages.
292
+ * @param props.totalSize Total size of all languages.
293
+ * @param props.hideValues Whether to hide stats values.
294
+ * @param props.statsFormat Stats format
295
+ * @returns Donut layout programming language SVG node.
291
296
  */
292
297
  const createDonutLanguagesNode = ({ langs, totalSize, hideValues, statsFormat, }) => {
293
298
  return flexLayout({
@@ -308,15 +313,14 @@ const createDonutLanguagesNode = ({ langs, totalSize, hideValues, statsFormat, }
308
313
  /**
309
314
  * Renders the default language card layout.
310
315
  *
311
- * @param {Lang[]} langs Array of programming languages.
312
- * @param {number} width Card width.
313
- * @param {number} totalLanguageSize Total size of all languages.
314
- * @param progBarBgColor Color of the background of progress bar.
315
- * @param {string} statsFormat Stats format.
316
- * @param {boolean=} hideValues Whether to hide stats values.
317
- * @returns {string} Normal layout card SVG object.
316
+ * @param langs Array of programming languages.
317
+ * @param width Card width.
318
+ * @param totalLanguageSize Total size of all languages.
319
+ * @param statsFormat Stats format.
320
+ * @param hideValues Whether to hide stats values.
321
+ * @returns Normal layout card SVG object.
318
322
  */
319
- const renderNormalLayout = (langs, width, totalLanguageSize, progBarBgColor, statsFormat, hideValues) => {
323
+ const renderNormalLayout = (langs, width, totalLanguageSize, statsFormat, hideValues) => {
320
324
  return flexLayout({
321
325
  items: langs.map((lang, index) => {
322
326
  return createProgressTextNode({
@@ -328,7 +332,6 @@ const renderNormalLayout = (langs, width, totalLanguageSize, progBarBgColor, sta
328
332
  statsFormat,
329
333
  hideValues,
330
334
  index,
331
- progBarBgColor,
332
335
  });
333
336
  }),
334
337
  gap: 40,
@@ -338,13 +341,13 @@ const renderNormalLayout = (langs, width, totalLanguageSize, progBarBgColor, sta
338
341
  /**
339
342
  * Renders the compact language card layout.
340
343
  *
341
- * @param {Lang[]} langs Array of programming languages.
342
- * @param {number} width Card width.
343
- * @param {number} totalLanguageSize Total size of all languages.
344
- * @param {boolean=} hideProgress Whether to hide progress bar.
345
- * @param {string} statsFormat Stats format.
346
- * @param {boolean=} hideValues Whether to hide stats values.
347
- * @returns {string} Compact layout card SVG object.
344
+ * @param langs Array of programming languages.
345
+ * @param width Card width.
346
+ * @param totalLanguageSize Total size of all languages.
347
+ * @param hideProgress Whether to hide progress bar.
348
+ * @param statsFormat Stats format.
349
+ * @param hideValues Whether to hide stats values.
350
+ * @returns Compact layout card SVG object.
348
351
  */
349
352
  const renderCompactLayout = (langs, width, totalLanguageSize, hideProgress, statsFormat = "percentages", hideValues) => {
350
353
  const paddingRight = 50;
@@ -354,6 +357,7 @@ const renderCompactLayout = (langs, width, totalLanguageSize, hideProgress, stat
354
357
  let progressOffset = 0;
355
358
  const compactProgressBar = langs
356
359
  .map((lang) => {
360
+ const langColor = resolveLangColor(lang);
357
361
  const percentage = parseFloat(((lang.size / totalLanguageSize) * offsetWidth).toFixed(2));
358
362
  const progress = percentage < 10 ? percentage + 10 : percentage;
359
363
  const output = `
@@ -364,7 +368,7 @@ const renderCompactLayout = (langs, width, totalLanguageSize, hideProgress, stat
364
368
  y="0"
365
369
  width="${progress}"
366
370
  height="8"
367
- fill="${lang.color || "#858585"}"
371
+ fill="${langColor}"
368
372
  />
369
373
  `;
370
374
  progressOffset += percentage;
@@ -394,35 +398,36 @@ const renderCompactLayout = (langs, width, totalLanguageSize, hideProgress, stat
394
398
  /**
395
399
  * Renders donut vertical layout to display user's most frequently used programming languages.
396
400
  *
397
- * @param {Lang[]} langs Array of programming languages.
398
- * @param {number} totalLanguageSize Total size of all languages.
399
- * @param {string} statsFormat Stats format.
400
- * @param {boolean=} hideValues Whether to hide stats values.
401
- * @returns {string} Compact layout card SVG object.
401
+ * @param langs Array of programming languages.
402
+ * @param totalLanguageSize Total size of all languages.
403
+ * @param statsFormat Stats format.
404
+ * @param hideValues Whether to hide stats values.
405
+ * @returns Compact layout card SVG object.
402
406
  */
403
407
  const renderDonutVerticalLayout = (langs, totalLanguageSize, statsFormat, hideValues) => {
404
408
  // Donut vertical chart radius and total length
405
409
  const radius = 80;
406
410
  const totalCircleLength = getCircleLength(radius);
407
411
  // SVG circles
408
- let circles = [];
412
+ const circles = [];
409
413
  // Start indent for donut vertical chart parts
410
414
  let indent = 0;
411
415
  // Start delay coefficient for donut vertical chart parts
412
416
  let startDelayCoefficient = 1;
413
417
  // Generate each donut vertical chart part
414
418
  for (const lang of langs) {
419
+ const langColor = resolveLangColor(lang);
415
420
  const percentage = (lang.size / totalLanguageSize) * 100;
416
421
  const circleLength = totalCircleLength * (percentage / 100);
417
422
  const delay = startDelayCoefficient * 100;
418
423
  circles.push(`
419
424
  <g class="stagger" style="animation-delay: ${delay}ms">
420
- <circle
425
+ <circle
421
426
  cx="150"
422
427
  cy="100"
423
428
  r="${radius}"
424
429
  fill="transparent"
425
- stroke="${lang.color}"
430
+ stroke="${langColor}"
426
431
  stroke-width="25"
427
432
  stroke-dasharray="${totalCircleLength}"
428
433
  stroke-dashoffset="${indent}"
@@ -460,11 +465,11 @@ const renderDonutVerticalLayout = (langs, totalLanguageSize, statsFormat, hideVa
460
465
  /**
461
466
  * Renders pie layout to display user's most frequently used programming languages.
462
467
  *
463
- * @param {Lang[]} langs Array of programming languages.
464
- * @param {number} totalLanguageSize Total size of all languages.
465
- * @param {string} statsFormat Stats format.
466
- * @param {boolean=} hideValues Whether to hide stats values.
467
- * @returns {string} Compact layout card SVG object.
468
+ * @param langs Array of programming languages.
469
+ * @param totalLanguageSize Total size of all languages.
470
+ * @param statsFormat Stats format.
471
+ * @param hideValues Whether to hide stats values.
472
+ * @returns Compact layout card SVG object.
468
473
  */
469
474
  const renderPieLayout = (langs, totalLanguageSize, statsFormat, hideValues) => {
470
475
  // Pie chart radius and center coordinates
@@ -479,6 +484,7 @@ const renderPieLayout = (langs, totalLanguageSize, statsFormat, hideValues) => {
479
484
  const paths = [];
480
485
  // Generate each pie chart part
481
486
  for (const lang of langs) {
487
+ const langColor = resolveLangColor(lang);
482
488
  if (langs.length === 1) {
483
489
  paths.push(`
484
490
  <circle
@@ -486,7 +492,7 @@ const renderPieLayout = (langs, totalLanguageSize, statsFormat, hideValues) => {
486
492
  cy="${centerY}"
487
493
  r="${radius}"
488
494
  stroke="none"
489
- fill="${lang.color}"
495
+ fill="${langColor}"
490
496
  data-testid="lang-pie"
491
497
  size="100"
492
498
  />
@@ -513,7 +519,7 @@ const renderPieLayout = (langs, totalLanguageSize, statsFormat, hideValues) => {
513
519
  data-testid="lang-pie"
514
520
  size="${percentage}"
515
521
  d="M ${centerX} ${centerY} L ${startPoint.x} ${startPoint.y} A ${radius} ${radius} 0 ${largeArcFlag} 1 ${endPoint.x} ${endPoint.y} Z"
516
- fill="${lang.color}"
522
+ fill="${langColor}"
517
523
  />
518
524
  </g>
519
525
  `);
@@ -546,27 +552,26 @@ const renderPieLayout = (langs, totalLanguageSize, statsFormat, hideValues) => {
546
552
  /**
547
553
  * Creates the SVG paths for the language donut chart.
548
554
  *
549
- * @param {number} cx Donut center x-position.
550
- * @param {number} cy Donut center y-position.
551
- * @param {number} radius Donut arc Radius.
552
- * @param {number[]} percentages Array with donut section percentages.
553
- * @returns {{d: string, percent: number}[]} Array of svg path elements
555
+ * @param cx Donut center x-position.
556
+ * @param cy Donut center y-position.
557
+ * @param radius Donut arc Radius.
558
+ * @param percentages Array with donut section percentages.
559
+ * @returns Array of svg path elements
554
560
  */
555
561
  const createDonutPaths = (cx, cy, radius, percentages) => {
556
562
  const paths = [];
557
563
  let startAngle = 0;
558
- let endAngle;
559
564
  const totalPercent = percentages.reduce((acc, curr) => acc + curr, 0);
560
- for (let i = 0; i < percentages.length; i++) {
561
- const tmpPath = {};
562
- let percent = parseFloat(((percentages[i] / totalPercent) * 100).toFixed(2));
563
- endAngle = 3.6 * percent + startAngle;
565
+ for (const rawPercent of percentages) {
566
+ const percent = parseFloat(((rawPercent / totalPercent) * 100).toFixed(2));
567
+ const endAngle = 3.6 * percent + startAngle;
564
568
  const startPoint = polarToCartesian(cx, cy, radius, endAngle - 90); // rotate donut 90 degrees counter-clockwise.
565
569
  const endPoint = polarToCartesian(cx, cy, radius, startAngle - 90); // rotate donut 90 degrees counter-clockwise.
566
570
  const largeArc = endAngle - startAngle <= 180 ? 0 : 1;
567
- tmpPath.percent = percent;
568
- tmpPath.d = `M ${startPoint.x} ${startPoint.y} A ${radius} ${radius} 0 ${largeArc} 0 ${endPoint.x} ${endPoint.y}`;
569
- paths.push(tmpPath);
571
+ paths.push({
572
+ percent,
573
+ d: `M ${startPoint.x} ${startPoint.y} A ${radius} ${radius} 0 ${largeArc} 0 ${endPoint.x} ${endPoint.y}`,
574
+ });
570
575
  startAngle = endAngle;
571
576
  }
572
577
  return paths;
@@ -574,23 +579,26 @@ const createDonutPaths = (cx, cy, radius, percentages) => {
574
579
  /**
575
580
  * Renders the donut language card layout.
576
581
  *
577
- * @param {Lang[]} langs Array of programming languages.
578
- * @param {number} width Card width.
579
- * @param {number} totalLanguageSize Total size of all languages.
580
- * @param {string} statsFormat Stats format.
581
- * @param {boolean=} hideValues Whether to hide stats values.
582
- * @returns {string} Donut layout card SVG object.
582
+ * @param langs Array of programming languages.
583
+ * @param width Card width.
584
+ * @param totalLanguageSize Total size of all languages.
585
+ * @param statsFormat Stats format.
586
+ * @param hideValues Whether to hide stats values.
587
+ * @returns Donut layout card SVG object.
583
588
  */
584
589
  const renderDonutLayout = (langs, width, totalLanguageSize, statsFormat, hideValues) => {
590
+ if (!Number.isFinite(width)) {
591
+ throw new Error(`Invalid width: "${width}"`);
592
+ }
585
593
  const centerX = width / 3;
586
594
  const centerY = width / 3;
587
595
  const radius = centerX - 60;
588
596
  const strokeWidth = 12;
589
- const colors = langs.map((lang) => lang.color);
597
+ const colors = langs.map(resolveLangColor);
590
598
  const langsPercents = langs.map((lang) => parseFloat(((lang.size / totalLanguageSize) * 100).toFixed(2)));
591
599
  const langPaths = createDonutPaths(centerX, centerY, radius, langsPercents);
592
600
  const donutPaths = langs.length === 1
593
- ? `<circle cx="${centerX}" cy="${centerY}" r="${radius}" stroke="${colors[0]}" fill="none" stroke-width="${strokeWidth}" data-testid="lang-donut" size="100"/>`
601
+ ? `<circle cx="${centerX}" cy="${centerY}" r="${radius}" stroke="${colors[0] ?? DEFAULT_LANG_COLOR}" fill="none" stroke-width="${strokeWidth}" data-testid="lang-donut" size="100"/>`
594
602
  : langPaths
595
603
  .map((section, index) => {
596
604
  const staggerDelay = (index + 3) * 100;
@@ -601,7 +609,7 @@ const renderDonutLayout = (langs, width, totalLanguageSize, statsFormat, hideVal
601
609
  data-testid="lang-donut"
602
610
  size="${section.percent}"
603
611
  d="${section.d}"
604
- stroke="${colors[index]}"
612
+ stroke="${colors[index] ?? DEFAULT_LANG_COLOR}"
605
613
  fill="none"
606
614
  stroke-width="${strokeWidth}">
607
615
  </path>
@@ -623,33 +631,28 @@ const renderDonutLayout = (langs, width, totalLanguageSize, statsFormat, hideVal
623
631
  </g>
624
632
  `;
625
633
  };
626
- /**
627
- * @typedef {import("./types").TopLangOptions} TopLangOptions
628
- * @typedef {TopLangOptions["layout"]} Layout
629
- */
630
634
  /**
631
635
  * Creates the no languages data SVG node.
632
636
  *
633
- * @param {object} props Object with function properties.
634
- * @param {string} props.color No languages data text color.
635
- * @param {string} props.text No languages data translated text.
636
- * @param {Layout | undefined} props.layout Card layout.
637
- * @returns {string} No languages data SVG node string.
637
+ * @param props Object with function properties.
638
+ * @param props.text No languages data translated text.
639
+ * @param props.layout Card layout.
640
+ * @returns No languages data SVG node string.
638
641
  */
639
- const noLanguagesDataNode = ({ color, text, layout }) => {
642
+ const noLanguagesDataNode = ({ text, layout, }) => {
640
643
  return `
641
- <text x="${layout === "pie" || layout === "donut-vertical" ? CARD_PADDING : 0}" y="11" class="stat bold" fill="${color}">${text}</text>
644
+ <text x="${layout === "pie" || layout === "donut-vertical" ? CARD_PADDING : 0}" y="11" class="stat bold">${encodeHTML(text)}</text>
642
645
  `;
643
646
  };
644
647
  /**
645
648
  * Get default languages count for provided card layout.
646
649
  *
647
- * @param {object} props Function properties.
648
- * @param {Layout=} props.layout Input layout string.
649
- * @param {boolean=} props.hide_progress Input hide_progress parameter value.
650
- * @returns {number} Default languages count for input layout.
650
+ * @param props Function properties.
651
+ * @param props.layout Input layout string.
652
+ * @param props.hide_progress Input hide_progress parameter value.
653
+ * @returns Default languages count for input layout.
651
654
  */
652
- const getDefaultLanguagesCountByLayout = ({ layout, hide_progress }) => {
655
+ const getDefaultLanguagesCountByLayout = ({ layout, hide_progress, }) => {
653
656
  if (layout === "compact" || hide_progress === true) {
654
657
  return COMPACT_LAYOUT_DEFAULT_LANGS_COUNT;
655
658
  }
@@ -666,18 +669,15 @@ const getDefaultLanguagesCountByLayout = ({ layout, hide_progress }) => {
666
669
  return NORMAL_LAYOUT_DEFAULT_LANGS_COUNT;
667
670
  }
668
671
  };
669
- /**
670
- * @typedef {import('../fetchers/types').TopLangData} TopLangData
671
- */
672
672
  /**
673
673
  * Renders card that display user's most frequently used programming languages.
674
674
  *
675
- * @param {TopLangData} topLangs User's most frequently used programming languages.
676
- * @param {Partial<TopLangOptions>} options Card options.
677
- * @returns {string} Language card SVG object.
675
+ * @param topLangs User's most frequently used programming languages.
676
+ * @param options Card options.
677
+ * @returns Language card SVG object.
678
678
  */
679
679
  const renderTopLanguages = (topLangs, options = {}) => {
680
- const { hide_title = false, hide_border = false, card_width, title_color, text_color, bg_color, prog_bar_bg_color, hide, hide_progress, hide_values, theme, layout, custom_title, locale, langs_count = getDefaultLanguagesCountByLayout({ layout, hide_progress }), border_radius, border_color, disable_animations, stats_format = "percentages", } = options;
680
+ const { hide_title = false, hide_border = false, card_width, hide, hide_progress, hide_values, layout, custom_title, locale, langs_count = getDefaultLanguagesCountByLayout({ layout, hide_progress }), border_radius, disable_animations, stats_format = "percentages", } = options;
681
681
  const i18n = new I18n({
682
682
  locale,
683
683
  translations: langCardLocales,
@@ -691,19 +691,11 @@ const renderTopLanguages = (topLangs, options = {}) => {
691
691
  : card_width
692
692
  : DEFAULT_CARD_WIDTH;
693
693
  let height = calculateNormalLayoutHeight(langs.length);
694
- // returns theme based colors with proper overrides and defaults
695
- const colors = getCardColors({
696
- title_color,
697
- text_color,
698
- bg_color,
699
- border_color,
700
- theme,
701
- });
694
+ const { lightColors, darkColors } = getLightDarkColors(options);
702
695
  let finalLayout;
703
696
  if (langs.length === 0) {
704
697
  height = COMPACT_LAYOUT_BASE_HEIGHT;
705
698
  finalLayout = noLanguagesDataNode({
706
- color: colors.textColor,
707
699
  text: i18n.t("langcard.nodata"),
708
700
  layout,
709
701
  });
@@ -716,7 +708,7 @@ const renderTopLanguages = (topLangs, options = {}) => {
716
708
  height = calculateDonutVerticalLayoutHeight(langs.length);
717
709
  finalLayout = renderDonutVerticalLayout(langs, totalLanguageSize, stats_format, hide_values);
718
710
  }
719
- else if (layout === "compact" || hide_progress == true) {
711
+ else if (layout === "compact" || hide_progress === true) {
720
712
  height =
721
713
  calculateCompactLayoutHeight(langs.length) + (hide_progress ? -25 : 0);
722
714
  finalLayout = renderCompactLayout(langs, width, totalLanguageSize, hide_progress, stats_format, hide_values);
@@ -727,7 +719,7 @@ const renderTopLanguages = (topLangs, options = {}) => {
727
719
  finalLayout = renderDonutLayout(langs, width, totalLanguageSize, stats_format, hide_values);
728
720
  }
729
721
  else {
730
- finalLayout = renderNormalLayout(langs, width, totalLanguageSize, fallbackColor(prog_bar_bg_color, "#ddd"), stats_format, hide_values);
722
+ finalLayout = renderNormalLayout(langs, width, totalLanguageSize, stats_format, hide_values);
731
723
  }
732
724
  const card = new Card({
733
725
  customTitle: custom_title,
@@ -735,14 +727,15 @@ const renderTopLanguages = (topLangs, options = {}) => {
735
727
  width,
736
728
  height,
737
729
  border_radius,
738
- colors,
730
+ colors: { light: lightColors, dark: darkColors },
739
731
  });
740
732
  if (disable_animations) {
741
733
  card.disableAnimations();
742
734
  }
743
735
  card.setHideBorder(hide_border);
744
736
  card.setHideTitle(hide_title);
745
- card.setCSS(`
737
+ card.setCSS({
738
+ light: `
746
739
  @keyframes slideInAnimation {
747
740
  from {
748
741
  width: 0;
@@ -760,7 +753,7 @@ const renderTopLanguages = (topLangs, options = {}) => {
760
753
  }
761
754
  }
762
755
  .stat {
763
- font: 600 14px 'Segoe UI', Ubuntu, "Helvetica Neue", Sans-Serif; fill: ${colors.textColor};
756
+ font: 600 14px 'Segoe UI', Ubuntu, "Helvetica Neue", Sans-Serif; fill: ${lightColors.textColor};
764
757
  }
765
758
  @supports(-moz-appearance: auto) {
766
759
  /* Selector detects Firefox */
@@ -769,7 +762,7 @@ const renderTopLanguages = (topLangs, options = {}) => {
769
762
  .bold { font-weight: 700 }
770
763
  .lang-name {
771
764
  font: 400 11px "Segoe UI", Ubuntu, Sans-Serif;
772
- fill: ${colors.textColor};
765
+ fill: ${lightColors.textColor};
773
766
  }
774
767
  .stagger {
775
768
  opacity: 0;
@@ -781,7 +774,16 @@ const renderTopLanguages = (topLangs, options = {}) => {
781
774
  .lang-progress{
782
775
  animation: growWidthAnimation 0.6s ease-in-out forwards;
783
776
  }
784
- `);
777
+ .progress-background { fill: ${lightColors.progBarBgColor}; }
778
+ `,
779
+ dark: darkColors
780
+ ? `
781
+ .stat { fill: ${darkColors.textColor}; }
782
+ .lang-name { fill: ${darkColors.textColor}; }
783
+ .progress-background { fill: ${darkColors.progBarBgColor}; }
784
+ `
785
+ : null,
786
+ });
785
787
  if (layout === "pie" || layout === "donut-vertical") {
786
788
  return card.render(finalLayout);
787
789
  }