@stats-forge/github-stats-forge-core 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +148 -0
  3. package/build/api/api-result.d.ts +44 -0
  4. package/build/api/api-result.d.ts.map +1 -0
  5. package/build/api/api-result.js +33 -0
  6. package/build/api/gist.d.ts +29 -0
  7. package/build/api/gist.d.ts.map +1 -0
  8. package/build/api/gist.js +52 -0
  9. package/build/api/index.d.ts +13 -0
  10. package/build/api/index.d.ts.map +1 -0
  11. package/build/api/index.js +8 -0
  12. package/build/api/params.d.ts +93 -0
  13. package/build/api/params.d.ts.map +1 -0
  14. package/build/api/params.js +145 -0
  15. package/build/api/pin.d.ts +47 -0
  16. package/build/api/pin.d.ts.map +1 -0
  17. package/build/api/pin.js +87 -0
  18. package/build/api/stats.d.ts +42 -0
  19. package/build/api/stats.d.ts.map +1 -0
  20. package/build/api/stats.js +132 -0
  21. package/build/api/top-langs.d.ts +37 -0
  22. package/build/api/top-langs.d.ts.map +1 -0
  23. package/build/api/top-langs.js +101 -0
  24. package/build/api/wakatime.d.ts +34 -0
  25. package/build/api/wakatime.d.ts.map +1 -0
  26. package/build/api/wakatime.js +91 -0
  27. package/build/calculateRank.d.ts +29 -0
  28. package/build/calculateRank.d.ts.map +1 -0
  29. package/build/calculateRank.js +58 -0
  30. package/build/cards/gist.d.ts +16 -0
  31. package/build/cards/gist.d.ts.map +1 -0
  32. package/build/cards/gist.js +119 -0
  33. package/build/cards/index.d.ts +11 -0
  34. package/build/cards/index.d.ts.map +1 -0
  35. package/build/cards/index.js +7 -0
  36. package/build/cards/options.d.ts +19 -0
  37. package/build/cards/options.d.ts.map +1 -0
  38. package/build/cards/options.js +1 -0
  39. package/build/cards/repo.d.ts +25 -0
  40. package/build/cards/repo.d.ts.map +1 -0
  41. package/build/cards/repo.js +276 -0
  42. package/build/cards/stats.d.ts +37 -0
  43. package/build/cards/stats.d.ts.map +1 -0
  44. package/build/cards/stats.js +457 -0
  45. package/build/cards/top-languages.d.ts +153 -0
  46. package/build/cards/top-languages.d.ts.map +1 -0
  47. package/build/cards/top-languages.js +805 -0
  48. package/build/cards/wakatime.d.ts +31 -0
  49. package/build/cards/wakatime.d.ts.map +1 -0
  50. package/build/cards/wakatime.js +376 -0
  51. package/build/common/Card.d.ts +121 -0
  52. package/build/common/Card.d.ts.map +1 -0
  53. package/build/common/Card.js +305 -0
  54. package/build/common/I18n.d.ts +27 -0
  55. package/build/common/I18n.d.ts.map +1 -0
  56. package/build/common/I18n.js +35 -0
  57. package/build/common/color.d.ts +112 -0
  58. package/build/common/color.d.ts.map +1 -0
  59. package/build/common/color.js +188 -0
  60. package/build/common/config.d.ts +53 -0
  61. package/build/common/config.d.ts.map +1 -0
  62. package/build/common/config.js +87 -0
  63. package/build/common/constants.d.ts +7 -0
  64. package/build/common/constants.d.ts.map +1 -0
  65. package/build/common/constants.js +9 -0
  66. package/build/common/date.d.ts +29 -0
  67. package/build/common/date.d.ts.map +1 -0
  68. package/build/common/date.js +23 -0
  69. package/build/common/error.d.ts +87 -0
  70. package/build/common/error.d.ts.map +1 -0
  71. package/build/common/error.js +108 -0
  72. package/build/common/fmt.d.ts +28 -0
  73. package/build/common/fmt.d.ts.map +1 -0
  74. package/build/common/fmt.js +68 -0
  75. package/build/common/html.d.ts +8 -0
  76. package/build/common/html.d.ts.map +1 -0
  77. package/build/common/html.js +14 -0
  78. package/build/common/http.d.ts +53 -0
  79. package/build/common/http.d.ts.map +1 -0
  80. package/build/common/http.js +47 -0
  81. package/build/common/icons.d.ts +28 -0
  82. package/build/common/icons.d.ts.map +1 -0
  83. package/build/common/icons.js +77 -0
  84. package/build/common/languageColors.d.ts +11 -0
  85. package/build/common/languageColors.d.ts.map +1 -0
  86. package/build/common/languageColors.js +15 -0
  87. package/build/common/languageColors.json +678 -0
  88. package/build/common/log.d.ts +9 -0
  89. package/build/common/log.d.ts.map +1 -0
  90. package/build/common/log.js +8 -0
  91. package/build/common/ops.d.ts +59 -0
  92. package/build/common/ops.d.ts.map +1 -0
  93. package/build/common/ops.js +115 -0
  94. package/build/common/render.d.ts +196 -0
  95. package/build/common/render.d.ts.map +1 -0
  96. package/build/common/render.js +474 -0
  97. package/build/common/retryer.d.ts +36 -0
  98. package/build/common/retryer.d.ts.map +1 -0
  99. package/build/common/retryer.js +67 -0
  100. package/build/fetchers/gist.d.ts +15 -0
  101. package/build/fetchers/gist.d.ts.map +1 -0
  102. package/build/fetchers/gist.js +68 -0
  103. package/build/fetchers/index.d.ts +13 -0
  104. package/build/fetchers/index.d.ts.map +1 -0
  105. package/build/fetchers/index.js +10 -0
  106. package/build/fetchers/repo.d.ts +27 -0
  107. package/build/fetchers/repo.d.ts.map +1 -0
  108. package/build/fetchers/repo.js +100 -0
  109. package/build/fetchers/stats.d.ts +74 -0
  110. package/build/fetchers/stats.d.ts.map +1 -0
  111. package/build/fetchers/stats.js +498 -0
  112. package/build/fetchers/top-languages.d.ts +23 -0
  113. package/build/fetchers/top-languages.d.ts.map +1 -0
  114. package/build/fetchers/top-languages.js +89 -0
  115. package/build/fetchers/types.d.ts +130 -0
  116. package/build/fetchers/types.d.ts.map +1 -0
  117. package/build/fetchers/types.js +1 -0
  118. package/build/fetchers/wakatime.d.ts +19 -0
  119. package/build/fetchers/wakatime.d.ts.map +1 -0
  120. package/build/fetchers/wakatime.js +36 -0
  121. package/build/graphql/contributionsDocument.d.ts +18 -0
  122. package/build/graphql/contributionsDocument.d.ts.map +1 -0
  123. package/build/graphql/contributionsDocument.js +30 -0
  124. package/build/graphql/generated/common.d.ts +42 -0
  125. package/build/graphql/generated/common.d.ts.map +1 -0
  126. package/build/graphql/generated/common.js +2 -0
  127. package/build/graphql/generated/gist.d.ts +44 -0
  128. package/build/graphql/generated/gist.d.ts.map +1 -0
  129. package/build/graphql/generated/gist.js +27 -0
  130. package/build/graphql/generated/repo.d.ts +67 -0
  131. package/build/graphql/generated/repo.d.ts.map +1 -0
  132. package/build/graphql/generated/repo.js +30 -0
  133. package/build/graphql/generated/stats.d.ts +150 -0
  134. package/build/graphql/generated/stats.d.ts.map +1 -0
  135. package/build/graphql/generated/stats.js +95 -0
  136. package/build/graphql/generated/top-languages.d.ts +54 -0
  137. package/build/graphql/generated/top-languages.d.ts.map +1 -0
  138. package/build/graphql/generated/top-languages.js +27 -0
  139. package/build/graphql/graphqlDocument.d.ts +22 -0
  140. package/build/graphql/graphqlDocument.d.ts.map +1 -0
  141. package/build/graphql/graphqlDocument.js +8 -0
  142. package/build/graphql/reposContributedToDocument.d.ts +27 -0
  143. package/build/graphql/reposContributedToDocument.d.ts.map +1 -0
  144. package/build/graphql/reposContributedToDocument.js +59 -0
  145. package/build/index.d.ts +15 -0
  146. package/build/index.d.ts.map +1 -0
  147. package/build/index.js +10 -0
  148. package/build/themes/index.d.ts +505 -0
  149. package/build/themes/index.d.ts.map +1 -0
  150. package/build/themes/index.js +505 -0
  151. package/build/translations.d.ts +1153 -0
  152. package/build/translations.d.ts.map +1 -0
  153. package/build/translations.js +1156 -0
  154. package/package.json +114 -0
  155. package/src/_emoji-name-map.d.ts +10 -0
  156. package/src/_github-username-regex.d.ts +4 -0
  157. package/src/api/api-result.ts +69 -0
  158. package/src/api/gist.ts +66 -0
  159. package/src/api/index.ts +16 -0
  160. package/src/api/params.ts +221 -0
  161. package/src/api/pin.ts +126 -0
  162. package/src/api/stats.ts +195 -0
  163. package/src/api/top-langs.ts +150 -0
  164. package/src/api/wakatime.ts +130 -0
  165. package/src/calculateRank.ts +91 -0
  166. package/src/cards/gist.ts +169 -0
  167. package/src/cards/index.ts +21 -0
  168. package/src/cards/options.ts +18 -0
  169. package/src/cards/repo.ts +376 -0
  170. package/src/cards/stats.ts +615 -0
  171. package/src/cards/top-languages.ts +1077 -0
  172. package/src/cards/wakatime.ts +501 -0
  173. package/src/common/Card.ts +364 -0
  174. package/src/common/I18n.ts +51 -0
  175. package/src/common/color.ts +296 -0
  176. package/src/common/config.ts +118 -0
  177. package/src/common/constants.ts +12 -0
  178. package/src/common/date.ts +34 -0
  179. package/src/common/error.ts +163 -0
  180. package/src/common/fmt.ts +86 -0
  181. package/src/common/html.ts +17 -0
  182. package/src/common/http.ts +98 -0
  183. package/src/common/icons.ts +80 -0
  184. package/src/common/languageColors.json +678 -0
  185. package/src/common/languageColors.ts +19 -0
  186. package/src/common/log.ts +12 -0
  187. package/src/common/ops.ts +145 -0
  188. package/src/common/render.ts +641 -0
  189. package/src/common/retryer.ts +111 -0
  190. package/src/fetchers/gist.ts +82 -0
  191. package/src/fetchers/index.ts +25 -0
  192. package/src/fetchers/repo.ts +140 -0
  193. package/src/fetchers/stats.ts +742 -0
  194. package/src/fetchers/top-languages.ts +134 -0
  195. package/src/fetchers/types.ts +136 -0
  196. package/src/fetchers/wakatime.ts +56 -0
  197. package/src/graphql/contributionsDocument.ts +44 -0
  198. package/src/graphql/generated/common.ts +26 -0
  199. package/src/graphql/generated/gist.ts +56 -0
  200. package/src/graphql/generated/repo.ts +79 -0
  201. package/src/graphql/generated/stats.ts +180 -0
  202. package/src/graphql/generated/top-languages.ts +66 -0
  203. package/src/graphql/graphqlDocument.ts +25 -0
  204. package/src/graphql/queries/gist.graphql +25 -0
  205. package/src/graphql/queries/repo.graphql +28 -0
  206. package/src/graphql/queries/stats.graphql +113 -0
  207. package/src/graphql/queries/top-languages.graphql +27 -0
  208. package/src/graphql/reposContributedToDocument.ts +83 -0
  209. package/src/index.ts +18 -0
  210. package/src/themes/index.ts +519 -0
  211. package/src/translations.ts +1169 -0
