craft-native 0.0.76 → 0.0.78

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.
@@ -62,9 +62,15 @@ export interface SpawnOptions {
62
62
  cwd?: string;
63
63
  env?: Record<string, string>;
64
64
  /**
65
- * Run the command through a shell. The Craft bridge translates this
66
- * automatically (cmd.exe on Windows, /bin/sh elsewhere). On the Node
67
- * fallback the command is rewritten to `cmd.exe /c <cmd>` on win32.
65
+ * Run `command` as a command line through a shell (cmd.exe on Windows,
66
+ * /bin/sh elsewhere) instead of executing it directly.
67
+ *
68
+ * Off by default, as in Node. Without it `command` is a program and `args`
69
+ * are its arguments, so a `;` or `|` inside an argument reaches the program
70
+ * as a plain character rather than as shell syntax.
71
+ *
72
+ * A shell takes one string, so `args` cannot be passed with it — supplying
73
+ * both is refused rather than silently honouring one of them.
68
74
  */
69
75
  shell?: boolean;
70
76
  }
@@ -101,11 +101,6 @@ export interface WindowCreateOptions {
101
101
  webSidebarMaterialOpacity?: number;
102
102
  /** Titlebar style (macOS) */
103
103
  titlebarStyle?: 'default' | 'hidden' | 'hiddenInset' | 'customButtonsOnHover';
104
- /** Traffic light position (macOS) */
105
- trafficLightPosition?: {
106
- x: number;
107
- y: number;
108
- };
109
104
  /** Vibrancy effect (macOS) */
110
105
  vibrancy?: 'appearance-based' | 'light' | 'dark' | 'titlebar' | 'selection' | 'menu' | 'popover' | 'sidebar' | 'header' | 'sheet' | 'window' | 'hud' | 'fullscreen-ui' | 'tooltip' | 'content' | 'under-window' | 'under-page';
111
106
  /** Background material (Windows 11) */
@@ -338,12 +333,23 @@ export declare class Window {
338
333
  */
339
334
  setVibrancy(vibrancy: WindowCreateOptions['vibrancy'] | null): Promise<void>;
340
335
  /**
341
- * Set traffic light position (macOS)
342
- */
343
- setTrafficLightPosition(position: {
344
- x: number;
345
- y: number;
346
- }): Promise<void>;
336
+ * Where the platform's window buttons are — read, not written.
337
+ *
338
+ * There was a `setTrafficLightPosition` here, and a `trafficLightPosition`
339
+ * window option beside it. Neither ever moved anything: the host has no
340
+ * handler for the call, and AppKit re-lays out the standard window buttons
341
+ * after any one-shot `setFrameOrigin:`, which is why `macos.zig` leaves them
342
+ * where the window server puts them and says so at length.
343
+ *
344
+ * What a layout actually needs is the opposite direction — where they *are*,
345
+ * which the host measures and publishes on every window:
346
+ *
347
+ * window.craft.windowControls { style, x, y, width, height, ... }
348
+ * --craft-window-controls-width the room to leave, in CSS
349
+ *
350
+ * See `CraftWindowControls`, and `docs/features/window-management.md`.
351
+ */
352
+ get windowControls(): import('../types.js').CraftWindowControls | undefined;
347
353
  /**
348
354
  * Set window level (macOS)
349
355
  */
package/dist/cli.js CHANGED
@@ -22,9 +22,12 @@ var exports_package = {};
22
22
  __export(exports_package, {
23
23
  windowsExecutableName: () => windowsExecutableName,
24
24
  windowsArchitecture: () => windowsArchitecture,
25
+ urlTypesEntry: () => urlTypesEntry,
25
26
  shouldRetryHdiutil: () => shouldRetryHdiutil,
26
27
  renderWixSource: () => renderWixSource,
27
28
  productbuildArguments: () => productbuildArguments,
29
+ pkgbuildComponentPlist: () => pkgbuildComponentPlist,
30
+ pkgbuildArguments: () => pkgbuildArguments,
28
31
  packageApp: () => packageApp,
29
32
  pack: () => pack,
30
33
  notarytoolArguments: () => notarytoolArguments,
@@ -162,6 +165,7 @@ async function packageMacOS(config, outDir) {
162
165
  category: opts.category,
163
166
  minimumSystemVersion: opts.minimumSystemVersion,
164
167
  menuBarOnly: opts.menuBarOnly,
168
+ urlSchemes: opts.urlSchemes,
165
169
  binaryPath: config.binaryPath,
166
170
  iconPath: config.iconPath,
167
171
  provisioningProfile: opts.provisioningProfile,
@@ -358,6 +362,36 @@ async function packageLinux(config, outDir) {
358
362
  }
359
363
  return results;
360
364
  }
365
+ function urlTypesEntry(bundleId, schemes) {
366
+ const seen = new Set;
367
+ const unique = [];
368
+ for (const scheme of schemes) {
369
+ if (!URL_SCHEME.test(scheme))
370
+ throw new Error(`Invalid URL scheme ${JSON.stringify(scheme)}: must start with a letter and contain only letters, digits, "+", "-" or "."`);
371
+ const key = scheme.toLowerCase();
372
+ if (seen.has(key))
373
+ continue;
374
+ seen.add(key);
375
+ unique.push(scheme);
376
+ }
377
+ if (unique.length === 0)
378
+ return null;
379
+ const list = unique.map((scheme) => ` <string>${xml(scheme)}</string>`).join(`
380
+ `);
381
+ return ` <key>CFBundleURLTypes</key>
382
+ <array>
383
+ <dict>
384
+ <key>CFBundleURLName</key>
385
+ <string>${xml(bundleId)}</string>
386
+ <key>CFBundleTypeRole</key>
387
+ <string>Viewer</string>
388
+ <key>CFBundleURLSchemes</key>
389
+ <array>
390
+ ${list}
391
+ </array>
392
+ </dict>
393
+ </array>`;
394
+ }
361
395
  function macOSInfoPlist(metadata) {
362
396
  const entries = [
363
397
  ["CFBundleExecutable", metadata.name],
@@ -376,9 +410,12 @@ function macOSInfoPlist(metadata) {
376
410
  entries.push(["LSMinimumSystemVersion", metadata.minimumSystemVersion]);
377
411
  if (metadata.menuBarOnly)
378
412
  entries.push(["LSUIElement", true]);
379
- const body = entries.map(([key, value]) => ` <key>${xml(key)}</key>
413
+ const scalars = entries.map(([key, value]) => ` <key>${xml(key)}</key>
380
414
  ${value === true ? " <true/>" : ` <string>${xml(value)}</string>`}`).join(`
381
415
  `);
416
+ const urlTypes = urlTypesEntry(metadata.bundleId, metadata.urlSchemes || []);
417
+ const body = urlTypes ? `${scalars}
418
+ ${urlTypes}` : scalars;
382
419
  return `<?xml version="1.0" encoding="UTF-8"?>
383
420
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
384
421
  <plist version="1.0">
@@ -528,6 +565,43 @@ async function createDMG(opts) {
528
565
  return { success: false, error: failures.join(`
529
566
  `) };
530
567
  }
568
+ function pkgbuildComponentPlist(rootRelativeBundlePath) {
569
+ return `<?xml version="1.0" encoding="UTF-8"?>
570
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
571
+ <plist version="1.0">
572
+ <array>
573
+ <dict>
574
+ <key>BundleHasStrictIdentifier</key>
575
+ <true/>
576
+ <key>BundleIsRelocatable</key>
577
+ <false/>
578
+ <key>BundleIsVersionChecked</key>
579
+ <false/>
580
+ <key>BundleOverwriteAction</key>
581
+ <string>upgrade</string>
582
+ <key>RootRelativeBundlePath</key>
583
+ <string>${xml(rootRelativeBundlePath)}</string>
584
+ </dict>
585
+ </array>
586
+ </plist>
587
+ `;
588
+ }
589
+ function pkgbuildArguments(opts) {
590
+ return [
591
+ "--root",
592
+ opts.root,
593
+ "--component-plist",
594
+ opts.componentPlistPath,
595
+ "--identifier",
596
+ opts.identifier,
597
+ "--version",
598
+ opts.version,
599
+ "--install-location",
600
+ "/",
601
+ ...opts.installerIdentity ? ["--sign", opts.installerIdentity] : [],
602
+ opts.outputPath
603
+ ];
604
+ }
531
605
  async function createPKG(opts) {
532
606
  if (!/^[a-zA-Z0-9._-]+$/.test(opts.identifier) || !opts.identifier.includes("."))
533
607
  return { success: false, error: `Invalid pkg identifier "${opts.identifier}"; expected reverse-DNS like com.example.app` };
@@ -545,21 +619,22 @@ async function createPKG(opts) {
545
619
  }
546
620
  const tempDir = mkdtempSync(join(tmpdir(), "craft-pkg-"));
547
621
  try {
548
- const appsDir = join(tempDir, "Applications");
549
- mkdirSync(appsDir, { recursive: true });
550
- cpSync(opts.appBundlePath, join(appsDir, basename(opts.appBundlePath)), { recursive: true });
551
- const built = await runTool("pkgbuild", [
552
- "--root",
553
- tempDir,
554
- "--identifier",
555
- opts.identifier,
556
- "--version",
557
- opts.version,
558
- "--install-location",
559
- "/",
560
- ...opts.installerIdentity ? ["--sign", opts.installerIdentity] : [],
561
- opts.outputPath
562
- ]);
622
+ const root = join(tempDir, "root");
623
+ const appsDir = join(root, "Applications");
624
+ mkdirSync(appsDir, { recursive: true, mode: 493 });
625
+ chmodSync(root, 493);
626
+ const bundleName = basename(opts.appBundlePath);
627
+ cpSync(opts.appBundlePath, join(appsDir, bundleName), { recursive: true });
628
+ const componentPlistPath = join(tempDir, "component.plist");
629
+ writeFileSync(componentPlistPath, pkgbuildComponentPlist(`Applications/${bundleName}`));
630
+ const built = await runTool("pkgbuild", pkgbuildArguments({
631
+ root,
632
+ componentPlistPath,
633
+ identifier: opts.identifier,
634
+ version: opts.version,
635
+ outputPath: opts.outputPath,
636
+ installerIdentity: opts.installerIdentity
637
+ }));
563
638
  return built.success ? { success: true, outputPath: opts.outputPath } : { success: false, error: built.error };
564
639
  } finally {
565
640
  rmSync(tempDir, { recursive: true, force: true });
@@ -956,7 +1031,7 @@ async function pack(options) {
956
1031
  platforms: [detectPlatform()]
957
1032
  });
958
1033
  }
959
- var CRC32_TABLE, MEBIBYTE, HDIUTIL_MAX_ATTEMPTS = 3, runHdiutil = (args) => runTool("hdiutil", args);
1034
+ var CRC32_TABLE, URL_SCHEME, MEBIBYTE, HDIUTIL_MAX_ATTEMPTS = 3, runHdiutil = (args) => runTool("hdiutil", args);
960
1035
  var init_package = __esm(() => {
961
1036
  CRC32_TABLE = (() => {
962
1037
  const table = new Uint32Array(256);
@@ -968,6 +1043,7 @@ var init_package = __esm(() => {
968
1043
  }
969
1044
  return table;
970
1045
  })();
1046
+ URL_SCHEME = /^[a-z][a-z0-9+.-]*$/i;
971
1047
  MEBIBYTE = 1024 * 1024;
972
1048
  });
973
1049
 
@@ -4115,7 +4191,7 @@ function craftBinaryNotFoundMessage(triedPath) {
4115
4191
  `);
4116
4192
  }
4117
4193
  // package.json
4118
- var version = "0.0.76";
4194
+ var version = "0.0.78";
4119
4195
 
4120
4196
  // bin/cli.ts
4121
4197
  var spawnedFrom = process6.env[CRAFT_CLI_SPAWN_MARKER];
package/dist/index.cjs CHANGED
@@ -425,6 +425,7 @@ async function packageMacOS(config, outDir) {
425
425
  category: opts.category,
426
426
  minimumSystemVersion: opts.minimumSystemVersion,
427
427
  menuBarOnly: opts.menuBarOnly,
428
+ urlSchemes: opts.urlSchemes,
428
429
  binaryPath: config.binaryPath,
429
430
  iconPath: config.iconPath,
430
431
  provisioningProfile: opts.provisioningProfile,
@@ -621,6 +622,37 @@ async function packageLinux(config, outDir) {
621
622
  }
622
623
  return results;
623
624
  }
625
+ var URL_SCHEME = /^[a-z][a-z0-9+.-]*$/i;
626
+ function urlTypesEntry(bundleId, schemes) {
627
+ const seen = new Set;
628
+ const unique = [];
629
+ for (const scheme of schemes) {
630
+ if (!URL_SCHEME.test(scheme))
631
+ throw new Error(`Invalid URL scheme ${JSON.stringify(scheme)}: must start with a letter and contain only letters, digits, "+", "-" or "."`);
632
+ const key = scheme.toLowerCase();
633
+ if (seen.has(key))
634
+ continue;
635
+ seen.add(key);
636
+ unique.push(scheme);
637
+ }
638
+ if (unique.length === 0)
639
+ return null;
640
+ const list = unique.map((scheme) => ` <string>${xml(scheme)}</string>`).join(`
641
+ `);
642
+ return ` <key>CFBundleURLTypes</key>
643
+ <array>
644
+ <dict>
645
+ <key>CFBundleURLName</key>
646
+ <string>${xml(bundleId)}</string>
647
+ <key>CFBundleTypeRole</key>
648
+ <string>Viewer</string>
649
+ <key>CFBundleURLSchemes</key>
650
+ <array>
651
+ ${list}
652
+ </array>
653
+ </dict>
654
+ </array>`;
655
+ }
624
656
  function macOSInfoPlist(metadata) {
625
657
  const entries = [
626
658
  ["CFBundleExecutable", metadata.name],
@@ -639,9 +671,12 @@ function macOSInfoPlist(metadata) {
639
671
  entries.push(["LSMinimumSystemVersion", metadata.minimumSystemVersion]);
640
672
  if (metadata.menuBarOnly)
641
673
  entries.push(["LSUIElement", true]);
642
- const body = entries.map(([key, value]) => ` <key>${xml(key)}</key>
674
+ const scalars = entries.map(([key, value]) => ` <key>${xml(key)}</key>
643
675
  ${value === true ? " <true/>" : ` <string>${xml(value)}</string>`}`).join(`
644
676
  `);
677
+ const urlTypes = urlTypesEntry(metadata.bundleId, metadata.urlSchemes || []);
678
+ const body = urlTypes ? `${scalars}
679
+ ${urlTypes}` : scalars;
645
680
  return `<?xml version="1.0" encoding="UTF-8"?>
646
681
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
647
682
  <plist version="1.0">
@@ -794,6 +829,43 @@ async function createDMG(opts) {
794
829
  return { success: false, error: failures.join(`
795
830
  `) };
796
831
  }
832
+ function pkgbuildComponentPlist(rootRelativeBundlePath) {
833
+ return `<?xml version="1.0" encoding="UTF-8"?>
834
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
835
+ <plist version="1.0">
836
+ <array>
837
+ <dict>
838
+ <key>BundleHasStrictIdentifier</key>
839
+ <true/>
840
+ <key>BundleIsRelocatable</key>
841
+ <false/>
842
+ <key>BundleIsVersionChecked</key>
843
+ <false/>
844
+ <key>BundleOverwriteAction</key>
845
+ <string>upgrade</string>
846
+ <key>RootRelativeBundlePath</key>
847
+ <string>${xml(rootRelativeBundlePath)}</string>
848
+ </dict>
849
+ </array>
850
+ </plist>
851
+ `;
852
+ }
853
+ function pkgbuildArguments(opts) {
854
+ return [
855
+ "--root",
856
+ opts.root,
857
+ "--component-plist",
858
+ opts.componentPlistPath,
859
+ "--identifier",
860
+ opts.identifier,
861
+ "--version",
862
+ opts.version,
863
+ "--install-location",
864
+ "/",
865
+ ...opts.installerIdentity ? ["--sign", opts.installerIdentity] : [],
866
+ opts.outputPath
867
+ ];
868
+ }
797
869
  async function createPKG(opts) {
798
870
  if (!/^[a-zA-Z0-9._-]+$/.test(opts.identifier) || !opts.identifier.includes("."))
799
871
  return { success: false, error: `Invalid pkg identifier "${opts.identifier}"; expected reverse-DNS like com.example.app` };
@@ -811,21 +883,22 @@ async function createPKG(opts) {
811
883
  }
812
884
  const tempDir = import_fs.mkdtempSync(import_path.join(import_os.tmpdir(), "craft-pkg-"));
813
885
  try {
814
- const appsDir = import_path.join(tempDir, "Applications");
815
- import_fs.mkdirSync(appsDir, { recursive: true });
816
- import_fs.cpSync(opts.appBundlePath, import_path.join(appsDir, import_path.basename(opts.appBundlePath)), { recursive: true });
817
- const built = await runTool("pkgbuild", [
818
- "--root",
819
- tempDir,
820
- "--identifier",
821
- opts.identifier,
822
- "--version",
823
- opts.version,
824
- "--install-location",
825
- "/",
826
- ...opts.installerIdentity ? ["--sign", opts.installerIdentity] : [],
827
- opts.outputPath
828
- ]);
886
+ const root = import_path.join(tempDir, "root");
887
+ const appsDir = import_path.join(root, "Applications");
888
+ import_fs.mkdirSync(appsDir, { recursive: true, mode: 493 });
889
+ import_fs.chmodSync(root, 493);
890
+ const bundleName = import_path.basename(opts.appBundlePath);
891
+ import_fs.cpSync(opts.appBundlePath, import_path.join(appsDir, bundleName), { recursive: true });
892
+ const componentPlistPath = import_path.join(tempDir, "component.plist");
893
+ import_fs.writeFileSync(componentPlistPath, pkgbuildComponentPlist(`Applications/${bundleName}`));
894
+ const built = await runTool("pkgbuild", pkgbuildArguments({
895
+ root,
896
+ componentPlistPath,
897
+ identifier: opts.identifier,
898
+ version: opts.version,
899
+ outputPath: opts.outputPath,
900
+ installerIdentity: opts.installerIdentity
901
+ }));
829
902
  return built.success ? { success: true, outputPath: opts.outputPath } : { success: false, error: built.error };
830
903
  } finally {
831
904
  import_fs.rmSync(tempDir, { recursive: true, force: true });
@@ -2459,8 +2532,8 @@ class Window {
2459
2532
  async setVibrancy(vibrancy) {
2460
2533
  await this._call("setVibrancy", { vibrancy });
2461
2534
  }
2462
- async setTrafficLightPosition(position) {
2463
- await this._call("setTrafficLightPosition", position);
2535
+ get windowControls() {
2536
+ return globalThis.craft?.windowControls;
2464
2537
  }
2465
2538
  async setWindowLevel(level) {
2466
2539
  await this._call("setWindowLevel", { level });
@@ -10624,7 +10697,9 @@ class CraftApp {
10624
10697
  }
10625
10698
  buildArgs() {
10626
10699
  const args = [];
10627
- const { window: window3, html, url } = this.config;
10700
+ const { window: window3, html, url, appName } = this.config;
10701
+ if (appName)
10702
+ args.push("--app-name", appName);
10628
10703
  if (!window3?.menubarOnly) {
10629
10704
  if (url) {
10630
10705
  args.push("--url", url);
@@ -10658,6 +10733,8 @@ class CraftApp {
10658
10733
  args.push("--hot-reload");
10659
10734
  if (window3?.devTools === false)
10660
10735
  args.push("--no-devtools");
10736
+ else if (window3?.devTools === true)
10737
+ args.push("--dev-tools");
10661
10738
  if (window3?.systemTray)
10662
10739
  args.push("--system-tray");
10663
10740
  if (window3?.hideDockIcon)
@@ -10666,6 +10743,12 @@ class CraftApp {
10666
10743
  args.push("--menubar-only");
10667
10744
  if (window3?.titlebarHidden)
10668
10745
  args.push("--titlebar-hidden");
10746
+ if (window3?.headless)
10747
+ args.push("--headless");
10748
+ if (window3?.keepRunning === true)
10749
+ args.push("--keep-running");
10750
+ else if (window3?.keepRunning === false)
10751
+ args.push("--quit-on-close");
10669
10752
  if (window3?.webSidebarMaterial) {
10670
10753
  args.push("--web-sidebar-material");
10671
10754
  if (window3?.webSidebarWidth)
@@ -10675,6 +10758,8 @@ class CraftApp {
10675
10758
  }
10676
10759
  if (window3?.icon)
10677
10760
  args.push("--icon", window3.icon);
10761
+ if (window3?.frameAutosave)
10762
+ args.push("--frame-autosave", window3.frameAutosave);
10678
10763
  if (window3?.nativeSidebar) {
10679
10764
  args.push("--native-sidebar");
10680
10765
  if (window3?.sidebarWidth)
package/dist/index.d.cts CHANGED
@@ -59,4 +59,4 @@ export declare function show(html: string, options?: WindowOptions): Promise<voi
59
59
  * Quick helper to load a URL
60
60
  */
61
61
  export declare function loadURL(url: string, options?: WindowOptions): Promise<void>;
62
- export type { WindowOptions, CraftSidebarAPI, AppConfig, CraftTrayAPI, CraftWindowAPI, CraftAppAPI, CraftBridgeAPI, Permission, HapticType, CameraOptions, PhotoPickerOptions, CraftMobileAPI, CraftFileSystemAPI, CraftDatabaseAPI, CraftHttpAPI, CraftCryptoAPI, CraftEventType, CraftEventMap, CraftEventHandler, CraftEventEmitter, IOSConfig, AndroidConfig, MacOSConfig, WindowsConfig, LinuxConfig, CraftAppConfig, } from './types.js';
62
+ export type { WindowOptions, CraftSidebarAPI, AppConfig, CraftTrayAPI, CraftWindowAPI, CraftAppAPI, CraftBridgeAPI, Permission, HapticType, CameraOptions, PhotoPickerOptions, CraftMobileAPI, CraftFileSystemAPI, CraftDatabaseAPI, CraftHttpAPI, CraftCryptoAPI, CraftCapabilities, CapabilityNamespace, CapabilityNamespaceStatus, CapabilityAction, CraftPreferencesAPI, CraftPreferencesInfo, CraftSettingsAPI, PreferenceValue, CraftEventType, CraftEventMap, CraftEventHandler, CraftEventEmitter, IOSConfig, AndroidConfig, MacOSConfig, WindowsConfig, LinuxConfig, CraftAppConfig, } from './types.js';
package/dist/index.d.ts CHANGED
@@ -59,4 +59,4 @@ export declare function show(html: string, options?: WindowOptions): Promise<voi
59
59
  * Quick helper to load a URL
60
60
  */
61
61
  export declare function loadURL(url: string, options?: WindowOptions): Promise<void>;
62
- export type { WindowOptions, CraftSidebarAPI, AppConfig, CraftTrayAPI, CraftWindowAPI, CraftAppAPI, CraftBridgeAPI, Permission, HapticType, CameraOptions, PhotoPickerOptions, CraftMobileAPI, CraftFileSystemAPI, CraftDatabaseAPI, CraftHttpAPI, CraftCryptoAPI, CraftEventType, CraftEventMap, CraftEventHandler, CraftEventEmitter, IOSConfig, AndroidConfig, MacOSConfig, WindowsConfig, LinuxConfig, CraftAppConfig, } from './types.js';
62
+ export type { WindowOptions, CraftSidebarAPI, AppConfig, CraftTrayAPI, CraftWindowAPI, CraftAppAPI, CraftBridgeAPI, Permission, HapticType, CameraOptions, PhotoPickerOptions, CraftMobileAPI, CraftFileSystemAPI, CraftDatabaseAPI, CraftHttpAPI, CraftCryptoAPI, CraftCapabilities, CapabilityNamespace, CapabilityNamespaceStatus, CapabilityAction, CraftPreferencesAPI, CraftPreferencesInfo, CraftSettingsAPI, PreferenceValue, CraftEventType, CraftEventMap, CraftEventHandler, CraftEventEmitter, IOSConfig, AndroidConfig, MacOSConfig, WindowsConfig, LinuxConfig, CraftAppConfig, } from './types.js';
package/dist/index.js CHANGED
@@ -195,6 +195,7 @@ async function packageMacOS(config, outDir) {
195
195
  category: opts.category,
196
196
  minimumSystemVersion: opts.minimumSystemVersion,
197
197
  menuBarOnly: opts.menuBarOnly,
198
+ urlSchemes: opts.urlSchemes,
198
199
  binaryPath: config.binaryPath,
199
200
  iconPath: config.iconPath,
200
201
  provisioningProfile: opts.provisioningProfile,
@@ -391,6 +392,37 @@ async function packageLinux(config, outDir) {
391
392
  }
392
393
  return results;
393
394
  }
395
+ var URL_SCHEME = /^[a-z][a-z0-9+.-]*$/i;
396
+ function urlTypesEntry(bundleId, schemes) {
397
+ const seen = new Set;
398
+ const unique = [];
399
+ for (const scheme of schemes) {
400
+ if (!URL_SCHEME.test(scheme))
401
+ throw new Error(`Invalid URL scheme ${JSON.stringify(scheme)}: must start with a letter and contain only letters, digits, "+", "-" or "."`);
402
+ const key = scheme.toLowerCase();
403
+ if (seen.has(key))
404
+ continue;
405
+ seen.add(key);
406
+ unique.push(scheme);
407
+ }
408
+ if (unique.length === 0)
409
+ return null;
410
+ const list = unique.map((scheme) => ` <string>${xml(scheme)}</string>`).join(`
411
+ `);
412
+ return ` <key>CFBundleURLTypes</key>
413
+ <array>
414
+ <dict>
415
+ <key>CFBundleURLName</key>
416
+ <string>${xml(bundleId)}</string>
417
+ <key>CFBundleTypeRole</key>
418
+ <string>Viewer</string>
419
+ <key>CFBundleURLSchemes</key>
420
+ <array>
421
+ ${list}
422
+ </array>
423
+ </dict>
424
+ </array>`;
425
+ }
394
426
  function macOSInfoPlist(metadata) {
395
427
  const entries = [
396
428
  ["CFBundleExecutable", metadata.name],
@@ -409,9 +441,12 @@ function macOSInfoPlist(metadata) {
409
441
  entries.push(["LSMinimumSystemVersion", metadata.minimumSystemVersion]);
410
442
  if (metadata.menuBarOnly)
411
443
  entries.push(["LSUIElement", true]);
412
- const body = entries.map(([key, value]) => ` <key>${xml(key)}</key>
444
+ const scalars = entries.map(([key, value]) => ` <key>${xml(key)}</key>
413
445
  ${value === true ? " <true/>" : ` <string>${xml(value)}</string>`}`).join(`
414
446
  `);
447
+ const urlTypes = urlTypesEntry(metadata.bundleId, metadata.urlSchemes || []);
448
+ const body = urlTypes ? `${scalars}
449
+ ${urlTypes}` : scalars;
415
450
  return `<?xml version="1.0" encoding="UTF-8"?>
416
451
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
417
452
  <plist version="1.0">
@@ -564,6 +599,43 @@ async function createDMG(opts) {
564
599
  return { success: false, error: failures.join(`
565
600
  `) };
566
601
  }
602
+ function pkgbuildComponentPlist(rootRelativeBundlePath) {
603
+ return `<?xml version="1.0" encoding="UTF-8"?>
604
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
605
+ <plist version="1.0">
606
+ <array>
607
+ <dict>
608
+ <key>BundleHasStrictIdentifier</key>
609
+ <true/>
610
+ <key>BundleIsRelocatable</key>
611
+ <false/>
612
+ <key>BundleIsVersionChecked</key>
613
+ <false/>
614
+ <key>BundleOverwriteAction</key>
615
+ <string>upgrade</string>
616
+ <key>RootRelativeBundlePath</key>
617
+ <string>${xml(rootRelativeBundlePath)}</string>
618
+ </dict>
619
+ </array>
620
+ </plist>
621
+ `;
622
+ }
623
+ function pkgbuildArguments(opts) {
624
+ return [
625
+ "--root",
626
+ opts.root,
627
+ "--component-plist",
628
+ opts.componentPlistPath,
629
+ "--identifier",
630
+ opts.identifier,
631
+ "--version",
632
+ opts.version,
633
+ "--install-location",
634
+ "/",
635
+ ...opts.installerIdentity ? ["--sign", opts.installerIdentity] : [],
636
+ opts.outputPath
637
+ ];
638
+ }
567
639
  async function createPKG(opts) {
568
640
  if (!/^[a-zA-Z0-9._-]+$/.test(opts.identifier) || !opts.identifier.includes("."))
569
641
  return { success: false, error: `Invalid pkg identifier "${opts.identifier}"; expected reverse-DNS like com.example.app` };
@@ -581,21 +653,22 @@ async function createPKG(opts) {
581
653
  }
582
654
  const tempDir = mkdtempSync(join(tmpdir(), "craft-pkg-"));
583
655
  try {
584
- const appsDir = join(tempDir, "Applications");
585
- mkdirSync(appsDir, { recursive: true });
586
- cpSync(opts.appBundlePath, join(appsDir, basename(opts.appBundlePath)), { recursive: true });
587
- const built = await runTool("pkgbuild", [
588
- "--root",
589
- tempDir,
590
- "--identifier",
591
- opts.identifier,
592
- "--version",
593
- opts.version,
594
- "--install-location",
595
- "/",
596
- ...opts.installerIdentity ? ["--sign", opts.installerIdentity] : [],
597
- opts.outputPath
598
- ]);
656
+ const root = join(tempDir, "root");
657
+ const appsDir = join(root, "Applications");
658
+ mkdirSync(appsDir, { recursive: true, mode: 493 });
659
+ chmodSync(root, 493);
660
+ const bundleName = basename(opts.appBundlePath);
661
+ cpSync(opts.appBundlePath, join(appsDir, bundleName), { recursive: true });
662
+ const componentPlistPath = join(tempDir, "component.plist");
663
+ writeFileSync(componentPlistPath, pkgbuildComponentPlist(`Applications/${bundleName}`));
664
+ const built = await runTool("pkgbuild", pkgbuildArguments({
665
+ root,
666
+ componentPlistPath,
667
+ identifier: opts.identifier,
668
+ version: opts.version,
669
+ outputPath: opts.outputPath,
670
+ installerIdentity: opts.installerIdentity
671
+ }));
599
672
  return built.success ? { success: true, outputPath: opts.outputPath } : { success: false, error: built.error };
600
673
  } finally {
601
674
  rmSync(tempDir, { recursive: true, force: true });
@@ -2229,8 +2302,8 @@ class Window {
2229
2302
  async setVibrancy(vibrancy) {
2230
2303
  await this._call("setVibrancy", { vibrancy });
2231
2304
  }
2232
- async setTrafficLightPosition(position) {
2233
- await this._call("setTrafficLightPosition", position);
2305
+ get windowControls() {
2306
+ return globalThis.craft?.windowControls;
2234
2307
  }
2235
2308
  async setWindowLevel(level) {
2236
2309
  await this._call("setWindowLevel", { level });
@@ -10394,7 +10467,9 @@ class CraftApp {
10394
10467
  }
10395
10468
  buildArgs() {
10396
10469
  const args = [];
10397
- const { window: window3, html, url } = this.config;
10470
+ const { window: window3, html, url, appName } = this.config;
10471
+ if (appName)
10472
+ args.push("--app-name", appName);
10398
10473
  if (!window3?.menubarOnly) {
10399
10474
  if (url) {
10400
10475
  args.push("--url", url);
@@ -10428,6 +10503,8 @@ class CraftApp {
10428
10503
  args.push("--hot-reload");
10429
10504
  if (window3?.devTools === false)
10430
10505
  args.push("--no-devtools");
10506
+ else if (window3?.devTools === true)
10507
+ args.push("--dev-tools");
10431
10508
  if (window3?.systemTray)
10432
10509
  args.push("--system-tray");
10433
10510
  if (window3?.hideDockIcon)
@@ -10436,6 +10513,12 @@ class CraftApp {
10436
10513
  args.push("--menubar-only");
10437
10514
  if (window3?.titlebarHidden)
10438
10515
  args.push("--titlebar-hidden");
10516
+ if (window3?.headless)
10517
+ args.push("--headless");
10518
+ if (window3?.keepRunning === true)
10519
+ args.push("--keep-running");
10520
+ else if (window3?.keepRunning === false)
10521
+ args.push("--quit-on-close");
10439
10522
  if (window3?.webSidebarMaterial) {
10440
10523
  args.push("--web-sidebar-material");
10441
10524
  if (window3?.webSidebarWidth)
@@ -10445,6 +10528,8 @@ class CraftApp {
10445
10528
  }
10446
10529
  if (window3?.icon)
10447
10530
  args.push("--icon", window3.icon);
10531
+ if (window3?.frameAutosave)
10532
+ args.push("--frame-autosave", window3.frameAutosave);
10448
10533
  if (window3?.nativeSidebar) {
10449
10534
  args.push("--native-sidebar");
10450
10535
  if (window3?.sidebarWidth)