@stats-forge/github-stats-forge-core 0.1.0 → 0.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 (142) hide show
  1. package/build/api/contributed-to.d.ts +23 -0
  2. package/build/api/contributed-to.d.ts.map +1 -0
  3. package/build/api/contributed-to.js +43 -0
  4. package/build/api/gist.d.ts +8 -15
  5. package/build/api/gist.d.ts.map +1 -1
  6. package/build/api/gist.js +14 -29
  7. package/build/api/handler.d.ts +16 -0
  8. package/build/api/handler.d.ts.map +1 -0
  9. package/build/api/handler.js +29 -0
  10. package/build/api/index.d.ts +1 -0
  11. package/build/api/index.d.ts.map +1 -1
  12. package/build/api/index.js +1 -0
  13. package/build/api/params.d.ts +11 -5
  14. package/build/api/params.d.ts.map +1 -1
  15. package/build/api/params.js +26 -6
  16. package/build/api/pin.d.ts +11 -15
  17. package/build/api/pin.d.ts.map +1 -1
  18. package/build/api/pin.js +34 -46
  19. package/build/api/stats.d.ts +15 -22
  20. package/build/api/stats.d.ts.map +1 -1
  21. package/build/api/stats.js +59 -72
  22. package/build/api/top-langs.d.ts +11 -22
  23. package/build/api/top-langs.d.ts.map +1 -1
  24. package/build/api/top-langs.js +34 -51
  25. package/build/api/wakatime.d.ts +10 -24
  26. package/build/api/wakatime.d.ts.map +1 -1
  27. package/build/api/wakatime.js +27 -47
  28. package/build/cards/contributed-to.d.ts +19 -0
  29. package/build/cards/contributed-to.d.ts.map +1 -0
  30. package/build/cards/contributed-to.js +242 -0
  31. package/build/cards/gist.d.ts +1 -0
  32. package/build/cards/gist.d.ts.map +1 -1
  33. package/build/cards/gist.js +13 -9
  34. package/build/cards/repo.d.ts +12 -1
  35. package/build/cards/repo.d.ts.map +1 -1
  36. package/build/cards/repo.js +40 -18
  37. package/build/cards/stats.d.ts +15 -3
  38. package/build/cards/stats.d.ts.map +1 -1
  39. package/build/cards/stats.js +91 -76
  40. package/build/cards/top-languages.d.ts +13 -2
  41. package/build/cards/top-languages.d.ts.map +1 -1
  42. package/build/cards/top-languages.js +26 -9
  43. package/build/cards/wakatime.d.ts +13 -2
  44. package/build/cards/wakatime.d.ts.map +1 -1
  45. package/build/cards/wakatime.js +22 -9
  46. package/build/common/Card.d.ts +9 -0
  47. package/build/common/Card.d.ts.map +1 -1
  48. package/build/common/Card.js +44 -9
  49. package/build/common/I18n.d.ts +1 -1
  50. package/build/common/I18n.d.ts.map +1 -1
  51. package/build/common/I18n.js +4 -2
  52. package/build/common/brand.d.ts +76 -0
  53. package/build/common/brand.d.ts.map +1 -0
  54. package/build/common/brand.js +73 -0
  55. package/build/common/constants.d.ts +3 -1
  56. package/build/common/constants.d.ts.map +1 -1
  57. package/build/common/constants.js +3 -1
  58. package/build/common/date.d.ts +50 -3
  59. package/build/common/date.d.ts.map +1 -1
  60. package/build/common/date.js +104 -2
  61. package/build/common/icons.d.ts +4 -2
  62. package/build/common/icons.d.ts.map +1 -1
  63. package/build/common/icons.js +10 -5
  64. package/build/common/ops.d.ts +8 -3
  65. package/build/common/ops.d.ts.map +1 -1
  66. package/build/common/ops.js +8 -14
  67. package/build/common/render.d.ts +7 -1
  68. package/build/common/render.d.ts.map +1 -1
  69. package/build/common/render.js +60 -58
  70. package/build/fetchers/contributed-to.d.ts +62 -0
  71. package/build/fetchers/contributed-to.d.ts.map +1 -0
  72. package/build/fetchers/contributed-to.js +184 -0
  73. package/build/fetchers/graphql-error.d.ts +13 -0
  74. package/build/fetchers/graphql-error.d.ts.map +1 -0
  75. package/build/fetchers/graphql-error.js +29 -0
  76. package/build/fetchers/index.d.ts +2 -1
  77. package/build/fetchers/index.d.ts.map +1 -1
  78. package/build/fetchers/index.js +1 -0
  79. package/build/fetchers/stats.d.ts +14 -8
  80. package/build/fetchers/stats.d.ts.map +1 -1
  81. package/build/fetchers/stats.js +72 -195
  82. package/build/fetchers/top-languages.d.ts.map +1 -1
  83. package/build/fetchers/top-languages.js +3 -18
  84. package/build/fetchers/types.d.ts +24 -0
  85. package/build/fetchers/types.d.ts.map +1 -1
  86. package/build/graphql/contributionsCollection.d.ts +9 -0
  87. package/build/graphql/contributionsCollection.d.ts.map +1 -0
  88. package/build/graphql/contributionsCollection.js +12 -0
  89. package/build/graphql/contributionsDocument.d.ts +5 -5
  90. package/build/graphql/contributionsDocument.d.ts.map +1 -1
  91. package/build/graphql/contributionsDocument.js +8 -15
  92. package/build/graphql/generated/contributed-to.d.ts +22 -0
  93. package/build/graphql/generated/contributed-to.d.ts.map +1 -0
  94. package/build/graphql/generated/contributed-to.js +11 -0
  95. package/build/graphql/generated/stats.d.ts +13 -1
  96. package/build/graphql/generated/stats.d.ts.map +1 -1
  97. package/build/graphql/generated/stats.js +2 -2
  98. package/build/graphql/graphqlDocument.d.ts +1 -1
  99. package/build/graphql/reposContributedToDocument.d.ts.map +1 -1
  100. package/build/graphql/reposContributedToDocument.js +11 -5
  101. package/build/translations.d.ts +64 -1
  102. package/build/translations.d.ts.map +1 -1
  103. package/build/translations.js +65 -1
  104. package/package.json +1 -1
  105. package/src/api/contributed-to.ts +84 -0
  106. package/src/api/gist.ts +20 -37
  107. package/src/api/handler.ts +42 -0
  108. package/src/api/index.ts +1 -0
  109. package/src/api/params.ts +48 -9
  110. package/src/api/pin.ts +33 -45
  111. package/src/api/stats.ts +78 -89
  112. package/src/api/top-langs.ts +34 -56
  113. package/src/api/wakatime.ts +33 -54
  114. package/src/cards/contributed-to.ts +373 -0
  115. package/src/cards/gist.ts +17 -8
  116. package/src/cards/repo.ts +44 -20
  117. package/src/cards/stats.ts +119 -90
  118. package/src/cards/top-languages.ts +27 -18
  119. package/src/cards/wakatime.ts +23 -13
  120. package/src/common/Card.ts +56 -11
  121. package/src/common/I18n.ts +4 -2
  122. package/src/common/brand.ts +109 -0
  123. package/src/common/constants.ts +4 -1
  124. package/src/common/date.ts +135 -3
  125. package/src/common/icons.ts +10 -5
  126. package/src/common/ops.ts +10 -19
  127. package/src/common/render.ts +65 -62
  128. package/src/fetchers/contributed-to.ts +271 -0
  129. package/src/fetchers/graphql-error.ts +36 -0
  130. package/src/fetchers/index.ts +3 -0
  131. package/src/fetchers/stats.ts +123 -274
  132. package/src/fetchers/top-languages.ts +5 -19
  133. package/src/fetchers/types.ts +26 -0
  134. package/src/graphql/contributionsCollection.ts +18 -0
  135. package/src/graphql/contributionsDocument.ts +11 -18
  136. package/src/graphql/generated/contributed-to.ts +25 -0
  137. package/src/graphql/generated/stats.ts +19 -6
  138. package/src/graphql/graphqlDocument.ts +1 -1
  139. package/src/graphql/queries/contributed-to.graphql +8 -0
  140. package/src/graphql/queries/stats.graphql +14 -2
  141. package/src/graphql/reposContributedToDocument.ts +11 -9
  142. package/src/translations.ts +77 -0
