craft-native 0.0.37 → 0.0.49

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 (89) hide show
  1. package/dist/android/src/index.js +268 -0
  2. package/dist/android/src/index.js.map +10 -0
  3. package/dist/android/templates/AndroidManifest.xml.template +69 -0
  4. package/dist/android/templates/CraftBridge.kt.template +3748 -0
  5. package/dist/android/templates/CraftBridgeExtensions.kt.template +383 -0
  6. package/dist/android/templates/CraftWidgetProvider.kt.template +246 -0
  7. package/dist/android/templates/MainActivity.kt.template +109 -0
  8. package/dist/android/templates/build.gradle.kts.app.template +80 -0
  9. package/dist/android/templates/build.gradle.kts.project.template +5 -0
  10. package/dist/android/templates/fastlane/Appfile +25 -0
  11. package/dist/android/templates/fastlane/Fastfile +192 -0
  12. package/dist/android/templates/fastlane/Gemfile +7 -0
  13. package/dist/android/templates/github-workflow-android.yml +248 -0
  14. package/dist/android/templates/github-workflow-test.yml +329 -0
  15. package/dist/android/templates/settings.gradle.kts.template +18 -0
  16. package/dist/android/templates/test-bridges.html +1463 -0
  17. package/dist/api/window.d.ts +18 -0
  18. package/dist/api/window.d.ts.map +1 -1
  19. package/dist/assets/index.d.ts.map +1 -1
  20. package/dist/bridge/core.d.ts +2 -0
  21. package/dist/bridge/core.d.ts.map +1 -1
  22. package/dist/cli.js +4168 -0
  23. package/dist/components/sidebar.d.ts +5 -1
  24. package/dist/components/sidebar.d.ts.map +1 -1
  25. package/dist/dev/devtools.d.ts +1 -1
  26. package/dist/dev/devtools.d.ts.map +1 -1
  27. package/dist/dev/hot-reload.d.ts +12 -3
  28. package/dist/dev/hot-reload.d.ts.map +1 -1
  29. package/dist/index.cjs +573 -208
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +585 -216
  32. package/dist/index.js.map +45 -0
  33. package/dist/ios/src/index.js +186 -0
  34. package/dist/ios/src/index.js.map +10 -0
  35. package/dist/ios/templates/CraftApp.swift +4241 -0
  36. package/dist/ios/templates/CraftAppExtensions.swift +361 -0
  37. package/dist/ios/templates/CraftWidget.swift +247 -0
  38. package/dist/ios/templates/Info.plist.template +65 -0
  39. package/dist/ios/templates/fastlane/Appfile +45 -0
  40. package/dist/ios/templates/fastlane/Fastfile +189 -0
  41. package/dist/ios/templates/fastlane/Gemfile +7 -0
  42. package/dist/ios/templates/fastlane/Matchfile +58 -0
  43. package/dist/ios/templates/github-workflow-ios.yml +192 -0
  44. package/dist/ios/templates/github-workflow-release.yml +175 -0
  45. package/dist/ios/templates/github-workflow-test.yml +272 -0
  46. package/dist/ios/templates/project.yml.template +25 -0
  47. package/dist/ios/templates/test-bridges.html +1463 -0
  48. package/dist/package.d.ts +106 -2
  49. package/dist/package.d.ts.map +1 -1
  50. package/dist/package.test.d.ts +2 -0
  51. package/dist/package.test.d.ts.map +1 -0
  52. package/dist/styles/sidebar-templates.d.ts.map +1 -1
  53. package/dist/styles/sidebars.d.ts +12 -8
  54. package/dist/styles/sidebars.d.ts.map +1 -1
  55. package/dist/templates/projects/blank/craft.config.ts +19 -0
  56. package/dist/templates/projects/blank/index.html +27 -0
  57. package/dist/templates/projects/blank/package.json +19 -0
  58. package/dist/templates/projects/blank/src/main.ts +26 -0
  59. package/dist/templates/projects/dashboard/craft.config.ts +42 -0
  60. package/dist/templates/projects/dashboard/index.html +13 -0
  61. package/dist/templates/projects/dashboard/package.json +20 -0
  62. package/dist/templates/projects/dashboard/src/main.ts +163 -0
  63. package/dist/templates/projects/dashboard/src/styles.css +280 -0
  64. package/dist/templates/projects/drawer/craft.config.ts +47 -0
  65. package/dist/templates/projects/drawer/index.html +13 -0
  66. package/dist/templates/projects/drawer/package.json +21 -0
  67. package/dist/templates/projects/drawer/src/main.ts +406 -0
  68. package/dist/templates/projects/drawer/src/styles.css +468 -0
  69. package/dist/templates/projects/ecommerce/craft.config.ts +32 -0
  70. package/dist/templates/projects/ecommerce/index.html +13 -0
  71. package/dist/templates/projects/ecommerce/package.json +19 -0
  72. package/dist/templates/projects/ecommerce/src/main.ts +194 -0
  73. package/dist/templates/projects/ecommerce/src/styles.css +169 -0
  74. package/dist/templates/projects/social/craft.config.ts +32 -0
  75. package/dist/templates/projects/social/index.html +13 -0
  76. package/dist/templates/projects/social/package.json +19 -0
  77. package/dist/templates/projects/social/src/main.ts +270 -0
  78. package/dist/templates/projects/social/src/styles.css +216 -0
  79. package/dist/templates/projects/tabs/craft.config.ts +47 -0
  80. package/dist/templates/projects/tabs/index.html +13 -0
  81. package/dist/templates/projects/tabs/package.json +21 -0
  82. package/dist/templates/projects/tabs/src/main.ts +177 -0
  83. package/dist/templates/projects/tabs/src/styles.css +163 -0
  84. package/dist/types.d.ts +29 -0
  85. package/dist/types.d.ts.map +1 -1
  86. package/dist/updater/index.d.ts +1 -0
  87. package/dist/updater/index.d.ts.map +1 -1
  88. package/package.json +5 -12
  89. package/bin/cli.ts +0 -937
