create-vidra-app 0.1.5 → 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.
Files changed (3) hide show
  1. package/dist/cli.js +464 -204
  2. package/dist/index.js +136 -56
  3. package/package.json +10 -1
package/dist/cli.js CHANGED
@@ -1,11 +1,7 @@
1
- // src/cli.ts
2
- import chalk7 from "chalk";
3
-
4
1
  // src/commands/build.ts
5
2
  import path5 from "path";
6
3
  import fs4 from "fs-extra";
7
4
  import { execSync as execSync3 } from "child_process";
8
- import chalk5 from "chalk";
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
  }
@@ -24,10 +30,69 @@ var parseArgs = (argv) => {
24
30
 
25
31
  // src/exec.ts
26
32
  import { execSync } from "child_process";
33
+
34
+ // src/theme.ts
27
35
  import chalk from "chalk";
28
- var toText = (value) => {
29
- if (value == null) return "";
30
- return Buffer.isBuffer(value) ? value.toString() : value;
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;
31
96
  };
32
97
  var formatProcessError = (error) => {
33
98
  const err = error;
@@ -49,7 +114,6 @@ var formatBuildError = (error) => {
49
114
  // src/project.ts
50
115
  import path from "path";
51
116
  import fs from "fs-extra";
52
- import chalk2 from "chalk";
53
117
  var detectPlatform = () => {
54
118
  switch (process.platform) {
55
119
  case "darwin":
@@ -88,9 +152,15 @@ var detectProject = (cwd) => {
88
152
  dir = parent;
89
153
  }
90
154
  console.error(
91
- chalk2.red(
92
- " Could not detect Vidra project. Run this command from your project root.\n Expected: package.json, ui/, src/<Name>.Host/<Name>.Host.csproj"
93
- )
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"))
94
164
  );
95
165
  process.exit(1);
96
166
  };
@@ -161,7 +231,6 @@ var macosTarget = {
161
231
  // src/signing.ts
162
232
  import path3 from "path";
163
233
  import { execFileSync } from "child_process";
164
- import chalk3 from "chalk";
165
234
  var signMacAppBundleIfPossible = (appBundle, options) => {
166
235
  if (process.platform !== "darwin") return;
167
236
  const identity = resolveMacCodeSigningIdentity();
@@ -176,20 +245,30 @@ var signMacAppBundleIfPossible = (appBundle, options) => {
176
245
  }
177
246
  );
178
247
  options.log(
179
- identity ? ` ${chalk3.dim("Signing:")} ${chalk3.cyan(label)} ${chalk3.dim(`with ${identity}`)}` : ` ${chalk3.dim("Signing:")} ${chalk3.cyan(label)} ${chalk3.dim("ad-hoc (no developer identity)")}`
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
+ })
180
254
  );
181
255
  } catch (error) {
182
256
  options.warn(
183
- chalk3.yellow(
184
- " Could not code-sign the macOS app bundle; it may fail to launch."
185
- )
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
+ })
186
263
  );
187
264
  options.warn(
188
- chalk3.yellow(
189
- " Install Xcode or the Command Line Tools (provides `codesign`), or set VIDRA_MACOS_CODESIGN_KEY."
265
+ footer(
266
+ dim(
267
+ "install Xcode or the Command Line Tools (provides `codesign`), or set VIDRA_MACOS_CODESIGN_KEY."
268
+ )
190
269
  )
191
270
  );
192
- options.warn(chalk3.dim(formatExecError(error)));
271
+ options.warn(dim(formatExecError(error)));
193
272
  }
194
273
  };
195
274
  var resolveMacCodeSigningIdentity = () => {
@@ -206,7 +285,7 @@ var resolveMacCodeSigningIdentity = () => {
206
285
  encoding: "utf8"
207
286
  }
208
287
  );
209
- 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);
210
289
  return identities.find((identity) => identity.startsWith("Apple Development:")) ?? identities.find((identity) => identity.startsWith("Developer ID Application:")) ?? null;
