craft-native 0.0.37 → 0.0.50

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