package/dist/index.cjs CHANGED
@@ -177,7 +177,7 @@ __export(exports_src, {
177
177
  focusFilters: () => focusFilters,
178
178
  focus: () => focus,
179
179
  exit: () => exit,
180
- exec: () => exec2,
180
+ exec: () => exec,
181
181
  env: () => env,
182
182
  dialog: () => dialog,
183
183
  device: () => device,
@@ -290,16 +290,90 @@ function craftBinaryNotFoundMessage(triedPath) {
290
290
 
291
291
  // src/package.ts
292
292
  var import_child_process = require("child_process");
293
+ var import_crypto = require("crypto");
293
294
  var import_fs = require("fs");
294
295
  var import_os = require("os");
295
296
  var import_path = require("path");
296
- var archiverFn = null;
297
- async function loadArchiver() {
298
- if (!archiverFn) {
299
- const mod = await import("archiver");
300
- archiverFn = mod.default ?? mod;
301
- }
302
- return archiverFn;
297
+ var import_zlib = require("zlib");
298
+ var CRC32_TABLE = (() => {
299
+ const table = new Uint32Array(256);
300
+ for (let n = 0;n < 256; n++) {
301
+ let c = n;
302
+ for (let k = 0;k < 8; k++)
303
+ c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
304
+ table[n] = c >>> 0;
305
+ }
306
+ return table;
307
+ })();
308
+ function crc32(data) {
309
+ let c = 4294967295;
310
+ for (let i = 0;i < data.length; i++)
311
+ c = CRC32_TABLE[(c ^ data[i]) & 255] ^ c >>> 8;
312
+ return (c ^ 4294967295) >>> 0;
313
+ }
314
+ function dosDateTime(d) {
315
+ const time = d.getHours() << 11 | d.getMinutes() << 5 | d.getSeconds() >> 1;
316
+ const date = d.getFullYear() - 1980 << 9 | d.getMonth() + 1 << 5 | d.getDate();
317
+ return { time: time & 65535, date: date & 65535 };
318
+ }
319
+ function buildZip(entries) {
320
+ const { time, date } = dosDateTime(new Date);
321
+ const locals = [];
322
+ const central = [];
323
+ let offset = 0;
324
+ for (const entry of entries) {
325
+ const nameBytes = Buffer.from(entry.name, "utf8");
326
+ const compressed = import_zlib.deflateRawSync(entry.data, { level: 9 });
327
+ const crc = crc32(entry.data);
328
+ const lfh = Buffer.alloc(30);
329
+ lfh.writeUInt32LE(67324752, 0);
330
+ lfh.writeUInt16LE(20, 4);
331
+ lfh.writeUInt16LE(0, 6);
332
+ lfh.writeUInt16LE(8, 8);
333
+ lfh.writeUInt16LE(time, 10);
334
+ lfh.writeUInt16LE(date, 12);
335
+ lfh.writeUInt32LE(crc, 14);
336
+ lfh.writeUInt32LE(compressed.length, 18);
337
+ lfh.writeUInt32LE(entry.data.length, 22);
338
+ lfh.writeUInt16LE(nameBytes.length, 26);
339
+ lfh.writeUInt16LE(0, 28);
340
+ locals.push(lfh, nameBytes, compressed);
341
+ const cdh = Buffer.alloc(46);
342
+ cdh.writeUInt32LE(33639248, 0);
343
+ cdh.writeUInt16LE(20, 4);
344
+ cdh.writeUInt16LE(20, 6);
345
+ cdh.writeUInt16LE(0, 8);
346
+ cdh.writeUInt16LE(8, 10);
347
+ cdh.writeUInt16LE(time, 12);
348
+ cdh.writeUInt16LE(date, 14);
349
+ cdh.writeUInt32LE(crc, 16);
350
+ cdh.writeUInt32LE(compressed.length, 20);
351
+ cdh.writeUInt32LE(entry.data.length, 24);
352
+ cdh.writeUInt16LE(nameBytes.length, 28);
353
+ cdh.writeUInt16LE(0, 30);
354
+ cdh.writeUInt16LE(0, 32);
355
+ cdh.writeUInt16LE(0, 34);
356
+ cdh.writeUInt16LE(0, 36);
357
+ cdh.writeUInt32LE(0, 38);
358
+ cdh.writeUInt32LE(offset, 42);
359
+ central.push(cdh, nameBytes);
360
+ offset += lfh.length + nameBytes.length + compressed.length;
361
+ }
362
+ const centralBuf = Buffer.concat(central);
363
+ const eocd = Buffer.alloc(22);
364
+ eocd.writeUInt32LE(101010256, 0);
365
+ eocd.writeUInt16LE(0, 4);
366
+ eocd.writeUInt16LE(0, 6);
367
+ eocd.writeUInt16LE(entries.length, 8);
368
+ eocd.writeUInt16LE(entries.length, 10);
369
+ eocd.writeUInt32LE(centralBuf.length, 12);
370
+ eocd.writeUInt32LE(offset, 16);
371
+ eocd.writeUInt16LE(0, 20);
372
+ return Buffer.concat([...locals, centralBuf, eocd]);
373
+ }
374
+ function formatPackagingCommandError(tool, code, stdout, stderr) {
375
+ const detail = `${stdout}${stderr}`.trim();
376
+ return `${tool} exited with code ${code}${detail ? `: ${detail}` : ""}`;
303
377
  }
304
378
  async function packageApp(config) {
305
379
  const results = [];
@@ -330,13 +404,20 @@ async function packageMacOS(config, outDir) {
330
404
  const results = [];
331
405
  const { name, version, bundleId = `com.myapp.${name.toLowerCase()}` } = config;
332
406
  const opts = config.macos || {};
407
+ const hardenedRuntime = !opts.appStore;
333
408
  const appBundlePath = import_path.join(outDir, `${name}.app`);
334
409
  const appBundle = createMacOSAppBundle({
335
410
  name,
336
411
  version,
337
412
  bundleId,
413
+ buildNumber: opts.buildNumber,
414
+ category: opts.category,
415
+ minimumSystemVersion: opts.minimumSystemVersion,
416
+ menuBarOnly: opts.menuBarOnly,
338
417
  binaryPath: config.binaryPath,
339
418
  iconPath: config.iconPath,
419
+ provisioningProfile: opts.provisioningProfile,
420
+ additionalExecutables: opts.additionalExecutables,
340
421
  outputPath: appBundlePath
341
422
  });
342
423
  if (!appBundle.success) {
@@ -348,12 +429,30 @@ async function packageMacOS(config, outDir) {
348
429
  });
349
430
  return results;
350
431
  }
351
- if (opts.dmg !== false) {
352
- const dmgResult = await createDMG({
353
- appBundlePath,
354
- outputPath: import_path.join(outDir, `${name}-${version}.dmg`),
355
- volumeName: name
432
+ if (opts.signIdentity) {
433
+ const signed = await runTool("codesign", codesignArguments({
434
+ path: appBundlePath,
435
+ identity: opts.signIdentity,
436
+ entitlements: opts.entitlements,
437
+ hardenedRuntime
438
+ }));
439
+ if (!signed.success) {
440
+ results.push({ success: false, platform: "macos", format: "app", error: signed.error });
441
+ return results;
442
+ }
443
+ } else if (opts.appStore) {
444
+ results.push({
445
+ success: false,
446
+ platform: "macos",
447
+ format: "pkg",
448
+ error: 'App Store packaging requires macos.signIdentity (a "3rd Party Mac Developer Application" identity)'
356
449
  });
450
+ return results;
451
+ }
452
+ results.push({ success: true, platform: "macos", format: "app", outputPath: appBundlePath });
453
+ if (opts.dmg !== false) {
454
+ const outputPath = import_path.join(outDir, `${name}-${version}.dmg`);
455
+ const dmgResult = await createDMG({ appBundlePath, outputPath, volumeName: name });
357
456
  results.push({
358
457
  success: dmgResult.success,
359
458
  platform: "macos",
@@ -361,24 +460,59 @@ async function packageMacOS(config, outDir) {
361
460
  outputPath: dmgResult.outputPath,
362
461
  error: dmgResult.error
363
462
  });
463
+ if (dmgResult.success)
464
+ results.push(...await notarizeIfRequested(opts, outputPath, "dmg"));
364
465
  }
365
- if (opts.pkg) {
466
+ if (opts.pkg || opts.appStore) {
467
+ const outputPath = import_path.join(outDir, `${name}-${version}.pkg`);
366
468
  const pkgResult = await createPKG({
367
469
  appBundlePath,
368
- outputPath: import_path.join(outDir, `${name}-${version}.pkg`),
470
+ outputPath,
369
471
  identifier: bundleId,
370
- version
472
+ version,
473
+ appStore: opts.appStore === true,
474
+ installerIdentity: opts.installerIdentity
371
475
  });
372
476
  results.push({
373
477
  success: pkgResult.success,
374
478
  platform: "macos",
375
- format: "pkg",
479
+ format: opts.appStore ? "pkg (app store)" : "pkg",
376
480
  outputPath: pkgResult.outputPath,
377
481
  error: pkgResult.error
378
482
  });
483
+ if (pkgResult.success && !opts.appStore)
484
+ results.push(...await notarizeIfRequested(opts, outputPath, "pkg"));
379
485
  }
380
486
  return results;
381
487
  }
488
+ async function notarizeIfRequested(opts, artifactPath, format) {
489
+ if (!opts.notarize)
490
+ return [];
491
+ if (!opts.appleId || !opts.applePassword || !opts.teamId) {
492
+ return [{
493
+ success: false,
494
+ platform: "macos",
495
+ format: `${format} (notarize)`,
496
+ error: "Notarization requires macos.appleId, macos.applePassword and macos.teamId"
497
+ }];
498
+ }
499
+ const submitted = await runTool("xcrun", notarytoolArguments({
500
+ artifactPath,
501
+ appleId: opts.appleId,
502
+ applePassword: opts.applePassword,
503
+ teamId: opts.teamId
504
+ }));
505
+ if (!submitted.success)
506
+ return [{ success: false, platform: "macos", format: `${format} (notarize)`, error: submitted.error }];
507
+ const stapled = await runTool("xcrun", ["stapler", "staple", artifactPath]);
508
+ return [{
509
+ success: stapled.success,
510
+ platform: "macos",
511
+ format: `${format} (notarize)`,
512
+ outputPath: stapled.success ? artifactPath : undefined,
513
+ error: stapled.success ? undefined : stapled.error
514
+ }];
515
+ }
382
516
  async function packageWindows(config, outDir) {
383
517
  const results = [];
384
518
  const { name, version } = config;
@@ -389,7 +523,10 @@ async function packageWindows(config, outDir) {
389
523
  version,
390
524
  binaryPath: config.binaryPath,
391
525
  outputPath: import_path.join(outDir, `${name}-${version}.msi`),
392
- manufacturer: config.author || "Unknown"
526
+ manufacturer: config.author || "Unknown",
527
+ architecture: opts.architecture || windowsArchitecture(process.arch),
528
+ certificatePath: opts.certificatePath,
529
+ certificatePassword: opts.certificatePassword
393
530
  });
394
531
  results.push({
395
532
  success: msiResult.success,
@@ -473,83 +610,200 @@ async function packageLinux(config, outDir) {
473
610
  }
474
611
  return results;
475
612
  }
476
- function createMacOSAppBundle(opts) {
477
- try {
478
- const { name, version, bundleId, binaryPath, outputPath } = opts;
479
- import_fs.mkdirSync(import_path.join(outputPath, "Contents", "MacOS"), { recursive: true });
480
- import_fs.mkdirSync(import_path.join(outputPath, "Contents", "Resources"), { recursive: true });
481
- import_fs.copyFileSync(binaryPath, import_path.join(outputPath, "Contents", "MacOS", name));
482
- import_fs.chmodSync(import_path.join(outputPath, "Contents", "MacOS", name), 493);
483
- const infoPlist = `<?xml version="1.0" encoding="UTF-8"?>
613
+ function macOSInfoPlist(metadata) {
614
+ const entries = [
615
+ ["CFBundleExecutable", metadata.name],
616
+ ["CFBundleIdentifier", metadata.bundleId],
617
+ ["CFBundleName", metadata.name],
618
+ ["CFBundleShortVersionString", metadata.version],
619
+ ["CFBundleVersion", metadata.buildNumber || metadata.version],
620
+ ["CFBundlePackageType", "APPL"],
621
+ ["NSHighResolutionCapable", true]
622
+ ];
623
+ if (metadata.iconName)
624
+ entries.push(["CFBundleIconFile", metadata.iconName]);
625
+ if (metadata.category)
626
+ entries.push(["LSApplicationCategoryType", metadata.category]);
627
+ if (metadata.minimumSystemVersion)
628
+ entries.push(["LSMinimumSystemVersion", metadata.minimumSystemVersion]);
629
+ if (metadata.menuBarOnly)
630
+ entries.push(["LSUIElement", true]);
631
+ const body = entries.map(([key, value]) => ` <key>${xml(key)}</key>
632
+ ${value === true ? " <true/>" : ` <string>${xml(value)}</string>`}`).join(`
633
+ `);
634
+ return `<?xml version="1.0" encoding="UTF-8"?>
484
635
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
485
636
  <plist version="1.0">
486
637
  <dict>
487
- <key>CFBundleExecutable</key>
488
- <string>${name}</string>
489
- <key>CFBundleIdentifier</key>
490
- <string>${bundleId}</string>
491
- <key>CFBundleName</key>
492
- <string>${name}</string>
493
- <key>CFBundleShortVersionString</key>
494
- <string>${version}</string>
495
- <key>CFBundleVersion</key>
496
- <string>${version}</string>
497
- <key>CFBundlePackageType</key>
498
- <string>APPL</string>
499
- <key>NSHighResolutionCapable</key>
500
- <true/>
638
+ ${body}
501
639
  </dict>
502
640
  </plist>`;
503
- import_fs.writeFileSync(import_path.join(outputPath, "Contents", "Info.plist"), infoPlist);
641
+ }
642
+ function codesignArguments(opts) {
643
+ return [
644
+ "--force",
645
+ "--sign",
646
+ opts.identity,
647
+ "--timestamp",
648
+ ...opts.hardenedRuntime ? ["--options", "runtime"] : [],
649
+ ...opts.entitlements ? ["--entitlements", opts.entitlements] : [],
650
+ "--deep",
651
+ opts.path
652
+ ];
653
+ }
654
+ function productbuildArguments(opts) {
655
+ return [
656
+ "--component",
657
+ opts.appBundlePath,
658
+ "/Applications",
659
+ "--sign",
660
+ opts.identity,
661
+ opts.outputPath
662
+ ];
663
+ }
664
+ function notarytoolArguments(opts) {
665
+ return [
666
+ "notarytool",
667
+ "submit",
668
+ opts.artifactPath,
669
+ "--apple-id",
670
+ opts.appleId,
671
+ "--password",
672
+ opts.applePassword,
673
+ "--team-id",
674
+ opts.teamId,
675
+ "--wait"
676
+ ];
677
+ }
678
+ function createMacOSAppBundle(opts) {
679
+ try {
680
+ const { name, binaryPath, iconPath, provisioningProfile, additionalExecutables, outputPath } = opts;
681
+ const contents = import_path.join(outputPath, "Contents");
682
+ const macOS = import_path.join(contents, "MacOS");
683
+ import_fs.mkdirSync(macOS, { recursive: true });
684
+ import_fs.mkdirSync(import_path.join(contents, "Resources"), { recursive: true });
685
+ import_fs.copyFileSync(binaryPath, import_path.join(macOS, name));
686
+ import_fs.chmodSync(import_path.join(macOS, name), 493);
687
+ for (const executable of additionalExecutables || []) {
688
+ if (!import_fs.existsSync(executable))
689
+ return { success: false, error: `Additional executable not found: ${executable}` };
690
+ const destination = import_path.join(macOS, import_path.basename(executable));
691
+ import_fs.copyFileSync(executable, destination);
692
+ import_fs.chmodSync(destination, 493);
693
+ }
694
+ let iconName;
695
+ if (iconPath) {
696
+ if (!import_fs.existsSync(iconPath))
697
+ return { success: false, error: `Icon not found: ${iconPath}` };
698
+ iconName = import_path.basename(iconPath).replace(/\.icns$/i, "");
699
+ import_fs.copyFileSync(iconPath, import_path.join(contents, "Resources", `${iconName}.icns`));
700
+ }
701
+ if (provisioningProfile) {
702
+ if (!import_fs.existsSync(provisioningProfile))
703
+ return { success: false, error: `Provisioning profile not found: ${provisioningProfile}` };
704
+ import_fs.copyFileSync(provisioningProfile, import_path.join(contents, "embedded.provisionprofile"));
705
+ }
706
+ import_fs.writeFileSync(import_path.join(contents, "Info.plist"), macOSInfoPlist({ ...opts, iconName }));
504
707
  return { success: true };
505
708
  } catch (error) {
506
709
  return { success: false, error: error.message };
507
710
  }
508
711
  }
509
- async function createDMG(opts) {
712
+ var MEBIBYTE = 1024 * 1024;
713
+ function pathContentBytes(path) {
714
+ const stat = import_fs.lstatSync(path);
715
+ if (!stat.isDirectory())
716
+ return stat.size;
717
+ return import_fs.readdirSync(path).reduce((total, entry) => total + pathContentBytes(import_path.join(path, entry)), 0);
718
+ }
719
+ function dmgCapacityMegabytes(contentBytes) {
720
+ if (!Number.isSafeInteger(contentBytes) || contentBytes < 0)
721
+ throw new Error(`DMG content size must be a non-negative safe integer: ${contentBytes}`);
722
+ const contentMegabytes = Math.ceil(contentBytes / MEBIBYTE);
723
+ return Math.max(64, Math.ceil(contentMegabytes * 1.25) + 32);
724
+ }
725
+ function dmgCreateArguments(opts, contentBytes) {
726
+ return [
727
+ "create",
728
+ "-volname",
729
+ opts.volumeName,
730
+ "-srcfolder",
731
+ opts.appBundlePath,
732
+ "-size",
733
+ `${dmgCapacityMegabytes(contentBytes)}m`,
734
+ "-ov",
735
+ "-format",
736
+ "UDZO",
737
+ opts.outputPath
738
+ ];
739
+ }
740
+ var HDIUTIL_MAX_ATTEMPTS = 3;
741
+ function shouldRetryHdiutil(error, attempt, maxAttempts = HDIUTIL_MAX_ATTEMPTS) {
742
+ if (!Number.isInteger(attempt) || attempt < 1 || !Number.isInteger(maxAttempts) || maxAttempts < 1)
743
+ throw new Error("hdiutil retry attempts must be positive integers");
744
+ return attempt < maxAttempts && /(?:resource busy|resource temporarily unavailable)/i.test(error);
745
+ }
746
+ function runTool(tool, args) {
510
747
  return new Promise((resolve) => {
511
- if (!/^[^/:\n]{1,27}$/.test(opts.volumeName)) {
512
- resolve({ success: false, error: `Invalid DMG volume name "${opts.volumeName}"; must be 1..27 chars without /, :, or newline` });
513
- return;
514
- }
515
- const proc = import_child_process.spawn("hdiutil", [
516
- "create",
517
- "-volname",
518
- opts.volumeName,
519
- "-srcfolder",
520
- opts.appBundlePath,
521
- "-ov",
522
- "-format",
523
- "UDZO",
524
- opts.outputPath
525
- ]);
526
- proc.on("close", (code) => {
527
- if (code === 0) {
528
- resolve({ success: true, outputPath: opts.outputPath });
529
- } else {
530
- resolve({ success: false, error: `hdiutil exited with code ${code}` });
531
- }
748
+ const proc = import_child_process.spawn(tool, args);
749
+ let stdout = "";
750
+ let stderr = "";
751
+ proc.stdout?.on("data", (chunk) => {
752
+ stdout += chunk.toString();
532
753
  });
533
- proc.on("error", (err) => {
534
- resolve({ success: false, error: err.message });
754
+ proc.stderr?.on("data", (chunk) => {
755
+ stderr += chunk.toString();
535
756
  });
757
+ proc.on("close", (code) => {
758
+ if (code === 0)
759
+ resolve({ success: true });
760
+ else
761
+ resolve({ success: false, error: formatPackagingCommandError(tool, code, stdout, stderr) });
762
+ });
763
+ proc.on("error", (err) => resolve({ success: false, error: err.message }));
536
764
  });
537
765
  }
766
+ var runHdiutil = (args) => runTool("hdiutil", args);
767
+ async function createDMG(opts) {
768
+ if (!/^[^/:\n]{1,27}$/.test(opts.volumeName))
769
+ return { success: false, error: `Invalid DMG volume name "${opts.volumeName}"; must be 1..27 chars without /, :, or newline` };
770
+ const args = dmgCreateArguments(opts, pathContentBytes(opts.appBundlePath));
771
+ const failures = [];
772
+ for (let attempt = 1;attempt <= HDIUTIL_MAX_ATTEMPTS; attempt++) {
773
+ const result = await runHdiutil(args);
774
+ if (result.success)
775
+ return { success: true, outputPath: opts.outputPath };
776
+ failures.push(`attempt ${attempt}: ${result.error}`);
777
+ if (!shouldRetryHdiutil(result.error, attempt))
778
+ return { success: false, error: failures.join(`
779
+ `) };
780
+ import_fs.rmSync(opts.outputPath, { force: true });
781
+ await new Promise((resolve) => setTimeout(resolve, attempt * 2000));
782
+ }
783
+ return { success: false, error: failures.join(`
784
+ `) };
785
+ }
538
786
  async function createPKG(opts) {
539
- return new Promise((resolve) => {
540
- if (!/^[a-zA-Z0-9._-]+$/.test(opts.identifier) || !opts.identifier.includes(".")) {
541
- resolve({ success: false, error: `Invalid pkg identifier "${opts.identifier}"; expected reverse-DNS like com.example.app` });
542
- return;
543
- }
544
- if (!/^[A-Za-z0-9._+-]+$/.test(opts.version)) {
545
- resolve({ success: false, error: `Invalid pkg version "${opts.version}"` });
546
- return;
547
- }
548
- const tempDir = import_fs.mkdtempSync(import_path.join(import_os.tmpdir(), "craft-pkg-"));
787
+ if (!/^[a-zA-Z0-9._-]+$/.test(opts.identifier) || !opts.identifier.includes("."))
788
+ return { success: false, error: `Invalid pkg identifier "${opts.identifier}"; expected reverse-DNS like com.example.app` };
789
+ if (!/^[A-Za-z0-9._+-]+$/.test(opts.version))
790
+ return { success: false, error: `Invalid pkg version "${opts.version}"` };
791
+ if (opts.appStore) {
792
+ if (!opts.installerIdentity)
793
+ return { success: false, error: 'App Store packaging requires macos.installerIdentity (a "3rd Party Mac Developer Installer" identity)' };
794
+ const built = await runTool("productbuild", productbuildArguments({
795
+ appBundlePath: opts.appBundlePath,
796
+ outputPath: opts.outputPath,
797
+ identity: opts.installerIdentity
798
+ }));
799
+ return built.success ? { success: true, outputPath: opts.outputPath } : { success: false, error: built.error };
800
+ }
801
+ const tempDir = import_fs.mkdtempSync(import_path.join(import_os.tmpdir(), "craft-pkg-"));
802
+ try {
549
803
  const appsDir = import_path.join(tempDir, "Applications");
550
804
  import_fs.mkdirSync(appsDir, { recursive: true });
551
805
  import_fs.cpSync(opts.appBundlePath, import_path.join(appsDir, import_path.basename(opts.appBundlePath)), { recursive: true });
552
- const proc = import_child_process.spawn("pkgbuild", [
806
+ const built = await runTool("pkgbuild", [
553
807
  "--root",
554
808
  tempDir,
555
809
  "--identifier",
@@ -558,64 +812,116 @@ async function createPKG(opts) {
558
812
  opts.version,
559
813
  "--install-location",
560
814
  "/",
815
+ ...opts.installerIdentity ? ["--sign", opts.installerIdentity] : [],
561
816
  opts.outputPath
562
817
  ]);
563
- proc.on("close", (code) => {
564
- import_fs.rmSync(tempDir, { recursive: true, force: true });
565
- if (code === 0) {
566
- resolve({ success: true, outputPath: opts.outputPath });
567
- } else {
568
- resolve({ success: false, error: `pkgbuild exited with code ${code}` });
569
- }
570
- });
571
- proc.on("error", (err) => {
572
- import_fs.rmSync(tempDir, { recursive: true, force: true });
573
- resolve({ success: false, error: err.message });
574
- });
575
- });
818
+ return built.success ? { success: true, outputPath: opts.outputPath } : { success: false, error: built.error };
819
+ } finally {
820
+ import_fs.rmSync(tempDir, { recursive: true, force: true });
821
+ }
822
+ }
823
+ function xml(value) {
824
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll(`'`, "&apos;");
825
+ }
826
+ function wixIdentifier(value) {
827
+ const sanitized = value.replace(/[^A-Za-z0-9_.]/g, "_");
828
+ return /^[A-Za-z_]/.test(sanitized) ? sanitized : `_${sanitized}`;
829
+ }
830
+ function deterministicGuid(value) {
831
+ const digest = import_crypto.createHash("sha256").update(value).digest("hex").slice(0, 32).split("");
832
+ digest[12] = "4";
833
+ digest[16] = (Number.parseInt(digest[16], 16) & 3 | 8).toString(16);
834
+ const hex = digest.join("").toUpperCase();
835
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
836
+ }
837
+ function windowsArchitecture(architecture) {
838
+ if (architecture === "ia32" || architecture === "x86")
839
+ return "x86";
840
+ if (architecture === "x64" || architecture === "arm64")
841
+ return architecture;
842
+ throw new Error(`Unsupported Windows package architecture: ${architecture}`);
843
+ }
844
+ function renderWixSource(opts, sourceName) {
845
+ if (!/^[0-9]+\.[0-9]+\.[0-9]+(?:\.[0-9]+)?$/.test(opts.version))
846
+ throw new Error(`MSI version must have 3 or 4 numeric parts: ${opts.version}`);
847
+ const id = wixIdentifier(opts.name);
848
+ const manufacturer = opts.manufacturer.trim() || "Unknown";
849
+ const upgradeCode = deterministicGuid(`${manufacturer}/${opts.name}`);
850
+ const programFilesFolder = opts.architecture === "x86" ? "ProgramFilesFolder" : "ProgramFiles64Folder";
851
+ const win64 = opts.architecture === "x86" ? "no" : "yes";
852
+ return `<?xml version="1.0" encoding="UTF-8"?>
853
+ <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
854
+ <Product Id="*" Name="${xml(opts.name)}" Language="1033" Version="${opts.version}" Manufacturer="${xml(manufacturer)}" UpgradeCode="${upgradeCode}">
855
+ <Package InstallerVersion="500" Compressed="yes" InstallScope="perMachine" Platform="${opts.architecture}" />
856
+ <MajorUpgrade AllowDowngrades="yes" />
857
+ <MediaTemplate EmbedCab="yes" />
858
+ <Directory Id="TARGETDIR" Name="SourceDir">
859
+ <Directory Id="${programFilesFolder}">
860
+ <Directory Id="INSTALLFOLDER" Name="${xml(opts.name)}">
861
+ <Component Id="${id}Executable" Guid="*" Win64="${win64}">
862
+ <File Id="${id}File" Source="${xml(sourceName)}" KeyPath="yes" />
863
+ </Component>
864
+ </Directory>
865
+ </Directory>
866
+ </Directory>
867
+ <Feature Id="ProductFeature" Title="${xml(opts.name)}" Level="1">
868
+ <ComponentRef Id="${id}Executable" />
869
+ </Feature>
870
+ </Product>
871
+ </Wix>
872
+ `;
576
873
  }
577
- async function createMSI(_opts) {
578
- return new Promise((resolve) => {
579
- import_child_process.exec("candle.exe -?", (error) => {
580
- if (error) {
581
- resolve({
582
- success: false,
583
- error: "WiX Toolset not found. Install from https://wixtoolset.org/"
584
- });
585
- } else {
586
- resolve({
587
- success: false,
588
- error: "MSI creation requires Windows platform and WiX Toolset"
589
- });
590
- }
591
- });
874
+ function windowsExecutableName(name) {
875
+ if (name.length === 0 || name.trim() !== name || /[<>:"/\\|?*\u0000-\u001F]/.test(name) || /[. ]$/.test(name))
876
+ throw new Error(`Invalid Windows application name: ${JSON.stringify(name)}`);
877
+ if (/^(?:CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(?:\.|$)/i.test(name))
878
+ throw new Error(`Reserved Windows application name: ${JSON.stringify(name)}`);
879
+ return `${name}.exe`;
880
+ }
881
+ function runCommand(command, args, cwd) {
882
+ return new Promise((resolve, reject) => {
883
+ const child = import_child_process.spawn(command, args, { cwd, stdio: "inherit", windowsHide: true });
884
+ child.once("error", reject);
885
+ child.once("close", (code) => code === 0 ? resolve() : reject(new Error(`${command} exited with code ${code}`)));
592
886
  });
593
887
  }
888
+ async function createMSI(opts) {
889
+ const tempDir = import_fs.mkdtempSync(import_path.join(import_os.tmpdir(), "craft-msi-"));
890
+ try {
891
+ const binaryName = windowsExecutableName(opts.name);
892
+ const sourcePath = import_path.join(tempDir, binaryName);
893
+ const wxsPath = import_path.join(tempDir, "installer.wxs");
894
+ const wixobjPath = import_path.join(tempDir, "installer.wixobj");
895
+ import_fs.copyFileSync(opts.binaryPath, sourcePath);
896
+ import_fs.writeFileSync(wxsPath, renderWixSource(opts, binaryName));
897
+ await runCommand("candle.exe", ["-nologo", "-out", wixobjPath, wxsPath], tempDir);
898
+ await runCommand("light.exe", ["-nologo", "-sval", "-out", opts.outputPath, wixobjPath], tempDir);
899
+ if (opts.certificatePath) {
900
+ const signArgs = ["sign", "/fd", "sha256", "/tr", "https://timestamp.digicert.com", "/td", "sha256", "/f", opts.certificatePath];
901
+ if (opts.certificatePassword)
902
+ signArgs.push("/p", opts.certificatePassword);
903
+ signArgs.push(opts.outputPath);
904
+ await runCommand("signtool.exe", signArgs);
905
+ await runCommand("signtool.exe", ["verify", "/pa", "/all", opts.outputPath]);
906
+ }
907
+ return { success: true, outputPath: opts.outputPath };
908
+ } catch (error) {
909
+ const message = error instanceof Error ? error.message : String(error);
910
+ const missingTool = /ENOENT|not found/i.test(message);
911
+ return { success: false, error: missingTool ? `WiX Toolset not found: ${message}` : message };
912
+ } finally {
913
+ import_fs.rmSync(tempDir, { recursive: true, force: true });
914
+ }
915
+ }
594
916
  async function createZIP(opts) {
595
- const archiverFn2 = await loadArchiver();
596
- return new Promise((resolve) => {
597
- const output = import_fs.createWriteStream(opts.outputPath);
598
- const archive = archiverFn2("zip", { zlib: { level: 9 } });
599
- let settled = false;
600
- const settle = (r) => {
601
- if (settled)
602
- return;
603
- settled = true;
604
- resolve(r);
605
- };
606
- output.on("close", () => {
607
- settle({ success: true, outputPath: opts.outputPath });
608
- });
609
- output.on("error", (err) => {
610
- settle({ success: false, error: `Failed to write ZIP: ${err.message}` });
611
- });
612
- archive.on("error", (err) => {
613
- settle({ success: false, error: err.message });
614
- });
615
- archive.pipe(output);
616
- archive.file(opts.binaryPath, { name: `${opts.name}.exe` });
617
- archive.finalize();
618
- });
917
+ try {
918
+ const data = new Uint8Array(import_fs.readFileSync(opts.binaryPath));
919
+ const zip = buildZip([{ name: `${opts.name}.exe`, data }]);
920
+ import_fs.writeFileSync(opts.outputPath, zip);
921
+ return { success: true, outputPath: opts.outputPath };
922
+ } catch (err) {
923
+ return { success: false, error: `Failed to write ZIP: ${err.message}` };
924
+ }
619
925
  }
620
926
  async function createDEB(opts) {
621
927
  return new Promise((resolve) => {
@@ -2082,6 +2388,9 @@ class Window {
2082
2388
  async setPosition(x, y, animate) {
2083
2389
  await this._call("setPosition", { x, y, animate });
2084
2390
  }
2391
+ async moveBy(dx, dy) {
2392
+ await this._call("moveBy", { dx, dy });
2393
+ }
2085
2394
  async getPosition() {
2086
2395
  return this._call("getPosition");
2087
2396
  }
@@ -2109,6 +2418,9 @@ class Window {
2109
2418
  async setMovable(movable) {
2110
2419
  await this._call("setMovable", { movable });
2111
2420
  }
2421
+ async startDrag() {
2422
+ await this._call("startDrag");
2423
+ }
2112
2424
  async isMovable() {
2113
2425
  return this._call("isMovable");
2114
2426
  }
@@ -2226,12 +2538,14 @@ class WindowManager {
2226
2538
  setTitle = (title) => this.current.setTitle(title);
2227
2539
  setSize = (width, height, animate) => this.current.setSize(width, height, animate);
2228
2540
  setPosition = (x, y, animate) => this.current.setPosition(x, y, animate);
2541
+ moveBy = (dx, dy) => this.current.moveBy(dx, dy);
2229
2542
  setBounds = (bounds, animate) => this.current.setBounds(bounds, animate);
2230
2543
  setAlwaysOnTop = (alwaysOnTop) => this.current.setAlwaysOnTop(alwaysOnTop);
2231
2544
  setOpacity = (opacity) => this.current.setOpacity(opacity);
2232
2545
  setBackgroundColor = (color) => this.current.setBackgroundColor(color);
2233
2546
  setVibrancy = (vibrancy) => this.current.setVibrancy(vibrancy);
2234
2547
  setResizable = (resizable) => this.current.setResizable(resizable);
2548
+ startDrag = () => this.current.startDrag();
2235
2549
  getState = () => this.current.getState();
2236
2550
  on = (event, handler) => this.current.on(event, handler);
2237
2551
  once = (event, handler) => this.current.once(event, handler);
@@ -3674,8 +3988,8 @@ var crypto = {
3674
3988
  if (!hasNodeCrypto) {
3675
3989
  throw new CraftCryptoError("MD5 is not available in this runtime — WebCrypto does not implement it. " + "Use sha256/sha512, or call this from Node.");
3676
3990
  }
3677
- const { createHash: createHash2 } = await import("node:crypto");
3678
- return createHash2("md5").update(data).digest("hex");
3991
+ const { createHash: createHash3 } = await import("node:crypto");
3992
+ return createHash3("md5").update(data).digest("hex");
3679
3993
  }
3680
3994
  if (typeof globalThis.crypto !== "undefined" && globalThis.crypto.subtle) {
3681
3995
  const encoder = new TextEncoder;
@@ -3683,8 +3997,8 @@ var crypto = {
3683
3997
  const hashBuffer = await globalThis.crypto.subtle.digest(algorithm === "sha256" ? "SHA-256" : "SHA-512", dataBuffer);
3684
3998
  return bufferToHex(hashBuffer);
3685
3999
  }
3686
- const { createHash } = await import("node:crypto");
3687
- return createHash(algorithm).update(data).digest("hex");
4000
+ const { createHash: createHash2 } = await import("node:crypto");
4001
+ return createHash2(algorithm).update(data).digest("hex");
3688
4002
  },
3689
4003
  async encrypt(data, key) {
3690
4004
  if (typeof window !== "undefined" && window.craft?.crypto) {
@@ -3999,7 +4313,7 @@ async function getSystemInfo() {
3999
4313
  uptime: 0
4000
4314
  };
4001
4315
  }
4002
- async function exec2(command, options) {
4316
+ async function exec(command, options) {
4003
4317
  if (typeof window !== "undefined" && window.craft) {
4004
4318
  return callBridge3("process.exec", { command, ...options });
4005
4319
  }
@@ -4179,18 +4493,18 @@ async function open(target) {
4179
4493
  const platform = getPlatform();
4180
4494
  const { execFile } = await import("node:child_process");
4181
4495
  const { promisify } = await import("node:util");
4182
- const exec3 = promisify(execFile);
4496
+ const exec2 = promisify(execFile);
4183
4497
  switch (platform) {
4184
4498
  case "darwin":
4185
4499
  case "macos":
4186
- await exec3("open", [target]);
4500
+ await exec2("open", [target]);
4187
4501
  return;
4188
4502
  case "win32":
4189
4503
  case "windows":
4190
- await exec3("cmd.exe", ["/c", "start", '""', target]);
4504
+ await exec2("cmd.exe", ["/c", "start", '""', target]);
4191
4505
  return;
4192
4506
  case "linux":
4193
- await exec3("xdg-open", [target]);
4507
+ await exec2("xdg-open", [target]);
4194
4508
  return;
4195
4509
  default:
4196
4510
  throw new Error(`Unsupported platform: ${platform}`);
@@ -8169,9 +8483,9 @@ async function buildCSS(options) {
8169
8483
  console.warn("buildCSS can only be run in Node.js environment");
8170
8484
  return;
8171
8485
  }
8172
- const { exec: exec3 } = await import("child_process");
8486
+ const { exec: exec2 } = await import("child_process");
8173
8487
  const { promisify } = await import("util");
8174
- const execAsync = promisify(exec3);
8488
+ const execAsync = promisify(exec2);
8175
8489
  const args = [
8176
8490
  "build",
8177
8491
  "--content",
@@ -8192,109 +8506,123 @@ async function buildCSS(options) {
8192
8506
  // src/styles/sidebars.ts
8193
8507
  var tahoeStyles = {
8194
8508
  sidebar: `
8195
- w-56 h-full flex flex-col
8196
- bg-white/70 dark:bg-neutral-900/70
8197
- backdrop-blur-xl backdrop-saturate-150
8198
- border-r border-black/5 dark:border-white/5
8509
+ w-[250px] h-full flex flex-col
8510
+ bg-white/55 dark:bg-[#1e1e23]/55
8511
+ backdrop-blur-[60px] backdrop-saturate-[1.8]
8512
+ text-black dark:text-white
8199
8513
  select-none
8200
8514
  `,
8201
8515
  content: `
8202
8516
  flex-1 overflow-y-auto overflow-x-hidden
8203
- py-2 px-2
8204
- scrollbar-thin scrollbar-thumb-black/10 dark:scrollbar-thumb-white/10
8517
+ px-[10px] pb-[10px]
8205
8518
  `,
8206
8519
  section: `
8207
- mb-4
8520
+ flex flex-col gap-[2px]
8208
8521
  `,
8209
8522
  sectionHeader: `
8210
- px-2 py-1.5 mb-1
8211
- text-[11px] font-semibold uppercase tracking-wider
8212
- text-neutral-500 dark:text-neutral-400
8523
+ pl-[8px] pr-[6px] pt-[16px] pb-[5px]
8524
+ text-[11px] font-semibold leading-[13px]
8525
+ text-[#3c3c43]/60 dark:text-[#ebebf5]/60
8213
8526
  `,
8214
8527
  sectionHeaderCollapsible: `
8215
- px-2 py-1.5 mb-1 flex items-center gap-1 cursor-pointer
8216
- text-[11px] font-semibold uppercase tracking-wider
8217
- text-neutral-500 dark:text-neutral-400
8218
- hover:text-neutral-700 dark:hover:text-neutral-200
8219
- transition-colors duration-150
8528
+ group flex items-center w-full
8529
+ pl-[8px] pr-[6px] pt-[16px] pb-[5px]
8530
+ text-[11px] font-semibold leading-[13px]
8531
+ text-[#3c3c43]/60 dark:text-[#ebebf5]/60
8532
+ cursor-default
8220
8533
  `,
8221
8534
  collapseChevron: `
8222
- w-3 h-3 transition-transform duration-200
8535
+ ml-auto h-[11px] w-[11px]
8536
+ text-[#3c3c43]/45 dark:text-[#ebebf5]/45
8537
+ opacity-0 group-hover:opacity-100
8538
+ transition-all duration-200
8223
8539
  `,
8224
8540
  item: `
8225
- flex items-center gap-2.5 px-2 py-1.5 mx-1 rounded-md
8226
- text-[13px] text-neutral-700 dark:text-neutral-200
8227
- cursor-pointer transition-all duration-150
8228
- hover:bg-black/5 dark:hover:bg-white/5
8541
+ flex w-full items-center
8542
+ h-[30px] rounded-[9px] pl-[4px] pr-[8px]
8543
+ text-[13px] leading-[16px] text-black dark:text-white
8544
+ cursor-default transition-colors duration-150 ease-out
8545
+ hover:bg-black/4 dark:hover:bg-white/6
8546
+ active:bg-black/10 dark:active:bg-white/18
8229
8547
  `,
8230
8548
  itemSelected: `
8231
- flex items-center gap-2.5 px-2 py-1.5 mx-1 rounded-md
8232
- text-[13px] text-white
8233
- cursor-pointer
8234
- bg-blue-500 dark:bg-blue-600
8235
- shadow-sm
8549
+ flex w-full items-center
8550
+ h-[30px] rounded-[9px] pl-[4px] pr-[8px]
8551
+ text-[13px] leading-[16px] text-black dark:text-white
8552
+ cursor-default
8553
+ bg-black/8 dark:bg-white/14
8554
+ `,
8555
+ disclosure: `
8556
+ flex h-[16px] w-[18px] shrink-0 items-center justify-center
8557
+ `,
8558
+ disclosureChevron: `
8559
+ h-[11px] w-[11px]
8560
+ text-[#3c3c43]/55 dark:text-[#ebebf5]/55
8561
+ transition-transform duration-200 ease-out
8236
8562
  `,
8237
8563
  itemIcon: `
8238
- w-4 h-4 flex-shrink-0
8239
- text-neutral-500 dark:text-neutral-400
8564
+ h-[17px] w-[17px] flex-shrink-0
8565
+ text-[#0088ff]
8240
8566
  `,
8241
8567
  itemIconSelected: `
8242
- w-4 h-4 flex-shrink-0
8243
- text-white/90
8568
+ h-[17px] w-[17px] flex-shrink-0
8569
+ text-[#0088ff]
8570
+ `,
8571
+ itemIconSlot: `
8572
+ flex h-[20px] w-[22px] shrink-0 items-center justify-center mr-[7px]
8244
8573
  `,
8245
8574
  itemLabel: `
8246
8575
  flex-1 truncate
8247
8576
  `,
8248
8577
  itemBadge: `
8249
- px-1.5 py-0.5 rounded-full text-[10px] font-medium
8250
- bg-neutral-200 dark:bg-neutral-700
8251
- text-neutral-600 dark:text-neutral-300
8578
+ ml-[8px] shrink-0 tabular-nums
8579
+ text-[13px] leading-[16px]
8580
+ text-[#3c3c43]/60 dark:text-[#ebebf5]/60
8252
8581
  `,
8253
8582
  itemBadgeSelected: `
8254
- px-1.5 py-0.5 rounded-full text-[10px] font-medium
8255
- bg-white/20 text-white
8583
+ ml-[8px] shrink-0 tabular-nums
8584
+ text-[13px] leading-[16px]
8585
+ text-[#3c3c43]/60 dark:text-[#ebebf5]/60
8256
8586
  `,
8257
8587
  children: `
8258
- ml-4 mt-0.5 border-l border-neutral-200 dark:border-neutral-700
8588
+ ml-[16px] mt-[2px] flex flex-col gap-[2px]
8259
8589
  `,
8260
8590
  dragIndicator: `
8261
- absolute left-0 right-0 h-0.5 bg-blue-500
8591
+ absolute left-0 right-0 h-0.5 bg-[#0088ff]
8262
8592
  pointer-events-none
8263
8593
  `,
8264
8594
  searchContainer: `
8265
- px-2 pb-2
8595
+ px-[10px] pb-[6px]
8266
8596
  `,
8267
8597
  searchInput: `
8268
- w-full px-3 py-1.5 rounded-md
8269
- text-[13px] placeholder:text-neutral-400
8270
- bg-black/5 dark:bg-white/5
8598
+ h-[28px] w-full rounded-[8px] px-[8px]
8599
+ text-[13px] text-black dark:text-white
8600
+ placeholder:text-[#3c3c43]/50 dark:placeholder:text-[#ebebf5]/50
8601
+ bg-black/5 dark:bg-white/10
8271
8602
  border border-transparent
8272
- focus:outline-none focus:border-blue-500/50 focus:bg-white dark:focus:bg-neutral-800
8603
+ focus:outline-none focus:ring-2 focus:ring-[#0088ff]/60
8273
8604
  transition-all duration-150
8274
8605
  `,
8275
8606
  header: `
8276
- px-3 py-3 border-b border-black/5 dark:border-white/5
8277
- flex items-center gap-3
8607
+ h-[52px] px-[21px] flex items-center gap-3
8278
8608
  `,
8279
8609
  headerAvatar: `
8280
- w-8 h-8 rounded-full bg-gradient-to-br from-blue-400 to-blue-600
8281
- flex items-center justify-center text-white text-sm font-medium
8610
+ w-[30px] h-[30px] rounded-full bg-black/10 dark:bg-white/15
8611
+ flex items-center justify-center text-[13px] font-semibold
8282
8612
  `,
8283
8613
  headerTitle: `
8284
- text-[13px] font-medium text-neutral-800 dark:text-neutral-100
8614
+ text-[13px] font-medium text-black dark:text-white
8285
8615
  `,
8286
8616
  headerSubtitle: `
8287
- text-[11px] text-neutral-500 dark:text-neutral-400
8617
+ text-[11px] text-[#3c3c43]/60 dark:text-[#ebebf5]/60
8288
8618
  `,
8289
8619
  footer: `
8290
- px-2 py-2 border-t border-black/5 dark:border-white/5
8291
- flex items-center gap-2
8620
+ px-[10px] pb-[8px] pt-[4px] flex items-center gap-2
8292
8621
  `,
8293
8622
  footerButton: `
8294
- p-1.5 rounded-md
8295
- text-neutral-500 dark:text-neutral-400
8296
- hover:bg-black/5 dark:hover:bg-white/5
8297
- hover:text-neutral-700 dark:hover:text-neutral-200
8623
+ grid h-[28px] w-[28px] place-items-center rounded-[7px]
8624
+ text-[#3c3c43]/70 dark:text-[#ebebf5]/70
8625
+ hover:bg-black/5 dark:hover:bg-white/10
8298
8626
  transition-colors duration-150
8299
8627
  `
8300
8628
  };
@@ -8713,19 +9041,21 @@ function renderTahoeSidebar(data) {
8713
9041
  const itemClass = isSelected ? s.itemSelected : s.item;
8714
9042
  const iconClass = isSelected ? s.itemIconSelected : s.itemIcon;
8715
9043
  const badgeClass = isSelected ? s.itemBadgeSelected : s.itemBadge;
9044
+ const hasChildren = (item.children?.length ?? 0) > 0;
9045
+ const chevron = hasChildren ? `<span class='${s.disclosureChevron}${item.expanded ? " rotate-90" : ""}'>${icons.chevronRight}</span>` : "";
9046
+ const tint = item.color ? ` style='color: ${item.color}'` : "";
8716
9047
  let html = `
8717
- <div class='${itemClass}' data-id='${item.id}' style='padding-left: ${8 + depth * 16}px'>
8718
- <span class='${iconClass}'>${getIcon2(item.icon || "document")}</span>
9048
+ <div class='${itemClass}' data-id='${item.id}'${depth > 0 ? ` style='padding-left: ${4 + depth * 16}px'` : ""}>
9049
+ <span class='${s.disclosure}'>${chevron}</span>
9050
+ <span class='${s.itemIconSlot}'><span class='${iconClass}'${tint}>${getIcon2(item.icon || "document")}</span></span>
8719
9051
  <span class='${s.itemLabel}'>${item.label}</span>
8720
9052
  ${item.badge ? `<span class='${badgeClass}'>${item.badge}</span>` : ""}
8721
9053
  </div>
8722
9054
  `;
8723
9055
  if (item.children && item.expanded) {
8724
- html += `<div class='${s.children}'>`;
8725
9056
  item.children.forEach((child) => {
8726
9057
  html += renderItem(child, depth + 1);
8727
9058
  });
8728
- html += `</div>`;
8729
9059
  }
8730
9060
  return html;
8731
9061
  };
@@ -8734,8 +9064,8 @@ function renderTahoeSidebar(data) {
8734
9064
  <div class='${s.section}'>
8735
9065
  ${section.title ? `
8736
9066
  <div class="${section.collapsible ? s.sectionHeaderCollapsible : s.sectionHeader}" data-section="${section.id}">
8737
- ${section.collapsible ? `<span class="${s.collapseChevron}${section.collapsed ? "" : " rotate-90"}">${icons.chevronRight}</span>` : ""}
8738
- ${section.title}
9067
+ <span class='truncate'>${section.title}</span>
9068
+ ${section.collapsible ? `<span class='${s.collapseChevron}${section.collapsed ? " -rotate-90" : ""}'>${icons.chevronDown}</span>` : ""}
8739
9069
  </div>
8740
9070
  ` : ""}
8741
9071
  ${!section.collapsed ? section.items.map((item) => renderItem(item)).join("") : ""}
@@ -9475,7 +9805,7 @@ __export(exports_updater, {
9475
9805
  var import_fs3 = require("fs");
9476
9806
  var import_path2 = require("path");
9477
9807
  var import_child_process2 = require("child_process");
9478
- var import_crypto2 = require("crypto");
9808
+ var import_crypto3 = require("crypto");
9479
9809
  var import_events3 = require("events");
9480
9810
  function findFirstByName(root, namePattern, kind) {
9481
9811
  const out = import_child_process2.execFileSync("find", [root, "-name", namePattern, "-type", kind, "-print0"]);
@@ -9492,6 +9822,12 @@ class AutoUpdater extends import_events3.EventEmitter {
9492
9822
  cachedEtag = null;
9493
9823
  cachedLastModified = null;
9494
9824
  cachedManifest = null;
9825
+ emitError(error) {
9826
+ if (this.listenerCount("error") > 0)
9827
+ this.emit("error", error);
9828
+ else
9829
+ console.error("[Updater]", error);
9830
+ }
9495
9831
  constructor(config) {
9496
9832
  super();
9497
9833
  const url = config.updateUrl;
@@ -9565,7 +9901,7 @@ class AutoUpdater extends import_events3.EventEmitter {
9565
9901
  return null;
9566
9902
  }
9567
9903
  } catch (error) {
9568
- this.emit("error", error);
9904
+ this.emitError(error);
9569
9905
  return null;
9570
9906
  }
9571
9907
  }
@@ -9619,11 +9955,32 @@ class AutoUpdater extends import_events3.EventEmitter {
9619
9955
  this.emit("download-progress", progress);
9620
9956
  }
9621
9957
  fileStream.end();
9958
+ await new Promise((resolve, reject) => {
9959
+ fileStream.once("finish", resolve);
9960
+ fileStream.once("error", reject);
9961
+ });
9622
9962
  const hash = await this.computeFileHash(this.downloadPath);
9623
9963
  if (hash !== expectedHash) {
9624
9964
  import_fs3.unlinkSync(this.downloadPath);
9625
9965
  throw new Error("Download verification failed: hash mismatch");
9626
9966
  }
9967
+ if (deltaUpdate) {
9968
+ if (!import_fs3.statSync(this.config.appPath).isFile()) {
9969
+ throw new Error("Delta updates require appPath to point to the installed bundle file");
9970
+ }
9971
+ const patchPath = this.downloadPath;
9972
+ const reconstructedPath = import_path2.join(downloadDir, `reconstructed-${import_path2.basename(platformUpdate.url)}`);
9973
+ await DeltaGenerator.apply(this.config.appPath, patchPath, reconstructedPath);
9974
+ const reconstructedHash = await this.computeFileHash(reconstructedPath);
9975
+ if (reconstructedHash !== platformUpdate.sha256) {
9976
+ try {
9977
+ import_fs3.unlinkSync(reconstructedPath);
9978
+ } catch {}
9979
+ throw new Error("Delta reconstruction failed: full bundle hash mismatch");
9980
+ }
9981
+ import_fs3.unlinkSync(patchPath);
9982
+ this.downloadPath = reconstructedPath;
9983
+ }
9627
9984
  if (this.config.publicKeyPem && !platformUpdate.signature) {
9628
9985
  import_fs3.unlinkSync(this.downloadPath);
9629
9986
  throw new Error("Updater: publicKeyPem is configured but the manifest entry has no signature. " + "Refusing to install unsigned update.");
@@ -9645,7 +10002,7 @@ class AutoUpdater extends import_events3.EventEmitter {
9645
10002
  }
9646
10003
  return this.downloadPath;
9647
10004
  } catch (error) {
9648
- this.emit("error", error);
10005
+ this.emitError(error);
9649
10006
  return null;
9650
10007
  }
9651
10008
  }
@@ -9705,7 +10062,8 @@ class AutoUpdater extends import_events3.EventEmitter {
9705
10062
  }
9706
10063
  } catch (error) {
9707
10064
  progress.phase = "error";
9708
- this.emit("error", error);
10065
+ this.emitError(error);
10066
+ throw error;
9709
10067
  }
9710
10068
  }
9711
10069
  async installMacOSUpdate() {
@@ -9824,7 +10182,7 @@ class AutoUpdater extends import_events3.EventEmitter {
9824
10182
  }
9825
10183
  async computeFileHash(filePath) {
9826
10184
  return new Promise((resolve, reject) => {
9827
- const hash = import_crypto2.createHash("sha256");
10185
+ const hash = import_crypto3.createHash("sha256");
9828
10186
  const stream = import_fs3.createReadStream(filePath);
9829
10187
  stream.on("data", (chunk) => hash.update(chunk));
9830
10188
  stream.on("end", () => resolve(hash.digest("hex")));
@@ -9896,7 +10254,7 @@ class DeltaGenerator {
9896
10254
  }
9897
10255
  static async computeHash(filePath) {
9898
10256
  return new Promise((resolve, reject) => {
9899
- const hash = import_crypto2.createHash("sha256");
10257
+ const hash = import_crypto3.createHash("sha256");
9900
10258
  const stream = import_fs3.createReadStream(filePath);
9901
10259
  stream.on("data", (chunk) => hash.update(chunk));
9902
10260
  stream.on("end", () => resolve(hash.digest("hex")));
@@ -9915,7 +10273,7 @@ function generateUpdateManifest(options) {
9915
10273
  if (!info)
9916
10274
  continue;
9917
10275
  const bytes = import_fs3.readFileSync(info.path);
9918
- const hash = import_crypto2.createHash("sha256").update(bytes).digest("hex");
10276
+ const hash = import_crypto3.createHash("sha256").update(bytes).digest("hex");
9919
10277
  manifest.platforms[platform] = {
9920
10278
  url: info.url,
9921
10279
  size: bytes.length,
@@ -9925,7 +10283,7 @@ function generateUpdateManifest(options) {
9925
10283
  const platformDeltas = options.deltas?.filter((d) => d.platform === platform) || [];
9926
10284
  for (const delta of platformDeltas) {
9927
10285
  const deltaBytes = import_fs3.readFileSync(delta.path);
9928
- const deltaHash = import_crypto2.createHash("sha256").update(deltaBytes).digest("hex");
10286
+ const deltaHash = import_crypto3.createHash("sha256").update(deltaBytes).digest("hex");
9929
10287
  manifest.platforms[platform].delta.push({
9930
10288
  fromVersion: delta.fromVersion,
9931
10289
  url: delta.url,
@@ -10023,7 +10381,7 @@ var execFileAsync = import_node_util.promisify(import_node_child_process.execFil
10023
10381
  var versionMismatchWarned = false;
10024
10382
  function getSdkVersion() {
10025
10383
  try {
10026
- const pkgPath = import_node_path.join("/home/runner/work/craft/craft/packages/typescript/src", "..", "package.json");
10384
+ const pkgPath = import_node_path.join("/Users/chris/Code/Tools/craft/packages/typescript/src", "..", "package.json");
10027
10385
  return JSON.parse(import_node_fs2.readFileSync(pkgPath, "utf-8")).version ?? "unknown";
10028
10386
  } catch {
10029
10387
  return "unknown";
@@ -10194,6 +10552,13 @@ class CraftApp {
10194
10552
  args.push("--menubar-only");
10195
10553
  if (window3?.titlebarHidden)
10196
10554
  args.push("--titlebar-hidden");
10555
+ if (window3?.webSidebarMaterial) {
10556
+ args.push("--web-sidebar-material");
10557
+ if (window3?.webSidebarWidth)
10558
+ args.push("--web-sidebar-width", String(window3.webSidebarWidth));
10559
+ if (window3?.webSidebarMaterialOpacity !== undefined)
10560
+ args.push("--web-sidebar-material-opacity", String(window3.webSidebarMaterialOpacity));
10561
+ }
10197
10562
  if (window3?.icon)
10198
10563
  args.push("--icon", window3.icon);
10199
10564
  if (window3?.nativeSidebar) {
@@ -10209,7 +10574,7 @@ class CraftApp {
10209
10574
  } : undefined;
10210
10575
  if (sidebarConfig?.variant === "desktop") {
10211
10576
  sidebarConfig.material = sidebarConfig.material ?? "sidebar";
10212
- sidebarConfig.backgroundEffect = sidebarConfig.backgroundEffect ?? "shimmer";
10577
+ sidebarConfig.backgroundEffect = sidebarConfig.backgroundEffect ?? "vibrancy";
10213
10578
  sidebarConfig.allowsVibrancy = sidebarConfig.allowsVibrancy ?? true;
10214
10579
  }
10215
10580
  if (sidebarConfig) {