@@ -1,31 +1,50 @@
1
+ import { CARD_ICON, CARD_WIDTH, FONT_SIZE, FONT_WEIGHT, firefoxFontSize, font, } from '../common/brand.js';
1
2
  import { Card } from '../common/Card.js';
2
3
  import { getLightDarkColors } from '../common/color.js';
4
+ import { formatRange } from '../common/date.js';
3
5
  import { CardError } from '../common/error.js';
4
6
  import { I18n } from '../common/I18n.js';
5
7
  import { icons, rankIcon } from '../common/icons.js';
6
8
  import { buildSearchFilter, clampValue } from '../common/ops.js';
7
- import { createTextNode, flexLayout, measureText } from '../common/render.js';
9
+ import { NUMBER_FORMATS, createTextNode, flexLayout, measureText } from '../common/render.js';
8
10
  import { atRule, cssComment, el, rule } from '../markup/index.js';
9
11
  import { statCardLocales, wakatimeCardLocales } from '../translations.js';
10
12
  const CARD_MIN_WIDTH = 287;
11
- const CARD_DEFAULT_WIDTH = 287;
12
- const RANK_CARD_MIN_WIDTH = 420;
13
- const RANK_CARD_DEFAULT_WIDTH = 450;
14
- const RANK_ONLY_CARD_MIN_WIDTH = 290;
15
- const RANK_ONLY_CARD_DEFAULT_WIDTH = 290;
16
- const STAT_FONT_SIZE = 14;
13
+ const CARD_DEFAULT_WIDTH = CARD_WIDTH.compact;
14
+ const RANK_CARD_DEFAULT_WIDTH = CARD_WIDTH.wide;
15
+ const RANK_ONLY_CARD_DEFAULT_WIDTH = CARD_WIDTH.compact;
16
+ const STAT_FONT_SIZE = FONT_SIZE.body;
17
17
  /** Padding the card keeps at its edges; matches `Card`'s own `paddingX`. */
18
18
  const CARD_PADDING_X = 25;
19
19
  /** How far a stat row is translated into the card; see `createTextNode`. */
20
20
  const STAT_ROW_X = 25;
21
21
  /** Room the rank ring needs at the right edge, so a value never runs into it. */
22
22
  const RANK_GUTTER = 120;
23
+ /** The ring's own `cx`, which its group's translation has to make up for. */
24
+ const RANK_CIRCLE_CX_OFFSET = 10;
23
25
  /** `createTextNode`'s own label offset, which it applies only when icons are shown. */
24
26
  const LABEL_X_OFFSET = 25;
25
27
  /** Smallest gap kept between the longest label and its value. */
26
28
  const LABEL_VALUE_GAP = 16;
27
29
  /** Rank indicators the card can draw; the api validates `rank_icon` against this. */
28
30
  const RANK_ICONS = ['default', 'github', 'percentile'];
31
+ /** Stats the card draws only when `show` names them. */
32
+ const SHOW_STATS = [
33
+ 'contributions',
34
+ 'prs_merged',
35
+ 'prs_merged_percentage',
36
+ 'reviews',
37
+ 'discussions_started',
38
+ 'discussions_answered',
39
+ 'prs_authored',
40
+ 'prs_commented',
41
+ 'prs_reviewed',
42
+ 'issues_authored',
43
+ 'issues_commented',
44
+ 'all_time_contribs',
45
+ ];
46
+ /** Stats the card always draws, and so the ones `hide` has anything to remove. */
47
+ const HIDE_STATS = ['stars', 'commits', 'prs', 'issues', 'contribs'];
29
48
  /** Long locales that need more space for text. Keep sorted alphabetically. */