211
290
  } catch {
212
291
  return null;
@@ -257,7 +336,6 @@ var windowsTarget = {
257
336
  // src/doctor.ts
258
337
  import { execFileSync as execFileSync2 } from "child_process";
259
338
  import prompts from "prompts";
260
- import chalk4 from "chalk";
261
339
  var DOTNET = process.platform === "win32" ? "dotnet.exe" : "dotnet";
262
340
  var MAUI_DOCS = "https://learn.microsoft.com/dotnet/maui/get-started/installation";
263
341
  var bufToStr = (v) => v == null ? "" : Buffer.isBuffer(v) ? v.toString() : v;
@@ -384,19 +462,32 @@ var collectRequirements = (opts = {}) => {
384
462
  }
385
463
  return reqs;
386
464
  };
465
+ var STATUS_GLYPH = {
466
+ ok: "done",
467
+ missing: "error",
468
+ unknown: "manual"
469
+ };
387
470
  var printRequirements = (reqs) => {
471
+ const labelWidth = Math.max(0, ...reqs.map((r) => r.name.length)) + 2;
388
472
  for (const r of reqs) {
389
- const icon = r.status === "ok" ? chalk4.green("\u2713") : r.status === "missing" ? chalk4.red("\u2717") : chalk4.yellow("?");
390
- const detail = r.detail ? ` ${chalk4.dim(`(${r.detail})`)}` : "";
391
- 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
+ );
392
481
  if (r.status === "missing" && r.fix) {
393
- console.log(` ${chalk4.dim("fix:")} ${chalk4.cyan(r.fix)}`);
482
+ console.log(fixLine(r.fix));
394
483
  }
395
484
  }
396
485
  };
