@wangs-ui/create-react-app 1.0.50 → 1.0.51
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/dist/bin.js +22 -1222
- package/package.json +3 -3
- package/template/agents/AGENTS.md +5 -0
- package/template/index.html +4 -3
- package/template/package.json +14 -11
- package/template/src/App.tsx +21 -61
- package/template/src/components/AuthLayout.tsx +31 -0
- package/template/src/components/Footer.tsx +41 -0
- package/template/src/index.css +1 -1
- package/template/src/main.tsx +19 -2
- package/template/src/pages/Landing.tsx +312 -0
- package/template/src/pages/Login.tsx +151 -0
- package/template/src/pages/Register.tsx +165 -0
- package/template/tsconfig.app.json +3 -2
- package/template/vite.config.ts +14 -1
- package/template/agents/CLAUDE.md +0 -22
- /package/template/{pnpm-workspace.yaml → _pnpm-workspace.yaml} +0 -0
package/dist/bin.js
CHANGED
|
@@ -1,1140 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import process$1
|
|
3
|
+
import process$1 from "node:process";
|
|
4
4
|
import fs from "node:fs";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
-
import {
|
|
7
|
-
import * as l from "node:readline";
|
|
8
|
-
import l__default from "node:readline";
|
|
9
|
-
import { ReadStream } from "node:tty";
|
|
6
|
+
import { cancel, confirm, intro, isCancel, multiselect, outro, select, spinner, text } from "@clack/prompts";
|
|
10
7
|
import { execSync } from "node:child_process";
|
|
11
|
-
|
|
12
|
-
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
13
|
-
//#endregion
|
|
14
|
-
//#region ../../node_modules/.pnpm/fast-string-truncated-width@3.0.3/node_modules/fast-string-truncated-width/dist/utils.js
|
|
15
|
-
var getCodePointsLength = (() => {
|
|
16
|
-
const SURROGATE_PAIR_RE = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g;
|
|
17
|
-
return (input) => {
|
|
18
|
-
let surrogatePairsNr = 0;
|
|
19
|
-
SURROGATE_PAIR_RE.lastIndex = 0;
|
|
20
|
-
while (SURROGATE_PAIR_RE.test(input)) surrogatePairsNr += 1;
|
|
21
|
-
return input.length - surrogatePairsNr;
|
|
22
|
-
};
|
|
23
|
-
})();
|
|
24
|
-
var isFullWidth = (x) => {
|
|
25
|
-
return x === 12288 || x >= 65281 && x <= 65376 || x >= 65504 && x <= 65510;
|
|
26
|
-
};
|
|
27
|
-
var isWideNotCJKTNotEmoji = (x) => {
|
|
28
|
-
return x === 8987 || x === 9001 || x >= 12272 && x <= 12287 || x >= 12289 && x <= 12350 || x >= 12441 && x <= 12543 || x >= 12549 && x <= 12591 || x >= 12593 && x <= 12686 || x >= 12688 && x <= 12771 || x >= 12783 && x <= 12830 || x >= 12832 && x <= 12871 || x >= 12880 && x <= 19903 || x >= 65040 && x <= 65049 || x >= 65072 && x <= 65106 || x >= 65108 && x <= 65126 || x >= 65128 && x <= 65131 || x >= 127488 && x <= 127490 || x >= 127504 && x <= 127547 || x >= 127552 && x <= 127560 || x >= 131072 && x <= 196605 || x >= 196608 && x <= 262141;
|
|
29
|
-
};
|
|
30
|
-
//#endregion
|
|
31
|
-
//#region ../../node_modules/.pnpm/fast-string-truncated-width@3.0.3/node_modules/fast-string-truncated-width/dist/index.js
|
|
32
|
-
var ANSI_RE = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]|\u001b\]8;[^;]*;.*?(?:\u0007|\u001b\u005c)/y;
|
|
33
|
-
var CONTROL_RE = /[\x00-\x08\x0A-\x1F\x7F-\x9F]{1,1000}/y;
|
|
34
|
-
var CJKT_WIDE_RE = /(?:(?![\uFF61-\uFF9F\uFF00-\uFFEF])[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}\p{Script=Tangut}]){1,1000}/uy;
|
|
35
|
-
var TAB_RE = /\t{1,1000}/y;
|
|
36
|
-
var EMOJI_RE = /[\u{1F1E6}-\u{1F1FF}]{2}|\u{1F3F4}[\u{E0061}-\u{E007A}]{2}[\u{E0030}-\u{E0039}\u{E0061}-\u{E007A}]{1,3}\u{E007F}|(?:\p{Emoji}\uFE0F\u20E3?|\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation})(?:\u200D(?:\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F\u20E3?))*/uy;
|
|
37
|
-
var LATIN_RE = /(?:[\x20-\x7E\xA0-\xFF](?!\uFE0F)){1,1000}/y;
|
|
38
|
-
var MODIFIER_RE = /\p{M}+/gu;
|
|
39
|
-
var NO_TRUNCATION$1 = {
|
|
40
|
-
limit: Infinity,
|
|
41
|
-
ellipsis: ""
|
|
42
|
-
};
|
|
43
|
-
var getStringTruncatedWidth = (input, truncationOptions = {}, widthOptions = {}) => {
|
|
44
|
-
const LIMIT = truncationOptions.limit ?? Infinity;
|
|
45
|
-
const ELLIPSIS = truncationOptions.ellipsis ?? "";
|
|
46
|
-
const ELLIPSIS_WIDTH = truncationOptions?.ellipsisWidth ?? (ELLIPSIS ? getStringTruncatedWidth(ELLIPSIS, NO_TRUNCATION$1, widthOptions).width : 0);
|
|
47
|
-
const ANSI_WIDTH = 0;
|
|
48
|
-
const CONTROL_WIDTH = widthOptions.controlWidth ?? 0;
|
|
49
|
-
const TAB_WIDTH = widthOptions.tabWidth ?? 8;
|
|
50
|
-
const EMOJI_WIDTH = widthOptions.emojiWidth ?? 2;
|
|
51
|
-
const FULL_WIDTH_WIDTH = 2;
|
|
52
|
-
const REGULAR_WIDTH = widthOptions.regularWidth ?? 1;
|
|
53
|
-
const WIDE_WIDTH = widthOptions.wideWidth ?? FULL_WIDTH_WIDTH;
|
|
54
|
-
const PARSE_BLOCKS = [
|
|
55
|
-
[LATIN_RE, REGULAR_WIDTH],
|
|
56
|
-
[ANSI_RE, ANSI_WIDTH],
|
|
57
|
-
[CONTROL_RE, CONTROL_WIDTH],
|
|
58
|
-
[TAB_RE, TAB_WIDTH],
|
|
59
|
-
[EMOJI_RE, EMOJI_WIDTH],
|
|
60
|
-
[CJKT_WIDE_RE, WIDE_WIDTH]
|
|
61
|
-
];
|
|
62
|
-
let indexPrev = 0;
|
|
63
|
-
let index = 0;
|
|
64
|
-
let length = input.length;
|
|
65
|
-
let lengthExtra = 0;
|
|
66
|
-
let truncationEnabled = false;
|
|
67
|
-
let truncationIndex = length;
|
|
68
|
-
let truncationLimit = Math.max(0, LIMIT - ELLIPSIS_WIDTH);
|
|
69
|
-
let unmatchedStart = 0;
|
|
70
|
-
let unmatchedEnd = 0;
|
|
71
|
-
let width = 0;
|
|
72
|
-
let widthExtra = 0;
|
|
73
|
-
outer: while (true) {
|
|
74
|
-
if (unmatchedEnd > unmatchedStart || index >= length && index > indexPrev) {
|
|
75
|
-
const unmatched = input.slice(unmatchedStart, unmatchedEnd) || input.slice(indexPrev, index);
|
|
76
|
-
lengthExtra = 0;
|
|
77
|
-
for (const char of unmatched.replaceAll(MODIFIER_RE, "")) {
|
|
78
|
-
const codePoint = char.codePointAt(0) || 0;
|
|
79
|
-
if (isFullWidth(codePoint)) widthExtra = FULL_WIDTH_WIDTH;
|
|
80
|
-
else if (isWideNotCJKTNotEmoji(codePoint)) widthExtra = WIDE_WIDTH;
|
|
81
|
-
else widthExtra = REGULAR_WIDTH;
|
|
82
|
-
if (width + widthExtra > truncationLimit) truncationIndex = Math.min(truncationIndex, Math.max(unmatchedStart, indexPrev) + lengthExtra);
|
|
83
|
-
if (width + widthExtra > LIMIT) {
|
|
84
|
-
truncationEnabled = true;
|
|
85
|
-
break outer;
|
|
86
|
-
}
|
|
87
|
-
lengthExtra += char.length;
|
|
88
|
-
width += widthExtra;
|
|
89
|
-
}
|
|
90
|
-
unmatchedStart = unmatchedEnd = 0;
|
|
91
|
-
}
|
|
92
|
-
if (index >= length) break outer;
|
|
93
|
-
for (let i = 0, l = PARSE_BLOCKS.length; i < l; i++) {
|
|
94
|
-
const [BLOCK_RE, BLOCK_WIDTH] = PARSE_BLOCKS[i];
|
|
95
|
-
BLOCK_RE.lastIndex = index;
|
|
96
|
-
if (BLOCK_RE.test(input)) {
|
|
97
|
-
lengthExtra = BLOCK_RE === CJKT_WIDE_RE ? getCodePointsLength(input.slice(index, BLOCK_RE.lastIndex)) : BLOCK_RE === EMOJI_RE ? 1 : BLOCK_RE.lastIndex - index;
|
|
98
|
-
widthExtra = lengthExtra * BLOCK_WIDTH;
|
|
99
|
-
if (width + widthExtra > truncationLimit) truncationIndex = Math.min(truncationIndex, index + Math.floor((truncationLimit - width) / BLOCK_WIDTH));
|
|
100
|
-
if (width + widthExtra > LIMIT) {
|
|
101
|
-
truncationEnabled = true;
|
|
102
|
-
break outer;
|
|
103
|
-
}
|
|
104
|
-
width += widthExtra;
|
|
105
|
-
unmatchedStart = indexPrev;
|
|
106
|
-
unmatchedEnd = index;
|
|
107
|
-
index = indexPrev = BLOCK_RE.lastIndex;
|
|
108
|
-
continue outer;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
index += 1;
|
|
112
|
-
}
|
|
113
|
-
return {
|
|
114
|
-
width: truncationEnabled ? truncationLimit : width,
|
|
115
|
-
index: truncationEnabled ? truncationIndex : length,
|
|
116
|
-
truncated: truncationEnabled,
|
|
117
|
-
ellipsed: truncationEnabled && LIMIT >= ELLIPSIS_WIDTH
|
|
118
|
-
};
|
|
119
|
-
};
|
|
120
|
-
//#endregion
|
|
121
|
-
//#region ../../node_modules/.pnpm/fast-string-width@3.0.2/node_modules/fast-string-width/dist/index.js
|
|
122
|
-
var NO_TRUNCATION = {
|
|
123
|
-
limit: Infinity,
|
|
124
|
-
ellipsis: "",
|
|
125
|
-
ellipsisWidth: 0
|
|
126
|
-
};
|
|
127
|
-
var fastStringWidth = (input, options = {}) => {
|
|
128
|
-
return getStringTruncatedWidth(input, NO_TRUNCATION, options).width;
|
|
129
|
-
};
|
|
130
|
-
//#endregion
|
|
131
|
-
//#region ../../node_modules/.pnpm/fast-wrap-ansi@0.2.2/node_modules/fast-wrap-ansi/lib/main.js
|
|
132
|
-
var ESC = "\x1B";
|
|
133
|
-
var CSI = "";
|
|
134
|
-
var END_CODE = 39;
|
|
135
|
-
var ANSI_ESCAPE_BELL = "\x07";
|
|
136
|
-
var ANSI_CSI = "[";
|
|
137
|
-
var ANSI_OSC = "]";
|
|
138
|
-
var ANSI_SGR_TERMINATOR = "m";
|
|
139
|
-
var ANSI_ESCAPE_LINK = `${ANSI_OSC}8;;`;
|
|
140
|
-
var GROUP_REGEX = new RegExp(`(?:\\${ANSI_CSI}(?<code>\\d+)m|\\${ANSI_ESCAPE_LINK}(?<uri>.*)${ANSI_ESCAPE_BELL})`, "y");
|
|
141
|
-
var getClosingCode = (openingCode) => {
|
|
142
|
-
if (openingCode >= 30 && openingCode <= 37) return 39;
|
|
143
|
-
if (openingCode >= 90 && openingCode <= 97) return 39;
|
|
144
|
-
if (openingCode >= 40 && openingCode <= 47) return 49;
|
|
145
|
-
if (openingCode >= 100 && openingCode <= 107) return 49;
|
|
146
|
-
if (openingCode === 1 || openingCode === 2) return 22;
|
|
147
|
-
if (openingCode === 3) return 23;
|
|
148
|
-
if (openingCode === 4) return 24;
|
|
149
|
-
if (openingCode === 7) return 27;
|
|
150
|
-
if (openingCode === 8) return 28;
|
|
151
|
-
if (openingCode === 9) return 29;
|
|
152
|
-
if (openingCode === 0) return 0;
|
|
153
|
-
};
|
|
154
|
-
var wrapAnsiCode = (code) => `${ESC}${ANSI_CSI}${code}${ANSI_SGR_TERMINATOR}`;
|
|
155
|
-
var wrapAnsiHyperlink = (url) => `${ESC}${ANSI_ESCAPE_LINK}${url}${ANSI_ESCAPE_BELL}`;
|
|
156
|
-
var wrapWord = (rows, word, columns) => {
|
|
157
|
-
const characters = word[Symbol.iterator]();
|
|
158
|
-
let isInsideEscape = false;
|
|
159
|
-
let isInsideLinkEscape = false;
|
|
160
|
-
let lastRow = rows.at(-1);
|
|
161
|
-
let visible = lastRow === void 0 ? 0 : fastStringWidth(lastRow);
|
|
162
|
-
let currentCharacter = characters.next();
|
|
163
|
-
let nextCharacter = characters.next();
|
|
164
|
-
let rawCharacterIndex = 0;
|
|
165
|
-
while (!currentCharacter.done) {
|
|
166
|
-
const character = currentCharacter.value;
|
|
167
|
-
const characterLength = fastStringWidth(character);
|
|
168
|
-
if (visible + characterLength <= columns) rows[rows.length - 1] += character;
|
|
169
|
-
else {
|
|
170
|
-
rows.push(character);
|
|
171
|
-
visible = 0;
|
|
172
|
-
}
|
|
173
|
-
if (character === ESC || character === CSI) {
|
|
174
|
-
isInsideEscape = true;
|
|
175
|
-
isInsideLinkEscape = word.startsWith(ANSI_ESCAPE_LINK, rawCharacterIndex + 1);
|
|
176
|
-
}
|
|
177
|
-
if (isInsideEscape) {
|
|
178
|
-
if (isInsideLinkEscape) {
|
|
179
|
-
if (character === ANSI_ESCAPE_BELL) {
|
|
180
|
-
isInsideEscape = false;
|
|
181
|
-
isInsideLinkEscape = false;
|
|
182
|
-
}
|
|
183
|
-
} else if (character === ANSI_SGR_TERMINATOR) isInsideEscape = false;
|
|
184
|
-
} else {
|
|
185
|
-
visible += characterLength;
|
|
186
|
-
if (visible === columns && !nextCharacter.done) {
|
|
187
|
-
rows.push("");
|
|
188
|
-
visible = 0;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
currentCharacter = nextCharacter;
|
|
192
|
-
nextCharacter = characters.next();
|
|
193
|
-
rawCharacterIndex += character.length;
|
|
194
|
-
}
|
|
195
|
-
lastRow = rows.at(-1);
|
|
196
|
-
if (!visible && lastRow !== void 0 && lastRow.length && rows.length > 1) rows[rows.length - 2] += rows.pop();
|
|
197
|
-
};
|
|
198
|
-
var stringVisibleTrimSpacesRight = (string) => {
|
|
199
|
-
const words = string.split(" ");
|
|
200
|
-
let last = words.length;
|
|
201
|
-
while (last) {
|
|
202
|
-
if (fastStringWidth(words[last - 1])) break;
|
|
203
|
-
last--;
|
|
204
|
-
}
|
|
205
|
-
if (last === words.length) return string;
|
|
206
|
-
return words.slice(0, last).join(" ") + words.slice(last).join("");
|
|
207
|
-
};
|
|
208
|
-
var exec = (string, columns, options = {}) => {
|
|
209
|
-
if (options.trim !== false && string.trim() === "") return "";
|
|
210
|
-
let returnValue = "";
|
|
211
|
-
let escapeCode;
|
|
212
|
-
let escapeUrl;
|
|
213
|
-
const words = string.split(" ");
|
|
214
|
-
let rows = [""];
|
|
215
|
-
let rowLength = 0;
|
|
216
|
-
for (let index = 0; index < words.length; index++) {
|
|
217
|
-
const word = words[index];
|
|
218
|
-
if (options.trim !== false) {
|
|
219
|
-
const row = rows.at(-1) ?? "";
|
|
220
|
-
const trimmed = row.trimStart();
|
|
221
|
-
if (row.length !== trimmed.length) {
|
|
222
|
-
rows[rows.length - 1] = trimmed;
|
|
223
|
-
rowLength = fastStringWidth(trimmed);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
if (index !== 0) {
|
|
227
|
-
if (rowLength >= columns && (options.wordWrap === false || options.trim === false)) {
|
|
228
|
-
rows.push("");
|
|
229
|
-
rowLength = 0;
|
|
230
|
-
}
|
|
231
|
-
if (rowLength || options.trim === false) {
|
|
232
|
-
rows[rows.length - 1] += " ";
|
|
233
|
-
rowLength++;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
const wordLength = fastStringWidth(word);
|
|
237
|
-
if (options.hard && wordLength > columns) {
|
|
238
|
-
const remainingColumns = columns - rowLength;
|
|
239
|
-
const breaksStartingThisLine = 1 + Math.floor((wordLength - remainingColumns - 1) / columns);
|
|
240
|
-
if (Math.floor((wordLength - 1) / columns) < breaksStartingThisLine) rows.push("");
|
|
241
|
-
wrapWord(rows, word, columns);
|
|
242
|
-
rowLength = fastStringWidth(rows.at(-1) ?? "");
|
|
243
|
-
continue;
|
|
244
|
-
}
|
|
245
|
-
if (rowLength + wordLength > columns && rowLength && wordLength) {
|
|
246
|
-
if (options.wordWrap === false && rowLength < columns) {
|
|
247
|
-
wrapWord(rows, word, columns);
|
|
248
|
-
rowLength = fastStringWidth(rows.at(-1) ?? "");
|
|
249
|
-
continue;
|
|
250
|
-
}
|
|
251
|
-
rows.push("");
|
|
252
|
-
rowLength = 0;
|
|
253
|
-
}
|
|
254
|
-
if (rowLength + wordLength > columns && options.wordWrap === false) {
|
|
255
|
-
wrapWord(rows, word, columns);
|
|
256
|
-
rowLength = fastStringWidth(rows.at(-1) ?? "");
|
|
257
|
-
continue;
|
|
258
|
-
}
|
|
259
|
-
rows[rows.length - 1] += word;
|
|
260
|
-
rowLength += wordLength;
|
|
261
|
-
}
|
|
262
|
-
if (options.trim !== false) rows = rows.map((row) => stringVisibleTrimSpacesRight(row));
|
|
263
|
-
const preString = rows.join("\n");
|
|
264
|
-
let inSurrogate = false;
|
|
265
|
-
for (let i = 0; i < preString.length; i++) {
|
|
266
|
-
const character = preString[i];
|
|
267
|
-
returnValue += character;
|
|
268
|
-
if (!inSurrogate) {
|
|
269
|
-
inSurrogate = character >= "\ud800" && character <= "\udbff";
|
|
270
|
-
if (inSurrogate) continue;
|
|
271
|
-
} else inSurrogate = false;
|
|
272
|
-
if (character === ESC || character === CSI) {
|
|
273
|
-
GROUP_REGEX.lastIndex = i + 1;
|
|
274
|
-
const groups = GROUP_REGEX.exec(preString)?.groups;
|
|
275
|
-
if (groups?.code !== void 0) {
|
|
276
|
-
const code = Number.parseFloat(groups.code);
|
|
277
|
-
escapeCode = code === END_CODE ? void 0 : code;
|
|
278
|
-
} else if (groups?.uri !== void 0) escapeUrl = groups.uri.length === 0 ? void 0 : groups.uri;
|
|
279
|
-
}
|
|
280
|
-
if (preString[i + 1] === "\n") {
|
|
281
|
-
if (escapeUrl) returnValue += wrapAnsiHyperlink("");
|
|
282
|
-
const closingCode = escapeCode ? getClosingCode(escapeCode) : void 0;
|
|
283
|
-
if (escapeCode && closingCode) returnValue += wrapAnsiCode(closingCode);
|
|
284
|
-
} else if (character === "\n") {
|
|
285
|
-
if (escapeCode && getClosingCode(escapeCode)) returnValue += wrapAnsiCode(escapeCode);
|
|
286
|
-
if (escapeUrl) returnValue += wrapAnsiHyperlink(escapeUrl);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
return returnValue;
|
|
290
|
-
};
|
|
291
|
-
var CRLF_OR_LF = /\r?\n/;
|
|
292
|
-
function wrapAnsi(string, columns, options) {
|
|
293
|
-
return String(string).normalize().split(CRLF_OR_LF).map((line) => exec(line, columns, options)).join("\n");
|
|
294
|
-
}
|
|
295
|
-
//#endregion
|
|
296
|
-
//#region ../../node_modules/.pnpm/@clack+core@1.4.3/node_modules/@clack/core/dist/index.mjs
|
|
297
|
-
var import_src = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
298
|
-
var ESC = "\x1B";
|
|
299
|
-
var CSI = `${ESC}[`;
|
|
300
|
-
var beep = "\x07";
|
|
301
|
-
var cursor = {
|
|
302
|
-
to(x, y) {
|
|
303
|
-
if (!y) return `${CSI}${x + 1}G`;
|
|
304
|
-
return `${CSI}${y + 1};${x + 1}H`;
|
|
305
|
-
},
|
|
306
|
-
move(x, y) {
|
|
307
|
-
let ret = "";
|
|
308
|
-
if (x < 0) ret += `${CSI}${-x}D`;
|
|
309
|
-
else if (x > 0) ret += `${CSI}${x}C`;
|
|
310
|
-
if (y < 0) ret += `${CSI}${-y}A`;
|
|
311
|
-
else if (y > 0) ret += `${CSI}${y}B`;
|
|
312
|
-
return ret;
|
|
313
|
-
},
|
|
314
|
-
up: (count = 1) => `${CSI}${count}A`,
|
|
315
|
-
down: (count = 1) => `${CSI}${count}B`,
|
|
316
|
-
forward: (count = 1) => `${CSI}${count}C`,
|
|
317
|
-
backward: (count = 1) => `${CSI}${count}D`,
|
|
318
|
-
nextLine: (count = 1) => `${CSI}E`.repeat(count),
|
|
319
|
-
prevLine: (count = 1) => `${CSI}F`.repeat(count),
|
|
320
|
-
left: `${CSI}G`,
|
|
321
|
-
hide: `${CSI}?25l`,
|
|
322
|
-
show: `${CSI}?25h`,
|
|
323
|
-
save: `${ESC}7`,
|
|
324
|
-
restore: `${ESC}8`
|
|
325
|
-
};
|
|
326
|
-
module.exports = {
|
|
327
|
-
cursor,
|
|
328
|
-
scroll: {
|
|
329
|
-
up: (count = 1) => `${CSI}S`.repeat(count),
|
|
330
|
-
down: (count = 1) => `${CSI}T`.repeat(count)
|
|
331
|
-
},
|
|
332
|
-
erase: {
|
|
333
|
-
screen: `${CSI}2J`,
|
|
334
|
-
up: (count = 1) => `${CSI}1J`.repeat(count),
|
|
335
|
-
down: (count = 1) => `${CSI}J`.repeat(count),
|
|
336
|
-
line: `${CSI}2K`,
|
|
337
|
-
lineEnd: `${CSI}K`,
|
|
338
|
-
lineStart: `${CSI}1K`,
|
|
339
|
-
lines(count) {
|
|
340
|
-
let clear = "";
|
|
341
|
-
for (let i = 0; i < count; i++) clear += this.line + (i < count - 1 ? cursor.up() : "");
|
|
342
|
-
if (count) clear += cursor.left;
|
|
343
|
-
return clear;
|
|
344
|
-
}
|
|
345
|
-
},
|
|
346
|
-
beep
|
|
347
|
-
};
|
|
348
|
-
})))();
|
|
349
|
-
function findCursor(s, o, l) {
|
|
350
|
-
if (!l.some((r) => !r.disabled)) return s;
|
|
351
|
-
const t = s + o, n = Math.max(l.length - 1, 0), e = t < 0 ? n : t > n ? 0 : t;
|
|
352
|
-
return l[e]?.disabled ? findCursor(e, o < 0 ? -1 : 1, l) : e;
|
|
353
|
-
}
|
|
354
|
-
var settings = {
|
|
355
|
-
actions: new Set([
|
|
356
|
-
"up",
|
|
357
|
-
"down",
|
|
358
|
-
"left",
|
|
359
|
-
"right",
|
|
360
|
-
"space",
|
|
361
|
-
"enter",
|
|
362
|
-
"cancel"
|
|
363
|
-
]),
|
|
364
|
-
aliases: /* @__PURE__ */ new Map([
|
|
365
|
-
["k", "up"],
|
|
366
|
-
["j", "down"],
|
|
367
|
-
["h", "left"],
|
|
368
|
-
["l", "right"],
|
|
369
|
-
["", "cancel"],
|
|
370
|
-
["escape", "cancel"]
|
|
371
|
-
]),
|
|
372
|
-
messages: {
|
|
373
|
-
cancel: "Canceled",
|
|
374
|
-
error: "Something went wrong"
|
|
375
|
-
},
|
|
376
|
-
withGuide: true,
|
|
377
|
-
date: {
|
|
378
|
-
monthNames: [...[
|
|
379
|
-
"January",
|
|
380
|
-
"February",
|
|
381
|
-
"March",
|
|
382
|
-
"April",
|
|
383
|
-
"May",
|
|
384
|
-
"June",
|
|
385
|
-
"July",
|
|
386
|
-
"August",
|
|
387
|
-
"September",
|
|
388
|
-
"October",
|
|
389
|
-
"November",
|
|
390
|
-
"December"
|
|
391
|
-
]],
|
|
392
|
-
messages: {
|
|
393
|
-
required: "Please enter a valid date",
|
|
394
|
-
invalidMonth: "There are only 12 months in a year",
|
|
395
|
-
invalidDay: (n, e) => `There are only ${n} days in ${e}`,
|
|
396
|
-
afterMin: (n) => `Date must be on or after ${n.toISOString().slice(0, 10)}`,
|
|
397
|
-
beforeMax: (n) => `Date must be on or before ${n.toISOString().slice(0, 10)}`
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
};
|
|
401
|
-
function isActionKey(n, e) {
|
|
402
|
-
if (typeof n == "string") return settings.aliases.get(n) === e;
|
|
403
|
-
for (const s of n) if (s !== void 0 && isActionKey(s, e)) return true;
|
|
404
|
-
return false;
|
|
405
|
-
}
|
|
406
|
-
function diffLines(i, s) {
|
|
407
|
-
if (i === s) return;
|
|
408
|
-
const e = i.split(`
|
|
409
|
-
`), t = s.split(`
|
|
410
|
-
`), r = Math.max(e.length, t.length), f = [];
|
|
411
|
-
for (let n = 0; n < r; n++) e[n] !== t[n] && f.push(n);
|
|
412
|
-
return {
|
|
413
|
-
lines: f,
|
|
414
|
-
numLinesBefore: e.length,
|
|
415
|
-
numLinesAfter: t.length,
|
|
416
|
-
numLines: r
|
|
417
|
-
};
|
|
418
|
-
}
|
|
419
|
-
var R = globalThis.process.platform.startsWith("win");
|
|
420
|
-
var CANCEL_SYMBOL = Symbol("clack:cancel");
|
|
421
|
-
function isCancel(e) {
|
|
422
|
-
return e === CANCEL_SYMBOL;
|
|
423
|
-
}
|
|
424
|
-
function setRawMode(e, r) {
|
|
425
|
-
const o = e;
|
|
426
|
-
o.isTTY && o.setRawMode(r);
|
|
427
|
-
}
|
|
428
|
-
function block({ input: e = stdin, output: r = stdout, overwrite: o = true, hideCursor: t = true } = {}) {
|
|
429
|
-
const s = l.createInterface({
|
|
430
|
-
input: e,
|
|
431
|
-
output: r,
|
|
432
|
-
prompt: "",
|
|
433
|
-
tabSize: 1
|
|
434
|
-
});
|
|
435
|
-
l.emitKeypressEvents(e, s), e instanceof ReadStream && e.isTTY && e.setRawMode(true);
|
|
436
|
-
const n = (f, { name: a, sequence: p }) => {
|
|
437
|
-
if (isActionKey([
|
|
438
|
-
String(f),
|
|
439
|
-
a,
|
|
440
|
-
p
|
|
441
|
-
], "cancel")) {
|
|
442
|
-
t && r.write(import_src.cursor.show), process.exit(0);
|
|
443
|
-
return;
|
|
444
|
-
}
|
|
445
|
-
if (!o) return;
|
|
446
|
-
const i = a === "return" ? 0 : -1, m = a === "return" ? -1 : 0;
|
|
447
|
-
l.moveCursor(r, i, m, () => {
|
|
448
|
-
l.clearLine(r, 1, () => {
|
|
449
|
-
e.once("keypress", n);
|
|
450
|
-
});
|
|
451
|
-
});
|
|
452
|
-
};
|
|
453
|
-
return t && r.write(import_src.cursor.hide), e.once("keypress", n), () => {
|
|
454
|
-
e.off("keypress", n), t && r.write(import_src.cursor.show), e instanceof ReadStream && e.isTTY && !R && e.setRawMode(false), s.terminal = false, s.close();
|
|
455
|
-
};
|
|
456
|
-
}
|
|
457
|
-
var getColumns = (e) => "columns" in e && typeof e.columns == "number" ? e.columns : 80, getRows = (e) => "rows" in e && typeof e.rows == "number" ? e.rows : 20;
|
|
458
|
-
function wrapTextWithPrefix(e, r, o, t = o, s = o, n) {
|
|
459
|
-
return wrapAnsi(r, getColumns(e ?? stdout) - o.length, {
|
|
460
|
-
hard: true,
|
|
461
|
-
trim: false
|
|
462
|
-
}).split(`
|
|
463
|
-
`).map((c, i, m) => {
|
|
464
|
-
const d = n ? n(c, i) : c;
|
|
465
|
-
return i === 0 ? `${t}${d}` : i === m.length - 1 ? `${s}${d}` : `${o}${d}`;
|
|
466
|
-
}).join(`
|
|
467
|
-
`);
|
|
468
|
-
}
|
|
469
|
-
function runValidation(e, n) {
|
|
470
|
-
if ("~standard" in e) {
|
|
471
|
-
const a = e["~standard"].validate(n);
|
|
472
|
-
if (a instanceof Promise) throw new TypeError("Schema validation must be synchronous. Update `validate()` and remove any asynchronous logic.");
|
|
473
|
-
return a.issues?.at(0)?.message;
|
|
474
|
-
}
|
|
475
|
-
return e(n);
|
|
476
|
-
}
|
|
477
|
-
var V = class {
|
|
478
|
-
input;
|
|
479
|
-
output;
|
|
480
|
-
_abortSignal;
|
|
481
|
-
rl;
|
|
482
|
-
opts;
|
|
483
|
-
_render;
|
|
484
|
-
_track = false;
|
|
485
|
-
_prevFrame = "";
|
|
486
|
-
_subscribers = /* @__PURE__ */ new Map();
|
|
487
|
-
_cursor = 0;
|
|
488
|
-
state = "initial";
|
|
489
|
-
error = "";
|
|
490
|
-
value;
|
|
491
|
-
userInput = "";
|
|
492
|
-
constructor(t, e = true) {
|
|
493
|
-
const { input: i = stdin, output: n = stdout, render: s, signal: r, ...o } = t;
|
|
494
|
-
this.opts = o, this.onKeypress = this.onKeypress.bind(this), this.close = this.close.bind(this), this.render = this.render.bind(this), this._render = s.bind(this), this._track = e, this._abortSignal = r, this.input = i, this.output = n;
|
|
495
|
-
}
|
|
496
|
-
/**
|
|
497
|
-
* Unsubscribe all listeners
|
|
498
|
-
*/
|
|
499
|
-
unsubscribe() {
|
|
500
|
-
this._subscribers.clear();
|
|
501
|
-
}
|
|
502
|
-
/**
|
|
503
|
-
* Set a subscriber with opts
|
|
504
|
-
* @param event - The event name
|
|
505
|
-
*/
|
|
506
|
-
setSubscriber(t, e) {
|
|
507
|
-
const i = this._subscribers.get(t) ?? [];
|
|
508
|
-
i.push(e), this._subscribers.set(t, i);
|
|
509
|
-
}
|
|
510
|
-
/**
|
|
511
|
-
* Subscribe to an event
|
|
512
|
-
* @param event - The event name
|
|
513
|
-
* @param cb - The callback
|
|
514
|
-
*/
|
|
515
|
-
on(t, e) {
|
|
516
|
-
this.setSubscriber(t, { cb: e });
|
|
517
|
-
}
|
|
518
|
-
/**
|
|
519
|
-
* Subscribe to an event once
|
|
520
|
-
* @param event - The event name
|
|
521
|
-
* @param cb - The callback
|
|
522
|
-
*/
|
|
523
|
-
once(t, e) {
|
|
524
|
-
this.setSubscriber(t, {
|
|
525
|
-
cb: e,
|
|
526
|
-
once: true
|
|
527
|
-
});
|
|
528
|
-
}
|
|
529
|
-
/**
|
|
530
|
-
* Emit an event with data
|
|
531
|
-
* @param event - The event name
|
|
532
|
-
* @param data - The data to pass to the callback
|
|
533
|
-
*/
|
|
534
|
-
emit(t, ...e) {
|
|
535
|
-
const i = this._subscribers.get(t) ?? [], n = [];
|
|
536
|
-
for (const s of i) s.cb(...e), s.once && n.push(() => i.splice(i.indexOf(s), 1));
|
|
537
|
-
for (const s of n) s();
|
|
538
|
-
}
|
|
539
|
-
prompt() {
|
|
540
|
-
return new Promise((t) => {
|
|
541
|
-
if (this._abortSignal) {
|
|
542
|
-
if (this._abortSignal.aborted) return this.state = "cancel", this.close(), t(CANCEL_SYMBOL);
|
|
543
|
-
this._abortSignal.addEventListener("abort", () => {
|
|
544
|
-
this.state = "cancel", this.close();
|
|
545
|
-
}, { once: true });
|
|
546
|
-
}
|
|
547
|
-
this.rl = l__default.createInterface({
|
|
548
|
-
input: this.input,
|
|
549
|
-
tabSize: 2,
|
|
550
|
-
prompt: "",
|
|
551
|
-
escapeCodeTimeout: 50,
|
|
552
|
-
terminal: true
|
|
553
|
-
}), this.rl.prompt(), this.opts.initialUserInput !== void 0 && this._setUserInput(this.opts.initialUserInput, true), this.input.on("keypress", this.onKeypress), setRawMode(this.input, true), this.output.on("resize", this.render), this.render(), this.once("submit", () => {
|
|
554
|
-
this.output.write(import_src.cursor.show), this.output.off("resize", this.render), setRawMode(this.input, false), t(this.value);
|
|
555
|
-
}), this.once("cancel", () => {
|
|
556
|
-
this.output.write(import_src.cursor.show), this.output.off("resize", this.render), setRawMode(this.input, false), t(CANCEL_SYMBOL);
|
|
557
|
-
});
|
|
558
|
-
});
|
|
559
|
-
}
|
|
560
|
-
_isActionKey(t, e) {
|
|
561
|
-
return t === " ";
|
|
562
|
-
}
|
|
563
|
-
_shouldSubmit(t, e) {
|
|
564
|
-
return true;
|
|
565
|
-
}
|
|
566
|
-
_setValue(t) {
|
|
567
|
-
this.value = t, this.emit("value", this.value);
|
|
568
|
-
}
|
|
569
|
-
_setUserInput(t, e) {
|
|
570
|
-
this.userInput = t ?? "", this.emit("userInput", this.userInput), e && this._track && this.rl && (this.rl.write(this.userInput), this._cursor = this.rl.cursor);
|
|
571
|
-
}
|
|
572
|
-
_clearUserInput() {
|
|
573
|
-
this.rl?.write(null, {
|
|
574
|
-
ctrl: true,
|
|
575
|
-
name: "u"
|
|
576
|
-
}), this._setUserInput("");
|
|
577
|
-
}
|
|
578
|
-
onKeypress(t, e) {
|
|
579
|
-
if (this._track && e.name !== "return" && (e.name && this._isActionKey(t, e) && this.rl?.write(null, {
|
|
580
|
-
ctrl: true,
|
|
581
|
-
name: "h"
|
|
582
|
-
}), this._cursor = this.rl?.cursor ?? 0, this._setUserInput(this.rl?.line)), this.state === "error" && (this.state = "active"), e?.name && (!this._track && settings.aliases.has(e.name) && this.emit("cursor", settings.aliases.get(e.name)), settings.actions.has(e.name) && this.emit("cursor", e.name)), t && (t.toLowerCase() === "y" || t.toLowerCase() === "n") && this.emit("confirm", t.toLowerCase() === "y"), this.emit("key", t, e), e?.name === "return" && this._shouldSubmit(t, e)) {
|
|
583
|
-
if (this.opts.validate) {
|
|
584
|
-
const i = runValidation(this.opts.validate, this.value);
|
|
585
|
-
i && (this.error = i instanceof Error ? i.message : i, this.state = "error", this.rl?.write(this.userInput));
|
|
586
|
-
}
|
|
587
|
-
this.state !== "error" && (this.state = "submit");
|
|
588
|
-
}
|
|
589
|
-
isActionKey([
|
|
590
|
-
t,
|
|
591
|
-
e?.name,
|
|
592
|
-
e?.sequence
|
|
593
|
-
], "cancel") && (this.state = "cancel"), (this.state === "submit" || this.state === "cancel") && this.emit("finalize"), this.render(), (this.state === "submit" || this.state === "cancel") && this.close();
|
|
594
|
-
}
|
|
595
|
-
close() {
|
|
596
|
-
this.input.unpipe(), this.input.removeListener("keypress", this.onKeypress), this.output.write(`
|
|
597
|
-
`), setRawMode(this.input, false), this.rl?.close(), this.rl = void 0, this.emit(`${this.state}`, this.value), this.unsubscribe();
|
|
598
|
-
}
|
|
599
|
-
restoreCursor() {
|
|
600
|
-
const t = wrapAnsi(this._prevFrame, process.stdout.columns, {
|
|
601
|
-
hard: true,
|
|
602
|
-
trim: false
|
|
603
|
-
}).split(`
|
|
604
|
-
`).length - 1;
|
|
605
|
-
this.output.write(import_src.cursor.move(-999, t * -1));
|
|
606
|
-
}
|
|
607
|
-
render() {
|
|
608
|
-
const t = wrapAnsi(this._render(this) ?? "", process.stdout.columns, {
|
|
609
|
-
hard: true,
|
|
610
|
-
trim: false
|
|
611
|
-
});
|
|
612
|
-
if (t !== this._prevFrame) {
|
|
613
|
-
if (this.state === "initial") this.output.write(import_src.cursor.hide);
|
|
614
|
-
else {
|
|
615
|
-
const e = diffLines(this._prevFrame, t), i = getRows(this.output);
|
|
616
|
-
if (this.restoreCursor(), e) {
|
|
617
|
-
const n = Math.max(0, e.numLinesAfter - i), s = Math.max(0, e.numLinesBefore - i);
|
|
618
|
-
let r = e.lines.find((o) => o >= n);
|
|
619
|
-
if (r === void 0) {
|
|
620
|
-
this._prevFrame = t;
|
|
621
|
-
return;
|
|
622
|
-
}
|
|
623
|
-
if (e.lines.length === 1) {
|
|
624
|
-
this.output.write(import_src.cursor.move(0, r - s)), this.output.write(import_src.erase.lines(1));
|
|
625
|
-
const o = t.split(`
|
|
626
|
-
`);
|
|
627
|
-
this.output.write(o[r]), this._prevFrame = t, this.output.write(import_src.cursor.move(0, o.length - r - 1));
|
|
628
|
-
return;
|
|
629
|
-
} else if (e.lines.length > 1) {
|
|
630
|
-
if (n < s) r = n;
|
|
631
|
-
else {
|
|
632
|
-
const h = r - s;
|
|
633
|
-
h > 0 && this.output.write(import_src.cursor.move(0, h));
|
|
634
|
-
}
|
|
635
|
-
this.output.write(import_src.erase.down());
|
|
636
|
-
const f = t.split(`
|
|
637
|
-
`).slice(r);
|
|
638
|
-
this.output.write(f.join(`
|
|
639
|
-
`)), this._prevFrame = t;
|
|
640
|
-
return;
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
this.output.write(import_src.erase.down());
|
|
644
|
-
}
|
|
645
|
-
this.output.write(t), this.state === "initial" && (this.state = "active"), this._prevFrame = t;
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
};
|
|
649
|
-
var r = class extends V {
|
|
650
|
-
get cursor() {
|
|
651
|
-
return this.value ? 0 : 1;
|
|
652
|
-
}
|
|
653
|
-
get _value() {
|
|
654
|
-
return this.cursor === 0;
|
|
655
|
-
}
|
|
656
|
-
constructor(t) {
|
|
657
|
-
super(t, false), this.value = !!t.initialValue, this.on("userInput", () => {
|
|
658
|
-
this.value = this._value;
|
|
659
|
-
}), this.on("confirm", (i) => {
|
|
660
|
-
this.output.write(import_src.cursor.move(0, -1)), this.value = i, this.state = "submit", this.close();
|
|
661
|
-
}), this.on("cursor", () => {
|
|
662
|
-
this.value = !this.value;
|
|
663
|
-
});
|
|
664
|
-
}
|
|
665
|
-
};
|
|
666
|
-
var a = class extends V {
|
|
667
|
-
options;
|
|
668
|
-
cursor = 0;
|
|
669
|
-
get _value() {
|
|
670
|
-
return this.options[this.cursor]?.value;
|
|
671
|
-
}
|
|
672
|
-
get _enabledOptions() {
|
|
673
|
-
return this.options.filter((e) => e.disabled !== true);
|
|
674
|
-
}
|
|
675
|
-
toggleAll() {
|
|
676
|
-
const e = this._enabledOptions, i = this.value !== void 0 && this.value.length === e.length;
|
|
677
|
-
this.value = i ? [] : e.map((t) => t.value);
|
|
678
|
-
}
|
|
679
|
-
toggleInvert() {
|
|
680
|
-
const e = this.value;
|
|
681
|
-
if (!e) return;
|
|
682
|
-
const i = this._enabledOptions.filter((t) => !e.includes(t.value));
|
|
683
|
-
this.value = i.map((t) => t.value);
|
|
684
|
-
}
|
|
685
|
-
toggleValue() {
|
|
686
|
-
this.value === void 0 && (this.value = []);
|
|
687
|
-
const e = this.value.includes(this._value);
|
|
688
|
-
this.value = e ? this.value.filter((i) => i !== this._value) : [...this.value, this._value];
|
|
689
|
-
}
|
|
690
|
-
constructor(e) {
|
|
691
|
-
super(e, false), this.options = e.options, this.value = [...e.initialValues ?? []];
|
|
692
|
-
const i = Math.max(this.options.findIndex(({ value: t }) => t === e.cursorAt), 0);
|
|
693
|
-
this.cursor = this.options[i]?.disabled ? findCursor(i, 1, this.options) : i, this.on("key", (t, l) => {
|
|
694
|
-
l.name === "a" && this.toggleAll(), l.name === "i" && this.toggleInvert();
|
|
695
|
-
}), this.on("cursor", (t) => {
|
|
696
|
-
switch (t) {
|
|
697
|
-
case "left":
|
|
698
|
-
case "up":
|
|
699
|
-
this.cursor = findCursor(this.cursor, -1, this.options);
|
|
700
|
-
break;
|
|
701
|
-
case "down":
|
|
702
|
-
case "right":
|
|
703
|
-
this.cursor = findCursor(this.cursor, 1, this.options);
|
|
704
|
-
break;
|
|
705
|
-
case "space":
|
|
706
|
-
this.toggleValue();
|
|
707
|
-
break;
|
|
708
|
-
}
|
|
709
|
-
});
|
|
710
|
-
}
|
|
711
|
-
};
|
|
712
|
-
var n$1 = class n extends V {
|
|
713
|
-
options;
|
|
714
|
-
cursor = 0;
|
|
715
|
-
get _selectedValue() {
|
|
716
|
-
return this.options[this.cursor];
|
|
717
|
-
}
|
|
718
|
-
changeValue() {
|
|
719
|
-
const e = this._selectedValue;
|
|
720
|
-
this.value = e === void 0 ? void 0 : e.value;
|
|
721
|
-
}
|
|
722
|
-
constructor(e) {
|
|
723
|
-
super(e, false), this.options = e.options;
|
|
724
|
-
const o = this.options.findIndex(({ value: s }) => s === e.initialValue), t = o === -1 ? 0 : o;
|
|
725
|
-
this.cursor = this.options[t]?.disabled ? findCursor(t, 1, this.options) : t, this.changeValue(), this.on("cursor", (s) => {
|
|
726
|
-
switch (s) {
|
|
727
|
-
case "left":
|
|
728
|
-
case "up":
|
|
729
|
-
this.cursor = findCursor(this.cursor, -1, this.options);
|
|
730
|
-
break;
|
|
731
|
-
case "down":
|
|
732
|
-
case "right":
|
|
733
|
-
this.cursor = findCursor(this.cursor, 1, this.options);
|
|
734
|
-
break;
|
|
735
|
-
}
|
|
736
|
-
this.changeValue();
|
|
737
|
-
});
|
|
738
|
-
}
|
|
739
|
-
};
|
|
740
|
-
var n = class extends V {
|
|
741
|
-
get userInputWithCursor() {
|
|
742
|
-
if (this.state === "submit") return this.userInput;
|
|
743
|
-
const t = this.userInput;
|
|
744
|
-
if (this.cursor >= t.length) return `${this.userInput}\u2588`;
|
|
745
|
-
const r = t.slice(0, this.cursor), s = t.slice(this.cursor, this.cursor + 1), e = t.slice(this.cursor + 1);
|
|
746
|
-
return `${r}${styleText("inverse", s)}${e}`;
|
|
747
|
-
}
|
|
748
|
-
get cursor() {
|
|
749
|
-
return this._cursor;
|
|
750
|
-
}
|
|
751
|
-
constructor(t) {
|
|
752
|
-
super({
|
|
753
|
-
...t,
|
|
754
|
-
initialUserInput: t.initialUserInput ?? t.initialValue
|
|
755
|
-
}), this.on("userInput", (r) => {
|
|
756
|
-
this._setValue(r);
|
|
757
|
-
}), this.on("finalize", () => {
|
|
758
|
-
this.value || (this.value = t.defaultValue), this.value === void 0 && (this.value = "");
|
|
759
|
-
});
|
|
760
|
-
}
|
|
761
|
-
};
|
|
762
|
-
//#endregion
|
|
763
|
-
//#region ../../node_modules/.pnpm/@clack+prompts@1.7.0/node_modules/@clack/prompts/dist/index.mjs
|
|
764
|
-
function isUnicodeSupported() {
|
|
765
|
-
if (process$1.platform !== "win32") return process$1.env.TERM !== "linux";
|
|
766
|
-
return Boolean(process$1.env.CI) || Boolean(process$1.env.WT_SESSION) || Boolean(process$1.env.TERMINUS_SUBLIME) || process$1.env.ConEmuTask === "{cmd::Cmder}" || process$1.env.TERM_PROGRAM === "Terminus-Sublime" || process$1.env.TERM_PROGRAM === "vscode" || process$1.env.TERM === "xterm-256color" || process$1.env.TERM === "alacritty" || process$1.env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
|
|
767
|
-
}
|
|
768
|
-
var unicode = isUnicodeSupported(), isCI = () => process.env.CI === "true", unicodeOr = (o, e) => unicode ? o : e, S_STEP_ACTIVE = unicodeOr("◆", "*"), S_STEP_CANCEL = unicodeOr("■", "x"), S_STEP_ERROR = unicodeOr("▲", "x"), S_STEP_SUBMIT = unicodeOr("◇", "o"), S_BAR_START = unicodeOr("┌", "T"), S_BAR = unicodeOr("│", "|"), S_BAR_END = unicodeOr("└", "—"), S_RADIO_ACTIVE = unicodeOr("●", ">"), S_RADIO_INACTIVE = unicodeOr("○", " "), S_CHECKBOX_ACTIVE = unicodeOr("◻", "[•]"), S_CHECKBOX_SELECTED = unicodeOr("◼", "[+]"), S_CHECKBOX_INACTIVE = unicodeOr("◻", "[ ]"), symbol = (o) => {
|
|
769
|
-
switch (o) {
|
|
770
|
-
case "initial":
|
|
771
|
-
case "active": return styleText("cyan", S_STEP_ACTIVE);
|
|
772
|
-
case "cancel": return styleText("red", S_STEP_CANCEL);
|
|
773
|
-
case "error": return styleText("yellow", S_STEP_ERROR);
|
|
774
|
-
case "submit": return styleText("green", S_STEP_SUBMIT);
|
|
775
|
-
}
|
|
776
|
-
}, symbolBar = (o) => {
|
|
777
|
-
switch (o) {
|
|
778
|
-
case "initial":
|
|
779
|
-
case "active": return styleText("cyan", S_BAR);
|
|
780
|
-
case "cancel": return styleText("red", S_BAR);
|
|
781
|
-
case "error": return styleText("yellow", S_BAR);
|
|
782
|
-
case "submit": return styleText("green", S_BAR);
|
|
783
|
-
}
|
|
784
|
-
};
|
|
785
|
-
function formatInstructionFooter(o, e) {
|
|
786
|
-
const r = [`${e ? `${styleText("cyan", S_BAR)} ` : ""}${o.join(" • ")}`];
|
|
787
|
-
return e && r.push(styleText("cyan", S_BAR_END)), r;
|
|
788
|
-
}
|
|
789
|
-
var I = (l, e, w, p, b, C = false) => {
|
|
790
|
-
let r = e, O = 0;
|
|
791
|
-
if (C) for (let i = p - 1; i >= w; i--) {
|
|
792
|
-
const m = l[i];
|
|
793
|
-
if (m && (r -= m.length), O++, r <= b) break;
|
|
794
|
-
}
|
|
795
|
-
else for (let i = w; i < p; i++) {
|
|
796
|
-
const m = l[i];
|
|
797
|
-
if (m && (r -= m.length), O++, r <= b) break;
|
|
798
|
-
}
|
|
799
|
-
return {
|
|
800
|
-
lineCount: r,
|
|
801
|
-
removals: O
|
|
802
|
-
};
|
|
803
|
-
};
|
|
804
|
-
var limitOptions = ({ cursor: l, options: e, style: w, output: p = process.stdout, maxItems: b = Number.POSITIVE_INFINITY, columnPadding: C = 0, rowPadding: r = 4 }) => {
|
|
805
|
-
const i = getColumns(p) - C, m = getRows(p), M = styleText("dim", "..."), v = Math.max(m - r, 0), a = Math.max(Math.min(b, v), 5);
|
|
806
|
-
let f = 0;
|
|
807
|
-
l >= a - 3 && (f = Math.max(Math.min(l - a + 3, e.length - a), 0));
|
|
808
|
-
let d = a < e.length && f > 0, c = a < e.length && f + a < e.length;
|
|
809
|
-
const W = Math.min(f + a, e.length), s = [];
|
|
810
|
-
let g = 0;
|
|
811
|
-
d && g++, c && g++;
|
|
812
|
-
const T = f + (d ? 1 : 0), y = W - (c ? 1 : 0);
|
|
813
|
-
for (let t = T; t < y; t++) {
|
|
814
|
-
const n = e[t], h = wrapAnsi(n ? w(n, t === l) : "", i, {
|
|
815
|
-
hard: true,
|
|
816
|
-
trim: false
|
|
817
|
-
}).split(`
|
|
818
|
-
`);
|
|
819
|
-
s.push(h), g += h.length;
|
|
820
|
-
}
|
|
821
|
-
if (g > v) {
|
|
822
|
-
let t = 0, n = 0, o = g;
|
|
823
|
-
const h = l - T;
|
|
824
|
-
let u = v;
|
|
825
|
-
const L = () => I(s, o, 0, h, u), E = () => I(s, o, h + 1, s.length, u, true);
|
|
826
|
-
d ? ({lineCount: o, removals: t} = L(), o > u && (c || (u -= 1), {lineCount: o, removals: n} = E())) : (c || (u -= 1), {lineCount: o, removals: n} = E(), o > u && (u -= 1, {lineCount: o, removals: t} = L())), t > 0 && (d = true, s.splice(0, t)), n > 0 && (c = true, s.splice(s.length - n, n));
|
|
827
|
-
}
|
|
828
|
-
const x = [];
|
|
829
|
-
d && x.push(M);
|
|
830
|
-
for (const t of s) for (const n of t) x.push(n);
|
|
831
|
-
return c && x.push(M), x;
|
|
832
|
-
};
|
|
833
|
-
var confirm = (i) => {
|
|
834
|
-
const a = i.active ?? "Yes", s = i.inactive ?? "No";
|
|
835
|
-
return new r({
|
|
836
|
-
active: a,
|
|
837
|
-
inactive: s,
|
|
838
|
-
signal: i.signal,
|
|
839
|
-
input: i.input,
|
|
840
|
-
output: i.output,
|
|
841
|
-
initialValue: i.initialValue ?? true,
|
|
842
|
-
render() {
|
|
843
|
-
const e = i.withGuide ?? settings.withGuide, u = `${symbol(this.state)} `, l = e ? `${styleText("gray", S_BAR)} ` : "", f = wrapTextWithPrefix(i.output, i.message, l, u), o = `${e ? `${styleText("gray", S_BAR)}
|
|
844
|
-
` : ""}${f}
|
|
845
|
-
`, c = this.value ? a : s;
|
|
846
|
-
switch (this.state) {
|
|
847
|
-
case "submit": return `${o}${e ? `${styleText("gray", S_BAR)} ` : ""}${styleText("dim", c)}`;
|
|
848
|
-
case "cancel": return `${o}${e ? `${styleText("gray", S_BAR)} ` : ""}${styleText(["strikethrough", "dim"], c)}${e ? `
|
|
849
|
-
${styleText("gray", S_BAR)}` : ""}`;
|
|
850
|
-
default: {
|
|
851
|
-
const r = e ? `${styleText("cyan", S_BAR)} ` : "", g = e ? styleText("cyan", S_BAR_END) : "";
|
|
852
|
-
return `${o}${r}${this.value ? `${styleText("green", S_RADIO_ACTIVE)} ${a}` : `${styleText("dim", S_RADIO_INACTIVE)} ${styleText("dim", a)}`}${i.vertical ? e ? `
|
|
853
|
-
${styleText("cyan", S_BAR)} ` : `
|
|
854
|
-
` : ` ${styleText("dim", "/")} `}${this.value ? `${styleText("dim", S_RADIO_INACTIVE)} ${styleText("dim", s)}` : `${styleText("green", S_RADIO_ACTIVE)} ${s}`}
|
|
855
|
-
${g}
|
|
856
|
-
`;
|
|
857
|
-
}
|
|
858
|
-
}
|
|
859
|
-
}
|
|
860
|
-
}).prompt();
|
|
861
|
-
};
|
|
862
|
-
var MULTISELECT_INSTRUCTIONS = [
|
|
863
|
-
`${styleText("dim", "↑/↓")} to navigate`,
|
|
864
|
-
`${styleText("dim", "Space:")} select`,
|
|
865
|
-
`${styleText("dim", "Enter:")} confirm`
|
|
866
|
-
];
|
|
867
|
-
var m = (i, u) => i.split(`
|
|
868
|
-
`).map((d) => u(d)).join(`
|
|
869
|
-
`);
|
|
870
|
-
var multiselect = (i) => {
|
|
871
|
-
const u = (t, a) => {
|
|
872
|
-
const r = t.label ?? String(t.value);
|
|
873
|
-
return a === "disabled" ? `${styleText("gray", S_CHECKBOX_INACTIVE)} ${m(r, (o) => styleText(["strikethrough", "gray"], o))}${t.hint ? ` ${styleText("dim", `(${t.hint ?? "disabled"})`)}` : ""}` : a === "active" ? `${styleText("cyan", S_CHECKBOX_ACTIVE)} ${r}${t.hint ? ` ${styleText("dim", `(${t.hint})`)}` : ""}` : a === "selected" ? `${styleText("green", S_CHECKBOX_SELECTED)} ${m(r, (o) => styleText("dim", o))}${t.hint ? ` ${styleText("dim", `(${t.hint})`)}` : ""}` : a === "cancelled" ? `${m(r, (o) => styleText(["strikethrough", "dim"], o))}` : a === "active-selected" ? `${styleText("green", S_CHECKBOX_SELECTED)} ${r}${t.hint ? ` ${styleText("dim", `(${t.hint})`)}` : ""}` : a === "submitted" ? `${m(r, (o) => styleText("dim", o))}` : `${styleText("dim", S_CHECKBOX_INACTIVE)} ${m(r, (o) => styleText("dim", o))}`;
|
|
874
|
-
}, d = i.required ?? true, v = i.showInstructions ?? true;
|
|
875
|
-
return new a({
|
|
876
|
-
options: i.options,
|
|
877
|
-
signal: i.signal,
|
|
878
|
-
input: i.input,
|
|
879
|
-
output: i.output,
|
|
880
|
-
initialValues: i.initialValues,
|
|
881
|
-
required: d,
|
|
882
|
-
cursorAt: i.cursorAt,
|
|
883
|
-
validate(t) {
|
|
884
|
-
if (d && (t === void 0 || t.length === 0)) return `Please select at least one option.
|
|
885
|
-
${styleText("reset", styleText("dim", `Press ${styleText([
|
|
886
|
-
"gray",
|
|
887
|
-
"bgWhite",
|
|
888
|
-
"inverse"
|
|
889
|
-
], " space ")} to select, ${styleText("gray", styleText("bgWhite", styleText("inverse", " enter ")))} to submit`))}`;
|
|
890
|
-
},
|
|
891
|
-
render() {
|
|
892
|
-
const t = i.withGuide ?? settings.withGuide, a = wrapTextWithPrefix(i.output, i.message, t ? `${symbolBar(this.state)} ` : "", `${symbol(this.state)} `), r = `${t ? `${styleText("gray", S_BAR)}
|
|
893
|
-
` : ""}${a}
|
|
894
|
-
`, o = this.value ?? [], p = (n, l) => {
|
|
895
|
-
if (n.disabled) return u(n, "disabled");
|
|
896
|
-
const s = o.includes(n.value);
|
|
897
|
-
return l && s ? u(n, "active-selected") : s ? u(n, "selected") : u(n, l ? "active" : "inactive");
|
|
898
|
-
};
|
|
899
|
-
switch (this.state) {
|
|
900
|
-
case "submit": {
|
|
901
|
-
const n = this.options.filter(({ value: s }) => o.includes(s)).map((s) => u(s, "submitted")).join(styleText("dim", ", ")) || styleText("dim", "none");
|
|
902
|
-
return `${r}${wrapTextWithPrefix(i.output, n, t ? `${styleText("gray", S_BAR)} ` : "")}`;
|
|
903
|
-
}
|
|
904
|
-
case "cancel": {
|
|
905
|
-
const n = this.options.filter(({ value: s }) => o.includes(s)).map((s) => u(s, "cancelled")).join(styleText("dim", ", "));
|
|
906
|
-
if (n.trim() === "") return `${r}${styleText("gray", S_BAR)}`;
|
|
907
|
-
return `${r}${wrapTextWithPrefix(i.output, n, t ? `${styleText("gray", S_BAR)} ` : "")}${t ? `
|
|
908
|
-
${styleText("gray", S_BAR)}` : ""}`;
|
|
909
|
-
}
|
|
910
|
-
case "error": {
|
|
911
|
-
const n = t ? `${styleText("yellow", S_BAR)} ` : "", l = this.error.split(`
|
|
912
|
-
`).map(($, C) => C === 0 ? `${t ? `${styleText("yellow", S_BAR_END)} ` : ""}${styleText("yellow", $)}` : ` ${$}`).join(`
|
|
913
|
-
`), s = r.split(`
|
|
914
|
-
`).length, h = l.split(`
|
|
915
|
-
`).length + 1;
|
|
916
|
-
return `${r}${n}${limitOptions({
|
|
917
|
-
output: i.output,
|
|
918
|
-
options: this.options,
|
|
919
|
-
cursor: this.cursor,
|
|
920
|
-
maxItems: i.maxItems,
|
|
921
|
-
columnPadding: n.length,
|
|
922
|
-
rowPadding: s + h,
|
|
923
|
-
style: p
|
|
924
|
-
}).join(`
|
|
925
|
-
${n}`)}
|
|
926
|
-
${l}
|
|
927
|
-
`;
|
|
928
|
-
}
|
|
929
|
-
default: {
|
|
930
|
-
const n = t ? `${styleText("cyan", S_BAR)} ` : "", l = r.split(`
|
|
931
|
-
`).length, s = v ? formatInstructionFooter(MULTISELECT_INSTRUCTIONS, t) : t ? [styleText("cyan", S_BAR_END)] : [], h = s.join(`
|
|
932
|
-
`), $ = s.length + 1;
|
|
933
|
-
return `${r}${n}${limitOptions({
|
|
934
|
-
output: i.output,
|
|
935
|
-
options: this.options,
|
|
936
|
-
cursor: this.cursor,
|
|
937
|
-
maxItems: i.maxItems,
|
|
938
|
-
columnPadding: n.length,
|
|
939
|
-
rowPadding: l + $,
|
|
940
|
-
style: p
|
|
941
|
-
}).join(`
|
|
942
|
-
${n}`)}
|
|
943
|
-
${h}
|
|
944
|
-
`;
|
|
945
|
-
}
|
|
946
|
-
}
|
|
947
|
-
}
|
|
948
|
-
}).prompt();
|
|
949
|
-
};
|
|
950
|
-
var cancel = (o = "", t) => {
|
|
951
|
-
const i = t?.output ?? process.stdout, e = t?.withGuide ?? settings.withGuide ? `${styleText("gray", S_BAR_END)} ` : "";
|
|
952
|
-
i.write(`${e}${styleText("red", o)}
|
|
953
|
-
|
|
954
|
-
`);
|
|
955
|
-
}, intro = (o = "", t) => {
|
|
956
|
-
const i = t?.output ?? process.stdout, e = t?.withGuide ?? settings.withGuide ? `${styleText("gray", S_BAR_START)} ` : "";
|
|
957
|
-
i.write(`${e}${o}
|
|
958
|
-
`);
|
|
959
|
-
}, outro = (o = "", t) => {
|
|
960
|
-
const i = t?.output ?? process.stdout, e = t?.withGuide ?? settings.withGuide ? `${styleText("gray", S_BAR)}
|
|
961
|
-
${styleText("gray", S_BAR_END)} ` : "";
|
|
962
|
-
i.write(`${e}${o}
|
|
963
|
-
|
|
964
|
-
`);
|
|
965
|
-
};
|
|
966
|
-
var W = (l) => styleText("magenta", l);
|
|
967
|
-
var spinner = ({ indicator: l = "dots", onCancel: h, output: n = process.stdout, cancelMessage: G, errorMessage: O, frames: E = unicode ? [
|
|
968
|
-
"◒",
|
|
969
|
-
"◐",
|
|
970
|
-
"◓",
|
|
971
|
-
"◑"
|
|
972
|
-
] : [
|
|
973
|
-
"•",
|
|
974
|
-
"o",
|
|
975
|
-
"O",
|
|
976
|
-
"0"
|
|
977
|
-
], delay: F = unicode ? 80 : 120, signal: m, ...I } = {}) => {
|
|
978
|
-
const u = isCI();
|
|
979
|
-
let M, T, d = false, S = false, s = "", p, w = performance.now();
|
|
980
|
-
const x = getColumns(n), k = I?.styleFrame ?? W, g = (e) => {
|
|
981
|
-
const r = e > 1 ? O ?? settings.messages.error : G ?? settings.messages.cancel;
|
|
982
|
-
S = e === 1, d && (a(r, e), S && typeof h == "function" && h());
|
|
983
|
-
}, f = () => g(2), i = () => g(1), A = () => {
|
|
984
|
-
process.on("uncaughtExceptionMonitor", f), process.on("unhandledRejection", f), process.on("SIGINT", i), process.on("SIGTERM", i), process.on("exit", g), m && m.addEventListener("abort", i);
|
|
985
|
-
}, H = () => {
|
|
986
|
-
process.removeListener("uncaughtExceptionMonitor", f), process.removeListener("unhandledRejection", f), process.removeListener("SIGINT", i), process.removeListener("SIGTERM", i), process.removeListener("exit", g), m && m.removeEventListener("abort", i);
|
|
987
|
-
}, y = () => {
|
|
988
|
-
if (p === void 0) return;
|
|
989
|
-
u && n.write(`
|
|
990
|
-
`);
|
|
991
|
-
const r = wrapAnsi(p, x, {
|
|
992
|
-
hard: true,
|
|
993
|
-
trim: false
|
|
994
|
-
}).split(`
|
|
995
|
-
`);
|
|
996
|
-
r.length > 1 && n.write(import_src.cursor.up(r.length - 1)), n.write(import_src.cursor.to(0)), n.write(import_src.erase.down());
|
|
997
|
-
}, C = (e) => e.replace(/\.+$/, ""), _ = (e) => {
|
|
998
|
-
const r = (performance.now() - e) / 1e3, t = Math.floor(r / 60), o = Math.floor(r % 60);
|
|
999
|
-
return t > 0 ? `[${t}m ${o}s]` : `[${o}s]`;
|
|
1000
|
-
}, N = I.withGuide ?? settings.withGuide, P = (e = "") => {
|
|
1001
|
-
d = true, M = block({ output: n }), s = C(e), w = performance.now(), N && n.write(`${styleText("gray", S_BAR)}
|
|
1002
|
-
`);
|
|
1003
|
-
let r = 0, t = 0;
|
|
1004
|
-
A(), T = setInterval(() => {
|
|
1005
|
-
if (u && s === p) return;
|
|
1006
|
-
y(), p = s;
|
|
1007
|
-
const o = k(E[r]);
|
|
1008
|
-
let v;
|
|
1009
|
-
if (u) v = `${o} ${s}...`;
|
|
1010
|
-
else if (l === "timer") v = `${o} ${s} ${_(w)}`;
|
|
1011
|
-
else {
|
|
1012
|
-
const B = ".".repeat(Math.floor(t)).slice(0, 3);
|
|
1013
|
-
v = `${o} ${s}${B}`;
|
|
1014
|
-
}
|
|
1015
|
-
const j = wrapAnsi(v, x, {
|
|
1016
|
-
hard: true,
|
|
1017
|
-
trim: false
|
|
1018
|
-
});
|
|
1019
|
-
n.write(j), r = r + 1 < E.length ? r + 1 : 0, t = t < 4 ? t + .125 : 0;
|
|
1020
|
-
}, F);
|
|
1021
|
-
}, a = (e = "", r = 0, t = false) => {
|
|
1022
|
-
if (!d) return;
|
|
1023
|
-
d = false, clearInterval(T), y();
|
|
1024
|
-
const o = r === 0 ? styleText("green", S_STEP_SUBMIT) : r === 1 ? styleText("red", S_STEP_CANCEL) : styleText("red", S_STEP_ERROR);
|
|
1025
|
-
s = e ?? s, t || (l === "timer" ? n.write(`${o} ${s} ${_(w)}
|
|
1026
|
-
`) : n.write(`${o} ${s}
|
|
1027
|
-
`)), H(), M();
|
|
1028
|
-
};
|
|
1029
|
-
return {
|
|
1030
|
-
start: P,
|
|
1031
|
-
stop: (e = "") => a(e, 0),
|
|
1032
|
-
message: (e = "") => {
|
|
1033
|
-
s = C(e ?? s);
|
|
1034
|
-
},
|
|
1035
|
-
cancel: (e = "") => a(e, 1),
|
|
1036
|
-
error: (e = "") => a(e, 2),
|
|
1037
|
-
clear: () => a("", 0, true),
|
|
1038
|
-
get isCancelled() {
|
|
1039
|
-
return S;
|
|
1040
|
-
}
|
|
1041
|
-
};
|
|
1042
|
-
};
|
|
1043
|
-
var SELECT_INSTRUCTIONS = [`${styleText("dim", "↑/↓")} to navigate`, `${styleText("dim", "Enter:")} confirm`];
|
|
1044
|
-
var c = (t, o) => t.includes(`
|
|
1045
|
-
`) ? t.split(`
|
|
1046
|
-
`).map((d) => o(d)).join(`
|
|
1047
|
-
`) : o(t);
|
|
1048
|
-
var select = (t) => {
|
|
1049
|
-
const o = (n, m) => {
|
|
1050
|
-
if (n === void 0) return "";
|
|
1051
|
-
const s = n.label ?? String(n.value);
|
|
1052
|
-
switch (m) {
|
|
1053
|
-
case "disabled": return `${styleText("gray", S_RADIO_INACTIVE)} ${c(s, (i) => styleText("gray", i))}${n.hint ? ` ${styleText("dim", `(${n.hint ?? "disabled"})`)}` : ""}`;
|
|
1054
|
-
case "selected": return `${c(s, (i) => styleText("dim", i))}`;
|
|
1055
|
-
case "active": return `${styleText("green", S_RADIO_ACTIVE)} ${s}${n.hint ? ` ${styleText("dim", `(${n.hint})`)}` : ""}`;
|
|
1056
|
-
case "cancelled": return `${c(s, (i) => styleText(["strikethrough", "dim"], i))}`;
|
|
1057
|
-
default: return `${styleText("dim", S_RADIO_INACTIVE)} ${c(s, (i) => styleText("dim", i))}`;
|
|
1058
|
-
}
|
|
1059
|
-
}, d = t.showInstructions ?? true;
|
|
1060
|
-
return new n$1({
|
|
1061
|
-
options: t.options,
|
|
1062
|
-
signal: t.signal,
|
|
1063
|
-
input: t.input,
|
|
1064
|
-
output: t.output,
|
|
1065
|
-
initialValue: t.initialValue,
|
|
1066
|
-
render() {
|
|
1067
|
-
const n = t.withGuide ?? settings.withGuide, m = `${symbol(this.state)} `, s = `${symbolBar(this.state)} `, i = wrapTextWithPrefix(t.output, t.message, s, m), u = `${n ? `${styleText("gray", S_BAR)}
|
|
1068
|
-
` : ""}${i}
|
|
1069
|
-
`;
|
|
1070
|
-
switch (this.state) {
|
|
1071
|
-
case "submit": {
|
|
1072
|
-
const r = n ? `${styleText("gray", S_BAR)} ` : "";
|
|
1073
|
-
return `${u}${wrapTextWithPrefix(t.output, o(this.options[this.cursor], "selected"), r)}`;
|
|
1074
|
-
}
|
|
1075
|
-
case "cancel": {
|
|
1076
|
-
const r = n ? `${styleText("gray", S_BAR)} ` : "";
|
|
1077
|
-
return `${u}${wrapTextWithPrefix(t.output, o(this.options[this.cursor], "cancelled"), r)}${n ? `
|
|
1078
|
-
${styleText("gray", S_BAR)}` : ""}`;
|
|
1079
|
-
}
|
|
1080
|
-
default: {
|
|
1081
|
-
const r = n ? `${styleText("cyan", S_BAR)} ` : "", a = u.split(`
|
|
1082
|
-
`).length, p = d ? formatInstructionFooter(SELECT_INSTRUCTIONS, n) : n ? [styleText("cyan", S_BAR_END)] : [], b = p.join(`
|
|
1083
|
-
`), f = p.length + 1;
|
|
1084
|
-
return `${u}${r}${limitOptions({
|
|
1085
|
-
output: t.output,
|
|
1086
|
-
cursor: this.cursor,
|
|
1087
|
-
options: this.options,
|
|
1088
|
-
maxItems: t.maxItems,
|
|
1089
|
-
columnPadding: r.length,
|
|
1090
|
-
rowPadding: a + f,
|
|
1091
|
-
style: (g, x) => o(g, g.disabled ? "disabled" : x ? "active" : "inactive")
|
|
1092
|
-
}).join(`
|
|
1093
|
-
${r}`)}
|
|
1094
|
-
${b}
|
|
1095
|
-
`;
|
|
1096
|
-
}
|
|
1097
|
-
}
|
|
1098
|
-
}
|
|
1099
|
-
}).prompt();
|
|
1100
|
-
};
|
|
1101
|
-
`${styleText("gray", S_BAR)}`;
|
|
1102
|
-
var text = (e) => new n({
|
|
1103
|
-
validate: e.validate,
|
|
1104
|
-
placeholder: e.placeholder,
|
|
1105
|
-
defaultValue: e.defaultValue,
|
|
1106
|
-
initialValue: e.initialValue,
|
|
1107
|
-
output: e.output,
|
|
1108
|
-
signal: e.signal,
|
|
1109
|
-
input: e.input,
|
|
1110
|
-
render() {
|
|
1111
|
-
const i = e?.withGuide ?? settings.withGuide, s = `${`${i ? `${styleText("gray", S_BAR)}
|
|
1112
|
-
` : ""}${symbol(this.state)} `}${e.message}
|
|
1113
|
-
`, c = e.placeholder && e.placeholder.length > 0 ? styleText("inverse", e.placeholder[0]) + styleText("dim", e.placeholder.slice(1)) : styleText(["inverse", "hidden"], "_"), o = this.userInput ? this.userInputWithCursor : c, l = this.value ?? "";
|
|
1114
|
-
switch (this.state) {
|
|
1115
|
-
case "error": {
|
|
1116
|
-
const n = this.error ? ` ${styleText("yellow", this.error)}` : "", r = i ? `${styleText("yellow", S_BAR)} ` : "", d = i ? styleText("yellow", S_BAR_END) : "";
|
|
1117
|
-
return `${s.trim()}
|
|
1118
|
-
${r}${o}
|
|
1119
|
-
${d}${n}
|
|
1120
|
-
`;
|
|
1121
|
-
}
|
|
1122
|
-
case "submit": {
|
|
1123
|
-
const n = l ? ` ${styleText("dim", l)}` : "";
|
|
1124
|
-
return `${s}${i ? styleText("gray", S_BAR) : ""}${n}`;
|
|
1125
|
-
}
|
|
1126
|
-
case "cancel": {
|
|
1127
|
-
const n = l ? ` ${styleText(["strikethrough", "dim"], l)}` : "", r = i ? styleText("gray", S_BAR) : "";
|
|
1128
|
-
return `${s}${r}${n}${l.trim() ? `
|
|
1129
|
-
${r}` : ""}`;
|
|
1130
|
-
}
|
|
1131
|
-
default: return `${s}${i ? `${styleText("cyan", S_BAR)} ` : ""}${o}
|
|
1132
|
-
${i ? styleText("cyan", S_BAR_END) : ""}
|
|
1133
|
-
`;
|
|
1134
|
-
}
|
|
1135
|
-
}
|
|
1136
|
-
}).prompt();
|
|
1137
|
-
//#endregion
|
|
8
|
+
import { installSkill, loadAllSkills } from "@wangs-ui/skills";
|
|
1138
9
|
//#region src/utils/fs.ts
|
|
1139
10
|
/**
|
|
1140
11
|
* Recursively copy a template directory into the target directory,
|
|
@@ -1151,6 +22,7 @@ function copyProjectTemplate(options) {
|
|
|
1151
22
|
let destName = entry.name;
|
|
1152
23
|
if (destName === "_gitignore") destName = ".gitignore";
|
|
1153
24
|
else if (destName === "_npmrc") destName = ".npmrc";
|
|
25
|
+
else if (destName === "_pnpm-workspace.yaml") destName = "pnpm-workspace.yaml";
|
|
1154
26
|
const destPath = path.join(destDir, destName);
|
|
1155
27
|
if (entry.isDirectory()) copyProjectTemplate({
|
|
1156
28
|
srcDir: srcPath,
|
|
@@ -1161,8 +33,20 @@ function copyProjectTemplate(options) {
|
|
|
1161
33
|
});
|
|
1162
34
|
else {
|
|
1163
35
|
let content = fs.readFileSync(srcPath, "utf8");
|
|
1164
|
-
if (destName === "package.json")
|
|
1165
|
-
|
|
36
|
+
if (destName === "package.json") {
|
|
37
|
+
content = content.replace(/"name":\s*"[^"]*"/, `"name": "${projectName}"`);
|
|
38
|
+
let cliVersion = "^1.0.50";
|
|
39
|
+
try {
|
|
40
|
+
const cliPackageJsonPath = path.resolve(__dirname, "../../package.json");
|
|
41
|
+
if (fs.existsSync(cliPackageJsonPath)) {
|
|
42
|
+
const cliPackageJson = JSON.parse(fs.readFileSync(cliPackageJsonPath, "utf8"));
|
|
43
|
+
if (cliPackageJson.version) cliVersion = `^${cliPackageJson.version}`;
|
|
44
|
+
}
|
|
45
|
+
} catch {}
|
|
46
|
+
content = content.replace(/"workspace:\*"/g, `"${cliVersion}"`);
|
|
47
|
+
} else if (destName === "index.html") content = content.replace(/<title>.*<\/title>/, `<title>${projectName}</title>`);
|
|
48
|
+
else if (destName === "tsconfig.app.json") content = content.replace(/\s*"customConditions":\s*\["wangs-ui-dev"\],?\n?/, "\n");
|
|
49
|
+
else if (destName === "vite.config.ts") content = content.replace(/\s*resolve:\s*\{\s*conditions:\s*\['wangs-ui-dev'\],\s*\},?\n?/, "\n");
|
|
1166
50
|
if (preset !== "blue") content = content.replace(/@wangs-ui\/react-presets\/blue/g, `@wangs-ui/react-presets/${preset}`).replace(/preset:\s*blue/g, `preset: ${preset}`).replace(/palette:\s*'blue'/g, `palette: '${preset}'`).replace(/import blue from/g, `import ${preset} from`);
|
|
1167
51
|
fs.writeFileSync(destPath, content, "utf8");
|
|
1168
52
|
}
|
|
@@ -1215,90 +99,6 @@ function initGitRepository(targetDir) {
|
|
|
1215
99
|
}
|
|
1216
100
|
}
|
|
1217
101
|
//#endregion
|
|
1218
|
-
//#region ../skills/dist/src-BG5LRS8J.js
|
|
1219
|
-
var SKILL_default$7 = "---\nname: create-form\ndescription: Architecture, validation workflows, and MCP discovery protocol for building forms and input controls with @wangs-ui/react-core.\n---\n\n# Skill: Form Architecture & Validation Workflows\n\nUse this skill when building forms, data entry panels, settings pages, or multipart forms in Wangs UI applications.\n\n---\n\n## 1. MCP Inspection Protocol (Mandatory Single Source of Truth)\n\nDo **NOT** hardcode or guess prop names, field configurations, or validation options. Retrieve active component definitions and live implementation stories directly from MCP:\n\n### Inspect Component & Form Contracts:\n\n```json\nget-documentation({ \"id\": \"form\" })\nget-documentation({ \"id\": \"field\" })\nget-documentation({ \"id\": \"input\" })\nget-documentation({ \"id\": \"numberinput\" })\nget-documentation({ \"id\": \"select\" })\nget-documentation({ \"id\": \"multiselect\" })\nget-documentation({ \"id\": \"datepicker\" })\nget-documentation({ \"id\": \"fileupload\" })\n```\n\n### Inspect Live Story Implementations:\n\n```json\nget-documentation-for-story({ \"id\": \"form\", \"storyName\": \"Default\" })\nget-documentation-for-story({ \"id\": \"field\", \"storyName\": \"Default\" })\nget-documentation-for-story({ \"id\": \"select\", \"storyName\": \"Basic\" })\nget-documentation-for-story({ \"id\": \"datepicker\", \"storyName\": \"Default\" })\nget-documentation-for-story({ \"id\": \"fileupload\", \"storyName\": \"Default\" })\n```\n\n### Inspect Knowledge Graph & Usages:\n\n```json\nquery_graph({ \"query\": \"useFormControl\" })\nquery_graph({ \"query\": \"Field\" })\n```\n\n---\n\n## 2. Form Architecture & State Principles\n\n1. **State & Control**:\n - Standard REST payload forms use `useFormControl` with JSON mode.\n - Multipart file upload workflows use `useFormControl` with FormData mode.\n2. **Field Composition**:\n - Form inputs are wrapped with `<Field>` layout containers for unified label, tooltip, helper text, and error rendering.\n - Exact props, slot rendering functions, and field binding options must be retrieved via MCP (`get-documentation({ \"id\": \"field\" })`).\n3. **Server Validation Error Mapping**:\n - Backend validation responses (e.g. `422 Unprocessable Entity`) are mapped back into the form instance via `formControl.setError()`.\n4. **Submission Lifecycle**:\n - In-flight network requests should manage loading state on submit actions and prevent accidental reset during mutations.\n\n---\n\n## 3. Mandatory Implementation Rules\n\n1. **Always Query MCP First**: Never guess input props or event signatures; obtain the exact types from `get-documentation`.\n2. **Strict Subpath Imports**: All components must be imported via their granular subpath (`@wangs-ui/react-core/primitive/*`, `@wangs-ui/form`).\n3. **Translate All Visible Strings**: Every field label, placeholder, helper text, and error message must be wrapped in `t('...')` from `@wangs-ui/react-i18n`.\n";
|
|
1220
|
-
var SKILL_default$6 = "---\nname: data-table\ndescription: Architecture, workflows, and MCP discovery protocol for building DataTables with sorting, pagination, filtering, selection, and export.\n---\n\n# Skill: DataTable Architecture & Integration Workflows\n\nUse this skill when implementing data grids, server-paginated tables, filterable listing views, or batch management interfaces with `@wangs-ui/react-core`.\n\n---\n\n## 1. MCP Inspection Protocol (Mandatory Single Source of Truth)\n\nDo **NOT** guess table prop names or hardcode table structures. Query the MCP server dynamically to inspect exact TypeScript signatures, live story implementations, and companion controls:\n\n### Inspect Component Contracts:\n\n```json\nget-documentation({ \"id\": \"datatable\" })\nget-documentation({ \"id\": \"exportbutton\" })\nget-documentation({ \"id\": \"filtercontainer\" })\nget-documentation({ \"id\": \"bulkactionbutton\" })\n```\n\n### Inspect Live Story Implementations:\n\n```json\nget-documentation-for-story({ \"id\": \"datatable\", \"storyName\": \"Basic\" })\nget-documentation-for-story({ \"id\": \"datatable\", \"storyName\": \"ServerPagination\" })\nget-documentation-for-story({ \"id\": \"datatable\", \"storyName\": \"Sortable\" })\nget-documentation-for-story({ \"id\": \"datatable\", \"storyName\": \"MultipleSelection\" })\nget-documentation-for-story({ \"id\": \"datatable\", \"storyName\": \"CustomColumn\" })\nget-documentation-for-story({ \"id\": \"exportbutton\", \"storyName\": \"WithTable\" })\n```\n\n### Inspect Knowledge Graph & Usages:\n\n```json\nquery_graph({ \"query\": \"DataTable\" })\nquery_graph({ \"query\": \"useDataTableFetch\" })\n```\n\n---\n\n## 2. Core Architecture & Mental Model\n\nThe Wangs UI `DataTable` is built on a modular, headless-first architecture:\n\n1. **Declarative Column Definitions (`TableColumn<T>[]`)**:\n Columns are configured as typed array objects, not as JSX children. Check `get-documentation({ \"id\": \"datatable\" })` for column field types.\n2. **Table Instance Hook (`useDataTable`)**:\n Coordinates table state (sorting, pagination, selection, column ordering, pinning, visibility).\n3. **Data Fetching Hook (`useDataTableFetch`)**:\n Feeds server-side data, handles loading indicators, manages query parameters (`search`, `filter`, `sort`, `page`, `limit`), and debounces requests automatically.\n4. **Ecosystem Companions**:\n - `FilterContainer` & `FilterToggleButton`: Filter popovers and faceted search.\n - `ExportButton`: Client/server export to Excel, CSV, PDF, or Print.\n - `BulkActionButton`: Contextual batch actions triggered when rows are selected.\n - `CustomColumn`: User-controlled column ordering, visibility toggling, and pinning.\n\n---\n\n## 3. Mandatory Implementation Rules\n\n1. **Query MCP for Current Code Patterns**: Always run `get-documentation-for-story` for `datatable` before drafting code.\n2. **Strict Subpath Imports**: Import via `@wangs-ui/react-core/primitive/datatable` and companion primitive paths.\n3. **Always Translate Visible Copy**: All column header labels, empty state messages, and action button labels must be wrapped in `t('...')` from `@wangs-ui/react-i18n`.\n4. **Stable Row Identity**: Always configure a unique key identifier for stable selection and row identity.\n";
|
|
1221
|
-
var SKILL_default$5 = "---\nname: dialog-modal\ndescription: Patterns, overlay selection criteria, and MCP discovery protocol for Dialog, Modal, and DialogForm components in Wangs UI.\n---\n\n# Skill: Dialog, Modal & Overlay Workflows\n\nUse this skill when building interactive modals, create/edit dialog forms, destructive action confirmations, or slide-in overlay panels.\n\n---\n\n## 1. MCP Inspection Protocol (Mandatory Single Source of Truth)\n\nDo **NOT** guess overlay props, event names, or footer slots. Query the MCP server dynamically to inspect exact contracts and live story implementations:\n\n### Inspect Overlay Contracts:\n\n```json\nget-documentation({ \"id\": \"dialog\" })\nget-documentation({ \"id\": \"dialogform\" })\nget-documentation({ \"id\": \"modal\" })\nget-documentation({ \"id\": \"toast\" })\n```\n\n### Inspect Live Story Implementations:\n\n```json\nget-documentation-for-story({ \"id\": \"dialog\", \"storyName\": \"Confirmation\" })\nget-documentation-for-story({ \"id\": \"dialogform\", \"storyName\": \"Default\" })\nget-documentation-for-story({ \"id\": \"modal\", \"storyName\": \"Default\" })\n```\n\n### Inspect Knowledge Graph & Usages:\n\n```json\nquery_graph({ \"query\": \"Dialog\" })\nquery_graph({ \"query\": \"DialogForm\" })\n```\n\n---\n\n## 2. Overlay Selection Matrix\n\n| Component | Primary Use Case | Key Characteristics |\n| :--------------- | :-------------------------------------------- | :------------------------------------------------------------------------------------ |\n| **`Dialog`** | Confirmations, alerts, simple detail previews | Standard `header`, `footer`, and body layout; built-in backdrop dimming. |\n| **`DialogForm`** | Create/Edit forms embedded inside a dialog | Built-in form submit/cancel action bar, dirty state tracking, and submit lifecycle. |\n| **`Modal`** | Slide-in drawers, complex custom viewports | Headless overlay primitive with flexible animations, size variants, and drawer modes. |\n\n---\n\n## 3. Mandatory Implementation Rules\n\n1. **Query MCP for Current Code Patterns**: Always inspect `dialog`, `dialogform`, or `modal` stories via MCP before writing overlay code.\n2. **Strict Subpath Imports**: Import via `@wangs-ui/react-core/primitive/dialog`, `@wangs-ui/react-core/primitive/dialogform`, `@wangs-ui/react-core/primitive/modal`, or `@wangs-ui/react-core/primitive/toast`.\n3. **Prevent Dismissal During Async Mutations**: Guard the close handler so users cannot accidentally dismiss the dialog while a mutation request is in-flight.\n4. **Coordinate with Toast Notifications**: Trigger feedback toasts on successful creation, update, or deletion actions.\n5. **Translate All Overlay Copy**: All dialog titles, confirmation descriptions, and button labels must be localized using `t('...')` from `@wangs-ui/react-i18n`.\n";
|
|
1222
|
-
var SKILL_default$4 = "---\nname: i18n-usage\ndescription: Guidelines, formatting rules, and MCP discovery protocol for internationalization with @wangs-ui/react-i18n and localized components.\n---\n\n# Skill: Application Internationalization & Localization Protocol\n\nUse this skill when handling multi-language interfaces, currency inputs, localized date formats, or dynamic text translations in Wangs UI applications.\n\n---\n\n## 1. MCP Inspection Protocol (Single Source of Truth)\n\nDo **NOT** guess component localization props or language switcher variants. Query the MCP server dynamically to inspect exact contracts and live story implementations:\n\n### Inspect Localized Component Contracts:\n\n```json\nget-documentation({ \"id\": \"languageswitcher\" })\nget-documentation({ \"id\": \"currencyinput\" })\nget-documentation({ \"id\": \"datepicker\" })\n```\n\n### Inspect Live Story Implementations:\n\n```json\nget-documentation-for-story({ \"id\": \"languageswitcher\", \"storyName\": \"Basic\" })\nget-documentation-for-story({ \"id\": \"currencyinput\", \"storyName\": \"Basic\" })\nget-documentation-for-story({ \"id\": \"datepicker\", \"storyName\": \"Default\" })\n```\n\n---\n\n## 2. Translation Syntax & Golden Rules\n\n1. **Sentence Keys in Natural English**:\n Always write human-readable English sentence keys:\n\n ```tsx\n // ✅ Good\n t('Invoice Summary');\n t('Are you sure you want to delete this customer?');\n\n // ❌ Bad — artificial dotted keys\n t('invoice.summary.title');\n ```\n\n2. **Dynamic Variables in Double Braces (`{{var}}`)**:\n Pass variables as an object using `{{variableName}}` interpolation:\n\n ```tsx\n // ✅ Good\n t('Welcome back, {{name}}!', { name: user.name });\n\n // ❌ Bad — string concatenation breaks translation word order\n t('Welcome back, ') + user.name + '!';\n ```\n\n3. **Pluralization with ICU Formats**:\n Use ICU plural format for quantity-dependent sentences:\n\n ```tsx\n t('{count, plural, =0 {No items selected} one {# item selected} other {# items selected}}', {\n count: selectedCount,\n });\n ```\n\n4. **Runtime Locale Switching**:\n Use the `useI18n()` hook to read or update active language:\n ```tsx\n import { useI18n } from '@wangs-ui/react-i18n';\n\n const { t, currentLocale, setLocale } = useI18n();\n ```\n\n---\n\n## 3. Mandatory Implementation Rules\n\n1. **Inspect Localized Components via MCP**: Query `currencyinput` and `datepicker` documentation before binding locale-sensitive formatters.\n2. **Never Hardcode User-Facing Text**: Every visible label, placeholder, dialog title, tooltip, and error message in the application must pass through `t()`.\n3. **Keep Context Intact**: Do not split sentences into separate phrases across JSX elements; translate the full sentence as a single unit.\n";
|
|
1223
|
-
var SKILL_default$3 = "---\nname: layout-navigation\ndescription: Architecture, navigation hierarchies, and MCP discovery protocol for AppLayout, Sidebar, Breadcrumb, and Tabs in Wangs UI.\n---\n\n# Skill: Application Layout & Navigation Hierarchy\n\nUse this skill when constructing application shells, multi-level sidebars, page headers, breadcrumbs, or tabbed views with `@wangs-ui/react-core`.\n\n---\n\n## 1. MCP Inspection Protocol (Mandatory Single Source of Truth)\n\nDo **NOT** guess layout block slots, sidebar item interfaces, or breadcrumb props. Query the MCP server dynamically to inspect exact contracts and live story implementations:\n\n### Inspect Layout & Navigation Contracts:\n\n```json\nget-documentation({ \"id\": \"applayout\" })\nget-documentation({ \"id\": \"sidebar\" })\nget-documentation({ \"id\": \"breadcrumb\" })\nget-documentation({ \"id\": \"tabs\" })\n```\n\n### Inspect Live Story Implementations:\n\n```json\nget-documentation-for-story({ \"id\": \"applayout\", \"storyName\": \"Default\" })\nget-documentation-for-story({ \"id\": \"sidebar\", \"storyName\": \"Default\" })\nget-documentation-for-story({ \"id\": \"breadcrumb\", \"storyName\": \"Default\" })\nget-documentation-for-story({ \"id\": \"tabs\", \"storyName\": \"Default\" })\n```\n\n### Inspect Knowledge Graph & Usages:\n\n```json\nquery_graph({ \"query\": \"AppLayout\" })\nquery_graph({ \"query\": \"Sidebar\" })\n```\n\n---\n\n## 2. Layout Architecture & Mental Model\n\n1. **Top-Level App Shell (`AppLayout`)**:\n Provides structured slots for `sidebar`, `header`, and main content view, handling responsive viewport scaling and mobile navigation overlays.\n2. **Hierarchical Menu (`Sidebar`)**:\n Renders single and nested navigation items, active route indicators, collapsible state, and notification badges.\n3. **Breadcrumb Trail (`Breadcrumb`)**:\n Maintains clear navigational hierarchy on page headers.\n4. **Tabbed Sub-Views (`Tabs`)**:\n Organizes complex entity detail views or multi-section settings into distinct tabbed panels.\n\n---\n\n## 3. Mandatory Implementation Rules\n\n1. **Query MCP for Current Code Patterns**: Inspect `applayout` and `sidebar` stories via MCP before assembling the layout.\n2. **Strict Subpath Imports**: Import layout blocks via `@wangs-ui/react-core/blocks/*` and primitives via `@wangs-ui/react-core/primitive/*`.\n3. **Consistent Spacing Grid**: Use standard container padding (`p-6` or `p-xxl`) across page contents.\n4. **Page Hierarchy Alignment**: Every page view inside the layout must provide a clear `.heading-1` hierarchy and synchronized breadcrumbs.\n5. **Translate Navigation Labels**: Wrap all sidebar item labels and breadcrumb texts in `t('...')` from `@wangs-ui/react-i18n`.\n";
|
|
1224
|
-
var SKILL_default$2 = "---\nname: react19-compiler-typescript\ndescription: Enforce idiomatic React 19 + TypeScript conventions built around the React Compiler's automatic memoization. Use this any time writing, generating, reviewing, or refactoring React components, hooks, or props in TypeScript/TSX — including code that manually wraps things in useMemo/useCallback/React.memo, uses forwardRef, mutates props/state, or needs typing for Actions, useOptimistic, use(), or refs. Trigger even if the user didn't say \"React 19\" or \"compiler\" explicitly; it applies whenever React component/hook code is being written or optimized.\n---\n\n# React 19 + TypeScript with the React Compiler\n\n## Why this matters\n\nReact Compiler (stable since React Compiler 1.0, October 2025) rewrites your components\nand hooks at build time, inserting memoization equivalent to `useMemo`/`useCallback`/\n`React.memo` automatically and more granularly than a human would by hand. It ships as\n`babel-plugin-react-compiler`, and its lint rules live inside `eslint-plugin-react-hooks`\n(recommended preset) so linting and compilation share one source of truth.\n\nThe practical consequence: **manual memoization is no longer the default** — it's\neither redundant, or actively harmful if it doesn't match what the compiler would have\ninferred (the compiler bails out silently rather than risk breaking your app). Writing\n\"optimized\" React in 2026 means writing _plain, rule-following_ React and trusting the\nbuild step, not sprinkling `useMemo` everywhere out of habit.\n\nThis skill assumes and builds on the base `typescript-strict-typing` skill for general\ntyping discipline (no `any`, `interface` for entities, discriminated unions for variant\nstate, etc.) — apply both together.\n\n## Core principle\n\n> Write plain, obviously-pure React. Let the compiler memoize. The Rules of React are no\n> longer just style guidance — the compiler's correctness depends on you following them.\n\n---\n\n## 1. Stop hand-rolling memoization\n\n> ⚠️ **Everything in this section assumes the compiler is confirmed active** (wired per\n> §7, verified via the \"Memo ✨\" badge in §8). If you drop manual memoization _without_\n> that confirmation, you don't get automatic memoization to replace it — you get\n> **neither**. That's not a correctness bug (React still renders the right output), but\n> every child re-renders on every parent render regardless of whether its props\n> actually changed, and every inline computation reruns every render with nothing\n> caching it. It's the pre-memoization default behavior of React — often invisible in\n> small trees, but a real source of jank in large lists, heavy computations, or deep\n> trees under a frequently-re-rendering parent. If you're not certain the compiler is\n> active yet, keep existing manual memoization until you've verified it, then remove it.\n\nDon't reach for `useMemo`, `useCallback`, or `React.memo` by default — the compiler adds\nthis automatically wherever it determines it helps.\n\n```tsx\n// ❌ Old habit — noisy, and a mismatched dependency array is a whole class of bugs\nconst filteredUsers = useMemo(() => users.filter((u) => u.isActive), [users]);\nconst handleClick = useCallback(() => onSelect(user.id), [onSelect, user.id]);\n\n// ✅ New default — just write the logic; the compiler memoizes what's worth memoizing\nconst filteredUsers = users.filter((u) => u.isActive);\nconst handleClick = () => onSelect(user.id);\n```\n\nManual memoization is still justified, narrowly, when:\n\n- You've **confirmed a compiler bail-out** (see §6) on a genuine hot path via profiling,\n and fixing the underlying Rules-of-React violation isn't possible right now.\n- A value must have **stable referential identity across a boundary the compiler can't\n see** — e.g. passed into a non-React library, a WebSocket subscription, or a\n third-party hook incompatible with the compiler (`react-hook-form`'s `useForm`,\n `@tanstack/react-table`'s `useReactTable` are known cases).\n- Keep any manual memoization it produces isolated and commented with _why_, so it\n doesn't silently rot into a bail-out later when the code around it changes.\n\n## 2. The Rules of React are now load-bearing\n\nThe compiler assumes your components and hooks are pure. Violating these rules doesn't\njust risk a subtle bug anymore — it causes the compiler to silently skip optimizing that\ncomponent:\n\n- **Idempotent renders** — given the same props/state/context, a component must return\n the same output. No random values, no `Date.now()`, no side effects during render.\n- **Immutability** — never mutate props, state, or context directly. Always create new\n objects/arrays for changes.\n- **Side effects only in effects or event handlers** — never during render.\n- **Hooks called unconditionally, top-level, same order every render** — no hooks inside\n conditionals, loops, or nested functions.\n\n```tsx\n// ❌ Mutates a prop — breaks purity and the compiler can't safely memoize this\nfunction TodoList({ todos }: { todos: Todo[] }) {\n todos.sort((a, b) => a.priority - b.priority); // mutates caller's array\n return (\n <ul>\n {todos.map((t) => (\n <li key={t.id}>{t.title}</li>\n ))}\n </ul>\n );\n}\n\n// ✅ Creates a new array — pure, compiler-safe\nfunction TodoList({ todos }: { todos: Todo[] }) {\n const sorted = [...todos].sort((a, b) => a.priority - b.priority);\n return (\n <ul>\n {sorted.map((t) => (\n <li key={t.id}>{t.title}</li>\n ))}\n </ul>\n );\n}\n```\n\n## 3. Naming conventions the compiler relies on\n\nThe compiler identifies what to optimize by naming heuristics, same as the Rules of\nHooks linter:\n\n| Kind | Convention | Notes |\n| ------------------------------------------------------------------------ | ------------------------------- | ----------------------------------------------------------------------------- |\n| Components | `PascalCase`, returns JSX | Compiler treats it as a component to optimize |\n| Custom hooks | `camelCase`, prefixed `use` | Required for both Rules-of-Hooks lint and compiler analysis |\n| Plain helper functions that return JSX-like values but aren't components | Avoid `PascalCase`/`use` naming | Prevents the compiler (and other devs) from mistaking it for a component/hook |\n\n## 4. Typing React 19 primitives\n\n**`ref` as a normal prop** — `forwardRef` is no longer required for most cases; function\ncomponents can accept `ref` directly.\n\n```tsx\ntype InputProps = {\n ref?: React.Ref<HTMLInputElement>;\n placeholder?: string;\n};\n\nfunction TextInput({ ref, placeholder }: InputProps) {\n return <input ref={ref} placeholder={placeholder} />;\n}\n```\n\n**Actions with `useActionState`** — type the state and payload as generics; model the\nresult as a discriminated union (per the base typing skill) rather than optional fields.\n\n```tsx\ntype FormState = { status: 'idle' } | { status: 'error'; message: string } | { status: 'success' };\n\nconst [state, formAction, isPending] = useActionState<FormState, FormData>(\n async (_previous, formData) => {\n const email = formData.get('email');\n if (typeof email !== 'string' || !email.includes('@')) {\n return { status: 'error', message: 'Invalid email' };\n }\n await submit(email);\n return { status: 'success' };\n },\n { status: 'idle' },\n);\n```\n\n**Optimistic updates with `useOptimistic`** — type both the state and the update shape.\n\n```tsx\nconst [optimisticTodos, addOptimisticTodo] = useOptimistic<Todo[], Todo>(\n todos,\n (state, newTodo) => [...state, newTodo],\n);\n```\n\n**Reading a promise or context with `use()`** — type the resolved value, not the\npromise wrapper; `use()` is not a hook and may be called conditionally.\n\n```tsx\nfunction Comments({ commentsPromise }: { commentsPromise: Promise<Comment[]> }) {\n const comments = use(commentsPromise); // suspends until resolved\n return (\n <ul>\n {comments.map((c) => (\n <li key={c.id}>{c.text}</li>\n ))}\n </ul>\n );\n}\n```\n\n**Stable event callbacks with `useEffectEvent`** (React 19.2+) — separates \"event\"\nlogic from \"reactive\" effect logic so the callback always sees the latest props/state\nwithout being listed as an effect dependency. Needs `eslint-plugin-react-hooks@6+` to\nlint correctly.\n\n```tsx\nconst onVisit = useEffectEvent((url: string) => {\n logVisit(url, theme); // always fresh `theme`, never re-triggers the effect\n});\n\nuseEffect(() => {\n onVisit(url);\n}, [url]); // `theme` intentionally omitted — onVisit is stable\n```\n\n## 5. Compiler-friendly render patterns\n\n- Creating new object/array/function literals inline in render (`style={{ color }}`,\n `onClick={() => ...}`) is fine — stop manually hoisting or `useMemo`-wrapping these\n preemptively; the compiler memoizes them if it determines it's worthwhile.\n- Avoid module-level mutable variables read or written during render — that state is\n invisible to the compiler and breaks idempotence.\n- Don't use `useRef` to store a value that should trigger a re-render when it changes —\n refs are an imperative escape hatch, not state, and the compiler treats them as such.\n- Keep components small and composable. The compiler optimizes per component/hook\n boundary, so a single 300-line component gives it far less to work with than several\n focused ones.\n\n## 6. Typing props (builds on `typescript-strict-typing`)\n\n- `interface` for a component's `Props` — it's an entity shape, often extended.\n- A discriminated union when a component has mutually exclusive prop combinations,\n instead of a pile of optional props that can contradict each other.\n\n```tsx\n// ❌ Bad — nothing stops passing both `href` and `onClick` incoherently\ninterface ButtonProps {\n label: string;\n href?: string;\n onClick?: () => void;\n}\n\n// ✅ Good — the two variants can't be mixed\ntype ButtonProps =\n | { variant: 'link'; label: string; href: string }\n | { variant: 'action'; label: string; onClick: () => void };\n```\n\n## 7. Tooling setup\n\n**The compiler is opt-in — no default setup enables it automatically.** Plain\n`@vitejs/plugin-react` (`react()`), plain Next.js, plain Babel/webpack config, etc. do\n**not** run the compiler on their own. Verify it's actually wired up before assuming any\nof the memoization guidance above applies to your build.\n\n```bash\n# Compiler (build-time transform)\nnpm install --save-dev --save-exact babel-plugin-react-compiler@latest\n```\n\n**Lint rules — oxlint.** Oxlint ships a **native, Rust-based** `react/react-compiler`\nrule that runs the same compiler analysis in lint-only mode — same diagnostics as the\nBabel-based ESLint version, no Babel needed for linting. It's experimental and **off by\ndefault**, so it has to be enabled explicitly:\n\n```json\n// .oxlintrc.json\n{\n \"plugins\": [\"react\"],\n \"rules\": {\n \"react/react-compiler\": \"error\"\n }\n}\n```\n\nThis single rule reports two distinct things — both worth fixing, but for different\nreasons:\n\n- **Rules-of-React violations** (conditional hooks, reading a ref during render, mutating\n props) — these are real bugs, independent of the compiler.\n- **Compiler bail-outs** — places the compiler declined to optimize (e.g. unsupported\n syntax) without a rule violation. Not incorrect code, just a missed optimization —\n lower priority than a violation, but worth knowing about on a hot path.\n\nIf you'd rather use an existing ESLint plugin's rules through oxlint instead of the\nnative one (e.g. to match a team convention), oxlint's `jsPlugins` can load\n`eslint-plugin-react-hooks` directly — slower than the native rule since it still runs\nthrough Babel, but useful if you need a rule the native port doesn't cover yet:\n\n```json\n{\n \"jsPlugins\": [{ \"name\": \"react-hooks-js\", \"specifier\": \"eslint-plugin-react-hooks\" }],\n \"rules\": { \"react-hooks-js/set-state-in-render\": \"error\" }\n}\n```\n\n**Lint rules — ESLint** (if not on oxlint): the same rules ship inside\n`eslint-plugin-react-hooks`.\n\n```bash\nnpm install --save-dev eslint-plugin-react-hooks@latest\n```\n\n```js\n// eslint.config.js\nimport reactHooks from 'eslint-plugin-react-hooks';\nimport { defineConfig } from 'eslint/config';\n\nexport default defineConfig([reactHooks.configs.flat.recommended]);\n```\n\n**Wiring it into Vite 8.** `@vitejs/plugin-react` v6+ (the version that ships with Vite 8) switched its default transform from Babel to oxc for speed, so the compiler is\n**never** on by default and the old `react({ babel: {...} })` option **does not work**\non this setup — it's silently ignored, not an error, which is an easy way to think the\ncompiler is running when it isn't. Wire it in explicitly, as a separate Babel pass that\nruns before `react()`:\n\n```js\n// vite.config.js\nimport { defineConfig } from 'vite';\nimport react, { reactCompilerPreset } from '@vitejs/plugin-react';\nimport babel from '@rolldown/plugin-babel';\n\nexport default defineConfig({\n plugins: [\n babel({ presets: [reactCompilerPreset()] }), // must run before react()\n react(),\n ],\n});\n```\n\n```bash\nnpm install --save-dev @rolldown/plugin-babel @babel/core babel-plugin-react-compiler\nnpm install --save-dev @types/babel__core # if using TypeScript\n```\n\n`reactCompilerPreset()` is a helper exported from `@vitejs/plugin-react` itself — it\nbundles `babel-plugin-react-compiler` with sane default include/exclude filters so you\ndon't have to hand-roll a Babel preset. It optionally accepts:\n\n- `compilationMode: 'annotation'` — only compile components explicitly marked with a\n `\"use memo\"` directive, instead of the whole codebase (useful for a gradual rollout).\n- `target: '17' | '18'` — if any part of the app still runs on an older React major and\n needs the `react-compiler-runtime` package instead of `react/compiler-runtime`.\n\nAfter adding this, confirm it's actually active via the React DevTools \"Memo ✨\" badge\n(§8) before trusting the \"don't hand-roll memoization\" guidance in §1 — a silently\nmisconfigured Babel order (`react()` before `babel()`) is a common way for this to look\nwired up but do nothing.\n\n- Treat compiler-related lint errors (Rules-of-React violations, mismatched manual\n memoization) as must-fix, not optional — an unfixed violation means that component\n silently gets **zero** compiler optimization.\n- For a large existing codebase, adopt incrementally by scoping the babel plugin to a\n directory (e.g. a UI component library) before enabling it globally.\n- If a specific function is genuinely incompatible with the compiler (e.g. it calls\n `useForm` from `react-hook-form`), opt it out with the `\"use no memo\"` directive as\n the **first line of the function body** — it's a temporary escape hatch, not a\n permanent fix, so leave a comment explaining why.\n\n```tsx\nfunction LegacyForm() {\n 'use no memo';\n const form = useForm(); // incompatible with the compiler today\n // ...\n}\n```\n\n## 8. Checking whether the compiler is actually optimizing\n\n- **React DevTools** — an optimized component shows a \"Memo ✨\" badge next to its name\n in the component tree.\n- **ESLint** — the compiler's recommended rules flag Rules-of-React violations at lint\n time, before they ever become a silent runtime bail-out.\n- A bail-out is not a crash — it just means that specific component/hook is running\n unoptimized. Treat a missing \"Memo ✨\" badge on a component you expect to be optimized\n as a signal to check for a Rules-of-React violation, not a compiler bug.\n\n---\n\n## Review checklist\n\n- [ ] Compiler confirmed active (\"Memo ✨\" badge) before removing any _existing_ manual\n memoization — don't strip it on faith\n- [ ] No new `useMemo`/`useCallback`/`React.memo` added without a documented reason\n (confirmed bail-out, or a boundary the compiler can't see through)\n- [ ] No prop/state/context mutation anywhere in render\n- [ ] All hooks called unconditionally at the top level, same order every render\n- [ ] Side effects live in `useEffect`/event handlers, never during render\n- [ ] Components are `PascalCase`; hooks are `camelCase` and prefixed `use`\n- [ ] `ref` accepted as a normal prop instead of `forwardRef`, unless targeting a version\n that requires it\n- [ ] Action/optimistic-update state modeled as a discriminated union, not optional\n fields\n- [ ] Mutually exclusive prop combinations modeled as a discriminated union `Props` type\n- [ ] `eslint-plugin-react-hooks` recommended config enabled and passing\n- [ ] Any `\"use no memo\"` usage has a comment explaining why\n\n## Quick reference\n\n| Situation | Do |\n| -------------------------------------------------------------- | ------------------------------------------------------------- |\n| Tempted to write `useMemo`/`useCallback` | Don't — write the plain expression, let the compiler decide |\n| Need a ref on a function component | Accept `ref` as a prop, skip `forwardRef` |\n| Form/async state with distinct outcomes | Discriminated union via `useActionState`, not optional fields |\n| Callback needs latest props/state without re-running an effect | `useEffectEvent` |\n| A hook/library is known-incompatible with the compiler | `\"use no memo\"` at the top of that function, with a comment |\n| Checking if optimization is happening | React DevTools \"Memo ✨\" badge + compiler ESLint rules |\n";
|
|
1225
|
-
var SKILL_default$1 = "---\nname: typescript-strict-typing\ndescription: Enforce strict TypeScript typing discipline and naming conventions whenever writing, generating, reviewing, or refactoring TypeScript/TSX code. Use this any time code contains `any`, loose/implicit types, untyped catch blocks, unchecked type assertions, boolean-flag state instead of variants, or inconsistent naming — even if the user didn't explicitly ask for a \"strict\" pass. Governs `any` vs `unknown`, narrowing, discriminated unions, `interface` vs `type` usage, naming conventions, and tsconfig strictness baseline.\n---\n\n# TypeScript Strict Typing Enforcer\n\n## Why this matters\n\nTypeScript's type system is only as strong as its weakest escape hatch. A single `any`,\nan un-narrowed `unknown`, or a lazy `as` assertion silently turns off the compiler for\neverything downstream of it — the bug doesn't disappear, it just moves to runtime where\nit's more expensive to find. The goal of this skill is not \"add types for the sake of\nit,\" it's **make illegal states unrepresentable** and **make the compiler prove\ncorrectness wherever possible**, so bugs surface at build time instead of in production.\n\nApply these rules by default whenever writing or editing TypeScript, without waiting for\nthe user to ask for \"strict mode\" explicitly. If a rule would need to be broken (e.g. a\nthird-party type is genuinely untyped), say so explicitly and isolate the escape hatch\nrather than letting it leak.\n\n## Core principle\n\n> Narrow, don't cast. Model states, don't flag them. Let the compiler do the checking.\n\n---\n\n## 1. Never use `any`\n\n`any` is not \"unknown type,\" it's \"type checking off.\" It's contagious — once a value is\n`any`, everything it touches becomes unchecked too.\n\n- Never write `any` for parameters, return types, variables, or generics.\n- Use `unknown` for genuinely unknown external data (API responses, `JSON.parse`, catch\n clauses, third-party callbacks) and narrow it before use.\n- Use generics (`<T>`) when a function needs to work across types but preserve the\n relationship between input and output.\n- If a library ships untyped, write a minimal local type/interface for the surface area\n you actually use instead of reaching for `any`.\n\n```ts\n// ❌ Bad\nfunction parseConfig(json: any) {\n return json.settings.theme; // no safety, no autocomplete, silent runtime crash\n}\n\n// ✅ Good\nfunction parseConfig(json: unknown): string {\n if (\n typeof json === 'object' &&\n json !== null &&\n 'settings' in json &&\n typeof (json as { settings: unknown }).settings === 'object'\n ) {\n // still narrow further or validate with a schema library (zod, valibot, etc.)\n }\n throw new Error('Invalid config shape');\n}\n```\n\nThe only acceptable `any` is a well-justified, isolated, and commented one (e.g.\ninterfacing with a genuinely untyped legacy module) — never a default.\n\n## 2. `unknown` + narrowing, not casting\n\nPrefer proving a type through control flow over asserting it with `as`.\n\n**Narrowing techniques, in order of preference:**\n\n1. **`typeof`** — primitives (`string`, `number`, `boolean`, `undefined`, `function`)\n2. **`instanceof`** — class instances, `Error`, `Date`, custom classes\n3. **`in`** — checking a property exists before accessing it on a union/unknown\n4. **User-defined type guards** — `function isUser(x: unknown): x is User`\n5. **Discriminated union tag checks** — `switch (value.kind) { ... }` (see §3)\n6. **Exhaustiveness checks** — a `never`-typed default branch so adding a new variant is\n a compile error until every switch/if-chain handles it\n\n```ts\n// ✅ Type guard\nfunction isUser(value: unknown): value is User {\n return typeof value === 'object' && value !== null && 'id' in value && 'email' in value;\n}\n\n// ✅ Exhaustiveness check\nfunction assertNever(x: never): never {\n throw new Error(`Unhandled case: ${JSON.stringify(x)}`);\n}\n\nfunction area(shape: Shape): number {\n switch (shape.kind) {\n case 'circle':\n return Math.PI * shape.radius ** 2;\n case 'square':\n return shape.side ** 2;\n default:\n return assertNever(shape); // compile error if a variant is missed\n }\n}\n```\n\nType assertions (`as X`) and the non-null assertion (`!`) bypass this entirely — treat\nthem as a last resort (see §7), not a shortcut.\n\n## 3. Discriminated unions for variant state\n\nWhenever a value can be one of several distinct \"shapes\" (loading/success/error states,\nevent types, API response variants), model it as a **discriminated union** with a\nliteral tag field — never as a loose object with optional fields or boolean flags.\n\n```ts\n// ❌ Bad — booleans can contradict each other; unclear which fields are valid together\ninterface FetchState {\n isLoading: boolean;\n isError: boolean;\n data?: User;\n error?: string;\n}\n\n// ✅ Good — only one shape is possible at a time, and the compiler enforces it\ntype FetchState =\n | { status: 'idle' }\n | { status: 'loading' }\n | { status: 'success'; data: User }\n | { status: 'error'; error: string };\n\nfunction render(state: FetchState) {\n switch (state.status) {\n case 'success':\n return state.data.name; // `data` is guaranteed to exist here\n case 'error':\n return state.error; // `error` is guaranteed to exist here\n default:\n return null;\n }\n}\n```\n\nUse a consistent tag field name across a codebase (`kind`, `type`, or `status` — pick\none and stick with it) so narrowing patterns stay predictable.\n\n## 4. `interface` vs `type` — pick by intent, not habit\n\nBoth can describe object shapes, but they signal different intent. Default rule:\n\n| Use `interface` for... | Use `type` for... |\n| -------------------------------------------------------------------------- | -------------------------------------------------------------------------- |\n| Object / entity shapes (a `User`, a `Product`, a component's `Props`) | Unions (`\"a\" \\| \"b\"`) and discriminated unions |\n| Public API contracts meant to be `implements`-ed by classes | Intersections (`A & B`) |\n| Shapes that consumers may want to **extend/augment** (declaration merging) | Tuples (`[string, number]`) |\n| | Function types / callback signatures |\n| | Mapped, conditional, or utility-derived types (`Partial<T>`, `Pick<T, K>`) |\n| | Aliasing a primitive or another type for readability |\n\n```ts\n// ✅ interface — an entity with identity, extendable\ninterface User {\n id: string;\n email: string;\n role: UserRole;\n}\n\ninterface AdminUser extends User {\n permissions: Permission[];\n}\n\n// ✅ type — union, alias, derived shape\ntype UserRole = 'admin' | 'editor' | 'viewer';\ntype UserId = User['id'];\ntype PartialUser = Partial<User>;\ntype Callback<T> = (value: T) => void;\n```\n\nDon't mix conventions arbitrarily within one file — if a shape is a plain data object\nthat will never need a union/intersection, `interface` is the default; the moment it\nneeds to express \"one of several shapes,\" reach for `type`.\n\n## 5. Naming conventions\n\n| Kind | Convention | Example |\n| ---------------------------------------------------------- | ------------------------------------------- | ----------------------------------- |\n| Types, interfaces, classes, enums | `PascalCase` | `UserProfile`, `OrderStatus` |\n| Interfaces | `PascalCase`, **no `I` prefix** | `User`, not `IUser` |\n| Type aliases | `PascalCase` | `type ApiResponse<T> = ...` |\n| Variables, functions, methods, properties | `camelCase` | `getUserById`, `isValid` |\n| Booleans | `camelCase` with `is/has/should/can` prefix | `isLoading`, `hasPermission` |\n| True constants (module-level, never reassigned, primitive) | `UPPER_SNAKE_CASE` | `MAX_RETRIES`, `DEFAULT_TIMEOUT_MS` |\n| Enum members | `PascalCase` | `enum Status { Active, Archived }` |\n| Generic type parameters (simple, single-purpose) | Single uppercase letter | `T`, `K`, `V`, `E` for errors |\n| Generic type parameters (multiple / non-obvious) | Descriptive, prefixed with `T` | `TInput`, `TOutput`, `TContext` |\n| Discriminated union tag field | Consistent across the codebase | `kind`, `type`, or `status` |\n| Files with a single exported entity | Match the entity name | `UserProfile.ts`, `useAuth.ts` |\n\nNaming should describe **intent**, not implementation — `fetchUser` not\n`getUserFromApiEndpoint`; `retryCount` not `numRetries2`.\n\n## 6. Baseline `tsconfig.json` strictness\n\nTreat these as the non-negotiable floor for any project this skill touches:\n\n```json\n{\n \"compilerOptions\": {\n \"strict\": true,\n \"noImplicitAny\": true,\n \"strictNullChecks\": true,\n \"strictFunctionTypes\": true,\n \"strictPropertyInitialization\": true,\n \"noUncheckedIndexedAccess\": true,\n \"exactOptionalPropertyTypes\": true,\n \"noImplicitOverride\": true,\n \"noFallthroughCasesInSwitch\": true,\n \"noUnusedLocals\": true,\n \"noUnusedParameters\": true,\n \"forceConsistentCasingInFileNames\": true\n }\n}\n```\n\n`strict: true` alone enables the core group (`noImplicitAny`, `strictNullChecks`, etc.),\nbut `noUncheckedIndexedAccess` and `exactOptionalPropertyTypes` are commonly missed and\nclose real gaps (array/object index access returning `T` instead of `T | undefined`;\noptional properties silently accepting `undefined` as an explicit value).\n\n## 7. Type assertions and non-null assertions are a last resort\n\n- `as X` and `x!` tell the compiler \"trust me\" — they produce zero runtime safety and\n actively hide bugs if wrong.\n- Acceptable only when the compiler genuinely cannot know something you do (e.g. a DOM\n query you've already null-checked, or narrowing a third-party type at a well-tested\n boundary) — and even then, prefer a type guard or a runtime check over a bare\n assertion.\n- Never use `as any` or `as unknown as X` to force an incompatible cast — that's `any`\n wearing a disguise.\n- `x!` should almost always be replaceable by an actual null check or optional chaining\n (`x?.y`) plus a real fallback.\n\n## 8. Readonly by default\n\nPrefer immutable shapes unless mutation is intentional and localized.\n\n```ts\ninterface Point {\n readonly x: number;\n readonly y: number;\n}\n\nfunction config(values: readonly string[]) {\n /* ... */\n}\n\nconst ROLES = ['admin', 'editor', 'viewer'] as const;\ntype UserRole = (typeof ROLES)[number];\n```\n\n## 9. Explicit return types on exported/public functions\n\nInference is fine for local, private helpers, but exported functions, class methods, and\nanything forming a public API should declare an explicit return type. This prevents an\ninternal implementation change from silently widening/narrowing the public contract.\n\n```ts\n// ❌ Return type is inferred and can silently drift\nexport function getActiveUsers(users: User[]) {\n return users.filter((u) => u.active);\n}\n\n// ✅ Explicit, intentional contract\nexport function getActiveUsers(users: User[]): User[] {\n return users.filter((u) => u.active);\n}\n```\n\n## 10. Prefer literal unions over numeric enums\n\nString literal unions are simpler, tree-shake better, and produce clearer error\nmessages than TypeScript `enum`. Reserve `enum` (or `as const` object maps) for cases\nthat need reverse lookup or genuinely benefit from a namespaced runtime value.\n\n```ts\n// ✅ Preferred\ntype OrderStatus = 'pending' | 'shipped' | 'delivered' | 'cancelled';\n\n// Acceptable when a namespaced runtime object is actually needed\nconst OrderStatus = {\n Pending: 'pending',\n Shipped: 'shipped',\n} as const;\ntype OrderStatus = (typeof OrderStatus)[keyof typeof OrderStatus];\n```\n\n---\n\n## Review checklist\n\nBefore considering TypeScript code \"done,\" verify:\n\n- [ ] No `any` anywhere (including implicit `any` from missing annotations)\n- [ ] External/uncertain data enters as `unknown` and is narrowed before use\n- [ ] Variant state is a discriminated union, not optional fields + booleans\n- [ ] `interface` used for object/entity shapes; `type` used for unions/aliases/intersections\n- [ ] No stray `I` prefixes on interfaces\n- [ ] Naming follows the casing table in §5 consistently\n- [ ] `as` / `!` are rare, justified, and can't be replaced by a guard or null check\n- [ ] Exported functions/methods have explicit return types\n- [ ] Switch statements over unions have an exhaustiveness (`never`) check\n- [ ] `tsconfig.json` includes the strictness baseline in §6\n\n## Quick reference\n\n| Situation | Use |\n| ---------------------------------------------- | ------------------------------------------------------------- |\n| External/uncertain data | `unknown` + narrowing |\n| \"This value is definitely one of these shapes\" | Discriminated union (`type`) |\n| Object with identity, may be extended | `interface` |\n| Union, intersection, tuple, mapped type | `type` |\n| Need to prove a type through logic | Type guard / narrowing |\n| Tempted to write `any` | Stop — use `unknown`, a generic, or a local interface instead |\n";
|
|
1226
|
-
var SKILL_default = "---\nname: wangs-ui-components\ndescription: Foundational rules, subpath imports, design tokens, and the MCP Discovery Protocol for building React apps with Wangs UI.\n---\n\n# Skill: Wangs UI Component Fundamentals & MCP Protocol\n\nUse this skill whenever you write or modify UI components using Wangs UI (`@wangs-ui/react-core`, `@wangs-ui/react-icons`, `@wangs-ui/react-presets`, `@wangs-ui/foundation`).\n\n---\n\n## 1. The MCP Discovery Protocol (Mandatory Single Source of Truth)\n\nDo **NOT** guess component props, Pass-Through (`pt`) slots, or event names. Always query the MCP server dynamically to retrieve the current API signatures and live story implementations:\n\n```mermaid\ngraph TD\n A[Identify Component Needed] --> B[Call get-documentation id]\n B --> C{Need live story / variant code?}\n C -->|Yes| D[Call get-documentation-for-story]\n C -->|No| E[Check Graphify: query_graph]\n D --> E\n E --> F[Implement Component with Subpath Imports]\n```\n\n### Discovery Steps:\n\n1. **Inspect Component Contract & Props**:\n ```json\n get-documentation({ \"id\": \"button\" })\n get-documentation({ \"id\": \"input\" })\n get-documentation({ \"id\": \"datatable\" })\n ```\n2. **Inspect Live Usage & Story Variants**:\n ```json\n get-documentation-for-story({ \"id\": \"button\", \"storyName\": \"Default\" })\n get-documentation-for-story({ \"id\": \"datatable\", \"storyName\": \"ServerPagination\" })\n ```\n3. **Inspect Relationships & Real Usages in Graph**:\n ```json\n query_graph({ \"query\": \"DataTable\" })\n query_graph({ \"query\": \"usePT\" })\n ```\n\n---\n\n## 2. Subpath Modular Imports (Mandatory)\n\nAlways import via specific subpaths to guarantee tree-shaking and avoid bundling entire packages:\n\n```tsx\n// Primitives (@wangs-ui/react-core/primitive/*)\nimport Button from '@wangs-ui/react-core/primitive/button';\nimport Input from '@wangs-ui/react-core/primitive/input';\nimport NumberInput from '@wangs-ui/react-core/primitive/numberinput';\nimport Select from '@wangs-ui/react-core/primitive/select';\nimport Badge from '@wangs-ui/react-core/primitive/badge';\nimport Card from '@wangs-ui/react-core/primitive/card';\nimport DataTable from '@wangs-ui/react-core/primitive/datatable';\n\n// Blocks (@wangs-ui/react-core/blocks/*)\nimport AppLayout from '@wangs-ui/react-core/blocks/applayout';\nimport Sidebar from '@wangs-ui/react-core/blocks/sidebar';\n\n// Providers & System Hooks\nimport { WangsUiProvider } from '@wangs-ui/react-core/api';\nimport { useI18n } from '@wangs-ui/react-i18n';\nimport { useTheme } from '@wangs-ui/foundation/theme';\n\n// Icons (@wangs-ui/react-icons)\nimport { SearchLine, AddLine, DeleteBin6Line, CheckLine } from '@wangs-ui/react-icons';\n```\n\n---\n\n## 3. Strict Primitive Substitution Rule\n\nNever write raw HTML elements when a Wangs UI primitive exists:\n\n| Forbidden Raw HTML | Mandatory Wangs UI Component | Subpath Import | MCP Documentation ID |\n| :------------------------ | :--------------------------- | :------------------------------------------- | :------------------- |\n| `<button>` | `Button` | `@wangs-ui/react-core/primitive/button` | `button` |\n| `<input type=\"text\">` | `Input` | `@wangs-ui/react-core/primitive/input` | `input` |\n| `<input type=\"number\">` | `NumberInput` | `@wangs-ui/react-core/primitive/numberinput` | `numberinput` |\n| `<input type=\"checkbox\">` | `Checkbox` | `@wangs-ui/react-core/primitive/checkbox` | `checkbox` |\n| `<select>` | `Select` | `@wangs-ui/react-core/primitive/select` | `select` |\n| `<dialog>` / modal | `Dialog` / `Modal` | `@wangs-ui/react-core/primitive/dialog` | `dialog`, `modal` |\n| `<table>` | `DataTable` | `@wangs-ui/react-core/primitive/datatable` | `datatable` |\n| Container box | `Card` | `@wangs-ui/react-core/primitive/card` | `card` |\n| Pill badge / status | `Badge` | `@wangs-ui/react-core/primitive/badge` | `badge` |\n\n---\n\n## 4. Typography Scale & 4px Spacing Tokens\n\n### Typography Helper Classes\n\n- `.heading-1` — Page title (22px, 600)\n- `.heading-2` — Section / Card title (18px, 600)\n- `.heading-3` — Sub-header (16px, 500)\n- `.heading-4` — Field label (14px, 500)\n- `.heading-5` — Small group header (12px, 600)\n- `.p` — Body copy (12px, 500)\n\n### 4px Spacing Tokens\n\n- Gap: `gap-xs` (4px), `gap-s` (6px), `gap-md` (8px), `gap-m` (12px), `gap-l` (16px), `gap-xl` (20px), `gap-xxl` (24px)\n- Padding: `p-xs`, `p-s`, `p-md`, `p-m`, `p-l`, `p-xl`, `p-xxl`\n";
|
|
1227
|
-
var __dirname$1 = path.dirname(fileURLToPath(import.meta.url));
|
|
1228
|
-
var EMBEDDED_SKILLS_RAW = {
|
|
1229
|
-
"create-form": SKILL_default$7,
|
|
1230
|
-
"data-table": SKILL_default$6,
|
|
1231
|
-
"dialog-modal": SKILL_default$5,
|
|
1232
|
-
"i18n-usage": SKILL_default$4,
|
|
1233
|
-
"layout-navigation": SKILL_default$3,
|
|
1234
|
-
"react19-compiler-typescript": SKILL_default$2,
|
|
1235
|
-
"typescript-strict-typing": SKILL_default$1,
|
|
1236
|
-
"wangs-ui-components": SKILL_default
|
|
1237
|
-
};
|
|
1238
|
-
function parseSkillContent(id, content) {
|
|
1239
|
-
let name = id;
|
|
1240
|
-
let description = "Wangs UI consumer skill";
|
|
1241
|
-
const frontmatterMatch = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n/);
|
|
1242
|
-
if (frontmatterMatch) {
|
|
1243
|
-
const fm = frontmatterMatch[1];
|
|
1244
|
-
const nameMatch = fm.match(/^name:\s*(.+)$/m);
|
|
1245
|
-
const descMatch = fm.match(/^description:\s*(.+)$/m);
|
|
1246
|
-
if (nameMatch) name = nameMatch[1].trim();
|
|
1247
|
-
if (descMatch) description = descMatch[1].trim();
|
|
1248
|
-
}
|
|
1249
|
-
return {
|
|
1250
|
-
id,
|
|
1251
|
-
name,
|
|
1252
|
-
description,
|
|
1253
|
-
content
|
|
1254
|
-
};
|
|
1255
|
-
}
|
|
1256
|
-
function loadAllSkills() {
|
|
1257
|
-
if (Object.keys(EMBEDDED_SKILLS_RAW).length > 0) return Object.entries(EMBEDDED_SKILLS_RAW).map(([id, content]) => parseSkillContent(id, content));
|
|
1258
|
-
const candidates = [
|
|
1259
|
-
path.resolve(__dirname$1, "skills"),
|
|
1260
|
-
path.resolve(__dirname$1, "../skills"),
|
|
1261
|
-
path.resolve(__dirname$1, "../../skills")
|
|
1262
|
-
];
|
|
1263
|
-
for (const skillsDir of candidates) if (fs.existsSync(skillsDir)) {
|
|
1264
|
-
const entries = fs.readdirSync(skillsDir, { withFileTypes: true });
|
|
1265
|
-
const skills = [];
|
|
1266
|
-
for (const entry of entries) if (entry.isDirectory()) {
|
|
1267
|
-
const skillMdPath = path.join(skillsDir, entry.name, "SKILL.md");
|
|
1268
|
-
if (fs.existsSync(skillMdPath)) {
|
|
1269
|
-
const content = fs.readFileSync(skillMdPath, "utf-8");
|
|
1270
|
-
skills.push(parseSkillContent(entry.name, content));
|
|
1271
|
-
}
|
|
1272
|
-
}
|
|
1273
|
-
if (skills.length > 0) return skills;
|
|
1274
|
-
}
|
|
1275
|
-
return [];
|
|
1276
|
-
}
|
|
1277
|
-
function getAgentSkillDirs(baseDir = process.cwd()) {
|
|
1278
|
-
const dirs = [];
|
|
1279
|
-
const candidates = [
|
|
1280
|
-
path.join(baseDir, ".agents", "skills"),
|
|
1281
|
-
path.join(baseDir, ".claude", "skills"),
|
|
1282
|
-
path.join(baseDir, ".opencode", "skills"),
|
|
1283
|
-
path.join(baseDir, ".kilo", "skills")
|
|
1284
|
-
];
|
|
1285
|
-
for (const c of candidates) if (fs.existsSync(c) || fs.existsSync(path.dirname(c))) dirs.push(c);
|
|
1286
|
-
if (dirs.length === 0) dirs.push(path.join(baseDir, ".agents", "skills"));
|
|
1287
|
-
return dirs;
|
|
1288
|
-
}
|
|
1289
|
-
function installSkill(skill, baseDir = process.cwd()) {
|
|
1290
|
-
const targetDirs = getAgentSkillDirs(baseDir);
|
|
1291
|
-
const writtenPaths = [];
|
|
1292
|
-
for (const baseSkillDir of targetDirs) {
|
|
1293
|
-
const destDir = path.join(baseSkillDir, skill.id);
|
|
1294
|
-
fs.mkdirSync(destDir, { recursive: true });
|
|
1295
|
-
const destFile = path.join(destDir, "SKILL.md");
|
|
1296
|
-
fs.writeFileSync(destFile, skill.content, "utf-8");
|
|
1297
|
-
writtenPaths.push(destFile);
|
|
1298
|
-
}
|
|
1299
|
-
return writtenPaths;
|
|
1300
|
-
}
|
|
1301
|
-
//#endregion
|
|
1302
102
|
//#region src/utils/mcpInstaller.ts
|
|
1303
103
|
function setupAgentConfigurations(options) {
|
|
1304
104
|
const { targetDir, templateDir, agents, skills } = options;
|
|
@@ -1380,11 +180,11 @@ function getRunCommand(pm, script) {
|
|
|
1380
180
|
}
|
|
1381
181
|
//#endregion
|
|
1382
182
|
//#region src/index.ts
|
|
1383
|
-
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
183
|
+
var __dirname$1 = path.dirname(fileURLToPath(import.meta.url));
|
|
1384
184
|
function generateProject(options) {
|
|
1385
185
|
const { projectName, targetDir, preset, agents, skills, git } = options;
|
|
1386
|
-
let templateDir = path.resolve(__dirname, "../template");
|
|
1387
|
-
if (!fs.existsSync(templateDir)) templateDir = path.resolve(__dirname, "template");
|
|
186
|
+
let templateDir = path.resolve(__dirname$1, "../template");
|
|
187
|
+
if (!fs.existsSync(templateDir)) templateDir = path.resolve(__dirname$1, "template");
|
|
1388
188
|
const s = spinner();
|
|
1389
189
|
s.start(`Scaffolding Wangs UI React Application in ${path.basename(targetDir)}...`);
|
|
1390
190
|
copyProjectTemplate({
|
|
@@ -1442,7 +242,7 @@ async function promptUser(initialOptions) {
|
|
|
1442
242
|
skills: skills || allSkills.map((s) => s.id),
|
|
1443
243
|
git: git !== void 0 ? git : true
|
|
1444
244
|
};
|
|
1445
|
-
intro(`\x1b[36m🚀 Wangs UI React App Scaffolder\x1b[0m [2m(v1.0.
|
|
245
|
+
intro(`\x1b[36m🚀 Wangs UI React App Scaffolder\x1b[0m [2m(v1.0.51)[0m`);
|
|
1446
246
|
if (!projectName) {
|
|
1447
247
|
const nameResponse = await text({
|
|
1448
248
|
message: "What is your project name?",
|