30
49
  const LONG_LOCALES = new Set([
31
50
  'az',
@@ -80,22 +99,22 @@ const getProgressAnimation = ({ progress }) => atRule('@keyframes rankAnimation'
80
99
  */
81
100
  const getStyles = ({ textColor, iconColor, ringColor, show_icons, progress, }) => [
82
101
  rule('.stat', {
83
- font: `400 ${STAT_FONT_SIZE}px 'Segoe UI', Ubuntu, "Helvetica Neue", Sans-Serif`,
102
+ font: font('regular', 'body'),
84
103
  fill: textColor,
85
104
  'font-variant-numeric': 'tabular-nums',
86
105
  }),
87
- atRule('@supports(-moz-appearance: auto)', cssComment('Selector detects Firefox'), rule('.stat', { 'font-size': '12px' })),
106
+ firefoxFontSize(['.stat'], 'small'),
88
107
  rule('.stagger', { opacity: 0, animation: 'fadeInAnimation 0.3s ease-in-out forwards' }),
89
108
  rule('.rank-text', {
90
- font: "700 22px 'Segoe UI', Ubuntu, Sans-Serif",
109
+ font: font('bold', 'display'),
91
110
  fill: textColor,
92
111
  animation: 'scaleInAnimation 0.3s ease-in-out forwards',
93
112
  }),
94
- rule('.rank-percentile-header', { 'font-size': '14px' }),
95
- rule('.rank-percentile-text', { 'font-size': '16px' }),
113
+ rule('.rank-percentile-header', { 'font-size': `${String(FONT_SIZE.body)}px` }),
114
+ rule('.rank-percentile-text', { 'font-size': `${String(FONT_SIZE.lead)}px` }),
96
115
  cssComment('Labels recede so that the values read first.'),
97
- rule('.not_bold', { 'font-weight': 400, opacity: 0.75 }),
98
- rule('.bold', { 'font-weight': 600 }),
116
+ rule('.not_bold', { 'font-weight': FONT_WEIGHT.regular, opacity: 0.75 }),
117
+ rule('.bold', { 'font-weight': FONT_WEIGHT.semibold }),
99
118
  rule('.icon', {
100
119
  fill: iconColor,
101
120
  opacity: 0.75,
@@ -121,25 +140,25 @@ const getStyles = ({ textColor, iconColor, ringColor, show_icons, progress, }) =
121
140
  getProgressAnimation({ progress }),
122
141
  ];
123
142
  /**
124
- * Return the label for commits according to the selected options
125
- *
126
- * @returns The label corresponding to the options.
143
+ * @returns What the commit count covers, for the stat's label.
127
144
  */
128
- const getTotalCommitsYearLabel = (include_all_commits, commits_year, i18n) => include_all_commits
145
+ const getTotalCommitsRangeLabel = (include_all_commits, commitsRange, i18n) => include_all_commits
129
146
  ? ''
130
- : commits_year
131
- ? ` (${commits_year})`
147
+ : commitsRange
148
+ ? ` (${formatRange(commitsRange)})`
132
149
  : ` (${i18n.t('wakatimecard.lastyear')})`;
133
150
  /**
134
151
  * Renders the stats card.
135
152
  *
136
153
  * @returns The stats card SVG object.
137
154
  */
138
- const renderStatsCard = (stats, options = {}, username, repo = [], owner = []) => {
139
- const { name, totalStars, totalCommits, totalIssues, totalPRs, totalPRsMerged, mergedPRsPercentage, totalReviews, totalDiscussionsStarted, totalDiscussionsAnswered, contributedTo, allTimeContributedTo, totalPRsAuthored, totalPRsCommented, totalPRsReviewed, totalIssuesAuthored, totalIssuesCommented, totalContributions, rank, } = stats;
140
- const { hide = [], show_icons = false, hide_title = false, hide_border = false, card_width, hide_rank = false, include_all_commits = false, commits_year, line_height = 25, text_bold = true, custom_title, border_radius, number_format = 'short', number_precision, locale, disable_animations = false, rank_icon = 'default', show = [], } = options;
155
+ const renderCard = (stats, options = {}, username, repo = [], owner = []) => {
156
+ const { name, totalStars, totalCommits, commitsRange, totalIssues, totalPRs, totalPRsMerged, mergedPRsPercentage, totalReviews, totalDiscussionsStarted, totalDiscussionsAnswered, contributedTo, allTimeContributedTo, totalPRsAuthored, totalPRsCommented, totalPRsReviewed, totalIssuesAuthored, totalIssuesCommented, totalContributions, rank, } = stats;
157
+ const { hide = [], show_icons = false, hide_title = false, hide_border = false, card_width, hide_rank = false, include_all_commits = false, line_height = 25, text_bold = true, custom_title, border_radius, number_format = 'short', number_precision, locale, disable_animations = false, rank_icon = 'default', show = [], } = options;
141
158
  const lheight = Number.parseInt(String(line_height), 10);
142
159
  const { lightColors, darkColors } = getLightDarkColors(options);
160
+ // Typed against the exported list, so a stat drawn here cannot be missing from it.
161
+ const shows = (stat) => show.includes(stat);
143
162
  const apostrophe = /s$/i.test(name.trim()) ? '' : 's';
144
163
  const i18n = new I18n({
145
164
  locale,
@@ -157,7 +176,7 @@ const renderStatsCard = (stats, options = {}, username, repo = [], owner = []) =
157
176
  id: 'stars',
158
177
  },
159
178
  };
160
- if (show.includes('contributions')) {
179
+ if (shows('contributions')) {
161
180
  STATS['contributions'] = {
162
181
  icon: icons.contributions,
163
182
  label: i18n.t('statcard.contributions'),
@@ -167,7 +186,7 @@ const renderStatsCard = (stats, options = {}, username, repo = [], owner = []) =
167
186
  }
168
187
  STATS['commits'] = {
169
188
  icon: icons.commits,
170
- label: `${i18n.t('statcard.commits')}${getTotalCommitsYearLabel(include_all_commits, commits_year, i18n)}`,
189
+ label: `${i18n.t('statcard.commits')}${getTotalCommitsRangeLabel(include_all_commits, commitsRange, i18n)}`,
171
190
  value: totalCommits,
172
191
  id: 'commits',
173
192
  };
@@ -177,7 +196,7 @@ const renderStatsCard = (stats, options = {}, username, repo = [], owner = []) =
177
196
  value: totalPRs,
178
197
  id: 'prs',
179
198
  };
180
- if (show.includes('prs_merged')) {
199
+ if (shows('prs_merged')) {
181
200
  STATS['prs_merged'] = {
182
201
  icon: icons.prs_merged,
183
202
  label: i18n.t('statcard.prs-merged'),
@@ -185,7 +204,7 @@ const renderStatsCard = (stats, options = {}, username, repo = [], owner = []) =
185
204
  id: 'prs_merged',
186
205
  };
187
206
  }
188
- if (show.includes('prs_merged_percentage')) {
207
+ if (shows('prs_merged_percentage')) {
189
208
  STATS['prs_merged_percentage'] = {
190
209
  icon: icons.prs_merged_percentage,
191
210
  label: i18n.t('statcard.prs-merged-percentage'),
@@ -196,7 +215,7 @@ const renderStatsCard = (stats, options = {}, username, repo = [], owner = []) =
196
215
  unitSymbol: '%',
197
216
  };
198
217
  }
199
- if (show.includes('reviews')) {
218
+ if (shows('reviews')) {
200
219
  STATS['reviews'] = {
201
220
  icon: icons.reviews,
202
221
  label: i18n.t('statcard.reviews'),
@@ -210,7 +229,7 @@ const renderStatsCard = (stats, options = {}, username, repo = [], owner = []) =
210
229
  value: totalIssues,
211
230
  id: 'issues',
212
231
  };
213
- if (show.includes('discussions_started')) {
232
+ if (shows('discussions_started')) {
214
233
  STATS['discussions_started'] = {
215
234
  icon: icons.discussions_started,
216
235
  label: i18n.t('statcard.discussions-started'),
@@ -218,7 +237,7 @@ const renderStatsCard = (stats, options = {}, username, repo = [], owner = []) =
218
237
  id: 'discussions_started',
219
238
  };
220
239
  }
221
- if (show.includes('discussions_answered')) {
240
+ if (shows('discussions_answered')) {
222
241
  STATS['discussions_answered'] = {
223
242
  icon: icons.discussions_answered,
224
243
  label: i18n.t('statcard.discussions-answered'),
@@ -228,7 +247,7 @@ const renderStatsCard = (stats, options = {}, username, repo = [], owner = []) =
228
247
  }
229
248
  const repoFilter = encodeURIComponent(buildSearchFilter(repo, owner));
230
249
  const encodedUsername = encodeURIComponent(username ?? '');
231
- if (show.includes('prs_authored')) {
250
+ if (shows('prs_authored')) {
232
251
  STATS['prs_authored'] = {
233
252
  icon: icons.prs,
234
253
  label: i18n.t('statcard.prs-authored'),
@@ -237,7 +256,7 @@ const renderStatsCard = (stats, options = {}, username, repo = [], owner = []) =
237
256
  link: `https://github.com/search?q=${repoFilter}author%3A${encodedUsername}&type=pullrequests`,
238
257
  };
239
258
  }
240
- if (show.includes('prs_commented')) {
259
+ if (shows('prs_commented')) {
241
260
  STATS['prs_commented'] = {
242
261
  icon: icons.comments,
243
262
  label: i18n.t('statcard.prs-commented'),
@@ -246,7 +265,7 @@ const renderStatsCard = (stats, options = {}, username, repo = [], owner = []) =
246
265
  link: `https://github.com/search?q=${repoFilter}commenter%3A${encodedUsername}+-author%3A${encodedUsername}&type=pullrequests`,
247
266
  };
248
267
  }
249
- if (show.includes('prs_reviewed')) {
268
+ if (shows('prs_reviewed')) {
250
269
  STATS['prs_reviewed'] = {
251
270
  icon: icons.reviews,
252
271
  label: i18n.t('statcard.prs-reviewed'),
@@ -255,7 +274,7 @@ const renderStatsCard = (stats, options = {}, username, repo = [], owner = []) =
255
274
  link: `https://github.com/search?q=${repoFilter}reviewed-by%3A${encodedUsername}+-author%3A${encodedUsername}&type=pullrequests`,
256
275
  };
257
276
  }
258
- if (show.includes('issues_authored')) {
277
+ if (shows('issues_authored')) {
259
278
  STATS['issues_authored'] = {
260
279
  icon: icons.issues,
261
280
  label: i18n.t('statcard.issues-authored'),
@@ -264,7 +283,7 @@ const renderStatsCard = (stats, options = {}, username, repo = [], owner = []) =
264
283
  link: `https://github.com/search?q=${repoFilter}author%3A${encodedUsername}&type=issues`,
265
284
  };
266
285
  }
267
- if (show.includes('issues_commented')) {
286
+ if (shows('issues_commented')) {
268
287
  STATS['issues_commented'] = {
269
288
  icon: icons.discussions_started,
270
289
  label: i18n.t('statcard.issues-commented'),
@@ -274,14 +293,14 @@ const renderStatsCard = (stats, options = {}, username, repo = [], owner = []) =
274
293
  };
275
294
  }
276
295
  STATS['contribs'] = {
277
- icon: icons.contribs,
296
+ icon: icons.repo,
278
297
  label: i18n.t('statcard.contribs'),
279
298
  value: contributedTo,
280
299
  id: 'contribs',
281
300
  };
282
- if (show.includes('all_time_contribs')) {
301
+ if (shows('all_time_contribs')) {
283
302
  STATS['all_time_contribs'] = {
284
- icon: icons.contribs,
303
+ icon: icons.repo,
285
304
  label: i18n.t('statcard.all-time-contribs'),
286
305
  value: allTimeContributedTo,
287
306
  id: 'all_time_contribs',
@@ -304,27 +323,21 @@ const renderStatsCard = (stats, options = {}, username, repo = [], owner = []) =
304
323
  const progress = 100 - rank.percentile;
305
324
  const calculateTextWidth = () => measureText(custom_title ||
306
325
  (visibleStats.length > 0 ? i18n.t('statcard.title') : i18n.t('statcard.ranktitle')));
307
- /*
308
- When hide_rank=true, the minimum card width is 270 px + the title length and padding.
309
- When hide_rank=false, the minimum card_width is 340 px + the icon width (if show_icons=true).
310
- Numbers are picked by looking at existing dimensions on production.
311
- */
312
326
  const iconWidth = show_icons && visibleStats.length > 0 ? 16 + /* padding */ 1 : 0;
313
- const minCardWidth = (hide_rank
314
- ? clampValue(50 /* padding */ + calculateTextWidth() * 2, CARD_MIN_WIDTH, Infinity)
315
- : visibleStats.length > 0
316
- ? RANK_CARD_MIN_WIDTH
317
- : RANK_ONLY_CARD_MIN_WIDTH) + iconWidth;
318
- const defaultCardWidth = (hide_rank
327
+ // The icons ride inside the step's own slack, so a default card lands on the width grid;
328
+ // only the minimum, which has no slack, still has to make room for them.
329
+ const defaultCardWidth = hide_rank
319
330
  ? CARD_DEFAULT_WIDTH
320
331
  : visibleStats.length > 0
321
332
  ? RANK_CARD_DEFAULT_WIDTH
322
- : RANK_ONLY_CARD_DEFAULT_WIDTH) + iconWidth;
323
- const width = card_width
324
- ? Number.isNaN(card_width)
325
- ? Math.max(defaultCardWidth, minCardWidth)
326
- : card_width
327
- : Math.max(defaultCardWidth, minCardWidth);
333
+ : RANK_ONLY_CARD_DEFAULT_WIDTH;
334
+ // Only a card sized by its own title can outgrow its step: the ranked layouts are fixed,
335
+ // so their step is also their floor.
336
+ const fittedWidth = hide_rank
337
+ ? Math.max(defaultCardWidth, clampValue(50 /* padding */ + calculateTextWidth() * 2, CARD_MIN_WIDTH, Infinity) +
338
+ iconWidth)
339
+ : defaultCardWidth;
340
+ const width = card_width ? (Number.isNaN(card_width) ? fittedWidth : card_width) : fittedWidth;
328
341
  // A value ends at the card's inner edge, clear of the rank ring — or right after
329
342
  // the longest label, on a card too narrow for that.
330
343
  const widestLabel = visibleStats.length > 0
@@ -351,6 +364,7 @@ const renderStatsCard = (stats, options = {}, username, repo = [], owner = []) =
351
364
  const card = new Card({
352
365
  customTitle: custom_title,
353
366
  defaultTitle: visibleStats.length > 0 ? i18n.t('statcard.title') : i18n.t('statcard.ranktitle'),
367
+ titlePrefixIcon: CARD_ICON.stats,
354
368
  width,
355
369
  height,
356
370
  border_radius,
@@ -364,43 +378,31 @@ const renderStatsCard = (stats, options = {}, username, repo = [], owner = []) =
364
378
  card.disableAnimations();
365
379
  }
366
380
  /**
367
- * Calculates the right rank circle translation values such that the rank circle
368
- * keeps respecting the following padding:
369
- *
370
- * width > RANK_CARD_DEFAULT_WIDTH: The default right padding of 70 px will be used.
371
- * width < RANK_CARD_DEFAULT_WIDTH: The left and right padding will be enlarged
372
- * equally from a certain minimum at RANK_CARD_MIN_WIDTH.
381
+ * The ring sits centred in the same gutter the values are kept clear of, so the gap
382
+ * between them holds at every width; a rank-only card has no values, so it centres.
373
383
  *
374
384
  * @returns Rank circle translation value.
375
385
  */
376
- const calculateRankXTranslation = () => {
377
- if (visibleStats.length > 0) {
378
- const minXTranslation = RANK_CARD_MIN_WIDTH + iconWidth - 70;
379
- if (width > RANK_CARD_DEFAULT_WIDTH) {
380
- const xMaxExpansion = minXTranslation + (450 - minCardWidth) / 2;
381
- return xMaxExpansion + width - RANK_CARD_DEFAULT_WIDTH;
382
- }
383
- return minXTranslation + (width - minCardWidth) / 2;
384
- }
385
- return width / 2 + 20 - 10;
386
- };
386
+ const calculateRankXTranslation = () => visibleStats.length > 0
387
+ ? width - CARD_PADDING_X - RANK_GUTTER / 2 + RANK_CIRCLE_CX_OFFSET
388
+ : width / 2 + 20 - 10;
387
389
  // Conditionally rendered elements
388
390
  const rankCircle = !hide_rank &&
389
391
  el('g', {
390
392
  'data-testid': 'rank-circle',
391
393
  transform: `translate(${calculateRankXTranslation()}, ${height / 2 - 50})`,
392
- }, el('circle', { class: 'rank-circle-rim', cx: -10, cy: 8, r: 40 }), el('circle', { class: 'rank-circle', cx: -10, cy: 8, r: 40 }), el('g', { class: 'rank-text' }, rankIcon(rank_icon, rank.level, rank.percentile)));
394
+ }, el('circle', { class: 'rank-circle-rim', cx: -RANK_CIRCLE_CX_OFFSET, cy: 8, r: 40 }), el('circle', { class: 'rank-circle', cx: -RANK_CIRCLE_CX_OFFSET, cy: 8, r: 40 }), el('g', { class: 'rank-text' }, rankIcon(rank_icon, rank.level, rank.percentile)));
393
395
  // Accessibility Labels
394
396
  const labels = visibleStats
395
397
  .map(([key, stat]) => {
396
398
  if (key === 'commits') {
397
- return `${i18n.t('statcard.commits')} ${getTotalCommitsYearLabel(include_all_commits, commits_year, i18n)} : ${stat.value}`;
399
+ return `${i18n.t('statcard.commits')} ${getTotalCommitsRangeLabel(include_all_commits, commitsRange, i18n)} : ${stat.value}`;
398
400
  }
399
401
  return `${stat.label}: ${stat.value}`;
400
402
  })
401
403
  .join(', ');
402
404
  card.setAccessibilityLabel({
403
- title: `${card.title}, Rank: ${rank.level}`,
405
+ title: `${card.title}, ${i18n.t('statcard.rank')}: ${rank.level}`,
404
406
  desc: labels,
405
407
  });
406
408
  return card.render([
@@ -408,4 +410,17 @@ const renderStatsCard = (stats, options = {}, username, repo = [], owner = []) =
408
410
  el('svg', { x: 0, y: 0 }, flexLayout({ items: statItems, gap: lheight, direction: 'column' })),
409
411
  ]);
410
412
  };
411
- export { RANK_ICONS, renderStatsCard };
413
+ /**
414
+ * The card, and the values each of its options accepts, keyed by the option's own name.
415
+ * They ride on the renderer so a list cannot be found without what draws it;
416
+ * the api handler forwards them onto its own export, which is what a UI reads.
417
+ */
418
+ const renderStatsCard = Object.assign(renderCard, {
419
+ OPTIONS: {
420
+ rank_icon: RANK_ICONS,
421
+ show: SHOW_STATS,
422
+ hide: HIDE_STATS,
423
+ number_format: NUMBER_FORMATS,
424
+ },
425
+ });
426
+ export { renderStatsCard };
@@ -122,6 +122,17 @@ declare const getDefaultLanguagesCountByLayout: ({ layout, hide_progress, }: {
122
122
  *
123
123
  * @returns Language card SVG object.
124
124
  */
125
- declare const renderTopLanguages: (topLangs: TopLangData, options?: CardOptions<TopLangOptions>) => string;
126
- export { getLongestLang, degreesToRadians, radiansToDegrees, polarToCartesian, cartesianToPolar, getCircleLength, calculateCompactLayoutHeight, calculateNormalLayoutHeight, calculateDonutLayoutHeight, calculateDonutVerticalLayoutHeight, calculatePieLayoutHeight, donutCenterTranslation, trimTopLanguages, renderTopLanguages, MIN_CARD_WIDTH, getDefaultLanguagesCountByLayout, TOP_LANG_LAYOUTS, TOP_LANG_STATS_FORMATS, };
125
+ declare const renderCard: (topLangs: TopLangData, options?: CardOptions<TopLangOptions>) => string;
126
+ /**
127
+ * The card, and the values each of its options accepts, keyed by the option's own name.
128
+ * They ride on the renderer so a list cannot be found without what draws it;
129
+ * the api handler forwards them onto its own export, which is what a UI reads.
130
+ */
131
+ declare const renderTopLanguages: typeof renderCard & {
132
+ OPTIONS: {
133
+ layout: readonly ["compact", "normal", "donut", "donut-vertical", "pie"];
134
+ stats_format: readonly ["percentages", "bytes"];
135
+ };
136
+ };
137
+ export { getLongestLang, degreesToRadians, radiansToDegrees, polarToCartesian, cartesianToPolar, getCircleLength, calculateCompactLayoutHeight, calculateNormalLayoutHeight, calculateDonutLayoutHeight, calculateDonutVerticalLayoutHeight, calculatePieLayoutHeight, donutCenterTranslation, trimTopLanguages, MIN_CARD_WIDTH, getDefaultLanguagesCountByLayout, renderTopLanguages, };
127
138
  //# sourceMappingURL=top-languages.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"top-languages.d.ts","sourceRoot":"","sources":["../../src/cards/top-languages.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"top-languages.d.ts","sourceRoot":"","sources":["../../src/cards/top-languages.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { CARD_ICON, CARD_WIDTH, FONT_WEIGHT, firefoxFontSize, font } from '../common/brand.js';
1
2
  import { Card } from '../common/Card.js';
2
3
  import { getLightDarkColors, isPrefixedHexColor } from '../common/color.js';
3
4
  import { formatBytes } from '../common/fmt.js';
@@ -5,9 +6,9 @@ import { I18n } from '../common/I18n.js';
5
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
- import { atRule, cssComment, el, rule } from '../markup/index.js';
9
+ import { atRule, el, rule } from '../markup/index.js';
9
10
  import { langCardLocales } from '../translations.js';
10
- const DEFAULT_CARD_WIDTH = 300;
11
+ const DEFAULT_CARD_WIDTH = CARD_WIDTH.compact;
11
12
  const MIN_CARD_WIDTH = 280;
12
13
  const CARD_PADDING = 25;
13
14
  const COMPACT_LAYOUT_BASE_HEIGHT = 90;
@@ -15,6 +16,8 @@ const MAXIMUM_LANGS_COUNT = 20;
15
16
  const NORMAL_LAYOUT_DEFAULT_LANGS_COUNT = 5;
16
17
  const COMPACT_LAYOUT_DEFAULT_LANGS_COUNT = 6;
17
18
  const DONUT_LAYOUT_DEFAULT_LANGS_COUNT = 5;
19
+ /** Room for the chart that sits beside the language list. */
20
+ const DONUT_EXTRA_WIDTH = 50;
18
21
  const PIE_LAYOUT_DEFAULT_LANGS_COUNT = 6;
19
22
  const DONUT_VERTICAL_LAYOUT_DEFAULT_LANGS_COUNT = 6;
20
23
  /** Layouts the card can draw; the api validates `layout` against this. */
@@ -499,7 +502,7 @@ const getDefaultLanguagesCountByLayout = ({ layout, hide_progress, }) => {
499
502
  *
500
503
  * @returns Language card SVG object.
501
504
  */
502
- const renderTopLanguages = (topLangs, options = {}) => {
505
+ const renderCard = (topLangs, options = {}) => {
503
506
  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;
504
507
  const i18n = new I18n({
505
508
  locale,
@@ -537,7 +540,9 @@ const renderTopLanguages = (topLangs, options = {}) => {
537
540
  }
538
541
  else if (layout === 'donut') {
539
542
  height = calculateDonutLayoutHeight(langs.length);
540
- width += 50; // padding
543
+ // A donut defaults a step wider so the chart has its room; a width asked for
544
+ // explicitly keeps the extra it has always been given instead of being snapped.
545
+ width = width === DEFAULT_CARD_WIDTH ? CARD_WIDTH.standard : width + DONUT_EXTRA_WIDTH;
541
546
  finalLayout = renderDonutLayout(langs, width, totalLanguageSize, stats_format, hide_values);
542
547
  }
543
548
  else {
@@ -546,6 +551,7 @@ const renderTopLanguages = (topLangs, options = {}) => {
546
551
  const card = new Card({
547
552
  customTitle: custom_title,
548
553
  defaultTitle: i18n.t('langcard.title'),
554
+ titlePrefixIcon: CARD_ICON.topLanguages,
549
555
  width,
550
556
  height,
551
557
  border_radius,
@@ -564,12 +570,12 @@ const renderTopLanguages = (topLangs, options = {}) => {
564
570
  rule('to', { width: 'calc(100%-100px)' })),
565
571
  atRule('@keyframes growWidthAnimation', rule('from', { width: 0 }), rule('to', { width: '100%' })),
566
572
  rule('.stat', {
567
- font: `600 14px 'Segoe UI', Ubuntu, "Helvetica Neue", Sans-Serif`,
573
+ font: font('regular', 'body'),
568
574
  fill: textColor,
569
575
  }),
570
- atRule('@supports(-moz-appearance: auto)', cssComment('Selector detects Firefox'), rule('.stat', { 'font-size': '12px' })),
571
- rule('.bold', { 'font-weight': 700 }),
572
- rule('.lang-name', { font: '400 11px "Segoe UI", Ubuntu, Sans-Serif', fill: textColor }),
576
+ firefoxFontSize(['.stat'], 'small'),
577
+ rule('.bold', { 'font-weight': FONT_WEIGHT.semibold }),
578
+ rule('.lang-name', { font: font('regular', 'micro'), fill: textColor }),
573
579
  rule('.stagger', { opacity: 0, animation: 'fadeInAnimation 0.3s ease-in-out forwards' }),
574
580
  rule('#rect-mask rect', { animation: 'slideInAnimation 1s ease-in-out forwards' }),
575
581
  rule('.lang-progress', { animation: 'growWidthAnimation 0.6s ease-in-out forwards' }),
@@ -594,4 +600,15 @@ const renderTopLanguages = (topLangs, options = {}) => {
594
600
  }
595
601
  return card.render(el('svg', { 'data-testid': 'lang-items', x: CARD_PADDING }, finalLayout));
596
602
  };
597
- export { getLongestLang, degreesToRadians, radiansToDegrees, polarToCartesian, cartesianToPolar, getCircleLength, calculateCompactLayoutHeight, calculateNormalLayoutHeight, calculateDonutLayoutHeight, calculateDonutVerticalLayoutHeight, calculatePieLayoutHeight, donutCenterTranslation, trimTopLanguages, renderTopLanguages, MIN_CARD_WIDTH, getDefaultLanguagesCountByLayout, TOP_LANG_LAYOUTS, TOP_LANG_STATS_FORMATS, };
603
+ /**
604
+ * The card, and the values each of its options accepts, keyed by the option's own name.
605
+ * They ride on the renderer so a list cannot be found without what draws it;
606
+ * the api handler forwards them onto its own export, which is what a UI reads.
607
+ */
608
+ const renderTopLanguages = Object.assign(renderCard, {
609
+ OPTIONS: {
610
+ layout: TOP_LANG_LAYOUTS,
611
+ stats_format: TOP_LANG_STATS_FORMATS,
612
+ },
613
+ });
614
+ export { getLongestLang, degreesToRadians, radiansToDegrees, polarToCartesian, cartesianToPolar, getCircleLength, calculateCompactLayoutHeight, calculateNormalLayoutHeight, calculateDonutLayoutHeight, calculateDonutVerticalLayoutHeight, calculatePieLayoutHeight, donutCenterTranslation, trimTopLanguages, MIN_CARD_WIDTH, getDefaultLanguagesCountByLayout, renderTopLanguages, };
@@ -24,6 +24,17 @@ interface WakaTimeOptions extends CommonCardOptions {
24
24
  *
25
25
  * @returns WakaTime card SVG.
26
26
  */
27
- declare const renderWakatimeCard: (stats?: Partial<WakaTimeData>, options?: CardOptions<WakaTimeOptions>) => string;
28
- export { DISPLAY_FORMATS, WAKATIME_LAYOUTS, renderWakatimeCard };
27
+ declare const renderCard: (stats?: Partial<WakaTimeData>, options?: CardOptions<WakaTimeOptions>) => string;
28
+ /**
29
+ * The card, and the values each of its options accepts, keyed by the option's own name.
30
+ * They ride on the renderer so a list cannot be found without what draws it;
31
+ * the api handler forwards them onto its own export, which is what a UI reads.
32
+ */
33
+ declare const renderWakatimeCard: typeof renderCard & {
34
+ OPTIONS: {
35
+ layout: readonly ["compact", "normal"];
36
+ display_format: readonly ["time", "percent"];
37
+ };
38
+ };
39
+ export { renderWakatimeCard };
29
40
  //# sourceMappingURL=wakatime.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"wakatime.d.ts","sourceRoot":"","sources":["../../src/cards/wakatime.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAgB,MAAM,sBAAsB,CAAC;AAKvE,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAWnE,0EAA0E;AAC1E,QAAA,MAAM,gBAAgB,YAAI,SAAS,EAAE,QAAQ,CAAU,CAAC;AACxD,KAAK,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAExD,yFAAyF;AACzF,QAAA,MAAM,eAAe,YAAI,MAAM,EAAE,SAAS,CAAU,CAAC;AACrD,KAAK,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtD,UAAU,eAAgB,SAAQ,iBAAiB;IACjD,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;IACpB,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,aAAa,EAAE,OAAO,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,cAAc,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,aAAa,CAAC;IAC9B,kBAAkB,EAAE,OAAO,CAAC;CAC7B;AAoND;;;;GAIG;AACH,QAAA,MAAM,kBAAkB,WACf,OAAO,CAAC,YAAY,CAAC,YACnB,WAAW,CAAC,eAAe,CAAC,KACpC,MAgNF,CAAC;AAEF,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,CAAC"}
1
+ {"version":3,"file":"wakatime.d.ts","sourceRoot":"","sources":["../../src/cards/wakatime.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,YAAY,EAAgB,MAAM,sBAAsB,CAAC;AAKvE,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAWnE,0EAA0E;AAC1E,QAAA,MAAM,gBAAgB,YAAI,SAAS,EAAE,QAAQ,CAAU,CAAC;AACxD,KAAK,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAExD,yFAAyF;AACzF,QAAA,MAAM,eAAe,YAAI,MAAM,EAAE,SAAS,CAAU,CAAC;AACrD,KAAK,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtD,UAAU,eAAgB,SAAQ,iBAAiB;IACjD,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;IACpB,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,aAAa,EAAE,OAAO,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,cAAc,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,aAAa,CAAC;IAC9B,kBAAkB,EAAE,OAAO,CAAC;CAC7B;AAgND;;;;GAIG;AACH,QAAA,MAAM,UAAU,WACP,OAAO,CAAC,YAAY,CAAC,YACnB,WAAW,CAAC,eAAe,CAAC,KACpC,MAiNF,CAAC;AAEF;;;;GAIG;AACH,QAAA,MAAM,kBAAkB;;QAEpB,MAAM;QACN,cAAc;;CAEhB,CAAC;AAEH,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
@@ -1,12 +1,13 @@
1
+ import { CARD_ICON, CARD_WIDTH, FONT_WEIGHT, firefoxFontSize, font } from '../common/brand.js';
1
2
  import { Card } from '../common/Card.js';
2
3
  import { getLightDarkColors, isPrefixedHexColor } from '../common/color.js';
3
4
  import { I18n } from '../common/I18n.js';
4
5
  import { getLanguageColor } from '../common/languageColors.js';
5
6
  import { clampValue, lowercaseTrim } from '../common/ops.js';
6
7
  import { createProgressNode, flexLayout } from '../common/render.js';
7
- import { atRule, cssComment, el, rule } from '../markup/index.js';
8
+ import { atRule, el, rule } from '../markup/index.js';
8
9
  import { wakatimeCardLocales } from '../translations.js';
9
- const DEFAULT_CARD_WIDTH = 495;
10
+ const DEFAULT_CARD_WIDTH = CARD_WIDTH.wide;
10
11
  const MIN_CARD_WIDTH = 250;
11
12
  const COMPACT_LAYOUT_MIN_WIDTH = 400;
12
13
  const DEFAULT_LINE_HEIGHT = 25;
@@ -118,13 +119,13 @@ const getStyles = ({ textColor }) => {
118
119
  }
119
120
  return [
120
121
  rule('.stat', {
121
- font: `600 14px 'Segoe UI', Ubuntu, "Helvetica Neue", Sans-Serif`,
122
+ font: font('regular', 'body'),
122
123
  fill: textColor,
123
124
  }),
124
- atRule('@supports(-moz-appearance: auto)', cssComment('Selector detects Firefox'), rule('.stat', { 'font-size': '12px' })),
125
+ firefoxFontSize(['.stat'], 'small'),
125
126
  rule('.stagger', { opacity: 0, animation: 'fadeInAnimation 0.3s ease-in-out forwards' }),
126
- rule('.not_bold', { 'font-weight': 400 }),
127
- rule('.bold', { 'font-weight': 700 }),
127
+ rule('.not_bold', { 'font-weight': FONT_WEIGHT.regular }),
128
+ rule('.bold', { 'font-weight': FONT_WEIGHT.semibold }),
128
129
  ];
129
130
  };
130
131
  /** A bar the same color as the text behind it would be invisible, so it goes transparent. */
@@ -145,7 +146,7 @@ const normalizeCardWidth = ({ value, layout, }) => {
145
146
  *
146
147
  * @returns WakaTime card SVG.
147
148
  */
148
- const renderWakatimeCard = (stats = {}, options = {}) => {
149
+ const renderCard = (stats = {}, options = {}) => {
149
150
  let { languages = [] } = stats;
150
151
  const { hide_title = false, hide_border = false, card_width, hide, line_height = DEFAULT_LINE_HEIGHT, hide_progress, custom_title, locale, layout, langs_count = languages.length, border_radius, display_format = 'time', disable_animations, } = options;
151
152
  const normalizedWidth = normalizeCardWidth({ value: card_width, layout });
@@ -261,6 +262,7 @@ const renderWakatimeCard = (stats = {}, options = {}) => {
261
262
  const card = new Card({
262
263
  customTitle: custom_title,
263
264
  defaultTitle: titleText,
265
+ titlePrefixIcon: CARD_ICON.wakatime,
264
266
  width: normalizedWidth,
265
267
  height,
266
268
  border_radius,
@@ -279,7 +281,7 @@ const renderWakatimeCard = (stats = {}, options = {}) => {
279
281
  // declaration and animate to the rect's own width, which is the reveal the mask wants.
280
282
  rule('to', { width: 'calc(100%-100px)' })),
281
283
  atRule('@keyframes growWidthAnimation', rule('from', { width: 0 }), rule('to', { width: '100%' })),
282
- rule('.lang-name', { font: "400 11px 'Segoe UI', Ubuntu, Sans-Serif", fill: textColor }),
284
+ rule('.lang-name', { font: font('regular', 'micro'), fill: textColor }),
283
285
  rule('#rect-mask rect', { animation: 'slideInAnimation 1s ease-in-out forwards' }),
284
286
  rule('.lang-progress', {
285
287
  animation: 'growWidthAnimation 0.6s ease-in-out forwards',
@@ -306,4 +308,15 @@ const renderWakatimeCard = (stats = {}, options = {}) => {
306
308
  });
307
309
  return card.render(el('svg', { x: 0, y: 0, width: '100%' }, finalLayout));
308
310
  };
309
- export { DISPLAY_FORMATS, WAKATIME_LAYOUTS, renderWakatimeCard };
311
+ /**
312
+ * The card, and the values each of its options accepts, keyed by the option's own name.
313
+ * They ride on the renderer so a list cannot be found without what draws it;
314
+ * the api handler forwards them onto its own export, which is what a UI reads.
315
+ */
316
+ const renderWakatimeCard = Object.assign(renderCard, {
317
+ OPTIONS: {
318
+ layout: WAKATIME_LAYOUTS,
319
+ display_format: DISPLAY_FORMATS,
320
+ },
321
+ });
322
+ export { renderWakatimeCard };
@@ -59,6 +59,15 @@ declare class Card {
59
59
  setTitle(text: string): void;
60
60
  /** @returns The rendered card title. */
61
61
  renderTitle(): MarkupElement;
62
+ /** Where the body starts, and so where the title band ends. */
63
+ private get bodyOffset();
64
+ /**
65
+ * The band behind the title. Its top corners follow the card's own and its bottom
66
+ * ones stay square, so it meets the body as an edge rather than a floating box.
67
+ *
68
+ * @returns The rendered title band.
69
+ */
70
+ private renderTitleBand;
62
71
  /** @returns The card's gradient definitions, or nothing when no color is a gradient. */
63
72
  renderGradient(): Child;
64
73
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"Card.d.ts","sourceRoot":"","sources":["../../src/common/Card.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAIzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAG7C;;GAEG;AACH,KAAK,cAAc,GAAG,CAAC,MAAM,EAAE,UAAU,KAAK,KAAK,CAAC,QAAQ,CAAC,CAAC;AA2C9D,cAAM,IAAI;IACR,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE;QAAE,KAAK,EAAE,UAAU,CAAC;QAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAAA;KAAE,CAAC;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IACrB,OAAO,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,KAAK,CAAC;IACvB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,YAAY,EACV,KAAW,EACX,MAAY,EACZ,aAAiB,EACjB,MAAiD,EACjD,WAAW,EACX,YAAiB,EACjB,eAAe,GAChB,EAAE;QACD,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAEhB,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACnC,MAAM,CAAC,EAAE;YAAE,KAAK,EAAE,UAAU,CAAC;YAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAAA;SAAE,CAAC;QACxD,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACjC,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,eAAe,CAAC,EAAE,KAAK,CAAC;KACzB,EAqBA;IAED,iBAAiB,IAAI,IAAI,CAExB;IAED,qBAAqB,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAG5E;IAED;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QAAE,KAAK,EAAE,cAAc,CAAC;QAAC,IAAI,EAAE,cAAc,CAAA;KAAE,GAAG,IAAI,CAG7E;IAED,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAElC;IAED,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAQjC;IAED,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAE3B;IAED,wCAAwC;IACxC,WAAW,IAAI,aAAa,CA4B3B;IAED,wFAAwF;IACxF,cAAc,IAAI,KAAK,CAwBtB;IAED;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAkB5B,kCAAkC;IAClC,MAAM,CAAC,IAAI,EAAE,KAAK,GAAG,MAAM,CA2E1B;CACF;AAED,OAAO,EAAE,IAAI,EAAE,CAAC"}
1
+ {"version":3,"file":"Card.d.ts","sourceRoot":"","sources":["../../src/common/Card.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAKzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAG7C;;GAEG;AACH,KAAK,cAAc,GAAG,CAAC,MAAM,EAAE,UAAU,KAAK,KAAK,CAAC,QAAQ,CAAC,CAAC;AA2C9D,cAAM,IAAI;IACR,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE;QAAE,KAAK,EAAE,UAAU,CAAC;QAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAAA;KAAE,CAAC;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IACrB,OAAO,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,KAAK,CAAC;IACvB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,YAAY,EACV,KAAW,EACX,MAAY,EACZ,aAAiB,EACjB,MAAiD,EACjD,WAAW,EACX,YAAiB,EACjB,eAAe,GAChB,EAAE;QACD,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAEhB,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACnC,MAAM,CAAC,EAAE;YAAE,KAAK,EAAE,UAAU,CAAC;YAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAAA;SAAE,CAAC;QACxD,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACjC,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,eAAe,CAAC,EAAE,KAAK,CAAC;KACzB,EAqBA;IAED,iBAAiB,IAAI,IAAI,CAExB;IAED,qBAAqB,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAG5E;IAED;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QAAE,KAAK,EAAE,cAAc,CAAC;QAAC,IAAI,EAAE,cAAc,CAAA;KAAE,GAAG,IAAI,CAG7E;IAED,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAElC;IAED,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAQjC;IAED,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAE3B;IAED,wCAAwC;IACxC,WAAW,IAAI,aAAa,CAqC3B;IAED,+DAA+D;IAC/D,OAAO,KAAK,UAAU,GAErB;IAED;;;;;OAKG;IACH,OAAO,CAAC,eAAe;IAYvB,wFAAwF;IACxF,cAAc,IAAI,KAAK,CAwBtB;IAED;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAqB5B,kCAAkC;IAClC,MAAM,CAAC,IAAI,EAAE,KAAK,GAAG,MAAM,CAoF1B;CACF;AAED,OAAO,EAAE,IAAI,EAAE,CAAC"}