Skip to content

Commit 27b8c2b

Browse files
committed
VS Code scaffold: UX polish — icon, auto-open, step progress, workspace guard
- Add $(file-code) icon to scaffold command - Auto-open first generated file; offer "Reveal in Explorer" instead of "Open File" - Show postInstructions as user-facing info message - Simplify boolean prompt: always Yes/No order with (default) marker - Guard against missing workspace with early warning - Show step progress in Quick Pick titles (e.g. "Controller (1/3)") - Add when clause to file/newFile entry to hide without workspace
1 parent 074be8b commit 27b8c2b

File tree

2 files changed

+46
-30
lines changed

2 files changed

+46
-30
lines changed

packages/b2c-vs-extension/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,7 @@
339339
{
340340
"command": "b2c-dx.scaffold.generate",
341341
"title": "New from Scaffold...",
342+
"icon": "$(file-code)",
342343
"category": "B2C DX"
343344
}
344345
],
@@ -480,7 +481,8 @@
480481
"file/newFile": [
481482
{
482483
"command": "b2c-dx.scaffold.generate",
483-
"group": "navigation"
484+
"group": "navigation",
485+
"when": "workspaceFolderCount > 0"
484486
}
485487
],
486488
"explorer/context": [

packages/b2c-vs-extension/src/scaffold/scaffold-commands.ts

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,11 @@ async function runScaffoldWizard(
6060
log: vscode.OutputChannel,
6161
builtInScaffoldsDir: string,
6262
): Promise<void> {
63-
const projectRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath ?? process.cwd();
63+
if (!vscode.workspace.workspaceFolders?.length) {
64+
vscode.window.showWarningMessage('Open a workspace folder to use scaffolds.');
65+
return;
66+
}
67+
const projectRoot = vscode.workspace.workspaceFolders[0].uri.fsPath;
6468
log.appendLine(`[Scaffold] Starting wizard, projectRoot=${projectRoot}`);
6569

6670
// Step 1: Discover and select scaffold
@@ -135,6 +139,14 @@ async function runScaffoldWizard(
135139
}
136140
}
137141

142+
// Count visible params for step progress (best-effort — conditional params may change)
143+
const visibleParams = scaffold.manifest.parameters.filter((p) => {
144+
if (resolvedVariables[p.name] !== undefined) return false;
145+
if (p.when && !evaluateCondition(p.when, resolvedVariables)) return false;
146+
return true;
147+
});
148+
149+
let stepIndex = 0;
138150
for (const param of scaffold.manifest.parameters) {
139151
// Skip if already pre-filled by context detection
140152
if (resolvedVariables[param.name] !== undefined) continue;
@@ -145,10 +157,12 @@ async function runScaffoldWizard(
145157
continue;
146158
}
147159

160+
stepIndex++;
161+
const stepTitle = `${scaffold.manifest.displayName} (${stepIndex}/${visibleParams.length})`;
148162
log.appendLine(`[Scaffold] Prompting for param: ${param.name} (type: ${param.type})`);
149163

150164
// eslint-disable-next-line no-await-in-loop
151-
const value = await promptForParameter(param, scaffold, projectRoot, configProvider, log);
165+
const value = await promptForParameter(param, scaffold, projectRoot, configProvider, log, stepTitle);
152166
if (value === undefined) {
153167
log.appendLine('[Scaffold] User cancelled');
154168
return;
@@ -227,13 +241,24 @@ async function runScaffoldWizard(
227241
return;
228242
}
229243

230-
const message = `Generated ${created.length} file(s) from ${scaffold.manifest.displayName} scaffold.`;
231-
const action = created.length > 0 ? await vscode.window.showInformationMessage(message, 'Open File') : undefined;
232-
233-
if (action === 'Open File' && created.length > 0) {
244+
if (created.length > 0) {
245+
// Open the first created file immediately
234246
const fileUri = vscode.Uri.file(created[0].absolutePath);
235247
const doc = await vscode.workspace.openTextDocument(fileUri);
236248
await vscode.window.showTextDocument(doc);
249+
250+
// Show message with Reveal action for the output directory
251+
const action = await vscode.window.showInformationMessage(
252+
`Generated ${created.length} file(s) from ${scaffold.manifest.displayName} scaffold.`,
253+
'Reveal in Explorer',
254+
);
255+
if (action === 'Reveal in Explorer') {
256+
await vscode.commands.executeCommand('revealInExplorer', fileUri);
257+
}
258+
}
259+
260+
if (result.postInstructions) {
261+
vscode.window.showInformationMessage(result.postInstructions);
237262
}
238263
}
239264

@@ -247,39 +272,40 @@ async function promptForParameter(
247272
projectRoot: string,
248273
configProvider: B2CExtensionConfig,
249274
log: vscode.OutputChannel,
275+
title?: string,
250276
): Promise<string | boolean | string[] | undefined> {
251-
const title = scaffold.manifest.displayName;
277+
const stepTitle = title ?? scaffold.manifest.displayName;
252278

253279
switch (param.type) {
254280
case 'boolean':
255-
return promptBoolean(param, title);
281+
return promptBoolean(param, stepTitle);
256282

257283
case 'choice': {
258284
const choices = await resolveChoices(param, projectRoot, configProvider, log);
259285
if (choices.length === 0) {
260286
if (param.source) {
261287
log.appendLine(`[Scaffold] No ${param.source} found, falling back to text input`);
262288
}
263-
return promptString(param, title);
289+
return promptString(param, stepTitle);
264290
}
265-
return promptChoice(param, choices, title);
291+
return promptChoice(param, choices, stepTitle);
266292
}
267293

268294
case 'multi-choice': {
269295
const choices = await resolveChoices(param, projectRoot, configProvider, log);
270296
if (choices.length === 0) return [];
271-
return promptMultiChoice(param, choices, title);
297+
return promptMultiChoice(param, choices, stepTitle);
272298
}
273299

274300
case 'string': {
275301
if (param.source) {
276302
const choices = await resolveChoices(param, projectRoot, configProvider, log);
277303
if (choices.length > 0) {
278-
return promptChoice(param, choices, title);
304+
return promptChoice(param, choices, stepTitle);
279305
}
280306
log.appendLine(`[Scaffold] No ${param.source} found, falling back to text input`);
281307
}
282-
return promptString(param, title);
308+
return promptString(param, stepTitle);
283309
}
284310

285311
default:
@@ -305,22 +331,10 @@ async function promptString(param: ScaffoldParameter, title: string): Promise<st
305331
}
306332

307333
async function promptBoolean(param: ScaffoldParameter, title: string): Promise<boolean | undefined> {
308-
const defaultIsTrue = param.default === true;
309-
const items: BooleanQuickPickItem[] = defaultIsTrue
310-
? [
311-
{label: 'Yes', description: '(default)', boolValue: true},
312-
{label: 'No', boolValue: false},
313-
]
314-
: [
315-
{label: 'No', description: param.default === false ? '(default)' : undefined, boolValue: false},
316-
{label: 'Yes', boolValue: true},
317-
];
318-
319-
// Default true → Yes first; default false or unset → No first
320-
if (!defaultIsTrue && param.default !== false) {
321-
// No default — show Yes first
322-
items.reverse();
323-
}
334+
const items: BooleanQuickPickItem[] = [
335+
{label: 'Yes', description: param.default === true ? '(default)' : undefined, boolValue: true},
336+
{label: 'No', description: param.default === false ? '(default)' : undefined, boolValue: false},
337+
];
324338

325339
const picked = await vscode.window.showQuickPick(items, {
326340
title,

0 commit comments

Comments
 (0)