grimoire-wizard 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -51
- package/dist/cli.js +509 -70
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +114 -12
- package/dist/index.js +511 -71
- package/dist/index.js.map +1 -1
- package/examples/yaml/demo.yaml +54 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -56,6 +56,11 @@ var separatorOptionSchema = z.object({
|
|
|
56
56
|
separator: z.string()
|
|
57
57
|
});
|
|
58
58
|
var selectChoiceSchema = z.union([selectOptionSchema, separatorOptionSchema]);
|
|
59
|
+
var stepReviewConfigSchema = z.object({
|
|
60
|
+
hide: z.boolean().optional(),
|
|
61
|
+
label: z.string().optional(),
|
|
62
|
+
format: z.enum(["none", "uppercase", "lowercase", "capitalize"]).optional()
|
|
63
|
+
}).optional();
|
|
59
64
|
var baseStepFields = {
|
|
60
65
|
id: z.string(),
|
|
61
66
|
message: z.string(),
|
|
@@ -64,7 +69,8 @@ var baseStepFields = {
|
|
|
64
69
|
when: conditionSchema.optional(),
|
|
65
70
|
keepValuesOnPrevious: z.boolean().optional(),
|
|
66
71
|
required: z.boolean().optional(),
|
|
67
|
-
group: z.string().optional()
|
|
72
|
+
group: z.string().optional(),
|
|
73
|
+
review: stepReviewConfigSchema
|
|
68
74
|
};
|
|
69
75
|
var textStepSchema = z.object({
|
|
70
76
|
...baseStepFields,
|
|
@@ -81,7 +87,8 @@ var selectStepSchema = z.object({
|
|
|
81
87
|
default: z.string().optional(),
|
|
82
88
|
routes: z.record(z.string(), z.string()).optional(),
|
|
83
89
|
pageSize: z.number().int().positive().optional(),
|
|
84
|
-
loop: z.boolean().optional()
|
|
90
|
+
loop: z.boolean().optional(),
|
|
91
|
+
columns: z.number().int().min(1).max(8).optional()
|
|
85
92
|
});
|
|
86
93
|
var multiSelectStepSchema = z.object({
|
|
87
94
|
...baseStepFields,
|
|
@@ -92,7 +99,8 @@ var multiSelectStepSchema = z.object({
|
|
|
92
99
|
min: z.number().int().nonnegative().optional(),
|
|
93
100
|
max: z.number().int().positive().optional(),
|
|
94
101
|
pageSize: z.number().int().positive().optional(),
|
|
95
|
-
loop: z.boolean().optional()
|
|
102
|
+
loop: z.boolean().optional(),
|
|
103
|
+
columns: z.number().int().min(1).max(8).optional()
|
|
96
104
|
});
|
|
97
105
|
var confirmStepSchema = z.object({
|
|
98
106
|
...baseStepFields,
|
|
@@ -120,7 +128,8 @@ var searchStepSchema = z.object({
|
|
|
120
128
|
default: z.string().optional(),
|
|
121
129
|
placeholder: z.string().optional(),
|
|
122
130
|
pageSize: z.number().int().positive().optional(),
|
|
123
|
-
loop: z.boolean().optional()
|
|
131
|
+
loop: z.boolean().optional(),
|
|
132
|
+
columns: z.number().int().min(1).max(8).optional()
|
|
124
133
|
});
|
|
125
134
|
var editorStepSchema = z.object({
|
|
126
135
|
...baseStepFields,
|
|
@@ -148,7 +157,14 @@ var messageStepSchema = z.object({
|
|
|
148
157
|
});
|
|
149
158
|
var noteStepSchema = z.object({
|
|
150
159
|
...baseStepFields,
|
|
151
|
-
type: z.literal("note")
|
|
160
|
+
type: z.literal("note"),
|
|
161
|
+
style: z.enum(["info", "warning", "error", "success", "code", "banner"]).optional()
|
|
162
|
+
});
|
|
163
|
+
var browserStepSchema = z.object({
|
|
164
|
+
...baseStepFields,
|
|
165
|
+
type: z.literal("browser"),
|
|
166
|
+
url: z.string(),
|
|
167
|
+
fallback: z.string().optional()
|
|
152
168
|
});
|
|
153
169
|
var stepConfigSchema = z.discriminatedUnion("type", [
|
|
154
170
|
textStepSchema,
|
|
@@ -162,12 +178,19 @@ var stepConfigSchema = z.discriminatedUnion("type", [
|
|
|
162
178
|
pathStepSchema,
|
|
163
179
|
toggleStepSchema,
|
|
164
180
|
messageStepSchema,
|
|
165
|
-
noteStepSchema
|
|
181
|
+
noteStepSchema,
|
|
182
|
+
browserStepSchema
|
|
166
183
|
]);
|
|
167
184
|
var hexColorSchema = z.string().regex(
|
|
168
185
|
/^#[0-9a-fA-F]{6}$/,
|
|
169
186
|
"Must be a 6-digit hex color (e.g., #FF0000)"
|
|
170
187
|
);
|
|
188
|
+
var progressBarConfigSchema = z.object({
|
|
189
|
+
width: z.number().int().positive().max(200).optional(),
|
|
190
|
+
filledColor: hexColorSchema.optional(),
|
|
191
|
+
emptyColor: hexColorSchema.optional(),
|
|
192
|
+
style: z.enum(["blocks", "line", "dots", "arrow"]).optional()
|
|
193
|
+
}).optional();
|
|
171
194
|
var themeConfigSchema = z.object({
|
|
172
195
|
preset: z.enum(["default", "catppuccin", "dracula", "nord", "tokyonight", "monokai"]).optional(),
|
|
173
196
|
tokens: z.object({
|
|
@@ -177,7 +200,12 @@ var themeConfigSchema = z.object({
|
|
|
177
200
|
warning: hexColorSchema.optional(),
|
|
178
201
|
info: hexColorSchema.optional(),
|
|
179
202
|
muted: hexColorSchema.optional(),
|
|
180
|
-
accent: hexColorSchema.optional()
|
|
203
|
+
accent: hexColorSchema.optional(),
|
|
204
|
+
highlight: hexColorSchema.optional(),
|
|
205
|
+
highlightBg: hexColorSchema.optional(),
|
|
206
|
+
pointer: hexColorSchema.optional(),
|
|
207
|
+
checked: hexColorSchema.optional(),
|
|
208
|
+
dimmed: hexColorSchema.optional()
|
|
181
209
|
}).optional(),
|
|
182
210
|
icons: z.object({
|
|
183
211
|
step: z.string().optional(),
|
|
@@ -191,12 +219,15 @@ var themeConfigSchema = z.object({
|
|
|
191
219
|
frames: z.array(z.string()).min(1),
|
|
192
220
|
interval: z.number().positive().optional()
|
|
193
221
|
})
|
|
194
|
-
]).optional()
|
|
222
|
+
]).optional(),
|
|
223
|
+
spinnerElapsed: z.boolean().optional(),
|
|
224
|
+
progressBar: progressBarConfigSchema
|
|
195
225
|
});
|
|
196
226
|
var preFlightCheckSchema = z.object({
|
|
197
227
|
name: z.string(),
|
|
198
228
|
run: z.string(),
|
|
199
|
-
message: z.string()
|
|
229
|
+
message: z.string(),
|
|
230
|
+
showOutput: z.boolean().optional()
|
|
200
231
|
});
|
|
201
232
|
var actionConfigSchema = z.object({
|
|
202
233
|
name: z.string().optional(),
|
|
@@ -209,7 +240,13 @@ var wizardConfigSchema = z.object({
|
|
|
209
240
|
version: z.string().optional(),
|
|
210
241
|
description: z.string().optional(),
|
|
211
242
|
review: z.boolean().optional(),
|
|
212
|
-
icon: z.string().optional()
|
|
243
|
+
icon: z.string().optional(),
|
|
244
|
+
iconSize: z.union([z.enum(["small", "medium", "large"]), z.number().int().positive()]).optional(),
|
|
245
|
+
font: z.string().min(1).optional(),
|
|
246
|
+
banner: z.union([z.string(), z.function()]).optional(),
|
|
247
|
+
subtitle: z.string().optional(),
|
|
248
|
+
clearBetweenSteps: z.boolean().optional(),
|
|
249
|
+
checksStyle: z.enum(["spinner", "tasklist"]).optional()
|
|
213
250
|
}),
|
|
214
251
|
theme: themeConfigSchema.optional(),
|
|
215
252
|
steps: z.array(stepConfigSchema).min(1),
|
|
@@ -490,11 +527,12 @@ async function loadWithInheritance(filePath, seen) {
|
|
|
490
527
|
async function loadWizardConfig(filePath) {
|
|
491
528
|
const config = await loadWithInheritance(filePath, /* @__PURE__ */ new Set());
|
|
492
529
|
detectCycles(config);
|
|
530
|
+
config._configFilePath = resolve(filePath);
|
|
493
531
|
return config;
|
|
494
532
|
}
|
|
495
533
|
|
|
496
534
|
// src/runner.ts
|
|
497
|
-
import { execSync } from "child_process";
|
|
535
|
+
import { execSync, execFileSync } from "child_process";
|
|
498
536
|
import { resolve as resolve2, dirname as dirname2 } from "path";
|
|
499
537
|
import { pathToFileURL } from "url";
|
|
500
538
|
|
|
@@ -671,7 +709,8 @@ function wizardReducer(state, transition, config) {
|
|
|
671
709
|
errors: { ...state.errors, [state.currentStepId]: validationError }
|
|
672
710
|
};
|
|
673
711
|
}
|
|
674
|
-
const
|
|
712
|
+
const isDisplayOnly = currentStep.type === "note" || currentStep.type === "message" || currentStep.type === "browser";
|
|
713
|
+
const updatedAnswers = isDisplayOnly ? { ...state.answers } : {
|
|
675
714
|
...state.answers,
|
|
676
715
|
[state.currentStepId]: transition.value
|
|
677
716
|
};
|
|
@@ -879,7 +918,12 @@ var DEFAULT_TOKENS = {
|
|
|
879
918
|
warning: "#FFD93D",
|
|
880
919
|
info: "#4D96FF",
|
|
881
920
|
muted: "#888888",
|
|
882
|
-
accent: "#C084FC"
|
|
921
|
+
accent: "#C084FC",
|
|
922
|
+
highlight: "#5B9BD5",
|
|
923
|
+
highlightBg: "#1E1E2E",
|
|
924
|
+
pointer: "#C084FC",
|
|
925
|
+
checked: "#6BCB77",
|
|
926
|
+
dimmed: "#555555"
|
|
883
927
|
};
|
|
884
928
|
var DEFAULT_ICONS = {
|
|
885
929
|
step: "\u25CF",
|
|
@@ -887,6 +931,25 @@ var DEFAULT_ICONS = {
|
|
|
887
931
|
stepPending: "\u25CB",
|
|
888
932
|
pointer: "\u203A"
|
|
889
933
|
};
|
|
934
|
+
var PROGRESS_BAR_CHARS = {
|
|
935
|
+
blocks: { filled: "\u2588", empty: "\u2591" },
|
|
936
|
+
line: { filled: "\u2500", empty: "\u2500" },
|
|
937
|
+
dots: { filled: "\u2022", empty: "\xB7" },
|
|
938
|
+
arrow: { filled: "\u2550", empty: "\u2500" }
|
|
939
|
+
};
|
|
940
|
+
function resolveProgressBar(config, tokens) {
|
|
941
|
+
const style = config?.style ?? "blocks";
|
|
942
|
+
const chars = PROGRESS_BAR_CHARS[style];
|
|
943
|
+
const filledHex = config?.filledColor ?? tokens?.success ?? DEFAULT_TOKENS.success;
|
|
944
|
+
const emptyHex = config?.emptyColor ?? tokens?.muted ?? DEFAULT_TOKENS.muted;
|
|
945
|
+
return {
|
|
946
|
+
width: config?.width ?? 20,
|
|
947
|
+
filledColor: chalk.hex(filledHex),
|
|
948
|
+
emptyColor: chalk.hex(emptyHex),
|
|
949
|
+
style,
|
|
950
|
+
chars
|
|
951
|
+
};
|
|
952
|
+
}
|
|
890
953
|
function resolveTheme(themeConfig) {
|
|
891
954
|
const presetTokens = themeConfig?.preset ? THEME_PRESETS[themeConfig.preset] : void 0;
|
|
892
955
|
const tokens = { ...DEFAULT_TOKENS, ...presetTokens, ...themeConfig?.tokens };
|
|
@@ -899,9 +962,16 @@ function resolveTheme(themeConfig) {
|
|
|
899
962
|
info: chalk.hex(tokens.info),
|
|
900
963
|
muted: chalk.hex(tokens.muted),
|
|
901
964
|
accent: chalk.hex(tokens.accent),
|
|
965
|
+
highlight: chalk.hex(tokens.highlight),
|
|
966
|
+
highlightBg: chalk.bgHex(tokens.highlightBg),
|
|
967
|
+
pointer: chalk.hex(tokens.pointer),
|
|
968
|
+
checked: chalk.hex(tokens.checked),
|
|
969
|
+
dimmed: chalk.hex(tokens.dimmed),
|
|
902
970
|
bold: chalk.bold,
|
|
903
971
|
icons,
|
|
904
|
-
spinner: resolveSpinner(themeConfig?.spinner)
|
|
972
|
+
spinner: resolveSpinner(themeConfig?.spinner),
|
|
973
|
+
spinnerElapsed: themeConfig?.spinnerElapsed ?? false,
|
|
974
|
+
progressBar: resolveProgressBar(themeConfig?.progressBar, tokens)
|
|
905
975
|
};
|
|
906
976
|
}
|
|
907
977
|
|
|
@@ -919,11 +989,11 @@ import {
|
|
|
919
989
|
} from "@inquirer/prompts";
|
|
920
990
|
var InquirerRenderer = class {
|
|
921
991
|
renderStepHeader(stepIndex, totalVisible, message, theme, description) {
|
|
922
|
-
const
|
|
923
|
-
const filledCount = totalVisible > 0 ? Math.round(stepIndex / totalVisible *
|
|
924
|
-
const remainingCount =
|
|
925
|
-
const filledBar =
|
|
926
|
-
const remainingBar =
|
|
992
|
+
const pb = theme.progressBar;
|
|
993
|
+
const filledCount = totalVisible > 0 ? Math.round(stepIndex / totalVisible * pb.width) : 0;
|
|
994
|
+
const remainingCount = pb.width - filledCount;
|
|
995
|
+
const filledBar = pb.filledColor(pb.chars.filled.repeat(filledCount));
|
|
996
|
+
const remainingBar = pb.emptyColor(pb.chars.empty.repeat(remainingCount));
|
|
927
997
|
const counter = theme.muted(`Step ${String(stepIndex + 1)}/${String(totalVisible)}`);
|
|
928
998
|
const stepMessage = theme.muted(`\u2014 ${message}`);
|
|
929
999
|
console.log(`
|
|
@@ -932,6 +1002,39 @@ var InquirerRenderer = class {
|
|
|
932
1002
|
console.log(` ${theme.muted(description)}`);
|
|
933
1003
|
}
|
|
934
1004
|
}
|
|
1005
|
+
renderNote(step, theme) {
|
|
1006
|
+
const title = step.message;
|
|
1007
|
+
const body = step.description ?? "";
|
|
1008
|
+
const style = step.style ?? "info";
|
|
1009
|
+
const { colorFn, icon } = getNoteStyleConfig(style, theme);
|
|
1010
|
+
if (style === "banner") {
|
|
1011
|
+
const width = Math.max(4, (process.stdout.columns ?? 60) - 4);
|
|
1012
|
+
const rule = colorFn("\u2500".repeat(width));
|
|
1013
|
+
console.log();
|
|
1014
|
+
console.log(` ${rule}`);
|
|
1015
|
+
console.log(` ${colorFn(icon)} ${theme.bold(title)}`);
|
|
1016
|
+
if (body) console.log(` ${theme.muted(body)}`);
|
|
1017
|
+
console.log(` ${rule}`);
|
|
1018
|
+
console.log();
|
|
1019
|
+
return;
|
|
1020
|
+
}
|
|
1021
|
+
const border = colorFn("\u2502");
|
|
1022
|
+
const topRule = colorFn(`\u250C${"\u2500".repeat(2)}`);
|
|
1023
|
+
const bottomRule = colorFn(`\u2514${"\u2500".repeat(2)}`);
|
|
1024
|
+
console.log();
|
|
1025
|
+
console.log(` ${topRule} ${colorFn(icon)} ${theme.bold(title)}`);
|
|
1026
|
+
if (body) {
|
|
1027
|
+
for (const line of body.split("\n")) {
|
|
1028
|
+
if (style === "code") {
|
|
1029
|
+
console.log(` ${border} ${theme.muted(line)}`);
|
|
1030
|
+
} else {
|
|
1031
|
+
console.log(` ${border} ${line}`);
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
console.log(` ${bottomRule}`);
|
|
1036
|
+
console.log();
|
|
1037
|
+
}
|
|
935
1038
|
async renderText(step, state, theme) {
|
|
936
1039
|
const existingAnswer = state.answers[step.id];
|
|
937
1040
|
const defaultValue = typeof existingAnswer === "string" ? existingAnswer : step.default;
|
|
@@ -944,7 +1047,7 @@ var InquirerRenderer = class {
|
|
|
944
1047
|
async renderSelect(step, state, theme) {
|
|
945
1048
|
const existingAnswer = state.answers[step.id];
|
|
946
1049
|
const defaultValue = typeof existingAnswer === "string" ? existingAnswer : step.default;
|
|
947
|
-
const
|
|
1050
|
+
const rawChoices = step.options.map((opt) => {
|
|
948
1051
|
if ("separator" in opt) {
|
|
949
1052
|
return new Separator(opt.separator);
|
|
950
1053
|
}
|
|
@@ -955,19 +1058,27 @@ var InquirerRenderer = class {
|
|
|
955
1058
|
disabled: opt.disabled
|
|
956
1059
|
};
|
|
957
1060
|
});
|
|
1061
|
+
const itemChoices = rawChoices.filter((c) => !(c instanceof Separator));
|
|
1062
|
+
const columnedItems = step.columns ? applyColumns(itemChoices, step.columns) : itemChoices;
|
|
1063
|
+
let itemIdx = 0;
|
|
1064
|
+
const choices = rawChoices.map((c) => c instanceof Separator ? c : columnedItems[itemIdx++] ?? c);
|
|
958
1065
|
return select({
|
|
959
1066
|
message: step.message,
|
|
960
1067
|
choices,
|
|
961
1068
|
default: defaultValue,
|
|
962
1069
|
pageSize: step.pageSize,
|
|
963
1070
|
loop: step.loop,
|
|
964
|
-
theme: {
|
|
1071
|
+
theme: {
|
|
1072
|
+
prefix: { idle: theme.icons.pointer, done: theme.icons.stepDone },
|
|
1073
|
+
icon: { cursor: theme.icons.pointer },
|
|
1074
|
+
style: { disabled: theme.dimmed }
|
|
1075
|
+
}
|
|
965
1076
|
});
|
|
966
1077
|
}
|
|
967
1078
|
async renderMultiSelect(step, state, theme) {
|
|
968
1079
|
const existingAnswer = state.answers[step.id];
|
|
969
1080
|
const previousSelections = Array.isArray(existingAnswer) ? existingAnswer.filter((v) => typeof v === "string") : step.default;
|
|
970
|
-
const
|
|
1081
|
+
const rawChoices = step.options.map((opt) => {
|
|
971
1082
|
if ("separator" in opt) {
|
|
972
1083
|
return new Separator(opt.separator);
|
|
973
1084
|
}
|
|
@@ -978,12 +1089,20 @@ var InquirerRenderer = class {
|
|
|
978
1089
|
disabled: opt.disabled
|
|
979
1090
|
};
|
|
980
1091
|
});
|
|
1092
|
+
const itemChoices = rawChoices.filter((c) => !(c instanceof Separator));
|
|
1093
|
+
const columnedItems = step.columns ? applyColumns(itemChoices, step.columns) : itemChoices;
|
|
1094
|
+
let itemIdx = 0;
|
|
1095
|
+
const choices = rawChoices.map((c) => c instanceof Separator ? c : columnedItems[itemIdx++] ?? c);
|
|
981
1096
|
return checkbox({
|
|
982
1097
|
message: step.message,
|
|
983
1098
|
choices,
|
|
984
1099
|
pageSize: step.pageSize,
|
|
985
1100
|
loop: step.loop,
|
|
986
|
-
theme: {
|
|
1101
|
+
theme: {
|
|
1102
|
+
prefix: { idle: theme.icons.pointer, done: theme.icons.stepDone },
|
|
1103
|
+
icon: { cursor: theme.icons.pointer, checked: theme.checked("\u2714"), unchecked: theme.muted("\u25CB") },
|
|
1104
|
+
style: { disabledChoice: theme.dimmed }
|
|
1105
|
+
}
|
|
987
1106
|
});
|
|
988
1107
|
}
|
|
989
1108
|
async renderConfirm(step, state, theme) {
|
|
@@ -1015,15 +1134,17 @@ var InquirerRenderer = class {
|
|
|
1015
1134
|
return result ?? defaultValue ?? 0;
|
|
1016
1135
|
}
|
|
1017
1136
|
async renderSearch(step, _state, theme) {
|
|
1137
|
+
const message = step.placeholder ? `${step.message} ${theme.muted(`(${step.placeholder})`)}` : step.message;
|
|
1018
1138
|
return search({
|
|
1019
|
-
message
|
|
1139
|
+
message,
|
|
1020
1140
|
source: (input4) => {
|
|
1021
1141
|
const term = (input4 ?? "").toLowerCase();
|
|
1022
|
-
|
|
1142
|
+
const filtered = step.options.filter((opt) => "value" in opt).filter((opt) => !opt.disabled && opt.label.toLowerCase().includes(term)).map((opt) => ({
|
|
1023
1143
|
name: opt.label,
|
|
1024
1144
|
value: opt.value,
|
|
1025
1145
|
description: opt.hint
|
|
1026
1146
|
}));
|
|
1147
|
+
return step.columns ? applyColumns(filtered, step.columns) : filtered;
|
|
1027
1148
|
},
|
|
1028
1149
|
pageSize: step.pageSize,
|
|
1029
1150
|
theme: { prefix: { idle: theme.icons.pointer, done: theme.icons.stepDone } }
|
|
@@ -1091,6 +1212,42 @@ ${theme.muted("\u2500".repeat(40))}`);
|
|
|
1091
1212
|
process.stdout.write("\x1B[2J\x1B[0f");
|
|
1092
1213
|
}
|
|
1093
1214
|
};
|
|
1215
|
+
var NOTE_STYLE_MAP = {
|
|
1216
|
+
info: { tokenKey: "info", icon: "\u2139" },
|
|
1217
|
+
warning: { tokenKey: "warning", icon: "\u26A0" },
|
|
1218
|
+
error: { tokenKey: "error", icon: "\u2716" },
|
|
1219
|
+
success: { tokenKey: "success", icon: "\u2714" },
|
|
1220
|
+
code: { tokenKey: "muted", icon: "\u276F" },
|
|
1221
|
+
banner: { tokenKey: "primary", icon: "\u2605" }
|
|
1222
|
+
};
|
|
1223
|
+
function getNoteStyleConfig(style, theme) {
|
|
1224
|
+
const entry = NOTE_STYLE_MAP[style];
|
|
1225
|
+
return {
|
|
1226
|
+
colorFn: theme[entry.tokenKey],
|
|
1227
|
+
icon: entry.icon
|
|
1228
|
+
};
|
|
1229
|
+
}
|
|
1230
|
+
function applyColumns(items, columns) {
|
|
1231
|
+
const cols = Math.max(1, Math.floor(columns));
|
|
1232
|
+
if (cols <= 1) return items;
|
|
1233
|
+
if (items.length === 0) return items;
|
|
1234
|
+
const maxLabel = Math.max(...items.map((i) => i.name.length));
|
|
1235
|
+
const colWidth = maxLabel + 4;
|
|
1236
|
+
const grouped = [];
|
|
1237
|
+
for (let i = 0; i < items.length; i += cols) {
|
|
1238
|
+
grouped.push(items.slice(i, i + cols));
|
|
1239
|
+
}
|
|
1240
|
+
const result = [];
|
|
1241
|
+
for (const row of grouped) {
|
|
1242
|
+
for (let c = 0; c < row.length; c++) {
|
|
1243
|
+
const item = row[c];
|
|
1244
|
+
const isLast = c === row.length - 1;
|
|
1245
|
+
const paddedName = isLast ? item.name : item.name.padEnd(colWidth);
|
|
1246
|
+
result.push({ ...item, name: paddedName });
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
return result;
|
|
1250
|
+
}
|
|
1094
1251
|
|
|
1095
1252
|
// src/resolve.ts
|
|
1096
1253
|
function resolveEnvDefault(value) {
|
|
@@ -1145,19 +1302,71 @@ function renderBanner(name, theme, options) {
|
|
|
1145
1302
|
const icon = options?.icon;
|
|
1146
1303
|
const prefix = icon ? `${icon} ` : "";
|
|
1147
1304
|
if (options?.plain) {
|
|
1148
|
-
|
|
1305
|
+
const line = ` ${prefix}${theme.bold(name)}`;
|
|
1306
|
+
return options?.subtitle ? `${line}
|
|
1307
|
+
${theme.muted(options.subtitle)}` : line;
|
|
1308
|
+
}
|
|
1309
|
+
if (options?.banner) {
|
|
1310
|
+
const bannerStr = (typeof options.banner === "function" ? options.banner(theme) : options.banner).replace(/\n$/, "");
|
|
1311
|
+
const raw = bannerStr.split("\n");
|
|
1312
|
+
const mid = Math.floor(raw.length / 2);
|
|
1313
|
+
const bannerLines = raw.map((l, i) => {
|
|
1314
|
+
if (icon && i === mid) return ` ${icon} ${l}`;
|
|
1315
|
+
return icon ? ` ${l}` : ` ${l}`;
|
|
1316
|
+
}).join("\n");
|
|
1317
|
+
const colored = typeof options.banner === "function" ? bannerLines : GRIMOIRE_GRADIENT(bannerLines);
|
|
1318
|
+
return options?.subtitle ? `${colored}
|
|
1319
|
+
${theme.muted(options.subtitle)}` : colored;
|
|
1149
1320
|
}
|
|
1150
1321
|
try {
|
|
1151
|
-
const
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1322
|
+
const requestedFont = options?.font ?? "Small";
|
|
1323
|
+
let art;
|
|
1324
|
+
try {
|
|
1325
|
+
art = figlet.textSync(name, { font: requestedFont, horizontalLayout: "default" });
|
|
1326
|
+
} catch {
|
|
1327
|
+
art = figlet.textSync(name, { font: "Small", horizontalLayout: "default" });
|
|
1328
|
+
}
|
|
1329
|
+
const artLines = art.split("\n");
|
|
1330
|
+
const lines = artLines.map((line, i) => {
|
|
1331
|
+
const iconStr = getIconForLine(icon, options?.iconSize, i, artLines.length);
|
|
1332
|
+
if (iconStr !== void 0) {
|
|
1333
|
+
return ` ${iconStr}${line}`;
|
|
1334
|
+
}
|
|
1335
|
+
return icon ? ` ${line}` : ` ${line}`;
|
|
1336
|
+
}).join("\n");
|
|
1337
|
+
const colored = GRIMOIRE_GRADIENT(lines);
|
|
1338
|
+
return options?.subtitle ? `${colored}
|
|
1339
|
+
${theme.muted(options.subtitle)}` : colored;
|
|
1159
1340
|
} catch {
|
|
1160
|
-
|
|
1341
|
+
const line = ` ${prefix}${theme.bold(name)}`;
|
|
1342
|
+
return options?.subtitle ? `${line}
|
|
1343
|
+
${theme.muted(options.subtitle)}` : line;
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
function getIconForLine(icon, iconSize, lineIndex, totalLines) {
|
|
1347
|
+
if (!icon) return void 0;
|
|
1348
|
+
const mid = Math.floor(totalLines / 2);
|
|
1349
|
+
const span = resolveIconSpan(iconSize, totalLines);
|
|
1350
|
+
const halfSpan = Math.floor(span / 2);
|
|
1351
|
+
const start = Math.max(0, mid - halfSpan);
|
|
1352
|
+
const end = Math.min(totalLines - 1, start + span - 1);
|
|
1353
|
+
if (lineIndex >= start && lineIndex <= end) {
|
|
1354
|
+
return lineIndex === mid ? `${icon} ` : ` `;
|
|
1355
|
+
}
|
|
1356
|
+
return void 0;
|
|
1357
|
+
}
|
|
1358
|
+
function resolveIconSpan(iconSize, totalLines) {
|
|
1359
|
+
if (typeof iconSize === "number") {
|
|
1360
|
+
const clamped = Math.max(1, Math.floor(isFinite(iconSize) ? iconSize : 1));
|
|
1361
|
+
return Math.min(clamped, totalLines);
|
|
1362
|
+
}
|
|
1363
|
+
switch (iconSize ?? "small") {
|
|
1364
|
+
case "small":
|
|
1365
|
+
return 1;
|
|
1366
|
+
case "medium":
|
|
1367
|
+
return 3;
|
|
1368
|
+
case "large":
|
|
1369
|
+
return totalLines;
|
|
1161
1370
|
}
|
|
1162
1371
|
}
|
|
1163
1372
|
|
|
@@ -1387,7 +1596,14 @@ function emitEvent(renderer, event, theme) {
|
|
|
1387
1596
|
renderer.onEvent(event, theme);
|
|
1388
1597
|
}
|
|
1389
1598
|
}
|
|
1390
|
-
function runPreFlightChecks(checks, theme, renderer) {
|
|
1599
|
+
function runPreFlightChecks(checks, theme, renderer, checksStyle) {
|
|
1600
|
+
if (checksStyle === "tasklist") {
|
|
1601
|
+
runPreFlightChecksTasklist(checks, theme, renderer);
|
|
1602
|
+
} else {
|
|
1603
|
+
runPreFlightChecksSpinner(checks, theme, renderer);
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
function runPreFlightChecksSpinner(checks, theme, renderer) {
|
|
1391
1607
|
if (renderer) emitEvent(renderer, { type: "checks:start", checks }, theme);
|
|
1392
1608
|
for (const check of checks) {
|
|
1393
1609
|
if (renderer) emitEvent(renderer, { type: "spinner:start", message: check.name }, theme);
|
|
@@ -1405,20 +1621,69 @@ function runPreFlightChecks(checks, theme, renderer) {
|
|
|
1405
1621
|
}
|
|
1406
1622
|
console.log();
|
|
1407
1623
|
}
|
|
1624
|
+
function runPreFlightChecksTasklist(checks, theme, renderer) {
|
|
1625
|
+
if (!process.stdout.isTTY) {
|
|
1626
|
+
runPreFlightChecksSpinner(checks, theme, renderer);
|
|
1627
|
+
return;
|
|
1628
|
+
}
|
|
1629
|
+
if (renderer) emitEvent(renderer, { type: "checks:start", checks, mode: "tasklist" }, theme);
|
|
1630
|
+
const statuses = checks.map(() => "pending");
|
|
1631
|
+
const icons = theme.icons;
|
|
1632
|
+
function renderTasklist() {
|
|
1633
|
+
process.stdout.write(`\x1B[${checks.length}A\x1B[0G`);
|
|
1634
|
+
for (let i = 0; i < checks.length; i++) {
|
|
1635
|
+
const check = checks[i];
|
|
1636
|
+
const s = statuses[i];
|
|
1637
|
+
let icon;
|
|
1638
|
+
if (s === "pass") icon = theme.success(icons.stepDone);
|
|
1639
|
+
else if (s === "fail") icon = theme.error("\u2717");
|
|
1640
|
+
else if (s === "running") icon = theme.info("\u280B");
|
|
1641
|
+
else icon = theme.muted(icons.stepPending);
|
|
1642
|
+
process.stdout.write(` ${icon} ${check.name}\x1B[K
|
|
1643
|
+
`);
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
for (let i = 0; i < checks.length; i++) {
|
|
1647
|
+
process.stdout.write(` ${theme.muted(icons.stepPending)} ${checks[i].name}
|
|
1648
|
+
`);
|
|
1649
|
+
}
|
|
1650
|
+
let failedCheck;
|
|
1651
|
+
for (let i = 0; i < checks.length; i++) {
|
|
1652
|
+
const check = checks[i];
|
|
1653
|
+
statuses[i] = "running";
|
|
1654
|
+
renderTasklist();
|
|
1655
|
+
try {
|
|
1656
|
+
execSync(check.run, { stdio: "pipe" });
|
|
1657
|
+
statuses[i] = "pass";
|
|
1658
|
+
renderTasklist();
|
|
1659
|
+
if (renderer) emitEvent(renderer, { type: "check:pass", name: check.name }, theme);
|
|
1660
|
+
} catch {
|
|
1661
|
+
statuses[i] = "fail";
|
|
1662
|
+
renderTasklist();
|
|
1663
|
+
failedCheck = check;
|
|
1664
|
+
if (renderer) emitEvent(renderer, { type: "check:fail", name: check.name, message: check.message }, theme);
|
|
1665
|
+
break;
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
renderTasklist();
|
|
1669
|
+
console.log();
|
|
1670
|
+
if (failedCheck) {
|
|
1671
|
+
throw new Error(`Pre-flight check failed: ${failedCheck.name} \u2014 ${failedCheck.message}`);
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
var MOCK_MISS = /* @__PURE__ */ Symbol("mock-miss");
|
|
1408
1675
|
function getMockValue(step, mockAnswers) {
|
|
1409
1676
|
if (step.id in mockAnswers) {
|
|
1410
1677
|
return mockAnswers[step.id];
|
|
1411
1678
|
}
|
|
1412
|
-
if (step.type === "message" || step.type === "note") {
|
|
1679
|
+
if (step.type === "message" || step.type === "note" || step.type === "browser") {
|
|
1413
1680
|
return true;
|
|
1414
1681
|
}
|
|
1415
1682
|
const defaultValue = getStepDefault(step);
|
|
1416
1683
|
if (defaultValue !== void 0) {
|
|
1417
1684
|
return defaultValue;
|
|
1418
1685
|
}
|
|
1419
|
-
|
|
1420
|
-
`Mock mode: no answer provided for step "${step.id}" and no default available`
|
|
1421
|
-
);
|
|
1686
|
+
return MOCK_MISS;
|
|
1422
1687
|
}
|
|
1423
1688
|
function getStepDefault(step) {
|
|
1424
1689
|
switch (step.type) {
|
|
@@ -1438,6 +1703,7 @@ function getStepDefault(step) {
|
|
|
1438
1703
|
case "password":
|
|
1439
1704
|
case "message":
|
|
1440
1705
|
case "note":
|
|
1706
|
+
case "browser":
|
|
1441
1707
|
return void 0;
|
|
1442
1708
|
}
|
|
1443
1709
|
}
|
|
@@ -1473,9 +1739,34 @@ async function runWizard(config, options) {
|
|
|
1473
1739
|
registerPlugin(plugin);
|
|
1474
1740
|
}
|
|
1475
1741
|
}
|
|
1742
|
+
let cancelFired = false;
|
|
1743
|
+
const performCancel = async () => {
|
|
1744
|
+
if (cancelFired) return;
|
|
1745
|
+
cancelFired = true;
|
|
1746
|
+
state = wizardReducer(state, { type: "CANCEL" }, config);
|
|
1747
|
+
const passwordStepIds = config.steps.filter((s) => s.type === "password").map((s) => s.id);
|
|
1748
|
+
saveProgress(config.meta.name, {
|
|
1749
|
+
currentStepId: state.currentStepId,
|
|
1750
|
+
answers: state.answers,
|
|
1751
|
+
history: state.history
|
|
1752
|
+
}, void 0, passwordStepIds);
|
|
1753
|
+
try {
|
|
1754
|
+
await options?.onCancel?.(state);
|
|
1755
|
+
} catch {
|
|
1756
|
+
}
|
|
1757
|
+
emitEvent(renderer, { type: "session:end", answers: state.answers, cancelled: true }, theme);
|
|
1758
|
+
if (!quiet) console.log(theme.warning("\n Wizard cancelled.\n"));
|
|
1759
|
+
};
|
|
1760
|
+
const signalHandler = !isMock ? () => {
|
|
1761
|
+
performCancel().finally(() => process.exit(130));
|
|
1762
|
+
} : void 0;
|
|
1763
|
+
if (signalHandler) {
|
|
1764
|
+
process.once("SIGINT", signalHandler);
|
|
1765
|
+
process.once("SIGTERM", signalHandler);
|
|
1766
|
+
}
|
|
1476
1767
|
try {
|
|
1477
1768
|
if (!isMock && config.checks && config.checks.length > 0) {
|
|
1478
|
-
runPreFlightChecks(config.checks, theme, renderer);
|
|
1769
|
+
runPreFlightChecks(config.checks, theme, renderer, config.meta.checksStyle);
|
|
1479
1770
|
}
|
|
1480
1771
|
if (!quiet) {
|
|
1481
1772
|
printWizardHeader(config, theme, options?.plain);
|
|
@@ -1485,7 +1776,62 @@ async function runWizard(config, options) {
|
|
|
1485
1776
|
let needsReview = true;
|
|
1486
1777
|
while (needsReview) {
|
|
1487
1778
|
let previousGroup;
|
|
1779
|
+
let stepsCompleted = 0;
|
|
1488
1780
|
while (state.status === "running") {
|
|
1781
|
+
if (!isMock && config.meta.clearBetweenSteps && stepsCompleted > 0) {
|
|
1782
|
+
renderer.clear();
|
|
1783
|
+
if (!quiet) {
|
|
1784
|
+
printWizardHeader(config, theme, options?.plain);
|
|
1785
|
+
}
|
|
1786
|
+
previousGroup = void 0;
|
|
1787
|
+
}
|
|
1788
|
+
let nextStepOverride;
|
|
1789
|
+
const createHookContext = (stateOverride) => ({
|
|
1790
|
+
answers: { ...(stateOverride ?? state).answers },
|
|
1791
|
+
state: stateOverride ?? state,
|
|
1792
|
+
showNote: (title, body) => {
|
|
1793
|
+
emitEvent(renderer, { type: "note", title, body }, theme);
|
|
1794
|
+
if (!("onEvent" in renderer) && process.stdout.isTTY) {
|
|
1795
|
+
console.log(`
|
|
1796
|
+
${theme.bold(title)}`);
|
|
1797
|
+
if (body) console.log(` ${theme.muted(body)}`);
|
|
1798
|
+
console.log();
|
|
1799
|
+
}
|
|
1800
|
+
},
|
|
1801
|
+
setNextStep: (stepId) => {
|
|
1802
|
+
nextStepOverride = stepId;
|
|
1803
|
+
},
|
|
1804
|
+
openBrowser: async (url) => {
|
|
1805
|
+
if (isMock) return;
|
|
1806
|
+
try {
|
|
1807
|
+
new URL(url);
|
|
1808
|
+
} catch {
|
|
1809
|
+
return;
|
|
1810
|
+
}
|
|
1811
|
+
if (process.platform === "darwin") {
|
|
1812
|
+
execFileSync("open", [url], { stdio: "ignore" });
|
|
1813
|
+
} else if (process.platform === "win32") {
|
|
1814
|
+
execFileSync("powershell", ["-NoProfile", "-Command", `Start-Process '${url.replace(/'/g, "''")}'`], { stdio: "ignore" });
|
|
1815
|
+
} else {
|
|
1816
|
+
execFileSync("xdg-open", [url], { stdio: "ignore" });
|
|
1817
|
+
}
|
|
1818
|
+
},
|
|
1819
|
+
prompt: async (promptConfig) => {
|
|
1820
|
+
if (isMock) {
|
|
1821
|
+
if (promptConfig.default !== void 0) return promptConfig.default;
|
|
1822
|
+
throw new Error("Mock mode: context.prompt() requires a default value");
|
|
1823
|
+
}
|
|
1824
|
+
const contextState = stateOverride ?? state;
|
|
1825
|
+
const tempStep = {
|
|
1826
|
+
id: "__hook_prompt__",
|
|
1827
|
+
message: promptConfig.message,
|
|
1828
|
+
type: promptConfig.type,
|
|
1829
|
+
...promptConfig.type === "select" ? { options: promptConfig.options } : {},
|
|
1830
|
+
default: promptConfig.default
|
|
1831
|
+
};
|
|
1832
|
+
return renderStep(renderer, tempStep, contextState, theme);
|
|
1833
|
+
}
|
|
1834
|
+
});
|
|
1489
1835
|
const visibleSteps = getVisibleSteps(config, state.answers);
|
|
1490
1836
|
const currentStep = config.steps.find((s) => s.id === state.currentStepId);
|
|
1491
1837
|
if (!currentStep) {
|
|
@@ -1508,9 +1854,13 @@ async function runWizard(config, options) {
|
|
|
1508
1854
|
emitEvent(renderer, { type: "step:start", stepId: currentStep.id, stepIndex, totalVisible: visibleSteps.length, step: currentStep }, theme);
|
|
1509
1855
|
if (currentStep.type === "note") {
|
|
1510
1856
|
emitEvent(renderer, { type: "note", title: resolvedMessage, body: resolvedDescription ?? "" }, theme);
|
|
1857
|
+
if (!isMock && "style" in currentStep && currentStep.style && "renderNote" in renderer && !("onEvent" in renderer)) {
|
|
1858
|
+
const resolvedStep2 = { ...currentStep, message: resolvedMessage, description: resolvedDescription };
|
|
1859
|
+
renderer.renderNote(resolvedStep2, theme);
|
|
1860
|
+
}
|
|
1511
1861
|
}
|
|
1512
1862
|
if (options?.onBeforeStep) {
|
|
1513
|
-
await options.onBeforeStep(currentStep.id, currentStep,
|
|
1863
|
+
await options.onBeforeStep(currentStep.id, currentStep, createHookContext());
|
|
1514
1864
|
}
|
|
1515
1865
|
const pluginStep = getPluginStep(currentStep.type);
|
|
1516
1866
|
const resolvedStep = pluginStep ? currentStep : resolveStepDefaults(currentStep, cachedAnswers);
|
|
@@ -1527,7 +1877,19 @@ async function runWizard(config, options) {
|
|
|
1527
1877
|
}
|
|
1528
1878
|
}
|
|
1529
1879
|
try {
|
|
1530
|
-
|
|
1880
|
+
let value;
|
|
1881
|
+
if (isMock) {
|
|
1882
|
+
const mockResult = getMockValue(finalStep, mockAnswers);
|
|
1883
|
+
if (mockResult === MOCK_MISS) {
|
|
1884
|
+
throw new Error(
|
|
1885
|
+
`Mock mode: no answer provided for step "${finalStep.id}" and no default available`
|
|
1886
|
+
);
|
|
1887
|
+
} else {
|
|
1888
|
+
value = mockResult;
|
|
1889
|
+
}
|
|
1890
|
+
} else {
|
|
1891
|
+
value = pluginStep ? await pluginStep.render(toStepRecord(finalStep), state, theme) : await renderStep(renderer, finalStep, state, theme);
|
|
1892
|
+
}
|
|
1531
1893
|
if (pluginStep?.validate) {
|
|
1532
1894
|
const pluginError = pluginStep.validate(value, toStepRecord(templatedStep));
|
|
1533
1895
|
if (pluginError) {
|
|
@@ -1568,28 +1930,31 @@ async function runWizard(config, options) {
|
|
|
1568
1930
|
}
|
|
1569
1931
|
}
|
|
1570
1932
|
if (options?.onAfterStep) {
|
|
1571
|
-
await options.onAfterStep(currentStep.id, value, nextState);
|
|
1933
|
+
await options.onAfterStep(currentStep.id, value, createHookContext(nextState));
|
|
1934
|
+
}
|
|
1935
|
+
if (nextStepOverride) {
|
|
1936
|
+
if (nextStepOverride === "__done__") {
|
|
1937
|
+
state = { ...nextState, status: "done" };
|
|
1938
|
+
} else {
|
|
1939
|
+
const targetExists = config.steps.some((s) => s.id === nextStepOverride);
|
|
1940
|
+
if (!targetExists) {
|
|
1941
|
+
throw new Error(`setNextStep: step "${nextStepOverride}" does not exist`);
|
|
1942
|
+
}
|
|
1943
|
+
state = { ...nextState, status: "running", currentStepId: nextStepOverride };
|
|
1944
|
+
}
|
|
1945
|
+
nextStepOverride = void 0;
|
|
1946
|
+
} else {
|
|
1947
|
+
state = nextState;
|
|
1572
1948
|
}
|
|
1573
|
-
state = nextState;
|
|
1574
1949
|
emitEvent(renderer, { type: "step:complete", stepId: currentStep.id, value, step: currentStep }, theme);
|
|
1575
1950
|
if (mruEnabled && isSelectLikeStep(currentStep.type)) {
|
|
1576
1951
|
recordSelection(config.meta.name, currentStep.id, value);
|
|
1577
1952
|
}
|
|
1578
1953
|
options?.onStepComplete?.(currentStep.id, value, state);
|
|
1954
|
+
stepsCompleted++;
|
|
1579
1955
|
} catch (error) {
|
|
1580
1956
|
if (!isMock && isUserCancel(error)) {
|
|
1581
|
-
|
|
1582
|
-
options?.onCancel?.(state);
|
|
1583
|
-
const passwordStepIds = config.steps.filter((s) => s.type === "password").map((s) => s.id);
|
|
1584
|
-
saveProgress(config.meta.name, {
|
|
1585
|
-
currentStepId: state.currentStepId,
|
|
1586
|
-
answers: state.answers,
|
|
1587
|
-
history: state.history
|
|
1588
|
-
}, void 0, passwordStepIds);
|
|
1589
|
-
emitEvent(renderer, { type: "session:end", answers: state.answers, cancelled: true }, theme);
|
|
1590
|
-
if (!quiet) {
|
|
1591
|
-
console.log(theme.warning("\n Wizard cancelled.\n"));
|
|
1592
|
-
}
|
|
1957
|
+
await performCancel();
|
|
1593
1958
|
return state.answers;
|
|
1594
1959
|
}
|
|
1595
1960
|
throw error;
|
|
@@ -1598,10 +1963,14 @@ async function runWizard(config, options) {
|
|
|
1598
1963
|
if (config.meta.review && !isMock && state.status === "done") {
|
|
1599
1964
|
const reviewLines = [];
|
|
1600
1965
|
for (const step of config.steps) {
|
|
1966
|
+
if (step.review?.hide) continue;
|
|
1967
|
+
if (step.type === "note" || step.type === "message") continue;
|
|
1601
1968
|
const answer = state.answers[step.id];
|
|
1602
1969
|
if (answer === void 0) continue;
|
|
1603
|
-
const
|
|
1604
|
-
|
|
1970
|
+
const label = step.review?.label ?? step.id;
|
|
1971
|
+
const raw = step.type === "password" ? "****" : Array.isArray(answer) ? answer.map(String).join(", ") : String(answer);
|
|
1972
|
+
const display = formatReviewValue(raw, step.review?.format);
|
|
1973
|
+
reviewLines.push(`${label}: ${display}`);
|
|
1605
1974
|
}
|
|
1606
1975
|
emitEvent(renderer, { type: "note", title: "Review your answers", body: reviewLines.join("\n") }, theme);
|
|
1607
1976
|
console.log(`
|
|
@@ -1621,12 +1990,16 @@ async function runWizard(config, options) {
|
|
|
1621
1990
|
} else {
|
|
1622
1991
|
const { select: selectPrompt } = await import("@inquirer/prompts");
|
|
1623
1992
|
const stepsWithAnswers = config.steps.filter(
|
|
1624
|
-
(s) => state.answers[s.id] !== void 0 && s.type !== "note" && s.type !== "message"
|
|
1993
|
+
(s) => state.answers[s.id] !== void 0 && s.type !== "note" && s.type !== "message" && !s.review?.hide
|
|
1625
1994
|
);
|
|
1995
|
+
if (stepsWithAnswers.length === 0) {
|
|
1996
|
+
needsReview = false;
|
|
1997
|
+
break;
|
|
1998
|
+
}
|
|
1626
1999
|
const stepToRevisit = await selectPrompt({
|
|
1627
2000
|
message: "Which step would you like to change?",
|
|
1628
2001
|
choices: stepsWithAnswers.map((s) => ({
|
|
1629
|
-
name: `${s.id}: ${s.type === "password" ? "****" : String(state.answers[s.id] ?? "")}`,
|
|
2002
|
+
name: `${s.review?.label ?? s.id}: ${s.type === "password" ? "****" : String(state.answers[s.id] ?? "")}`,
|
|
1630
2003
|
value: s.id
|
|
1631
2004
|
}))
|
|
1632
2005
|
});
|
|
@@ -1669,6 +2042,10 @@ async function runWizard(config, options) {
|
|
|
1669
2042
|
}
|
|
1670
2043
|
return state.answers;
|
|
1671
2044
|
} finally {
|
|
2045
|
+
if (signalHandler) {
|
|
2046
|
+
process.removeListener("SIGINT", signalHandler);
|
|
2047
|
+
process.removeListener("SIGTERM", signalHandler);
|
|
2048
|
+
}
|
|
1672
2049
|
if (userPlugins) {
|
|
1673
2050
|
clearPlugins();
|
|
1674
2051
|
}
|
|
@@ -1708,6 +2085,18 @@ function renderStep(renderer, step, state, theme) {
|
|
|
1708
2085
|
return Promise.resolve(true);
|
|
1709
2086
|
case "note":
|
|
1710
2087
|
return Promise.resolve(true);
|
|
2088
|
+
case "browser": {
|
|
2089
|
+
const resolvedUrl = resolveTemplate(step.url, state.answers);
|
|
2090
|
+
console.log(`
|
|
2091
|
+
${theme.info("\u2192")} ${step.message}`);
|
|
2092
|
+
console.log(` ${theme.muted(resolvedUrl)}`);
|
|
2093
|
+
openUrl(resolvedUrl);
|
|
2094
|
+
if (step.fallback) {
|
|
2095
|
+
console.log(` ${theme.muted(step.fallback)}`);
|
|
2096
|
+
}
|
|
2097
|
+
console.log();
|
|
2098
|
+
return Promise.resolve(true);
|
|
2099
|
+
}
|
|
1711
2100
|
}
|
|
1712
2101
|
}
|
|
1713
2102
|
function resolveStepDefaults(step, cachedAnswers) {
|
|
@@ -1759,6 +2148,7 @@ function resolveStepDefaults(step, cachedAnswers) {
|
|
|
1759
2148
|
case "password":
|
|
1760
2149
|
case "message":
|
|
1761
2150
|
case "note":
|
|
2151
|
+
case "browser":
|
|
1762
2152
|
return step;
|
|
1763
2153
|
}
|
|
1764
2154
|
}
|
|
@@ -1768,7 +2158,7 @@ function getCachedDefault(stepId, cachedAnswers) {
|
|
|
1768
2158
|
}
|
|
1769
2159
|
function applyTemplateDefaults(step, templateAnswers) {
|
|
1770
2160
|
if (!(step.id in templateAnswers)) return step;
|
|
1771
|
-
if (step.type === "password" || step.type === "message" || step.type === "note") return step;
|
|
2161
|
+
if (step.type === "password" || step.type === "message" || step.type === "note" || step.type === "browser") return step;
|
|
1772
2162
|
const value = templateAnswers[step.id];
|
|
1773
2163
|
switch (step.type) {
|
|
1774
2164
|
case "text":
|
|
@@ -1858,6 +2248,7 @@ function resolveStepTemplates(step, answers) {
|
|
|
1858
2248
|
case "toggle":
|
|
1859
2249
|
case "message":
|
|
1860
2250
|
case "note":
|
|
2251
|
+
case "browser":
|
|
1861
2252
|
return {
|
|
1862
2253
|
...step,
|
|
1863
2254
|
description: step.description ? resolveTemplate(step.description, answers) : void 0
|
|
@@ -1915,18 +2306,50 @@ async function executeActions(actions, answers, theme, renderer) {
|
|
|
1915
2306
|
}
|
|
1916
2307
|
function printWizardHeader(config, theme, plain) {
|
|
1917
2308
|
console.log();
|
|
1918
|
-
console.log(renderBanner(config.meta.name, theme, {
|
|
2309
|
+
console.log(renderBanner(config.meta.name, theme, {
|
|
2310
|
+
plain,
|
|
2311
|
+
icon: config.meta.icon,
|
|
2312
|
+
iconSize: config.meta.iconSize,
|
|
2313
|
+
font: config.meta.font,
|
|
2314
|
+
banner: config.meta.banner,
|
|
2315
|
+
subtitle: config.meta.subtitle
|
|
2316
|
+
}));
|
|
1919
2317
|
if (config.meta.description) {
|
|
1920
2318
|
console.log(` ${theme.muted(config.meta.description)}`);
|
|
1921
2319
|
}
|
|
1922
2320
|
console.log();
|
|
1923
2321
|
}
|
|
2322
|
+
function openUrl(url) {
|
|
2323
|
+
try {
|
|
2324
|
+
if (process.platform === "darwin") {
|
|
2325
|
+
execFileSync("open", [url], { stdio: "ignore" });
|
|
2326
|
+
} else if (process.platform === "win32") {
|
|
2327
|
+
const safeUrl = url.replace(/'/g, "''");
|
|
2328
|
+
execFileSync("powershell", ["-NoProfile", "-Command", `Start-Process '${safeUrl}'`], { stdio: "ignore" });
|
|
2329
|
+
} else {
|
|
2330
|
+
execFileSync("xdg-open", [url], { stdio: "ignore" });
|
|
2331
|
+
}
|
|
2332
|
+
} catch {
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
1924
2335
|
function isUserCancel(error) {
|
|
1925
2336
|
if (error instanceof Error) {
|
|
1926
2337
|
return error.message.includes("User force closed") || error.name === "ExitPromptError";
|
|
1927
2338
|
}
|
|
1928
2339
|
return false;
|
|
1929
2340
|
}
|
|
2341
|
+
function formatReviewValue(value, format) {
|
|
2342
|
+
switch (format) {
|
|
2343
|
+
case "uppercase":
|
|
2344
|
+
return value.toUpperCase();
|
|
2345
|
+
case "lowercase":
|
|
2346
|
+
return value.toLowerCase();
|
|
2347
|
+
case "capitalize":
|
|
2348
|
+
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
2349
|
+
default:
|
|
2350
|
+
return value;
|
|
2351
|
+
}
|
|
2352
|
+
}
|
|
1930
2353
|
|
|
1931
2354
|
// src/scaffolder.ts
|
|
1932
2355
|
import { input as input2, select as select2, confirm as confirm2, number as number2 } from "@inquirer/prompts";
|
|
@@ -2396,8 +2819,9 @@ var InkRenderer = class {
|
|
|
2396
2819
|
return result ?? defaultValue ?? 0;
|
|
2397
2820
|
}
|
|
2398
2821
|
async renderSearch(step, _state, theme) {
|
|
2822
|
+
const message = step.placeholder ? `${step.message} ${theme.muted(`(${step.placeholder})`)}` : step.message;
|
|
2399
2823
|
return search2({
|
|
2400
|
-
message
|
|
2824
|
+
message,
|
|
2401
2825
|
source: (term) => {
|
|
2402
2826
|
const query = (term ?? "").toLowerCase();
|
|
2403
2827
|
return step.options.filter((opt) => "value" in opt).filter((opt) => !opt.disabled && opt.label.toLowerCase().includes(query)).map((opt) => ({
|
|
@@ -2501,6 +2925,8 @@ var S_BAR_H = u("\u2500", "-");
|
|
|
2501
2925
|
var ClackRenderer = class extends InquirerRenderer {
|
|
2502
2926
|
spinnerInterval;
|
|
2503
2927
|
spinnerFrameIndex = 0;
|
|
2928
|
+
spinnerStartTime = 0;
|
|
2929
|
+
checksMode = void 0;
|
|
2504
2930
|
renderStepHeader() {
|
|
2505
2931
|
}
|
|
2506
2932
|
renderGroupHeader() {
|
|
@@ -2557,18 +2983,25 @@ var ClackRenderer = class extends InquirerRenderer {
|
|
|
2557
2983
|
this.stopSpinner(event.message, theme);
|
|
2558
2984
|
break;
|
|
2559
2985
|
case "checks:start":
|
|
2560
|
-
|
|
2986
|
+
this.checksMode = event.mode === "tasklist" ? "tasklist" : "spinner";
|
|
2987
|
+
if (this.checksMode !== "tasklist") {
|
|
2988
|
+
process.stdout.write(`${chalk2.gray(S_BAR)}
|
|
2561
2989
|
`);
|
|
2562
|
-
|
|
2990
|
+
process.stdout.write(`${chalk2.gray(S_BAR)} ${theme.bold("Running checks...")}
|
|
2563
2991
|
`);
|
|
2992
|
+
}
|
|
2564
2993
|
break;
|
|
2565
2994
|
case "check:pass":
|
|
2566
|
-
|
|
2995
|
+
if (this.checksMode !== "tasklist") {
|
|
2996
|
+
process.stdout.write(`${chalk2.gray(S_BAR)} ${theme.success(S_STEP_SUBMIT)} ${event.name}
|
|
2567
2997
|
`);
|
|
2998
|
+
}
|
|
2568
2999
|
break;
|
|
2569
3000
|
case "check:fail":
|
|
2570
|
-
|
|
3001
|
+
if (this.checksMode !== "tasklist") {
|
|
3002
|
+
process.stdout.write(`${chalk2.gray(S_BAR)} ${theme.error(S_STEP_ERROR)} ${event.name}: ${event.message}
|
|
2571
3003
|
`);
|
|
3004
|
+
}
|
|
2572
3005
|
break;
|
|
2573
3006
|
case "actions:start":
|
|
2574
3007
|
process.stdout.write(`${chalk2.gray(S_BAR)}
|
|
@@ -2635,11 +3068,17 @@ var ClackRenderer = class extends InquirerRenderer {
|
|
|
2635
3068
|
`);
|
|
2636
3069
|
}
|
|
2637
3070
|
startSpinner(message, theme) {
|
|
3071
|
+
if (this.spinnerInterval) {
|
|
3072
|
+
clearInterval(this.spinnerInterval);
|
|
3073
|
+
this.spinnerInterval = void 0;
|
|
3074
|
+
}
|
|
2638
3075
|
this.spinnerFrameIndex = 0;
|
|
3076
|
+
this.spinnerStartTime = Date.now();
|
|
2639
3077
|
const { frames, interval } = theme.spinner;
|
|
2640
3078
|
this.spinnerInterval = setInterval(() => {
|
|
2641
3079
|
const frame = frames[this.spinnerFrameIndex % frames.length];
|
|
2642
|
-
|
|
3080
|
+
const elapsed = theme.spinnerElapsed ? ` ${chalk2.gray(`(${((Date.now() - this.spinnerStartTime) / 1e3).toFixed(1)}s)`)}` : "";
|
|
3081
|
+
process.stdout.write(`\r${chalk2.gray(S_BAR)} ${chalk2.cyan(frame ?? "")} ${message}${elapsed}`);
|
|
2643
3082
|
this.spinnerFrameIndex++;
|
|
2644
3083
|
}, interval);
|
|
2645
3084
|
}
|
|
@@ -2649,7 +3088,7 @@ var ClackRenderer = class extends InquirerRenderer {
|
|
|
2649
3088
|
this.spinnerInterval = void 0;
|
|
2650
3089
|
}
|
|
2651
3090
|
const finalMessage = message ?? "Done";
|
|
2652
|
-
process.stdout.write(`\r${chalk2.gray(S_BAR)} ${theme.success(S_STEP_SUBMIT)} ${finalMessage}
|
|
3091
|
+
process.stdout.write(`\r${chalk2.gray(S_BAR)} ${theme.success(S_STEP_SUBMIT)} ${finalMessage}\x1B[K
|
|
2653
3092
|
`);
|
|
2654
3093
|
}
|
|
2655
3094
|
};
|