@pacaf/wizard-ux 3.0.12 → 3.0.13

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/index.html CHANGED
@@ -28,7 +28,7 @@
28
28
  }
29
29
  @keyframes spin { to { transform: rotate(360deg); } }
30
30
  </style>
31
- <script type="module" crossorigin src="/assets/index-BIqBKzCb.js"></script>
31
+ <script type="module" crossorigin src="/assets/index-DrhEBZpl.js"></script>
32
32
  </head>
33
33
  <body>
34
34
  <div id="root"><div id="boot"><div class="ring"></div></div></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pacaf/wizard-ux",
3
- "version": "3.0.12",
3
+ "version": "3.0.13",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Browser-based setup wizard for Power Apps Code Apps (parallel to @pacaf/wizard CLI).",
@@ -38,7 +38,7 @@
38
38
  "react-dom": "^19.0.0",
39
39
  "react-resizable-panels": "^2.1.7",
40
40
  "react-router-dom": "^7.1.0",
41
- "@pacaf/wizard": "3.1.4"
41
+ "@pacaf/wizard": "3.1.5"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@types/react": "^19.0.0",
@@ -16,6 +16,15 @@ function pickTargetEnvUrl(state) {
16
16
  return { target, environmentUrl: raw.replace(/\/$/, '') };
17
17
  }
18
18
 
19
+ // Append ?hideNavBar=true (or & form) so the Power Apps "purple bar" is hidden
20
+ // by default for every Code App built from this template. See issue #44 and
21
+ // 04-deployment.instructions.md ("Default play URL: ?hideNavBar=true").
22
+ function withHideNavBar(url) {
23
+ if (!url) return url;
24
+ if (/[?&]hideNavBar=/i.test(url)) return url;
25
+ return url + (url.includes('?') ? '&' : '?') + 'hideNavBar=true';
26
+ }
27
+
19
28
  function readPowerAppInfo(rootDir, state) {
20
29
  const projectDir = resolve(rootDir, String(state.PROJECT_DIR || '.'));
21
30
  const powerConfigPath = join(projectDir, 'power.config.json');
@@ -43,7 +52,7 @@ function readPowerAppInfo(rootDir, state) {
43
52
  appId,
44
53
  targetEnv: target,
45
54
  environmentUrl,
46
- launchUrl: deployedUrl,
55
+ launchUrl: withHideNavBar(deployedUrl),
47
56
  };
48
57
  }
49
58
 
