craft-native 0.0.330 → 0.0.340
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.
- package/dist/cli.js +40 -6
- package/dist/index.cjs +38 -4
- package/dist/index.js +47 -13
- package/dist/ios/templates/CraftNativeFlatList.swift +40 -10
- package/dist/ios/templates/CraftNativeScreen.swift +4 -2
- package/dist/package.d.ts +9 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -126,7 +126,7 @@ var init_temp_dir = __esm(() => {
|
|
|
126
126
|
});
|
|
127
127
|
|
|
128
128
|
// src/package.ts
|
|
129
|
-
import { spawn } from "child_process";
|
|
129
|
+
import { spawn, spawnSync } from "child_process";
|
|
130
130
|
import { createHash } from "crypto";
|
|
131
131
|
import {
|
|
132
132
|
chmodSync,
|
|
@@ -144,7 +144,7 @@ import {
|
|
|
144
144
|
writeFileSync as writeFileSync3
|
|
145
145
|
} from "fs";
|
|
146
146
|
import { homedir } from "os";
|
|
147
|
-
import { basename, join as join2, resolve } from "path";
|
|
147
|
+
import { basename, dirname, join as join2, resolve } from "path";
|
|
148
148
|
import { deflateRawSync } from "zlib";
|
|
149
149
|
function crc32(data) {
|
|
150
150
|
let c = 4294967295;
|
|
@@ -241,6 +241,19 @@ async function packageApp(config) {
|
|
|
241
241
|
}
|
|
242
242
|
return results;
|
|
243
243
|
}
|
|
244
|
+
function macOSAppStoreRuntimePath(providerPath) {
|
|
245
|
+
const runtime = basename(providerPath) === "craft-app-store" ? providerPath : join2(dirname(providerPath), "craft-app-store");
|
|
246
|
+
if (!existsSync4(runtime))
|
|
247
|
+
throw new Error("App Store packaging requires Pantry\u2019s craft-app-store runtime. Upgrade Craft with Pantry before building.");
|
|
248
|
+
const bytes = readFileSync2(runtime);
|
|
249
|
+
const privateMarkers = ["MenuBarClientCore", "MBAssessmentMode", "developerExtrasEnabled", "javaScriptCanAccessClipboard", "drawsBackground"];
|
|
250
|
+
if (privateMarkers.some((marker) => bytes.includes(Buffer.from(marker))))
|
|
251
|
+
throw new Error("App Store packaging refused a runtime that includes private platform APIs.");
|
|
252
|
+
const probe = spawnSync(runtime, ["--distribution"], { encoding: "utf8", timeout: 1e4 });
|
|
253
|
+
if (probe.status !== 0 || probe.stdout.trim() !== "app-store")
|
|
254
|
+
throw new Error("App Store packaging refused a runtime that includes private platform APIs.");
|
|
255
|
+
return runtime;
|
|
256
|
+
}
|
|
244
257
|
async function packageMacOS(config, outDir) {
|
|
245
258
|
const results = [];
|
|
246
259
|
const { name, version, bundleId = `com.myapp.${name.toLowerCase()}` } = config;
|
|
@@ -248,6 +261,14 @@ async function packageMacOS(config, outDir) {
|
|
|
248
261
|
const hardenedRuntime = !opts.appStore;
|
|
249
262
|
if (opts.appStore && (!opts.entitlements || !opts.provisioningProfile))
|
|
250
263
|
return [{ success: false, platform: "macos", format: "app", error: "App Store packaging requires macos.entitlements and macos.provisioningProfile" }];
|
|
264
|
+
let nativeRuntimePath = opts.nativeRuntimePath;
|
|
265
|
+
if (opts.appStore && nativeRuntimePath) {
|
|
266
|
+
try {
|
|
267
|
+
nativeRuntimePath = macOSAppStoreRuntimePath(nativeRuntimePath);
|
|
268
|
+
} catch (error) {
|
|
269
|
+
return [{ success: false, platform: "macos", format: "app", error: error.message }];
|
|
270
|
+
}
|
|
271
|
+
}
|
|
251
272
|
const appBundlePath = join2(outDir, `${name}.app`);
|
|
252
273
|
const appBundle = opts.appBundlePath ? copyMacOSAppBundle(opts.appBundlePath, appBundlePath) : createMacOSAppBundle({
|
|
253
274
|
name,
|
|
@@ -262,7 +283,9 @@ async function packageMacOS(config, outDir) {
|
|
|
262
283
|
iconPath: config.iconPath,
|
|
263
284
|
provisioningProfile: opts.provisioningProfile,
|
|
264
285
|
additionalExecutables: opts.additionalExecutables,
|
|
265
|
-
nativeRuntimePath
|
|
286
|
+
nativeRuntimePath,
|
|
287
|
+
nativeHost: opts.appStore === true && Boolean(nativeRuntimePath),
|
|
288
|
+
nativeWindow: opts.nativeWindow,
|
|
266
289
|
outputPath: appBundlePath
|
|
267
290
|
});
|
|
268
291
|
if (!appBundle.success) {
|
|
@@ -715,7 +738,7 @@ function createMacOSAppBundle(opts) {
|
|
|
715
738
|
const macOS = join2(contents, "MacOS");
|
|
716
739
|
mkdirSync(macOS, { recursive: true });
|
|
717
740
|
mkdirSync(join2(contents, "Resources"), { recursive: true });
|
|
718
|
-
copyFileSync(binaryPath, join2(macOS, name));
|
|
741
|
+
copyFileSync(opts.nativeHost ? opts.nativeRuntimePath : binaryPath, join2(macOS, name));
|
|
719
742
|
chmodSync(join2(macOS, name), 493);
|
|
720
743
|
for (const executable of additionalExecutables || []) {
|
|
721
744
|
if (!existsSync4(executable))
|
|
@@ -724,7 +747,18 @@ function createMacOSAppBundle(opts) {
|
|
|
724
747
|
copyFileSync(executable, destination);
|
|
725
748
|
chmodSync(destination, 493);
|
|
726
749
|
}
|
|
727
|
-
if (opts.
|
|
750
|
+
if (opts.nativeHost) {
|
|
751
|
+
const backend = join2(macOS, "craft-backend");
|
|
752
|
+
copyFileSync(binaryPath, backend);
|
|
753
|
+
chmodSync(backend, 493);
|
|
754
|
+
writeFileSync3(join2(contents, "Resources", "craft.json"), `${JSON.stringify({
|
|
755
|
+
title: name,
|
|
756
|
+
appName: name,
|
|
757
|
+
...opts.nativeWindow,
|
|
758
|
+
backend: { executable: "craft-backend" }
|
|
759
|
+
}, null, 2)}
|
|
760
|
+
`);
|
|
761
|
+
} else if (opts.nativeRuntimePath) {
|
|
728
762
|
const helper = macOSRuntimeExecutablePath(outputPath);
|
|
729
763
|
const helperContents = resolve(helper, "..", "..");
|
|
730
764
|
mkdirSync(join2(helperContents, "MacOS"), { recursive: true });
|
|
@@ -2988,7 +3022,7 @@ function pinCraftNativeDependency(packagePath, craftVersion) {
|
|
|
2988
3022
|
`);
|
|
2989
3023
|
}
|
|
2990
3024
|
// package.json
|
|
2991
|
-
var version = "0.0.
|
|
3025
|
+
var version = "0.0.340";
|
|
2992
3026
|
|
|
2993
3027
|
// bin/cli.ts
|
|
2994
3028
|
var spawnedFrom = process6.env[CRAFT_CLI_SPAWN_MARKER];
|
package/dist/index.cjs
CHANGED
|
@@ -375,7 +375,7 @@ function craftBinaryNotFoundMessage(triedPath) {
|
|
|
375
375
|
`);
|
|
376
376
|
}
|
|
377
377
|
// package.json
|
|
378
|
-
var version = "0.0.
|
|
378
|
+
var version = "0.0.340";
|
|
379
379
|
|
|
380
380
|
// src/sdk-version.ts
|
|
381
381
|
var SDK_VERSION = version;
|
|
@@ -607,6 +607,19 @@ async function packageApp(config) {
|
|
|
607
607
|
}
|
|
608
608
|
return results;
|
|
609
609
|
}
|
|
610
|
+
function macOSAppStoreRuntimePath(providerPath) {
|
|
611
|
+
const runtime = import_path.basename(providerPath) === "craft-app-store" ? providerPath : import_path.join(import_path.dirname(providerPath), "craft-app-store");
|
|
612
|
+
if (!import_fs.existsSync(runtime))
|
|
613
|
+
throw new Error("App Store packaging requires Pantry’s craft-app-store runtime. Upgrade Craft with Pantry before building.");
|
|
614
|
+
const bytes = import_fs.readFileSync(runtime);
|
|
615
|
+
const privateMarkers = ["MenuBarClientCore", "MBAssessmentMode", "developerExtrasEnabled", "javaScriptCanAccessClipboard", "drawsBackground"];
|
|
616
|
+
if (privateMarkers.some((marker) => bytes.includes(Buffer.from(marker))))
|
|
617
|
+
throw new Error("App Store packaging refused a runtime that includes private platform APIs.");
|
|
618
|
+
const probe = import_child_process.spawnSync(runtime, ["--distribution"], { encoding: "utf8", timeout: 1e4 });
|
|
619
|
+
if (probe.status !== 0 || probe.stdout.trim() !== "app-store")
|
|
620
|
+
throw new Error("App Store packaging refused a runtime that includes private platform APIs.");
|
|
621
|
+
return runtime;
|
|
622
|
+
}
|
|
610
623
|
async function packageMacOS(config, outDir) {
|
|
611
624
|
const results = [];
|
|
612
625
|
const { name, version, bundleId = `com.myapp.${name.toLowerCase()}` } = config;
|
|
@@ -614,6 +627,14 @@ async function packageMacOS(config, outDir) {
|
|
|
614
627
|
const hardenedRuntime = !opts.appStore;
|
|
615
628
|
if (opts.appStore && (!opts.entitlements || !opts.provisioningProfile))
|
|
616
629
|
return [{ success: false, platform: "macos", format: "app", error: "App Store packaging requires macos.entitlements and macos.provisioningProfile" }];
|
|
630
|
+
let nativeRuntimePath = opts.nativeRuntimePath;
|
|
631
|
+
if (opts.appStore && nativeRuntimePath) {
|
|
632
|
+
try {
|
|
633
|
+
nativeRuntimePath = macOSAppStoreRuntimePath(nativeRuntimePath);
|
|
634
|
+
} catch (error) {
|
|
635
|
+
return [{ success: false, platform: "macos", format: "app", error: error.message }];
|
|
636
|
+
}
|
|
637
|
+
}
|
|
617
638
|
const appBundlePath = import_path.join(outDir, `${name}.app`);
|
|
618
639
|
const appBundle = opts.appBundlePath ? copyMacOSAppBundle(opts.appBundlePath, appBundlePath) : createMacOSAppBundle({
|
|
619
640
|
name,
|
|
@@ -628,7 +649,9 @@ async function packageMacOS(config, outDir) {
|
|
|
628
649
|
iconPath: config.iconPath,
|
|
629
650
|
provisioningProfile: opts.provisioningProfile,
|
|
630
651
|
additionalExecutables: opts.additionalExecutables,
|
|
631
|
-
nativeRuntimePath
|
|
652
|
+
nativeRuntimePath,
|
|
653
|
+
nativeHost: opts.appStore === true && Boolean(nativeRuntimePath),
|
|
654
|
+
nativeWindow: opts.nativeWindow,
|
|
632
655
|
outputPath: appBundlePath
|
|
633
656
|
});
|
|
634
657
|
if (!appBundle.success) {
|
|
@@ -1082,7 +1105,7 @@ function createMacOSAppBundle(opts) {
|
|
|
1082
1105
|
const macOS = import_path.join(contents, "MacOS");
|
|
1083
1106
|
import_fs.mkdirSync(macOS, { recursive: true });
|
|
1084
1107
|
import_fs.mkdirSync(import_path.join(contents, "Resources"), { recursive: true });
|
|
1085
|
-
import_fs.copyFileSync(binaryPath, import_path.join(macOS, name));
|
|
1108
|
+
import_fs.copyFileSync(opts.nativeHost ? opts.nativeRuntimePath : binaryPath, import_path.join(macOS, name));
|
|
1086
1109
|
import_fs.chmodSync(import_path.join(macOS, name), 493);
|
|
1087
1110
|
for (const executable of additionalExecutables || []) {
|
|
1088
1111
|
if (!import_fs.existsSync(executable))
|
|
@@ -1091,7 +1114,18 @@ function createMacOSAppBundle(opts) {
|
|
|
1091
1114
|
import_fs.copyFileSync(executable, destination);
|
|
1092
1115
|
import_fs.chmodSync(destination, 493);
|
|
1093
1116
|
}
|
|
1094
|
-
if (opts.
|
|
1117
|
+
if (opts.nativeHost) {
|
|
1118
|
+
const backend = import_path.join(macOS, "craft-backend");
|
|
1119
|
+
import_fs.copyFileSync(binaryPath, backend);
|
|
1120
|
+
import_fs.chmodSync(backend, 493);
|
|
1121
|
+
import_fs.writeFileSync(import_path.join(contents, "Resources", "craft.json"), `${JSON.stringify({
|
|
1122
|
+
title: name,
|
|
1123
|
+
appName: name,
|
|
1124
|
+
...opts.nativeWindow,
|
|
1125
|
+
backend: { executable: "craft-backend" }
|
|
1126
|
+
}, null, 2)}
|
|
1127
|
+
`);
|
|
1128
|
+
} else if (opts.nativeRuntimePath) {
|
|
1095
1129
|
const helper = macOSRuntimeExecutablePath(outputPath);
|
|
1096
1130
|
const helperContents = import_path.resolve(helper, "..", "..");
|
|
1097
1131
|
import_fs.mkdirSync(import_path.join(helperContents, "MacOS"), { recursive: true });
|
package/dist/index.js
CHANGED
|
@@ -117,7 +117,7 @@ function craftBinaryNotFoundMessage(triedPath) {
|
|
|
117
117
|
`);
|
|
118
118
|
}
|
|
119
119
|
// package.json
|
|
120
|
-
var version = "0.0.
|
|
120
|
+
var version = "0.0.340";
|
|
121
121
|
|
|
122
122
|
// src/sdk-version.ts
|
|
123
123
|
var SDK_VERSION = version;
|
|
@@ -237,7 +237,7 @@ function removeTempDir(dir) {
|
|
|
237
237
|
}
|
|
238
238
|
|
|
239
239
|
// src/package.ts
|
|
240
|
-
import { spawn } from "child_process";
|
|
240
|
+
import { spawn, spawnSync } from "child_process";
|
|
241
241
|
import { createHash } from "crypto";
|
|
242
242
|
import {
|
|
243
243
|
chmodSync,
|
|
@@ -255,7 +255,7 @@ import {
|
|
|
255
255
|
writeFileSync as writeFileSync2
|
|
256
256
|
} from "fs";
|
|
257
257
|
import { homedir } from "os";
|
|
258
|
-
import { basename, join as join2, resolve } from "path";
|
|
258
|
+
import { basename, dirname, join as join2, resolve } from "path";
|
|
259
259
|
import { deflateRawSync } from "zlib";
|
|
260
260
|
var DEFAULT_DEB_DEPENDENCIES = ["libgtk-3-0", "libwebkit2gtk-4.1-37", "libnotify-bin"];
|
|
261
261
|
var CRC32_TABLE = (() => {
|
|
@@ -363,6 +363,19 @@ async function packageApp(config) {
|
|
|
363
363
|
}
|
|
364
364
|
return results;
|
|
365
365
|
}
|
|
366
|
+
function macOSAppStoreRuntimePath(providerPath) {
|
|
367
|
+
const runtime = basename(providerPath) === "craft-app-store" ? providerPath : join2(dirname(providerPath), "craft-app-store");
|
|
368
|
+
if (!existsSync3(runtime))
|
|
369
|
+
throw new Error("App Store packaging requires Pantry\u2019s craft-app-store runtime. Upgrade Craft with Pantry before building.");
|
|
370
|
+
const bytes = readFileSync(runtime);
|
|
371
|
+
const privateMarkers = ["MenuBarClientCore", "MBAssessmentMode", "developerExtrasEnabled", "javaScriptCanAccessClipboard", "drawsBackground"];
|
|
372
|
+
if (privateMarkers.some((marker) => bytes.includes(Buffer.from(marker))))
|
|
373
|
+
throw new Error("App Store packaging refused a runtime that includes private platform APIs.");
|
|
374
|
+
const probe = spawnSync(runtime, ["--distribution"], { encoding: "utf8", timeout: 1e4 });
|
|
375
|
+
if (probe.status !== 0 || probe.stdout.trim() !== "app-store")
|
|
376
|
+
throw new Error("App Store packaging refused a runtime that includes private platform APIs.");
|
|
377
|
+
return runtime;
|
|
378
|
+
}
|
|
366
379
|
async function packageMacOS(config, outDir) {
|
|
367
380
|
const results = [];
|
|
368
381
|
const { name, version, bundleId = `com.myapp.${name.toLowerCase()}` } = config;
|
|
@@ -370,6 +383,14 @@ async function packageMacOS(config, outDir) {
|
|
|
370
383
|
const hardenedRuntime = !opts.appStore;
|
|
371
384
|
if (opts.appStore && (!opts.entitlements || !opts.provisioningProfile))
|
|
372
385
|
return [{ success: false, platform: "macos", format: "app", error: "App Store packaging requires macos.entitlements and macos.provisioningProfile" }];
|
|
386
|
+
let nativeRuntimePath = opts.nativeRuntimePath;
|
|
387
|
+
if (opts.appStore && nativeRuntimePath) {
|
|
388
|
+
try {
|
|
389
|
+
nativeRuntimePath = macOSAppStoreRuntimePath(nativeRuntimePath);
|
|
390
|
+
} catch (error) {
|
|
391
|
+
return [{ success: false, platform: "macos", format: "app", error: error.message }];
|
|
392
|
+
}
|
|
393
|
+
}
|
|
373
394
|
const appBundlePath = join2(outDir, `${name}.app`);
|
|
374
395
|
const appBundle = opts.appBundlePath ? copyMacOSAppBundle(opts.appBundlePath, appBundlePath) : createMacOSAppBundle({
|
|
375
396
|
name,
|
|
@@ -384,7 +405,9 @@ async function packageMacOS(config, outDir) {
|
|
|
384
405
|
iconPath: config.iconPath,
|
|
385
406
|
provisioningProfile: opts.provisioningProfile,
|
|
386
407
|
additionalExecutables: opts.additionalExecutables,
|
|
387
|
-
nativeRuntimePath
|
|
408
|
+
nativeRuntimePath,
|
|
409
|
+
nativeHost: opts.appStore === true && Boolean(nativeRuntimePath),
|
|
410
|
+
nativeWindow: opts.nativeWindow,
|
|
388
411
|
outputPath: appBundlePath
|
|
389
412
|
});
|
|
390
413
|
if (!appBundle.success) {
|
|
@@ -838,7 +861,7 @@ function createMacOSAppBundle(opts) {
|
|
|
838
861
|
const macOS = join2(contents, "MacOS");
|
|
839
862
|
mkdirSync(macOS, { recursive: true });
|
|
840
863
|
mkdirSync(join2(contents, "Resources"), { recursive: true });
|
|
841
|
-
copyFileSync(binaryPath, join2(macOS, name));
|
|
864
|
+
copyFileSync(opts.nativeHost ? opts.nativeRuntimePath : binaryPath, join2(macOS, name));
|
|
842
865
|
chmodSync(join2(macOS, name), 493);
|
|
843
866
|
for (const executable of additionalExecutables || []) {
|
|
844
867
|
if (!existsSync3(executable))
|
|
@@ -847,7 +870,18 @@ function createMacOSAppBundle(opts) {
|
|
|
847
870
|
copyFileSync(executable, destination);
|
|
848
871
|
chmodSync(destination, 493);
|
|
849
872
|
}
|
|
850
|
-
if (opts.
|
|
873
|
+
if (opts.nativeHost) {
|
|
874
|
+
const backend = join2(macOS, "craft-backend");
|
|
875
|
+
copyFileSync(binaryPath, backend);
|
|
876
|
+
chmodSync(backend, 493);
|
|
877
|
+
writeFileSync2(join2(contents, "Resources", "craft.json"), `${JSON.stringify({
|
|
878
|
+
title: name,
|
|
879
|
+
appName: name,
|
|
880
|
+
...opts.nativeWindow,
|
|
881
|
+
backend: { executable: "craft-backend" }
|
|
882
|
+
}, null, 2)}
|
|
883
|
+
`);
|
|
884
|
+
} else if (opts.nativeRuntimePath) {
|
|
851
885
|
const helper = macOSRuntimeExecutablePath(outputPath);
|
|
852
886
|
const helperContents = resolve(helper, "..", "..");
|
|
853
887
|
mkdirSync(join2(helperContents, "MacOS"), { recursive: true });
|
|
@@ -10387,7 +10421,7 @@ var commonOptimizations = {
|
|
|
10387
10421
|
};
|
|
10388
10422
|
// src/updater/index.ts
|
|
10389
10423
|
import { createReadStream, createWriteStream, existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync2, rmdirSync, statSync as statSync3, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
|
|
10390
|
-
import { join as join4, basename as basename3, dirname as
|
|
10424
|
+
import { join as join4, basename as basename3, dirname as dirname3 } from "path";
|
|
10391
10425
|
import { tmpdir as tmpdir2 } from "os";
|
|
10392
10426
|
import { execFileSync as execFileSync2, spawn as spawn3 } from "child_process";
|
|
10393
10427
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -10396,7 +10430,7 @@ import { EventEmitter as EventEmitter3 } from "events";
|
|
|
10396
10430
|
// src/updater/macos-bundle.ts
|
|
10397
10431
|
import { execFile, execFileSync } from "child_process";
|
|
10398
10432
|
import { accessSync, constants, existsSync as existsSync4, renameSync, rmSync as rmSync3, statSync as statSync2 } from "fs";
|
|
10399
|
-
import { basename as basename2, dirname, join as join3 } from "path";
|
|
10433
|
+
import { basename as basename2, dirname as dirname2, join as join3 } from "path";
|
|
10400
10434
|
import { promisify } from "util";
|
|
10401
10435
|
var execFileAsync = promisify(execFile);
|
|
10402
10436
|
function isMacOS() {
|
|
@@ -10549,7 +10583,7 @@ async function extractBundle(archivePath, stagingDir) {
|
|
|
10549
10583
|
async function swapBundle(stagedPath, installedPath) {
|
|
10550
10584
|
if (!existsSync4(stagedPath))
|
|
10551
10585
|
throw new Error(`Staged bundle is missing: ${stagedPath}`);
|
|
10552
|
-
const parent =
|
|
10586
|
+
const parent = dirname2(installedPath);
|
|
10553
10587
|
const stamp = `${Date.now()}.${process.pid}`;
|
|
10554
10588
|
const incoming = join3(parent, `.${basename2(installedPath)}.${stamp}.incoming`);
|
|
10555
10589
|
const retired = join3(parent, `.${basename2(installedPath)}.${stamp}.retired`);
|
|
@@ -10590,7 +10624,7 @@ async function swapBundle(stagedPath, installedPath) {
|
|
|
10590
10624
|
}
|
|
10591
10625
|
function canReplaceBundle(installedPath) {
|
|
10592
10626
|
try {
|
|
10593
|
-
const parent =
|
|
10627
|
+
const parent = dirname2(installedPath);
|
|
10594
10628
|
accessSync(parent, constants.W_OK);
|
|
10595
10629
|
if (existsSync4(installedPath))
|
|
10596
10630
|
statSync2(installedPath);
|
|
@@ -10911,7 +10945,7 @@ class AutoUpdater extends EventEmitter3 {
|
|
|
10911
10945
|
return;
|
|
10912
10946
|
}
|
|
10913
10947
|
if (!canReplaceBundle(appPath)) {
|
|
10914
|
-
throw new Error(`Cannot replace ${appPath}: ${
|
|
10948
|
+
throw new Error(`Cannot replace ${appPath}: ${dirname3(appPath)} is not writable by this user. ` + "Install the update manually, or move the app somewhere you own.");
|
|
10915
10949
|
}
|
|
10916
10950
|
const staged = await extractBundle(downloadPath);
|
|
10917
10951
|
try {
|
|
@@ -10957,7 +10991,7 @@ class AutoUpdater extends EventEmitter3 {
|
|
|
10957
10991
|
stdio: "ignore"
|
|
10958
10992
|
});
|
|
10959
10993
|
} else if (downloadPath.endsWith(".zip")) {
|
|
10960
|
-
const appDir =
|
|
10994
|
+
const appDir = dirname3(this.config.appPath);
|
|
10961
10995
|
execFileSync2("powershell", [
|
|
10962
10996
|
"-NoProfile",
|
|
10963
10997
|
"-NonInteractive",
|
|
@@ -10979,7 +11013,7 @@ class AutoUpdater extends EventEmitter3 {
|
|
|
10979
11013
|
} else if (downloadPath.endsWith(".rpm")) {
|
|
10980
11014
|
execFileSync2("sudo", ["rpm", "-U", downloadPath]);
|
|
10981
11015
|
} else if (downloadPath.endsWith(".tar.gz")) {
|
|
10982
|
-
const appDir =
|
|
11016
|
+
const appDir = dirname3(this.config.appPath);
|
|
10983
11017
|
execFileSync2("tar", ["-xzf", downloadPath, "-C", appDir]);
|
|
10984
11018
|
}
|
|
10985
11019
|
}
|
|
@@ -60,7 +60,7 @@ final class CraftNativeFlatList: UICollectionView, UICollectionViewDelegateFlowL
|
|
|
60
60
|
/// The row this cell showed is gone; its views stay in the cell, and
|
|
61
61
|
/// the next row it shows reuses them when it has the same shape.
|
|
62
62
|
func recycle() {
|
|
63
|
-
if let identity = representedIdentity { onRecycle?(identity) }
|
|
63
|
+
if let identity = representedIdentity, showsHostedView { onRecycle?(identity) }
|
|
64
64
|
representedIdentity = nil
|
|
65
65
|
onRecycle = nil
|
|
66
66
|
}
|
|
@@ -81,6 +81,7 @@ final class CraftNativeFlatList: UICollectionView, UICollectionViewDelegateFlowL
|
|
|
81
81
|
// since moved to another cell is that cell's now, and pulling it
|
|
82
82
|
// from there blanked a row on screen.
|
|
83
83
|
if let shown = hostedView, shown !== view, shown.superview === contentView { shown.removeFromSuperview() }
|
|
84
|
+
NSLayoutConstraint.deactivate(hostedConstraints)
|
|
84
85
|
hostedView = view
|
|
85
86
|
view.translatesAutoresizingMaskIntoConstraints = false
|
|
86
87
|
contentView.addSubview(view)
|
|
@@ -110,6 +111,8 @@ final class CraftNativeFlatList: UICollectionView, UICollectionViewDelegateFlowL
|
|
|
110
111
|
func releaseHostedView() {
|
|
111
112
|
recycle()
|
|
112
113
|
if let shown = hostedView, shown.superview === contentView { shown.removeFromSuperview() }
|
|
114
|
+
NSLayoutConstraint.deactivate(hostedConstraints)
|
|
115
|
+
hostedConstraints = []
|
|
113
116
|
hostedView = nil
|
|
114
117
|
}
|
|
115
118
|
|
|
@@ -195,8 +198,11 @@ final class CraftNativeFlatList: UICollectionView, UICollectionViewDelegateFlowL
|
|
|
195
198
|
private var endReachedThreshold = 0.1
|
|
196
199
|
private var endReachedSignature: String?
|
|
197
200
|
private var dataContentSignature = ""
|
|
201
|
+
private var dataOrdinals: [Int] = []
|
|
202
|
+
private var dataItemCount = 0
|
|
198
203
|
private var columns = 1
|
|
199
204
|
private var isApplyingSnapshot = false
|
|
205
|
+
private var snapshotNeedsReset = false
|
|
200
206
|
private var pendingApply: (() -> Void)?
|
|
201
207
|
private var pendingThemeRefresh = false
|
|
202
208
|
private let nativeRefreshControl = UIRefreshControl()
|
|
@@ -224,6 +230,10 @@ final class CraftNativeFlatList: UICollectionView, UICollectionViewDelegateFlowL
|
|
|
224
230
|
backgroundColor = .clear
|
|
225
231
|
alwaysBounceVertical = true
|
|
226
232
|
delegate = self
|
|
233
|
+
// The renderer keeps one live view per keyed row. UIKit's speculative
|
|
234
|
+
// cell prefetch can otherwise move that view out of its visible cell.
|
|
235
|
+
// UICollectionView still recycles and self-sizes the visible window.
|
|
236
|
+
isPrefetchingEnabled = false
|
|
227
237
|
keyboardTapGesture.cancelsTouchesInView = false
|
|
228
238
|
keyboardTapGesture.delegate = self
|
|
229
239
|
keyboardTapGesture.addTarget(self, action: #selector(keyboardTap))
|
|
@@ -382,13 +392,15 @@ final class CraftNativeFlatList: UICollectionView, UICollectionViewDelegateFlowL
|
|
|
382
392
|
let padding = nonNegative(raw?["padding"]) ?? 0
|
|
383
393
|
let horizontal = nonNegative(raw?["paddingHorizontal"]) ?? padding
|
|
384
394
|
let vertical = nonNegative(raw?["paddingVertical"]) ?? padding
|
|
385
|
-
|
|
395
|
+
let inset = UIEdgeInsets(
|
|
386
396
|
top: nonNegative(raw?["paddingTop"]) ?? vertical,
|
|
387
397
|
left: nonNegative(raw?["paddingLeft"]) ?? horizontal,
|
|
388
398
|
bottom: nonNegative(raw?["paddingBottom"]) ?? vertical,
|
|
389
399
|
right: nonNegative(raw?["paddingRight"]) ?? horizontal
|
|
390
400
|
)
|
|
391
|
-
|
|
401
|
+
guard contentInset != inset else { return }
|
|
402
|
+
contentInset = inset
|
|
403
|
+
scrollIndicatorInsets = inset
|
|
392
404
|
collectionViewLayout.invalidateLayout()
|
|
393
405
|
}
|
|
394
406
|
|
|
@@ -479,7 +491,9 @@ final class CraftNativeFlatList: UICollectionView, UICollectionViewDelegateFlowL
|
|
|
479
491
|
self.removeItem = removeItem
|
|
480
492
|
self.endReached = endReached
|
|
481
493
|
self.endReachedThreshold = max(0, endReachedThreshold)
|
|
482
|
-
|
|
494
|
+
let nextColumns = max(1, columns)
|
|
495
|
+
let layoutChanged = self.columns != nextColumns || flowLayout.scrollDirection != (horizontal ? .horizontal : .vertical)
|
|
496
|
+
self.columns = nextColumns
|
|
483
497
|
|
|
484
498
|
layoutIfNeeded()
|
|
485
499
|
let oldItems = itemsByIdentity
|
|
@@ -496,6 +510,19 @@ final class CraftNativeFlatList: UICollectionView, UICollectionViewDelegateFlowL
|
|
|
496
510
|
return Item(identity: identity, node: node, signature: signature, isChrome: role != nil && role != "item")
|
|
497
511
|
}
|
|
498
512
|
if inverted { next.reverse() }
|
|
513
|
+
// Reapplying an identical keyed tree during a timer or badge update
|
|
514
|
+
// must not reconfigure cells, invalidate self-sizing, or reset a drag.
|
|
515
|
+
if !snapshotNeedsReset, !layoutChanged, next.count == items.count,
|
|
516
|
+
zip(next, items).allSatisfy({ $0.identity == $1.identity && $0.signature == $1.signature }) {
|
|
517
|
+
finishApplyingSnapshot()
|
|
518
|
+
return
|
|
519
|
+
}
|
|
520
|
+
snapshotNeedsReset = false
|
|
521
|
+
dataItemCount = 0
|
|
522
|
+
dataOrdinals = next.map { item in
|
|
523
|
+
if !item.isChrome { dataItemCount += 1 }
|
|
524
|
+
return dataItemCount - 1
|
|
525
|
+
}
|
|
499
526
|
items = next
|
|
500
527
|
itemsByIdentity = Dictionary(uniqueKeysWithValues: next.map { ($0.identity, $0) })
|
|
501
528
|
|
|
@@ -504,7 +531,7 @@ final class CraftNativeFlatList: UICollectionView, UICollectionViewDelegateFlowL
|
|
|
504
531
|
alwaysBounceVertical = !horizontal
|
|
505
532
|
showsHorizontalScrollIndicator = horizontal
|
|
506
533
|
showsVerticalScrollIndicator = !horizontal
|
|
507
|
-
flowLayout.invalidateLayout()
|
|
534
|
+
if layoutChanged { flowLayout.invalidateLayout() }
|
|
508
535
|
|
|
509
536
|
let nextIds = Set(itemsByIdentity.keys)
|
|
510
537
|
for identity in oldItems.keys where !nextIds.contains(identity) { removeItem(identity) }
|
|
@@ -550,9 +577,12 @@ final class CraftNativeFlatList: UICollectionView, UICollectionViewDelegateFlowL
|
|
|
550
577
|
}
|
|
551
578
|
|
|
552
579
|
func discardAll() {
|
|
580
|
+
snapshotNeedsReset = true
|
|
553
581
|
visibleCells.compactMap { $0 as? Cell }.forEach { $0.releaseHostedView() }
|
|
554
582
|
itemsByIdentity.keys.forEach { recycleItem?($0) }
|
|
555
583
|
items = []
|
|
584
|
+
dataOrdinals = []
|
|
585
|
+
dataItemCount = 0
|
|
556
586
|
itemsByIdentity = [:]
|
|
557
587
|
renderItem = nil
|
|
558
588
|
recycleItem = nil
|
|
@@ -638,11 +668,11 @@ final class CraftNativeFlatList: UICollectionView, UICollectionViewDelegateFlowL
|
|
|
638
668
|
guard !isApplyingSnapshot, pendingApply == nil,
|
|
639
669
|
let endReached = endReached,
|
|
640
670
|
let lastVisible = indexPathsForVisibleItems.map(\.item).max() else { return }
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
let remaining =
|
|
645
|
-
let thresholdItems = max(1, Int(ceil(Double(
|
|
671
|
+
guard dataItemCount > 0, dataOrdinals.indices.contains(lastVisible) else { return }
|
|
672
|
+
let lastDataOrdinal = dataOrdinals[lastVisible]
|
|
673
|
+
guard lastDataOrdinal >= 0 else { return }
|
|
674
|
+
let remaining = dataItemCount - lastDataOrdinal - 1
|
|
675
|
+
let thresholdItems = max(1, Int(ceil(Double(dataItemCount) * endReachedThreshold)))
|
|
646
676
|
guard remaining <= thresholdItems else { return }
|
|
647
677
|
let signature = dataContentSignature
|
|
648
678
|
guard endReachedSignature != signature else { return }
|
|
@@ -6919,10 +6919,12 @@ final class CraftNativeScreenController: UIViewController, UIScrollViewDelegate,
|
|
|
6919
6919
|
}
|
|
6920
6920
|
// The app script has no screen, so no bar and no tab bar to change.
|
|
6921
6921
|
guard !isAppScript else { return }
|
|
6922
|
+
// Prepared tabs have a window and a top controller too. Keep their
|
|
6923
|
+
// options, but only the focused screen may change the shared chrome.
|
|
6922
6924
|
if options.keys.contains("tabAccessory") {
|
|
6923
6925
|
tabAccessory = options["tabAccessory"] as? [String: Any]
|
|
6924
6926
|
tabAccessoryDeclared = true
|
|
6925
|
-
if navigationController?.topViewController === self,
|
|
6927
|
+
if navigationController?.topViewController === self, isShownOnScreen {
|
|
6926
6928
|
CraftChrome.shared.updateTabAccessory(tabAccessory, owner: self)
|
|
6927
6929
|
}
|
|
6928
6930
|
}
|
|
@@ -6941,7 +6943,7 @@ final class CraftNativeScreenController: UIViewController, UIScrollViewDelegate,
|
|
|
6941
6943
|
}
|
|
6942
6944
|
if let hidden = options["tabBarHidden"] as? Bool {
|
|
6943
6945
|
tabBarHidden = hidden
|
|
6944
|
-
if navigationController?.topViewController === self,
|
|
6946
|
+
if navigationController?.topViewController === self, isShownOnScreen {
|
|
6945
6947
|
CraftChrome.shared.setHiddenByScreen(hidden)
|
|
6946
6948
|
}
|
|
6947
6949
|
}
|
package/dist/package.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Enables users to create installers for their Craft applications
|
|
5
5
|
* across all platforms (macOS, Windows, Linux)
|
|
6
6
|
*/
|
|
7
|
+
import type { WindowOptions } from './types.js';
|
|
7
8
|
/** Runtime packages required by the default Linux DEB, including notify-send. */
|
|
8
9
|
export declare const DEFAULT_DEB_DEPENDENCIES: readonly ['libgtk-3-0', 'libwebkit2gtk-4.1-37', 'libnotify-bin'];
|
|
9
10
|
export interface PackageConfig {
|
|
@@ -89,6 +90,10 @@ export interface PackageConfig {
|
|
|
89
90
|
* the GUI process to its own CFBundleExecutable.
|
|
90
91
|
*/
|
|
91
92
|
nativeRuntimePath?: string;
|
|
93
|
+
/** Window settings for Craft's sandboxed App Store UI host. The compiled
|
|
94
|
+
* binary becomes its inherited backend and implements the bundle protocol.
|
|
95
|
+
*/
|
|
96
|
+
nativeWindow?: Pick<WindowOptions, 'width' | 'height' | 'x' | 'y' | 'headless' | 'systemTray' | 'keepRunning' | 'hideDockIcon' | 'titlebarHidden' | 'frameless' | 'transparent' | 'alwaysOnTop' | 'resizable' | 'darkMode' | 'trayPopover'>;
|
|
92
97
|
/**
|
|
93
98
|
* Extra executables copied into `Contents/MacOS/` beside the app binary.
|
|
94
99
|
*
|
|
@@ -175,6 +180,10 @@ export declare function formatPackagingCommandError(tool: string, code: number |
|
|
|
175
180
|
* Package a Craft application for distribution
|
|
176
181
|
*/
|
|
177
182
|
export declare function packageApp(config: PackageConfig): Promise<PackageResult[]>;
|
|
183
|
+
/** Select the public-only runtime shipped beside Pantry's direct runtime.
|
|
184
|
+
* App Store packaging fails closed if an older provider has no public build.
|
|
185
|
+
*/
|
|
186
|
+
export declare function macOSAppStoreRuntimePath(providerPath: string): string;
|
|
178
187
|
export interface MacOSBundleMetadata {
|
|
179
188
|
name: string;
|
|
180
189
|
/** Actual executable basename when it differs from the app's display name. */
|