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.js CHANGED
@@ -17,7 +17,7 @@ var __require = import.meta.require;
17
17
 
18
18
  // src/index.ts
19
19
  import { execFile, spawn as spawn4 } from "child_process";
20
- import { mkdtempSync as mkdtempSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
20
+ import { mkdtempSync as mkdtempSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
21
21
  import { tmpdir as tmpdir2 } from "os";
22
22
  import { join as join3 } from "path";
23
23
  import { promisify } from "util";
@@ -58,27 +58,103 @@ function craftBinaryNotFoundMessage(triedPath) {
58
58
  }
59
59
 
60
60
  // src/package.ts
61
- import { exec, spawn } from "child_process";
61
+ import { spawn } from "child_process";
62
+ import { createHash } from "crypto";
62
63
  import {
63
64
  chmodSync,
64
65
  copyFileSync,
65
66
  cpSync,
66
- createWriteStream,
67
67
  existsSync as existsSync2,
68
+ lstatSync,
68
69
  mkdirSync,
69
70
  mkdtempSync,
71
+ readFileSync,
72
+ readdirSync,
70
73
  rmSync,
71
74
  writeFileSync
72
75
  } from "fs";
73
76
  import { homedir, tmpdir } from "os";
74
77
  import { basename, join } from "path";
75
- var archiverFn = null;
76
- async function loadArchiver() {
77
- if (!archiverFn) {
78
- const mod = await import("archiver");
79
- archiverFn = mod.default ?? mod;
80
- }
81
- return archiverFn;
78
+ import { deflateRawSync } from "zlib";
79
+ var CRC32_TABLE = (() => {
80
+ const table = new Uint32Array(256);
81
+ for (let n = 0;n < 256; n++) {
82
+ let c = n;
83
+ for (let k = 0;k < 8; k++)
84
+ c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
85
+ table[n] = c >>> 0;
86
+ }
87
+ return table;
88
+ })();
89
+ function crc32(data) {
90
+ let c = 4294967295;
91
+ for (let i = 0;i < data.length; i++)
92
+ c = CRC32_TABLE[(c ^ data[i]) & 255] ^ c >>> 8;
93
+ return (c ^ 4294967295) >>> 0;
94
+ }
95
+ function dosDateTime(d) {
96
+ const time = d.getHours() << 11 | d.getMinutes() << 5 | d.getSeconds() >> 1;
97
+ const date = d.getFullYear() - 1980 << 9 | d.getMonth() + 1 << 5 | d.getDate();
98
+ return { time: time & 65535, date: date & 65535 };
99
+ }
100
+ function buildZip(entries) {
101
+ const { time, date } = dosDateTime(new Date);
102
+ const locals = [];
103
+ const central = [];
104
+ let offset = 0;
105
+ for (const entry of entries) {
106
+ const nameBytes = Buffer.from(entry.name, "utf8");
107
+ const compressed = deflateRawSync(entry.data, { level: 9 });
108
+ const crc = crc32(entry.data);
109
+ const lfh = Buffer.alloc(30);
110
+ lfh.writeUInt32LE(67324752, 0);
111
+ lfh.writeUInt16LE(20, 4);
112
+ lfh.writeUInt16LE(0, 6);
113
+ lfh.writeUInt16LE(8, 8);
114
+ lfh.writeUInt16LE(time, 10);
115
+ lfh.writeUInt16LE(date, 12);
116
+ lfh.writeUInt32LE(crc, 14);
117
+ lfh.writeUInt32LE(compressed.length, 18);
118
+ lfh.writeUInt32LE(entry.data.length, 22);
119
+ lfh.writeUInt16LE(nameBytes.length, 26);
120
+ lfh.writeUInt16LE(0, 28);
121
+ locals.push(lfh, nameBytes, compressed);
122
+ const cdh = Buffer.alloc(46);
123
+ cdh.writeUInt32LE(33639248, 0);
124
+ cdh.writeUInt16LE(20, 4);
125
+ cdh.writeUInt16LE(20, 6);
126
+ cdh.writeUInt16LE(0, 8);
127
+ cdh.writeUInt16LE(8, 10);
128
+ cdh.writeUInt16LE(time, 12);
129
+ cdh.writeUInt16LE(date, 14);
130
+ cdh.writeUInt32LE(crc, 16);
131
+ cdh.writeUInt32LE(compressed.length, 20);
132
+ cdh.writeUInt32LE(entry.data.length, 24);
133
+ cdh.writeUInt16LE(nameBytes.length, 28);
134
+ cdh.writeUInt16LE(0, 30);
135
+ cdh.writeUInt16LE(0, 32);
136
+ cdh.writeUInt16LE(0, 34);
137
+ cdh.writeUInt16LE(0, 36);
138
+ cdh.writeUInt32LE(0, 38);
139
+ cdh.writeUInt32LE(offset, 42);
140
+ central.push(cdh, nameBytes);
141
+ offset += lfh.length + nameBytes.length + compressed.length;
142
+ }
143
+ const centralBuf = Buffer.concat(central);
144
+ const eocd = Buffer.alloc(22);
145
+ eocd.writeUInt32LE(101010256, 0);
146
+ eocd.writeUInt16LE(0, 4);
147
+ eocd.writeUInt16LE(0, 6);
148
+ eocd.writeUInt16LE(entries.length, 8);
149
+ eocd.writeUInt16LE(entries.length, 10);
150
+ eocd.writeUInt32LE(centralBuf.length, 12);
151
+ eocd.writeUInt32LE(offset, 16);
152
+ eocd.writeUInt16LE(0, 20);
153
+ return Buffer.concat([...locals, centralBuf, eocd]);
154
+ }
155
+ function formatPackagingCommandError(tool, code, stdout, stderr) {
156
+ const detail = `${stdout}${stderr}`.trim();
157
+ return `${tool} exited with code ${code}${detail ? `: ${detail}` : ""}`;
82
158
  }
83
159
  async function packageApp(config) {
84
160
  const results = [];
@@ -109,13 +185,20 @@ async function packageMacOS(config, outDir) {
109
185
  const results = [];
110
186
  const { name, version, bundleId = `com.myapp.${name.toLowerCase()}` } = config;
111
187
  const opts = config.macos || {};
188
+ const hardenedRuntime = !opts.appStore;
112
189
  const appBundlePath = join(outDir, `${name}.app`);
113
190
  const appBundle = createMacOSAppBundle({
114
191
  name,
115
192
  version,
116
193
  bundleId,
194
+ buildNumber: opts.buildNumber,
195
+ category: opts.category,
196
+ minimumSystemVersion: opts.minimumSystemVersion,
197
+ menuBarOnly: opts.menuBarOnly,
117
198
  binaryPath: config.binaryPath,
118
199
  iconPath: config.iconPath,
200
+ provisioningProfile: opts.provisioningProfile,
201
+ additionalExecutables: opts.additionalExecutables,
119
202
  outputPath: appBundlePath
120
203
  });
121
204
  if (!appBundle.success) {
@@ -127,12 +210,30 @@ async function packageMacOS(config, outDir) {
127
210
  });
128
211
  return results;
129
212
  }
130
- if (opts.dmg !== false) {
131
- const dmgResult = await createDMG({
132
- appBundlePath,
133
- outputPath: join(outDir, `${name}-${version}.dmg`),
134
- volumeName: name
213
+ if (opts.signIdentity) {
214
+ const signed = await runTool("codesign", codesignArguments({
215
+ path: appBundlePath,
216
+ identity: opts.signIdentity,
217
+ entitlements: opts.entitlements,
218
+ hardenedRuntime
219
+ }));
220
+ if (!signed.success) {
221
+ results.push({ success: false, platform: "macos", format: "app", error: signed.error });
222
+ return results;
223
+ }
224
+ } else if (opts.appStore) {
225
+ results.push({
226
+ success: false,
227
+ platform: "macos",
228
+ format: "pkg",
229
+ error: 'App Store packaging requires macos.signIdentity (a "3rd Party Mac Developer Application" identity)'
135
230
  });
231
+ return results;
232
+ }
233
+ results.push({ success: true, platform: "macos", format: "app", outputPath: appBundlePath });
234
+ if (opts.dmg !== false) {
235
+ const outputPath = join(outDir, `${name}-${version}.dmg`);
236
+ const dmgResult = await createDMG({ appBundlePath, outputPath, volumeName: name });
136
237
  results.push({
137
238
  success: dmgResult.success,
138
239
  platform: "macos",
@@ -140,24 +241,59 @@ async function packageMacOS(config, outDir) {
140
241
  outputPath: dmgResult.outputPath,
141
242
  error: dmgResult.error
142
243
  });
244
+ if (dmgResult.success)
245
+ results.push(...await notarizeIfRequested(opts, outputPath, "dmg"));
143
246
  }
144
- if (opts.pkg) {
247
+ if (opts.pkg || opts.appStore) {
248
+ const outputPath = join(outDir, `${name}-${version}.pkg`);
145
249
  const pkgResult = await createPKG({
146
250
  appBundlePath,
147
- outputPath: join(outDir, `${name}-${version}.pkg`),
251
+ outputPath,
148
252
  identifier: bundleId,
149
- version
253
+ version,
254
+ appStore: opts.appStore === true,
255
+ installerIdentity: opts.installerIdentity
150
256
  });
151
257
  results.push({
152
258
  success: pkgResult.success,
153
259
  platform: "macos",
154
- format: "pkg",
260
+ format: opts.appStore ? "pkg (app store)" : "pkg",
155
261
  outputPath: pkgResult.outputPath,
156
262
  error: pkgResult.error
157
263
  });
264
+ if (pkgResult.success && !opts.appStore)
265
+ results.push(...await notarizeIfRequested(opts, outputPath, "pkg"));
158
266
  }
159
267
  return results;
160
268
  }
269
+ async function notarizeIfRequested(opts, artifactPath, format) {
270
+ if (!opts.notarize)
271
+ return [];
272
+ if (!opts.appleId || !opts.applePassword || !opts.teamId) {
273
+ return [{
274
+ success: false,
275
+ platform: "macos",
276
+ format: `${format} (notarize)`,
277
+ error: "Notarization requires macos.appleId, macos.applePassword and macos.teamId"
278
+ }];
279
+ }
280
+ const submitted = await runTool("xcrun", notarytoolArguments({
281
+ artifactPath,
282
+ appleId: opts.appleId,
283
+ applePassword: opts.applePassword,
284
+ teamId: opts.teamId
285
+ }));
286
+ if (!submitted.success)
287
+ return [{ success: false, platform: "macos", format: `${format} (notarize)`, error: submitted.error }];
288
+ const stapled = await runTool("xcrun", ["stapler", "staple", artifactPath]);
289
+ return [{
290
+ success: stapled.success,
291
+ platform: "macos",
292
+ format: `${format} (notarize)`,
293
+ outputPath: stapled.success ? artifactPath : undefined,
294
+ error: stapled.success ? undefined : stapled.error
295
+ }];
296
+ }
161
297
  async function packageWindows(config, outDir) {
162
298
  const results = [];
163
299
  const { name, version } = config;
@@ -168,7 +304,10 @@ async function packageWindows(config, outDir) {
168
304
  version,
169
305
  binaryPath: config.binaryPath,
170
306
  outputPath: join(outDir, `${name}-${version}.msi`),
171
- manufacturer: config.author || "Unknown"
307
+ manufacturer: config.author || "Unknown",
308
+ architecture: opts.architecture || windowsArchitecture(process.arch),
309
+ certificatePath: opts.certificatePath,
310
+ certificatePassword: opts.certificatePassword
172
311
  });
173
312
  results.push({
174
313
  success: msiResult.success,
@@ -252,83 +391,200 @@ async function packageLinux(config, outDir) {
252
391
  }
253
392
  return results;
254
393
  }
255
- function createMacOSAppBundle(opts) {
256
- try {
257
- const { name, version, bundleId, binaryPath, outputPath } = opts;
258
- mkdirSync(join(outputPath, "Contents", "MacOS"), { recursive: true });
259
- mkdirSync(join(outputPath, "Contents", "Resources"), { recursive: true });
260
- copyFileSync(binaryPath, join(outputPath, "Contents", "MacOS", name));
261
- chmodSync(join(outputPath, "Contents", "MacOS", name), 493);
262
- const infoPlist = `<?xml version="1.0" encoding="UTF-8"?>
394
+ function macOSInfoPlist(metadata) {
395
+ const entries = [
396
+ ["CFBundleExecutable", metadata.name],
397
+ ["CFBundleIdentifier", metadata.bundleId],
398
+ ["CFBundleName", metadata.name],
399
+ ["CFBundleShortVersionString", metadata.version],
400
+ ["CFBundleVersion", metadata.buildNumber || metadata.version],
401
+ ["CFBundlePackageType", "APPL"],
402
+ ["NSHighResolutionCapable", true]
403
+ ];
404
+ if (metadata.iconName)
405
+ entries.push(["CFBundleIconFile", metadata.iconName]);
406
+ if (metadata.category)
407
+ entries.push(["LSApplicationCategoryType", metadata.category]);
408
+ if (metadata.minimumSystemVersion)
409
+ entries.push(["LSMinimumSystemVersion", metadata.minimumSystemVersion]);
410
+ if (metadata.menuBarOnly)
411
+ entries.push(["LSUIElement", true]);
412
+ const body = entries.map(([key, value]) => ` <key>${xml(key)}</key>
413
+ ${value === true ? " <true/>" : ` <string>${xml(value)}</string>`}`).join(`
414
+ `);
415
+ return `<?xml version="1.0" encoding="UTF-8"?>
263
416
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
264
417
  <plist version="1.0">
265
418
  <dict>
266
- <key>CFBundleExecutable</key>
267
- <string>${name}</string>
268
- <key>CFBundleIdentifier</key>
269
- <string>${bundleId}</string>
270
- <key>CFBundleName</key>
271
- <string>${name}</string>
272
- <key>CFBundleShortVersionString</key>
273
- <string>${version}</string>
274
- <key>CFBundleVersion</key>
275
- <string>${version}</string>
276
- <key>CFBundlePackageType</key>
277
- <string>APPL</string>
278
- <key>NSHighResolutionCapable</key>
279
- <true/>
419
+ ${body}
280
420
  </dict>
281
421
  </plist>`;
282
- writeFileSync(join(outputPath, "Contents", "Info.plist"), infoPlist);
422
+ }
423
+ function codesignArguments(opts) {
424
+ return [
425
+ "--force",
426
+ "--sign",
427
+ opts.identity,
428
+ "--timestamp",
429
+ ...opts.hardenedRuntime ? ["--options", "runtime"] : [],
430
+ ...opts.entitlements ? ["--entitlements", opts.entitlements] : [],
431
+ "--deep",
432
+ opts.path
433
+ ];
434
+ }
435
+ function productbuildArguments(opts) {
436
+ return [
437
+ "--component",
438
+ opts.appBundlePath,
439
+ "/Applications",
440
+ "--sign",
441
+ opts.identity,
442
+ opts.outputPath
443
+ ];
444
+ }
445
+ function notarytoolArguments(opts) {
446
+ return [
447
+ "notarytool",
448
+ "submit",
449
+ opts.artifactPath,
450
+ "--apple-id",
451
+ opts.appleId,
452
+ "--password",
453
+ opts.applePassword,
454
+ "--team-id",
455
+ opts.teamId,
456
+ "--wait"
457
+ ];
458
+ }
459
+ function createMacOSAppBundle(opts) {
460
+ try {
461
+ const { name, binaryPath, iconPath, provisioningProfile, additionalExecutables, outputPath } = opts;
462
+ const contents = join(outputPath, "Contents");
463
+ const macOS = join(contents, "MacOS");
464
+ mkdirSync(macOS, { recursive: true });
465
+ mkdirSync(join(contents, "Resources"), { recursive: true });
466
+ copyFileSync(binaryPath, join(macOS, name));
467
+ chmodSync(join(macOS, name), 493);
468
+ for (const executable of additionalExecutables || []) {
469
+ if (!existsSync2(executable))
470
+ return { success: false, error: `Additional executable not found: ${executable}` };
471
+ const destination = join(macOS, basename(executable));
472
+ copyFileSync(executable, destination);
473
+ chmodSync(destination, 493);
474
+ }
475
+ let iconName;
476
+ if (iconPath) {
477
+ if (!existsSync2(iconPath))
478
+ return { success: false, error: `Icon not found: ${iconPath}` };
479
+ iconName = basename(iconPath).replace(/\.icns$/i, "");
480
+ copyFileSync(iconPath, join(contents, "Resources", `${iconName}.icns`));
481
+ }
482
+ if (provisioningProfile) {
483
+ if (!existsSync2(provisioningProfile))
484
+ return { success: false, error: `Provisioning profile not found: ${provisioningProfile}` };
485
+ copyFileSync(provisioningProfile, join(contents, "embedded.provisionprofile"));
486
+ }
487
+ writeFileSync(join(contents, "Info.plist"), macOSInfoPlist({ ...opts, iconName }));
283
488
  return { success: true };
284
489
  } catch (error) {
285
490
  return { success: false, error: error.message };
286
491
  }
287
492
  }
288
- async function createDMG(opts) {
493
+ var MEBIBYTE = 1024 * 1024;
494
+ function pathContentBytes(path) {
495
+ const stat = lstatSync(path);
496
+ if (!stat.isDirectory())
497
+ return stat.size;
498
+ return readdirSync(path).reduce((total, entry) => total + pathContentBytes(join(path, entry)), 0);
499
+ }
500
+ function dmgCapacityMegabytes(contentBytes) {
501
+ if (!Number.isSafeInteger(contentBytes) || contentBytes < 0)
502
+ throw new Error(`DMG content size must be a non-negative safe integer: ${contentBytes}`);
503
+ const contentMegabytes = Math.ceil(contentBytes / MEBIBYTE);
504
+ return Math.max(64, Math.ceil(contentMegabytes * 1.25) + 32);
505
+ }
506
+ function dmgCreateArguments(opts, contentBytes) {
507
+ return [
508
+ "create",
509
+ "-volname",
510
+ opts.volumeName,
511
+ "-srcfolder",
512
+ opts.appBundlePath,
513
+ "-size",
514
+ `${dmgCapacityMegabytes(contentBytes)}m`,
515
+ "-ov",
516
+ "-format",
517
+ "UDZO",
518
+ opts.outputPath
519
+ ];
520
+ }
521
+ var HDIUTIL_MAX_ATTEMPTS = 3;
522
+ function shouldRetryHdiutil(error, attempt, maxAttempts = HDIUTIL_MAX_ATTEMPTS) {
523
+ if (!Number.isInteger(attempt) || attempt < 1 || !Number.isInteger(maxAttempts) || maxAttempts < 1)
524
+ throw new Error("hdiutil retry attempts must be positive integers");
525
+ return attempt < maxAttempts && /(?:resource busy|resource temporarily unavailable)/i.test(error);
526
+ }
527
+ function runTool(tool, args) {
289
528
  return new Promise((resolve) => {
290
- if (!/^[^/:\n]{1,27}$/.test(opts.volumeName)) {
291
- resolve({ success: false, error: `Invalid DMG volume name "${opts.volumeName}"; must be 1..27 chars without /, :, or newline` });
292
- return;
293
- }
294
- const proc = spawn("hdiutil", [
295
- "create",
296
- "-volname",
297
- opts.volumeName,
298
- "-srcfolder",
299
- opts.appBundlePath,
300
- "-ov",
301
- "-format",
302
- "UDZO",
303
- opts.outputPath
304
- ]);
305
- proc.on("close", (code) => {
306
- if (code === 0) {
307
- resolve({ success: true, outputPath: opts.outputPath });
308
- } else {
309
- resolve({ success: false, error: `hdiutil exited with code ${code}` });
310
- }
529
+ const proc = spawn(tool, args);
530
+ let stdout = "";
531
+ let stderr = "";
532
+ proc.stdout?.on("data", (chunk) => {
533
+ stdout += chunk.toString();
311
534
  });
312
- proc.on("error", (err) => {
313
- resolve({ success: false, error: err.message });
535
+ proc.stderr?.on("data", (chunk) => {
536
+ stderr += chunk.toString();
314
537
  });
538
+ proc.on("close", (code) => {
539
+ if (code === 0)
540
+ resolve({ success: true });
541
+ else
542
+ resolve({ success: false, error: formatPackagingCommandError(tool, code, stdout, stderr) });
543
+ });
544
+ proc.on("error", (err) => resolve({ success: false, error: err.message }));
315
545
  });
316
546
  }
547
+ var runHdiutil = (args) => runTool("hdiutil", args);
548
+ async function createDMG(opts) {
549
+ if (!/^[^/:\n]{1,27}$/.test(opts.volumeName))
550
+ return { success: false, error: `Invalid DMG volume name "${opts.volumeName}"; must be 1..27 chars without /, :, or newline` };
551
+ const args = dmgCreateArguments(opts, pathContentBytes(opts.appBundlePath));
552
+ const failures = [];
553
+ for (let attempt = 1;attempt <= HDIUTIL_MAX_ATTEMPTS; attempt++) {
554
+ const result = await runHdiutil(args);
555
+ if (result.success)
556
+ return { success: true, outputPath: opts.outputPath };
557
+ failures.push(`attempt ${attempt}: ${result.error}`);
558
+ if (!shouldRetryHdiutil(result.error, attempt))
559
+ return { success: false, error: failures.join(`
560
+ `) };
561
+ rmSync(opts.outputPath, { force: true });
562
+ await new Promise((resolve) => setTimeout(resolve, attempt * 2000));
563
+ }
564
+ return { success: false, error: failures.join(`
565
+ `) };
566
+ }
317
567
  async function createPKG(opts) {
318
- return new Promise((resolve) => {
319
- if (!/^[a-zA-Z0-9._-]+$/.test(opts.identifier) || !opts.identifier.includes(".")) {
320
- resolve({ success: false, error: `Invalid pkg identifier "${opts.identifier}"; expected reverse-DNS like com.example.app` });
321
- return;
322
- }
323
- if (!/^[A-Za-z0-9._+-]+$/.test(opts.version)) {
324
- resolve({ success: false, error: `Invalid pkg version "${opts.version}"` });
325
- return;
326
- }
327
- const tempDir = mkdtempSync(join(tmpdir(), "craft-pkg-"));
568
+ if (!/^[a-zA-Z0-9._-]+$/.test(opts.identifier) || !opts.identifier.includes("."))
569
+ return { success: false, error: `Invalid pkg identifier "${opts.identifier}"; expected reverse-DNS like com.example.app` };
570
+ if (!/^[A-Za-z0-9._+-]+$/.test(opts.version))
571
+ return { success: false, error: `Invalid pkg version "${opts.version}"` };
572
+ if (opts.appStore) {
573
+ if (!opts.installerIdentity)
574
+ return { success: false, error: 'App Store packaging requires macos.installerIdentity (a "3rd Party Mac Developer Installer" identity)' };
575
+ const built = await runTool("productbuild", productbuildArguments({
576
+ appBundlePath: opts.appBundlePath,
577
+ outputPath: opts.outputPath,
578
+ identity: opts.installerIdentity
579
+ }));
580
+ return built.success ? { success: true, outputPath: opts.outputPath } : { success: false, error: built.error };
581
+ }
582
+ const tempDir = mkdtempSync(join(tmpdir(), "craft-pkg-"));
583
+ try {
328
584
  const appsDir = join(tempDir, "Applications");
329
585
  mkdirSync(appsDir, { recursive: true });
330
586
  cpSync(opts.appBundlePath, join(appsDir, basename(opts.appBundlePath)), { recursive: true });
331
- const proc = spawn("pkgbuild", [
587
+ const built = await runTool("pkgbuild", [
332
588
  "--root",
333
589
  tempDir,
334
590
  "--identifier",
@@ -337,64 +593,116 @@ async function createPKG(opts) {
337
593
  opts.version,
338
594
  "--install-location",
339
595
  "/",
596
+ ...opts.installerIdentity ? ["--sign", opts.installerIdentity] : [],
340
597
  opts.outputPath
341
598
  ]);
342
- proc.on("close", (code) => {
343
- rmSync(tempDir, { recursive: true, force: true });
344
- if (code === 0) {
345
- resolve({ success: true, outputPath: opts.outputPath });
346
- } else {
347
- resolve({ success: false, error: `pkgbuild exited with code ${code}` });
348
- }
349
- });
350
- proc.on("error", (err) => {
351
- rmSync(tempDir, { recursive: true, force: true });
352
- resolve({ success: false, error: err.message });
353
- });
354
- });
599
+ return built.success ? { success: true, outputPath: opts.outputPath } : { success: false, error: built.error };
600
+ } finally {
601
+ rmSync(tempDir, { recursive: true, force: true });
602
+ }
603
+ }
604
+ function xml(value) {
605
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll(`'`, "&apos;");
606
+ }
607
+ function wixIdentifier(value) {
608
+ const sanitized = value.replace(/[^A-Za-z0-9_.]/g, "_");
609
+ return /^[A-Za-z_]/.test(sanitized) ? sanitized : `_${sanitized}`;
610
+ }
611
+ function deterministicGuid(value) {
612
+ const digest = createHash("sha256").update(value).digest("hex").slice(0, 32).split("");
613
+ digest[12] = "4";
614
+ digest[16] = (Number.parseInt(digest[16], 16) & 3 | 8).toString(16);
615
+ const hex = digest.join("").toUpperCase();
616
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
617
+ }
618
+ function windowsArchitecture(architecture) {
619
+ if (architecture === "ia32" || architecture === "x86")
620
+ return "x86";
621
+ if (architecture === "x64" || architecture === "arm64")
622
+ return architecture;
623
+ throw new Error(`Unsupported Windows package architecture: ${architecture}`);
624
+ }
625
+ function renderWixSource(opts, sourceName) {
626
+ if (!/^[0-9]+\.[0-9]+\.[0-9]+(?:\.[0-9]+)?$/.test(opts.version))
627
+ throw new Error(`MSI version must have 3 or 4 numeric parts: ${opts.version}`);
628
+ const id = wixIdentifier(opts.name);
629
+ const manufacturer = opts.manufacturer.trim() || "Unknown";
630
+ const upgradeCode = deterministicGuid(`${manufacturer}/${opts.name}`);
631
+ const programFilesFolder = opts.architecture === "x86" ? "ProgramFilesFolder" : "ProgramFiles64Folder";
632
+ const win64 = opts.architecture === "x86" ? "no" : "yes";
633
+ return `<?xml version="1.0" encoding="UTF-8"?>
634
+ <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
635
+ <Product Id="*" Name="${xml(opts.name)}" Language="1033" Version="${opts.version}" Manufacturer="${xml(manufacturer)}" UpgradeCode="${upgradeCode}">
636
+ <Package InstallerVersion="500" Compressed="yes" InstallScope="perMachine" Platform="${opts.architecture}" />
637
+ <MajorUpgrade AllowDowngrades="yes" />
638
+ <MediaTemplate EmbedCab="yes" />
639
+ <Directory Id="TARGETDIR" Name="SourceDir">
640
+ <Directory Id="${programFilesFolder}">
641
+ <Directory Id="INSTALLFOLDER" Name="${xml(opts.name)}">
642
+ <Component Id="${id}Executable" Guid="*" Win64="${win64}">
643
+ <File Id="${id}File" Source="${xml(sourceName)}" KeyPath="yes" />
644
+ </Component>
645
+ </Directory>
646
+ </Directory>
647
+ </Directory>
648
+ <Feature Id="ProductFeature" Title="${xml(opts.name)}" Level="1">
649
+ <ComponentRef Id="${id}Executable" />
650
+ </Feature>
651
+ </Product>
652
+ </Wix>
653
+ `;
355
654
  }
356
- async function createMSI(_opts) {
357
- return new Promise((resolve) => {
358
- exec("candle.exe -?", (error) => {
359
- if (error) {
360
- resolve({
361
- success: false,
362
- error: "WiX Toolset not found. Install from https://wixtoolset.org/"
363
- });
364
- } else {
365
- resolve({
366
- success: false,
367
- error: "MSI creation requires Windows platform and WiX Toolset"
368
- });
369
- }
370
- });
655
+ function windowsExecutableName(name) {
656
+ if (name.length === 0 || name.trim() !== name || /[<>:"/\\|?*\u0000-\u001F]/.test(name) || /[. ]$/.test(name))
657
+ throw new Error(`Invalid Windows application name: ${JSON.stringify(name)}`);
658
+ if (/^(?:CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(?:\.|$)/i.test(name))
659
+ throw new Error(`Reserved Windows application name: ${JSON.stringify(name)}`);
660
+ return `${name}.exe`;
661
+ }
662
+ function runCommand(command, args, cwd) {
663
+ return new Promise((resolve, reject) => {
664
+ const child = spawn(command, args, { cwd, stdio: "inherit", windowsHide: true });
665
+ child.once("error", reject);
666
+ child.once("close", (code) => code === 0 ? resolve() : reject(new Error(`${command} exited with code ${code}`)));
371
667
  });
372
668
  }
669
+ async function createMSI(opts) {
670
+ const tempDir = mkdtempSync(join(tmpdir(), "craft-msi-"));
671
+ try {
672
+ const binaryName = windowsExecutableName(opts.name);
673
+ const sourcePath = join(tempDir, binaryName);
674
+ const wxsPath = join(tempDir, "installer.wxs");
675
+ const wixobjPath = join(tempDir, "installer.wixobj");
676
+ copyFileSync(opts.binaryPath, sourcePath);
677
+ writeFileSync(wxsPath, renderWixSource(opts, binaryName));
678
+ await runCommand("candle.exe", ["-nologo", "-out", wixobjPath, wxsPath], tempDir);
679
+ await runCommand("light.exe", ["-nologo", "-sval", "-out", opts.outputPath, wixobjPath], tempDir);
680
+ if (opts.certificatePath) {
681
+ const signArgs = ["sign", "/fd", "sha256", "/tr", "https://timestamp.digicert.com", "/td", "sha256", "/f", opts.certificatePath];
682
+ if (opts.certificatePassword)
683
+ signArgs.push("/p", opts.certificatePassword);
684
+ signArgs.push(opts.outputPath);
685
+ await runCommand("signtool.exe", signArgs);
686
+ await runCommand("signtool.exe", ["verify", "/pa", "/all", opts.outputPath]);
687
+ }
688
+ return { success: true, outputPath: opts.outputPath };
689
+ } catch (error) {
690
+ const message = error instanceof Error ? error.message : String(error);
691
+ const missingTool = /ENOENT|not found/i.test(message);
692
+ return { success: false, error: missingTool ? `WiX Toolset not found: ${message}` : message };
693
+ } finally {
694
+ rmSync(tempDir, { recursive: true, force: true });
695
+ }
696
+ }
373
697
  async function createZIP(opts) {
374
- const archiverFn2 = await loadArchiver();
375
- return new Promise((resolve) => {
376
- const output = createWriteStream(opts.outputPath);
377
- const archive = archiverFn2("zip", { zlib: { level: 9 } });
378
- let settled = false;
379
- const settle = (r) => {
380
- if (settled)
381
- return;
382
- settled = true;
383
- resolve(r);
384
- };
385
- output.on("close", () => {
386
- settle({ success: true, outputPath: opts.outputPath });
387
- });
388
- output.on("error", (err) => {
389
- settle({ success: false, error: `Failed to write ZIP: ${err.message}` });
390
- });
391
- archive.on("error", (err) => {
392
- settle({ success: false, error: err.message });
393
- });
394
- archive.pipe(output);
395
- archive.file(opts.binaryPath, { name: `${opts.name}.exe` });
396
- archive.finalize();
397
- });
698
+ try {
699
+ const data = new Uint8Array(readFileSync(opts.binaryPath));
700
+ const zip = buildZip([{ name: `${opts.name}.exe`, data }]);
701
+ writeFileSync(opts.outputPath, zip);
702
+ return { success: true, outputPath: opts.outputPath };
703
+ } catch (err) {
704
+ return { success: false, error: `Failed to write ZIP: ${err.message}` };
705
+ }
398
706
  }
399
707
  async function createDEB(opts) {
400
708
  return new Promise((resolve) => {
@@ -1861,6 +2169,9 @@ class Window {
1861
2169
  async setPosition(x, y, animate) {
1862
2170
  await this._call("setPosition", { x, y, animate });
1863
2171
  }
2172
+ async moveBy(dx, dy) {
2173
+ await this._call("moveBy", { dx, dy });
2174
+ }
1864
2175
  async getPosition() {
1865
2176
  return this._call("getPosition");
1866
2177
  }
@@ -1888,6 +2199,9 @@ class Window {
1888
2199
  async setMovable(movable) {
1889
2200
  await this._call("setMovable", { movable });
1890
2201
  }
2202
+ async startDrag() {
2203
+ await this._call("startDrag");
2204
+ }
1891
2205
  async isMovable() {
1892
2206
  return this._call("isMovable");
1893
2207
  }
@@ -2005,12 +2319,14 @@ class WindowManager {
2005
2319
  setTitle = (title) => this.current.setTitle(title);
2006
2320
  setSize = (width, height, animate) => this.current.setSize(width, height, animate);
2007
2321
  setPosition = (x, y, animate) => this.current.setPosition(x, y, animate);
2322
+ moveBy = (dx, dy) => this.current.moveBy(dx, dy);
2008
2323
  setBounds = (bounds, animate) => this.current.setBounds(bounds, animate);
2009
2324
  setAlwaysOnTop = (alwaysOnTop) => this.current.setAlwaysOnTop(alwaysOnTop);
2010
2325
  setOpacity = (opacity) => this.current.setOpacity(opacity);
2011
2326
  setBackgroundColor = (color) => this.current.setBackgroundColor(color);
2012
2327
  setVibrancy = (vibrancy) => this.current.setVibrancy(vibrancy);
2013
2328
  setResizable = (resizable) => this.current.setResizable(resizable);
2329
+ startDrag = () => this.current.startDrag();
2014
2330
  getState = () => this.current.getState();
2015
2331
  on = (event, handler) => this.current.on(event, handler);
2016
2332
  once = (event, handler) => this.current.once(event, handler);
@@ -3453,8 +3769,8 @@ var crypto = {
3453
3769
  if (!hasNodeCrypto) {
3454
3770
  throw new CraftCryptoError("MD5 is not available in this runtime \u2014 WebCrypto does not implement it. " + "Use sha256/sha512, or call this from Node.");
3455
3771
  }
3456
- const { createHash: createHash2 } = await import("crypto");
3457
- return createHash2("md5").update(data).digest("hex");
3772
+ const { createHash: createHash3 } = await import("crypto");
3773
+ return createHash3("md5").update(data).digest("hex");
3458
3774
  }
3459
3775
  if (typeof globalThis.crypto !== "undefined" && globalThis.crypto.subtle) {
3460
3776
  const encoder = new TextEncoder;
@@ -3462,8 +3778,8 @@ var crypto = {
3462
3778
  const hashBuffer = await globalThis.crypto.subtle.digest(algorithm === "sha256" ? "SHA-256" : "SHA-512", dataBuffer);
3463
3779
  return bufferToHex(hashBuffer);
3464
3780
  }
3465
- const { createHash } = await import("crypto");
3466
- return createHash(algorithm).update(data).digest("hex");
3781
+ const { createHash: createHash2 } = await import("crypto");
3782
+ return createHash2(algorithm).update(data).digest("hex");
3467
3783
  },
3468
3784
  async encrypt(data, key) {
3469
3785
  if (typeof window !== "undefined" && window.craft?.crypto) {
@@ -3778,7 +4094,7 @@ async function getSystemInfo() {
3778
4094
  uptime: 0
3779
4095
  };
3780
4096
  }
3781
- async function exec2(command, options) {
4097
+ async function exec(command, options) {
3782
4098
  if (typeof window !== "undefined" && window.craft) {
3783
4099
  return callBridge3("process.exec", { command, ...options });
3784
4100
  }
@@ -3958,18 +4274,18 @@ async function open(target) {
3958
4274
  const platform = getPlatform();
3959
4275
  const { execFile } = await import("child_process");
3960
4276
  const { promisify } = await import("util");
3961
- const exec3 = promisify(execFile);
4277
+ const exec2 = promisify(execFile);
3962
4278
  switch (platform) {
3963
4279
  case "darwin":
3964
4280
  case "macos":
3965
- await exec3("open", [target]);
4281
+ await exec2("open", [target]);
3966
4282
  return;
3967
4283
  case "win32":
3968
4284
  case "windows":
3969
- await exec3("cmd.exe", ["/c", "start", '""', target]);
4285
+ await exec2("cmd.exe", ["/c", "start", '""', target]);
3970
4286
  return;
3971
4287
  case "linux":
3972
- await exec3("xdg-open", [target]);
4288
+ await exec2("xdg-open", [target]);
3973
4289
  return;
3974
4290
  default:
3975
4291
  throw new Error(`Unsupported platform: ${platform}`);
@@ -7948,9 +8264,9 @@ async function buildCSS(options) {
7948
8264
  console.warn("buildCSS can only be run in Node.js environment");
7949
8265
  return;
7950
8266
  }
7951
- const { exec: exec3 } = await import("child_process");
8267
+ const { exec: exec2 } = await import("child_process");
7952
8268
  const { promisify } = await import("util");
7953
- const execAsync = promisify(exec3);
8269
+ const execAsync = promisify(exec2);
7954
8270
  const args = [
7955
8271
  "build",
7956
8272
  "--content",
@@ -7971,109 +8287,123 @@ async function buildCSS(options) {
7971
8287
  // src/styles/sidebars.ts
7972
8288
  var tahoeStyles = {
7973
8289
  sidebar: `
7974
- w-56 h-full flex flex-col
7975
- bg-white/70 dark:bg-neutral-900/70
7976
- backdrop-blur-xl backdrop-saturate-150
7977
- border-r border-black/5 dark:border-white/5
8290
+ w-[250px] h-full flex flex-col
8291
+ bg-white/55 dark:bg-[#1e1e23]/55
8292
+ backdrop-blur-[60px] backdrop-saturate-[1.8]
8293
+ text-black dark:text-white
7978
8294
  select-none
7979
8295
  `,
7980
8296
  content: `
7981
8297
  flex-1 overflow-y-auto overflow-x-hidden
7982
- py-2 px-2
7983
- scrollbar-thin scrollbar-thumb-black/10 dark:scrollbar-thumb-white/10
8298
+ px-[10px] pb-[10px]
7984
8299
  `,
7985
8300
  section: `
7986
- mb-4
8301
+ flex flex-col gap-[2px]
7987
8302
  `,
7988
8303
  sectionHeader: `
7989
- px-2 py-1.5 mb-1
7990
- text-[11px] font-semibold uppercase tracking-wider
7991
- text-neutral-500 dark:text-neutral-400
8304
+ pl-[8px] pr-[6px] pt-[16px] pb-[5px]
8305
+ text-[11px] font-semibold leading-[13px]
8306
+ text-[#3c3c43]/60 dark:text-[#ebebf5]/60
7992
8307
  `,
7993
8308
  sectionHeaderCollapsible: `
7994
- px-2 py-1.5 mb-1 flex items-center gap-1 cursor-pointer
7995
- text-[11px] font-semibold uppercase tracking-wider
7996
- text-neutral-500 dark:text-neutral-400
7997
- hover:text-neutral-700 dark:hover:text-neutral-200
7998
- transition-colors duration-150
8309
+ group flex items-center w-full
8310
+ pl-[8px] pr-[6px] pt-[16px] pb-[5px]
8311
+ text-[11px] font-semibold leading-[13px]
8312
+ text-[#3c3c43]/60 dark:text-[#ebebf5]/60
8313
+ cursor-default
7999
8314
  `,
8000
8315
  collapseChevron: `
8001
- w-3 h-3 transition-transform duration-200
8316
+ ml-auto h-[11px] w-[11px]
8317
+ text-[#3c3c43]/45 dark:text-[#ebebf5]/45
8318
+ opacity-0 group-hover:opacity-100
8319
+ transition-all duration-200
8002
8320
  `,
8003
8321
  item: `
8004
- flex items-center gap-2.5 px-2 py-1.5 mx-1 rounded-md
8005
- text-[13px] text-neutral-700 dark:text-neutral-200
8006
- cursor-pointer transition-all duration-150
8007
- hover:bg-black/5 dark:hover:bg-white/5
8322
+ flex w-full items-center
8323
+ h-[30px] rounded-[9px] pl-[4px] pr-[8px]
8324
+ text-[13px] leading-[16px] text-black dark:text-white
8325
+ cursor-default transition-colors duration-150 ease-out
8326
+ hover:bg-black/4 dark:hover:bg-white/6
8327
+ active:bg-black/10 dark:active:bg-white/18
8008
8328
  `,
8009
8329
  itemSelected: `
8010
- flex items-center gap-2.5 px-2 py-1.5 mx-1 rounded-md
8011
- text-[13px] text-white
8012
- cursor-pointer
8013
- bg-blue-500 dark:bg-blue-600
8014
- shadow-sm
8330
+ flex w-full items-center
8331
+ h-[30px] rounded-[9px] pl-[4px] pr-[8px]
8332
+ text-[13px] leading-[16px] text-black dark:text-white
8333
+ cursor-default
8334
+ bg-black/8 dark:bg-white/14
8335
+ `,
8336
+ disclosure: `
8337
+ flex h-[16px] w-[18px] shrink-0 items-center justify-center
8338
+ `,
8339
+ disclosureChevron: `
8340
+ h-[11px] w-[11px]
8341
+ text-[#3c3c43]/55 dark:text-[#ebebf5]/55
8342
+ transition-transform duration-200 ease-out
8015
8343
  `,
8016
8344
  itemIcon: `
8017
- w-4 h-4 flex-shrink-0
8018
- text-neutral-500 dark:text-neutral-400
8345
+ h-[17px] w-[17px] flex-shrink-0
8346
+ text-[#0088ff]
8019
8347
  `,
8020
8348
  itemIconSelected: `
8021
- w-4 h-4 flex-shrink-0
8022
- text-white/90
8349
+ h-[17px] w-[17px] flex-shrink-0
8350
+ text-[#0088ff]
8351
+ `,
8352
+ itemIconSlot: `
8353
+ flex h-[20px] w-[22px] shrink-0 items-center justify-center mr-[7px]
8023
8354
  `,
8024
8355
  itemLabel: `
8025
8356
  flex-1 truncate
8026
8357
  `,
8027
8358
  itemBadge: `
8028
- px-1.5 py-0.5 rounded-full text-[10px] font-medium
8029
- bg-neutral-200 dark:bg-neutral-700
8030
- text-neutral-600 dark:text-neutral-300
8359
+ ml-[8px] shrink-0 tabular-nums
8360
+ text-[13px] leading-[16px]
8361
+ text-[#3c3c43]/60 dark:text-[#ebebf5]/60
8031
8362
  `,
8032
8363
  itemBadgeSelected: `
8033
- px-1.5 py-0.5 rounded-full text-[10px] font-medium
8034
- bg-white/20 text-white
8364
+ ml-[8px] shrink-0 tabular-nums
8365
+ text-[13px] leading-[16px]
8366
+ text-[#3c3c43]/60 dark:text-[#ebebf5]/60
8035
8367
  `,
8036
8368
  children: `
8037
- ml-4 mt-0.5 border-l border-neutral-200 dark:border-neutral-700
8369
+ ml-[16px] mt-[2px] flex flex-col gap-[2px]
8038
8370
  `,
8039
8371
  dragIndicator: `
8040
- absolute left-0 right-0 h-0.5 bg-blue-500
8372
+ absolute left-0 right-0 h-0.5 bg-[#0088ff]
8041
8373
  pointer-events-none
8042
8374
  `,
8043
8375
  searchContainer: `
8044
- px-2 pb-2
8376
+ px-[10px] pb-[6px]
8045
8377
  `,
8046
8378
  searchInput: `
8047
- w-full px-3 py-1.5 rounded-md
8048
- text-[13px] placeholder:text-neutral-400
8049
- bg-black/5 dark:bg-white/5
8379
+ h-[28px] w-full rounded-[8px] px-[8px]
8380
+ text-[13px] text-black dark:text-white
8381
+ placeholder:text-[#3c3c43]/50 dark:placeholder:text-[#ebebf5]/50
8382
+ bg-black/5 dark:bg-white/10
8050
8383
  border border-transparent
8051
- focus:outline-none focus:border-blue-500/50 focus:bg-white dark:focus:bg-neutral-800
8384
+ focus:outline-none focus:ring-2 focus:ring-[#0088ff]/60
8052
8385
  transition-all duration-150
8053
8386
  `,
8054
8387
  header: `
8055
- px-3 py-3 border-b border-black/5 dark:border-white/5
8056
- flex items-center gap-3
8388
+ h-[52px] px-[21px] flex items-center gap-3
8057
8389
  `,
8058
8390
  headerAvatar: `
8059
- w-8 h-8 rounded-full bg-gradient-to-br from-blue-400 to-blue-600
8060
- flex items-center justify-center text-white text-sm font-medium
8391
+ w-[30px] h-[30px] rounded-full bg-black/10 dark:bg-white/15
8392
+ flex items-center justify-center text-[13px] font-semibold
8061
8393
  `,
8062
8394
  headerTitle: `
8063
- text-[13px] font-medium text-neutral-800 dark:text-neutral-100
8395
+ text-[13px] font-medium text-black dark:text-white
8064
8396
  `,
8065
8397
  headerSubtitle: `
8066
- text-[11px] text-neutral-500 dark:text-neutral-400
8398
+ text-[11px] text-[#3c3c43]/60 dark:text-[#ebebf5]/60
8067
8399
  `,
8068
8400
  footer: `
8069
- px-2 py-2 border-t border-black/5 dark:border-white/5
8070
- flex items-center gap-2
8401
+ px-[10px] pb-[8px] pt-[4px] flex items-center gap-2
8071
8402
  `,
8072
8403
  footerButton: `
8073
- p-1.5 rounded-md
8074
- text-neutral-500 dark:text-neutral-400
8075
- hover:bg-black/5 dark:hover:bg-white/5
8076
- hover:text-neutral-700 dark:hover:text-neutral-200
8404
+ grid h-[28px] w-[28px] place-items-center rounded-[7px]
8405
+ text-[#3c3c43]/70 dark:text-[#ebebf5]/70
8406
+ hover:bg-black/5 dark:hover:bg-white/10
8077
8407
  transition-colors duration-150
8078
8408
  `
8079
8409
  };
@@ -8492,19 +8822,21 @@ function renderTahoeSidebar(data) {
8492
8822
  const itemClass = isSelected ? s.itemSelected : s.item;
8493
8823
  const iconClass = isSelected ? s.itemIconSelected : s.itemIcon;
8494
8824
  const badgeClass = isSelected ? s.itemBadgeSelected : s.itemBadge;
8825
+ const hasChildren = (item.children?.length ?? 0) > 0;
8826
+ const chevron = hasChildren ? `<span class='${s.disclosureChevron}${item.expanded ? " rotate-90" : ""}'>${icons.chevronRight}</span>` : "";
8827
+ const tint = item.color ? ` style='color: ${item.color}'` : "";
8495
8828
  let html = `
8496
- <div class='${itemClass}' data-id='${item.id}' style='padding-left: ${8 + depth * 16}px'>
8497
- <span class='${iconClass}'>${getIcon2(item.icon || "document")}</span>
8829
+ <div class='${itemClass}' data-id='${item.id}'${depth > 0 ? ` style='padding-left: ${4 + depth * 16}px'` : ""}>
8830
+ <span class='${s.disclosure}'>${chevron}</span>
8831
+ <span class='${s.itemIconSlot}'><span class='${iconClass}'${tint}>${getIcon2(item.icon || "document")}</span></span>
8498
8832
  <span class='${s.itemLabel}'>${item.label}</span>
8499
8833
  ${item.badge ? `<span class='${badgeClass}'>${item.badge}</span>` : ""}
8500
8834
  </div>
8501
8835
  `;
8502
8836
  if (item.children && item.expanded) {
8503
- html += `<div class='${s.children}'>`;
8504
8837
  item.children.forEach((child) => {
8505
8838
  html += renderItem(child, depth + 1);
8506
8839
  });
8507
- html += `</div>`;
8508
8840
  }
8509
8841
  return html;
8510
8842
  };
@@ -8513,8 +8845,8 @@ function renderTahoeSidebar(data) {
8513
8845
  <div class='${s.section}'>
8514
8846
  ${section.title ? `
8515
8847
  <div class="${section.collapsible ? s.sectionHeaderCollapsible : s.sectionHeader}" data-section="${section.id}">
8516
- ${section.collapsible ? `<span class="${s.collapseChevron}${section.collapsed ? "" : " rotate-90"}">${icons.chevronRight}</span>` : ""}
8517
- ${section.title}
8848
+ <span class='truncate'>${section.title}</span>
8849
+ ${section.collapsible ? `<span class='${s.collapseChevron}${section.collapsed ? " -rotate-90" : ""}'>${icons.chevronDown}</span>` : ""}
8518
8850
  </div>
8519
8851
  ` : ""}
8520
8852
  ${!section.collapsed ? section.items.map((item) => renderItem(item)).join("") : ""}
@@ -9251,10 +9583,10 @@ __export(exports_updater, {
9251
9583
  DeltaGenerator: () => DeltaGenerator,
9252
9584
  AutoUpdater: () => AutoUpdater
9253
9585
  });
9254
- import { createReadStream, createWriteStream as createWriteStream2, existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync, statSync, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
9586
+ import { createReadStream, createWriteStream, existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, statSync, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
9255
9587
  import { join as join2, basename as basename2, dirname } from "path";
9256
9588
  import { execFileSync, spawn as spawn3 } from "child_process";
9257
- import { createHash } from "crypto";
9589
+ import { createHash as createHash2 } from "crypto";
9258
9590
  import { EventEmitter as EventEmitter3 } from "events";
9259
9591
  function findFirstByName(root, namePattern, kind) {
9260
9592
  const out = execFileSync("find", [root, "-name", namePattern, "-type", kind, "-print0"]);
@@ -9271,6 +9603,12 @@ class AutoUpdater extends EventEmitter3 {
9271
9603
  cachedEtag = null;
9272
9604
  cachedLastModified = null;
9273
9605
  cachedManifest = null;
9606
+ emitError(error) {
9607
+ if (this.listenerCount("error") > 0)
9608
+ this.emit("error", error);
9609
+ else
9610
+ console.error("[Updater]", error);
9611
+ }
9274
9612
  constructor(config) {
9275
9613
  super();
9276
9614
  const url = config.updateUrl;
@@ -9344,7 +9682,7 @@ class AutoUpdater extends EventEmitter3 {
9344
9682
  return null;
9345
9683
  }
9346
9684
  } catch (error) {
9347
- this.emit("error", error);
9685
+ this.emitError(error);
9348
9686
  return null;
9349
9687
  }
9350
9688
  }
@@ -9375,7 +9713,7 @@ class AutoUpdater extends EventEmitter3 {
9375
9713
  const total = contentLength || expectedSize;
9376
9714
  let downloaded = 0;
9377
9715
  const startTime = Date.now();
9378
- const fileStream = createWriteStream2(this.downloadPath);
9716
+ const fileStream = createWriteStream(this.downloadPath);
9379
9717
  const reader = response.body?.getReader();
9380
9718
  if (!reader) {
9381
9719
  throw new Error("Failed to get response reader");
@@ -9398,11 +9736,32 @@ class AutoUpdater extends EventEmitter3 {
9398
9736
  this.emit("download-progress", progress);
9399
9737
  }
9400
9738
  fileStream.end();
9739
+ await new Promise((resolve, reject) => {
9740
+ fileStream.once("finish", resolve);
9741
+ fileStream.once("error", reject);
9742
+ });
9401
9743
  const hash = await this.computeFileHash(this.downloadPath);
9402
9744
  if (hash !== expectedHash) {
9403
9745
  unlinkSync(this.downloadPath);
9404
9746
  throw new Error("Download verification failed: hash mismatch");
9405
9747
  }
9748
+ if (deltaUpdate) {
9749
+ if (!statSync(this.config.appPath).isFile()) {
9750
+ throw new Error("Delta updates require appPath to point to the installed bundle file");
9751
+ }
9752
+ const patchPath = this.downloadPath;
9753
+ const reconstructedPath = join2(downloadDir, `reconstructed-${basename2(platformUpdate.url)}`);
9754
+ await DeltaGenerator.apply(this.config.appPath, patchPath, reconstructedPath);
9755
+ const reconstructedHash = await this.computeFileHash(reconstructedPath);
9756
+ if (reconstructedHash !== platformUpdate.sha256) {
9757
+ try {
9758
+ unlinkSync(reconstructedPath);
9759
+ } catch {}
9760
+ throw new Error("Delta reconstruction failed: full bundle hash mismatch");
9761
+ }
9762
+ unlinkSync(patchPath);
9763
+ this.downloadPath = reconstructedPath;
9764
+ }
9406
9765
  if (this.config.publicKeyPem && !platformUpdate.signature) {
9407
9766
  unlinkSync(this.downloadPath);
9408
9767
  throw new Error("Updater: publicKeyPem is configured but the manifest entry has no signature. " + "Refusing to install unsigned update.");
@@ -9424,7 +9783,7 @@ class AutoUpdater extends EventEmitter3 {
9424
9783
  }
9425
9784
  return this.downloadPath;
9426
9785
  } catch (error) {
9427
- this.emit("error", error);
9786
+ this.emitError(error);
9428
9787
  return null;
9429
9788
  }
9430
9789
  }
@@ -9484,7 +9843,8 @@ class AutoUpdater extends EventEmitter3 {
9484
9843
  }
9485
9844
  } catch (error) {
9486
9845
  progress.phase = "error";
9487
- this.emit("error", error);
9846
+ this.emitError(error);
9847
+ throw error;
9488
9848
  }
9489
9849
  }
9490
9850
  async installMacOSUpdate() {
@@ -9603,7 +9963,7 @@ class AutoUpdater extends EventEmitter3 {
9603
9963
  }
9604
9964
  async computeFileHash(filePath) {
9605
9965
  return new Promise((resolve, reject) => {
9606
- const hash = createHash("sha256");
9966
+ const hash = createHash2("sha256");
9607
9967
  const stream = createReadStream(filePath);
9608
9968
  stream.on("data", (chunk) => hash.update(chunk));
9609
9969
  stream.on("end", () => resolve(hash.digest("hex")));
@@ -9629,7 +9989,7 @@ class AutoUpdater extends EventEmitter3 {
9629
9989
  } catch {
9630
9990
  return false;
9631
9991
  }
9632
- const data = readFileSync(filePath);
9992
+ const data = readFileSync2(filePath);
9633
9993
  if (algo === "ed25519") {
9634
9994
  return verify(null, data, publicKey, sigBytes);
9635
9995
  }
@@ -9675,7 +10035,7 @@ class DeltaGenerator {
9675
10035
  }
9676
10036
  static async computeHash(filePath) {
9677
10037
  return new Promise((resolve, reject) => {
9678
- const hash = createHash("sha256");
10038
+ const hash = createHash2("sha256");
9679
10039
  const stream = createReadStream(filePath);
9680
10040
  stream.on("data", (chunk) => hash.update(chunk));
9681
10041
  stream.on("end", () => resolve(hash.digest("hex")));
@@ -9693,8 +10053,8 @@ function generateUpdateManifest(options) {
9693
10053
  for (const [platform, info] of Object.entries(options.platforms)) {
9694
10054
  if (!info)
9695
10055
  continue;
9696
- const bytes = readFileSync(info.path);
9697
- const hash = createHash("sha256").update(bytes).digest("hex");
10056
+ const bytes = readFileSync2(info.path);
10057
+ const hash = createHash2("sha256").update(bytes).digest("hex");
9698
10058
  manifest.platforms[platform] = {
9699
10059
  url: info.url,
9700
10060
  size: bytes.length,
@@ -9703,8 +10063,8 @@ function generateUpdateManifest(options) {
9703
10063
  };
9704
10064
  const platformDeltas = options.deltas?.filter((d) => d.platform === platform) || [];
9705
10065
  for (const delta of platformDeltas) {
9706
- const deltaBytes = readFileSync(delta.path);
9707
- const deltaHash = createHash("sha256").update(deltaBytes).digest("hex");
10066
+ const deltaBytes = readFileSync2(delta.path);
10067
+ const deltaHash = createHash2("sha256").update(deltaBytes).digest("hex");
9708
10068
  manifest.platforms[platform].delta.push({
9709
10069
  fromVersion: delta.fromVersion,
9710
10070
  url: delta.url,
@@ -9803,7 +10163,7 @@ var versionMismatchWarned = false;
9803
10163
  function getSdkVersion() {
9804
10164
  try {
9805
10165
  const pkgPath = join3(import.meta.dir, "..", "package.json");
9806
- return JSON.parse(readFileSync2(pkgPath, "utf-8")).version ?? "unknown";
10166
+ return JSON.parse(readFileSync3(pkgPath, "utf-8")).version ?? "unknown";
9807
10167
  } catch {
9808
10168
  return "unknown";
9809
10169
  }
@@ -9973,6 +10333,13 @@ class CraftApp {
9973
10333
  args.push("--menubar-only");
9974
10334
  if (window3?.titlebarHidden)
9975
10335
  args.push("--titlebar-hidden");
10336
+ if (window3?.webSidebarMaterial) {
10337
+ args.push("--web-sidebar-material");
10338
+ if (window3?.webSidebarWidth)
10339
+ args.push("--web-sidebar-width", String(window3.webSidebarWidth));
10340
+ if (window3?.webSidebarMaterialOpacity !== undefined)
10341
+ args.push("--web-sidebar-material-opacity", String(window3.webSidebarMaterialOpacity));
10342
+ }
9976
10343
  if (window3?.icon)
9977
10344
  args.push("--icon", window3.icon);
9978
10345
  if (window3?.nativeSidebar) {
@@ -9988,7 +10355,7 @@ class CraftApp {
9988
10355
  } : undefined;
9989
10356
  if (sidebarConfig?.variant === "desktop") {
9990
10357
  sidebarConfig.material = sidebarConfig.material ?? "sidebar";
9991
- sidebarConfig.backgroundEffect = sidebarConfig.backgroundEffect ?? "shimmer";
10358
+ sidebarConfig.backgroundEffect = sidebarConfig.backgroundEffect ?? "vibrancy";
9992
10359
  sidebarConfig.allowsVibrancy = sidebarConfig.allowsVibrancy ?? true;
9993
10360
  }
9994
10361
  if (sidebarConfig) {
@@ -10169,7 +10536,7 @@ export {
10169
10536
  focusFilters,
10170
10537
  focus,
10171
10538
  exit,
10172
- exec2 as exec,
10539
+ exec,
10173
10540
  env,
10174
10541
  dialog,
10175
10542
  device,
@@ -10238,3 +10605,5 @@ export {
10238
10605
  AudioManager,
10239
10606
  Animated
10240
10607
  };
10608
+
10609
+ //# debugId=141B94C437209F2B64756E2164756E21