@@ -40,7 +40,9 @@ function runCommand(log, command, opts = {}) {
40
40
  function runFile(log, file, args, opts = {}) {
41
41
  return new Promise((resolvePromise) => {
42
42
  log.info(`$ ${SHELL.formatCommandForLog(file, args)}`);
43
- const child = SHELL.spawnSafe(file, args, { cwd: opts.cwd || process.cwd(), stdio: ['ignore', 'pipe', 'pipe'] });
43
+ const spawnOpts = { cwd: opts.cwd || process.cwd(), stdio: ['ignore', 'pipe', 'pipe'] };
44
+ if (opts.env) spawnOpts.env = opts.env;
45
+ const child = SHELL.spawnSafe(file, args, spawnOpts);
44
46
  child.stdout.setEncoding('utf-8');
45
47
  child.stderr.setEncoding('utf-8');
46
48
  child.stdout.on('data', (chunk) => log.info(String(chunk).trimEnd()));
@@ -53,6 +55,47 @@ function runFile(log, file, args, opts = {}) {
53
55
  });
54
56
  }
55
57
 
58
+ // Build a noisy-but-reassuring env for `npm install` / `pnpm install` so the
59
+ // SSE-piped output stops looking frozen during long cold installs:
60
+ // - `npm_config_loglevel=http` is added at the CLI level (so it shows the
61
+ // `npm http fetch ...` per-package line).
62
+ // - `npm_config_progress=true` keeps progress hints on (npm still suppresses
63
+ // its TTY bar but at least won't strip extra hints).
64
+ // - `FORCE_COLOR=0` keeps ANSI escapes out of the SSE stream.
65
+ // - `CI` is unset to avoid npm dropping output thinking it is in CI.
66
+ function installEnv() {
67
+ const env = { ...process.env, npm_config_progress: 'true', FORCE_COLOR: '0' };
68
+ delete env.CI;
69
+ return env;
70
+ }
71
+
72
+ // Detect whether `pnpm` is available on PATH. pnpm prints staged progress
73
+ // (`Progress: resolved X, reused Y, downloaded Z`) even in non-TTY mode and
74
+ // is materially faster on a cold cache, so prefer it when present.
75
+ function detectPnpm() {
76
+ try {
77
+ execFileSync(process.platform === 'win32' ? 'where' : 'which', ['pnpm'], { stdio: 'ignore' });
78
+ return true;
79
+ } catch {
80
+ return false;
81
+ }
82
+ }
83
+
84
+ async function runInstall(log, { stage, label, projectDir, pnpm, mode, packages = [] }) {
85
+ log.info('');
86
+ log.info(`[${stage}] ${label} (typically 30s–3min on a cold cache)…`);
87
+ const bin = pnpm
88
+ ? toolCommand('pnpm')
89
+ : toolCommand('npm');
90
+ const baseArgs = pnpm
91
+ ? (mode === 'base' ? ['install'] : mode === 'dev' ? ['add', '-D', ...packages] : ['add', ...packages])
92
+ : (mode === 'base' ? ['install'] : mode === 'dev' ? ['install', '-D', ...packages] : ['install', ...packages]);
93
+ const noisyArgs = pnpm
94
+ ? ['--reporter=append-only', ...baseArgs]
95
+ : ['--loglevel=http', '--no-audit', '--no-fund', ...baseArgs];
96
+ return runFile(log, bin, noisyArgs, { cwd: projectDir, env: installEnv() });
97
+ }
98
+
56
99
  function toolCommand(name) {
57
100
  return process.platform === 'win32' ? `${name}.cmd` : name;
58
101
  }
@@ -233,16 +276,32 @@ export default {
233
276
  }
234
277
 
235
278
  log.info('Installing dependencies...');
236
- if (await runFile(log, toolCommand('npm'), ['install'], { cwd: projectDir })) log.ok('Base dependencies installed');
237
- else log.warn('Base dependency install reported errors; continuing to merge required packages.');
279
+ const pnpm = detectPnpm();
280
+ if (pnpm) {
281
+ log.info('Detected pnpm — using it for faster installs and shared dependency cache.');
282
+ } else {
283
+ log.info('pnpm not found — using npm. Tip: `corepack enable && corepack prepare pnpm@latest --activate` makes Step 7 noticeably faster.');
284
+ }
285
+
286
+ if (await runInstall(log, { stage: '1/3', label: 'Installing base dependencies', projectDir, pnpm, mode: 'base' })) {
287
+ log.ok('[1/3] Base dependencies installed');
288
+ } else {
289
+ log.warn('[1/3] Base dependency install reported errors; continuing to merge required packages.');
290
+ }
238
291
 
239
292
  const prodPkgs = SCAFFOLD.packageSpecs(SCAFFOLD.REQUIRED_RUNTIME_PACKAGES);
240
- if (await runFile(log, toolCommand('npm'), ['install', ...prodPkgs], { cwd: projectDir })) log.ok('Runtime packages installed');
241
- else log.warn('Some runtime packages failed to install.');
293
+ if (await runInstall(log, { stage: '2/3', label: 'Installing runtime packages (React, Fluent UI, TanStack Query, SDK)', projectDir, pnpm, mode: 'prod', packages: prodPkgs })) {
294
+ log.ok('[2/3] Runtime packages installed');
295
+ } else {
296
+ log.warn('[2/3] Some runtime packages failed to install.');
297
+ }
242
298
 
243
299
  const devPkgs = SCAFFOLD.packageSpecs(SCAFFOLD.REQUIRED_DEV_PACKAGES);
244
- if (await runFile(log, toolCommand('npm'), ['install', '-D', ...devPkgs], { cwd: projectDir })) log.ok('Dev packages installed');
245
- else log.warn('Some dev packages failed to install.');
300
+ if (await runInstall(log, { stage: '3/3', label: 'Installing dev dependencies (Vitest, ESLint, Playwright, @pacaf/scripts)', projectDir, pnpm, mode: 'dev', packages: devPkgs })) {
301
+ log.ok('[3/3] Dev packages installed');
302
+ } else {
303
+ log.warn('[3/3] Some dev packages failed to install.');
304
+ }
246
305
 
247
306
  SCAFFOLD.writeConfig(projectDir, foundationLogger);
248
307
  SCAFFOLD.mergePackageJsonScripts(projectDir, foundationLogger);
@@ -82,6 +82,16 @@ function runFileCapture(log, file, args, opts = {}) {
82
82
 
83
83
  const PAC_HTTP_ERROR_RE = /HTTP error status:\s*[45]\d\d/i;
84
84
 
85
+ // Append ?hideNavBar=true (or &hideNavBar=true if the URL already has a query
86
+ // string) so the Power Apps "purple bar" — the top chrome rendered by the
87
+ // Power Apps host around any Code App — is hidden by default. See
88
+ // .github/instructions/04-deployment.instructions.md and issue #44.
89
+ function withHideNavBar(url) {
90
+ if (!url) return url;
91
+ if (/[?&]hideNavBar=/i.test(url)) return url;
92
+ return url + (url.includes('?') ? '&' : '?') + 'hideNavBar=true';
93
+ }
94
+
85
95
  function resolveCredentialValues(state) {
86
96
  return PAC_TARGET.resolveCredentialValues({
87
97
  rootDir: PROJECT_DIR,
@@ -198,10 +208,12 @@ export default {
198
208
  // "The app was successfully published. URL: https://apps.powerapps.com/play/e/<envId>/a/<appId>"
199
209
  // Capture the first matching apps.powerapps.com/play URL and persist it
200
210
  // to wizard state so the Summary can show the real launch URL.
211
+ // Always append ?hideNavBar=true so the deployed app hides the Power
212
+ // Apps "purple bar" by default (see issue #44 and 04-deployment.instructions.md).
201
213
  const deployedUrlMatch = pushOutput.match(/https:\/\/apps\.powerapps\.com\/play\/[^\s'"<>)]+/i);
202
214
  const stateUpdate = { PROJECT_DIR: projectDir };
203
215
  if (deployedUrlMatch) {
204
- const deployedUrl = deployedUrlMatch[0].replace(/[.,;]+$/, '');
216
+ const deployedUrl = withHideNavBar(deployedUrlMatch[0].replace(/[.,;]+$/, ''));
205
217
  stateUpdate.DEPLOYED_APP_URL = deployedUrl;
206
218
  log.ok(`App URL: ${deployedUrl}`);
207
219
  } else {