create-vidra-app 0.1.4 → 0.1.6

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/cli.js CHANGED
@@ -1,11 +1,7 @@
1
- // src/cli.ts
2
- import chalk6 from "chalk";
3
-
4
1
  // src/commands/build.ts
5
2
  import path5 from "path";
6
3
  import fs4 from "fs-extra";
7
- import { execSync as execSync2 } from "child_process";
8
- import chalk4 from "chalk";
4
+ import { execSync as execSync3 } from "child_process";
9
5
 
10
6
  // src/utils.ts
11
7
  var parseArgs = (argv) => {
@@ -14,7 +10,17 @@ var parseArgs = (argv) => {
14
10
  const arg = argv[i];
15
11
  if (arg.startsWith("--")) {
16
12
  const [key, val] = arg.slice(2).split("=");
17
- args[key] = val ?? argv[++i] ?? true;
13
+ if (val !== void 0) {
14
+ args[key] = val;
15
+ continue;
16
+ }
17
+ const next = argv[i + 1];
18
+ if (next !== void 0 && !next.startsWith("--")) {
19
+ args[key] = next;
20
+ i++;
21
+ } else {
22
+ args[key] = true;
23
+ }
18
24
  } else {
19
25
  args._.push(arg);
20
26
  }
@@ -22,10 +28,92 @@ var parseArgs = (argv) => {
22
28
  return args;
23
29
  };
24
30
 
31
+ // src/exec.ts
32
+ import { execSync } from "child_process";
33
+
34
+ // src/theme.ts
35
+ import chalk from "chalk";
36
+ var CLI_VERSION = "0.1.5";
37
+ var STEP_LABEL_WIDTH = 16;
38
+ var lime = chalk.hex("#c8f751");
39
+ var value = chalk.hex("#e8e8ec");
40
+ var dim = chalk.hex("#7a7a86");
41
+ var amber = chalk.hex("#ffcf5c");
42
+ var green = chalk.hex("#86d98f");
43
+ var red = chalk.hex("#ff8585");
44
+ var slate = chalk.hex("#a9b0bd");
45
+ var uiColor = chalk.hex("#6fd3e0");
46
+ var hostColor = chalk.hex("#a48ce8");
47
+ var GLYPH_CHAR = {
48
+ done: "\u2713",
49
+ active: "\u25B8",
50
+ error: "\u2717",
51
+ manual: "?",
52
+ plan: "\u25C6",
53
+ skip: "\u2298"
54
+ };
55
+ var GLYPH_COLOR = {
56
+ done: green,
57
+ active: lime,
58
+ error: red,
59
+ manual: amber,
60
+ plan: slate,
61
+ skip: slate
62
+ };
63
+ var glyph = (name) => GLYPH_COLOR[name](GLYPH_CHAR[name]);
64
+ var TAG_COLOR = {
65
+ ui: uiColor,
66
+ host: hostColor
67
+ };
68
+ var TAG_WIDTH = 6;
69
+ var tag = (name) => TAG_COLOR[name](`[${name}]`.padEnd(TAG_WIDTH));
70
+ var blankTag = () => " ".repeat(TAG_WIDTH);
71
+ var wordmark = () => lime("vidra");
72
+ var header = (sub, context) => {
73
+ const base = ` ${wordmark()} ${value(sub)}`;
74
+ return context ? `${base} ${dim(`\u2014 ${context}`)}` : base;
75
+ };
76
+ var kv = (label, val, width = 9) => ` ${dim(label.padEnd(width))} ${value(val)}`;
77
+ var row = (opts) => {
78
+ const parts = [` ${glyph(opts.glyph)}`];
79
+ if (opts.label !== void 0) {
80
+ const padded = opts.labelWidth ? opts.label.padEnd(opts.labelWidth) : opts.label;
81
+ parts.push(value(padded));
82
+ }
83
+ if (opts.detail) parts.push(opts.detail);
84
+ return parts.join(" ");
85
+ };
86
+ var taggedRow = (glyphName, tagName, text) => ` ${glyph(glyphName)} ${tagName ? tag(tagName) : blankTag()} ${text}`;
87
+ var streamPrefix = (tagName) => ` ${dim("\xB7")} ${tag(tagName)}`;
88
+ var footer = (content) => ` ${content}`;
89
+ var fixLine = (cmd, label = "fix:") => ` ${amber(label)} ${lime(cmd)}`;
90
+ var planBadge = (text = "PLAN ONLY \xB7 BEING BUILT") => amber(`[ ${text} ]`);
91
+
92
+ // src/exec.ts
93
+ var toText = (value2) => {
94
+ if (value2 == null) return "";
95
+ return Buffer.isBuffer(value2) ? value2.toString() : value2;
96
+ };
97
+ var formatProcessError = (error) => {
98
+ const err = error;
99
+ const combined = [toText(err.stderr), toText(err.stdout)].map((s) => s.trim()).filter((s) => s.length > 0).join("\n").trim();
100
+ return combined.length > 0 ? combined : err.message ?? String(error);
101
+ };
102
+ var formatBuildError = (error) => {
103
+ const raw = formatProcessError(error);
104
+ const lines = raw.split(/\r?\n/);
105
+ const errorLines = lines.filter(
106
+ (line) => /(:\s*error\b|\berror\s+[A-Z]{2,}\d+|Build FAILED|MSB\d{4}|NETSDK\d{4})/i.test(
107
+ line
108
+ )
109
+ );
110
+ const picked = errorLines.length > 0 ? errorLines : lines.filter((line) => line.trim().length > 0).slice(-30);
111
+ return picked.join("\n").trim();
112
+ };
113
+
25
114
  // src/project.ts
26
115
  import path from "path";
27
116
  import fs from "fs-extra";
28
- import chalk from "chalk";
29
117
  var detectPlatform = () => {
30
118
  switch (process.platform) {
31
119
  case "darwin":
@@ -64,9 +152,15 @@ var detectProject = (cwd) => {
64
152
  dir = parent;
65
153
  }
66
154
  console.error(
67
- chalk.red(
68
- " Could not detect Vidra project. Run this command from your project root.\n Expected: package.json, ui/, src/<Name>.Host/<Name>.Host.csproj"
69
- )
155
+ row({
156
+ glyph: "error",
157
+ detail: dim(
158
+ "Could not detect Vidra project. Run this command from your project root."
159
+ )
160
+ })
161
+ );
162
+ console.error(
163
+ footer(dim("expected: package.json, ui/, src/<Name>.Host/<Name>.Host.csproj"))
70
164
  );
71
165
  process.exit(1);
72
166
  };
@@ -97,7 +191,7 @@ var readCsprojVersion = (csprojPath) => {
97
191
  // src/targets/macos.ts
98
192
  import path2 from "path";
99
193
  import fs2 from "fs-extra";
100
- import { execSync } from "child_process";
194
+ import { execSync as execSync2 } from "child_process";
101
195
  import os from "os";
102
196
  var macosTarget = {
103
197
  name: "macos",
@@ -123,7 +217,7 @@ var macosTarget = {
123
217
  if (fs2.existsSync(dmgPath)) {
124
218
  fs2.removeSync(dmgPath);
125
219
  }
126
- execSync(
220
+ execSync2(
127
221
  `hdiutil create -volname "${volName}" -srcfolder "${staging}" -ov -format UDZO "${dmgPath}"`,
128
222
  { stdio: "pipe" }
129
223
  );
@@ -137,41 +231,44 @@ var macosTarget = {
137
231
  // src/signing.ts
138
232
  import path3 from "path";
139
233
  import { execFileSync } from "child_process";
140
- import chalk2 from "chalk";
141
234
  var signMacAppBundleIfPossible = (appBundle, options) => {
142
235
  if (process.platform !== "darwin") return;
143
- const codesignIdentity = resolveMacCodeSigningIdentity();
144
- if (!codesignIdentity) {
145
- options.warn(
146
- chalk2.yellow(
147
- " No usable macOS signing identity found. The app will remain ad-hoc signed."
148
- )
149
- );
150
- options.warn(
151
- chalk2.yellow(
152
- " Set VIDRA_MACOS_CODESIGN_KEY to override identity selection if needed."
153
- )
154
- );
155
- return;
156
- }
236
+ const identity = resolveMacCodeSigningIdentity();
237
+ const signWith = identity ?? "-";
238
+ const label = path3.basename(appBundle);
157
239
  try {
158
240
  execFileSync(
159
241
  "codesign",
160
- ["--force", "--deep", "--sign", codesignIdentity, appBundle],
242
+ ["--force", "--deep", "--sign", signWith, appBundle],
161
243
  {
162
244
  stdio: options.verbose ? "inherit" : "pipe"
163
245
  }
164
246
  );
165
247
  options.log(
166
- ` ${chalk2.dim("Signing:")} ${chalk2.cyan(path3.basename(appBundle))} ${chalk2.dim(`with ${codesignIdentity}`)}`
248
+ row({
249
+ glyph: "done",
250
+ label: "codesign",
251
+ labelWidth: STEP_LABEL_WIDTH,
252
+ detail: identity ? `${value(label)} ${dim(`with ${identity}`)}` : `${value(label)} ${dim("ad-hoc (-)")}`
253
+ })
167
254
  );
168
255
  } catch (error) {
169
256
  options.warn(
170
- chalk2.yellow(
171
- " Failed to re-sign the macOS app bundle. It may remain ad-hoc signed."
257
+ row({
258
+ glyph: "manual",
259
+ label: "codesign",
260
+ labelWidth: STEP_LABEL_WIDTH,
261
+ detail: dim("could not sign the app bundle; it may fail to launch")
262
+ })
263
+ );
264
+ options.warn(
265
+ footer(
266
+ dim(
267
+ "install Xcode or the Command Line Tools (provides `codesign`), or set VIDRA_MACOS_CODESIGN_KEY."
268
+ )
172
269
  )
173
270
  );
174
- options.warn(chalk2.dim(formatExecError(error)));
271
+ options.warn(dim(formatExecError(error)));
175
272
  }
176
273
  };
177
274
  var resolveMacCodeSigningIdentity = () => {
@@ -188,7 +285,7 @@ var resolveMacCodeSigningIdentity = () => {
188
285
  encoding: "utf8"
189
286
  }
190
287
  );
191
- const identities = output.split(/\r?\n/).map((line) => line.match(/"([^"]+)"/)?.[1] ?? null).filter((value) => value !== null);
288
+ const identities = output.split(/\r?\n/).map((line) => line.match(/"([^"]+)"/)?.[1] ?? null).filter((value2) => value2 !== null);
192
289
  return identities.find((identity) => identity.startsWith("Apple Development:")) ?? identities.find((identity) => identity.startsWith("Developer ID Application:")) ?? null;
193
290
  } catch {
194
291
  return null;
@@ -239,7 +336,6 @@ var windowsTarget = {
239
336
  // src/doctor.ts
240
337
  import { execFileSync as execFileSync2 } from "child_process";
241
338
  import prompts from "prompts";
242
- import chalk3 from "chalk";
243
339
  var DOTNET = process.platform === "win32" ? "dotnet.exe" : "dotnet";
244
340
  var MAUI_DOCS = "https://learn.microsoft.com/dotnet/maui/get-started/installation";
245
341
  var bufToStr = (v) => v == null ? "" : Buffer.isBuffer(v) ? v.toString() : v;
@@ -272,6 +368,11 @@ var looksLikeMissingWorkload = (output) => [
272
368
  /maui-windows/i,
273
369
  /to\s+install\s+the\s+.*workload/i
274
370
  ].some((re) => re.test(output));
371
+ var looksLikeMissingXcode = (output) => [
372
+ /valid\s+Xcode\s+installation\s+was\s+not\s+found/i,
373
+ /could\s+not\s+find\s+a\s+valid\s+Xcode\s+app\s+bundle/i,
374
+ /macios-missing-xcode/i
375
+ ].some((re) => re.test(output));
275
376
  var checkDotnetSdk = () => {
276
377
  const res = run(DOTNET, ["--list-sdks"]);
277
378
  if (!res.found) {
@@ -361,19 +462,32 @@ var collectRequirements = (opts = {}) => {
361
462
  }
362
463
  return reqs;
363
464
  };
465
+ var STATUS_GLYPH = {
466
+ ok: "done",
467
+ missing: "error",
468
+ unknown: "manual"
469
+ };
364
470
  var printRequirements = (reqs) => {
471
+ const labelWidth = Math.max(0, ...reqs.map((r) => r.name.length)) + 2;
365
472
  for (const r of reqs) {
366
- const icon = r.status === "ok" ? chalk3.green("\u2713") : r.status === "missing" ? chalk3.red("\u2717") : chalk3.yellow("?");
367
- const detail = r.detail ? ` ${chalk3.dim(`(${r.detail})`)}` : "";
368
- console.log(` ${icon} ${r.name}${detail}`);
473
+ console.log(
474
+ row({
475
+ glyph: STATUS_GLYPH[r.status],
476
+ label: r.name,
477
+ labelWidth,
478
+ detail: r.detail ? dim(r.detail) : void 0
479
+ })
480
+ );
369
481
  if (r.status === "missing" && r.fix) {
370
- console.log(` ${chalk3.dim("fix:")} ${chalk3.cyan(r.fix)}`);
482
+ console.log(fixLine(r.fix));
371
483
  }
372
484
  }
373
485
  };
374
486
  var runDoctor = async () => {
375
487
  console.log();
376
- console.log(` ${chalk3.bold.cyan("vidra doctor")}`);
488
+ console.log(` ${lime("vidra")} ${value("doctor")}`);
489
+ console.log();
490
+ console.log(footer(dim("checking your environment\u2026")));
377
491
  console.log();
378
492
  const reqs = collectRequirements();
379
493
  printRequirements(reqs);
@@ -381,17 +495,22 @@ var runDoctor = async () => {
381
495
  const missing = reqs.filter((r) => r.status === "missing");
382
496
  if (missing.length === 0) {
383
497
  console.log(
384
- ` ${chalk3.green("All checks passed.")} You're ready to run ${chalk3.cyan(
385
- "vidra dev"
386
- )}.`
498
+ footer(
499
+ `${dim("all checks passed \u2014 you're ready to run")} ${lime(
500
+ "vidra dev"
501
+ )}${dim(".")}`
502
+ )
387
503
  );
388
504
  console.log();
389
505
  return 0;
390
506
  }
507
+ const n = missing.length;
391
508
  console.log(
392
- ` ${chalk3.yellow(
393
- `${missing.length} issue(s) found.`
394
- )} Apply the fixes above, then re-run ${chalk3.cyan("vidra doctor")}.`
509
+ footer(
510
+ `${dim(
511
+ `${n} issue${n === 1 ? "" : "s"} found. apply the ${n === 1 ? "fix" : "fixes"} above, then re-run`
512
+ )} ${lime("vidra doctor")}${dim(".")}`
513
+ )
395
514
  );
396
515
  console.log();
397
516
  return 1;
@@ -399,11 +518,16 @@ var runDoctor = async () => {
399
518
  var installWorkload = (csprojPath) => {
400
519
  const args = csprojPath ? ["workload", "restore", csprojPath] : ["workload", "install", "maui"];
401
520
  console.log();
402
- console.log(` ${chalk3.dim(`Running: ${DOTNET} ${args.join(" ")}`)}`);
403
521
  console.log(
404
- ` ${chalk3.dim(
405
- "This can download several hundred MB and take a few minutes."
406
- )}`
522
+ row({
523
+ glyph: "active",
524
+ detail: `${dim("running")} ${value(`${DOTNET} ${args.join(" ")}`)}`
525
+ })
526
+ );
527
+ console.log(
528
+ footer(
529
+ dim("this can download several hundred MB and take a few minutes.")
530
+ )
407
531
  );
408
532
  console.log();
409
533
  try {
@@ -411,13 +535,15 @@ var installWorkload = (csprojPath) => {
411
535
  return true;
412
536
  } catch {
413
537
  console.error();
414
- console.error(` ${chalk3.red("Workload install failed.")}`);
538
+ console.error(row({ glyph: "error", label: "workload install failed" }));
415
539
  console.error(
416
- ` ${chalk3.dim(
417
- "If this is a permissions error, your SDK is in a system location and needs elevation:"
418
- )}`
540
+ footer(
541
+ dim(
542
+ "if this is a permissions error, your SDK is in a system location and needs elevation:"
543
+ )
544
+ )
419
545
  );
420
- console.error(` ${chalk3.cyan("sudo dotnet workload install maui")}`);
546
+ console.error(fixLine("sudo dotnet workload install maui"));
421
547
  console.error();
422
548
  return false;
423
549
  }
@@ -426,9 +552,15 @@ var ensureMauiWorkload = async (opts = {}) => {
426
552
  const dotnet = checkDotnetSdk();
427
553
  if (dotnet.status === "missing") {
428
554
  console.log();
429
- console.log(` ${chalk3.yellow("!")} ${dotnet.name} \u2014 ${dotnet.detail}`);
555
+ console.log(
556
+ row({
557
+ glyph: "error",
558
+ label: dotnet.name,
559
+ detail: dotnet.detail ? dim(dotnet.detail) : void 0
560
+ })
561
+ );
430
562
  if (dotnet.fix) {
431
- console.log(` ${chalk3.dim("fix:")} ${chalk3.cyan(dotnet.fix)}`);
563
+ console.log(fixLine(dotnet.fix));
432
564
  }
433
565
  return false;
434
566
  }
@@ -436,7 +568,11 @@ var ensureMauiWorkload = async (opts = {}) => {
436
568
  if (isMauiWorkloadInstalled()) return true;
437
569
  console.log();
438
570
  console.log(
439
- ` ${chalk3.yellow("!")} The .NET MAUI workload is required but not installed.`
571
+ row({
572
+ glyph: "error",
573
+ label: ".NET MAUI workload",
574
+ detail: dim("required but not installed")
575
+ })
440
576
  );
441
577
  const interactive = opts.interactive ?? isInteractive();
442
578
  if (interactive) {
@@ -454,61 +590,91 @@ var ensureMauiWorkload = async (opts = {}) => {
454
590
  }
455
591
  if (install) {
456
592
  if (installWorkload(opts.csprojPath) && isMauiWorkloadInstalled()) {
457
- console.log(` ${chalk3.green("\u2713")} MAUI workload installed.`);
593
+ console.log(
594
+ row({
595
+ glyph: "done",
596
+ label: ".NET MAUI workload",
597
+ detail: dim("installed")
598
+ })
599
+ );
458
600
  return true;
459
601
  }
460
602
  return false;
461
603
  }
462
604
  }
463
- console.log(
464
- ` ${chalk3.dim("run:")} ${chalk3.cyan("dotnet workload install maui")}`
465
- );
466
- console.log(` ${chalk3.dim("docs:")} ${chalk3.cyan(MAUI_DOCS)}`);
605
+ console.log(fixLine("dotnet workload install maui", "run:"));
606
+ console.log(fixLine(MAUI_DOCS, "docs:"));
467
607
  return false;
468
608
  };
469
609
  var printWorkloadHint = () => {
470
610
  console.error();
471
611
  console.error(
472
- ` ${chalk3.yellow("This looks like a missing .NET MAUI workload.")}`
612
+ row({ glyph: "manual", label: "this looks like a missing .NET MAUI workload." })
473
613
  );
614
+ console.error(fixLine("dotnet workload install maui"));
615
+ console.error(fixLine("vidra doctor", "check:"));
616
+ console.error();
617
+ };
618
+ var printXcodeHint = () => {
619
+ console.error();
474
620
  console.error(
475
- ` ${chalk3.dim("fix: ")} ${chalk3.cyan("dotnet workload install maui")}`
621
+ row({
622
+ glyph: "manual",
623
+ label: "Mac Catalyst needs the full Xcode app, not just the Command Line Tools."
624
+ })
476
625
  );
626
+ console.error(` ${dim("1.")} ${value("install Xcode from the App Store")}`);
477
627
  console.error(
478
- ` ${chalk3.dim("check:")} ${chalk3.cyan("vidra doctor")}`
628
+ ` ${dim("2.")} ${lime(
629
+ "sudo xcode-select -s /Applications/Xcode.app/Contents/Developer"
630
+ )}`
479
631
  );
632
+ console.error(` ${dim("3.")} ${lime("sudo xcodebuild -runFirstLaunch")}`);
633
+ console.error(fixLine("vidra doctor", "check:"));
480
634
  console.error();
481
635
  };
482
636
 
483
637
  // src/commands/build.ts
484
- var VERSION = "0.1.0";
485
638
  var TARGETS = {
486
639
  macos: macosTarget,
487
640
  windows: windowsTarget
488
641
  };
642
+ var packageLabel = (target) => target.name === "macos" ? "package DMG" : "package MSIX";
643
+ var artifactName = (project, target) => `${project.projectName}-${project.displayVersion}-${target.name}.${target.name === "macos" ? "dmg" : "msix"}`;
489
644
  var buildCommand = async (argv) => {
490
645
  const args = parseArgs(["_", "_", ...argv]);
491
646
  const verbose = !!args["verbose"];
647
+ const plan = !!args["plan"] || !!args["dry-run"];
492
648
  const targetName = args["target"] || detectPlatform();
493
- console.log();
494
- console.log(
495
- ` ${chalk4.bold.cyan("vidra build")} ${chalk4.dim(`v${VERSION}`)}`
496
- );
497
- console.log();
498
649
  const target = TARGETS[targetName];
499
650
  if (!target) {
500
651
  const supported = Object.keys(TARGETS).join(", ");
652
+ console.error();
501
653
  console.error(
502
- chalk4.red(
503
- ` Unsupported target: ${targetName}. Supported: ${supported}`
504
- )
654
+ row({
655
+ glyph: "error",
656
+ detail: dim(`unsupported target: ${targetName} \u2014 supported: ${supported}`)
657
+ })
505
658
  );
506
659
  process.exit(1);
507
660
  }
508
661
  const project = detectProject(process.cwd());
509
- console.log(` ${chalk4.dim("Project:")} ${chalk4.cyan(project.projectName)}`);
510
- console.log(` ${chalk4.dim("Target:")} ${chalk4.cyan(target.name)} (${target.framework})`);
511
662
  console.log();
663
+ console.log(
664
+ header("build", `${target.name} \xB7 Release${plan ? " \xB7 plan" : ""}`)
665
+ );
666
+ console.log(kv("project", project.projectName));
667
+ console.log(kv("target", target.framework));
668
+ console.log();
669
+ if (plan) {
670
+ printBuildPlan(project, target);
671
+ console.log();
672
+ console.log(
673
+ footer(`${dim("nothing has run. re-run without")} ${lime("--plan")} ${dim("to apply.")}`)
674
+ );
675
+ console.log();
676
+ return;
677
+ }
512
678
  if (!await ensureMauiWorkload({ csprojPath: project.csprojPath })) {
513
679
  process.exit(1);
514
680
  }
@@ -518,13 +684,13 @@ var buildCommand = async (argv) => {
518
684
  const bundlePath = target.findBundle(publishDir, project.projectName);
519
685
  if (!bundlePath) {
520
686
  console.error(
521
- chalk4.red(` Could not find build artifact in ${publishDir}`)
687
+ row({
688
+ glyph: "error",
689
+ detail: dim(`could not find build artifact in ${publishDir}`)
690
+ })
522
691
  );
523
692
  process.exit(1);
524
693
  }
525
- console.log(
526
- ` ${chalk4.dim("Bundle:")} ${chalk4.cyan(path5.basename(bundlePath))}`
527
- );
528
694
  if (target.name === "macos") {
529
695
  signMacAppBundleIfPossible(bundlePath, {
530
696
  verbose,
@@ -532,58 +698,116 @@ var buildCommand = async (argv) => {
532
698
  warn: console.warn
533
699
  });
534
700
  }
535
- const outputDir = path5.join(project.root, "dist");
536
- fs4.ensureDirSync(outputDir);
701
+ const outputPath = await stepPackage(project, target, bundlePath);
537
702
  console.log();
538
- console.log(` ${chalk4.dim(`Packaging for ${target.name}...`)}`);
539
- const startPkg = Date.now();
540
- let outputPath;
541
- try {
542
- outputPath = await target.package(bundlePath, outputDir, {
543
- projectName: project.projectName,
544
- displayVersion: project.displayVersion
545
- });
546
- } catch (e) {
547
- const err = e;
548
- console.error(chalk4.red(` Packaging failed.`));
549
- console.error(chalk4.dim(err.stderr?.toString() || err.message));
550
- process.exit(1);
551
- }
552
- const pkgTime = ((Date.now() - startPkg) / 1e3).toFixed(1);
553
- const sizeBytes = fs4.statSync(outputPath).size;
554
- const sizeMB = (sizeBytes / (1024 * 1024)).toFixed(1);
555
703
  console.log(
556
- ` ${chalk4.green(">")} ${path5.basename(outputPath)} ${chalk4.dim(`(${sizeMB} MB, ${pkgTime}s)`)}`
704
+ footer(
705
+ `${dim("done \u2014")} ${value(path5.relative(project.root, outputPath))}`
706
+ )
557
707
  );
558
708
  console.log();
709
+ };
710
+ var printBuildPlan = (project, target) => {
559
711
  console.log(
560
- ` ${chalk4.green("Done!")} Output: ${chalk4.cyan(path5.relative(project.root, outputPath))}`
712
+ row({
713
+ glyph: "done",
714
+ label: "build UI",
715
+ labelWidth: STEP_LABEL_WIDTH,
716
+ detail: `${dim("vite \u2192")} ${value("ui/dist")}`
717
+ })
561
718
  );
562
- console.log();
719
+ console.log(
720
+ row({
721
+ glyph: "done",
722
+ label: "copy assets",
723
+ labelWidth: STEP_LABEL_WIDTH,
724
+ detail: `${dim("\u2192")} ${value("Resources/Raw/wwwroot")}`
725
+ })
726
+ );
727
+ console.log(
728
+ row({
729
+ glyph: "done",
730
+ label: "publish .NET",
731
+ labelWidth: STEP_LABEL_WIDTH,
732
+ detail: `${dim("Release \xB7")} ${value(target.framework)}`
733
+ })
734
+ );
735
+ if (target.name === "macos") {
736
+ console.log(
737
+ row({
738
+ glyph: "done",
739
+ label: "codesign .app",
740
+ labelWidth: STEP_LABEL_WIDTH,
741
+ detail: dim("Apple Development, or ad-hoc (-)")
742
+ })
743
+ );
744
+ console.log(
745
+ row({
746
+ glyph: "plan",
747
+ label: "notarize",
748
+ labelWidth: STEP_LABEL_WIDTH,
749
+ detail: planBadge()
750
+ })
751
+ );
752
+ console.log(
753
+ row({
754
+ glyph: "active",
755
+ label: "package DMG",
756
+ labelWidth: STEP_LABEL_WIDTH,
757
+ detail: `${dim("hdiutil UDZO \u2192")} ${value(artifactName(project, target))}`
758
+ })
759
+ );
760
+ } else {
761
+ console.log(
762
+ row({
763
+ glyph: "active",
764
+ label: "package MSIX",
765
+ labelWidth: STEP_LABEL_WIDTH,
766
+ detail: `${dim("\u2192")} ${value(artifactName(project, target))}`
767
+ })
768
+ );
769
+ }
563
770
  };
564
771
  var stepBuildUi = (project, verbose) => {
565
- console.log(` ${chalk4.dim("Building UI...")}`);
566
772
  const start = Date.now();
567
773
  try {
568
- execSync2("npm run build", {
774
+ execSync3("npm run build", {
569
775
  cwd: project.uiDir,
570
776
  stdio: verbose ? "inherit" : "pipe"
571
777
  });
572
778
  } catch (e) {
573
- const err = e;
574
- console.error(chalk4.red(" Vite build failed."));
575
- console.error(chalk4.dim(err.stderr?.toString() || err.message));
779
+ console.error(
780
+ row({
781
+ glyph: "error",
782
+ label: "build UI",
783
+ labelWidth: STEP_LABEL_WIDTH,
784
+ detail: dim("vite build failed")
785
+ })
786
+ );
787
+ console.error(dim(formatBuildError(e)));
576
788
  process.exit(1);
577
789
  }
578
790
  const elapsed = ((Date.now() - start) / 1e3).toFixed(1);
579
- console.log(` ${chalk4.green(">")} Vite build complete ${chalk4.dim(`(${elapsed}s)`)}`);
580
- console.log();
791
+ console.log(
792
+ row({
793
+ glyph: "done",
794
+ label: "build UI",
795
+ labelWidth: STEP_LABEL_WIDTH,
796
+ detail: `${dim("vite \u2192")} ${value("ui/dist")} ${dim(`(${elapsed}s)`)}`
797
+ })
798
+ );
581
799
  };
582
800
  var stepCopyAssets = (project) => {
583
- console.log(` ${chalk4.dim("Copying assets to host project...")}`);
584
801
  const viteDist = path5.join(project.uiDir, "dist");
585
802
  if (!fs4.existsSync(viteDist)) {
586
- console.error(chalk4.red(` ui/dist not found. Vite build may have failed.`));
803
+ console.error(
804
+ row({
805
+ glyph: "error",
806
+ label: "copy assets",
807
+ labelWidth: STEP_LABEL_WIDTH,
808
+ detail: dim("ui/dist not found \u2014 vite build may have failed")
809
+ })
810
+ );
587
811
  process.exit(1);
588
812
  }
589
813
  const wwwroot = path5.join(project.hostDir, "Resources", "Raw", "wwwroot");
@@ -591,9 +815,13 @@ var stepCopyAssets = (project) => {
591
815
  fs4.copySync(viteDist, wwwroot);
592
816
  const fileCount = countFiles(wwwroot);
593
817
  console.log(
594
- ` ${chalk4.green(">")} ${fileCount} files -> ${chalk4.dim("Resources/Raw/wwwroot/")}`
818
+ row({
819
+ glyph: "done",
820
+ label: "copy assets",
821
+ labelWidth: STEP_LABEL_WIDTH,
822
+ detail: `${dim("\u2192")} ${value("Resources/Raw/wwwroot")} ${dim(`(${fileCount} files)`)}`
823
+ })
595
824
  );
596
- console.log();
597
825
  };
598
826
  var countFiles = (dir) => {
599
827
  let count = 0;
@@ -607,13 +835,10 @@ var countFiles = (dir) => {
607
835
  return count;
608
836
  };
609
837
  var stepDotnetPublish = (project, target, verbose) => {
610
- console.log(
611
- ` ${chalk4.dim(`Publishing .NET host (${target.framework})...`)}`
612
- );
613
838
  const start = Date.now();
614
839
  const extraArgs = target.extraPublishArgs ?? "-p:CreatePackage=false";
615
840
  try {
616
- execSync2(
841
+ execSync3(
617
842
  `dotnet publish "${project.csprojPath}" -c Release -f ${target.framework} ${extraArgs}`,
618
843
  {
619
844
  cwd: project.root,
@@ -621,24 +846,67 @@ var stepDotnetPublish = (project, target, verbose) => {
621
846
  }
622
847
  );
623
848
  } catch (e) {
624
- const err = e;
625
- const output = err.stderr?.toString() || err.message;
626
- console.error(chalk4.red(" dotnet publish failed."));
627
- console.error(chalk4.dim(output));
849
+ const output = formatBuildError(e);
850
+ console.error(
851
+ row({
852
+ glyph: "error",
853
+ label: "publish .NET",
854
+ labelWidth: STEP_LABEL_WIDTH,
855
+ detail: dim("dotnet publish failed")
856
+ })
857
+ );
858
+ console.error(dim(output));
628
859
  if (looksLikeMissingWorkload(output)) printWorkloadHint();
860
+ else if (looksLikeMissingXcode(output)) printXcodeHint();
861
+ if (!verbose) {
862
+ console.error(footer(dim("re-run with --verbose for the full build log.")));
863
+ }
629
864
  process.exit(1);
630
865
  }
631
866
  const elapsed = ((Date.now() - start) / 1e3).toFixed(1);
632
867
  console.log(
633
- ` ${chalk4.green(">")} dotnet publish complete ${chalk4.dim(`(${elapsed}s)`)}`
868
+ row({
869
+ glyph: "done",
870
+ label: "publish .NET",
871
+ labelWidth: STEP_LABEL_WIDTH,
872
+ detail: `${dim("Release \xB7")} ${value(target.framework)} ${dim(`(${elapsed}s)`)}`
873
+ })
634
874
  );
635
- const publishDir = path5.join(
636
- project.hostDir,
637
- "bin",
638
- "Release",
639
- target.framework
875
+ return path5.join(project.hostDir, "bin", "Release", target.framework);
876
+ };
877
+ var stepPackage = async (project, target, bundlePath) => {
878
+ const outputDir = path5.join(project.root, "dist");
879
+ fs4.ensureDirSync(outputDir);
880
+ const start = Date.now();
881
+ let outputPath;
882
+ try {
883
+ outputPath = await target.package(bundlePath, outputDir, {
884
+ projectName: project.projectName,
885
+ displayVersion: project.displayVersion
886
+ });
887
+ } catch (e) {
888
+ console.error(
889
+ row({
890
+ glyph: "error",
891
+ label: packageLabel(target),
892
+ labelWidth: STEP_LABEL_WIDTH,
893
+ detail: dim("packaging failed")
894
+ })
895
+ );
896
+ console.error(dim(formatProcessError(e)));
897
+ process.exit(1);
898
+ }
899
+ const pkgTime = ((Date.now() - start) / 1e3).toFixed(1);
900
+ const sizeMB = (fs4.statSync(outputPath).size / (1024 * 1024)).toFixed(1);
901
+ console.log(
902
+ row({
903
+ glyph: "done",
904
+ label: packageLabel(target),
905
+ labelWidth: STEP_LABEL_WIDTH,
906
+ detail: `${value(path5.basename(outputPath))} ${dim(`(${sizeMB} MB, ${pkgTime}s)`)}`
907
+ })
640
908
  );
641
- return publishDir;
909
+ return outputPath;
642
910
  };
643
911
 
644
912
  // src/commands/dev.ts
@@ -646,8 +914,6 @@ import path6 from "path";
646
914
  import fs5 from "fs-extra";
647
915
  import { execFileSync as execFileSync3, spawn } from "child_process";
648
916
  import { request } from "http";
649
- import chalk5 from "chalk";
650
- var VERSION2 = "0.1.0";
651
917
  var POLL_INTERVAL_MS = 500;
652
918
  var POLL_TIMEOUT_MS = 3e4;
653
919
  var NPM_COMMAND = process.platform === "win32" ? "npm.cmd" : "npm";
@@ -662,7 +928,9 @@ var TARGETS2 = {
662
928
  framework: "net10.0-windows10.0.19041.0"
663
929
  }
664
930
  };
665
- var devCommand = async (argv) => {
931
+ var devCommand = (argv) => startSession(argv, { vite: true });
932
+ var runCommand = (argv) => startSession(argv, { vite: false });
933
+ var startSession = async (argv, opts) => {
666
934
  const args = parseArgs(["_", "_", ...argv]);
667
935
  const targetName = args["target"] || detectPlatform();
668
936
  const verbose = !!args["verbose"];
@@ -671,9 +939,10 @@ var devCommand = async (argv) => {
671
939
  if (!target) {
672
940
  const supported = Object.keys(TARGETS2).join(", ");
673
941
  console.error(
674
- chalk5.red(
675
- ` Unsupported target: ${targetName}. Supported: ${supported}`
676
- )
942
+ row({
943
+ glyph: "error",
944
+ detail: dim(`unsupported target: ${targetName} \u2014 supported: ${supported}`)
945
+ })
677
946
  );
678
947
  process.exit(1);
679
948
  }
@@ -682,15 +951,16 @@ var devCommand = async (argv) => {
682
951
  if (!await ensureMauiWorkload({ csprojPath: project.csprojPath })) {
683
952
  process.exit(1);
684
953
  }
685
- const session = new DevSession(project, target, viteUrl, verbose);
954
+ const session = new DevSession(project, target, viteUrl, verbose, opts);
686
955
  await session.run();
687
956
  };
688
957
  var DevSession = class {
689
- constructor(project, target, viteUrl, verbose) {
958
+ constructor(project, target, viteUrl, verbose, options = {}) {
690
959
  this.project = project;
691
960
  this.target = target;
692
961
  this.viteUrl = viteUrl;
693
962
  this.verbose = verbose;
963
+ this.vite = options.vite ?? true;
694
964
  }
695
965
  project;
696
966
  target;
@@ -698,36 +968,65 @@ var DevSession = class {
698
968
  verbose;
699
969
  children = [];
700
970
  buildConfig = process.env.VIDRA_BUILD_CONFIG || "Debug";
971
+ vite;
701
972
  shuttingDown = false;
702
973
  async run() {
703
974
  this.installSignalHandlers();
704
975
  console.log();
705
- console.log(` ${chalk5.bold.cyan("vidra dev")} ${chalk5.dim(`v${VERSION2}`)}`);
706
- console.log();
707
- console.log(` ${chalk5.dim("Project:")} ${chalk5.cyan(this.project.projectName)}`);
708
- console.log(
709
- ` ${chalk5.dim("Target:")} ${chalk5.cyan(this.target.name)} (${this.target.framework})`
710
- );
976
+ console.log(header(this.vite ? "dev" : "run", this.target.name));
977
+ console.log(kv("project", this.project.projectName));
978
+ console.log(kv("target", this.target.framework));
711
979
  console.log();
712
- const vite = this.startVite();
713
- try {
714
- await waitForServer(this.viteUrl, POLL_TIMEOUT_MS);
715
- } catch (error) {
716
- console.error(
717
- chalk5.red(
718
- ` ${error.message}`
980
+ let vite;
981
+ if (this.vite) {
982
+ vite = this.startVite();
983
+ try {
984
+ await waitForServer(this.viteUrl, POLL_TIMEOUT_MS);
985
+ } catch (error) {
986
+ console.error(row({ glyph: "error", detail: dim(error.message) }));
987
+ this.shutdown(1);
988
+ }
989
+ console.log(
990
+ taggedRow("active", "ui", `${dim("vite ready \u2014")} ${value(this.viteUrl)}`)
991
+ );
992
+ } else {
993
+ console.log(
994
+ taggedRow(
995
+ "skip",
996
+ "ui",
997
+ `${dim("vite not started \u2014")} ${value("npm run dev:ui")}`
719
998
  )
720
999
  );
721
- this.shutdown(1);
722
1000
  }
723
- console.log(` ${chalk5.dim("Vite:")} ${chalk5.cyan(this.viteUrl)}`);
724
- console.log();
725
1001
  const host = this.target.name === "macos" ? this.launchMacosHost() : this.launchWindowsHost();
726
- await waitForExit(vite, host);
1002
+ if (this.vite) {
1003
+ console.log(
1004
+ taggedRow(
1005
+ "active",
1006
+ null,
1007
+ `${lime("hot reload active")} ${dim("\u2014 edit ui/src and save")}`
1008
+ )
1009
+ );
1010
+ console.log();
1011
+ console.log(
1012
+ footer(
1013
+ `${dim("watching")} ${value("ui/")} ${dim(
1014
+ "\xB7 hot reload on save \xB7 ctrl-c to stop"
1015
+ )}`
1016
+ )
1017
+ );
1018
+ } else {
1019
+ console.log();
1020
+ console.log(
1021
+ footer(dim("host only \xB7 serve the UI separately \xB7 ctrl-c to stop"))
1022
+ );
1023
+ }
1024
+ console.log();
1025
+ await waitForExit(...vite ? [vite, host] : [host]);
727
1026
  }
728
1027
  installSignalHandlers() {
729
1028
  process.on("SIGINT", () => {
730
- console.log("\n\x1B[90m[vidra]\x1B[0m Shutting down...");
1029
+ console.log("\n" + footer(dim("shutting down\u2026")));
731
1030
  this.shutdown(0);
732
1031
  });
733
1032
  process.on("SIGTERM", () => {
@@ -735,7 +1034,7 @@ var DevSession = class {
735
1034
  });
736
1035
  }
737
1036
  startVite() {
738
- console.log(` ${chalk5.dim("Starting Vite dev server...")}`);
1037
+ console.log(taggedRow("active", "ui", dim("starting dev server\u2026")));
739
1038
  const vite = spawn(NPM_COMMAND, ["run", "dev"], {
740
1039
  cwd: this.project.uiDir,
741
1040
  stdio: ["ignore", "pipe", "pipe"]
@@ -744,7 +1043,11 @@ var DevSession = class {
744
1043
  }
745
1044
  launchMacosHost() {
746
1045
  console.log(
747
- ` ${chalk5.dim(`Building MAUI host (${this.target.framework})...`)}`
1046
+ taggedRow(
1047
+ "active",
1048
+ "host",
1049
+ `${dim("building")} ${value(this.target.framework)} ${dim("\u2026")}`
1050
+ )
748
1051
  );
749
1052
  try {
750
1053
  execFileSync3(
@@ -763,10 +1066,14 @@ var DevSession = class {
763
1066
  }
764
1067
  );
765
1068
  } catch (error) {
766
- const output = formatExecError2(error);
767
- console.error(chalk5.red(" MAUI build failed."));
768
- console.error(chalk5.dim(output));
1069
+ const output = formatBuildError(error);
1070
+ console.error(taggedRow("error", "host", dim("MAUI build failed")));
1071
+ console.error(dim(output));
769
1072
  if (looksLikeMissingWorkload(output)) printWorkloadHint();
1073
+ else if (looksLikeMissingXcode(output)) printXcodeHint();
1074
+ if (!this.verbose) {
1075
+ console.error(footer(dim("re-run with --verbose for the full build log.")));
1076
+ }
770
1077
  process.exit(1);
771
1078
  }
772
1079
  const appBundle = findMacAppBundle(
@@ -776,9 +1083,12 @@ var DevSession = class {
776
1083
  );
777
1084
  if (!appBundle) {
778
1085
  console.error(
779
- chalk5.red(
780
- ` Could not find .app bundle in ${path6.join(this.project.hostDir, "bin", this.buildConfig, this.target.framework)}`
781
- )
1086
+ row({
1087
+ glyph: "error",
1088
+ detail: dim(
1089
+ `could not find .app bundle in ${path6.join(this.project.hostDir, "bin", this.buildConfig, this.target.framework)}`
1090
+ )
1091
+ })
782
1092
  );
783
1093
  process.exit(1);
784
1094
  }
@@ -790,11 +1100,16 @@ var DevSession = class {
790
1100
  const binary = findMacExecutable(appBundle);
791
1101
  if (!binary) {
792
1102
  console.error(
793
- chalk5.red(` Could not find the app executable in ${appBundle}.`)
1103
+ row({
1104
+ glyph: "error",
1105
+ detail: dim(`could not find the app executable in ${appBundle}`)
1106
+ })
794
1107
  );
795
1108
  process.exit(1);
796
1109
  }
797
- console.log(` ${chalk5.dim("Launching host...")}`);
1110
+ console.log(
1111
+ taggedRow("done", "host", `${dim("launched")} ${value(path6.basename(appBundle))}`)
1112
+ );
798
1113
  const host = spawn(binary, [], {
799
1114
  cwd: this.project.root,
800
1115
  stdio: ["ignore", "pipe", "pipe"],
@@ -803,7 +1118,7 @@ var DevSession = class {
803
1118
  return this.registerChild(host, "host", path6.basename(binary));
804
1119
  }
805
1120
  launchWindowsHost() {
806
- console.log(` ${chalk5.dim("Launching host...")}`);
1121
+ console.log(taggedRow("active", "host", dim("launching\u2026")));
807
1122
  const host = spawn(
808
1123
  DOTNET_COMMAND,
809
1124
  [
@@ -823,31 +1138,44 @@ var DevSession = class {
823
1138
  );
824
1139
  return this.registerChild(host, "host", "MAUI host");
825
1140
  }
826
- registerChild(child, tag, label) {
1141
+ registerChild(child, tag2, label) {
827
1142
  this.children.push(child);
828
- prefixStream(child.stdout, tag);
829
- prefixStream(child.stderr, tag);
830
- child.on("exit", (code) => {
1143
+ prefixStream(child.stdout, tag2);
1144
+ prefixStream(child.stderr, tag2);
1145
+ child.on("exit", (code, signal) => {
831
1146
  if (this.shuttingDown) return;
832
- if (tag === "ui") {
1147
+ if (tag2 === "ui") {
833
1148
  const exitCode = code ?? 1;
834
1149
  console.error(
835
- chalk5.red(`
836
- ${label} exited with code ${exitCode}.`)
1150
+ "\n" + row({ glyph: "error", detail: dim(`${label} exited with code ${exitCode}`) })
837
1151
  );
838
1152
  this.shutdown(exitCode);
839
1153
  return;
840
1154
  }
841
- if (code !== null && code !== 0) {
842
- console.error(chalk5.red(`
843
- ${label} exited with code ${code}.`));
1155
+ const failed = code !== null && code !== 0 || signal !== null;
1156
+ if (failed) {
1157
+ console.error(
1158
+ "\n" + row({
1159
+ glyph: "error",
1160
+ detail: dim(
1161
+ `${label} exited with ${signal ? `signal ${signal}` : `code ${code}`}`
1162
+ )
1163
+ })
1164
+ );
1165
+ if (tag2 === "host" && this.target.name === "macos") {
1166
+ printMacLaunchHint();
1167
+ }
844
1168
  }
845
- this.shutdown(code ?? 0);
1169
+ this.shutdown(code ?? (signal ? 1 : 0));
846
1170
  });
847
1171
  child.on("error", (error) => {
848
1172
  if (this.shuttingDown) return;
849
- console.error(chalk5.red(`
850
- Failed to start ${label}: ${error.message}`));
1173
+ console.error(
1174
+ "\n" + row({ glyph: "error", detail: dim(`failed to start ${label}: ${error.message}`) })
1175
+ );
1176
+ if (tag2 === "host" && this.target.name === "macos") {
1177
+ printMacLaunchHint();
1178
+ }
851
1179
  this.shutdown(1);
852
1180
  });
853
1181
  return child;
@@ -863,23 +1191,26 @@ var DevSession = class {
863
1191
  };
864
1192
  var ensureTargetMatchesHostOs = (targetName) => {
865
1193
  if (targetName === "macos" && process.platform !== "darwin") {
866
- console.error(chalk5.red(" The macOS dev target can only run on macOS."));
1194
+ console.error(
1195
+ row({ glyph: "error", detail: dim("the macOS target can only run on macOS") })
1196
+ );
867
1197
  process.exit(1);
868
1198
  }
869
1199
  if (targetName === "windows" && process.platform !== "win32") {
870
1200
  console.error(
871
- chalk5.red(" The Windows dev target can only run on Windows.")
1201
+ row({ glyph: "error", detail: dim("the Windows target can only run on Windows") })
872
1202
  );
873
1203
  process.exit(1);
874
1204
  }
875
1205
  };
876
- var prefixStream = (stream, tag) => {
1206
+ var prefixStream = (stream, tag2) => {
877
1207
  if (!stream) return;
1208
+ const prefix = streamPrefix(tag2);
878
1209
  stream.on("data", (chunk) => {
879
1210
  const lines = chunk.toString().split("\n");
880
1211
  for (const line of lines) {
881
1212
  if (line.length > 0) {
882
- process.stdout.write(`\x1B[90m[${tag}]\x1B[0m ${line}
1213
+ process.stdout.write(`${prefix} ${line}
883
1214
  `);
884
1215
  }
885
1216
  }
@@ -916,11 +1247,12 @@ var waitForServer = (url, timeoutMs) => {
916
1247
  poll();
917
1248
  });
918
1249
  };
919
- var waitForExit = (vite, host) => {
1250
+ var waitForExit = (...children) => {
920
1251
  return new Promise((resolve) => {
921
1252
  const resolveOnce = () => resolve();
922
- vite.once("exit", resolveOnce);
923
- host.once("exit", resolveOnce);
1253
+ for (const child of children) {
1254
+ child.once("exit", resolveOnce);
1255
+ }
924
1256
  });
925
1257
  };
926
1258
  var findMacAppBundle = (hostDir, framework, buildConfig) => {
@@ -965,39 +1297,52 @@ var killChild = (child) => {
965
1297
  }
966
1298
  child.kill("SIGTERM");
967
1299
  };
968
- var formatExecError2 = (error) => {
969
- const err = error;
970
- if (Buffer.isBuffer(err.stderr)) {
971
- return err.stderr.toString();
972
- }
973
- if (typeof err.stderr === "string") {
974
- return err.stderr;
975
- }
976
- return err.message;
1300
+ var printMacLaunchHint = () => {
1301
+ console.error();
1302
+ console.error(
1303
+ row({ glyph: "manual", label: "the host built but the app couldn't launch." })
1304
+ );
1305
+ console.error(
1306
+ footer(
1307
+ dim(
1308
+ "on macOS this is usually code signing / Gatekeeper for a locally built app:"
1309
+ )
1310
+ )
1311
+ );
1312
+ console.error(
1313
+ ` ${dim("\u2022")} ${dim("install full Xcode, then run")} ${lime("vidra doctor")} ${dim("to verify")}`
1314
+ );
1315
+ console.error(
1316
+ ` ${dim("\u2022")} ${dim("approve it once in Finder: right-click the")} ${value(".app")} ${dim("and choose")} ${value("Open")}`
1317
+ );
1318
+ console.error(
1319
+ ` ${dim("\u2022")} ${dim("or provide a signing identity via")} ${value("VIDRA_MACOS_CODESIGN_KEY")}`
1320
+ );
1321
+ console.error();
977
1322
  };
978
1323
 
979
1324
  // src/cli.ts
980
- var VERSION3 = "0.1.3";
981
1325
  var printHelp = () => {
1326
+ const cmd = (name, desc) => ` ${value(name.padEnd(10))} ${dim(desc)}`;
1327
+ const ex = (args, comment) => ` ${lime("vidra")} ${value(args.padEnd(22))} ${dim(`# ${comment}`)}`;
982
1328
  console.log(`
983
- ${chalk6.bold("vidra")} ${chalk6.dim(`v${VERSION3}`)}
1329
+ ${wordmark()} ${dim(`v${CLI_VERSION}`)}
984
1330
 
985
- ${chalk6.dim("Usage:")}
986
- vidra <command> [options]
1331
+ ${dim("usage")}
1332
+ ${lime("vidra")} ${dim("<command> [options]")}
987
1333
 
988
- ${chalk6.dim("Commands:")}
989
- dev Start the development environment
990
- build Build and package the application for distribution
991
- doctor Check that your environment is set up to build Vidra apps
992
- help Show this help message
1334
+ ${dim("commands")}
1335
+ ${cmd("dev", "start vite + the native host (hot reload)")}
1336
+ ${cmd("run", "launch the native host only")}
1337
+ ${cmd("build", "build & package for distribution")}
1338
+ ${cmd("doctor", "check your environment")}
1339
+ ${cmd("help", "show this message")}
993
1340
 
994
- ${chalk6.dim("Examples:")}
995
- vidra dev ${chalk6.dim("# start Vite + native host")}
996
- vidra dev --target windows ${chalk6.dim("# run the Windows host")}
997
- vidra build ${chalk6.dim("# auto-detect platform")}
998
- vidra build --target macos ${chalk6.dim("# macOS DMG")}
999
- vidra build --verbose ${chalk6.dim("# show full build output")}
1000
- vidra doctor ${chalk6.dim("# verify .NET SDK + MAUI workload")}
1341
+ ${dim("examples")}
1342
+ ${ex("dev --target windows", "run the windows host")}
1343
+ ${ex("build --plan", "preview the build, run nothing")}
1344
+ ${ex("build --target macos", "build & package a macOS DMG")}
1345
+ ${ex("doctor", "verify .NET SDK + MAUI workload")}
1001
1346
  `);
1002
1347
  };
1003
1348
  var main = async () => {
@@ -1007,6 +1352,9 @@ var main = async () => {
1007
1352
  case "dev":
1008
1353
  await devCommand(args.slice(1));
1009
1354
  break;
1355
+ case "run":
1356
+ await runCommand(args.slice(1));
1357
+ break;
1010
1358
  case "build":
1011
1359
  await buildCommand(args.slice(1));
1012
1360
  break;
@@ -1021,16 +1369,15 @@ var main = async () => {
1021
1369
  break;
1022
1370
  case "--version":
1023
1371
  case "-v":
1024
- console.log(VERSION3);
1372
+ console.log(CLI_VERSION);
1025
1373
  break;
1026
1374
  default:
1027
- console.error(chalk6.red(` Unknown command: ${command}
1028
- `));
1375
+ console.error(row({ glyph: "error", detail: dim(`unknown command: ${command}`) }));
1029
1376
  printHelp();
1030
1377
  process.exit(1);
1031
1378
  }
1032
1379
  };
1033
1380
  main().catch((e) => {
1034
- console.error(chalk6.red(e.message));
1381
+ console.error(row({ glyph: "error", detail: dim(e.message) }));
1035
1382
  process.exit(1);
1036
1383
  });