@@ -0,0 +1,305 @@
1
+ import { getCardColors, isPrefixedHexColor, isValidGradient } from './color.js';
2
+ import { encodeHTML } from './html.js';
3
+ import { flexLayout } from './render.js';
4
+ class Card {
5
+ width;
6
+ height;
7
+ hideBorder;
8
+ hideTitle;
9
+ border_radius;
10
+ colors;
11
+ title;
12
+ css;
13
+ darkCss;
14
+ paddingX;
15
+ paddingY;
16
+ titlePrefixIcon;
17
+ animations;
18
+ a11yTitle;
19
+ a11yDesc;
20
+ /**
21
+ * Creates a new card instance.
22
+ *
23
+ * The caller must ensure that the passed `titlePrefixIcon` is properly sanitized!
24
+ *
25
+ * @param props Card arguments.
26
+ * @param props.width Card width.
27
+ * @param props.height Card height.
28
+ * @param props.border_radius Card border radius.
29
+ * @param props.colors Card colors for light and dark mode.
30
+ * @param props.colors.light Card colors for light mode.
31
+ * @param props.colors.dark Card colors for dark mode, or `null` when no dark-mode override is needed.
32
+ * @param props.customTitle Card custom title.
33
+ * @param props.defaultTitle Card default title.
34
+ * @param props.titlePrefixIcon Sanitized card title prefix icon.
35
+ */
36
+ constructor({ width = 100, height = 100, border_radius = 4.5, colors = { light: getCardColors({}), dark: null }, customTitle, defaultTitle = '', titlePrefixIcon, }) {
37
+ this.width = width;
38
+ this.height = height;
39
+ this.hideBorder = false;
40
+ this.hideTitle = false;
41
+ this.border_radius = parseFloat(String(border_radius));
42
+ this.colors = colors;
43
+ this.title = customTitle === undefined ? defaultTitle : customTitle;
44
+ this.css = '';
45
+ this.darkCss = '';
46
+ this.paddingX = 25;
47
+ this.paddingY = 35;
48
+ this.titlePrefixIcon = titlePrefixIcon;
49
+ this.animations = true;
50
+ this.a11yTitle = '';
51
+ this.a11yDesc = '';
52
+ }
53
+ disableAnimations() {
54
+ this.animations = false;
55
+ }
56
+ /**
57
+ * @param props The props object.
58
+ * @param props.title Accessibility title.
59
+ * @param props.desc Accessibility description.
60
+ */
61
+ setAccessibilityLabel({ title, desc }) {
62
+ this.a11yTitle = title;
63
+ this.a11yDesc = desc;
64
+ }
65
+ /**
66
+ * Sets the card CSS for light and dark mode.
67
+ *
68
+ * Each builder receives the colors of its own color scheme,
69
+ * so a card never handles a missing dark palette itself:
70
+ * `dark` runs only when the card has dark colors, and always with non-null ones.
71
+ *
72
+ * @warning The caller must ensure that the returned CSS strings are properly sanitized!
73
+ *
74
+ * @param props The props object.
75
+ * @param props.light Builds the CSS applied unconditionally (light/default mode).
76
+ * @param props.dark Builds the CSS placed inside a `@media (prefers-color-scheme: dark)` block.
77
+ */
78
+ setCSS({ light, dark }) {
79
+ this.css = light(this.colors.light);
80
+ this.darkCss = this.colors.dark ? dark(this.colors.dark) : '';
81
+ }
82
+ /**
83
+ * @param value Whether to hide the border or not.
84
+ */
85
+ setHideBorder(value) {
86
+ this.hideBorder = value;
87
+ }
88
+ /**
89
+ * @param value Whether to hide the title or not.
90
+ */
91
+ setHideTitle(value) {
92
+ if (value && !this.hideTitle) {
93
+ this.height -= 30;
94
+ }
95
+ if (!value && this.hideTitle) {
96
+ this.height += 30;
97
+ }
98
+ this.hideTitle = value;
99
+ }
100
+ /**
101
+ * @param text The title to set.
102
+ */
103
+ setTitle(text) {
104
+ this.title = text;
105
+ }
106
+ /**
107
+ * @returns The rendered card title.
108
+ */
109
+ renderTitle() {
110
+ const titleText = `
111
+ <text
112
+ x="0"
113
+ y="0"
114
+ class="header"
115
+ data-testid="header"
116
+ >${encodeHTML(this.title)}</text>
117
+ `;
118
+ const prefixIcon = `
119
+ <svg
120
+ class="icon"
121
+ x="0"
122
+ y="-13"
123
+ viewBox="0 0 16 16"
124
+ version="1.1"
125
+ width="16"
126
+ height="16"
127
+ >
128
+ ${String(this.titlePrefixIcon)}
129
+ </svg>
130
+ `;
131
+ return `
132
+ <g
133
+ data-testid="card-title"
134
+ transform="translate(${this.paddingX}, ${this.paddingY})"
135
+ >
136
+ ${flexLayout({
137
+ items: [this.titlePrefixIcon ? prefixIcon : '', titleText],
138
+ gap: 25,
139
+ }).join('')}
140
+ </g>
141
+ `;
142
+ }
143
+ /**
144
+ * @returns The rendered card gradient.
145
+ */
146
+ renderGradient() {
147
+ const buildGradientDef = (id, bgColor) => {
148
+ const gradients = bgColor.slice(1);
149
+ return `
150
+ <linearGradient
151
+ id="${id}"
152
+ gradientTransform="rotate(${String(bgColor[0])})"
153
+ gradientUnits="userSpaceOnUse"
154
+ >
155
+ ${gradients
156
+ .map((grad, index) => {
157
+ const offset = (index * 100) / (gradients.length - 1);
158
+ return `<stop offset="${offset}%" stop-color="#${grad}" />`;
159
+ })
160
+ .join(',')}
161
+ </linearGradient>`;
162
+ };
163
+ if (typeof this.colors.light.bgColor === 'object' &&
164
+ !isValidGradient(this.colors.light.bgColor)) {
165
+ throw new Error(`Invalid gradient: ${this.colors.light.bgColor.join(',')}`);
166
+ }
167
+ if (this.colors.dark &&
168
+ typeof this.colors.dark.bgColor === 'object' &&
169
+ !isValidGradient(this.colors.dark.bgColor)) {
170
+ throw new Error(`Invalid dark gradient: ${this.colors.dark.bgColor.join(',')}`);
171
+ }
172
+ return `
173
+ <defs>
174
+ ${typeof this.colors.light.bgColor === 'object' ? buildGradientDef('gradient', this.colors.light.bgColor) : ''}
175
+ ${this.colors.dark && typeof this.colors.dark.bgColor === 'object' ? buildGradientDef('gradient-dark', this.colors.dark.bgColor) : ''}
176
+ </defs>
177
+ `;
178
+ }
179
+ /**
180
+ * Retrieves css animations for a card.
181
+ *
182
+ * @returns Animation css.
183
+ */
184
+ getAnimations = () => {
185
+ return `
186
+ /* Animations */
187
+ @keyframes scaleInAnimation {
188
+ from {
189
+ transform: translate(-5px, 5px) scale(0);
190
+ }
191
+ to {
192
+ transform: translate(-5px, 5px) scale(1);
193
+ }
194
+ }
195
+ @keyframes fadeInAnimation {
196
+ from {
197
+ opacity: 0;
198
+ }
199
+ to {
200
+ opacity: 1;
201
+ }
202
+ }
203
+ `;
204
+ };
205
+ /**
206
+ * Builds the @media (prefers-color-scheme: dark) CSS block for the card.
207
+ * Returns an empty string when no dark colors are set.
208
+ */
209
+ renderDarkMediaBlock() {
210
+ if (!this.colors.dark) {
211
+ return '';
212
+ }
213
+ const bgFill = typeof this.colors.dark.bgColor === 'object'
214
+ ? 'url(#gradient-dark)'
215
+ : this.colors.dark.bgColor;
216
+ return `
217
+ @media (prefers-color-scheme: dark) {
218
+ .header { fill: ${this.colors.dark.titleColor}; }
219
+ .card-bg { fill: ${bgFill}; stroke: ${this.colors.dark.borderColor}; }
220
+ ${this.darkCss}
221
+ }`;
222
+ }
223
+ /**
224
+ * The caller must ensure that the passed `body` string is properly sanitized!
225
+ *
226
+ * @param body The sanitized inner body of the card.
227
+ * @returns The rendered card.
228
+ */
229
+ render(body) {
230
+ if (!Number.isFinite(this.border_radius)) {
231
+ throw new Error(`Invalid border radius: "${this.border_radius}"`);
232
+ }
233
+ if (!isPrefixedHexColor(this.colors.light.titleColor)) {
234
+ throw new Error(`Invalid title color: "${this.colors.light.titleColor}"`);
235
+ }
236
+ if (!isPrefixedHexColor(this.colors.light.borderColor)) {
237
+ throw new Error(`Invalid border color: "${this.colors.light.borderColor}"`);
238
+ }
239
+ if (!(typeof this.colors.light.bgColor === 'object'
240
+ ? isValidGradient(this.colors.light.bgColor)
241
+ : isPrefixedHexColor(this.colors.light.bgColor))) {
242
+ throw new Error(`Invalid background color: ${String(this.colors.light.bgColor)}`);
243
+ }
244
+ return `
245
+ <svg
246
+ width="${this.width}"
247
+ height="${this.height}"
248
+ viewBox="0 0 ${this.width} ${this.height}"
249
+ fill="none"
250
+ xmlns="http://www.w3.org/2000/svg"
251
+ role="img"
252
+ aria-labelledby="titleId descId"
253
+ >
254
+ <title id="titleId">${encodeHTML(this.a11yTitle)}</title>
255
+ <desc id="descId">${encodeHTML(this.a11yDesc)}</desc>
256
+ <style>
257
+ .header {
258
+ font: 600 18px 'Segoe UI', Ubuntu, Sans-Serif;
259
+ fill: ${this.colors.light.titleColor};
260
+ animation: fadeInAnimation 0.8s ease-in-out forwards;
261
+ }
262
+ @supports(-moz-appearance: auto) {
263
+ /* Selector detects Firefox */
264
+ .header { font-size: 15.5px; }
265
+ }
266
+ ${this.css}
267
+ ${this.renderDarkMediaBlock()}
268
+
269
+ ${this.getAnimations()}
270
+ ${this.animations
271
+ ? ''
272
+ : `* { animation-duration: 0s !important; animation-delay: 0s !important; }`}
273
+ </style>
274
+
275
+ ${this.renderGradient()}
276
+
277
+ <rect
278
+ data-testid="card-bg"
279
+ class="card-bg"
280
+ x="0.5"
281
+ y="0.5"
282
+ rx="${this.border_radius}"
283
+ height="99%"
284
+ stroke="${this.colors.light.borderColor}"
285
+ width="${this.width - 1}"
286
+ fill="${typeof this.colors.light.bgColor === 'object'
287
+ ? 'url(#gradient)'
288
+ : this.colors.light.bgColor}"
289
+ stroke-opacity="${this.hideBorder ? 0 : 1}"
290
+ />
291
+
292
+ ${this.hideTitle ? '' : this.renderTitle()}
293
+
294
+ <g
295
+ data-testid="main-card-body"
296
+ transform="translate(0, ${this.hideTitle ? this.paddingX : this.paddingY + 20})"
297
+ >
298
+ ${body}
299
+ </g>
300
+ </svg>
301
+ `;
302
+ }
303
+ }
304
+ export { Card };
305
+ export default Card;
@@ -0,0 +1,27 @@
1
+ /** A map of translation keys to per-locale strings. */
2
+ type TranslationsMap = Record<string, Record<string, string>>;
3
+ /**
4
+ * I18n translation class.
5
+ */
6
+ declare class I18n<Translations extends TranslationsMap = TranslationsMap> {
7
+ locale: string;
8
+ translations: Translations;
9
+ /**
10
+ * @param props Constructor arguments.
11
+ * @param props.locale Locale.
12
+ * @param props.translations Translations.
13
+ */
14
+ constructor({ locale, translations, }: {
15
+ locale?: string | undefined;
16
+ translations: Translations;
17
+ });
18
+ /**
19
+ * Get translation.
20
+ *
21
+ * @param str String to translate.
22
+ * @returns Translated string.
23
+ */
24
+ t(str: keyof Translations & string): string;
25
+ }
26
+ export { I18n };
27
+ //# sourceMappingURL=I18n.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"I18n.d.ts","sourceRoot":"","sources":["../../src/common/I18n.ts"],"names":[],"mappings":"AAEA,uDAAuD;AACvD,KAAK,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAE9D;;GAEG;AACH,cAAM,IAAI,CAAC,YAAY,SAAS,eAAe,GAAG,eAAe;IAC/D,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,YAAY,CAAC;IAE3B;;;;OAIG;gBACS,EACV,MAAM,EACN,YAAY,GACb,EAAE;QAED,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC5B,YAAY,EAAE,YAAY,CAAC;KAC5B;IAKD;;;;;OAKG;IACH,CAAC,CAAC,GAAG,EAAE,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM;CAa5C;AAED,OAAO,EAAE,IAAI,EAAE,CAAC"}
@@ -0,0 +1,35 @@
1
+ const FALLBACK_LOCALE = 'en';
2
+ /**
3
+ * I18n translation class.
4
+ */
5
+ class I18n {
6
+ locale;
7
+ translations;
8
+ /**
9
+ * @param props Constructor arguments.
10
+ * @param props.locale Locale.
11
+ * @param props.translations Translations.
12
+ */
13
+ constructor({ locale, translations, }) {
14
+ this.locale = locale || FALLBACK_LOCALE;
15
+ this.translations = translations;
16
+ }
17
+ /**
18
+ * Get translation.
19
+ *
20
+ * @param str String to translate.
21
+ * @returns Translated string.
22
+ */
23
+ t(str) {
24
+ const translation = this.translations[str];
25
+ if (!translation) {
26
+ throw new Error(`${str} Translation string not found`);
27
+ }
28
+ const localized = translation[this.locale];
29
+ if (!localized) {
30
+ throw new Error(`'${str}' translation not found for locale '${this.locale}'`);
31
+ }
32
+ return localized;
33
+ }
34
+ }
35
+ export { I18n };
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Checks if a value is a bare hex color, i.e. hex digits with no `#` prefix
3
+ * (`"f00"`, `"ffffff"`). This is the form user-supplied color params and
4
+ * gradient stops arrive in.
5
+ *
6
+ * @param value Value to check.
7
+ * @returns True if the value is a bare hex color.
8
+ */
9
+ declare const isBareHexColor: (value: unknown) => boolean;
10
+ /**
11
+ * Checks if a value is a `#`-prefixed hex color (`"#f00"`, `"#ffffff"`). This
12
+ * is the form colors take once resolved by {@link getCardColors}, i.e. right
13
+ * before they are written into the SVG.
14
+ *
15
+ * @param value Value to check.
16
+ * @returns True if the value is a `#`-prefixed hex color.
17
+ */
18
+ declare const isPrefixedHexColor: (value: unknown) => boolean;
19
+ /**
20
+ * Checks if the given parts form a valid gradient: a finite numeric angle
21
+ * followed by at least two bare-hex color stops, e.g. `["90", "f00", "0f0"]`.
22
+ * The angle is written into the SVG `gradientTransform="rotate(...)"`.
23
+ *
24
+ * @param parts Gradient parts: `[angle, ...stops]`.
25
+ * @returns True if the parts form a valid gradient.
26
+ */
27
+ declare const isValidGradient: (parts: Array<string>) => boolean;
28
+ /**
29
+ * Checks if a string is a valid input for a color or gradient.
30
+ *
31
+ * @param color String to check, may be null or undefined.
32
+ * @returns True if the given string is a valid input.
33
+ */
34
+ declare const isValidColorInput: (color: string | null | undefined) => boolean;
35
+ /**
36
+ * Resolved card colors for a single color scheme, as written into the SVG.
37
+ */
38
+ interface CardColors {
39
+ /** Card title color. */
40
+ titleColor: string;
41
+ /** Card icon color. */
42
+ iconColor: string;
43
+ /** Card text color. */
44
+ textColor: string;
45
+ /** Card background color, or a gradient as `[angle, ...stops]`. */
46
+ bgColor: string | Array<string>;
47
+ /** Card border color. */
48
+ borderColor: string;
49
+ /** Stats card rank ring color. */
50
+ ringColor: string;
51
+ /** Progress bar background color. */
52
+ progBarBgColor: string;
53
+ }
54
+ /**
55
+ * Every color param a card accepts, before any `_light` / `_dark` suffix.
56
+ *
57
+ * Single source of truth: the param types and {@link COLOR_PARAM_KEYS} are all
58
+ * derived from this, so adding a param here is enough.
59
+ */
60
+ declare const BASE_COLOR_KEYS: readonly ["title_color", "icon_color", "text_color", "bg_color", "border_color", "ring_color", "prog_bar_bg_color", "theme"];
61
+ declare const THEME_VARIANTS: readonly ["light", "dark"];
62
+ type BaseColorKey = (typeof BASE_COLOR_KEYS)[number];
63
+ type ThemeVariant = (typeof THEME_VARIANTS)[number];
64
+ /**
65
+ * Object with all input color params. Not every field is consumed by every card
66
+ * (e.g. `prog_bar_bg_color` is only used by the top-languages card's `normal`
67
+ * layout).
68
+ */
69
+ type ColorInput = Partial<Record<BaseColorKey, string | undefined>>;
70
+ /**
71
+ * Returns theme based colors with proper overrides and defaults.
72
+ *
73
+ * @param props Function arguments.
74
+ * @param props.title_color Card title color.
75
+ * @param props.text_color Card text color.
76
+ * @param props.icon_color Card icon color.
77
+ * @param props.bg_color Card background color.
78
+ * @param props.border_color Card border color.
79
+ * @param props.ring_color Card ring color.
80
+ * @param props.prog_bar_bg_color Progress bar background color.
81
+ * @param props.theme Card theme.
82
+ * @returns Card colors.
83
+ */
84
+ declare const getCardColors: ({ title_color, text_color, icon_color, bg_color, border_color, ring_color, prog_bar_bg_color, theme, }: ColorInput) => CardColors;
85
+ type LightDarkColorParams = Partial<Record<`${BaseColorKey}_${ThemeVariant}`, string | undefined>>;
86
+ /**
87
+ * Returns resolved colors for both light and dark mode given all input params.
88
+ *
89
+ * Each mode resolves independently, then runs the normal `getCardColors` precedence
90
+ * (explicit color -> theme color -> default theme):
91
+ * light: `theme_light ?? theme`, with `*_light` params overriding general ones
92
+ * dark: `theme_dark ?? theme`, with `*_dark` params overriding general ones
93
+ *
94
+ * Anything a mode does not override falls back to the general params,
95
+ * so a partial override such as `bg_color_dark` alone keeps every other color from the base theme.
96
+ *
97
+ * When no `_light` / `_dark` param is provided at all, `darkColors` is `null` and the caller emits no dark-mode block.
98
+ *
99
+ * @param params Raw query params, containing both general and `_light`/`_dark` suffixed colors and themes.
100
+ * @returns `{ lightColors, darkColors }`, resolved colors for both light and dark mode
101
+ */
102
+ declare const getLightDarkColors: (params: ColorInput & LightDarkColorParams) => {
103
+ lightColors: CardColors;
104
+ darkColors: CardColors | null;
105
+ };
106
+ type ColorParams = ColorInput & LightDarkColorParams;
107
+ declare const COLOR_PARAM_KEYS: ReadonlyArray<keyof ColorParams>;
108
+ /** Params naming a theme rather than holding a color value. */
109
+ declare const THEME_PARAM_KEYS: ReadonlyArray<keyof ColorParams>;
110
+ export type { CardColors, ColorParams };
111
+ export { isValidColorInput, THEME_PARAM_KEYS, getCardColors, getLightDarkColors, isValidGradient, isBareHexColor, isPrefixedHexColor, BASE_COLOR_KEYS, THEME_VARIANTS, COLOR_PARAM_KEYS, };
112
+ //# sourceMappingURL=color.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"color.d.ts","sourceRoot":"","sources":["../../src/common/color.ts"],"names":[],"mappings":"AAKA;;;;;;;GAOG;AACH,QAAA,MAAM,cAAc,GAAI,OAAO,OAAO,KAAG,OAExC,CAAC;AAEF;;;;;;;GAOG;AACH,QAAA,MAAM,kBAAkB,GAAI,OAAO,OAAO,KAAG,OAE5C,CAAC;AAEF;;;;;;;GAOG;AACH,QAAA,MAAM,eAAe,GAAI,OAAO,KAAK,CAAC,MAAM,CAAC,KAAG,OAS/C,CAAC;AAEF;;;;;GAKG;AACH,QAAA,MAAM,iBAAiB,GAAI,OAAO,MAAM,GAAG,IAAI,GAAG,SAAS,KAAG,OAK7D,CAAC;AAyBF;;GAEG;AACH,UAAU,UAAU;IAClB,wBAAwB;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,uBAAuB;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,uBAAuB;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,OAAO,EAAE,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;IAChC,yBAAyB;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,kCAAkC;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,qCAAqC;IACrC,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;;GAKG;AACH,QAAA,MAAM,eAAe,8HASX,CAAC;AAEX,QAAA,MAAM,cAAc,4BAA6B,CAAC;AAElD,KAAK,YAAY,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AACrD,KAAK,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAEpD;;;;GAIG;AACH,KAAK,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;AAEpE;;;;;;;;;;;;;GAaG;AACH,QAAA,MAAM,aAAa,GAAI,wGASpB,UAAU,KAAG,UAoDf,CAAC;AAEF,KAAK,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,YAAY,IAAI,YAAY,EAAE,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;AAenG;;;;;;;;;;;;;;;GAeG;AACH,QAAA,MAAM,kBAAkB,GACtB,QAAQ,UAAU,GAAG,oBAAoB,KACxC;IAAE,WAAW,EAAE,UAAU,CAAC;IAAC,UAAU,EAAE,UAAU,GAAG,IAAI,CAAA;CAmB1D,CAAC;AAEF,KAAK,WAAW,GAAG,UAAU,GAAG,oBAAoB,CAAC;AAErD,QAAA,MAAM,gBAAgB,EAAE,aAAa,CAAC,MAAM,WAAW,CAKtD,CAAC;AAEF,+DAA+D;AAC/D,QAAA,MAAM,gBAAgB,EAAE,aAAa,CAAC,MAAM,WAAW,CAGtD,CAAC;AAEF,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC;AAExC,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EAClB,eAAe,EACf,cAAc,EACd,kBAAkB,EAIlB,eAAe,EACf,cAAc,EACd,gBAAgB,GACjB,CAAC"}
@@ -0,0 +1,188 @@
1
+ import { isThemeName, themes } from '../themes/index.js';
2
+ /** Matches a 3-, 4-, 6-, or 8-digit hex color with no leading `#`. */
3
+ const HEX_COLOR = /^([A-Fa-f0-9]{8}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{4}|[A-Fa-f0-9]{3})$/;
4
+ /**
5
+ * Checks if a value is a bare hex color, i.e. hex digits with no `#` prefix
6
+ * (`"f00"`, `"ffffff"`). This is the form user-supplied color params and
7
+ * gradient stops arrive in.
8
+ *
9
+ * @param value Value to check.
10
+ * @returns True if the value is a bare hex color.
11
+ */
12
+ const isBareHexColor = (value) => {
13
+ return typeof value === 'string' && HEX_COLOR.test(value);
14
+ };
15
+ /**
16
+ * Checks if a value is a `#`-prefixed hex color (`"#f00"`, `"#ffffff"`). This
17
+ * is the form colors take once resolved by {@link getCardColors}, i.e. right
18
+ * before they are written into the SVG.
19
+ *
20
+ * @param value Value to check.
21
+ * @returns True if the value is a `#`-prefixed hex color.
22
+ */
23
+ const isPrefixedHexColor = (value) => {
24
+ return typeof value === 'string' && value.startsWith('#') && HEX_COLOR.test(value.slice(1));
25
+ };
26
+ /**
27
+ * Checks if the given parts form a valid gradient: a finite numeric angle
28
+ * followed by at least two bare-hex color stops, e.g. `["90", "f00", "0f0"]`.
29
+ * The angle is written into the SVG `gradientTransform="rotate(...)"`.
30
+ *
31
+ * @param parts Gradient parts: `[angle, ...stops]`.
32
+ * @returns True if the parts form a valid gradient.
33
+ */
34
+ const isValidGradient = (parts) => {
35
+ const [angle, ...stops] = parts;
36
+ return (stops.length >= 2 &&
37
+ angle !== undefined &&
38
+ angle.trim() !== '' &&
39
+ Number.isFinite(Number(angle)) &&
40
+ stops.every(isBareHexColor));
41
+ };
42
+ /**
43
+ * Checks if a string is a valid input for a color or gradient.
44
+ *
45
+ * @param color String to check, may be null or undefined.
46
+ * @returns True if the given string is a valid input.
47
+ */
48
+ const isValidColorInput = (color) => {
49
+ if (color === null || color === undefined) {
50
+ return true;
51
+ }
52
+ return isValidGradient(color.split(',')) || isBareHexColor(color);
53
+ };
54
+ /**
55
+ * Retrieves a gradient if color has more than one valid hex codes else a single color.
56
+ *
57
+ * @param color The color to parse.
58
+ * @param fallbackColor The fallback color.
59
+ * @returns The gradient or color.
60
+ */
61
+ const fallbackColor = (color, fallbackColor) => {
62
+ const colors = color ? color.split(',') : [];
63
+ if (colors.length > 1 && isValidGradient(colors)) {
64
+ return colors;
65
+ }
66
+ if (color !== undefined && isBareHexColor(color)) {
67
+ return `#${color}`;
68
+ }
69
+ return fallbackColor;
70
+ };
71
+ /**
72
+ * Every color param a card accepts, before any `_light` / `_dark` suffix.
73
+ *
74
+ * Single source of truth: the param types and {@link COLOR_PARAM_KEYS} are all
75
+ * derived from this, so adding a param here is enough.
76
+ */
77
+ const BASE_COLOR_KEYS = [
78
+ 'title_color',
79
+ 'icon_color',
80
+ 'text_color',
81
+ 'bg_color',
82
+ 'border_color',
83
+ 'ring_color',
84
+ 'prog_bar_bg_color',
85
+ 'theme',
86
+ ];
87
+ const THEME_VARIANTS = ['light', 'dark'];
88
+ /**
89
+ * Returns theme based colors with proper overrides and defaults.
90
+ *
91
+ * @param props Function arguments.
92
+ * @param props.title_color Card title color.
93
+ * @param props.text_color Card text color.
94
+ * @param props.icon_color Card icon color.
95
+ * @param props.bg_color Card background color.
96
+ * @param props.border_color Card border color.
97
+ * @param props.ring_color Card ring color.
98
+ * @param props.prog_bar_bg_color Progress bar background color.
99
+ * @param props.theme Card theme.
100
+ * @returns Card colors.
101
+ */
102
+ const getCardColors = ({ title_color, text_color, icon_color, bg_color, border_color, ring_color, prog_bar_bg_color, theme, }) => {
103
+ const defaultTheme = themes.default;
104
+ const selectedTheme = isThemeName(theme) ? themes[theme] : defaultTheme;
105
+ const defaultBorderColor = 'border_color' in selectedTheme ? selectedTheme.border_color : defaultTheme.border_color;
106
+ // get the color provided by the user else the theme color
107
+ // finally if both colors are invalid fallback to default theme
108
+ const titleColor = fallbackColor(title_color || selectedTheme.title_color, '#' + defaultTheme.title_color);
109
+ // get the color provided by the user else the theme color
110
+ // finally if both colors are invalid we use the titleColor
111
+ const iconColor = fallbackColor(icon_color || selectedTheme.icon_color, '#' + defaultTheme.icon_color);
112
+ const textColor = fallbackColor(text_color || selectedTheme.text_color, '#' + defaultTheme.text_color);
113
+ const bgColor = fallbackColor(bg_color || selectedTheme.bg_color, '#' + defaultTheme.bg_color);
114
+ const borderColor = fallbackColor(border_color || defaultBorderColor, '#' + defaultBorderColor);
115
+ // No theme defines `ring_color`, so it falls back to the title color.
116
+ const ringColor = fallbackColor(ring_color, titleColor);
117
+ // No theme defines `prog_bar_bg_color`, so it falls back to "#ddd".
118
+ const progBarBgColor = fallbackColor(prog_bar_bg_color, '#ddd');
119
+ if (typeof titleColor !== 'string' ||
120
+ typeof textColor !== 'string' ||
121
+ typeof ringColor !== 'string' ||
122
+ typeof progBarBgColor !== 'string' ||
123
+ typeof iconColor !== 'string' ||
124
+ typeof borderColor !== 'string') {
125
+ throw new Error('Unexpected behavior, all colors except background should be string.');
126
+ }
127
+ return {
128
+ titleColor,
129
+ iconColor,
130
+ textColor,
131
+ bgColor,
132
+ borderColor,
133
+ ringColor,
134
+ progBarBgColor,
135
+ };
136
+ };
137
+ /**
138
+ * Returns the light- or dark-mode-specific color params, given a set of
139
+ * raw query params. Also removes the "_light" or "_dark" suffixes.
140
+ *
141
+ * @param params Raw query params with optional `_light` / `_dark` suffixes.
142
+ * @param suffix `"_light"` or `"_dark"`.
143
+ * @returns ColorInput with the suffix stripped, ready for `getCardColors`.
144
+ */
145
+ const extractLightDarkColors = (params, suffix) => Object.fromEntries(BASE_COLOR_KEYS.map((key) => [key, params[`${key}${suffix}`]]));
146
+ /**
147
+ * Returns resolved colors for both light and dark mode given all input params.
148
+ *
149
+ * Each mode resolves independently, then runs the normal `getCardColors` precedence
150
+ * (explicit color -> theme color -> default theme):
151
+ * light: `theme_light ?? theme`, with `*_light` params overriding general ones
152
+ * dark: `theme_dark ?? theme`, with `*_dark` params overriding general ones
153
+ *
154
+ * Anything a mode does not override falls back to the general params,
155
+ * so a partial override such as `bg_color_dark` alone keeps every other color from the base theme.
156
+ *
157
+ * When no `_light` / `_dark` param is provided at all, `darkColors` is `null` and the caller emits no dark-mode block.
158
+ *
159
+ * @param params Raw query params, containing both general and `_light`/`_dark` suffixed colors and themes.
160
+ * @returns `{ lightColors, darkColors }`, resolved colors for both light and dark mode
161
+ */
162
+ const getLightDarkColors = (params) => {
163
+ const lightOverrides = extractLightDarkColors(params, '_light');
164
+ const darkOverrides = extractLightDarkColors(params, '_dark');
165
+ const hasModeOverrides = Object.values(lightOverrides).some((v) => v !== undefined) ||
166
+ Object.values(darkOverrides).some((v) => v !== undefined);
167
+ if (!hasModeOverrides) {
168
+ return { lightColors: getCardColors(params), darkColors: null };
169
+ }
170
+ const defined = (obj) => Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined));
171
+ return {
172
+ lightColors: getCardColors({ ...params, ...defined(lightOverrides) }),
173
+ darkColors: getCardColors({ ...params, ...defined(darkOverrides) }),
174
+ };
175
+ };
176
+ const COLOR_PARAM_KEYS = [
177
+ ...BASE_COLOR_KEYS,
178
+ ...THEME_VARIANTS.flatMap((variant) => BASE_COLOR_KEYS.map((key) => `${key}_${variant}`)),
179
+ ];
180
+ /** Params naming a theme rather than holding a color value. */
181
+ const THEME_PARAM_KEYS = [
182
+ 'theme',
183
+ ...THEME_VARIANTS.map((variant) => `theme_${variant}`),
184
+ ];
185
+ export { isValidColorInput, THEME_PARAM_KEYS, getCardColors, getLightDarkColors, isValidGradient, isBareHexColor, isPrefixedHexColor,
186
+ // Not re-exported from the package index: internal,
187
+ // exposed so tests can pin the accepted param list.
188
+ BASE_COLOR_KEYS, THEME_VARIANTS, COLOR_PARAM_KEYS, };