boo-flash 0.1.5 → 0.1.7

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/README.md +13 -0
  2. package/dist/app.js +395 -514
  3. package/package.json +2 -3
package/dist/app.js CHANGED
@@ -2552,7 +2552,6 @@ import { createHash as createHash2 } from "node:crypto";
2552
2552
  import { chmod as chmod2, mkdtemp, open as open2, rm } from "node:fs/promises";
2553
2553
  import { tmpdir as tmpdir4 } from "node:os";
2554
2554
  import { dirname as dirname3, join as join5 } from "node:path";
2555
- import { setTimeout as delay } from "node:timers/promises";
2556
2555
  import { fileURLToPath as fileURLToPath2 } from "node:url";
2557
2556
 
2558
2557
  // src/backends/macos.ts
@@ -2588,7 +2587,6 @@ function resolvePackage(name2) {
2588
2587
 
2589
2588
  // src/dependencies.ts
2590
2589
  var commonTools = ["curl", "zstd"];
2591
- var candidateTools = ["gh", "unzip"];
2592
2590
  function preferredExecutable(name2, platform3, path, macSystemExists) {
2593
2591
  if (platform3 === "darwin" && (name2 === "curl" || name2 === "unzip") && macSystemExists) return `/usr/bin/${name2}`;
2594
2592
  return path;
@@ -2643,7 +2641,7 @@ async function command(argv, signal, options = {}) {
2643
2641
  if (!binary) throw new Error(`Missing ${argv[0]}. Refresh dependency setup before continuing.`);
2644
2642
  const child = spawn(binary, argv.slice(1), {
2645
2643
  stdio: options.terminal ? "inherit" : ["pipe", "pipe", "pipe"],
2646
- env: { ...process.env, GH_DEBUG: "", GH_PAGER: "cat", PAGER: "cat", GH_PROMPT_DISABLED: "1" }
2644
+ env: { ...process.env, PAGER: "cat" }
2647
2645
  });
2648
2646
  let failure;
2649
2647
  const exited = new Promise((resolve) => {
@@ -2654,7 +2652,7 @@ async function command(argv, signal, options = {}) {
2654
2652
  });
2655
2653
  let stderr = "";
2656
2654
  child.stderr?.on("data", (data) => {
2657
- if (argv[0] !== "gh") stderr = (stderr + data.toString()).slice(-1500);
2655
+ stderr = (stderr + data.toString()).slice(-1500);
2658
2656
  });
2659
2657
  child.stdin?.on("error", (error) => {
2660
2658
  failure = error;
@@ -2677,8 +2675,7 @@ async function command(argv, signal, options = {}) {
2677
2675
  const code2 = await exited;
2678
2676
  signal.throwIfAborted();
2679
2677
  if (failure || code2 !== 0) {
2680
- const message = argv[0] === "gh" ? `GitHub request failed. Run ${cleanText(binary)} auth login if needed; check repository access and your connection, then refresh.` : `${argv[0].split("/").pop()} failed (${code2 ?? "not started"}). ${cleanText(stderr || failure?.message || "")}`;
2681
- throw new Error(message);
2678
+ throw new Error(`${argv[0].split("/").pop()} failed (${code2 ?? "not started"}). ${cleanText(stderr || failure?.message || "")}`);
2682
2679
  }
2683
2680
  return output;
2684
2681
  } finally {
@@ -2690,10 +2687,9 @@ async function command(argv, signal, options = {}) {
2690
2687
 
2691
2688
  // src/installers/homebrew.ts
2692
2689
  import { spawn as spawn2 } from "node:child_process";
2693
- var packages = [...commonTools, ...candidateTools];
2694
- function dependencies(includeGitHub = false) {
2695
- const required2 = includeGitHub ? packages : commonTools;
2696
- const absent = missing(required2);
2690
+ var packages = commonTools;
2691
+ function dependencies() {
2692
+ const absent = missing(packages);
2697
2693
  return {
2698
2694
  missing: absent,
2699
2695
  installer: absent.length ? executable("brew") : null,
@@ -2804,8 +2800,6 @@ async function disks(signal, source, staging = tmpdir(), unmounted) {
2804
2800
  const zstd = executable("zstd");
2805
2801
  if (!zstd) throw new Error("Install zstd before flashing.");
2806
2802
  protectedPaths2.push(zstd);
2807
- const gh = executable("gh");
2808
- if (gh) protectedPaths2.push(gh);
2809
2803
  for (const path of protectedPaths2) {
2810
2804
  const canonical = await realpath(path);
2811
2805
  const device = (await command(["/usr/bin/stat", "-f", "%Sd", canonical], signal)).trim();
@@ -3044,7 +3038,7 @@ async function protectedPaths(source, staging) {
3044
3038
  }
3045
3039
  }
3046
3040
  if (process.env.OTUI_ASSET_ROOT) paths.push(process.env.OTUI_ASSET_ROOT);
3047
- for (const name2 of [...commonTools, ...candidateTools, ...tools]) {
3041
+ for (const name2 of [...commonTools, ...tools]) {
3048
3042
  const path = executable(name2);
3049
3043
  if (path) paths.push(path);
3050
3044
  }
@@ -3239,8 +3233,8 @@ async function disks2(signal, source, staging = tmpdir2(), unmounted) {
3239
3233
  }
3240
3234
  return result;
3241
3235
  }
3242
- function dependencies2(includeGitHub = false) {
3243
- const required2 = [...commonTools, ...tools, ...includeGitHub ? candidateTools : []];
3236
+ function dependencies2() {
3237
+ const required2 = [...commonTools, ...tools];
3244
3238
  return {
3245
3239
  missing: missing(required2),
3246
3240
  installer: null,
@@ -3423,8 +3417,6 @@ async function clearCachedImages(signal) {
3423
3417
 
3424
3418
  // src/backend.ts
3425
3419
  var profiles = ["intel", "amd", "nvidia"];
3426
- var repository = "anomalyco/boo";
3427
- var repositoryIdentities = /* @__PURE__ */ new Set([repository, "anomalyco/ghostty-distro"]);
3428
3420
  var releaseOrigin = "https://images.os.boo";
3429
3421
  var diskID3 = /^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/;
3430
3422
  var sha256 = /^[a-f0-9]{64}$/;
@@ -3529,152 +3521,97 @@ async function discoverImages(signal) {
3529
3521
  });
3530
3522
  return { run: build, version: channel.version, revision: channel.revision, timestamp, images };
3531
3523
  }
3532
- async function discoverCandidates(signal) {
3533
- const api = async (path) => JSON.parse(await command(["gh", "api", "--hostname", "github.com", `repos/${repository}/${path}`], signal));
3534
- const result = await api("actions/workflows/candidate.yml/runs?status=success&per_page=10");
3535
- if (!Array.isArray(result.workflow_runs)) throw new Error("Invalid GitHub candidate workflow response.");
3536
- const candidates = [];
3537
- for (const run of result.workflow_runs.slice(0, 10)) {
3538
- if (run.status !== "completed" || run.conclusion !== "success" || run.path !== ".github/workflows/candidate.yml" || !positive(run.id) || !positive(run.run_number) || !Number.isFinite(Date.parse(run.created_at))) continue;
3539
- const batch = await api(`actions/runs/${run.id}/artifacts?per_page=100`);
3540
- if (!Array.isArray(batch.artifacts)) throw new Error("Invalid GitHub artifact response.");
3541
- for (const a of batch.artifacts) {
3542
- const match = /^boo-(intel|amd|nvidia)-candidate-([a-f0-9]{40})$/.exec(a.name);
3543
- if (!match || a.expired !== false || !(Date.parse(a.expires_at) > Date.now()) || !positive(a.id) || !positive(a.size_in_bytes) || !/^sha256:[a-f0-9]{64}$/.test(a.digest) || batch.artifacts.filter((other) => other.name === a.name).length !== 1) continue;
3544
- candidates.push({
3545
- run: run.id,
3546
- revision: match[2],
3547
- timestamp: new Date(run.created_at).toISOString(),
3548
- images: [{
3549
- profile: match[1],
3550
- id: a.id,
3551
- name: a.name,
3552
- bytes: a.size_in_bytes,
3553
- digest: a.digest.slice(7)
3554
- }],
3555
- candidate: { number: run.run_number }
3556
- });
3557
- if (candidates.length === 10) return candidates;
3524
+ function candidateCatalog(result) {
3525
+ if (result?.schema !== 1 || !Array.isArray(result.candidates) || result.candidates.length > 10) {
3526
+ throw new Error("Invalid Boo candidate catalog.");
3527
+ }
3528
+ return result.candidates.map((candidate) => {
3529
+ const profile = candidate?.profile;
3530
+ const image = candidate?.image;
3531
+ const name2 = `boo-${candidate?.version}-candidate-${candidate?.revision?.slice(0, 12)}-x86_64-${profile}.raw.zst`;
3532
+ if (!positive(candidate?.number) || !Number.isFinite(Date.parse(candidate?.timestamp)) || !/^[a-f0-9]{40}$/.test(candidate?.revision) || !profiles.includes(profile) || !semanticVersion.test(candidate?.version) || image?.file !== name2 || image?.prefix !== `candidates/${candidate.revision}/${profile}` || !positive(image?.bytes) || !sha256.test(image?.sha256) || !positive(image?.raw_bytes) || !sha256.test(image?.raw_sha256)) {
3533
+ throw new Error("Invalid Boo candidate metadata.");
3558
3534
  }
3559
- }
3560
- return candidates;
3535
+ return {
3536
+ run: candidate.number,
3537
+ version: candidate.version,
3538
+ revision: candidate.revision,
3539
+ timestamp: new Date(candidate.timestamp).toISOString(),
3540
+ candidate: { number: candidate.number },
3541
+ images: [{
3542
+ profile,
3543
+ id: candidate.number,
3544
+ name: name2,
3545
+ bytes: image.bytes,
3546
+ digest: image.sha256,
3547
+ cacheKind: "raw.zst",
3548
+ url: `${releaseOrigin}/${image.prefix}/${name2}`,
3549
+ rawBytes: image.raw_bytes,
3550
+ rawDigest: image.raw_sha256
3551
+ }]
3552
+ };
3553
+ });
3554
+ }
3555
+ async function discoverCandidates(signal) {
3556
+ return candidateCatalog(JSON.parse(await command([
3557
+ "curl",
3558
+ "--fail",
3559
+ "--silent",
3560
+ "--show-error",
3561
+ "--proto",
3562
+ "=https",
3563
+ "--header",
3564
+ "Cache-Control: no-cache",
3565
+ `${releaseOrigin}/candidates/index.json`
3566
+ ], signal, { limit: 1024 ** 2 })));
3561
3567
  }
3562
3568
  var FlashBackend = class {
3563
- constructor(demo2) {
3564
- this.demo = demo2;
3565
- }
3566
- demo;
3567
- demoInstalled = false;
3568
3569
  requirePlatform() {
3569
3570
  if (process.platform === "darwin") return platform;
3570
3571
  if (process.platform === "linux") return platform2;
3571
- throw new Error("Boo Flash supports macOS and Linux. Use --demo for a non-writing preview.");
3572
+ throw new Error("Boo Flash supports macOS and Linux.");
3572
3573
  }
3573
3574
  get platformLabel() {
3574
- return this.demo ? "DEMO - no disks written" : this.requirePlatform().label;
3575
+ return this.requirePlatform().label;
3575
3576
  }
3576
3577
  get successTitle() {
3577
- return this.demo ? "Demo complete" : this.requirePlatform().successTitle;
3578
+ return this.requirePlatform().successTitle;
3578
3579
  }
3579
3580
  get successDetail() {
3580
- return this.demo ? "Simulated success only. No USB was written or ejected." : this.requirePlatform().successDetail;
3581
+ return this.requirePlatform().successDetail;
3581
3582
  }
3582
3583
  async clearCache(signal) {
3583
- if (this.demo) return "Demo only: no cached images or USB contents were changed.";
3584
3584
  return clearCachedImages(signal);
3585
3585
  }
3586
- dependencies(includeGitHub = false) {
3587
- if (!this.demo) return this.requirePlatform().dependencies(includeGitHub);
3588
- const setup = this.demo.startsWith("setup") || this.demo === "no-homebrew";
3589
- return {
3590
- missing: !setup || this.demoInstalled ? [] : this.demo === "setup-gh" ? includeGitHub ? ["gh"] : [] : this.demo === "setup-zstd" ? ["zstd"] : includeGitHub ? ["gh", "zstd"] : ["zstd"],
3591
- installer: this.demo === "no-homebrew" ? null : "Homebrew",
3592
- guidance: "Demo dependency guidance."
3593
- };
3586
+ dependencies() {
3587
+ return this.requirePlatform().dependencies();
3594
3588
  }
3595
3589
  async installDependencies(approved, signal, terminal) {
3596
- if (!this.demo) {
3597
- return terminal(() => this.requirePlatform().installDependencies(approved, signal));
3598
- }
3599
- await delay(2e3, void 0, { signal });
3600
- return terminal(async () => {
3601
- process.stdout.write("\nDEMO dependency setup: no software is installed.\nSimulating Homebrew terminal handoff. Ctrl-C cancels and quits.\n");
3602
- await delay(3e3);
3603
- if (this.demo === "setup-interrupted") process.stdout.write("Simulated Homebrew interruption result, without signaling Boo Flash. Quitting.\n");
3604
- if (signal.aborted || this.demo === "setup-interrupted") return "cancelled";
3605
- if (this.demo === "setup-error") throw new Error("Simulated Homebrew failure. No software was installed. Resolve the reported issue, then refresh to retry.");
3606
- this.demoInstalled = true;
3607
- return "installed";
3608
- });
3590
+ return terminal(() => this.requirePlatform().installDependencies(approved, signal));
3609
3591
  }
3610
3592
  async catalog(signal) {
3611
- if (!this.demo) return discoverImages(signal);
3612
- await delay(350, void 0, { signal });
3613
- return { run: 149, version: "0.1.0+149", revision: "9b7574859120add5a1a478bc7923aeb1629be4ba", timestamp: new Date(Date.now() - 10 * 60 * 1e3).toISOString(), images: profiles.map((profile, index) => ({
3614
- profile,
3615
- id: index,
3616
- name: `demo-${profile}`,
3617
- bytes: 1083197973,
3618
- digest: "demo"
3619
- })) };
3593
+ return discoverImages(signal);
3620
3594
  }
3621
3595
  async candidates(signal) {
3622
- if (!this.demo) return discoverCandidates(signal);
3623
- const catalog = await this.catalog(signal);
3624
- return this.demo === "empty" ? [] : [{
3625
- ...catalog,
3626
- timestamp: new Date(Date.now() - 5 * 60 * 1e3).toISOString(),
3627
- images: [catalog.images[0]],
3628
- candidate: { number: 1 }
3629
- }];
3596
+ return discoverCandidates(signal);
3630
3597
  }
3631
3598
  async disks(signal, staging = tmpdir4(), unmounted) {
3632
- if (this.demo) {
3633
- await delay(250, void 0, { signal });
3634
- return this.demo === "empty" ? [] : [
3635
- { id: "demo-usb-a", name: "SanDisk Ultra USB 3.0", bytes: 32 * 1024 ** 3, identity: "demo-a" },
3636
- { id: "demo-usb-b", name: "Kingston DataTraveler", bytes: 64 * 1024 ** 3, identity: "demo-b" }
3637
- ];
3638
- }
3639
3599
  if (process.getuid?.() === 0) throw new Error(`Run Boo Flash on ${this.requirePlatform().id === "macos" ? "macOS" : "Linux"} as your normal user, not with sudo.`);
3640
3600
  return this.requirePlatform().disks(signal, moduleDirectory, staging, unmounted);
3641
3601
  }
3642
3602
  async flash(catalog, image, disk, signal, update, authorize2) {
3643
- if (this.demo) {
3644
- if (!disk.id.startsWith("demo-") || image.digest !== "demo") throw new Error("Invalid demo selection.");
3645
- for (const stage of ["Downloading", "Verifying", "Unmounting", "Writing", "Flushing", "Ejecting"]) {
3646
- const total = stage === "Downloading" ? image.bytes : stage === "Writing" ? 8 * 1024 ** 3 : void 0;
3647
- for (let i = 0; i <= (total ? 30 : 5); i++) {
3648
- signal.throwIfAborted();
3649
- update({
3650
- stage,
3651
- detail: "Simulated activity. No disk commands are run.",
3652
- total,
3653
- bytes: total ? Math.round(total * i / 30) : void 0,
3654
- speed: total ? total / 3 : void 0
3655
- });
3656
- await delay(100, void 0, { signal });
3657
- if (this.demo === "error" && stage === "Writing" && i === 13) throw new Error("Demo write failure. No disks were written or ejected. Refresh to choose again.");
3658
- }
3659
- }
3660
- return;
3661
- }
3662
3603
  const platform3 = this.requirePlatform();
3663
3604
  if (process.getuid?.() === 0) throw new Error(`Run Boo Flash on ${platform3.id === "macos" ? "macOS" : "Linux"} as your normal user, not with sudo.`);
3664
3605
  if (!diskID3.test(disk.id) || !catalog.images.includes(image) || !profiles.includes(image.profile) || !positive(image.id) || !positive(image.bytes) || !sha256.test(image.digest)) throw new Error("Invalid flash selection.");
3665
3606
  if (catalog.candidate) {
3666
- if (!positive(catalog.run) || !/^[a-f0-9]{40}$/.test(catalog.revision) || image.cacheKind || image.name !== `boo-${image.profile}-candidate-${catalog.revision}`) throw new Error("Invalid candidate selection.");
3667
- const artifact = JSON.parse(await command(["gh", "api", "--hostname", "github.com", `repos/${repository}/actions/artifacts/${image.id}`], signal));
3668
- if (artifact.id !== image.id || artifact.name !== image.name || artifact.workflow_run?.id !== catalog.run || artifact.expired !== false || !(Date.parse(artifact.expires_at) > Date.now()) || artifact.size_in_bytes !== image.bytes || artifact.digest !== `sha256:${image.digest}`) {
3669
- throw new Error("Selected candidate artifact changed or expired. Refresh Candidates. No disk was written.");
3670
- }
3607
+ if (!positive(catalog.run) || !/^[a-f0-9]{40}$/.test(catalog.revision) || image.id !== catalog.run || image.cacheKind !== "raw.zst" || image.name !== `boo-${catalog.version}-candidate-${catalog.revision.slice(0, 12)}-x86_64-${image.profile}.raw.zst` || image.url !== `${releaseOrigin}/candidates/${catalog.revision}/${image.profile}/${image.name}` || !positive(image.rawBytes) || !sha256.test(image.rawDigest ?? "")) throw new Error("Invalid candidate selection.");
3671
3608
  } else {
3672
3609
  const build = Number(catalog.version?.slice(catalog.version.lastIndexOf("+") + 1));
3673
3610
  const name2 = `boo-${catalog.version}-x86_64-${image.profile}.raw.zst`;
3674
3611
  if (!releaseVersion.test(catalog.version ?? "") || !positive(build) || catalog.run !== build || image.id !== build || !/^[a-f0-9]{40}$/.test(catalog.revision) || image.name !== name2 || image.cacheKind !== "raw.zst" || image.url !== `${releaseOrigin}/releases/${catalog.version}/${name2}` || !positive(image.rawBytes) || !sha256.test(image.rawDigest ?? "")) throw new Error("Invalid Boo release selection.");
3675
3612
  }
3676
3613
  const staging = await mkdtemp(join5(tmpdir4(), "boo-flash-"));
3677
- const payload = join5(staging, catalog.candidate ? "image.zip" : "image.raw.zst");
3614
+ const payload = join5(staging, "image.raw.zst");
3678
3615
  let unretained = false;
3679
3616
  let destructive = false;
3680
3617
  try {
@@ -3682,17 +3619,17 @@ var FlashBackend = class {
3682
3619
  let count = 0;
3683
3620
  if (!await reuseArchive(image, payload, signal, (count2) => update({
3684
3621
  stage: "Using cached image",
3685
- detail: `Verifying cached ${catalog.candidate ? "GitHub ZIP" : `Boo ${catalog.version} image`} size and SHA-256; no download.`,
3622
+ detail: `Verifying cached ${catalog.candidate ? "Boo candidate" : `Boo ${catalog.version} image`} size and SHA-256; no download.`,
3686
3623
  bytes: count2,
3687
3624
  total: image.bytes
3688
3625
  }))) {
3689
3626
  const payloadFile = await open2(payload, "wx", 384);
3690
3627
  const payloadHash = createHash2("sha256");
3691
3628
  const started = performance.now();
3692
- const detail = catalog.candidate ? "Selected candidate artifact only; authenticated with GitHub." : `Boo ${catalog.version} from images.os.boo.`;
3629
+ const detail = catalog.candidate ? "Selected candidate from images.os.boo." : `Boo ${catalog.version} from images.os.boo.`;
3693
3630
  update({ stage: "Downloading", detail, bytes: 0, total: image.bytes });
3694
3631
  try {
3695
- const argv = catalog.candidate ? ["gh", "api", "--hostname", "github.com", `repos/${repository}/actions/artifacts/${image.id}/zip`] : ["curl", "--fail", "--silent", "--show-error", "--proto", "=https", image.url];
3632
+ const argv = ["curl", "--fail", "--silent", "--show-error", "--proto", "=https", image.url];
3696
3633
  await command(argv, signal, {
3697
3634
  limit: image.bytes,
3698
3635
  consume: async (chunk) => {
@@ -3712,60 +3649,17 @@ var FlashBackend = class {
3712
3649
  await payloadFile.close();
3713
3650
  }
3714
3651
  if (count !== image.bytes || payloadHash.digest("hex") !== image.digest) {
3715
- throw new Error(`${catalog.candidate ? "GitHub ZIP" : "Release image"} size or SHA-256 mismatch. No disk was written.`);
3652
+ throw new Error(`${catalog.candidate ? "Candidate" : "Release"} image size or SHA-256 mismatch. No disk was written.`);
3716
3653
  }
3717
3654
  unretained = true;
3718
3655
  await publishArchive(image, payload, signal);
3719
3656
  unretained = false;
3720
3657
  }
3721
3658
  signal.throwIfAborted();
3722
- let compressed = payload;
3723
- let rawBytes = image.rawBytes;
3724
- let rawDigest = image.rawDigest;
3725
- if (catalog.candidate) {
3726
- update({ stage: "Verifying", detail: "Checking GitHub ZIP digest, build identity and image checksums." });
3727
- const names = (await command(["unzip", "-Z1", payload], signal)).trim().split("\n");
3728
- for (const name2 of ["build-info.json", "SHA256SUMS", "SHA256SUMS.raw"]) {
3729
- if (names.filter((n) => n === name2).length !== 1) throw new Error(`Archive is missing a unique ${name2}.`);
3730
- }
3731
- const extractText = (name2) => command(["unzip", "-p", payload, name2], signal, { limit: 1024 ** 2 });
3732
- const metadata = JSON.parse(await extractText("build-info.json"));
3733
- const basename2 = `boo-${image.profile}-x86_64.raw.zst`;
3734
- if (metadata.semantic_version !== void 0 && (!semanticVersion.test(metadata.semantic_version) || metadata.build_number !== null || metadata.version !== metadata.semantic_version)) {
3735
- throw new Error("Build metadata has an invalid candidate image version.");
3736
- }
3737
- if (metadata.image !== basename2 || names.filter((name2) => name2 === basename2).length !== 1 || metadata.schema !== 1 || !repositoryIdentities.has(metadata.repository) || metadata.revision !== catalog.revision || metadata.variant !== image.profile || metadata.source_state !== "clean" || !positive(metadata.compressed_bytes) || metadata.compressed_bytes > image.bytes || !positive(metadata.raw_apparent_bytes) || !sha256.test(metadata.compressed_sha256) || !sha256.test(metadata.raw_sha256)) {
3738
- throw new Error("Build metadata does not match the selected candidate.");
3739
- }
3740
- if (metadata.candidate?.resolved_sha !== catalog.revision || metadata.candidate?.profile !== image.profile || metadata.candidate?.qualification !== "build-only" || typeof metadata.candidate?.source_ref !== "string" || !metadata.candidate.source_ref.trim()) {
3741
- throw new Error("Candidate source metadata does not match the selected artifact.");
3742
- }
3743
- if ((await extractText("SHA256SUMS")).trim() !== `${metadata.compressed_sha256} ${basename2}` || (await extractText("SHA256SUMS.raw")).trim() !== `${metadata.raw_sha256} ${basename2.slice(0, -4)}`) {
3744
- throw new Error("Image checksum manifests disagree with build metadata.");
3745
- }
3746
- compressed = join5(staging, basename2);
3747
- const file = await open2(compressed, "wx", 384);
3748
- const hash = createHash2("sha256");
3749
- count = 0;
3750
- try {
3751
- await command(["unzip", "-p", payload, basename2], signal, {
3752
- limit: metadata.compressed_bytes,
3753
- consume: async (chunk) => {
3754
- hash.update(chunk);
3755
- await file.writeFile(chunk);
3756
- count += chunk.length;
3757
- update({ stage: "Verifying", detail: "Extracting only the selected image; checking compressed SHA-256.", bytes: count, total: metadata.compressed_bytes });
3758
- }
3759
- });
3760
- } finally {
3761
- await file.close();
3762
- }
3763
- if (count !== metadata.compressed_bytes || hash.digest("hex") !== metadata.compressed_sha256) throw new Error("Compressed image size or SHA-256 mismatch.");
3764
- rawBytes = metadata.raw_apparent_bytes;
3765
- rawDigest = metadata.raw_sha256;
3766
- } else {
3767
- update({ stage: "Verifying", detail: `Boo ${catalog.version} release metadata and compressed SHA-256 verified.` });
3768
- }
3659
+ const compressed = payload;
3660
+ const rawBytes = image.rawBytes;
3661
+ const rawDigest = image.rawDigest;
3662
+ update({ stage: "Verifying", detail: `${catalog.candidate ? "Boo candidate" : `Boo ${catalog.version}`} metadata and compressed SHA-256 verified.` });
3769
3663
  if (!positive(rawBytes) || !sha256.test(rawDigest ?? "")) throw new Error("Image raw-stream metadata is invalid.");
3770
3664
  if (rawBytes > disk.bytes) throw new Error(`Image needs ${bytes(rawBytes)}; the selected USB is too small.`);
3771
3665
  const revalidate = async (unmounted = false) => {
@@ -3830,7 +3724,6 @@ var FlashBackend = class {
3830
3724
  }
3831
3725
  }
3832
3726
  async authorize(signal) {
3833
- if (this.demo) return;
3834
3727
  if (process.getuid?.() === 0) throw new Error("Run Boo Flash as your normal user, not with sudo.");
3835
3728
  await this.requirePlatform().authorize(signal);
3836
3729
  }
@@ -3885,7 +3778,12 @@ function App(props) {
3885
3778
  const visible = () => dimensions().width >= 40 && dimensions().height >= 16;
3886
3779
  const options = () => screen() === "profile" ? 5 : screen() === "cleared" ? 1 : screen() === "candidates" ? Math.max(1, candidates().length) : screen() === "disks" ? disks3().length : 2;
3887
3780
  const percent = () => progress().total ? Math.min(100, Math.floor((progress().bytes ?? 0) / progress().total * 100)) : void 0;
3888
- const path = (d) => props.backend.demo ? d.id : `/dev/${d.id}`;
3781
+ const path = (d) => `/dev/${d.id}`;
3782
+ const selection = () => {
3783
+ const c = catalog(), i = image();
3784
+ if (!c || !i) return "";
3785
+ return `Boo ${c.version}${c.candidate ? ` candidate #${c.candidate.number}` : ""} / ${i.profile.toUpperCase()}${c.candidate ? ` / ${c.revision.slice(0, 7)}` : ""}`;
3786
+ };
3889
3787
  const operation = (work) => {
3890
3788
  controller = new AbortController();
3891
3789
  setStopping(false);
@@ -3896,8 +3794,8 @@ function App(props) {
3896
3794
  } catch (e) {
3897
3795
  if (!closing) {
3898
3796
  const setup = screen() === "installing";
3899
- setError(controller.signal.aborted && setup ? "Setup cancelled. Refresh to check what is still missing." : controller.signal.aborted && props.backend.demo ? "Demo cancelled. No disks were written or ejected." : e instanceof Error ? e.message : "Operation failed. Refresh and try again.");
3900
- if (setup) setDependencies(props.backend.dependencies(setupCandidates()));
3797
+ setError(controller.signal.aborted && setup ? "Setup cancelled. Refresh to check what is still missing." : e instanceof Error ? e.message : "Operation failed. Refresh and try again.");
3798
+ if (setup) setDependencies(props.backend.dependencies());
3901
3799
  setScreen(setup ? "setup" : "error");
3902
3800
  setCursor(0);
3903
3801
  }
@@ -3921,6 +3819,7 @@ function App(props) {
3921
3819
  const refresh = () => {
3922
3820
  setError("");
3923
3821
  setCatalog(void 0);
3822
+ setImage(void 0);
3924
3823
  setDisk(void 0);
3925
3824
  setScreen("profile");
3926
3825
  setCursor(0);
@@ -3932,7 +3831,7 @@ function App(props) {
3932
3831
  setCursor(0);
3933
3832
  setScreen("candidates");
3934
3833
  operation(async (signal) => {
3935
- const state = props.backend.dependencies(true);
3834
+ const state = props.backend.dependencies();
3936
3835
  if (state.missing.length) {
3937
3836
  setDependencies(state);
3938
3837
  setScreen("setup");
@@ -3950,6 +3849,7 @@ function App(props) {
3950
3849
  if (state.missing.length) {
3951
3850
  setDependencies(state);
3952
3851
  setCatalog(void 0);
3852
+ setImage(void 0);
3953
3853
  setScreen("setup");
3954
3854
  setCursor(0);
3955
3855
  return;
@@ -4053,6 +3953,9 @@ function App(props) {
4053
3953
  await props.backend.flash(catalog(), image(), disk(), signal, setProgress, async () => {
4054
3954
  renderer.suspend();
4055
3955
  try {
3956
+ process.stdout.write(`
3957
+ ${selection()}
3958
+ `);
4056
3959
  await props.backend.authorize(signal);
4057
3960
  } finally {
4058
3961
  renderer.resume();
@@ -4134,7 +4037,7 @@ function App(props) {
4134
4037
  return _el$;
4135
4038
  })();
4136
4039
  return (() => {
4137
- var _el$4 = createElement("box"), _el$5 = createElement("box"), _el$6 = createElement("box"), _el$7 = createElement("text"), _el$9 = createElement("text"), _el$0 = createElement("box"), _el$1 = createElement("box"), _el$87 = createElement("text");
4040
+ var _el$4 = createElement("box"), _el$5 = createElement("box"), _el$6 = createElement("box"), _el$7 = createElement("text"), _el$9 = createElement("text"), _el$0 = createElement("box"), _el$1 = createElement("box"), _el$80 = createElement("text");
4138
4041
  insertNode(_el$4, _el$5);
4139
4042
  setProp(_el$4, "width", "100%");
4140
4043
  setProp(_el$4, "height", "100%");
@@ -4143,7 +4046,7 @@ function App(props) {
4143
4046
  insertNode(_el$5, _el$6);
4144
4047
  insertNode(_el$5, _el$0);
4145
4048
  insertNode(_el$5, _el$1);
4146
- insertNode(_el$5, _el$87);
4049
+ insertNode(_el$5, _el$80);
4147
4050
  setProp(_el$5, "border", true);
4148
4051
  setProp(_el$5, "borderStyle", "single");
4149
4052
  setProp(_el$5, "flexDirection", "column");
@@ -4167,56 +4070,67 @@ function App(props) {
4167
4070
  },
4168
4071
  get fallback() {
4169
4072
  return (() => {
4170
- var _el$88 = createElement("text");
4171
- insertNode(_el$88, createTextNode(`Enlarge the terminal to show the full choices and erase warning. Q quits.`));
4172
- effect((_$p) => setProp(_el$88, "fg", palette.warning, _$p));
4173
- return _el$88;
4073
+ var _el$81 = createElement("text");
4074
+ insertNode(_el$81, createTextNode(`Enlarge the terminal to show the full choices and erase warning. Q quits.`));
4075
+ effect((_$p) => setProp(_el$81, "fg", palette.warning, _$p));
4076
+ return _el$81;
4174
4077
  })();
4175
4078
  },
4176
4079
  get children() {
4177
4080
  return [createComponent2(Show, {
4081
+ get when() {
4082
+ return selection();
4083
+ },
4084
+ get children() {
4085
+ var _el$10 = createElement("text");
4086
+ setProp(_el$10, "flexShrink", 0);
4087
+ insert(_el$10, selection);
4088
+ effect((_$p) => setProp(_el$10, "fg", palette.foreground, _$p));
4089
+ return _el$10;
4090
+ }
4091
+ }), createComponent2(Show, {
4178
4092
  get when() {
4179
4093
  return screen() === "profile";
4180
4094
  },
4181
4095
  get children() {
4182
4096
  return [(() => {
4183
- var _el$10 = createElement("text");
4184
- insertNode(_el$10, createTextNode(`Choose the target computer's GPU`));
4097
+ var _el$11 = createElement("text");
4098
+ insertNode(_el$11, createTextNode(`Choose the target computer's GPU`));
4185
4099
  effect((_p$) => {
4186
4100
  var _v$5 = palette.foreground, _v$6 = TextAttributes2.BOLD;
4187
- _v$5 !== _p$.e && (_p$.e = setProp(_el$10, "fg", _v$5, _p$.e));
4188
- _v$6 !== _p$.t && (_p$.t = setProp(_el$10, "attributes", _v$6, _p$.t));
4101
+ _v$5 !== _p$.e && (_p$.e = setProp(_el$11, "fg", _v$5, _p$.e));
4102
+ _v$6 !== _p$.t && (_p$.t = setProp(_el$11, "attributes", _v$6, _p$.t));
4189
4103
  return _p$;
4190
4104
  }, {
4191
4105
  e: void 0,
4192
4106
  t: void 0
4193
4107
  });
4194
- return _el$10;
4108
+ return _el$11;
4195
4109
  })(), (() => {
4196
- var _el$12 = createElement("text");
4197
- insert(_el$12, (() => {
4110
+ var _el$13 = createElement("text");
4111
+ insert(_el$13, (() => {
4198
4112
  var _c$ = memo2(() => !!loading());
4199
4113
  return () => _c$() ? `${spinner()} Finding latest Boo release...` : memo2(() => !!catalog())() ? `Boo ${catalog().version} / ${catalog().revision.slice(0, 7)} / ${imageTime(catalog().timestamp, now())}` : "Dependencies needed. Enter to set up.";
4200
4114
  })());
4201
- effect((_$p) => setProp(_el$12, "fg", palette.muted, _$p));
4202
- return _el$12;
4115
+ effect((_$p) => setProp(_el$13, "fg", palette.muted, _$p));
4116
+ return _el$13;
4203
4117
  })(), (() => {
4204
- var _el$13 = createElement("box");
4205
- setProp(_el$13, "gap", 0);
4206
- setProp(_el$13, "marginBottom", 1);
4207
- setProp(_el$13, "flexShrink", 0);
4208
- insert(_el$13, createComponent2(For, {
4118
+ var _el$14 = createElement("box");
4119
+ setProp(_el$14, "gap", 0);
4120
+ setProp(_el$14, "marginBottom", 1);
4121
+ setProp(_el$14, "flexShrink", 0);
4122
+ insert(_el$14, createComponent2(For, {
4209
4123
  each: profiles,
4210
4124
  children: (profile, index) => row(profile.toUpperCase(), profile === "nvidia" ? "NVIDIA graphics" : `${profile === "intel" ? "Intel" : "AMD Radeon"} graphics`, index())
4211
4125
  }), null);
4212
- insert(_el$13, () => row("Candidates", "", 3), null);
4213
- insert(_el$13, () => row("Clear cached images", "", 4), null);
4214
- return _el$13;
4215
- })(), (() => {
4216
- var _el$14 = createElement("text");
4217
- insertNode(_el$14, createTextNode(`All images support Intel and AMD x86_64 CPUs.`));
4218
- effect((_$p) => setProp(_el$14, "fg", palette.muted, _$p));
4126
+ insert(_el$14, () => row("Candidates", "", 3), null);
4127
+ insert(_el$14, () => row("Clear cached images", "", 4), null);
4219
4128
  return _el$14;
4129
+ })(), (() => {
4130
+ var _el$15 = createElement("text");
4131
+ insertNode(_el$15, createTextNode(`All images support Intel and AMD x86_64 CPUs.`));
4132
+ effect((_$p) => setProp(_el$15, "fg", palette.muted, _$p));
4133
+ return _el$15;
4220
4134
  })()];
4221
4135
  }
4222
4136
  }), createComponent2(Show, {
@@ -4225,34 +4139,34 @@ function App(props) {
4225
4139
  },
4226
4140
  get children() {
4227
4141
  return [(() => {
4228
- var _el$16 = createElement("text");
4229
- insertNode(_el$16, createTextNode(`Clear cached images?`));
4142
+ var _el$17 = createElement("text");
4143
+ insertNode(_el$17, createTextNode(`Clear cached images?`));
4230
4144
  effect((_p$) => {
4231
4145
  var _v$7 = palette.warning, _v$8 = TextAttributes2.BOLD;
4232
- _v$7 !== _p$.e && (_p$.e = setProp(_el$16, "fg", _v$7, _p$.e));
4233
- _v$8 !== _p$.t && (_p$.t = setProp(_el$16, "attributes", _v$8, _p$.t));
4146
+ _v$7 !== _p$.e && (_p$.e = setProp(_el$17, "fg", _v$7, _p$.e));
4147
+ _v$8 !== _p$.t && (_p$.t = setProp(_el$17, "attributes", _v$8, _p$.t));
4234
4148
  return _p$;
4235
4149
  }, {
4236
4150
  e: void 0,
4237
4151
  t: void 0
4238
4152
  });
4239
- return _el$16;
4153
+ return _el$17;
4240
4154
  })(), (() => {
4241
- var _el$18 = createElement("text");
4242
- insertNode(_el$18, createTextNode(`Removes downloaded archives only, NOT USB contents. Active flashes are unaffected.`));
4243
- effect((_$p) => setProp(_el$18, "fg", palette.foreground, _$p));
4244
- return _el$18;
4155
+ var _el$19 = createElement("text");
4156
+ insertNode(_el$19, createTextNode(`Removes downloaded archives only, NOT USB contents. Active flashes are unaffected.`));
4157
+ effect((_$p) => setProp(_el$19, "fg", palette.foreground, _$p));
4158
+ return _el$19;
4245
4159
  })(), (() => {
4246
- var _el$20 = createElement("text");
4247
- insertNode(_el$20, createTextNode(`Images will download again when needed. Pending downloads may still finish and be cached.`));
4248
- effect((_$p) => setProp(_el$20, "fg", palette.muted, _$p));
4249
- return _el$20;
4160
+ var _el$21 = createElement("text");
4161
+ insertNode(_el$21, createTextNode(`Images will download again when needed. Pending downloads may still finish and be cached.`));
4162
+ effect((_$p) => setProp(_el$21, "fg", palette.muted, _$p));
4163
+ return _el$21;
4250
4164
  })(), (() => {
4251
- var _el$22 = createElement("box");
4252
- insert(_el$22, () => row("Go back", "", 0), null);
4253
- insert(_el$22, () => row(loading() ? "Clearing..." : "Clear cached images", "", 1, true), null);
4254
- effect((_$p) => setProp(_el$22, "gap", tight() ? 0 : 1, _$p));
4255
- return _el$22;
4165
+ var _el$23 = createElement("box");
4166
+ insert(_el$23, () => row("Go back", "", 0), null);
4167
+ insert(_el$23, () => row(loading() ? "Clearing..." : "Clear cached images", "", 1, true), null);
4168
+ effect((_$p) => setProp(_el$23, "gap", tight() ? 0 : 1, _$p));
4169
+ return _el$23;
4256
4170
  })()];
4257
4171
  }
4258
4172
  }), createComponent2(Show, {
@@ -4261,23 +4175,23 @@ function App(props) {
4261
4175
  },
4262
4176
  get children() {
4263
4177
  return [(() => {
4264
- var _el$23 = createElement("text");
4265
- insertNode(_el$23, createTextNode(`Cache cleared`));
4178
+ var _el$24 = createElement("text");
4179
+ insertNode(_el$24, createTextNode(`Cache cleared`));
4266
4180
  effect((_p$) => {
4267
4181
  var _v$9 = palette.success, _v$0 = TextAttributes2.BOLD;
4268
- _v$9 !== _p$.e && (_p$.e = setProp(_el$23, "fg", _v$9, _p$.e));
4269
- _v$0 !== _p$.t && (_p$.t = setProp(_el$23, "attributes", _v$0, _p$.t));
4182
+ _v$9 !== _p$.e && (_p$.e = setProp(_el$24, "fg", _v$9, _p$.e));
4183
+ _v$0 !== _p$.t && (_p$.t = setProp(_el$24, "attributes", _v$0, _p$.t));
4270
4184
  return _p$;
4271
4185
  }, {
4272
4186
  e: void 0,
4273
4187
  t: void 0
4274
4188
  });
4275
- return _el$23;
4189
+ return _el$24;
4276
4190
  })(), (() => {
4277
- var _el$25 = createElement("text");
4278
- insert(_el$25, notice);
4279
- effect((_$p) => setProp(_el$25, "fg", palette.foreground, _$p));
4280
- return _el$25;
4191
+ var _el$26 = createElement("text");
4192
+ insert(_el$26, notice);
4193
+ effect((_$p) => setProp(_el$26, "fg", palette.foreground, _$p));
4194
+ return _el$26;
4281
4195
  })(), memo2(() => row("Go back", "", 0))];
4282
4196
  }
4283
4197
  }), createComponent2(Show, {
@@ -4286,34 +4200,29 @@ function App(props) {
4286
4200
  },
4287
4201
  get children() {
4288
4202
  return [(() => {
4289
- var _el$26 = createElement("text");
4290
- insertNode(_el$26, createTextNode(`Candidates`));
4203
+ var _el$27 = createElement("text");
4204
+ insertNode(_el$27, createTextNode(`Candidates`));
4291
4205
  effect((_p$) => {
4292
4206
  var _v$1 = palette.foreground, _v$10 = TextAttributes2.BOLD;
4293
- _v$1 !== _p$.e && (_p$.e = setProp(_el$26, "fg", _v$1, _p$.e));
4294
- _v$10 !== _p$.t && (_p$.t = setProp(_el$26, "attributes", _v$10, _p$.t));
4207
+ _v$1 !== _p$.e && (_p$.e = setProp(_el$27, "fg", _v$1, _p$.e));
4208
+ _v$10 !== _p$.t && (_p$.t = setProp(_el$27, "attributes", _v$10, _p$.t));
4295
4209
  return _p$;
4296
4210
  }, {
4297
4211
  e: void 0,
4298
4212
  t: void 0
4299
4213
  });
4300
- return _el$26;
4301
- })(), (() => {
4302
- var _el$28 = createElement("text");
4303
- insertNode(_el$28, createTextNode(`Build-only; not boot/install/hardware qualified.`));
4304
- effect((_$p) => setProp(_el$28, "fg", palette.warning, _$p));
4305
- return _el$28;
4214
+ return _el$27;
4306
4215
  })(), createComponent2(Show, {
4307
4216
  get when() {
4308
4217
  return !loading();
4309
4218
  },
4310
4219
  get fallback() {
4311
4220
  return (() => {
4312
- var _el$90 = createElement("text"), _el$91 = createTextNode(` Finding completed candidates...`);
4313
- insertNode(_el$90, _el$91);
4314
- insert(_el$90, spinner, _el$91);
4315
- effect((_$p) => setProp(_el$90, "fg", palette.muted, _$p));
4316
- return _el$90;
4221
+ var _el$83 = createElement("text"), _el$84 = createTextNode(` Finding completed candidates...`);
4222
+ insertNode(_el$83, _el$84);
4223
+ insert(_el$83, spinner, _el$84);
4224
+ effect((_$p) => setProp(_el$83, "fg", palette.muted, _$p));
4225
+ return _el$83;
4317
4226
  })();
4318
4227
  },
4319
4228
  get children() {
@@ -4323,31 +4232,31 @@ function App(props) {
4323
4232
  },
4324
4233
  get fallback() {
4325
4234
  return [(() => {
4326
- var _el$92 = createElement("text");
4327
- insertNode(_el$92, createTextNode(`No completed candidates available`));
4328
- effect((_$p) => setProp(_el$92, "fg", palette.muted, _$p));
4329
- return _el$92;
4235
+ var _el$85 = createElement("text");
4236
+ insertNode(_el$85, createTextNode(`No completed candidates available`));
4237
+ effect((_$p) => setProp(_el$85, "fg", palette.muted, _$p));
4238
+ return _el$85;
4330
4239
  })(), memo2(() => row("Go back", "", 0))];
4331
4240
  },
4332
4241
  get children() {
4333
4242
  return [(() => {
4334
- var _el$30 = createElement("box");
4335
- setProp(_el$30, "gap", 0);
4336
- insert(_el$30, createComponent2(For, {
4243
+ var _el$29 = createElement("box");
4244
+ setProp(_el$29, "gap", 0);
4245
+ insert(_el$29, createComponent2(For, {
4337
4246
  get each() {
4338
4247
  return candidates().slice(Math.max(0, cursor() - 2), Math.max(3, cursor() + 1));
4339
4248
  },
4340
4249
  children: (c, index) => row(`${c.images[0].profile.toUpperCase()} ${c.revision.slice(0, 7)} #${c.candidate.number}`, imageTime(c.timestamp, now()), index() + Math.max(0, cursor() - 2), false, true)
4341
4250
  }));
4342
- return _el$30;
4251
+ return _el$29;
4343
4252
  })(), (() => {
4344
- var _el$31 = createElement("text"), _el$32 = createTextNode(` of `), _el$33 = createTextNode(` candidates / Enter to choose`);
4345
- insertNode(_el$31, _el$32);
4346
- insertNode(_el$31, _el$33);
4347
- insert(_el$31, () => cursor() + 1, _el$32);
4348
- insert(_el$31, () => candidates().length, _el$33);
4349
- effect((_$p) => setProp(_el$31, "fg", palette.muted, _$p));
4350
- return _el$31;
4253
+ var _el$30 = createElement("text"), _el$31 = createTextNode(` of `), _el$32 = createTextNode(` candidates / Enter to choose`);
4254
+ insertNode(_el$30, _el$31);
4255
+ insertNode(_el$30, _el$32);
4256
+ insert(_el$30, () => cursor() + 1, _el$31);
4257
+ insert(_el$30, () => candidates().length, _el$32);
4258
+ effect((_$p) => setProp(_el$30, "fg", palette.muted, _$p));
4259
+ return _el$30;
4351
4260
  })()];
4352
4261
  }
4353
4262
  });
@@ -4360,71 +4269,61 @@ function App(props) {
4360
4269
  },
4361
4270
  get children() {
4362
4271
  return [(() => {
4363
- var _el$34 = createElement("text");
4364
- insertNode(_el$34, createTextNode(`Set up required tools`));
4272
+ var _el$33 = createElement("text");
4273
+ insertNode(_el$33, createTextNode(`Set up required tools`));
4365
4274
  effect((_p$) => {
4366
4275
  var _v$11 = palette.accent, _v$12 = TextAttributes2.BOLD;
4367
- _v$11 !== _p$.e && (_p$.e = setProp(_el$34, "fg", _v$11, _p$.e));
4368
- _v$12 !== _p$.t && (_p$.t = setProp(_el$34, "attributes", _v$12, _p$.t));
4276
+ _v$11 !== _p$.e && (_p$.e = setProp(_el$33, "fg", _v$11, _p$.e));
4277
+ _v$12 !== _p$.t && (_p$.t = setProp(_el$33, "attributes", _v$12, _p$.t));
4369
4278
  return _p$;
4370
4279
  }, {
4371
4280
  e: void 0,
4372
4281
  t: void 0
4373
4282
  });
4374
- return _el$34;
4283
+ return _el$33;
4375
4284
  })(), (() => {
4376
- var _el$36 = createElement("text"), _el$37 = createTextNode(`Missing: `);
4377
- insertNode(_el$36, _el$37);
4378
- insert(_el$36, () => dependencies3().missing.join(", "), null);
4379
- effect((_$p) => setProp(_el$36, "fg", palette.foreground, _$p));
4380
- return _el$36;
4285
+ var _el$35 = createElement("text"), _el$36 = createTextNode(`Missing: `);
4286
+ insertNode(_el$35, _el$36);
4287
+ insert(_el$35, () => dependencies3().missing.join(", "), null);
4288
+ effect((_$p) => setProp(_el$35, "fg", palette.foreground, _$p));
4289
+ return _el$35;
4381
4290
  })(), createComponent2(Show, {
4382
4291
  get when() {
4383
4292
  return !error();
4384
4293
  },
4385
4294
  get fallback() {
4386
4295
  return (() => {
4387
- var _el$94 = createElement("text");
4388
- insert(_el$94, error);
4389
- effect((_$p) => setProp(_el$94, "fg", palette.warning, _$p));
4390
- return _el$94;
4296
+ var _el$87 = createElement("text");
4297
+ insert(_el$87, error);
4298
+ effect((_$p) => setProp(_el$87, "fg", palette.warning, _$p));
4299
+ return _el$87;
4391
4300
  })();
4392
4301
  },
4393
4302
  get children() {
4394
- var _el$38 = createElement("text");
4395
- insert(_el$38, (() => {
4303
+ var _el$37 = createElement("text");
4304
+ insert(_el$37, (() => {
4396
4305
  var _c$2 = memo2(() => !!dependencies3().installer);
4397
4306
  return () => _c$2() ? `${dependencies3().installer} will install ${dependencies3().missing.join(" and ")} and required dependencies, then continue.` : dependencies3().guidance;
4398
4307
  })());
4399
- effect((_$p) => setProp(_el$38, "fg", palette.muted, _$p));
4400
- return _el$38;
4308
+ effect((_$p) => setProp(_el$37, "fg", palette.muted, _$p));
4309
+ return _el$37;
4401
4310
  }
4402
4311
  }), createComponent2(Show, {
4403
4312
  get when() {
4404
4313
  return !tight();
4405
4314
  },
4406
4315
  get children() {
4407
- var _el$39 = createElement("text");
4408
- insert(_el$39, () => dependencies3().installer ? "Uses the ordinary terminal. Ctrl-C cancels and quits." : "Boo Flash will not change system packages on this platform.");
4409
- effect((_$p) => setProp(_el$39, "fg", palette.muted, _$p));
4410
- return _el$39;
4411
- }
4412
- }), createComponent2(Show, {
4413
- get when() {
4414
- return props.backend.demo;
4415
- },
4416
- get children() {
4417
- var _el$40 = createElement("text");
4418
- insertNode(_el$40, createTextNode(`Setup simulation; no software installed.`));
4419
- effect((_$p) => setProp(_el$40, "fg", palette.warning, _$p));
4420
- return _el$40;
4316
+ var _el$38 = createElement("text");
4317
+ insert(_el$38, () => dependencies3().installer ? "Uses the ordinary terminal. Ctrl-C cancels and quits." : "Boo Flash will not change system packages on this platform.");
4318
+ effect((_$p) => setProp(_el$38, "fg", palette.muted, _$p));
4319
+ return _el$38;
4421
4320
  }
4422
4321
  }), (() => {
4423
- var _el$42 = createElement("box");
4424
- insert(_el$42, () => row("Go back", "", 0), null);
4425
- insert(_el$42, () => row(dependencies3().installer ? `Install ${dependencies3().missing.join(" + ")}` : "Refresh", "", 1), null);
4426
- effect((_$p) => setProp(_el$42, "gap", tight() ? 0 : 1, _$p));
4427
- return _el$42;
4322
+ var _el$39 = createElement("box");
4323
+ insert(_el$39, () => row("Go back", "", 0), null);
4324
+ insert(_el$39, () => row(dependencies3().installer ? `Install ${dependencies3().missing.join(" + ")}` : "Refresh", "", 1), null);
4325
+ effect((_$p) => setProp(_el$39, "gap", tight() ? 0 : 1, _$p));
4326
+ return _el$39;
4428
4327
  })()];
4429
4328
  }
4430
4329
  }), createComponent2(Show, {
@@ -4433,44 +4332,34 @@ function App(props) {
4433
4332
  },
4434
4333
  get children() {
4435
4334
  return [(() => {
4436
- var _el$43 = createElement("text"), _el$44 = createTextNode(` `);
4437
- insertNode(_el$43, _el$44);
4438
- insert(_el$43, spinner, _el$44);
4439
- insert(_el$43, () => stopping() ? "Waiting for setup to stop..." : "Installing required tools", null);
4335
+ var _el$40 = createElement("text"), _el$41 = createTextNode(` `);
4336
+ insertNode(_el$40, _el$41);
4337
+ insert(_el$40, spinner, _el$41);
4338
+ insert(_el$40, () => stopping() ? "Waiting for setup to stop..." : "Installing required tools", null);
4440
4339
  effect((_p$) => {
4441
4340
  var _v$13 = palette.accent, _v$14 = TextAttributes2.BOLD;
4442
- _v$13 !== _p$.e && (_p$.e = setProp(_el$43, "fg", _v$13, _p$.e));
4443
- _v$14 !== _p$.t && (_p$.t = setProp(_el$43, "attributes", _v$14, _p$.t));
4341
+ _v$13 !== _p$.e && (_p$.e = setProp(_el$40, "fg", _v$13, _p$.e));
4342
+ _v$14 !== _p$.t && (_p$.t = setProp(_el$40, "attributes", _v$14, _p$.t));
4444
4343
  return _p$;
4445
4344
  }, {
4446
4345
  e: void 0,
4447
4346
  t: void 0
4448
4347
  });
4449
- return _el$43;
4348
+ return _el$40;
4450
4349
  })(), (() => {
4451
- var _el$45 = createElement("text"), _el$46 = createTextNode(` via `);
4452
- insertNode(_el$45, _el$46);
4453
- insert(_el$45, () => dependencies3().missing.join(" + "), _el$46);
4454
- insert(_el$45, () => dependencies3().installer, null);
4455
- effect((_$p) => setProp(_el$45, "fg", palette.foreground, _$p));
4456
- return _el$45;
4350
+ var _el$42 = createElement("text"), _el$43 = createTextNode(` via `);
4351
+ insertNode(_el$42, _el$43);
4352
+ insert(_el$42, () => dependencies3().missing.join(" + "), _el$43);
4353
+ insert(_el$42, () => dependencies3().installer, null);
4354
+ effect((_$p) => setProp(_el$42, "fg", palette.foreground, _$p));
4355
+ return _el$42;
4457
4356
  })(), (() => {
4458
- var _el$47 = createElement("text"), _el$48 = createTextNode(` uses the ordinary terminal. Ctrl-C cancels and quits; wait for cleanup.`);
4459
- insertNode(_el$47, _el$48);
4460
- insert(_el$47, () => dependencies3().installer, _el$48);
4461
- effect((_$p) => setProp(_el$47, "fg", palette.muted, _$p));
4462
- return _el$47;
4463
- })(), createComponent2(Show, {
4464
- get when() {
4465
- return props.backend.demo;
4466
- },
4467
- get children() {
4468
- var _el$49 = createElement("text");
4469
- insertNode(_el$49, createTextNode(`Setup simulation; no software installed.`));
4470
- effect((_$p) => setProp(_el$49, "fg", palette.warning, _$p));
4471
- return _el$49;
4472
- }
4473
- })];
4357
+ var _el$44 = createElement("text"), _el$45 = createTextNode(` uses the ordinary terminal. Ctrl-C cancels and quits; wait for cleanup.`);
4358
+ insertNode(_el$44, _el$45);
4359
+ insert(_el$44, () => dependencies3().installer, _el$45);
4360
+ effect((_$p) => setProp(_el$44, "fg", palette.muted, _$p));
4361
+ return _el$44;
4362
+ })()];
4474
4363
  }
4475
4364
  }), createComponent2(Show, {
4476
4365
  get when() {
@@ -4478,26 +4367,26 @@ function App(props) {
4478
4367
  },
4479
4368
  get children() {
4480
4369
  return [(() => {
4481
- var _el$51 = createElement("text");
4482
- insertNode(_el$51, createTextNode(`Choose a USB drive`));
4370
+ var _el$46 = createElement("text");
4371
+ insertNode(_el$46, createTextNode(`Choose a USB drive`));
4483
4372
  effect((_p$) => {
4484
4373
  var _v$15 = palette.foreground, _v$16 = TextAttributes2.BOLD;
4485
- _v$15 !== _p$.e && (_p$.e = setProp(_el$51, "fg", _v$15, _p$.e));
4486
- _v$16 !== _p$.t && (_p$.t = setProp(_el$51, "attributes", _v$16, _p$.t));
4374
+ _v$15 !== _p$.e && (_p$.e = setProp(_el$46, "fg", _v$15, _p$.e));
4375
+ _v$16 !== _p$.t && (_p$.t = setProp(_el$46, "attributes", _v$16, _p$.t));
4487
4376
  return _p$;
4488
4377
  }, {
4489
4378
  e: void 0,
4490
4379
  t: void 0
4491
4380
  });
4492
- return _el$51;
4381
+ return _el$46;
4493
4382
  })(), (() => {
4494
- var _el$53 = createElement("text");
4495
- insert(_el$53, (() => {
4383
+ var _el$48 = createElement("text");
4384
+ insert(_el$48, (() => {
4496
4385
  var _c$3 = memo2(() => !!loading());
4497
4386
  return () => _c$3() ? `${spinner()} Checking disks and protected storage...` : "System, home and app storage are excluded.";
4498
4387
  })());
4499
- effect((_$p) => setProp(_el$53, "fg", palette.muted, _$p));
4500
- return _el$53;
4388
+ effect((_$p) => setProp(_el$48, "fg", palette.muted, _$p));
4389
+ return _el$48;
4501
4390
  })(), createComponent2(Show, {
4502
4391
  get when() {
4503
4392
  return !loading();
@@ -4509,31 +4398,31 @@ function App(props) {
4509
4398
  },
4510
4399
  get fallback() {
4511
4400
  return (() => {
4512
- var _el$95 = createElement("text");
4513
- insertNode(_el$95, createTextNode(`No eligible USB drives. Connect a writable external USB drive, then press R to refresh.`));
4514
- effect((_$p) => setProp(_el$95, "fg", palette.warning, _$p));
4515
- return _el$95;
4401
+ var _el$88 = createElement("text");
4402
+ insertNode(_el$88, createTextNode(`No eligible USB drives. Connect a writable external USB drive, then press R to refresh.`));
4403
+ effect((_$p) => setProp(_el$88, "fg", palette.warning, _$p));
4404
+ return _el$88;
4516
4405
  })();
4517
4406
  },
4518
4407
  get children() {
4519
4408
  return [(() => {
4520
- var _el$54 = createElement("box");
4521
- setProp(_el$54, "gap", 0);
4522
- insert(_el$54, createComponent2(For, {
4409
+ var _el$49 = createElement("box");
4410
+ setProp(_el$49, "gap", 0);
4411
+ insert(_el$49, createComponent2(For, {
4523
4412
  get each() {
4524
4413
  return disks3().slice(Math.max(0, cursor() - 2), Math.max(3, cursor() + 1));
4525
4414
  },
4526
4415
  children: (d, index) => row(d.name, `${path(d)} / ${bytes(d.bytes)} / USB`, index() + Math.max(0, cursor() - 2))
4527
4416
  }));
4528
- return _el$54;
4417
+ return _el$49;
4529
4418
  })(), (() => {
4530
- var _el$55 = createElement("text");
4531
- insert(_el$55, (() => {
4419
+ var _el$50 = createElement("text");
4420
+ insert(_el$50, (() => {
4532
4421
  var _c$4 = memo2(() => cursor() < 0);
4533
4422
  return () => _c$4() ? "Use arrows to select a drive. Nothing is selected yet." : `${cursor() + 1} of ${disks3().length} drives / Enter to review`;
4534
4423
  })());
4535
- effect((_$p) => setProp(_el$55, "fg", palette.muted, _$p));
4536
- return _el$55;
4424
+ effect((_$p) => setProp(_el$50, "fg", palette.muted, _$p));
4425
+ return _el$50;
4537
4426
  })()];
4538
4427
  }
4539
4428
  });
@@ -4546,64 +4435,55 @@ function App(props) {
4546
4435
  },
4547
4436
  get children() {
4548
4437
  return [(() => {
4549
- var _el$56 = createElement("text");
4550
- insertNode(_el$56, createTextNode(`Erase this entire USB drive?`));
4438
+ var _el$51 = createElement("text");
4439
+ insertNode(_el$51, createTextNode(`Erase this entire USB drive?`));
4551
4440
  effect((_p$) => {
4552
4441
  var _v$17 = palette.warning, _v$18 = TextAttributes2.BOLD;
4553
- _v$17 !== _p$.e && (_p$.e = setProp(_el$56, "fg", _v$17, _p$.e));
4554
- _v$18 !== _p$.t && (_p$.t = setProp(_el$56, "attributes", _v$18, _p$.t));
4442
+ _v$17 !== _p$.e && (_p$.e = setProp(_el$51, "fg", _v$17, _p$.e));
4443
+ _v$18 !== _p$.t && (_p$.t = setProp(_el$51, "attributes", _v$18, _p$.t));
4555
4444
  return _p$;
4556
4445
  }, {
4557
4446
  e: void 0,
4558
4447
  t: void 0
4559
4448
  });
4560
- return _el$56;
4449
+ return _el$51;
4561
4450
  })(), (() => {
4562
- var _el$58 = createElement("box"), _el$59 = createElement("text"), _el$60 = createElement("text"), _el$61 = createElement("text"), _el$62 = createTextNode(` / `);
4563
- insertNode(_el$58, _el$59);
4564
- insertNode(_el$58, _el$60);
4565
- insertNode(_el$58, _el$61);
4566
- setProp(_el$58, "paddingX", 1);
4567
- setProp(_el$59, "height", 1);
4568
- setProp(_el$59, "wrapMode", "none");
4569
- insert(_el$59, () => disk()?.name);
4570
- insert(_el$60, (() => {
4451
+ var _el$53 = createElement("box"), _el$54 = createElement("text"), _el$55 = createElement("text");
4452
+ insertNode(_el$53, _el$54);
4453
+ insertNode(_el$53, _el$55);
4454
+ setProp(_el$53, "paddingX", 1);
4455
+ setProp(_el$54, "height", 1);
4456
+ setProp(_el$54, "wrapMode", "none");
4457
+ insert(_el$54, () => disk()?.name);
4458
+ insert(_el$55, (() => {
4571
4459
  var _c$5 = memo2(() => !!disk());
4572
4460
  return () => _c$5() ? `${path(disk())} / ${bytes(disk().bytes)}` : "";
4573
4461
  })());
4574
- insertNode(_el$61, _el$62);
4575
- insert(_el$61, () => image()?.profile.toUpperCase(), _el$62);
4576
- insert(_el$61, (() => {
4577
- var _c$6 = memo2(() => !!catalog()?.candidate);
4578
- return () => _c$6() ? `candidate (build-only) ${catalog()?.revision.slice(0, 7)}` : `Boo ${catalog()?.version}`;
4579
- })(), null);
4580
4462
  effect((_p$) => {
4581
- var _v$19 = palette.panelAlt, _v$20 = palette.foreground, _v$21 = TextAttributes2.BOLD, _v$22 = palette.accent, _v$23 = catalog()?.candidate ? palette.warning : palette.muted;
4582
- _v$19 !== _p$.e && (_p$.e = setProp(_el$58, "backgroundColor", _v$19, _p$.e));
4583
- _v$20 !== _p$.t && (_p$.t = setProp(_el$59, "fg", _v$20, _p$.t));
4584
- _v$21 !== _p$.a && (_p$.a = setProp(_el$59, "attributes", _v$21, _p$.a));
4585
- _v$22 !== _p$.o && (_p$.o = setProp(_el$60, "fg", _v$22, _p$.o));
4586
- _v$23 !== _p$.i && (_p$.i = setProp(_el$61, "fg", _v$23, _p$.i));
4463
+ var _v$19 = palette.panelAlt, _v$20 = palette.foreground, _v$21 = TextAttributes2.BOLD, _v$22 = palette.accent;
4464
+ _v$19 !== _p$.e && (_p$.e = setProp(_el$53, "backgroundColor", _v$19, _p$.e));
4465
+ _v$20 !== _p$.t && (_p$.t = setProp(_el$54, "fg", _v$20, _p$.t));
4466
+ _v$21 !== _p$.a && (_p$.a = setProp(_el$54, "attributes", _v$21, _p$.a));
4467
+ _v$22 !== _p$.o && (_p$.o = setProp(_el$55, "fg", _v$22, _p$.o));
4587
4468
  return _p$;
4588
4469
  }, {
4589
4470
  e: void 0,
4590
4471
  t: void 0,
4591
4472
  a: void 0,
4592
- o: void 0,
4593
- i: void 0
4473
+ o: void 0
4594
4474
  });
4595
- return _el$58;
4475
+ return _el$53;
4596
4476
  })(), (() => {
4597
- var _el$63 = createElement("text");
4598
- insertNode(_el$63, createTextNode(`ALL data and partitions on this drive will be lost. Back up anything you need before continuing.`));
4599
- effect((_$p) => setProp(_el$63, "fg", palette.warning, _$p));
4600
- return _el$63;
4477
+ var _el$56 = createElement("text");
4478
+ insertNode(_el$56, createTextNode(`ALL data and partitions on this drive will be lost. Back up anything you need before continuing.`));
4479
+ effect((_$p) => setProp(_el$56, "fg", palette.warning, _$p));
4480
+ return _el$56;
4601
4481
  })(), (() => {
4602
- var _el$65 = createElement("box");
4603
- insert(_el$65, () => row("Go back", "", 0), null);
4604
- insert(_el$65, () => row("Erase & flash", "", 1, true), null);
4605
- effect((_$p) => setProp(_el$65, "gap", tight() ? 0 : 1, _$p));
4606
- return _el$65;
4482
+ var _el$58 = createElement("box");
4483
+ insert(_el$58, () => row("Go back", "", 0), null);
4484
+ insert(_el$58, () => row("Erase & flash", "", 1, true), null);
4485
+ effect((_$p) => setProp(_el$58, "gap", tight() ? 0 : 1, _$p));
4486
+ return _el$58;
4607
4487
  })()];
4608
4488
  }
4609
4489
  }), createComponent2(Show, {
@@ -4612,108 +4492,114 @@ function App(props) {
4612
4492
  },
4613
4493
  get children() {
4614
4494
  return [(() => {
4615
- var _el$66 = createElement("text"), _el$67 = createTextNode(` `);
4616
- insertNode(_el$66, _el$67);
4617
- insert(_el$66, spinner, _el$67);
4618
- insert(_el$66, (() => {
4619
- var _c$7 = memo2(() => !!stopping());
4620
- return () => _c$7() ? "Stopping safely..." : progress().stage;
4495
+ var _el$59 = createElement("text"), _el$60 = createTextNode(` `);
4496
+ insertNode(_el$59, _el$60);
4497
+ insert(_el$59, spinner, _el$60);
4498
+ insert(_el$59, (() => {
4499
+ var _c$6 = memo2(() => !!stopping());
4500
+ return () => _c$6() ? "Stopping safely..." : progress().stage;
4621
4501
  })(), null);
4622
4502
  effect((_p$) => {
4623
- var _v$24 = palette.accent, _v$25 = TextAttributes2.BOLD;
4624
- _v$24 !== _p$.e && (_p$.e = setProp(_el$66, "fg", _v$24, _p$.e));
4625
- _v$25 !== _p$.t && (_p$.t = setProp(_el$66, "attributes", _v$25, _p$.t));
4503
+ var _v$23 = palette.accent, _v$24 = TextAttributes2.BOLD;
4504
+ _v$23 !== _p$.e && (_p$.e = setProp(_el$59, "fg", _v$23, _p$.e));
4505
+ _v$24 !== _p$.t && (_p$.t = setProp(_el$59, "attributes", _v$24, _p$.t));
4626
4506
  return _p$;
4627
4507
  }, {
4628
4508
  e: void 0,
4629
4509
  t: void 0
4630
4510
  });
4631
- return _el$66;
4511
+ return _el$59;
4632
4512
  })(), (() => {
4633
- var _el$68 = createElement("text"), _el$69 = createTextNode(` / `);
4634
- insertNode(_el$68, _el$69);
4635
- insert(_el$68, () => disk()?.name, _el$69);
4636
- insert(_el$68, () => image()?.profile.toUpperCase(), null);
4637
- effect((_$p) => setProp(_el$68, "fg", palette.foreground, _$p));
4638
- return _el$68;
4513
+ var _el$61 = createElement("text");
4514
+ insert(_el$61, () => disk()?.name);
4515
+ effect((_$p) => setProp(_el$61, "fg", palette.foreground, _$p));
4516
+ return _el$61;
4639
4517
  })(), (() => {
4640
- var _el$70 = createElement("text");
4641
- insert(_el$70, (() => {
4642
- var _c$8 = memo2(() => !!stopping());
4643
- return () => _c$8() ? "Waiting for all child processes to stop. Keep the USB connected." : progress().detail;
4518
+ var _el$62 = createElement("text");
4519
+ insert(_el$62, (() => {
4520
+ var _c$7 = memo2(() => !!stopping());
4521
+ return () => _c$7() ? "Waiting for all child processes to stop. Keep the USB connected." : progress().detail;
4644
4522
  })());
4645
- effect((_$p) => setProp(_el$70, "fg", palette.muted, _$p));
4646
- return _el$70;
4523
+ effect((_$p) => setProp(_el$62, "fg", palette.muted, _$p));
4524
+ return _el$62;
4647
4525
  })(), createComponent2(Show, {
4648
4526
  get when() {
4649
4527
  return percent() !== void 0;
4650
4528
  },
4651
4529
  get children() {
4652
4530
  return [(() => {
4653
- var _el$71 = createElement("box");
4654
- setProp(_el$71, "border", true);
4655
- setProp(_el$71, "height", 3);
4656
- insert(_el$71, createComponent2(Show, {
4531
+ var _el$63 = createElement("box");
4532
+ setProp(_el$63, "flexShrink", 0);
4533
+ insert(_el$63, createComponent2(Show, {
4657
4534
  get when() {
4658
4535
  return percent() > 0;
4659
4536
  },
4660
4537
  get children() {
4661
- var _el$72 = createElement("box");
4662
- setProp(_el$72, "height", 1);
4538
+ var _el$64 = createElement("box");
4539
+ setProp(_el$64, "height", 1);
4663
4540
  effect((_p$) => {
4664
- var _v$26 = `${percent() ?? 0}%`, _v$27 = palette.accent;
4665
- _v$26 !== _p$.e && (_p$.e = setProp(_el$72, "width", _v$26, _p$.e));
4666
- _v$27 !== _p$.t && (_p$.t = setProp(_el$72, "backgroundColor", _v$27, _p$.t));
4541
+ var _v$25 = `${percent() ?? 0}%`, _v$26 = palette.accent;
4542
+ _v$25 !== _p$.e && (_p$.e = setProp(_el$64, "width", _v$25, _p$.e));
4543
+ _v$26 !== _p$.t && (_p$.t = setProp(_el$64, "backgroundColor", _v$26, _p$.t));
4667
4544
  return _p$;
4668
4545
  }, {
4669
4546
  e: void 0,
4670
4547
  t: void 0
4671
4548
  });
4672
- return _el$72;
4549
+ return _el$64;
4673
4550
  }
4674
4551
  }));
4675
4552
  effect((_p$) => {
4676
- var _v$28 = palette.accent, _v$29 = palette.panelAlt;
4677
- _v$28 !== _p$.e && (_p$.e = setProp(_el$71, "borderColor", _v$28, _p$.e));
4678
- _v$29 !== _p$.t && (_p$.t = setProp(_el$71, "backgroundColor", _v$29, _p$.t));
4553
+ var _v$27 = !tight(), _v$28 = palette.accent, _v$29 = tight() ? 1 : 3, _v$30 = palette.panelAlt;
4554
+ _v$27 !== _p$.e && (_p$.e = setProp(_el$63, "border", _v$27, _p$.e));
4555
+ _v$28 !== _p$.t && (_p$.t = setProp(_el$63, "borderColor", _v$28, _p$.t));
4556
+ _v$29 !== _p$.a && (_p$.a = setProp(_el$63, "height", _v$29, _p$.a));
4557
+ _v$30 !== _p$.o && (_p$.o = setProp(_el$63, "backgroundColor", _v$30, _p$.o));
4679
4558
  return _p$;
4680
4559
  }, {
4681
4560
  e: void 0,
4682
- t: void 0
4561
+ t: void 0,
4562
+ a: void 0,
4563
+ o: void 0
4683
4564
  });
4684
- return _el$71;
4565
+ return _el$63;
4685
4566
  })(), (() => {
4686
- var _el$73 = createElement("text"), _el$74 = createTextNode(`% `), _el$75 = createTextNode(` / `);
4687
- insertNode(_el$73, _el$74);
4688
- insertNode(_el$73, _el$75);
4689
- insert(_el$73, percent, _el$74);
4690
- insert(_el$73, () => bytes(progress().bytes ?? 0), _el$75);
4691
- insert(_el$73, () => bytes(progress().total ?? 0), null);
4567
+ var _el$65 = createElement("text"), _el$66 = createTextNode(`% `), _el$67 = createTextNode(` / `);
4568
+ insertNode(_el$65, _el$66);
4569
+ insertNode(_el$65, _el$67);
4570
+ insert(_el$65, percent, _el$66);
4571
+ insert(_el$65, () => bytes(progress().bytes ?? 0), _el$67);
4572
+ insert(_el$65, () => bytes(progress().total ?? 0), null);
4692
4573
  effect((_p$) => {
4693
- var _v$30 = palette.foreground, _v$31 = TextAttributes2.BOLD;
4694
- _v$30 !== _p$.e && (_p$.e = setProp(_el$73, "fg", _v$30, _p$.e));
4695
- _v$31 !== _p$.t && (_p$.t = setProp(_el$73, "attributes", _v$31, _p$.t));
4574
+ var _v$31 = palette.foreground, _v$32 = TextAttributes2.BOLD;
4575
+ _v$31 !== _p$.e && (_p$.e = setProp(_el$65, "fg", _v$31, _p$.e));
4576
+ _v$32 !== _p$.t && (_p$.t = setProp(_el$65, "attributes", _v$32, _p$.t));
4696
4577
  return _p$;
4697
4578
  }, {
4698
4579
  e: void 0,
4699
4580
  t: void 0
4700
4581
  });
4701
- return _el$73;
4702
- })(), (() => {
4703
- var _el$76 = createElement("text");
4704
- insert(_el$76, (() => {
4705
- var _c$9 = memo2(() => !!progress().speed);
4706
- return () => _c$9() ? `${bytes(progress().speed)} / s` : "";
4707
- })());
4708
- effect((_$p) => setProp(_el$76, "fg", palette.muted, _$p));
4709
- return _el$76;
4710
- })()];
4582
+ return _el$65;
4583
+ })(), createComponent2(Show, {
4584
+ get when() {
4585
+ return !tight();
4586
+ },
4587
+ get children() {
4588
+ var _el$68 = createElement("text");
4589
+ insert(_el$68, (() => {
4590
+ var _c$8 = memo2(() => !!progress().speed);
4591
+ return () => _c$8() ? `${bytes(progress().speed)} / s` : "";
4592
+ })());
4593
+ effect((_$p) => setProp(_el$68, "fg", palette.muted, _$p));
4594
+ return _el$68;
4595
+ }
4596
+ })];
4711
4597
  }
4712
4598
  }), (() => {
4713
- var _el$77 = createElement("text");
4714
- insertNode(_el$77, createTextNode(`Keep the USB connected until safe removal succeeds.`));
4715
- effect((_$p) => setProp(_el$77, "fg", palette.warning, _$p));
4716
- return _el$77;
4599
+ var _el$69 = createElement("text");
4600
+ insertNode(_el$69, createTextNode(`Keep the USB connected until safe removal succeeds.`));
4601
+ effect((_$p) => setProp(_el$69, "fg", palette.warning, _$p));
4602
+ return _el$69;
4717
4603
  })()];
4718
4604
  }
4719
4605
  }), createComponent2(Show, {
@@ -4722,37 +4608,34 @@ function App(props) {
4722
4608
  },
4723
4609
  get children() {
4724
4610
  return [(() => {
4725
- var _el$79 = createElement("text");
4726
- insert(_el$79, () => props.backend.successTitle);
4611
+ var _el$71 = createElement("text");
4612
+ insert(_el$71, () => props.backend.successTitle);
4727
4613
  effect((_p$) => {
4728
- var _v$32 = palette.success, _v$33 = TextAttributes2.BOLD;
4729
- _v$32 !== _p$.e && (_p$.e = setProp(_el$79, "fg", _v$32, _p$.e));
4730
- _v$33 !== _p$.t && (_p$.t = setProp(_el$79, "attributes", _v$33, _p$.t));
4614
+ var _v$33 = palette.success, _v$34 = TextAttributes2.BOLD;
4615
+ _v$33 !== _p$.e && (_p$.e = setProp(_el$71, "fg", _v$33, _p$.e));
4616
+ _v$34 !== _p$.t && (_p$.t = setProp(_el$71, "attributes", _v$34, _p$.t));
4731
4617
  return _p$;
4732
4618
  }, {
4733
4619
  e: void 0,
4734
4620
  t: void 0
4735
4621
  });
4736
- return _el$79;
4622
+ return _el$71;
4737
4623
  })(), (() => {
4738
- var _el$80 = createElement("text");
4739
- insert(_el$80, () => props.backend.successDetail);
4740
- effect((_$p) => setProp(_el$80, "fg", palette.foreground, _$p));
4741
- return _el$80;
4624
+ var _el$72 = createElement("text");
4625
+ insert(_el$72, () => props.backend.successDetail);
4626
+ effect((_$p) => setProp(_el$72, "fg", palette.foreground, _$p));
4627
+ return _el$72;
4742
4628
  })(), (() => {
4743
- var _el$81 = createElement("text");
4744
- insert(_el$81, (() => {
4745
- var _c$0 = memo2(() => !!props.backend.demo);
4746
- return () => _c$0() ? "The real flow checks the write, flush and safe removal." : catalog()?.candidate ? "GitHub ZIP, compressed image and raw stream verified. Not a device read-back." : "Compressed image and raw stream verified. Not a device read-back.";
4747
- })());
4748
- effect((_$p) => setProp(_el$81, "fg", palette.muted, _$p));
4749
- return _el$81;
4629
+ var _el$73 = createElement("text");
4630
+ insertNode(_el$73, createTextNode(`Compressed image and raw stream verified. Not a device read-back.`));
4631
+ effect((_$p) => setProp(_el$73, "fg", palette.muted, _$p));
4632
+ return _el$73;
4750
4633
  })(), (() => {
4751
- var _el$82 = createElement("box");
4752
- setProp(_el$82, "gap", 1);
4753
- insert(_el$82, () => row("Choose another image", "", 0), null);
4754
- insert(_el$82, () => row("Quit", "", 1), null);
4755
- return _el$82;
4634
+ var _el$75 = createElement("box");
4635
+ setProp(_el$75, "gap", 1);
4636
+ insert(_el$75, () => row("Choose another image", "", 0), null);
4637
+ insert(_el$75, () => row("Quit", "", 1), null);
4638
+ return _el$75;
4756
4639
  })()];
4757
4640
  }
4758
4641
  }), createComponent2(Show, {
@@ -4761,56 +4644,56 @@ function App(props) {
4761
4644
  },
4762
4645
  get children() {
4763
4646
  return [(() => {
4764
- var _el$83 = createElement("text");
4765
- insertNode(_el$83, createTextNode(`Flash not completed`));
4647
+ var _el$76 = createElement("text");
4648
+ insertNode(_el$76, createTextNode(`Flash not completed`));
4766
4649
  effect((_p$) => {
4767
- var _v$34 = palette.danger, _v$35 = TextAttributes2.BOLD;
4768
- _v$34 !== _p$.e && (_p$.e = setProp(_el$83, "fg", _v$34, _p$.e));
4769
- _v$35 !== _p$.t && (_p$.t = setProp(_el$83, "attributes", _v$35, _p$.t));
4650
+ var _v$35 = palette.danger, _v$36 = TextAttributes2.BOLD;
4651
+ _v$35 !== _p$.e && (_p$.e = setProp(_el$76, "fg", _v$35, _p$.e));
4652
+ _v$36 !== _p$.t && (_p$.t = setProp(_el$76, "attributes", _v$36, _p$.t));
4770
4653
  return _p$;
4771
4654
  }, {
4772
4655
  e: void 0,
4773
4656
  t: void 0
4774
4657
  });
4775
- return _el$83;
4658
+ return _el$76;
4776
4659
  })(), (() => {
4777
- var _el$85 = createElement("text");
4778
- insert(_el$85, error);
4779
- effect((_$p) => setProp(_el$85, "fg", palette.foreground, _$p));
4780
- return _el$85;
4660
+ var _el$78 = createElement("text");
4661
+ insert(_el$78, error);
4662
+ effect((_$p) => setProp(_el$78, "fg", palette.foreground, _$p));
4663
+ return _el$78;
4781
4664
  })(), (() => {
4782
- var _el$86 = createElement("box");
4783
- setProp(_el$86, "gap", 1);
4784
- insert(_el$86, () => row("Refresh & try again", "", 0), null);
4785
- insert(_el$86, () => row("Quit", "", 1), null);
4786
- return _el$86;
4665
+ var _el$79 = createElement("box");
4666
+ setProp(_el$79, "gap", 1);
4667
+ insert(_el$79, () => row("Refresh & try again", "", 0), null);
4668
+ insert(_el$79, () => row("Quit", "", 1), null);
4669
+ return _el$79;
4787
4670
  })()];
4788
4671
  }
4789
4672
  })];
4790
4673
  }
4791
4674
  }));
4792
- setProp(_el$87, "height", 1);
4793
- setProp(_el$87, "flexShrink", 0);
4794
- setProp(_el$87, "wrapMode", "none");
4795
- insert(_el$87, (() => {
4796
- var _c$1 = memo2(() => !!busy());
4797
- return () => _c$1() ? "Esc cancel / Q stop & quit" : dimensions().width < 56 ? "Arrows / Enter / Esc / R / Q quit" : "Arrows / Enter / Esc back / R refresh / Q quit";
4675
+ setProp(_el$80, "height", 1);
4676
+ setProp(_el$80, "flexShrink", 0);
4677
+ setProp(_el$80, "wrapMode", "none");
4678
+ insert(_el$80, (() => {
4679
+ var _c$9 = memo2(() => !!busy());
4680
+ return () => _c$9() ? "Esc cancel / Q stop & quit" : dimensions().width < 56 ? "Arrows / Enter / Esc / R / Q quit" : "Arrows / Enter / Esc back / R refresh / Q quit";
4798
4681
  })());
4799
4682
  effect((_p$) => {
4800
- var _v$36 = palette.background, _v$37 = dimensions().width <= 80 ? "100%" : 80, _v$38 = dimensions().height <= 24 ? "100%" : 24, _v$39 = palette.border, _v$40 = palette.card, _v$41 = palette.accent, _v$42 = TextAttributes2.BOLD, _v$43 = props.backend.demo ? palette.warning : palette.muted, _v$44 = palette.danger, _v$45 = tight() ? 0 : 1, _v$46 = tight() ? 0 : 1, _v$47 = tight() || (compact() || dimensions().width < 56) && screen() === "setup" ? 0 : 1, _v$48 = palette.muted;
4801
- _v$36 !== _p$.e && (_p$.e = setProp(_el$4, "backgroundColor", _v$36, _p$.e));
4802
- _v$37 !== _p$.t && (_p$.t = setProp(_el$5, "width", _v$37, _p$.t));
4803
- _v$38 !== _p$.a && (_p$.a = setProp(_el$5, "height", _v$38, _p$.a));
4804
- _v$39 !== _p$.o && (_p$.o = setProp(_el$5, "borderColor", _v$39, _p$.o));
4805
- _v$40 !== _p$.i && (_p$.i = setProp(_el$5, "backgroundColor", _v$40, _p$.i));
4806
- _v$41 !== _p$.n && (_p$.n = setProp(_el$7, "fg", _v$41, _p$.n));
4807
- _v$42 !== _p$.s && (_p$.s = setProp(_el$7, "attributes", _v$42, _p$.s));
4808
- _v$43 !== _p$.h && (_p$.h = setProp(_el$9, "fg", _v$43, _p$.h));
4809
- _v$44 !== _p$.r && (_p$.r = setProp(_el$0, "backgroundColor", _v$44, _p$.r));
4810
- _v$45 !== _p$.d && (_p$.d = setProp(_el$0, "marginTop", _v$45, _p$.d));
4811
- _v$46 !== _p$.l && (_p$.l = setProp(_el$1, "paddingTop", _v$46, _p$.l));
4812
- _v$47 !== _p$.u && (_p$.u = setProp(_el$1, "gap", _v$47, _p$.u));
4813
- _v$48 !== _p$.c && (_p$.c = setProp(_el$87, "fg", _v$48, _p$.c));
4683
+ var _v$37 = palette.background, _v$38 = dimensions().width <= 80 ? "100%" : 80, _v$39 = dimensions().height <= 24 ? "100%" : 24, _v$40 = palette.border, _v$41 = palette.card, _v$42 = palette.accent, _v$43 = TextAttributes2.BOLD, _v$44 = palette.muted, _v$45 = palette.danger, _v$46 = tight() ? 0 : 1, _v$47 = tight() ? 0 : 1, _v$48 = tight() || (compact() || dimensions().width < 56) && (screen() === "setup" || !!image()) ? 0 : 1, _v$49 = palette.muted;
4684
+ _v$37 !== _p$.e && (_p$.e = setProp(_el$4, "backgroundColor", _v$37, _p$.e));
4685
+ _v$38 !== _p$.t && (_p$.t = setProp(_el$5, "width", _v$38, _p$.t));
4686
+ _v$39 !== _p$.a && (_p$.a = setProp(_el$5, "height", _v$39, _p$.a));
4687
+ _v$40 !== _p$.o && (_p$.o = setProp(_el$5, "borderColor", _v$40, _p$.o));
4688
+ _v$41 !== _p$.i && (_p$.i = setProp(_el$5, "backgroundColor", _v$41, _p$.i));
4689
+ _v$42 !== _p$.n && (_p$.n = setProp(_el$7, "fg", _v$42, _p$.n));
4690
+ _v$43 !== _p$.s && (_p$.s = setProp(_el$7, "attributes", _v$43, _p$.s));
4691
+ _v$44 !== _p$.h && (_p$.h = setProp(_el$9, "fg", _v$44, _p$.h));
4692
+ _v$45 !== _p$.r && (_p$.r = setProp(_el$0, "backgroundColor", _v$45, _p$.r));
4693
+ _v$46 !== _p$.d && (_p$.d = setProp(_el$0, "marginTop", _v$46, _p$.d));
4694
+ _v$47 !== _p$.l && (_p$.l = setProp(_el$1, "paddingTop", _v$47, _p$.l));
4695
+ _v$48 !== _p$.u && (_p$.u = setProp(_el$1, "gap", _v$48, _p$.u));
4696
+ _v$49 !== _p$.c && (_p$.c = setProp(_el$80, "fg", _v$49, _p$.c));
4814
4697
  return _p$;
4815
4698
  }, {
4816
4699
  e: void 0,
@@ -4833,10 +4716,8 @@ function App(props) {
4833
4716
 
4834
4717
  // src/index.tsx
4835
4718
  var args = process.argv.slice(2);
4836
- var demo = args.includes("--demo");
4837
- var fixture = args.find((arg) => arg.startsWith("--demo="))?.slice(7);
4838
- if (args.length > 1 || args.some((arg) => !["--demo", "--demo=empty", "--demo=error", "--demo=setup", "--demo=setup-gh", "--demo=setup-zstd", "--demo=setup-error", "--demo=setup-interrupted", "--demo=no-homebrew", "--list-images", "--clear-cache"].includes(arg))) {
4839
- console.error("Usage: bun start [--demo[=empty|error|setup|setup-gh|setup-zstd|setup-error|setup-interrupted|no-homebrew] | --list-images | --clear-cache]");
4719
+ if (args.length > 1 || args.some((arg) => !["--list-images", "--clear-cache"].includes(arg))) {
4720
+ console.error("Usage: boo-flash [--list-images | --clear-cache]");
4840
4721
  process.exitCode = 1;
4841
4722
  } else if (args.includes("--clear-cache")) {
4842
4723
  try {
@@ -4852,11 +4733,11 @@ if (args.length > 1 || args.some((arg) => !["--demo", "--demo=empty", "--demo=er
4852
4733
  console.error(e instanceof Error ? e.message : "Image discovery failed.");
4853
4734
  process.exitCode = 1;
4854
4735
  }
4855
- } else if (!demo && !fixture && (!["darwin", "linux"].includes(process.platform) || process.getuid?.() === 0)) {
4856
- console.error("Boo Flash requires macOS or Linux and a normal user account. Use --demo for a non-writing UI preview.");
4736
+ } else if (!["darwin", "linux"].includes(process.platform) || process.getuid?.() === 0) {
4737
+ console.error("Boo Flash requires macOS or Linux and a normal user account.");
4857
4738
  process.exitCode = 1;
4858
4739
  } else {
4859
- const backend = new FlashBackend(demo ? "normal" : fixture);
4740
+ const backend = new FlashBackend();
4860
4741
  const renderer = await createCliRenderer2({
4861
4742
  backgroundColor: palette.background,
4862
4743
  targetFps: 30,