@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.
- package/build/api/contributed-to.d.ts +23 -0
- package/build/api/contributed-to.d.ts.map +1 -0
- package/build/api/contributed-to.js +43 -0
- package/build/api/gist.d.ts +8 -15
- package/build/api/gist.d.ts.map +1 -1
- package/build/api/gist.js +14 -29
- package/build/api/handler.d.ts +16 -0
- package/build/api/handler.d.ts.map +1 -0
- package/build/api/handler.js +29 -0
- package/build/api/index.d.ts +1 -0
- package/build/api/index.d.ts.map +1 -1
- package/build/api/index.js +1 -0
- package/build/api/params.d.ts +11 -5
- package/build/api/params.d.ts.map +1 -1
- package/build/api/params.js +26 -6
- package/build/api/pin.d.ts +11 -15
- package/build/api/pin.d.ts.map +1 -1
- package/build/api/pin.js +34 -46
- package/build/api/stats.d.ts +15 -22
- package/build/api/stats.d.ts.map +1 -1
- package/build/api/stats.js +59 -72
- package/build/api/top-langs.d.ts +11 -22
- package/build/api/top-langs.d.ts.map +1 -1
- package/build/api/top-langs.js +34 -51
- package/build/api/wakatime.d.ts +10 -24
- package/build/api/wakatime.d.ts.map +1 -1
- package/build/api/wakatime.js +27 -47
- package/build/cards/contributed-to.d.ts +19 -0
- package/build/cards/contributed-to.d.ts.map +1 -0
- package/build/cards/contributed-to.js +242 -0
- package/build/cards/gist.d.ts +1 -0
- package/build/cards/gist.d.ts.map +1 -1
- package/build/cards/gist.js +13 -9
- package/build/cards/repo.d.ts +12 -1
- package/build/cards/repo.d.ts.map +1 -1
- package/build/cards/repo.js +40 -18
- package/build/cards/stats.d.ts +15 -3
- package/build/cards/stats.d.ts.map +1 -1
- package/build/cards/stats.js +91 -76
- package/build/cards/top-languages.d.ts +13 -2
- package/build/cards/top-languages.d.ts.map +1 -1
- package/build/cards/top-languages.js +26 -9
- package/build/cards/wakatime.d.ts +13 -2
- package/build/cards/wakatime.d.ts.map +1 -1
- package/build/cards/wakatime.js +22 -9
- package/build/common/Card.d.ts +9 -0
- package/build/common/Card.d.ts.map +1 -1
- package/build/common/Card.js +44 -9
- package/build/common/I18n.d.ts +1 -1
- package/build/common/I18n.d.ts.map +1 -1
- package/build/common/I18n.js +4 -2
- package/build/common/brand.d.ts +76 -0
- package/build/common/brand.d.ts.map +1 -0
- package/build/common/brand.js +73 -0
- package/build/common/constants.d.ts +3 -1
- package/build/common/constants.d.ts.map +1 -1
- package/build/common/constants.js +3 -1
- package/build/common/date.d.ts +50 -3
- package/build/common/date.d.ts.map +1 -1
- package/build/common/date.js +104 -2
- package/build/common/icons.d.ts +4 -2
- package/build/common/icons.d.ts.map +1 -1
- package/build/common/icons.js +10 -5
- package/build/common/ops.d.ts +8 -3
- package/build/common/ops.d.ts.map +1 -1
- package/build/common/ops.js +8 -14
- package/build/common/render.d.ts +7 -1
- package/build/common/render.d.ts.map +1 -1
- package/build/common/render.js +60 -58
- package/build/fetchers/contributed-to.d.ts +62 -0
- package/build/fetchers/contributed-to.d.ts.map +1 -0
- package/build/fetchers/contributed-to.js +184 -0
- package/build/fetchers/graphql-error.d.ts +13 -0
- package/build/fetchers/graphql-error.d.ts.map +1 -0
- package/build/fetchers/graphql-error.js +29 -0
- package/build/fetchers/index.d.ts +2 -1
- package/build/fetchers/index.d.ts.map +1 -1
- package/build/fetchers/index.js +1 -0
- package/build/fetchers/stats.d.ts +14 -8
- package/build/fetchers/stats.d.ts.map +1 -1
- package/build/fetchers/stats.js +72 -195
- package/build/fetchers/top-languages.d.ts.map +1 -1
- package/build/fetchers/top-languages.js +3 -18
- package/build/fetchers/types.d.ts +24 -0
- package/build/fetchers/types.d.ts.map +1 -1
- package/build/graphql/contributionsCollection.d.ts +9 -0
- package/build/graphql/contributionsCollection.d.ts.map +1 -0
- package/build/graphql/contributionsCollection.js +12 -0
- package/build/graphql/contributionsDocument.d.ts +5 -5
- package/build/graphql/contributionsDocument.d.ts.map +1 -1
- package/build/graphql/contributionsDocument.js +8 -15
- package/build/graphql/generated/contributed-to.d.ts +22 -0
- package/build/graphql/generated/contributed-to.d.ts.map +1 -0
- package/build/graphql/generated/contributed-to.js +11 -0
- package/build/graphql/generated/stats.d.ts +13 -1
- package/build/graphql/generated/stats.d.ts.map +1 -1
- package/build/graphql/generated/stats.js +2 -2
- package/build/graphql/graphqlDocument.d.ts +1 -1
- package/build/graphql/reposContributedToDocument.d.ts.map +1 -1
- package/build/graphql/reposContributedToDocument.js +11 -5
- package/build/translations.d.ts +64 -1
- package/build/translations.d.ts.map +1 -1
- package/build/translations.js +65 -1
- package/package.json +1 -1
- package/src/api/contributed-to.ts +84 -0
- package/src/api/gist.ts +20 -37
- package/src/api/handler.ts +42 -0
- package/src/api/index.ts +1 -0
- package/src/api/params.ts +48 -9
- package/src/api/pin.ts +33 -45
- package/src/api/stats.ts +78 -89
- package/src/api/top-langs.ts +34 -56
- package/src/api/wakatime.ts +33 -54
- package/src/cards/contributed-to.ts +373 -0
- package/src/cards/gist.ts +17 -8
- package/src/cards/repo.ts +44 -20
- package/src/cards/stats.ts +119 -90
- package/src/cards/top-languages.ts +27 -18
- package/src/cards/wakatime.ts +23 -13
- package/src/common/Card.ts +56 -11
- package/src/common/I18n.ts +4 -2
- package/src/common/brand.ts +109 -0
- package/src/common/constants.ts +4 -1
- package/src/common/date.ts +135 -3
- package/src/common/icons.ts +10 -5
- package/src/common/ops.ts +10 -19
- package/src/common/render.ts +65 -62
- package/src/fetchers/contributed-to.ts +271 -0
- package/src/fetchers/graphql-error.ts +36 -0
- package/src/fetchers/index.ts +3 -0
- package/src/fetchers/stats.ts +123 -274
- package/src/fetchers/top-languages.ts +5 -19
- package/src/fetchers/types.ts +26 -0
- package/src/graphql/contributionsCollection.ts +18 -0
- package/src/graphql/contributionsDocument.ts +11 -18
- package/src/graphql/generated/contributed-to.ts +25 -0
- package/src/graphql/generated/stats.ts +19 -6
- package/src/graphql/graphqlDocument.ts +1 -1
- package/src/graphql/queries/contributed-to.graphql +8 -0
- package/src/graphql/queries/stats.graphql +14 -2
- package/src/graphql/reposContributedToDocument.ts +11 -9
- package/src/translations.ts +77 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { CARD_ICON, CARD_WIDTH, FONT_SIZE, firefoxFontSize, font } from '../common/brand.js';
|
|
2
|
+
import { Card } from '../common/Card.js';
|
|
3
|
+
import { getLightDarkColors } from '../common/color.js';
|
|
4
|
+
import { formatYears } from '../common/date.js';
|
|
5
|
+
import { kFormatter } from '../common/fmt.js';
|
|
6
|
+
import { I18n } from '../common/I18n.js';
|
|
7
|
+
import { clampValue } from '../common/ops.js';
|
|
8
|
+
import { createProgressNode, measureText } from '../common/render.js';
|
|
9
|
+
import { atRule, el, rule } from '../markup/index.js';
|
|
10
|
+
import { contributedToCardLocales } from '../translations.js';
|
|
11
|
+
const CARD_DEFAULT_WIDTH = CARD_WIDTH.wide;
|
|
12
|
+
const MIN_CARD_WIDTH = 340;
|
|
13
|
+
/** Padding the card keeps at its edges; matches `Card`'s own `paddingX`. */
|
|
14
|
+
const CARD_PADDING = 25;
|
|
15
|
+
const TITLE_FONT_SIZE = FONT_SIZE.title;
|
|
16
|
+
/** What `Card`'s title layout reserves for the prefix icon, so the title text starts past it. */
|
|
17
|
+
const TITLE_ICON_COLUMN = 25;
|
|
18
|
+
const NAME_FONT_SIZE = FONT_SIZE.meta;
|
|
19
|
+
/** Room kept at the right edge for the contribution count. */
|
|
20
|
+
const COUNT_WIDTH = 46;
|
|
21
|
+
const BAR_WIDTH = 130;
|
|
22
|
+
const COLUMN_GAP = 12;
|
|
23
|
+
/** Baseline of the first row, measured from the top of the card body. */
|
|
24
|
+
const FIRST_ROW_Y = 10;
|
|
25
|
+
const ROW_HEIGHT = 38;
|
|
26
|
+
/** A row with no year marks under it needs only its own line. */
|
|
27
|
+
const ROW_HEIGHT_NO_YEARS = 24;
|
|
28
|
+
/** How far the year marks sit below their row's baseline. */
|
|
29
|
+
const YEAR_STRIP_Y = 8;
|
|
30
|
+
const YEAR_MARK_SIZE = 7;
|
|
31
|
+
const YEAR_MARK_GAP = 3;
|
|
32
|
+
/** Gap between the last row and the footer's own baseline. */
|
|
33
|
+
const FOOTER_GAP = 12;
|
|
34
|
+
/** Where `Card` puts the body while the title is shown; `setHideTitle` adjusts the height by the same 30. */
|
|
35
|
+
const BODY_OFFSET_Y = 55;
|
|
36
|
+
const BOTTOM_PADDING = 18;
|
|
37
|
+
/**
|
|
38
|
+
* The title, naming the account unless that makes it too wide for the card.
|
|
39
|
+
*
|
|
40
|
+
* A login is up to 39 characters, which at the title's size overruns the default width
|
|
41
|
+
* by half again — so the plain wording is the fallback rather than a truncated login.
|
|
42
|
+
*
|
|
43
|
+
* @returns The default title, which `custom_title` still overrides.
|
|
44
|
+
*/
|
|
45
|
+
const defaultTitleFor = (i18n, contentWidth) => {
|
|
46
|
+
const named = i18n.t('contributedtocard.title');
|
|
47
|
+
return measureText(named, TITLE_FONT_SIZE) <= contentWidth
|
|
48
|
+
? named
|
|
49
|
+
: i18n.t('contributedtocard.title-unnamed');
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Shortens a name to fit its column, ending it with an ellipsis when it does not.
|
|
53
|
+
*
|
|
54
|
+
* @returns The name as it is drawn.
|
|
55
|
+
*/
|
|
56
|
+
const truncateName = (name, maxWidth) => {
|
|
57
|
+
if (measureText(name, NAME_FONT_SIZE) <= maxWidth) {
|
|
58
|
+
return name;
|
|
59
|
+
}
|
|
60
|
+
let trimmed = name;
|
|
61
|
+
while (trimmed.length > 1 && measureText(`${trimmed}…`, NAME_FONT_SIZE) > maxWidth) {
|
|
62
|
+
trimmed = trimmed.slice(0, -1);
|
|
63
|
+
}
|
|
64
|
+
return `${trimmed}…`;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* The marks under a repository's name, one per contribution year of the account,
|
|
68
|
+
* filled for the years that repository got a contribution.
|
|
69
|
+
*
|
|
70
|
+
* The step shrinks to fit, so a long-lived account keeps the strip inside its column
|
|
71
|
+
* rather than running it under the bar.
|
|
72
|
+
*
|
|
73
|
+
* @returns The strip, positioned relative to its row's baseline.
|
|
74
|
+
*/
|
|
75
|
+
const createYearStrip = (repoYears, years, columnWidth) => {
|
|
76
|
+
const contributed = new Set(repoYears);
|
|
77
|
+
const step = Math.min(YEAR_MARK_SIZE + YEAR_MARK_GAP, Math.floor(columnWidth / years.length));
|
|
78
|
+
const size = Math.max(step - YEAR_MARK_GAP, 3);
|
|
79
|
+
return el('g', { 'data-testid': 'year-strip', transform: `translate(0, ${YEAR_STRIP_Y})` }, years.map((year, index) => el('rect', {
|
|
80
|
+
'data-testid': contributed.has(year) ? 'year-on' : 'year-off',
|
|
81
|
+
class: contributed.has(year) ? 'year-on' : 'year-off',
|
|
82
|
+
x: index * step,
|
|
83
|
+
y: 0,
|
|
84
|
+
width: size,
|
|
85
|
+
height: size,
|
|
86
|
+
rx: 1,
|
|
87
|
+
ry: 1,
|
|
88
|
+
})));
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* One repository: its name, a bar for its share of the top repository's contributions,
|
|
92
|
+
* and the count itself.
|
|
93
|
+
*
|
|
94
|
+
* @returns The row, positioned by its own transform.
|
|
95
|
+
*/
|
|
96
|
+
const createRepoRow = ({ repo, index, maxContributions, years, nameWidth, contentWidth, rowHeight, }) => el('g', {
|
|
97
|
+
class: 'stagger',
|
|
98
|
+
style: `animation-delay: ${450 + index * 150}ms`,
|
|
99
|
+
transform: `translate(0, ${FIRST_ROW_Y + index * rowHeight})`,
|
|
100
|
+
}, el('text', { class: 'repo-name', 'data-testid': 'repo-name', x: 0, y: 0 }, truncateName(repo.nameWithOwner, nameWidth)), createProgressNode({
|
|
101
|
+
x: nameWidth + COLUMN_GAP,
|
|
102
|
+
y: -9,
|
|
103
|
+
width: BAR_WIDTH,
|
|
104
|
+
progress: (repo.contributions / maxContributions) * 100,
|
|
105
|
+
delay: 450 + index * 150,
|
|
106
|
+
}), el('text', {
|
|
107
|
+
class: 'count',
|
|
108
|
+
'data-testid': 'repo-contributions',
|
|
109
|
+
x: contentWidth,
|
|
110
|
+
y: 0,
|
|
111
|
+
'text-anchor': 'end',
|
|
112
|
+
}, kFormatter(repo.contributions)), years.length > 0 && createYearStrip(repo.years, years, nameWidth));
|
|
113
|
+
/**
|
|
114
|
+
* The line under the rows: how much of the walk the card is showing, and over what years.
|
|
115
|
+
*
|
|
116
|
+
* Never dropped, because the card shows a slice of the repositories
|
|
117
|
+
* and a rank means nothing without the total it was taken from.
|
|
118
|
+
*
|
|
119
|
+
* @returns The footer text.
|
|
120
|
+
*/
|
|
121
|
+
const footerText = ({ i18n, shown, totalRepos, years, }) => {
|
|
122
|
+
const parts = [
|
|
123
|
+
i18n.t(shown < totalRepos ? 'contributedtocard.footer-top' : 'contributedtocard.footer-all'),
|
|
124
|
+
];
|
|
125
|
+
const [firstYear] = years;
|
|
126
|
+
const lastYear = years.at(-1);
|
|
127
|
+
if (firstYear !== undefined && lastYear !== undefined) {
|
|
128
|
+
parts.push(formatYears(firstYear, lastYear));
|
|
129
|
+
}
|
|
130
|
+
return parts.join(' · ');
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* Renders the repositories a user contributed to, ranked by contributions.
|
|
134
|
+
*
|
|
135
|
+
* @returns Contributed-to card SVG string.
|
|
136
|
+
*/
|
|
137
|
+
const renderContributedToCard = (data, options = {}) => {
|
|
138
|
+
const { locale, hide_title = false, hide_border = false, card_width, custom_title, disable_animations = false, hide_years = false, border_radius, theme = 'default', } = options;
|
|
139
|
+
const { login, repos, totalRepos } = data;
|
|
140
|
+
const years = hide_years ? [] : data.years;
|
|
141
|
+
const width = card_width && !Number.isNaN(card_width)
|
|
142
|
+
? clampValue(card_width, MIN_CARD_WIDTH, Number.MAX_SAFE_INTEGER)
|
|
143
|
+
: CARD_DEFAULT_WIDTH;
|
|
144
|
+
const contentWidth = width - 2 * CARD_PADDING;
|
|
145
|
+
const nameWidth = contentWidth - COUNT_WIDTH - BAR_WIDTH - 2 * COLUMN_GAP;
|
|
146
|
+
const rowHeight = years.length > 0 ? ROW_HEIGHT : ROW_HEIGHT_NO_YEARS;
|
|
147
|
+
// the count is a share of the busiest repository, so the first bar is always full
|
|
148
|
+
const maxContributions = Math.max(...repos.map((repo) => repo.contributions), 1);
|
|
149
|
+
const rows = repos.map((repo, index) => createRepoRow({
|
|
150
|
+
repo,
|
|
151
|
+
index,
|
|
152
|
+
maxContributions,
|
|
153
|
+
years,
|
|
154
|
+
nameWidth,
|
|
155
|
+
contentWidth,
|
|
156
|
+
rowHeight,
|
|
157
|
+
}));
|
|
158
|
+
const i18n = new I18n({
|
|
159
|
+
locale,
|
|
160
|
+
translations: contributedToCardLocales({ login, shown: repos.length, totalRepos }),
|
|
161
|
+
});
|
|
162
|
+
const footer = footerText({ i18n, shown: repos.length, totalRepos, years });
|
|
163
|
+
const footerY = FIRST_ROW_Y + Math.max(repos.length, 1) * rowHeight + FOOTER_GAP;
|
|
164
|
+
const height = BODY_OFFSET_Y + footerY + BOTTOM_PADDING;
|
|
165
|
+
const { lightColors, darkColors } = getLightDarkColors({ ...options, theme });
|
|
166
|
+
const card = new Card({
|
|
167
|
+
customTitle: custom_title,
|
|
168
|
+
defaultTitle: defaultTitleFor(i18n, contentWidth - TITLE_ICON_COLUMN),
|
|
169
|
+
titlePrefixIcon: CARD_ICON.contributedTo,
|
|
170
|
+
width,
|
|
171
|
+
height,
|
|
172
|
+
border_radius,
|
|
173
|
+
colors: { light: lightColors, dark: darkColors },
|
|
174
|
+
});
|
|
175
|
+
if (disable_animations) {
|
|
176
|
+
card.disableAnimations();
|
|
177
|
+
}
|
|
178
|
+
card.setHideBorder(hide_border);
|
|
179
|
+
card.setHideTitle(hide_title);
|
|
180
|
+
card.setCSS({
|
|
181
|
+
light: ({ textColor, titleColor, iconColor, progBarBgColor }) => [
|
|
182
|
+
atRule('@keyframes growWidthAnimation', rule('from', { width: 0 }), rule('to', { width: '100%' })),
|
|
183
|
+
rule('.repo-name', {
|
|
184
|
+
font: font('regular', 'meta'),
|
|
185
|
+
fill: textColor,
|
|
186
|
+
}),
|
|
187
|
+
rule('.count', {
|
|
188
|
+
font: font('semibold', 'meta'),
|
|
189
|
+
fill: textColor,
|
|
190
|
+
'font-variant-numeric': 'tabular-nums',
|
|
191
|
+
}),
|
|
192
|
+
rule('.footer', {
|
|
193
|
+
font: font('regular', 'micro'),
|
|
194
|
+
fill: textColor,
|
|
195
|
+
opacity: 0.7,
|
|
196
|
+
}),
|
|
197
|
+
firefoxFontSize(['.repo-name', '.count'], 'small'),
|
|
198
|
+
rule('.year-on', { fill: titleColor }),
|
|
199
|
+
// a year with no contribution reads off the text color, not `progBarBgColor`:
|
|
200
|
+
// on a dark theme that color is a light grey, and the empty marks came out looking filled
|
|
201
|
+
rule('.year-off', { fill: textColor, opacity: 0.25 }),
|
|
202
|
+
rule('.progress-background', { fill: progBarBgColor }),
|
|
203
|
+
rule('.lang-progress', {
|
|
204
|
+
fill: titleColor,
|
|
205
|
+
animation: 'growWidthAnimation 0.6s ease-in-out forwards',
|
|
206
|
+
}),
|
|
207
|
+
rule('.stagger', { opacity: 0, animation: 'fadeInAnimation 0.3s ease-in-out forwards' }),
|
|
208
|
+
rule('.icon', { fill: iconColor, display: 'block' }),
|
|
209
|
+
],
|
|
210
|
+
dark: ({ textColor, titleColor, iconColor, progBarBgColor }) => [
|
|
211
|
+
rule('.repo-name', { fill: textColor }),
|
|
212
|
+
rule('.count', { fill: textColor }),
|
|
213
|
+
rule('.footer', { fill: textColor }),
|
|
214
|
+
rule('.year-on', { fill: titleColor }),
|
|
215
|
+
// a year with no contribution reads off the text color, not `progBarBgColor`:
|
|
216
|
+
// on a dark theme that color is a light grey, and the empty marks came out looking filled
|
|
217
|
+
rule('.year-off', { fill: textColor, opacity: 0.25 }),
|
|
218
|
+
rule('.progress-background', { fill: progBarBgColor }),
|
|
219
|
+
rule('.lang-progress', { fill: titleColor }),
|
|
220
|
+
rule('.icon', { fill: iconColor }),
|
|
221
|
+
],
|
|
222
|
+
});
|
|
223
|
+
// `role="img"` hides the inner text from assistive tech, so everything the card
|
|
224
|
+
// shows has to be repeated here — the year marks included, since they carry
|
|
225
|
+
// information no other element on the card does.
|
|
226
|
+
card.setAccessibilityLabel({
|
|
227
|
+
title: card.title,
|
|
228
|
+
desc: [
|
|
229
|
+
...repos.map((repo) => {
|
|
230
|
+
const inYears = hide_years
|
|
231
|
+
? ''
|
|
232
|
+
: `, ${i18n.t('contributedtocard.years')}: ${repo.years.join(', ')}`;
|
|
233
|
+
return `${repo.nameWithOwner}: ${repo.contributions} ${i18n.t('contributedtocard.contributions')}${inYears}`;
|
|
234
|
+
}),
|
|
235
|
+
footer,
|
|
236
|
+
].join('; '),
|
|
237
|
+
});
|
|
238
|
+
return card.render(el('g', { 'data-testid': 'contributed-to-body', transform: `translate(${CARD_PADDING}, 0)` }, repos.length === 0
|
|
239
|
+
? el('text', { class: 'repo-name', 'data-testid': 'no-repos', x: 0, y: FIRST_ROW_Y }, i18n.t('contributedtocard.no-contributions'))
|
|
240
|
+
: rows, el('text', { class: 'footer', 'data-testid': 'footer', x: 0, y: footerY }, footer)));
|
|
241
|
+
};
|
|
242
|
+
export { renderContributedToCard, MIN_CARD_WIDTH };
|
package/build/cards/gist.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gist.d.ts","sourceRoot":"","sources":["../../src/cards/gist.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"gist.d.ts","sourceRoot":"","sources":["../../src/cards/gist.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAKrD,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAWnE,UAAU,eAAgB,SAAQ,iBAAiB;IACjD,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;IACpB,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAED;;;;GAIG;AACH,QAAA,MAAM,cAAc,aAAc,QAAQ,YAAW,WAAW,CAAC,eAAe,CAAC,KAAQ,MA+HxF,CAAC;AAEF,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
package/build/cards/gist.js
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
|
+
import { CARD_ICON, CARD_WIDTH, FONT_SIZE, font } from '../common/brand.js';
|
|
1
2
|
import { Card } from '../common/Card.js';
|
|
2
3
|
import { getLightDarkColors } from '../common/color.js';
|
|
3
4
|
import { kFormatter } from '../common/fmt.js';
|
|
5
|
+
import { I18n } from '../common/I18n.js';
|
|
4
6
|
import { icons } from '../common/icons.js';
|
|
5
7
|
import { getLanguageColor } from '../common/languageColors.js';
|
|
6
8
|
import { parseEmojis } from '../common/ops.js';
|
|
7
9
|
import { countWrappedLines, wrapTextMultiline, createLanguageNode, flexLayout, iconWithLabel, measureText, wrappedTextNode, wrappedTextStyles, } from '../common/render.js';
|
|
8
10
|
import { el, rule } from '../markup/index.js';
|
|
11
|
+
import { gistCardLocales } from '../translations.js';
|
|
9
12
|
const ICON_SIZE = 16;
|
|
10
|
-
const CARD_DEFAULT_WIDTH =
|
|
13
|
+
const CARD_DEFAULT_WIDTH = CARD_WIDTH.standard;
|
|
11
14
|
const X_OFFSET = 25;
|
|
12
15
|
const HEADER_MAX_LENGTH = 35;
|
|
13
16
|
const DESCRIPTION_BOX_WIDTH = CARD_DEFAULT_WIDTH - 2 * X_OFFSET;
|
|
14
|
-
const DESCRIPTION_FONT_SIZE =
|
|
17
|
+
const DESCRIPTION_FONT_SIZE = FONT_SIZE.meta;
|
|
15
18
|
const DESCRIPTION_LINE_HEIGHT_PX = 16;
|
|
16
19
|
const DESCRIPTION_MAX_LINES = 10;
|
|
17
20
|
/**
|
|
@@ -21,9 +24,10 @@ const DESCRIPTION_MAX_LINES = 10;
|
|
|
21
24
|
*/
|
|
22
25
|
const renderGistCard = (gistData, options = {}) => {
|
|
23
26
|
const { name, nameWithOwner, description, language, starsCount, forksCount } = gistData;
|
|
24
|
-
const { theme = 'default_repocard', border_radius, show_owner = false, browser_rendering = false, hide_border = false, } = options;
|
|
27
|
+
const { locale, theme = 'default_repocard', border_radius, show_owner = false, browser_rendering = false, hide_border = false, } = options;
|
|
25
28
|
const { lightColors, darkColors } = getLightDarkColors({ ...options, theme });
|
|
26
|
-
const
|
|
29
|
+
const i18n = new I18n({ locale, translations: gistCardLocales });
|
|
30
|
+
const desc = parseEmojis(description || i18n.t('gistcard.no-description'));
|
|
27
31
|
let descriptionLines;
|
|
28
32
|
let descriptionSvg;
|
|
29
33
|
if (browser_rendering) {
|
|
@@ -55,7 +59,7 @@ const renderGistCard = (gistData, options = {}) => {
|
|
|
55
59
|
const totalForks = kFormatter(forksCount);
|
|
56
60
|
const svgStars = iconWithLabel(icons.star, totalStars, 'starsCount', ICON_SIZE);
|
|
57
61
|
const svgForks = iconWithLabel(icons.fork, totalForks, 'forksCount', ICON_SIZE);
|
|
58
|
-
const languageName = language || '
|
|
62
|
+
const languageName = language || i18n.t('gistcard.unspecified-language');
|
|
59
63
|
const languageColor = getLanguageColor(languageName);
|
|
60
64
|
const svgLanguage = createLanguageNode(languageName, languageColor);
|
|
61
65
|
const starAndForkCount = flexLayout({
|
|
@@ -70,7 +74,7 @@ const renderGistCard = (gistData, options = {}) => {
|
|
|
70
74
|
const header = show_owner ? nameWithOwner : name;
|
|
71
75
|
const card = new Card({
|
|
72
76
|
defaultTitle: header.length > HEADER_MAX_LENGTH ? `${header.slice(0, HEADER_MAX_LENGTH)}...` : header,
|
|
73
|
-
titlePrefixIcon:
|
|
77
|
+
titlePrefixIcon: CARD_ICON.gist,
|
|
74
78
|
width: CARD_DEFAULT_WIDTH,
|
|
75
79
|
height,
|
|
76
80
|
border_radius,
|
|
@@ -79,11 +83,11 @@ const renderGistCard = (gistData, options = {}) => {
|
|
|
79
83
|
card.setCSS({
|
|
80
84
|
light: ({ textColor, iconColor }) => [
|
|
81
85
|
rule('.description', {
|
|
82
|
-
font:
|
|
86
|
+
font: font('regular', 'meta'),
|
|
83
87
|
fill: textColor,
|
|
84
88
|
...(browser_rendering ? wrappedTextStyles(textColor) : {}),
|
|
85
89
|
}),
|
|
86
|
-
rule('.gray', { font:
|
|
90
|
+
rule('.gray', { font: font('regular', 'small'), fill: textColor }),
|
|
87
91
|
rule('.icon', { fill: iconColor }),
|
|
88
92
|
],
|
|
89
93
|
dark: ({ textColor, iconColor }) => [
|
|
@@ -100,7 +104,7 @@ const renderGistCard = (gistData, options = {}) => {
|
|
|
100
104
|
// shows has to be repeated here.
|
|
101
105
|
card.setAccessibilityLabel({
|
|
102
106
|
title: card.title,
|
|
103
|
-
desc: `${desc}.
|
|
107
|
+
desc: `${desc}. ${i18n.t('gistcard.language')}: ${languageName}, ${i18n.t('gistcard.stars')}: ${totalStars}, ${i18n.t('gistcard.forks')}: ${totalForks}`,
|
|
104
108
|
});
|
|
105
109
|
return card.render([
|
|
106
110
|
descriptionSvg,
|
package/build/cards/repo.d.ts
CHANGED
|
@@ -18,6 +18,17 @@ interface RepoCardOptions extends CommonCardOptions {
|
|
|
18
18
|
*
|
|
19
19
|
* @returns Repository card SVG object.
|
|
20
20
|
*/
|
|
21
|
-
declare const
|
|
21
|
+
declare const renderCard: (repo: RepositoryData, options?: CardOptions<RepoCardOptions>) => string;
|
|
22
|
+
/**
|
|
23
|
+
* The card, and the values each of its options accepts, keyed by the option's own name.
|
|
24
|
+
* They ride on the renderer so a list cannot be found without what draws it;
|
|
25
|
+
* the api handler forwards them onto its own export, which is what a UI reads.
|
|
26
|
+
*/
|
|
27
|
+
declare const renderRepoCard: typeof renderCard & {
|
|
28
|
+
OPTIONS: {
|
|
29
|
+
show: readonly ["prs_authored", "prs_commented", "prs_reviewed", "issues_authored", "issues_commented"];
|
|
30
|
+
number_format: readonly ["short", "long"];
|
|
31
|
+
};
|
|
32
|
+
};
|
|
22
33
|
export { renderRepoCard };
|
|
23
34
|
//# sourceMappingURL=repo.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repo.d.ts","sourceRoot":"","sources":["../../src/cards/repo.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"repo.d.ts","sourceRoot":"","sources":["../../src/cards/repo.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAK3D,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAmBnE,UAAU,eAAgB,SAAQ,iBAAiB;IACjD,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;IACpB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,uBAAuB,EAAE,MAAM,CAAC;IAChC,gBAAgB,EAAE,MAAM,CAAC;IACzB,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAuCD;;;;GAIG;AACH,QAAA,MAAM,UAAU,SAAU,cAAc,YAAW,WAAW,CAAC,eAAe,CAAC,KAAQ,MAoRtF,CAAC;AAEF;;;;GAIG;AACH,QAAA,MAAM,cAAc;;QAEhB,IAAI;QACJ,aAAa;;CAEf,CAAC;AAEH,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
package/build/cards/repo.js
CHANGED
|
@@ -1,18 +1,27 @@
|
|
|
1
|
+
import { CARD_ICON, CARD_WIDTH, FONT_SIZE, FONT_WEIGHT, font } from '../common/brand.js';
|
|
1
2
|
import { Card } from '../common/Card.js';
|
|
2
3
|
import { getLightDarkColors } from '../common/color.js';
|
|
3
4
|
import { kFormatter } from '../common/fmt.js';
|
|
4
5
|
import { I18n } from '../common/I18n.js';
|
|
5
6
|
import { icons } from '../common/icons.js';
|
|
6
7
|
import { buildSearchFilter, clampValue, parseEmojis } from '../common/ops.js';
|
|
7
|
-
import { countWrappedLines, wrapTextMultiline, createLanguageNode, createTextNode, flexLayout, iconWithLabel, measureText, wrappedTextNode, wrappedTextStyles, } from '../common/render.js';
|
|
8
|
+
import { NUMBER_FORMATS, countWrappedLines, wrapTextMultiline, createLanguageNode, createTextNode, flexLayout, iconWithLabel, measureText, wrappedTextNode, wrappedTextStyles, } from '../common/render.js';
|
|
8
9
|
import { el, rule } from '../markup/index.js';
|
|
9
10
|
import { repoCardLocales } from '../translations.js';
|
|
10
11
|
const ICON_SIZE = 16;
|
|
11
|
-
const CARD_DEFAULT_WIDTH =
|
|
12
|
+
const CARD_DEFAULT_WIDTH = CARD_WIDTH.standard;
|
|
12
13
|
const X_OFFSET = 25;
|
|
13
|
-
const DESCRIPTION_FONT_SIZE =
|
|
14
|
+
const DESCRIPTION_FONT_SIZE = FONT_SIZE.meta;
|
|
14
15
|
const DESCRIPTION_LINE_HEIGHT_PX = 16;
|
|
15
16
|
const DESCRIPTION_MAX_LINES = 3;
|
|
17
|
+
/** Stats the card draws only when `show` names them. */
|
|
18
|
+
const REPO_SHOW_STATS = [
|
|
19
|
+
'prs_authored',
|
|
20
|
+
'prs_commented',
|
|
21
|
+
'prs_reviewed',
|
|
22
|
+
'issues_authored',
|
|
23
|
+
'issues_commented',
|
|
24
|
+
];
|
|
16
25
|
/**
|
|
17
26
|
* Retrieves the repository description and wraps it to fit the card width.
|
|
18
27
|
*
|
|
@@ -35,7 +44,7 @@ const getBadgeSVG = (label, xOffset = 0) => {
|
|
|
35
44
|
*
|
|
36
45
|
* @returns Repository card SVG object.
|
|
37
46
|
*/
|
|
38
|
-
const
|
|
47
|
+
const renderCard = (repo, options = {}) => {
|
|
39
48
|
const { name, nameWithOwner, description, primaryLanguage, isArchived, isTemplate, stargazerCount, forkCount, totalPRsAuthored, totalPRsCommented, totalPRsReviewed, totalIssuesAuthored, totalIssuesCommented, } = repo;
|
|
40
49
|
const { hide_border = false, card_width_input, show_owner = false, browser_rendering = false, show = [], show_icons = true, number_format = 'short', text_bold = false, line_height = 22, username, theme = 'default_repocard', border_radius, locale, description_lines_count, } = options;
|
|
41
50
|
const card_width = card_width_input && !Number.isNaN(card_width_input)
|
|
@@ -47,10 +56,12 @@ const renderRepoCard = (repo, options = {}) => {
|
|
|
47
56
|
locale,
|
|
48
57
|
translations: repoCardLocales,
|
|
49
58
|
});
|
|
59
|
+
// Typed against the exported list, so a stat drawn here cannot be missing from it.
|
|
60
|
+
const shows = (stat) => show.includes(stat);
|
|
50
61
|
const repoFilter = encodeURIComponent(buildSearchFilter([nameWithOwner], []));
|
|
51
62
|
const encodedUsername = encodeURIComponent(username ?? '');
|
|
52
63
|
const STATS = {};
|
|
53
|
-
if (
|
|
64
|
+
if (shows('prs_authored')) {
|
|
54
65
|
STATS['prs_authored'] = {
|
|
55
66
|
icon: icons.prs,
|
|
56
67
|
label: i18n.t('repocard.prs-authored'),
|
|
@@ -59,7 +70,7 @@ const renderRepoCard = (repo, options = {}) => {
|
|
|
59
70
|
link: `https://github.com/search?q=${repoFilter}author%3A${encodedUsername}&type=pullrequests`,
|
|
60
71
|
};
|
|
61
72
|
}
|
|
62
|
-
if (
|
|
73
|
+
if (shows('prs_commented')) {
|
|
63
74
|
STATS['prs_commented'] = {
|
|
64
75
|
icon: icons.comments,
|
|
65
76
|
label: i18n.t('repocard.prs-commented'),
|
|
@@ -68,7 +79,7 @@ const renderRepoCard = (repo, options = {}) => {
|
|
|
68
79
|
link: `https://github.com/search?q=${repoFilter}commenter%3A${encodedUsername}+-author%3A${encodedUsername}&type=pullrequests`,
|
|
69
80
|
};
|
|
70
81
|
}
|
|
71
|
-
if (
|
|
82
|
+
if (shows('prs_reviewed')) {
|
|
72
83
|
STATS['prs_reviewed'] = {
|
|
73
84
|
icon: icons.reviews,
|
|
74
85
|
label: i18n.t('repocard.prs-reviewed'),
|
|
@@ -77,7 +88,7 @@ const renderRepoCard = (repo, options = {}) => {
|
|
|
77
88
|
link: `https://github.com/search?q=${repoFilter}reviewed-by%3A${encodedUsername}+-author%3A${encodedUsername}&type=pullrequests`,
|
|
78
89
|
};
|
|
79
90
|
}
|
|
80
|
-
if (
|
|
91
|
+
if (shows('issues_authored')) {
|
|
81
92
|
STATS['issues_authored'] = {
|
|
82
93
|
icon: icons.issues,
|
|
83
94
|
label: i18n.t('repocard.issues-authored'),
|
|
@@ -86,7 +97,7 @@ const renderRepoCard = (repo, options = {}) => {
|
|
|
86
97
|
link: `https://github.com/search?q=${repoFilter}author%3A${encodedUsername}&type=issues`,
|
|
87
98
|
};
|
|
88
99
|
}
|
|
89
|
-
if (
|
|
100
|
+
if (shows('issues_commented')) {
|
|
90
101
|
STATS['issues_commented'] = {
|
|
91
102
|
icon: icons.discussions_started,
|
|
92
103
|
label: i18n.t('repocard.issues-commented'),
|
|
@@ -114,9 +125,9 @@ const renderRepoCard = (repo, options = {}) => {
|
|
|
114
125
|
const extraLHeight = Number.parseInt(String(line_height), 10);
|
|
115
126
|
const lineHeight = 10;
|
|
116
127
|
const header = show_owner ? nameWithOwner : name;
|
|
117
|
-
const langName = primaryLanguage?.name || '
|
|
128
|
+
const langName = primaryLanguage?.name || i18n.t('repocard.unspecified-language');
|
|
118
129
|
const langColor = primaryLanguage?.color || '#333';
|
|
119
|
-
const desc = parseEmojis(description || '
|
|
130
|
+
const desc = parseEmojis(description || i18n.t('repocard.no-description'));
|
|
120
131
|
const descriptionBoxWidth = card_width - 2 * X_OFFSET;
|
|
121
132
|
let descriptionLinesCount;
|
|
122
133
|
let descriptionSvg;
|
|
@@ -177,7 +188,7 @@ const renderRepoCard = (repo, options = {}) => {
|
|
|
177
188
|
const extraItems = el('svg', { x: 0, y: 0 }, el('g', { transform: `translate(-3, ${height - 52 - extraHeight})` }, flexLayout({ items: extraRows, gap: extraLHeight, direction: 'column' })));
|
|
178
189
|
const card = new Card({
|
|
179
190
|
defaultTitle: header.length > 35 ? `${header.slice(0, 35)}...` : header,
|
|
180
|
-
titlePrefixIcon:
|
|
191
|
+
titlePrefixIcon: CARD_ICON.repo,
|
|
181
192
|
width: card_width,
|
|
182
193
|
height,
|
|
183
194
|
border_radius,
|
|
@@ -189,21 +200,21 @@ const renderRepoCard = (repo, options = {}) => {
|
|
|
189
200
|
card.setCSS({
|
|
190
201
|
light: ({ textColor, iconColor }) => [
|
|
191
202
|
rule('.description', {
|
|
192
|
-
font:
|
|
203
|
+
font: font('regular', 'meta'),
|
|
193
204
|
fill: textColor,
|
|
194
205
|
...(browser_rendering ? wrappedTextStyles(textColor) : {}),
|
|
195
206
|
}),
|
|
196
|
-
rule('.gray', { font:
|
|
197
|
-
rule('.badge', { font:
|
|
207
|
+
rule('.gray', { font: font('regular', 'small'), fill: textColor }),
|
|
208
|
+
rule('.badge', { font: font('semibold', 'micro') }),
|
|
198
209
|
rule('.badge rect', { opacity: 0.2, stroke: textColor }),
|
|
199
210
|
rule('.badge text', { fill: textColor }),
|
|
200
|
-
rule('.stat', { font:
|
|
211
|
+
rule('.stat', { font: font('regular', 'small'), fill: textColor }),
|
|
201
212
|
rule('.stagger', {
|
|
202
213
|
opacity: 0,
|
|
203
214
|
animation: 'fadeInAnimation 0.3s ease-in-out forwards',
|
|
204
215
|
}),
|
|
205
|
-
rule('.not_bold', { 'font-weight':
|
|
206
|
-
rule('.bold', { 'font-weight':
|
|
216
|
+
rule('.not_bold', { 'font-weight': FONT_WEIGHT.regular }),
|
|
217
|
+
rule('.bold', { 'font-weight': FONT_WEIGHT.semibold }),
|
|
207
218
|
rule('.icon', { fill: iconColor, display: 'block' }),
|
|
208
219
|
],
|
|
209
220
|
dark: ({ textColor, iconColor }) => [
|
|
@@ -241,4 +252,15 @@ const renderRepoCard = (repo, options = {}) => {
|
|
|
241
252
|
extraItems,
|
|
242
253
|
]);
|
|
243
254
|
};
|
|
255
|
+
/**
|
|
256
|
+
* The card, and the values each of its options accepts, keyed by the option's own name.
|
|
257
|
+
* They ride on the renderer so a list cannot be found without what draws it;
|
|
258
|
+
* the api handler forwards them onto its own export, which is what a UI reads.
|
|
259
|
+
*/
|
|
260
|
+
const renderRepoCard = Object.assign(renderCard, {
|
|
261
|
+
OPTIONS: {
|
|
262
|
+
show: REPO_SHOW_STATS,
|
|
263
|
+
number_format: NUMBER_FORMATS,
|
|
264
|
+
},
|
|
265
|
+
});
|
|
244
266
|
export { renderRepoCard };
|
package/build/cards/stats.d.ts
CHANGED
|
@@ -11,7 +11,6 @@ interface StatCardOptions extends CommonCardOptions {
|
|
|
11
11
|
card_width: number;
|
|
12
12
|
hide_rank: boolean;
|
|
13
13
|
include_all_commits: boolean;
|
|
14
|
-
commits_year: number;
|
|
15
14
|
line_height: number | string;
|
|
16
15
|
custom_title: string;
|
|
17
16
|
disable_animations: boolean;
|
|
@@ -27,6 +26,19 @@ interface StatCardOptions extends CommonCardOptions {
|
|
|
27
26
|
*
|
|
28
27
|
* @returns The stats card SVG object.
|
|
29
28
|
*/
|
|
30
|
-
declare const
|
|
31
|
-
|
|
29
|
+
declare const renderCard: (stats: StatsData, options?: CardOptions<StatCardOptions>, username?: string, repo?: Array<string>, owner?: Array<string>) => string;
|
|
30
|
+
/**
|
|
31
|
+
* The card, and the values each of its options accepts, keyed by the option's own name.
|
|
32
|
+
* They ride on the renderer so a list cannot be found without what draws it;
|
|
33
|
+
* the api handler forwards them onto its own export, which is what a UI reads.
|
|
34
|
+
*/
|
|
35
|
+
declare const renderStatsCard: typeof renderCard & {
|
|
36
|
+
OPTIONS: {
|
|
37
|
+
rank_icon: readonly ["default", "github", "percentile"];
|
|
38
|
+
show: readonly ["contributions", "prs_merged", "prs_merged_percentage", "reviews", "discussions_started", "discussions_answered", "prs_authored", "prs_commented", "prs_reviewed", "issues_authored", "issues_commented", "all_time_contribs"];
|
|
39
|
+
hide: readonly ["stars", "commits", "prs", "issues", "contribs"];
|
|
40
|
+
number_format: readonly ["short", "long"];
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
export { renderStatsCard };
|
|
32
44
|
//# sourceMappingURL=stats.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../src/cards/stats.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../src/cards/stats.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAKtD,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAqBnE,qFAAqF;AACrF,QAAA,MAAM,UAAU,YAAI,SAAS,EAAE,QAAQ,EAAE,YAAY,CAAU,CAAC;AAChE,KAAK,QAAQ,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;AA0B5C,UAAU,eAAgB,SAAQ,iBAAiB;IACjD,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,QAAQ,CAAC;IACpB,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACrB;AA8ID;;;;GAIG;AACH,QAAA,MAAM,UAAU,UACP,SAAS,YACP,WAAW,CAAC,eAAe,CAAC,aAC1B,MAAM,SACX,KAAK,CAAC,MAAM,CAAC,UACZ,KAAK,CAAC,MAAM,CAAC,KACnB,MA8WF,CAAC;AAEF;;;;GAIG;AACH,QAAA,MAAM,eAAe;;QAEjB,SAAS;QACT,IAAI;QACJ,IAAI;QACJ,aAAa;;CAEf,CAAC;AAEH,OAAO,EAAE,eAAe,EAAE,CAAC"}
|