397
486
  var runDoctor = async () => {
398
487
  console.log();
399
- console.log(` ${chalk4.bold.cyan("vidra doctor")}`);
488
+ console.log(` ${lime("vidra")} ${value("doctor")}`);
489
+ console.log();
490
+ console.log(footer(dim("checking your environment\u2026")));
400
491
  console.log();
401
492
  const reqs = collectRequirements();
402
493
  printRequirements(reqs);
@@ -404,17 +495,22 @@ var runDoctor = async () => {
404
495
  const missing = reqs.filter((r) => r.status === "missing");
405
496
  if (missing.length === 0) {
406
497
  console.log(
407
- ` ${chalk4.green("All checks passed.")} You're ready to run ${chalk4.cyan(
408
- "vidra dev"
409
- )}.`
498
+ footer(
499
+ `${dim("all checks passed \u2014 you're ready to run")} ${lime(
500
+ "vidra dev"
501
+ )}${dim(".")}`
502
+ )
410
503
  );
411
504
  console.log();
412
505
  return 0;
413
506
  }
507
+ const n = missing.length;
414
508
  console.log(
415
- ` ${chalk4.yellow(
416
- `${missing.length} issue(s) found.`
417
- )} Apply the fixes above, then re-run ${chalk4.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
+ )
418
514
  );
419
515
  console.log();
420
516
  return 1;
@@ -422,11 +518,16 @@ var runDoctor = async () => {
422
518
  var installWorkload = (csprojPath) => {
423
519
  const args = csprojPath ? ["workload", "restore", csprojPath] : ["workload", "install", "maui"];
424
520
  console.log();
425
- console.log(` ${chalk4.dim(`Running: ${DOTNET} ${args.join(" ")}`)}`);
426
521
  console.log(
427
- ` ${chalk4.dim(
428
- "This can download several hundred MB and take a few minutes."
429
- )}`
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
+ )
430
531
  );
431
532
  console.log();
432
533
  try {
@@ -434,13 +535,15 @@ var installWorkload = (csprojPath) => {
434
535
  return true;
435
536
  } catch {
436
537
  console.error();
437
- console.error(` ${chalk4.red("Workload install failed.")}`);
538
+ console.error(row({ glyph: "error", label: "workload install failed" }));
438
539
  console.error(
439
- ` ${chalk4.dim(
440
- "If this is a permissions error, your SDK is in a system location and needs elevation:"
441
- )}`
540
+ footer(
541
+ dim(
542
+ "if this is a permissions error, your SDK is in a system location and needs elevation:"
543
+ )
544
+ )
442
545
  );
443
- console.error(` ${chalk4.cyan("sudo dotnet workload install maui")}`);
546
+ console.error(fixLine("sudo dotnet workload install maui"));
444
547
  console.error();
445
548
  return false;
446
549
  }
@@ -449,9 +552,15 @@ var ensureMauiWorkload = async (opts = {}) => {
449
552
  const dotnet = checkDotnetSdk();
450
553
  if (dotnet.status === "missing") {
451
554
  console.log();
452
- console.log(` ${chalk4.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
+ );
453
562
  if (dotnet.fix) {
454
- console.log(` ${chalk4.dim("fix:")} ${chalk4.cyan(dotnet.fix)}`);
563
+ console.log(fixLine(dotnet.fix));
455
564
  }
456
565
  return false;
457
566
  }
@@ -459,7 +568,11 @@ var ensureMauiWorkload = async (opts = {}) => {
459
568
  if (isMauiWorkloadInstalled()) return true;
460
569
  console.log();
461
570
  console.log(
462
- ` ${chalk4.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
+ })
463
576
  );
464
577
  const interactive = opts.interactive ?? isInteractive();
465
578
  if (interactive) {
@@ -477,80 +590,91 @@ var ensureMauiWorkload = async (opts = {}) => {
477
590
  }
478
591
  if (install) {
479
592
  if (installWorkload(opts.csprojPath) && isMauiWorkloadInstalled()) {
480
- console.log(` ${chalk4.green("\u2713")} MAUI workload installed.`);
593
+ console.log(
594
+ row({
595
+ glyph: "done",
596
+ label: ".NET MAUI workload",
597
+ detail: dim("installed")
598
+ })
599
+ );
481
600
  return true;
482
601
  }
483
602
  return false;
484
603
  }
485
604
  }
486
- console.log(
487
- ` ${chalk4.dim("run:")} ${chalk4.cyan("dotnet workload install maui")}`
488
- );
489
- console.log(` ${chalk4.dim("docs:")} ${chalk4.cyan(MAUI_DOCS)}`);
605
+ console.log(fixLine("dotnet workload install maui", "run:"));
606
+ console.log(fixLine(MAUI_DOCS, "docs:"));
490
607
  return false;
491
608
  };
492
609
  var printWorkloadHint = () => {
493
610
  console.error();
494
611
  console.error(
495
- ` ${chalk4.yellow("This looks like a missing .NET MAUI workload.")}`
496
- );
497
- console.error(
498
- ` ${chalk4.dim("fix: ")} ${chalk4.cyan("dotnet workload install maui")}`
499
- );
500
- console.error(
501
- ` ${chalk4.dim("check:")} ${chalk4.cyan("vidra doctor")}`
612
+ row({ glyph: "manual", label: "this looks like a missing .NET MAUI workload." })
502
613
  );
614
+ console.error(fixLine("dotnet workload install maui"));
615
+ console.error(fixLine("vidra doctor", "check:"));
503
616
  console.error();
504
617
  };
505
618
  var printXcodeHint = () => {
506
619
  console.error();
507
620
  console.error(
508
- ` ${chalk4.yellow(
509
- "Mac Catalyst builds need the full Xcode app, not just the Command Line Tools."
510
- )}`
621
+ row({
622
+ glyph: "manual",
623
+ label: "Mac Catalyst needs the full Xcode app, not just the Command Line Tools."
624
+ })
511
625
  );
512
- console.error(` ${chalk4.dim("1.")} Install Xcode from the App Store`);
626
+ console.error(` ${dim("1.")} ${value("install Xcode from the App Store")}`);
513
627
  console.error(
514
- ` ${chalk4.dim("2.")} ${chalk4.cyan(
628
+ ` ${dim("2.")} ${lime(
515
629
  "sudo xcode-select -s /Applications/Xcode.app/Contents/Developer"
516
630
  )}`
517
631
  );
518
- console.error(
519
- ` ${chalk4.dim("3.")} ${chalk4.cyan("sudo xcodebuild -runFirstLaunch")}`
520
- );
521
- console.error(` ${chalk4.dim("check:")} ${chalk4.cyan("vidra doctor")}`);
632
+ console.error(` ${dim("3.")} ${lime("sudo xcodebuild -runFirstLaunch")}`);
633
+ console.error(fixLine("vidra doctor", "check:"));
522
634
  console.error();
523
635
  };
524
636
 
525
637
  // src/commands/build.ts
526
- var VERSION = "0.1.0";
527
638
  var TARGETS = {
528
639
  macos: macosTarget,
529
640
  windows: windowsTarget
530
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"}`;
531
644
  var buildCommand = async (argv) => {
532
645
  const args = parseArgs(["_", "_", ...argv]);
533
646
  const verbose = !!args["verbose"];
647
+ const plan = !!args["plan"] || !!args["dry-run"];
534
648
  const targetName = args["target"] || detectPlatform();
535
- console.log();
536
- console.log(
537
- ` ${chalk5.bold.cyan("vidra build")} ${chalk5.dim(`v${VERSION}`)}`
538
- );
539
- console.log();
540
649
  const target = TARGETS[targetName];
541
650
  if (!target) {
542
651
  const supported = Object.keys(TARGETS).join(", ");
652
+ console.error();
543
653
  console.error(
544
- chalk5.red(
545
- ` Unsupported target: ${targetName}. Supported: ${supported}`
546
- )
654
+ row({
655
+ glyph: "error",
656
+ detail: dim(`unsupported target: ${targetName} \u2014 supported: ${supported}`)
657
+ })
547
658
  );
548
659
  process.exit(1);
549
660
  }
550
661
  const project = detectProject(process.cwd());
551
- console.log(` ${chalk5.dim("Project:")} ${chalk5.cyan(project.projectName)}`);
552
- console.log(` ${chalk5.dim("Target:")} ${chalk5.cyan(target.name)} (${target.framework})`);
553
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
+ }
554
678
  if (!await ensureMauiWorkload({ csprojPath: project.csprojPath })) {
555
679
  process.exit(1);
556
680
  }
@@ -560,13 +684,13 @@ var buildCommand = async (argv) => {
560
684
  const bundlePath = target.findBundle(publishDir, project.projectName);
561
685
  if (!bundlePath) {
562
686
  console.error(
563
- chalk5.red(` Could not find build artifact in ${publishDir}`)
687
+ row({
688
+ glyph: "error",
689
+ detail: dim(`could not find build artifact in ${publishDir}`)
690
+ })
564
691
  );
565
692
  process.exit(1);
566
693
  }
567
- console.log(
568
- ` ${chalk5.dim("Bundle:")} ${chalk5.cyan(path5.basename(bundlePath))}`
569
- );
570
694
  if (target.name === "macos") {
571
695
  signMacAppBundleIfPossible(bundlePath, {
572
696
  verbose,
@@ -574,36 +698,77 @@ var buildCommand = async (argv) => {
574
698
  warn: console.warn
575
699
  });
576
700
  }
577
- const outputDir = path5.join(project.root, "dist");
578
- fs4.ensureDirSync(outputDir);
701
+ const outputPath = await stepPackage(project, target, bundlePath);
579
702
  console.log();
580
- console.log(` ${chalk5.dim(`Packaging for ${target.name}...`)}`);
581
- const startPkg = Date.now();
582
- let outputPath;
583
- try {
584
- outputPath = await target.package(bundlePath, outputDir, {
585
- projectName: project.projectName,
586
- displayVersion: project.displayVersion
587
- });
588
- } catch (e) {
589
- console.error(chalk5.red(` Packaging failed.`));
590
- console.error(chalk5.dim(formatProcessError(e)));
591
- process.exit(1);
592
- }
593
- const pkgTime = ((Date.now() - startPkg) / 1e3).toFixed(1);
594
- const sizeBytes = fs4.statSync(outputPath).size;
595
- const sizeMB = (sizeBytes / (1024 * 1024)).toFixed(1);
596
703
  console.log(
597
- ` ${chalk5.green(">")} ${path5.basename(outputPath)} ${chalk5.dim(`(${sizeMB} MB, ${pkgTime}s)`)}`
704
+ footer(
705
+ `${dim("done \u2014")} ${value(path5.relative(project.root, outputPath))}`
706
+ )
598
707
  );
599
708
  console.log();
709
+ };
710
+ var printBuildPlan = (project, target) => {
600
711
  console.log(
601
- ` ${chalk5.green("Done!")} Output: ${chalk5.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
+ })
602
718
  );
603
- 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
+ }
604
770
  };
605
771
  var stepBuildUi = (project, verbose) => {
606
- console.log(` ${chalk5.dim("Building UI...")}`);
607
772
  const start = Date.now();
608
773
  try {
609
774
  execSync3("npm run build", {
@@ -611,19 +776,38 @@ var stepBuildUi = (project, verbose) => {
611
776
  stdio: verbose ? "inherit" : "pipe"
612
777
  });
613
778
  } catch (e) {
614
- console.error(chalk5.red(" Vite build failed."));
615
- console.error(chalk5.dim(formatBuildError(e)));
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)));
616
788
  process.exit(1);
617
789
  }
618
790
  const elapsed = ((Date.now() - start) / 1e3).toFixed(1);
619
- console.log(` ${chalk5.green(">")} Vite build complete ${chalk5.dim(`(${elapsed}s)`)}`);
620
- 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
+ );
621
799
  };
622
800
  var stepCopyAssets = (project) => {
623
- console.log(` ${chalk5.dim("Copying assets to host project...")}`);
624
801
  const viteDist = path5.join(project.uiDir, "dist");
625
802
  if (!fs4.existsSync(viteDist)) {
626
- console.error(chalk5.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
+ );
627
811
  process.exit(1);
628
812
  }
629
813
  const wwwroot = path5.join(project.hostDir, "Resources", "Raw", "wwwroot");
@@ -631,9 +815,13 @@ var stepCopyAssets = (project) => {
631
815
  fs4.copySync(viteDist, wwwroot);
632
816
  const fileCount = countFiles(wwwroot);
633
817
  console.log(
634
- ` ${chalk5.green(">")} ${fileCount} files -> ${chalk5.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
+ })
635
824
  );
636
- console.log();
637
825
  };
638
826
  var countFiles = (dir) => {
639
827
  let count = 0;
@@ -647,9 +835,6 @@ var countFiles = (dir) => {
647
835
  return count;
648
836
  };
649
837
  var stepDotnetPublish = (project, target, verbose) => {
650
- console.log(
651
- ` ${chalk5.dim(`Publishing .NET host (${target.framework})...`)}`
652
- );
653
838
  const start = Date.now();
654
839
  const extraArgs = target.extraPublishArgs ?? "-p:CreatePackage=false";
655
840
  try {
@@ -662,28 +847,66 @@ var stepDotnetPublish = (project, target, verbose) => {
662
847
  );
663
848
  } catch (e) {
664
849
  const output = formatBuildError(e);
665
- console.error(chalk5.red(" dotnet publish failed."));
666
- console.error(chalk5.dim(output));
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));
667
859
  if (looksLikeMissingWorkload(output)) printWorkloadHint();
668
860
  else if (looksLikeMissingXcode(output)) printXcodeHint();
669
861
  if (!verbose) {
670
- console.error(
671
- chalk5.dim(" Re-run with --verbose for the full build log.")
672
- );
862
+ console.error(footer(dim("re-run with --verbose for the full build log.")));
673
863
  }
674
864
  process.exit(1);
675
865
  }
676
866
  const elapsed = ((Date.now() - start) / 1e3).toFixed(1);
677
867
  console.log(
678
- ` ${chalk5.green(">")} dotnet publish complete ${chalk5.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
+ })
679
874
  );
680
- const publishDir = path5.join(
681
- project.hostDir,
682
- "bin",
683
- "Release",
684
- 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
+ })
685
908
  );
686
- return publishDir;
909
+ return outputPath;
687
910
  };
688
911
 
689
912
  // src/commands/dev.ts
@@ -691,8 +914,6 @@ import path6 from "path";
691
914
  import fs5 from "fs-extra";
692
915
  import { execFileSync as execFileSync3, spawn } from "child_process";
693
916
  import { request } from "http";
694
- import chalk6 from "chalk";
695
- var VERSION2 = "0.1.0";
696
917
  var POLL_INTERVAL_MS = 500;
697
918
  var POLL_TIMEOUT_MS = 3e4;
698
919
  var NPM_COMMAND = process.platform === "win32" ? "npm.cmd" : "npm";
@@ -718,9 +939,10 @@ var startSession = async (argv, opts) => {
718
939
  if (!target) {
719
940
  const supported = Object.keys(TARGETS2).join(", ");
720
941
  console.error(
721
- chalk6.red(
722
- ` Unsupported target: ${targetName}. Supported: ${supported}`
723
- )
942
+ row({
943
+ glyph: "error",
944
+ detail: dim(`unsupported target: ${targetName} \u2014 supported: ${supported}`)
945
+ })
724
946
  );
725
947
  process.exit(1);
726
948
  }
@@ -751,14 +973,9 @@ var DevSession = class {
751
973
  async run() {
752
974
  this.installSignalHandlers();
753
975
  console.log();
754
- console.log(
755
- ` ${chalk6.bold.cyan(this.vite ? "vidra dev" : "vidra run")} ${chalk6.dim(`v${VERSION2}`)}`
756
- );
757
- console.log();
758
- console.log(` ${chalk6.dim("Project:")} ${chalk6.cyan(this.project.projectName)}`);
759
- console.log(
760
- ` ${chalk6.dim("Target:")} ${chalk6.cyan(this.target.name)} (${this.target.framework})`
761
- );
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));
762
979
  console.log();
763
980
  let vite;
764
981
  if (this.vite) {
@@ -766,23 +983,50 @@ var DevSession = class {
766
983
  try {
767
984
  await waitForServer(this.viteUrl, POLL_TIMEOUT_MS);
768
985
  } catch (error) {
769
- console.error(chalk6.red(` ${error.message}`));
986
+ console.error(row({ glyph: "error", detail: dim(error.message) }));
770
987
  this.shutdown(1);
771
988
  }
772
- console.log(` ${chalk6.dim("Vite:")} ${chalk6.cyan(this.viteUrl)}`);
773
- console.log();
989
+ console.log(
990
+ taggedRow("active", "ui", `${dim("vite ready \u2014")} ${value(this.viteUrl)}`)
991
+ );
774
992
  } else {
775
993
  console.log(
776
- ` ${chalk6.dim("UI:")} ${chalk6.cyan(this.viteUrl)} ${chalk6.dim("(start it separately, e.g. `npm run dev:ui`)")}`
994
+ taggedRow(
995
+ "skip",
996
+ "ui",
997
+ `${dim("vite not started \u2014")} ${value("npm run dev:ui")}`
998
+ )
777
999
  );
778
- console.log();
779
1000
  }
780
1001
  const host = this.target.name === "macos" ? this.launchMacosHost() : this.launchWindowsHost();
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();
781
1025
  await waitForExit(...vite ? [vite, host] : [host]);
782
1026
  }
783
1027
  installSignalHandlers() {
784
1028
  process.on("SIGINT", () => {
785
- console.log("\n\x1B[90m[vidra]\x1B[0m Shutting down...");
1029
+ console.log("\n" + footer(dim("shutting down\u2026")));
786
1030
  this.shutdown(0);
787
1031
  });
788
1032
  process.on("SIGTERM", () => {
@@ -790,7 +1034,7 @@ var DevSession = class {
790
1034
  });
791
1035
  }
792
1036
  startVite() {
793
- console.log(` ${chalk6.dim("Starting Vite dev server...")}`);
1037
+ console.log(taggedRow("active", "ui", dim("starting dev server\u2026")));
794
1038
  const vite = spawn(NPM_COMMAND, ["run", "dev"], {
795
1039
  cwd: this.project.uiDir,
796
1040
  stdio: ["ignore", "pipe", "pipe"]
@@ -799,7 +1043,11 @@ var DevSession = class {
799
1043
  }
800
1044
  launchMacosHost() {
801
1045
  console.log(
802
- ` ${chalk6.dim(`Building MAUI host (${this.target.framework})...`)}`
1046
+ taggedRow(
1047
+ "active",
1048
+ "host",
1049
+ `${dim("building")} ${value(this.target.framework)} ${dim("\u2026")}`
1050
+ )
803
1051
  );
804
1052
  try {
805
1053
  execFileSync3(
@@ -819,14 +1067,12 @@ var DevSession = class {
819
1067
  );
820
1068
  } catch (error) {
821
1069
  const output = formatBuildError(error);
822
- console.error(chalk6.red(" MAUI build failed."));
823
- console.error(chalk6.dim(output));
1070
+ console.error(taggedRow("error", "host", dim("MAUI build failed")));
1071
+ console.error(dim(output));
824
1072
  if (looksLikeMissingWorkload(output)) printWorkloadHint();
825
1073
  else if (looksLikeMissingXcode(output)) printXcodeHint();
826
1074
  if (!this.verbose) {
827
- console.error(
828
- chalk6.dim(" Re-run with --verbose for the full build log.")
829
- );
1075
+ console.error(footer(dim("re-run with --verbose for the full build log.")));
830
1076
  }
831
1077
  process.exit(1);
832
1078
  }
@@ -837,9 +1083,12 @@ var DevSession = class {
837
1083
  );
838
1084
  if (!appBundle) {
839
1085
  console.error(
840
- chalk6.red(
841
- ` Could not find .app bundle in ${path6.join(this.project.hostDir, "bin", this.buildConfig, this.target.framework)}`
842
- )
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
+ })
843
1092
  );
844
1093
  process.exit(1);
845
1094
  }
@@ -851,11 +1100,16 @@ var DevSession = class {
851
1100
  const binary = findMacExecutable(appBundle);
852
1101
  if (!binary) {
853
1102
  console.error(
854
- chalk6.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
+ })
855
1107
  );
856
1108
  process.exit(1);
857
1109
  }
858
- console.log(` ${chalk6.dim("Launching host...")}`);
1110
+ console.log(
1111
+ taggedRow("done", "host", `${dim("launched")} ${value(path6.basename(appBundle))}`)
1112
+ );
859
1113
  const host = spawn(binary, [], {
860
1114
  cwd: this.project.root,
861
1115
  stdio: ["ignore", "pipe", "pipe"],
@@ -864,7 +1118,7 @@ var DevSession = class {
864
1118
  return this.registerChild(host, "host", path6.basename(binary));
865
1119
  }
866
1120
  launchWindowsHost() {
867
- console.log(` ${chalk6.dim("Launching host...")}`);
1121
+ console.log(taggedRow("active", "host", dim("launching\u2026")));
868
1122
  const host = spawn(
869
1123
  DOTNET_COMMAND,
870
1124
  [
@@ -884,17 +1138,16 @@ var DevSession = class {
884
1138
  );
885
1139
  return this.registerChild(host, "host", "MAUI host");
886
1140
  }
887
- registerChild(child, tag, label) {
1141
+ registerChild(child, tag2, label) {
888
1142
  this.children.push(child);
889
- prefixStream(child.stdout, tag);
890
- prefixStream(child.stderr, tag);
1143
+ prefixStream(child.stdout, tag2);
1144
+ prefixStream(child.stderr, tag2);
891
1145
  child.on("exit", (code, signal) => {
892
1146
  if (this.shuttingDown) return;
893
- if (tag === "ui") {
1147
+ if (tag2 === "ui") {
894
1148
  const exitCode = code ?? 1;
895
1149
  console.error(
896
- chalk6.red(`
897
- ${label} exited with code ${exitCode}.`)
1150
+ "\n" + row({ glyph: "error", detail: dim(`${label} exited with code ${exitCode}`) })
898
1151
  );
899
1152
  this.shutdown(exitCode);
900
1153
  return;
@@ -902,12 +1155,14 @@ var DevSession = class {
902
1155
  const failed = code !== null && code !== 0 || signal !== null;
903
1156
  if (failed) {
904
1157
  console.error(
905
- chalk6.red(
906
- `
907
- ${label} exited with ${signal ? `signal ${signal}` : `code ${code}`}.`
908
- )
1158
+ "\n" + row({
1159
+ glyph: "error",
1160
+ detail: dim(
1161
+ `${label} exited with ${signal ? `signal ${signal}` : `code ${code}`}`
1162
+ )
1163
+ })
909
1164
  );
910
- if (tag === "host" && this.target.name === "macos") {
1165
+ if (tag2 === "host" && this.target.name === "macos") {
911
1166
  printMacLaunchHint();
912
1167
  }
913
1168
  }
@@ -915,9 +1170,10 @@ var DevSession = class {
915
1170
  });
916
1171
  child.on("error", (error) => {
917
1172
  if (this.shuttingDown) return;
918
- console.error(chalk6.red(`
919
- Failed to start ${label}: ${error.message}`));
920
- if (tag === "host" && this.target.name === "macos") {
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") {
921
1177
  printMacLaunchHint();
922
1178
  }
923
1179
  this.shutdown(1);
@@ -935,23 +1191,26 @@ var DevSession = class {
935
1191
  };
936
1192
  var ensureTargetMatchesHostOs = (targetName) => {
937
1193
  if (targetName === "macos" && process.platform !== "darwin") {
938
- console.error(chalk6.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
+ );
939
1197
  process.exit(1);
940
1198
  }
941
1199
  if (targetName === "windows" && process.platform !== "win32") {
942
1200
  console.error(
943
- chalk6.red(" The Windows dev target can only run on Windows.")
1201
+ row({ glyph: "error", detail: dim("the Windows target can only run on Windows") })
944
1202
  );
945
1203
  process.exit(1);
946
1204
  }
947
1205
  };
948
- var prefixStream = (stream, tag) => {
1206
+ var prefixStream = (stream, tag2) => {
949
1207
  if (!stream) return;
1208
+ const prefix = streamPrefix(tag2);
950
1209
  stream.on("data", (chunk) => {
951
1210
  const lines = chunk.toString().split("\n");
952
1211
  for (const line of lines) {
953
1212
  if (line.length > 0) {
954
- process.stdout.write(`\x1B[90m[${tag}]\x1B[0m ${line}
1213
+ process.stdout.write(`${prefix} ${line}
955
1214
  `);
956
1215
  }
957
1216
  }
@@ -1040,48 +1299,50 @@ var killChild = (child) => {
1040
1299
  };
1041
1300
  var printMacLaunchHint = () => {
1042
1301
  console.error();
1043
- console.error(chalk6.yellow(" The host built but the app couldn't launch."));
1044
1302
  console.error(
1045
- chalk6.dim(
1046
- " On macOS this is usually code signing / Gatekeeper for a locally built app:"
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
+ )
1047
1310
  )
1048
1311
  );
1049
1312
  console.error(
1050
- ` ${chalk6.dim("\u2022")} Install full Xcode, then run ${chalk6.cyan("vidra doctor")} to verify`
1313
+ ` ${dim("\u2022")} ${dim("install full Xcode, then run")} ${lime("vidra doctor")} ${dim("to verify")}`
1051
1314
  );
1052
1315
  console.error(
1053
- ` ${chalk6.dim("\u2022")} Approve it once in Finder: right-click the ${chalk6.cyan(".app")} and choose ${chalk6.cyan("Open")}`
1316
+ ` ${dim("\u2022")} ${dim("approve it once in Finder: right-click the")} ${value(".app")} ${dim("and choose")} ${value("Open")}`
1054
1317
  );
1055
1318
  console.error(
1056
- ` ${chalk6.dim("\u2022")} Or provide a signing identity via ${chalk6.cyan("VIDRA_MACOS_CODESIGN_KEY")}`
1319
+ ` ${dim("\u2022")} ${dim("or provide a signing identity via")} ${value("VIDRA_MACOS_CODESIGN_KEY")}`
1057
1320
  );
1058
1321
  console.error();
1059
1322
  };
1060
1323
 
1061
1324
  // src/cli.ts
1062
- var VERSION3 = "0.1.5";
1063
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}`)}`;
1064
1328
  console.log(`
1065
- ${chalk7.bold("vidra")} ${chalk7.dim(`v${VERSION3}`)}
1329
+ ${wordmark()} ${dim(`v${CLI_VERSION}`)}
1066
1330
 
1067
- ${chalk7.dim("Usage:")}
1068
- vidra <command> [options]
1331
+ ${dim("usage")}
1332
+ ${lime("vidra")} ${dim("<command> [options]")}
1069
1333
 
1070
- ${chalk7.dim("Commands:")}
1071
- dev Start the development environment (Vite + native host)
1072
- run Build and launch the native host only (no Vite dev server)
1073
- build Build and package the application for distribution
1074
- doctor Check that your environment is set up to build Vidra apps
1075
- 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")}
1076
1340
 
1077
- ${chalk7.dim("Examples:")}
1078
- vidra dev ${chalk7.dim("# start Vite + native host")}
1079
- vidra dev --target windows ${chalk7.dim("# run the Windows host")}
1080
- vidra run ${chalk7.dim("# launch the host (UI served separately)")}
1081
- vidra build ${chalk7.dim("# auto-detect platform")}
1082
- vidra build --target macos ${chalk7.dim("# macOS DMG")}
1083
- vidra build --verbose ${chalk7.dim("# show full build output")}
1084
- vidra doctor ${chalk7.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")}
1085
1346
  `);
1086
1347
  };
1087
1348
  var main = async () => {
@@ -1108,16 +1369,15 @@ var main = async () => {
1108
1369
  break;
1109
1370
  case "--version":
1110
1371
  case "-v":
1111
- console.log(VERSION3);
1372
+ console.log(CLI_VERSION);
1112
1373
  break;
1113
1374
  default:
1114
- console.error(chalk7.red(` Unknown command: ${command}
1115
- `));
1375
+ console.error(row({ glyph: "error", detail: dim(`unknown command: ${command}`) }));
1116
1376
  printHelp();
1117
1377
  process.exit(1);
1118
1378
  }
1119
1379
  };
1120
1380
  main().catch((e) => {
1121
- console.error(chalk7.red(e.message));
1381
+ console.error(row({ glyph: "error", detail: dim(e.message) }));
1122
1382
  process.exit(1);
1123
1383
  });