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