@stats-forge/github-stats-forge-cli 0.1.0 → 0.3.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/cards.d.ts +3 -1
- package/build/cards.d.ts.map +1 -1
- package/build/cards.js +75 -25
- package/build/prompts.d.ts.map +1 -1
- package/build/prompts.js +10 -1
- package/build/saved-card.d.ts +2 -1
- package/build/saved-card.d.ts.map +1 -1
- package/build/saved-card.js +19 -5
- package/package.json +6 -2
- package/src/cards.ts +77 -25
- package/src/prompts.ts +11 -1
- package/src/saved-card.ts +21 -8
package/build/cards.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export interface CardOption {
|
|
|
14
14
|
/** What the prompt asks. */
|
|
15
15
|
label: string;
|
|
16
16
|
kind: OptionKind;
|
|
17
|
-
/** The accepted values, for `choice
|
|
17
|
+
/** The accepted values, for `choice` and for a `list` whose values are a closed set. */
|
|
18
18
|
choices?: ReadonlyArray<string>;
|
|
19
19
|
/** Shown under the prompt, for anything the label cannot say. */
|
|
20
20
|
hint?: string;
|
|
@@ -32,6 +32,8 @@ export interface CardKind {
|
|
|
32
32
|
/** @returns The rendered card, or the rendered error. */
|
|
33
33
|
render: (query: Record<string, string>, config: CardConfig) => Promise<ApiResult>;
|
|
34
34
|
}
|
|
35
|
+
/** Colors and the theme, which every card accepts. */
|
|
36
|
+
export declare const COMMON_OPTIONS: ReadonlyArray<CardOption>;
|
|
35
37
|
/** Every card, with the options every card shares appended to its own. */
|
|
36
38
|
export declare const cards: ReadonlyArray<CardKind>;
|
|
37
39
|
/**
|
package/build/cards.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cards.d.ts","sourceRoot":"","sources":["../src/cards.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cards.d.ts","sourceRoot":"","sources":["../src/cards.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,0CAA0C,CAAC;AAEtF;;;;;GAKG;AAEH,iFAAiF;AACjF,KAAK,UAAU,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEpE,6BAA6B;AAC7B,MAAM,WAAW,UAAU;IACzB,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,4BAA4B;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,UAAU,CAAC;IACjB,wFAAwF;IACxF,OAAO,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAChC,iEAAiE;IACjE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,gEAAgE;AAChE,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,UAAU,EAAE,OAAO,CAAC;IACpB,0DAA0D;IAC1D,QAAQ,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;IACpC,+CAA+C;IAC/C,OAAO,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;IACnC,yDAAyD;IACzD,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;CACnF;AAID,sDAAsD;AACtD,eAAO,MAAM,cAAc,EAAE,aAAa,CAAC,UAAU,CA8BpD,CAAC;AAkTF,0EAA0E;AAC1E,eAAO,MAAM,KAAK,EAAE,aAAa,CAAC,QAAQ,CAGvC,CAAC;AAEJ;;GAEG;AACH,eAAO,MAAM,QAAQ,OAAQ,MAAM,KAAG,QAAQ,GAAG,SAAiD,CAAC"}
|
package/build/cards.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { gist, pin, stats, themes, topLangs, wakatime, } from '@stats-forge/github-stats-forge-core/api';
|
|
1
|
+
import { contributedTo, gist, pin, stats, themes, topLangs, wakatime, } from '@stats-forge/github-stats-forge-core/api';
|
|
2
2
|
const THEME_NAMES = Object.keys(themes);
|
|
3
3
|
/** Colors and the theme, which every card accepts. */
|
|
4
|
-
const COMMON_OPTIONS = [
|
|
4
|
+
export const COMMON_OPTIONS = [
|
|
5
5
|
{
|
|
6
6
|
name: 'theme',
|
|
7
7
|
label: 'Theme',
|
|
@@ -32,6 +32,18 @@ const COMMON_OPTIONS = [
|
|
|
32
32
|
{ name: 'border_radius', label: 'Border radius', kind: 'number' },
|
|
33
33
|
{ name: 'hide_border', label: 'Hide the border', kind: 'boolean' },
|
|
34
34
|
];
|
|
35
|
+
/**
|
|
36
|
+
* The two ends of a card's date range.
|
|
37
|
+
*
|
|
38
|
+
* @returns The pair, naming what it counts.
|
|
39
|
+
*/
|
|
40
|
+
const rangeOptions = (counted) => {
|
|
41
|
+
const hint = 'A year, a month or a day: 2024, 2024-03, 2024-03-15';
|
|
42
|
+
return [
|
|
43
|
+
{ name: 'from', label: `Count ${counted} from`, kind: 'text', hint },
|
|
44
|
+
{ name: 'to', label: `Count ${counted} up to`, kind: 'text', hint },
|
|
45
|
+
];
|
|
46
|
+
};
|
|
35
47
|
const LOCALE_OPTION = {
|
|
36
48
|
name: 'locale',
|
|
37
49
|
label: 'Locale',
|
|
@@ -49,13 +61,13 @@ const CARDS = [
|
|
|
49
61
|
name: 'show',
|
|
50
62
|
label: 'Extra stats to show',
|
|
51
63
|
kind: 'list',
|
|
52
|
-
|
|
64
|
+
choices: stats.OPTIONS.show,
|
|
53
65
|
},
|
|
54
66
|
{
|
|
55
67
|
name: 'hide',
|
|
56
68
|
label: 'Stats to hide',
|
|
57
69
|
kind: 'list',
|
|
58
|
-
|
|
70
|
+
choices: stats.OPTIONS.hide,
|
|
59
71
|
},
|
|
60
72
|
{ name: 'show_icons', label: 'Show the stat icons', kind: 'boolean' },
|
|
61
73
|
{ name: 'hide_rank', label: 'Hide the rank circle', kind: 'boolean' },
|
|
@@ -63,19 +75,14 @@ const CARDS = [
|
|
|
63
75
|
name: 'rank_icon',
|
|
64
76
|
label: 'Rank indicator',
|
|
65
77
|
kind: 'choice',
|
|
66
|
-
choices: stats.
|
|
78
|
+
choices: stats.OPTIONS.rank_icon,
|
|
67
79
|
},
|
|
68
80
|
{
|
|
69
81
|
name: 'include_all_commits',
|
|
70
82
|
label: 'Count commits of all time',
|
|
71
83
|
kind: 'boolean',
|
|
72
84
|
},
|
|
73
|
-
|
|
74
|
-
name: 'commits_year',
|
|
75
|
-
label: 'Count commits for one year',
|
|
76
|
-
kind: 'number',
|
|
77
|
-
hint: 'Four digits, e.g. 2025',
|
|
78
|
-
},
|
|
85
|
+
...rangeOptions('commits'),
|
|
79
86
|
{
|
|
80
87
|
name: 'exclude_repo',
|
|
81
88
|
label: 'Repositories to exclude',
|
|
@@ -95,7 +102,7 @@ const CARDS = [
|
|
|
95
102
|
name: 'role',
|
|
96
103
|
label: 'Owner affiliations to include',
|
|
97
104
|
kind: 'list',
|
|
98
|
-
|
|
105
|
+
choices: stats.OPTIONS.role,
|
|
99
106
|
},
|
|
100
107
|
{
|
|
101
108
|
name: 'contribs_include_own_repos',
|
|
@@ -111,7 +118,7 @@ const CARDS = [
|
|
|
111
118
|
name: 'number_format',
|
|
112
119
|
label: 'Number format',
|
|
113
120
|
kind: 'choice',
|
|
114
|
-
choices:
|
|
121
|
+
choices: stats.OPTIONS.number_format,
|
|
115
122
|
},
|
|
116
123
|
{
|
|
117
124
|
name: 'number_precision',
|
|
@@ -126,7 +133,7 @@ const CARDS = [
|
|
|
126
133
|
{ name: 'ring_color', label: 'Rank ring color', kind: 'text' },
|
|
127
134
|
LOCALE_OPTION,
|
|
128
135
|
],
|
|
129
|
-
render:
|
|
136
|
+
render: stats,
|
|
130
137
|
},
|
|
131
138
|
{
|
|
132
139
|
id: 'top-langs',
|
|
@@ -138,7 +145,7 @@ const CARDS = [
|
|
|
138
145
|
name: 'layout',
|
|
139
146
|
label: 'Layout',
|
|
140
147
|
kind: 'choice',
|
|
141
|
-
choices: topLangs.
|
|
148
|
+
choices: topLangs.OPTIONS.layout,
|
|
142
149
|
},
|
|
143
150
|
{ name: 'langs_count', label: 'Languages to show', kind: 'number' },
|
|
144
151
|
{ name: 'hide', label: 'Languages to hide', kind: 'list' },
|
|
@@ -157,7 +164,7 @@ const CARDS = [
|
|
|
157
164
|
name: 'stats_format',
|
|
158
165
|
label: 'Show values as',
|
|
159
166
|
kind: 'choice',
|
|
160
|
-
choices: topLangs.
|
|
167
|
+
choices: topLangs.OPTIONS.stats_format,
|
|
161
168
|
},
|
|
162
169
|
{
|
|
163
170
|
name: 'hide_progress',
|
|
@@ -170,7 +177,12 @@ const CARDS = [
|
|
|
170
177
|
label: 'Progress bar background color',
|
|
171
178
|
kind: 'text',
|
|
172
179
|
},
|
|
173
|
-
{
|
|
180
|
+
{
|
|
181
|
+
name: 'role',
|
|
182
|
+
label: 'Owner affiliations to include',
|
|
183
|
+
kind: 'list',
|
|
184
|
+
choices: topLangs.OPTIONS.role,
|
|
185
|
+
},
|
|
174
186
|
{ name: 'custom_title', label: 'Card title', kind: 'text' },
|
|
175
187
|
{ name: 'hide_title', label: 'Hide the title', kind: 'boolean' },
|
|
176
188
|
{ name: 'card_width', label: 'Card width', kind: 'number' },
|
|
@@ -181,7 +193,7 @@ const CARDS = [
|
|
|
181
193
|
},
|
|
182
194
|
LOCALE_OPTION,
|
|
183
195
|
],
|
|
184
|
-
render:
|
|
196
|
+
render: topLangs,
|
|
185
197
|
},
|
|
186
198
|
{
|
|
187
199
|
id: 'pin',
|
|
@@ -197,7 +209,7 @@ const CARDS = [
|
|
|
197
209
|
name: 'show',
|
|
198
210
|
label: 'Extra stats to show',
|
|
199
211
|
kind: 'list',
|
|
200
|
-
|
|
212
|
+
choices: pin.OPTIONS.show,
|
|
201
213
|
},
|
|
202
214
|
{ name: 'show_icons', label: 'Show the stat icons', kind: 'boolean' },
|
|
203
215
|
{
|
|
@@ -217,11 +229,48 @@ const CARDS = [
|
|
|
217
229
|
name: 'number_format',
|
|
218
230
|
label: 'Number format',
|
|
219
231
|
kind: 'choice',
|
|
220
|
-
choices:
|
|
232
|
+
choices: pin.OPTIONS.number_format,
|
|
233
|
+
},
|
|
234
|
+
LOCALE_OPTION,
|
|
235
|
+
],
|
|
236
|
+
render: pin,
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
id: 'contributed-to',
|
|
240
|
+
label: 'Contributed to — repositories you work on, ranked',
|
|
241
|
+
needsToken: true,
|
|
242
|
+
required: [{ name: 'username', label: 'GitHub username', kind: 'text' }],
|
|
243
|
+
options: [
|
|
244
|
+
{ name: 'repos_count', label: 'Repositories to show', kind: 'number' },
|
|
245
|
+
{
|
|
246
|
+
name: 'include_own_repos',
|
|
247
|
+
label: 'Include your own repositories',
|
|
248
|
+
kind: 'boolean',
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
name: 'exclude_repo',
|
|
252
|
+
label: 'Repositories to exclude',
|
|
253
|
+
kind: 'list',
|
|
254
|
+
hint: 'Each one an owner/name, or just the name',
|
|
255
|
+
},
|
|
256
|
+
...rangeOptions('contributions'),
|
|
257
|
+
{
|
|
258
|
+
name: 'hide_years',
|
|
259
|
+
label: 'Hide the year marks',
|
|
260
|
+
kind: 'boolean',
|
|
261
|
+
hint: 'One mark per contribution year, filled for the years that repo got one',
|
|
262
|
+
},
|
|
263
|
+
{ name: 'custom_title', label: 'Card title', kind: 'text' },
|
|
264
|
+
{ name: 'hide_title', label: 'Hide the title', kind: 'boolean' },
|
|
265
|
+
{ name: 'card_width', label: 'Card width', kind: 'number' },
|
|
266
|
+
{
|
|
267
|
+
name: 'disable_animations',
|
|
268
|
+
label: 'Disable the animations',
|
|
269
|
+
kind: 'boolean',
|
|
221
270
|
},
|
|
222
271
|
LOCALE_OPTION,
|
|
223
272
|
],
|
|
224
|
-
render:
|
|
273
|
+
render: contributedTo,
|
|
225
274
|
},
|
|
226
275
|
{
|
|
227
276
|
id: 'gist',
|
|
@@ -235,8 +284,9 @@ const CARDS = [
|
|
|
235
284
|
label: 'Let the browser wrap the description',
|
|
236
285
|
kind: 'boolean',
|
|
237
286
|
},
|
|
287
|
+
LOCALE_OPTION,
|
|
238
288
|
],
|
|
239
|
-
render:
|
|
289
|
+
render: gist,
|
|
240
290
|
},
|
|
241
291
|
{
|
|
242
292
|
id: 'wakatime',
|
|
@@ -248,13 +298,13 @@ const CARDS = [
|
|
|
248
298
|
name: 'layout',
|
|
249
299
|
label: 'Layout',
|
|
250
300
|
kind: 'choice',
|
|
251
|
-
choices: wakatime.
|
|
301
|
+
choices: wakatime.OPTIONS.layout,
|
|
252
302
|
},
|
|
253
303
|
{
|
|
254
304
|
name: 'display_format',
|
|
255
305
|
label: 'Show values as',
|
|
256
306
|
kind: 'choice',
|
|
257
|
-
choices: wakatime.
|
|
307
|
+
choices: wakatime.OPTIONS.display_format,
|
|
258
308
|
},
|
|
259
309
|
{ name: 'langs_count', label: 'Languages to show', kind: 'number' },
|
|
260
310
|
{ name: 'hide', label: 'Languages to hide', kind: 'list' },
|
|
@@ -280,7 +330,7 @@ const CARDS = [
|
|
|
280
330
|
},
|
|
281
331
|
LOCALE_OPTION,
|
|
282
332
|
],
|
|
283
|
-
render:
|
|
333
|
+
render: wakatime,
|
|
284
334
|
},
|
|
285
335
|
];
|
|
286
336
|
/** Every card, with the options every card shares appended to its own. */
|
package/build/prompts.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEvD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAGzC;;;;;GAKG;AAEH,mCAAmC;AACnC,eAAO,MAAM,QAAQ,QAAO,OAAO,CAAC,QAAQ,CAIxC,CAAC;
|
|
1
|
+
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEvD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAGzC;;;;;GAKG;AAEH,mCAAmC;AACnC,eAAO,MAAM,QAAQ,QAAO,OAAO,CAAC,QAAQ,CAIxC,CAAC;AAuCL,gDAAgD;AAChD,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC;AAEtD,sDAAsD;AACtD,MAAM,WAAW,IAAI;IACnB,uCAAuC;IACvC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B;;;;OAIG;IACH,MAAM,CAAC,EAAE,UAAU,GAAG,UAAU,GAAG,SAAS,CAAC;CAC9C;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,SACpB,QAAQ,QACR,IAAI,WACD,MAAM,KACd,OAAO,CAAC,UAAU,CAmCpB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,WAAW,SAAgB,QAAQ,KAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAY7E,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,WAAW,eAAsB,MAAM,KAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAMhF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,QAAQ,QAAa,OAAO,CAAC,MAAM,GAAG,SAAS,CAM3D,CAAC"}
|
package/build/prompts.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { confirm, input, password, select } from '@inquirer/prompts';
|
|
1
|
+
import { checkbox, confirm, input, password, select } from '@inquirer/prompts';
|
|
2
2
|
import { cards } from './cards.js';
|
|
3
3
|
import { describeAnswer } from './query.js';
|
|
4
4
|
/**
|
|
@@ -22,6 +22,15 @@ const askOption = async (option, current) => {
|
|
|
22
22
|
if (option.kind === 'boolean') {
|
|
23
23
|
return confirm({ message, default: current === true });
|
|
24
24
|
}
|
|
25
|
+
if (option.kind === 'list' && option.choices) {
|
|
26
|
+
const chosen = new Set(Array.isArray(current) ? current : []);
|
|
27
|
+
const picked = await checkbox({
|
|
28
|
+
message,
|
|
29
|
+
pageSize: 15,
|
|
30
|
+
choices: option.choices.map((value) => ({ value, checked: chosen.has(value) })),
|
|
31
|
+
});
|
|
32
|
+
return picked.length > 0 ? picked : undefined;
|
|
33
|
+
}
|
|
25
34
|
if (option.kind === 'choice') {
|
|
26
35
|
const choices = [
|
|
27
36
|
{ name: '— leave unset —', value: undefined },
|
package/build/saved-card.d.ts
CHANGED
|
@@ -25,7 +25,8 @@ export declare const writeSavedCard: (path: string, card: CardKind, options: Rec
|
|
|
25
25
|
* Turns saved options back into answers the menu can show and edit.
|
|
26
26
|
*
|
|
27
27
|
* Everything on a query string is a string;
|
|
28
|
-
* a boolean
|
|
28
|
+
* a boolean becomes one again, and a list splits back into its values,
|
|
29
|
+
* so each prompt opens on the answer it was saved with.
|
|
29
30
|
*
|
|
30
31
|
* @returns The answers, ready for the menu.
|
|
31
32
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"saved-card.d.ts","sourceRoot":"","sources":["../src/saved-card.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAiBzC;;GAEG;AACH,eAAO,MAAM,eAAe,SAAU,MAAM,KAAG,OAAmD,CAAC;AAEnG;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,SAClB,MAAM,KACX,OAAO,CAAC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAyB7D,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,cAAc,SACnB,MAAM,QACN,QAAQ,WACL,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAC9B,OAAO,CAAC,MAAM,CAKhB,CAAC;AAEF
|
|
1
|
+
{"version":3,"file":"saved-card.d.ts","sourceRoot":"","sources":["../src/saved-card.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAiBzC;;GAEG;AACH,eAAO,MAAM,eAAe,SAAU,MAAM,KAAG,OAAmD,CAAC;AAEnG;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,SAClB,MAAM,KACX,OAAO,CAAC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAyB7D,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,cAAc,SACnB,MAAM,QACN,QAAQ,WACL,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAC9B,OAAO,CAAC,MAAM,CAKhB,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,SAAS,SAAU,QAAQ,WAAW,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAuB7F,CAAC"}
|
package/build/saved-card.js
CHANGED
|
@@ -49,14 +49,28 @@ export const writeSavedCard = async (path, card, options) => {
|
|
|
49
49
|
* Turns saved options back into answers the menu can show and edit.
|
|
50
50
|
*
|
|
51
51
|
* Everything on a query string is a string;
|
|
52
|
-
* a boolean
|
|
52
|
+
* a boolean becomes one again, and a list splits back into its values,
|
|
53
|
+
* so each prompt opens on the answer it was saved with.
|
|
53
54
|
*
|
|
54
55
|
* @returns The answers, ready for the menu.
|
|
55
56
|
*/
|
|
56
57
|
export const toAnswers = (card, options) => {
|
|
57
58
|
const kinds = new Map([...card.required, ...card.options].map((option) => [option.name, option.kind]));
|
|
58
|
-
|
|
59
|
-
name
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
const toAnswer = (name, value) => {
|
|
60
|
+
switch (kinds.get(name)) {
|
|
61
|
+
case 'boolean': {
|
|
62
|
+
return value === 'true';
|
|
63
|
+
}
|
|
64
|
+
case 'list': {
|
|
65
|
+
return value
|
|
66
|
+
.split(',')
|
|
67
|
+
.map((item) => item.trim())
|
|
68
|
+
.filter(Boolean);
|
|
69
|
+
}
|
|
70
|
+
default: {
|
|
71
|
+
return value;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
return new Map(Object.entries(options).map(([name, value]) => [name, toAnswer(name, value)]));
|
|
62
76
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stats-forge/github-stats-forge-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Render a GitHub stats card to a local SVG file, one prompt at a time",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -34,6 +34,10 @@
|
|
|
34
34
|
".": {
|
|
35
35
|
"@stats/source": "./src/index.ts",
|
|
36
36
|
"default": "./build/index.js"
|
|
37
|
+
},
|
|
38
|
+
"./cards": {
|
|
39
|
+
"@stats/source": "./src/cards.ts",
|
|
40
|
+
"default": "./build/cards.js"
|
|
37
41
|
}
|
|
38
42
|
},
|
|
39
43
|
"publishConfig": {
|
|
@@ -41,7 +45,7 @@
|
|
|
41
45
|
},
|
|
42
46
|
"dependencies": {
|
|
43
47
|
"@inquirer/prompts": "8.7.0",
|
|
44
|
-
"@stats-forge/github-stats-forge-core": "^0.
|
|
48
|
+
"@stats-forge/github-stats-forge-core": "^0.3.0"
|
|
45
49
|
},
|
|
46
50
|
"devDependencies": {
|
|
47
51
|
"vitest": "4.1.11"
|
package/src/cards.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
contributedTo,
|
|
2
3
|
gist,
|
|
3
4
|
pin,
|
|
4
5
|
stats,
|
|
@@ -25,7 +26,7 @@ export interface CardOption {
|
|
|
25
26
|
/** What the prompt asks. */
|
|
26
27
|
label: string;
|
|
27
28
|
kind: OptionKind;
|
|
28
|
-
/** The accepted values, for `choice
|
|
29
|
+
/** The accepted values, for `choice` and for a `list` whose values are a closed set. */
|
|
29
30
|
choices?: ReadonlyArray<string>;
|
|
30
31
|
/** Shown under the prompt, for anything the label cannot say. */
|
|
31
32
|
hint?: string;
|
|
@@ -48,7 +49,7 @@ export interface CardKind {
|
|
|
48
49
|
const THEME_NAMES = Object.keys(themes);
|
|
49
50
|
|
|
50
51
|
/** Colors and the theme, which every card accepts. */
|
|
51
|
-
const COMMON_OPTIONS: ReadonlyArray<CardOption> = [
|
|
52
|
+
export const COMMON_OPTIONS: ReadonlyArray<CardOption> = [
|
|
52
53
|
{
|
|
53
54
|
name: 'theme',
|
|
54
55
|
label: 'Theme',
|
|
@@ -80,6 +81,19 @@ const COMMON_OPTIONS: ReadonlyArray<CardOption> = [
|
|
|
80
81
|
{ name: 'hide_border', label: 'Hide the border', kind: 'boolean' },
|
|
81
82
|
];
|
|
82
83
|
|
|
84
|
+
/**
|
|
85
|
+
* The two ends of a card's date range.
|
|
86
|
+
*
|
|
87
|
+
* @returns The pair, naming what it counts.
|
|
88
|
+
*/
|
|
89
|
+
const rangeOptions = (counted: string): ReadonlyArray<CardOption> => {
|
|
90
|
+
const hint = 'A year, a month or a day: 2024, 2024-03, 2024-03-15';
|
|
91
|
+
return [
|
|
92
|
+
{ name: 'from', label: `Count ${counted} from`, kind: 'text', hint },
|
|
93
|
+
{ name: 'to', label: `Count ${counted} up to`, kind: 'text', hint },
|
|
94
|
+
];
|
|
95
|
+
};
|
|
96
|
+
|
|
83
97
|
const LOCALE_OPTION: CardOption = {
|
|
84
98
|
name: 'locale',
|
|
85
99
|
label: 'Locale',
|
|
@@ -98,13 +112,13 @@ const CARDS: ReadonlyArray<CardKind> = [
|
|
|
98
112
|
name: 'show',
|
|
99
113
|
label: 'Extra stats to show',
|
|
100
114
|
kind: 'list',
|
|
101
|
-
|
|
115
|
+
choices: stats.OPTIONS.show,
|
|
102
116
|
},
|
|
103
117
|
{
|
|
104
118
|
name: 'hide',
|
|
105
119
|
label: 'Stats to hide',
|
|
106
120
|
kind: 'list',
|
|
107
|
-
|
|
121
|
+
choices: stats.OPTIONS.hide,
|
|
108
122
|
},
|
|
109
123
|
{ name: 'show_icons', label: 'Show the stat icons', kind: 'boolean' },
|
|
110
124
|
{ name: 'hide_rank', label: 'Hide the rank circle', kind: 'boolean' },
|
|
@@ -112,19 +126,14 @@ const CARDS: ReadonlyArray<CardKind> = [
|
|
|
112
126
|
name: 'rank_icon',
|
|
113
127
|
label: 'Rank indicator',
|
|
114
128
|
kind: 'choice',
|
|
115
|
-
choices: stats.
|
|
129
|
+
choices: stats.OPTIONS.rank_icon,
|
|
116
130
|
},
|
|
117
131
|
{
|
|
118
132
|
name: 'include_all_commits',
|
|
119
133
|
label: 'Count commits of all time',
|
|
120
134
|
kind: 'boolean',
|
|
121
135
|
},
|
|
122
|
-
|
|
123
|
-
name: 'commits_year',
|
|
124
|
-
label: 'Count commits for one year',
|
|
125
|
-
kind: 'number',
|
|
126
|
-
hint: 'Four digits, e.g. 2025',
|
|
127
|
-
},
|
|
136
|
+
...rangeOptions('commits'),
|
|
128
137
|
{
|
|
129
138
|
name: 'exclude_repo',
|
|
130
139
|
label: 'Repositories to exclude',
|
|
@@ -144,7 +153,7 @@ const CARDS: ReadonlyArray<CardKind> = [
|
|
|
144
153
|
name: 'role',
|
|
145
154
|
label: 'Owner affiliations to include',
|
|
146
155
|
kind: 'list',
|
|
147
|
-
|
|
156
|
+
choices: stats.OPTIONS.role,
|
|
148
157
|
},
|
|
149
158
|
{
|
|
150
159
|
name: 'contribs_include_own_repos',
|
|
@@ -160,7 +169,7 @@ const CARDS: ReadonlyArray<CardKind> = [
|
|
|
160
169
|
name: 'number_format',
|
|
161
170
|
label: 'Number format',
|
|
162
171
|
kind: 'choice',
|
|
163
|
-
choices:
|
|
172
|
+
choices: stats.OPTIONS.number_format,
|
|
164
173
|
},
|
|
165
174
|
{
|
|
166
175
|
name: 'number_precision',
|
|
@@ -175,7 +184,7 @@ const CARDS: ReadonlyArray<CardKind> = [
|
|
|
175
184
|
{ name: 'ring_color', label: 'Rank ring color', kind: 'text' },
|
|
176
185
|
LOCALE_OPTION,
|
|
177
186
|
],
|
|
178
|
-
render:
|
|
187
|
+
render: stats,
|
|
179
188
|
},
|
|
180
189
|
{
|
|
181
190
|
id: 'top-langs',
|
|
@@ -187,7 +196,7 @@ const CARDS: ReadonlyArray<CardKind> = [
|
|
|
187
196
|
name: 'layout',
|
|
188
197
|
label: 'Layout',
|
|
189
198
|
kind: 'choice',
|
|
190
|
-
choices: topLangs.
|
|
199
|
+
choices: topLangs.OPTIONS.layout,
|
|
191
200
|
},
|
|
192
201
|
{ name: 'langs_count', label: 'Languages to show', kind: 'number' },
|
|
193
202
|
{ name: 'hide', label: 'Languages to hide', kind: 'list' },
|
|
@@ -206,7 +215,7 @@ const CARDS: ReadonlyArray<CardKind> = [
|
|
|
206
215
|
name: 'stats_format',
|
|
207
216
|
label: 'Show values as',
|
|
208
217
|
kind: 'choice',
|
|
209
|
-
choices: topLangs.
|
|
218
|
+
choices: topLangs.OPTIONS.stats_format,
|
|
210
219
|
},
|
|
211
220
|
{
|
|
212
221
|
name: 'hide_progress',
|
|
@@ -219,7 +228,12 @@ const CARDS: ReadonlyArray<CardKind> = [
|
|
|
219
228
|
label: 'Progress bar background color',
|
|
220
229
|
kind: 'text',
|
|
221
230
|
},
|
|
222
|
-
{
|
|
231
|
+
{
|
|
232
|
+
name: 'role',
|
|
233
|
+
label: 'Owner affiliations to include',
|
|
234
|
+
kind: 'list',
|
|
235
|
+
choices: topLangs.OPTIONS.role,
|
|
236
|
+
},
|
|
223
237
|
{ name: 'custom_title', label: 'Card title', kind: 'text' },
|
|
224
238
|
{ name: 'hide_title', label: 'Hide the title', kind: 'boolean' },
|
|
225
239
|
{ name: 'card_width', label: 'Card width', kind: 'number' },
|
|
@@ -230,7 +244,7 @@ const CARDS: ReadonlyArray<CardKind> = [
|
|
|
230
244
|
},
|
|
231
245
|
LOCALE_OPTION,
|
|
232
246
|
],
|
|
233
|
-
render:
|
|
247
|
+
render: topLangs,
|
|
234
248
|
},
|
|
235
249
|
{
|
|
236
250
|
id: 'pin',
|
|
@@ -246,7 +260,7 @@ const CARDS: ReadonlyArray<CardKind> = [
|
|
|
246
260
|
name: 'show',
|
|
247
261
|
label: 'Extra stats to show',
|
|
248
262
|
kind: 'list',
|
|
249
|
-
|
|
263
|
+
choices: pin.OPTIONS.show,
|
|
250
264
|
},
|
|
251
265
|
{ name: 'show_icons', label: 'Show the stat icons', kind: 'boolean' },
|
|
252
266
|
{
|
|
@@ -266,11 +280,48 @@ const CARDS: ReadonlyArray<CardKind> = [
|
|
|
266
280
|
name: 'number_format',
|
|
267
281
|
label: 'Number format',
|
|
268
282
|
kind: 'choice',
|
|
269
|
-
choices:
|
|
283
|
+
choices: pin.OPTIONS.number_format,
|
|
284
|
+
},
|
|
285
|
+
LOCALE_OPTION,
|
|
286
|
+
],
|
|
287
|
+
render: pin,
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
id: 'contributed-to',
|
|
291
|
+
label: 'Contributed to — repositories you work on, ranked',
|
|
292
|
+
needsToken: true,
|
|
293
|
+
required: [{ name: 'username', label: 'GitHub username', kind: 'text' }],
|
|
294
|
+
options: [
|
|
295
|
+
{ name: 'repos_count', label: 'Repositories to show', kind: 'number' },
|
|
296
|
+
{
|
|
297
|
+
name: 'include_own_repos',
|
|
298
|
+
label: 'Include your own repositories',
|
|
299
|
+
kind: 'boolean',
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
name: 'exclude_repo',
|
|
303
|
+
label: 'Repositories to exclude',
|
|
304
|
+
kind: 'list',
|
|
305
|
+
hint: 'Each one an owner/name, or just the name',
|
|
306
|
+
},
|
|
307
|
+
...rangeOptions('contributions'),
|
|
308
|
+
{
|
|
309
|
+
name: 'hide_years',
|
|
310
|
+
label: 'Hide the year marks',
|
|
311
|
+
kind: 'boolean',
|
|
312
|
+
hint: 'One mark per contribution year, filled for the years that repo got one',
|
|
313
|
+
},
|
|
314
|
+
{ name: 'custom_title', label: 'Card title', kind: 'text' },
|
|
315
|
+
{ name: 'hide_title', label: 'Hide the title', kind: 'boolean' },
|
|
316
|
+
{ name: 'card_width', label: 'Card width', kind: 'number' },
|
|
317
|
+
{
|
|
318
|
+
name: 'disable_animations',
|
|
319
|
+
label: 'Disable the animations',
|
|
320
|
+
kind: 'boolean',
|
|
270
321
|
},
|
|
271
322
|
LOCALE_OPTION,
|
|
272
323
|
],
|
|
273
|
-
render:
|
|
324
|
+
render: contributedTo,
|
|
274
325
|
},
|
|
275
326
|
{
|
|
276
327
|
id: 'gist',
|
|
@@ -284,8 +335,9 @@ const CARDS: ReadonlyArray<CardKind> = [
|
|
|
284
335
|
label: 'Let the browser wrap the description',
|
|
285
336
|
kind: 'boolean',
|
|
286
337
|
},
|
|
338
|
+
LOCALE_OPTION,
|
|
287
339
|
],
|
|
288
|
-
render:
|
|
340
|
+
render: gist,
|
|
289
341
|
},
|
|
290
342
|
{
|
|
291
343
|
id: 'wakatime',
|
|
@@ -297,13 +349,13 @@ const CARDS: ReadonlyArray<CardKind> = [
|
|
|
297
349
|
name: 'layout',
|
|
298
350
|
label: 'Layout',
|
|
299
351
|
kind: 'choice',
|
|
300
|
-
choices: wakatime.
|
|
352
|
+
choices: wakatime.OPTIONS.layout,
|
|
301
353
|
},
|
|
302
354
|
{
|
|
303
355
|
name: 'display_format',
|
|
304
356
|
label: 'Show values as',
|
|
305
357
|
kind: 'choice',
|
|
306
|
-
choices: wakatime.
|
|
358
|
+
choices: wakatime.OPTIONS.display_format,
|
|
307
359
|
},
|
|
308
360
|
{ name: 'langs_count', label: 'Languages to show', kind: 'number' },
|
|
309
361
|
{ name: 'hide', label: 'Languages to hide', kind: 'list' },
|
|
@@ -329,7 +381,7 @@ const CARDS: ReadonlyArray<CardKind> = [
|
|
|
329
381
|
},
|
|
330
382
|
LOCALE_OPTION,
|
|
331
383
|
],
|
|
332
|
-
render:
|
|
384
|
+
render: wakatime,
|
|
333
385
|
},
|
|
334
386
|
];
|
|
335
387
|
|
package/src/prompts.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { confirm, input, password, select } from '@inquirer/prompts';
|
|
1
|
+
import { checkbox, confirm, input, password, select } from '@inquirer/prompts';
|
|
2
2
|
|
|
3
3
|
import type { CardKind, CardOption } from './cards.ts';
|
|
4
4
|
import { cards } from './cards.ts';
|
|
@@ -31,6 +31,16 @@ const askOption = async (option: CardOption, current: Answer): Promise<Answer> =
|
|
|
31
31
|
return confirm({ message, default: current === true });
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
if (option.kind === 'list' && option.choices) {
|
|
35
|
+
const chosen = new Set(Array.isArray(current) ? current : []);
|
|
36
|
+
const picked = await checkbox({
|
|
37
|
+
message,
|
|
38
|
+
pageSize: 15,
|
|
39
|
+
choices: option.choices.map((value) => ({ value, checked: chosen.has(value) })),
|
|
40
|
+
});
|
|
41
|
+
return picked.length > 0 ? picked : undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
34
44
|
if (option.kind === 'choice') {
|
|
35
45
|
const choices = [
|
|
36
46
|
{ name: '— leave unset —', value: undefined as Answer },
|
package/src/saved-card.ts
CHANGED
|
@@ -15,7 +15,7 @@ import type { Answer } from './query.ts';
|
|
|
15
15
|
|
|
16
16
|
/** A card and the answers it was rendered from: the shape of the file. */
|
|
17
17
|
interface SavedCard {
|
|
18
|
-
/** Which card
|
|
18
|
+
/** Which card, by the id the catalog gives it. */
|
|
19
19
|
card: string;
|
|
20
20
|
/** The options, exactly as they reach the endpoint. */
|
|
21
21
|
options: Record<string, string>;
|
|
@@ -82,7 +82,8 @@ export const writeSavedCard = async (
|
|
|
82
82
|
* Turns saved options back into answers the menu can show and edit.
|
|
83
83
|
*
|
|
84
84
|
* Everything on a query string is a string;
|
|
85
|
-
* a boolean
|
|
85
|
+
* a boolean becomes one again, and a list splits back into its values,
|
|
86
|
+
* so each prompt opens on the answer it was saved with.
|
|
86
87
|
*
|
|
87
88
|
* @returns The answers, ready for the menu.
|
|
88
89
|
*/
|
|
@@ -91,10 +92,22 @@ export const toAnswers = (card: CardKind, options: Record<string, string>): Map<
|
|
|
91
92
|
[...card.required, ...card.options].map((option) => [option.name, option.kind]),
|
|
92
93
|
);
|
|
93
94
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
95
|
+
const toAnswer = (name: string, value: string): Answer => {
|
|
96
|
+
switch (kinds.get(name)) {
|
|
97
|
+
case 'boolean': {
|
|
98
|
+
return value === 'true';
|
|
99
|
+
}
|
|
100
|
+
case 'list': {
|
|
101
|
+
return value
|
|
102
|
+
.split(',')
|
|
103
|
+
.map((item) => item.trim())
|
|
104
|
+
.filter(Boolean);
|
|
105
|
+
}
|
|
106
|
+
default: {
|
|
107
|
+
return value;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
return new Map(Object.entries(options).map(([name, value]) => [name, toAnswer(name, value)]));
|
|
100
113
|
};
|